aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO3
-rw-r--r--include/Body.hpp205
-rw-r--r--include/BodyItem.hpp175
-rw-r--r--include/QuickMedia.hpp2
-rw-r--r--plugins/Matrix.hpp2
-rw-r--r--plugins/Page.hpp1
-rw-r--r--src/Body.cpp192
-rw-r--r--src/BodyItem.cpp83
-rw-r--r--src/QuickMedia.cpp291
-rw-r--r--src/Storage.cpp3
-rw-r--r--src/plugins/FileManager.cpp2
-rw-r--r--src/plugins/Fourchan.cpp2
-rw-r--r--src/plugins/HotExamples.cpp2
-rw-r--r--src/plugins/MangaGeneric.cpp2
-rw-r--r--src/plugins/Mangadex.cpp4
-rw-r--r--src/plugins/Manganelo.cpp2
-rw-r--r--src/plugins/Matrix.cpp166
-rw-r--r--src/plugins/NyaaSi.cpp14
-rw-r--r--src/plugins/Soundcloud.cpp6
19 files changed, 655 insertions, 502 deletions
diff --git a/TODO b/TODO
index 5bc6668..82467dd 100644
--- a/TODO
+++ b/TODO
@@ -191,4 +191,5 @@ Handle users_default power level in matrix.
Remove power_levels from sync and request them manually when entering a room.
Synapse is gay and mentions do not actually include the whole mxid. It only includes the username part of it. This conflicts with quickmedia notification and mentions where quickmedia includes the mxid in the mention instead,
while the user might have disable username mentions. In those cases the user wont get a notification for mxid mention. Mention name instead?
-Make it possible to redact invites. \ No newline at end of file
+Make it possible to redact invites.
+Reapply filter when changing body item text. \ No newline at end of file
diff --git a/include/Body.hpp b/include/Body.hpp
index 43fdf22..b44a386 100644
--- a/include/Body.hpp
+++ b/include/Body.hpp
@@ -1,9 +1,8 @@
#pragma once
-#include "Text.hpp"
+#include "BodyItem.hpp"
#include "AsyncImageLoader.hpp"
#include "RoundedRectangle.hpp"
-#include <SFML/Graphics/Text.hpp>
#include <SFML/Graphics/RectangleShape.hpp>
#include <SFML/Graphics/Sprite.hpp>
#include <json/value.h>
@@ -18,171 +17,22 @@ namespace sf {
}
namespace QuickMedia {
- class Program;
-
- enum class FetchStatus {
- NONE,
- QUEUED_LOADING,
- LOADING,
- FINISHED_LOADING,
- FAILED_TO_LOAD
- };
-
- enum class ThumbnailMaskType {
- NONE,
- CIRCLE,
- ROUNDED_RECTANGLE
- };
-
- enum BodyTheme : int {
- BODY_THEME_MINIMAL,
- BODY_THEME_MODERN_SPACIOUS
- };
-
- // TODO: Remove and create an Userdata class instead to replace the void* userdata in BodyItem
- class BodyItemExtra {
- public:
- virtual ~BodyItemExtra() = default;
- };
-
- struct Reaction {
- std::unique_ptr<Text> text;
- void *userdata = nullptr;
- };
+ using BodyItemRenderCallback = std::function<void(std::shared_ptr<BodyItem> &body_item)>;
+ // Return true to merge
+ using BodyItemMergeHandler = std::function<bool(BodyItem *prev_item, BodyItem *this_item)>;
- class BodyItem {
+ class BodyItemList {
public:
- BodyItem(std::string _title);
- BodyItem(const BodyItem&) = delete;
- BodyItem& operator=(const BodyItem &other);
-
- static std::shared_ptr<BodyItem> create(std::string title) { return std::make_shared<BodyItem>(std::move(title)); }
-
- void set_title(std::string new_title) {
- if(title == new_title)
- return;
- title = std::move(new_title);
- dirty = true;
- }
-
- void set_description(std::string new_description) {
- if(description == new_description)
- return;
- description = std::move(new_description);
- dirty_description = true;
- }
-
- void set_author(std::string new_author) {
- if(author == new_author)
- return;
- author = std::move(new_author);
- dirty_author = true;
- }
-
- // |new_timestamp| is in milliseconds
- void set_timestamp(time_t new_timestamp) {
- if(new_timestamp == timestamp)
- return;
- timestamp = new_timestamp;
- dirty_timestamp = true;
- }
-
- void set_title_color(sf::Color new_color) {
- if(new_color == title_color)
- return;
- title_color = new_color;
- dirty = true;
- }
-
- void set_description_color(sf::Color new_color) {
- if(new_color == description_color)
- return;
- description_color = new_color;
- dirty_description = true;
- }
-
- void set_author_color(sf::Color new_color) {
- if(new_color == author_color)
- return;
- author_color = new_color;
- dirty_author = true;
- }
-
- void add_reaction(std::string text, void *userdata);
-
- // Returns true if reaction is found
- bool remove_reaction_by_userdata(void *userdata) {
- for(auto it = reactions.begin(); it != reactions.end(); ++it) {
- if(it->userdata == userdata) {
- reactions.erase(it);
- return true;
- }
- }
- return false;
- }
-
- const std::string& get_title() const { return title; }
- const std::string& get_description() const { return description; }
- const std::string& get_author() const { return author; }
- // In milliseconds
- time_t get_timestamp() const { return timestamp; }
-
- sf::Color get_title_color() const { return title_color; }
- sf::Color get_description_color() const { return description_color; }
- sf::Color get_author_color() const { return author_color; }
-
- // TODO: Use a list of strings instead, not all plugins need all of these fields
- std::string url;
- std::string thumbnail_url;
- bool visible;
- bool dirty;
- bool dirty_description;
- bool dirty_author;
- bool dirty_timestamp;
- // TODO: Remove this and instead if |thumbnail_url| starts with file://, then its a local file
- bool thumbnail_is_local;
- std::unique_ptr<Text> title_text;
- std::unique_ptr<Text> description_text;
- std::unique_ptr<Text> author_text;
- std::unique_ptr<sf::Text> timestamp_text; // TODO: Remove
- // Used by image boards for example. The elements are indices to other body items
- std::vector<size_t> replies_to;
- // Used by image boards for example. The elements are indices to other body items
- std::vector<size_t> replies;
- std::string post_number;
- void *userdata; // Not managed, should be deallocated by whoever sets this
- float loaded_height = 0.0f;
- sf::Vector2f loaded_image_size;
- float loaded_content_height = 0.0f;
- FetchStatus embedded_item_status = FetchStatus::NONE;
- // Important! Should refer to a new BodyItem, not one that already exists in the body.
- // TODO: Allow referring to an existing body item. This doesn't work properly at the moment because max width of text and line count calculation getting messed up
- // if an embedded item wraps but not the original body item.
- std::shared_ptr<BodyItem> embedded_item; // Used by matrix for example to display reply message body. Note: only the first level of embedded items is rendered (not recursive, this is done on purpose)
- ThumbnailMaskType thumbnail_mask_type = ThumbnailMaskType::NONE;
- sf::Vector2i thumbnail_size;
- std::vector<Reaction> reactions; // TODO: Move to a different body item type
- std::shared_ptr<BodyItemExtra> extra; // TODO: Remove
-
- // Internal use only
- int keep_alive_frames = 0;
+ BodyItemList(BodyItems *body_items) : body_items(body_items) {}
+ std::shared_ptr<BodyItem>* data() { return body_items->data(); }
+ const std::shared_ptr<BodyItem>* data() const { return body_items->data(); }
+ std::shared_ptr<BodyItem>& operator[](size_t index) { return (*body_items)[index]; }
+ const std::shared_ptr<BodyItem>& operator[](size_t index) const { return (*body_items)[index]; }
+ size_t size() const { return body_items->size(); }
private:
- // TODO: Clean up these strings when set in text, and get_title for example should return |title_text.getString()|
- // TODO: Use sf::String instead, removes the need to convert to utf32 every time the text is dirty (for example when resizing window)
- std::string title;
- std::string description;
- std::string author;
- time_t timestamp;
- sf::Color title_color;
- sf::Color author_color;
- sf::Color description_color;
+ BodyItems *body_items;
};
- using BodyItems = std::vector<std::shared_ptr<BodyItem>>;
- using BodyItemRenderCallback = std::function<void(std::shared_ptr<BodyItem> &body_item)>;
- // Return true to merge
- using BodyItemMergeHandler = std::function<bool(BodyItem *prev_item, BodyItem *this_item)>;
-
enum class AttachSide {
TOP,
BOTTOM
@@ -212,13 +62,34 @@ namespace QuickMedia {
void select_first_item(bool reset_page_scroll = true);
void select_last_item();
+ void set_items(BodyItems items);
void clear_items();
+ void prepend_item(std::shared_ptr<BodyItem> body_item);
void prepend_items_reverse(BodyItems new_items);
+ void append_item(std::shared_ptr<BodyItem> body_item);
void append_items(BodyItems new_items);
+ void insert_item(std::shared_ptr<BodyItem> body_item, int index);
+ void move_items_to(Body *other_body);
+ void move_item(size_t src_index, size_t dst_index);
// Returns the inserted position
size_t insert_item_by_timestamp(std::shared_ptr<BodyItem> body_item);
// Note: keeps the selected item (moving the selected_item index if necessary)
void insert_items_by_timestamps(BodyItems new_items);
+ void for_each_item(std::function<void(std::shared_ptr<BodyItem>&)> callback);
+ // Return true if the item is the match
+ std::shared_ptr<BodyItem> find_item(std::function<bool(std::shared_ptr<BodyItem>&)> callback);
+ // Returns -1 if no item is found
+ int find_item_index(std::function<bool(std::shared_ptr<BodyItem>&)> callback);
+ // Return true to remove the current item.
+ // Returns true if the item was found.
+ bool erase_item(std::function<bool(std::shared_ptr<BodyItem>&)> callback);
+ std::shared_ptr<BodyItem> get_item_by_index(size_t index);
+ BodyItemList get_items();
+ BodyItems get_items_copy();
+ // If |end_index| is (size_t)-1, then all items starting from |start_index| until the end are copied
+ void copy_range(size_t start_index, size_t end_index, BodyItems &target);
+ size_t get_num_items() const;
+ void reverse_items();
void clear_cache();
void clear_text_cache();
@@ -255,17 +126,12 @@ namespace QuickMedia {
// This is the item we can see the end of
bool is_bottom_cut_off() const { return bottom_cut_off; }
int get_num_visible_items() const { return num_visible_items; };
-
- // Call this once after adding new items
- void items_set_dirty(bool force = false);
void apply_search_filter_for_item(BodyItem *body_item);
bool can_move_left() const { return selected_column > 0; }
bool can_move_right() const { return selected_column + 1 < num_columns; }
- // TODO: Prevent items from being removed when render is in progress. That would invalidate references and cause a crash
- BodyItems items;
bool draw_thumbnails;
// Set to {0, 0} to disable resizing
sf::Vector2i thumbnail_max_size;
@@ -315,6 +181,9 @@ namespace QuickMedia {
BOTTOM
};
+ // TODO: Prevent items from being removed when render is in progress. That would invalidate references and cause a crash
+ BodyItems items;
+
BodyTheme body_theme;
int selected_item;
@@ -376,8 +245,6 @@ namespace QuickMedia {
sf::Vector2f item_background_target_size;
float item_background_target_height = 0.0f;
TargetSetState target_set = TargetSetState::NOT_SET;
- // TODO: Instead of using this, add functions for modifying |items| and apply the filter on those new items
- DirtyState items_dirty = DirtyState::FALSE;
std::string current_filter;
bool using_filter = false;
sf::Shader *rounded_rectangle_mask_shader;
diff --git a/include/BodyItem.hpp b/include/BodyItem.hpp
new file mode 100644
index 0000000..ed53908
--- /dev/null
+++ b/include/BodyItem.hpp
@@ -0,0 +1,175 @@
+#pragma once
+
+#include "Text.hpp"
+#include <memory>
+#include <SFML/Graphics/Text.hpp>
+
+namespace sf {
+ class RenderTarget;
+}
+
+namespace QuickMedia {
+ class Body;
+
+ enum class FetchStatus {
+ NONE,
+ QUEUED_LOADING,
+ LOADING,
+ FINISHED_LOADING,
+ FAILED_TO_LOAD
+ };
+
+ enum class ThumbnailMaskType {
+ NONE,
+ CIRCLE,
+ ROUNDED_RECTANGLE
+ };
+
+ enum BodyTheme : int {
+ BODY_THEME_MINIMAL,
+ BODY_THEME_MODERN_SPACIOUS
+ };
+
+ // TODO: Remove and create an Userdata class instead to replace the void* userdata in BodyItem
+ class BodyItemExtra {
+ public:
+ virtual ~BodyItemExtra() = default;
+ };
+
+ struct Reaction {
+ std::unique_ptr<Text> text;
+ void *userdata = nullptr;
+ };
+
+ class BodyItem {
+ public:
+ BodyItem(std::string _title);
+ BodyItem(const BodyItem&) = delete;
+ BodyItem& operator=(const BodyItem &other);
+
+ static std::shared_ptr<BodyItem> create(std::string title) { return std::make_shared<BodyItem>(std::move(title)); }
+
+ void set_title(std::string new_title) {
+ if(title == new_title)
+ return;
+ title = std::move(new_title);
+ dirty = true;
+ }
+
+ void set_description(std::string new_description) {
+ if(description == new_description)
+ return;
+ description = std::move(new_description);
+ dirty_description = true;
+ }
+
+ void set_author(std::string new_author) {
+ if(author == new_author)
+ return;
+ author = std::move(new_author);
+ dirty_author = true;
+ }
+
+ // |new_timestamp| is in milliseconds
+ void set_timestamp(time_t new_timestamp) {
+ if(new_timestamp == timestamp)
+ return;
+ timestamp = new_timestamp;
+ dirty_timestamp = true;
+ }
+
+ void set_title_color(sf::Color new_color) {
+ if(new_color == title_color)
+ return;
+ title_color = new_color;
+ dirty = true;
+ }
+
+ void set_description_color(sf::Color new_color) {
+ if(new_color == description_color)
+ return;
+ description_color = new_color;
+ dirty_description = true;
+ }
+
+ void set_author_color(sf::Color new_color) {
+ if(new_color == author_color)
+ return;
+ author_color = new_color;
+ dirty_author = true;
+ }
+
+ void add_reaction(std::string text, void *userdata);
+
+ // Returns true if reaction is found
+ bool remove_reaction_by_userdata(void *userdata) {
+ for(auto it = reactions.begin(); it != reactions.end(); ++it) {
+ if(it->userdata == userdata) {
+ reactions.erase(it);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ const std::string& get_title() const { return title; }
+ const std::string& get_description() const { return description; }
+ const std::string& get_author() const { return author; }
+ // In milliseconds
+ time_t get_timestamp() const { return timestamp; }
+
+ sf::Color get_title_color() const { return title_color; }
+ sf::Color get_description_color() const { return description_color; }
+ sf::Color get_author_color() const { return author_color; }
+
+ void draw_list(Body *body, sf::RenderTarget &render_target);
+
+ // TODO: Use a list of strings instead, not all plugins need all of these fields
+ std::string url;
+ std::string thumbnail_url;
+ bool visible;
+ bool dirty;
+ bool dirty_description;
+ bool dirty_author;
+ bool dirty_timestamp;
+ // TODO: Remove this and instead if |thumbnail_url| starts with file://, then its a local file
+ bool thumbnail_is_local;
+ std::unique_ptr<Text> title_text;
+ std::unique_ptr<Text> description_text;
+ std::unique_ptr<Text> author_text;
+ std::unique_ptr<sf::Text> timestamp_text; // TODO: Remove
+ // Used by image boards for example. The elements are indices to other body items
+ std::vector<size_t> replies_to;
+ // Used by image boards for example. The elements are indices to other body items
+ std::vector<size_t> replies;
+ std::string post_number;
+ void *userdata; // Not managed, should be deallocated by whoever sets this
+ float loaded_height = 0.0f;
+ sf::Vector2f loaded_image_size;
+ float loaded_content_height = 0.0f;
+ FetchStatus embedded_item_status = FetchStatus::NONE;
+ // Important! Should refer to a new BodyItem, not one that already exists in the body.
+ // TODO: Allow referring to an existing body item. This doesn't work properly at the moment because max width of text and line count calculation getting messed up
+ // if an embedded item wraps but not the original body item.
+ std::shared_ptr<BodyItem> embedded_item; // Used by matrix for example to display reply message body. Note: only the first level of embedded items is rendered (not recursive, this is done on purpose)
+ ThumbnailMaskType thumbnail_mask_type = ThumbnailMaskType::NONE;
+ sf::Vector2i thumbnail_size;
+ std::vector<Reaction> reactions; // TODO: Move to a different body item type
+ std::shared_ptr<BodyItemExtra> extra; // TODO: Remove
+
+ // Internal use only
+ int keep_alive_frames = 0;
+ private:
+ // TODO: Clean up these strings when set in text, and get_title for example should return |title_text.getString()|
+ // TODO: Use sf::String instead, removes the need to convert to utf32 every time the text is dirty (for example when resizing window)
+ std::string title;
+ std::string description;
+ std::string author;
+ time_t timestamp;
+ sf::Color title_color;
+ sf::Color author_color;
+ sf::Color description_color;
+ };
+
+ using BodyItems = std::vector<std::shared_ptr<BodyItem>>;
+} \ No newline at end of file
diff --git a/include/QuickMedia.hpp b/include/QuickMedia.hpp
index 0cd65b4..4399c3d 100644
--- a/include/QuickMedia.hpp
+++ b/include/QuickMedia.hpp
@@ -115,7 +115,7 @@ namespace QuickMedia {
bool page_loop(std::vector<Tab> &tabs, int start_tab_index = 0, PageLoopSubmitHandler after_submit_handler = nullptr);
void video_page_download_video(const std::string &url, sf::WindowHandle video_player_window = None);
bool video_download_if_non_streamable(std::string &video_url, std::string &audio_url, bool &is_audio_only, bool &has_embedded_audio, PageType previous_page);
- void video_content_page(Page *parent_page, VideoPage *video_page, std::string video_title, bool download_if_streaming_fails, Body *parent_body, BodyItems &next_play_items, int play_index, int *parent_body_page = nullptr, const std::string &parent_page_search = "");
+ void video_content_page(Page *parent_page, VideoPage *video_page, std::string video_title, bool download_if_streaming_fails, Body *parent_body, int play_index, int *parent_body_page = nullptr, const std::string &parent_page_search = "");
// Returns -1 to go to previous chapter, 0 to stay on same chapter and 1 to go to next chapter
int image_page(MangaImagesPage *images_page, Body *chapters_body);
void image_continuous_page(MangaImagesPage *images_page);
diff --git a/plugins/Matrix.hpp b/plugins/Matrix.hpp
index dd633e5..e94cc13 100644
--- a/plugins/Matrix.hpp
+++ b/plugins/Matrix.hpp
@@ -321,7 +321,6 @@ namespace QuickMedia {
PluginResult submit(const std::string &title, const std::string &url, std::vector<Tab> &result_tabs) override;
bool submit_is_async() override { return false; }
bool clear_search_after_submit() override { return true; }
- void on_navigate_to_page(Body *body) override;
void add_body_item(std::shared_ptr<BodyItem> body_item);
@@ -352,7 +351,6 @@ namespace QuickMedia {
PluginResult submit(const std::string &title, const std::string &url, std::vector<Tab> &result_tabs) override;
bool submit_is_async() override { return false; }
bool clear_search_after_submit() override { return true; }
- void on_navigate_to_page(Body *body) override;
void add_room_body_item_to_tag(std::shared_ptr<BodyItem> body_item, const std::string &tag);
void remove_room_body_item_from_tag(std::shared_ptr<BodyItem> body_item, const std::string &tag);
diff --git a/plugins/Page.hpp b/plugins/Page.hpp
index 6cea186..c61fd6d 100644
--- a/plugins/Page.hpp
+++ b/plugins/Page.hpp
@@ -8,6 +8,7 @@
#include "../include/MediaChapter.hpp"
namespace QuickMedia {
+ class Program;
constexpr int SEARCH_DELAY_FILTER = 50;
// TODO: Remove to PageType when the other PageType is removed
diff --git a/src/Body.cpp b/src/Body.cpp
index 0804399..6e01266 100644
--- a/src/Body.cpp
+++ b/src/Body.cpp
@@ -92,79 +92,6 @@ namespace QuickMedia {
return sf::Vector2f(vec.x, vec.y);
}
- BodyItem::BodyItem(std::string _title) :
- visible(true),
- dirty(false),
- dirty_description(false),
- dirty_author(false),
- dirty_timestamp(false),
- thumbnail_is_local(false),
- userdata(nullptr),
- timestamp(0),
- title_color(get_current_theme().text_color),
- author_color(get_current_theme().text_color),
- description_color(get_current_theme().text_color)
- {
- if(!_title.empty())
- set_title(std::move(_title));
- }
-
- BodyItem& BodyItem::operator=(const BodyItem &other) {
- url = other.url;
- thumbnail_url = other.thumbnail_url;
- visible = other.visible;
- dirty = !other.title.empty();
- dirty_description = !other.description.empty();
- dirty_author = !other.author.empty();
- dirty_timestamp = other.timestamp != 0;
- thumbnail_is_local = other.thumbnail_is_local;
- title_text.reset();
- description_text.reset();
- author_text.reset();
- timestamp_text.reset();
- replies_to = other.replies_to;
- replies = other.replies;
- post_number = other.post_number;
- userdata = other.userdata;
- loaded_height = 0.0f;
- loaded_image_size = sf::Vector2f(0.0f, 0.0f);
- loaded_content_height = 0.0f;
- embedded_item_status = other.embedded_item_status;
- if(other.embedded_item) {
- embedded_item.reset(new BodyItem(""));
- *embedded_item = *other.embedded_item;
- } else {
- embedded_item.reset();
- }
- thumbnail_mask_type = other.thumbnail_mask_type;
- thumbnail_size = other.thumbnail_size;
- reactions.clear();
- for(auto &reaction : other.reactions) {
- Reaction reaction_copy;
- reaction_copy.text = std::make_unique<Text>(*reaction.text);
- reaction_copy.userdata = reaction.userdata;
- reactions.push_back(std::move(reaction_copy));
- }
- title = other.title;
- description = other.description;
- author = other.author;
- timestamp = other.timestamp;
- title_color = other.title_color;
- author_color = other.author_color;
- description_color = other.description_color;
- extra = other.extra;
- keep_alive_frames = other.keep_alive_frames;
- return *this;
- }
-
- void BodyItem::add_reaction(std::string text, void *userdata) {
- sf::String str = sf::String::fromUtf8(text.begin(), text.end());
- Reaction reaction;
- reaction.text = std::make_unique<Text>(std::move(str), false, std::floor(14 * get_ui_scale()), 0.0f);
- reaction.userdata = userdata;
- reactions.push_back(std::move(reaction));
- }
-
Body::Body(BodyTheme body_theme, sf::Texture &loading_icon_texture, sf::Shader *rounded_rectangle_shader, sf::Shader *rounded_rectangle_mask_shader) :
draw_thumbnails(true),
body_item_render_callback(nullptr),
@@ -329,6 +256,13 @@ namespace QuickMedia {
clamp_selection();
}
+ void Body::set_items(BodyItems items) {
+ for(auto &item : items) {
+ filter_search_fuzzy_item(current_filter, item.get());
+ }
+ this->items = std::move(items);
+ }
+
void Body::clear_items() {
items.clear();
selected_item = 0;
@@ -336,18 +270,53 @@ namespace QuickMedia {
page_scroll = 0.0f;
}
+ void Body::prepend_item(std::shared_ptr<BodyItem> body_item) {
+ apply_search_filter_for_item(body_item.get());
+ items.insert(items.begin(), std::move(body_item));
+ }
+
void Body::prepend_items_reverse(BodyItems new_items) {
+ for(auto &item : new_items) {
+ filter_search_fuzzy_item(current_filter, item.get());
+ }
items.insert(items.begin(), std::make_move_iterator(new_items.rbegin()), std::make_move_iterator(new_items.rend()));
- items_set_dirty();
+ }
+
+ void Body::append_item(std::shared_ptr<BodyItem> body_item) {
+ apply_search_filter_for_item(body_item.get());
+ items.push_back(std::move(body_item));
}
void Body::append_items(BodyItems new_items) {
+ for(auto &item : new_items) {
+ filter_search_fuzzy_item(current_filter, item.get());
+ }
items.insert(items.end(), std::make_move_iterator(new_items.begin()), std::make_move_iterator(new_items.end()));
- items_set_dirty();
+ }
+
+ void Body::insert_item(std::shared_ptr<BodyItem> body_item, int index) {
+ apply_search_filter_for_item(body_item.get());
+ items.insert(items.begin() + index, std::move(body_item));
+ }
+
+ void Body::move_items_to(Body *other_body) {
+ other_body->set_items(std::move(items));
+ }
+
+ void Body::move_item(size_t src_index, size_t dst_index) {
+ assert(src_index < items.size());
+ assert(dst_index < items.size());
+ auto item_to_move = std::move(items[src_index]);
+ items.erase(items.begin() + src_index);
+ if(dst_index <= src_index)
+ items.insert(items.begin() + dst_index, std::move(item_to_move));
+ else
+ items.insert(items.begin() + dst_index - 1, std::move(item_to_move));
}
// TODO: Binary search and use hint to start search from start or end (for example when adding "previous" items or "next" items)
size_t Body::insert_item_by_timestamp(std::shared_ptr<BodyItem> body_item) {
+ apply_search_filter_for_item(body_item.get());
for(size_t i = 0; i < items.size(); ++i) {
if(body_item->get_timestamp() < items[i]->get_timestamp()) {
items.insert(items.begin() + i, std::move(body_item));
@@ -369,7 +338,6 @@ namespace QuickMedia {
}
clamp_selection();
- items_set_dirty(); // TODO: Apply this now, otherwise the below code wont work
if(!selected_body_item)
return;
@@ -382,6 +350,66 @@ namespace QuickMedia {
}
}
+ void Body::for_each_item(std::function<void(std::shared_ptr<BodyItem>&)> callback) {
+ for(std::shared_ptr<BodyItem> &body_item : items) {
+ callback(body_item);
+ }
+ }
+
+ std::shared_ptr<BodyItem> Body::find_item(std::function<bool(std::shared_ptr<BodyItem>&)> callback) {
+ for(std::shared_ptr<BodyItem> &body_item : items) {
+ if(callback(body_item))
+ return body_item;
+ }
+ return nullptr;
+ }
+
+ int Body::find_item_index(std::function<bool(std::shared_ptr<BodyItem>&)> callback) {
+ for(int i = 0; i != (int)items.size(); ++i) {
+ std::shared_ptr<BodyItem> &body_item = items[i];
+ if(callback(body_item))
+ return i;
+ }
+ return -1;
+ }
+
+ bool Body::erase_item(std::function<bool(std::shared_ptr<BodyItem>&)> callback) {
+ for(auto it = items.begin(), end = items.end(); it != end; ++it) {
+ if(callback(*it)) {
+ items.erase(it);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ std::shared_ptr<BodyItem> Body::get_item_by_index(size_t index) {
+ assert(index < items.size());
+ return items[index];
+ }
+
+ BodyItemList Body::get_items() {
+ return BodyItemList(&items);
+ }
+
+ BodyItems Body::get_items_copy() {
+ return items;
+ }
+
+ void Body::copy_range(size_t start_index, size_t end_index, BodyItems &target) {
+ assert(end_index == (size_t)-1 || end_index >= start_index);
+ assert(start_index < items.size() && (end_index == (size_t)-1 || end_index < items.size()));
+ target.insert(target.end(), items.begin() + start_index, end_index == (size_t)-1 ? items.end() : (items.begin() + end_index));
+ }
+
+ size_t Body::get_num_items() const {
+ return items.size();
+ }
+
+ void Body::reverse_items() {
+ std::reverse(items.begin(), items.end());
+ }
+
void Body::clear_cache() {
clear_text_cache();
malloc_trim(0);
@@ -554,12 +582,6 @@ namespace QuickMedia {
// TODO: Use a render target for the whole body so all images can be put into one.
// TODO: Load thumbnails with more than one thread.
void Body::draw(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size, const Json::Value &content_progress) {
- if(items_dirty != DirtyState::FALSE) {
- if(using_filter || items_dirty == DirtyState::FORCE_TRUE)
- filter_search_fuzzy(current_filter);
- items_dirty = DirtyState::FALSE;
- }
-
const bool rendering_card_view = card_view && card_view_enabled;
body_size_changed = std::abs(size.x - body_size.x) > 0.1f || std::abs(size.y - body_size.y) > 0.1f;
@@ -1599,7 +1621,6 @@ namespace QuickMedia {
// TODO: Support utf-8 case insensitive find
static bool string_find_fuzzy_case_insensitive(const std::string &str, const std::string &substr) {
- if(str.empty()) return false;
if(substr.empty()) return true;
size_t str_index = 0;
@@ -1623,7 +1644,6 @@ namespace QuickMedia {
void Body::filter_search_fuzzy(const std::string &text) {
current_filter = text;
- items_dirty = DirtyState::FALSE;
if(text.empty()) {
for(auto &item : items) {
@@ -1670,10 +1690,6 @@ namespace QuickMedia {
page_scroll = scroll;
}
- void Body::items_set_dirty(bool force) {
- items_dirty = force ? DirtyState::FORCE_TRUE : DirtyState::TRUE;
- }
-
void Body::apply_search_filter_for_item(BodyItem *body_item) {
filter_search_fuzzy_item(current_filter, body_item);
}
diff --git a/src/BodyItem.cpp b/src/BodyItem.cpp
new file mode 100644
index 0000000..bf6e45d
--- /dev/null
+++ b/src/BodyItem.cpp
@@ -0,0 +1,83 @@
+#include "../include/BodyItem.hpp"
+#include "../include/Theme.hpp"
+#include "../include/Utils.hpp"
+#include <cmath>
+
+namespace QuickMedia {
+ BodyItem::BodyItem(std::string _title) :
+ visible(true),
+ dirty(false),
+ dirty_description(false),
+ dirty_author(false),
+ dirty_timestamp(false),
+ thumbnail_is_local(false),
+ userdata(nullptr),
+ timestamp(0),
+ title_color(get_current_theme().text_color),
+ author_color(get_current_theme().text_color),
+ description_color(get_current_theme().text_color)
+ {
+ if(!_title.empty())
+ set_title(std::move(_title));
+ }
+
+ BodyItem& BodyItem::operator=(const BodyItem &other) {
+ url = other.url;
+ thumbnail_url = other.thumbnail_url;
+ visible = other.visible;
+ dirty = !other.title.empty();
+ dirty_description = !other.description.empty();
+ dirty_author = !other.author.empty();
+ dirty_timestamp = other.timestamp != 0;
+ thumbnail_is_local = other.thumbnail_is_local;
+ title_text.reset();
+ description_text.reset();
+ author_text.reset();
+ timestamp_text.reset();
+ replies_to = other.replies_to;
+ replies = other.replies;
+ post_number = other.post_number;
+ userdata = other.userdata;
+ loaded_height = 0.0f;
+ loaded_image_size = sf::Vector2f(0.0f, 0.0f);
+ loaded_content_height = 0.0f;
+ embedded_item_status = other.embedded_item_status;
+ if(other.embedded_item) {
+ embedded_item.reset(new BodyItem(""));
+ *embedded_item = *other.embedded_item;
+ } else {
+ embedded_item.reset();
+ }
+ thumbnail_mask_type = other.thumbnail_mask_type;
+ thumbnail_size = other.thumbnail_size;
+ reactions.clear();
+ for(auto &reaction : other.reactions) {
+ Reaction reaction_copy;
+ reaction_copy.text = std::make_unique<Text>(*reaction.text);
+ reaction_copy.userdata = reaction.userdata;
+ reactions.push_back(std::move(reaction_copy));
+ }
+ title = other.title;
+ description = other.description;
+ author = other.author;
+ timestamp = other.timestamp;
+ title_color = other.title_color;
+ author_color = other.author_color;
+ description_color = other.description_color;
+ extra = other.extra;
+ keep_alive_frames = other.keep_alive_frames;
+ return *this;
+ }
+
+ void BodyItem::add_reaction(std::string text, void *userdata) {
+ sf::String str = sf::String::fromUtf8(text.begin(), text.end());
+ Reaction reaction;
+ reaction.text = std::make_unique<Text>(std::move(str), false, std::floor(14 * get_ui_scale()), 0.0f);
+ reaction.userdata = userdata;
+ reactions.push_back(std::move(reaction));
+ }
+
+ void BodyItem::draw_list(Body *body, sf::RenderTarget &render_target) {
+
+ }
+} \ No newline at end of file
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index a4dd182..2db5537 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -310,7 +310,7 @@ namespace QuickMedia {
bool submit_is_async() override { return false; }
- void add_option(BodyItems &items, std::string title, std::string description, OptionsPageHandler handler) {
+ void add_option(Body *body, std::string title, std::string description, OptionsPageHandler handler) {
assert(handler);
auto body_item = BodyItem::create(std::move(title));
if(!description.empty()) {
@@ -319,7 +319,7 @@ namespace QuickMedia {
}
body_item->url = std::to_string(handlers.size());
handlers.push_back(std::move(handler));
- items.push_back(std::move(body_item));
+ body->append_item(std::move(body_item));
}
private:
std::string title;
@@ -1084,22 +1084,24 @@ namespace QuickMedia {
if(strcmp(plugin_name, "launcher") == 0) {
auto pipe_body = create_body(true);
- pipe_body->items.push_back(create_launcher_body_item("4chan", "4chan", resources_root + "icons/4chan_launcher.png"));
- pipe_body->items.push_back(create_launcher_body_item("Hot Examples", "hotexamples", ""));
- pipe_body->items.push_back(create_launcher_body_item("Manga (all)", "manga", ""));
- pipe_body->items.push_back(create_launcher_body_item("Mangadex", "mangadex", resources_root + "icons/mangadex_launcher.png"));
- pipe_body->items.push_back(create_launcher_body_item("Mangakatana", "mangakatana", resources_root + "icons/mangakatana_launcher.png"));
- pipe_body->items.push_back(create_launcher_body_item("Manganelo", "manganelo", resources_root + "icons/manganelo_launcher.png"));
- pipe_body->items.push_back(create_launcher_body_item("Manganelos", "manganelos", resources_root + "icons/manganelos_launcher.png"));
- pipe_body->items.push_back(create_launcher_body_item("Mangatown", "mangatown", resources_root + "icons/mangatown_launcher.png"));
- pipe_body->items.push_back(create_launcher_body_item("Onimanga", "onimanga", ""));
- pipe_body->items.push_back(create_launcher_body_item("Readm", "readm", resources_root + "icons/readm_launcher.png"));
- pipe_body->items.push_back(create_launcher_body_item("Matrix", "matrix", resources_root + "icons/matrix_launcher.png"));
- pipe_body->items.push_back(create_launcher_body_item("Nyaa.si", "nyaa.si", resources_root + "icons/nyaa_si_launcher.png"));
- pipe_body->items.push_back(create_launcher_body_item("SauceNAO", "saucenao", ""));
- pipe_body->items.push_back(create_launcher_body_item("Soundcloud", "soundcloud", resources_root + "icons/soundcloud_launcher.png"));
- pipe_body->items.push_back(create_launcher_body_item("YouTube", "youtube", resources_root + "icons/yt_launcher.png"));
- pipe_body->items.push_back(create_launcher_body_item("YouTube (audio only)", "youtube-audio", resources_root + "icons/yt_launcher.png"));
+ pipe_body->set_items({
+ create_launcher_body_item("4chan", "4chan", resources_root + "icons/4chan_launcher.png"),
+ create_launcher_body_item("Hot Examples", "hotexamples", ""),
+ create_launcher_body_item("Manga (all)", "manga", ""),
+ create_launcher_body_item("Mangadex", "mangadex", resources_root + "icons/mangadex_launcher.png"),
+ create_launcher_body_item("Mangakatana", "mangakatana", resources_root + "icons/mangakatana_launcher.png"),
+ create_launcher_body_item("Manganelo", "manganelo", resources_root + "icons/manganelo_launcher.png"),
+ create_launcher_body_item("Manganelos", "manganelos", resources_root + "icons/manganelos_launcher.png"),
+ create_launcher_body_item("Mangatown", "mangatown", resources_root + "icons/mangatown_launcher.png"),
+ create_launcher_body_item("Onimanga", "onimanga", ""),
+ create_launcher_body_item("Readm", "readm", resources_root + "icons/readm_launcher.png"),
+ create_launcher_body_item("Matrix", "matrix", resources_root + "icons/matrix_launcher.png"),
+ create_launcher_body_item("Nyaa.si", "nyaa.si", resources_root + "icons/nyaa_si_launcher.png"),
+ create_launcher_body_item("SauceNAO", "saucenao", ""),
+ create_launcher_body_item("Soundcloud", "soundcloud", resources_root + "icons/soundcloud_launcher.png"),
+ create_launcher_body_item("YouTube", "youtube", resources_root + "icons/yt_launcher.png"),
+ create_launcher_body_item("YouTube (audio only)", "youtube-audio", resources_root + "icons/yt_launcher.png"),
+ });
tabs.push_back(Tab{std::move(pipe_body), std::make_unique<PipePage>(this, "Select plugin to launch"), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
} else if(strcmp(plugin_name, "manganelo") == 0) {
tabs.push_back(Tab{create_body(false, true), std::make_unique<ManganeloSearchPage>(this), create_search_bar("Search...", 400)});
@@ -1176,31 +1178,42 @@ namespace QuickMedia {
tabs.push_back(Tab{create_body(), std::make_unique<MangaCombinedSearchPage>(this, std::move(pages)), create_search_bar("Search...", 400)});
} else if(strcmp(plugin_name, "nyaa.si") == 0) {
auto categories_nyaa_si_body = create_body();
- get_nyaa_si_categories(categories_nyaa_si_body->items);
+ BodyItems body_items;
+ get_nyaa_si_categories(body_items);
+ categories_nyaa_si_body->set_items(std::move(body_items));
tabs.push_back(Tab{std::move(categories_nyaa_si_body), std::make_unique<NyaaSiCategoryPage>(this, false), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
auto categories_sukebei_body = create_body();
- get_sukebei_categories(categories_sukebei_body->items);
+ get_sukebei_categories(body_items);
+ categories_sukebei_body->set_items(body_items);
tabs.push_back(Tab{std::move(categories_sukebei_body), std::make_unique<NyaaSiCategoryPage>(this, true), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
} else if(strcmp(plugin_name, "4chan") == 0) {
auto boards_page = std::make_unique<FourchanBoardsPage>(this, resources_root);
auto boards_body = create_body();
- boards_page->get_boards(boards_body->items);
+ BodyItems body_items;
+ boards_page->get_boards(body_items);
+ boards_body->set_items(std::move(body_items));
tabs.push_back(Tab{std::move(boards_body), std::move(boards_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
} else if(strcmp(plugin_name, "hotexamples") == 0) {
auto body = create_body();
- hot_examples_front_page_fill(body->items);
+ BodyItems body_items;
+ hot_examples_front_page_fill(body_items);
+ body->set_items(std::move(body_items));
tabs.push_back(Tab{std::move(body), std::make_unique<HotExamplesLanguageSelectPage>(this), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
} else if(strcmp(plugin_name, "file-manager") == 0) {
auto file_manager_page = std::make_unique<FileManagerPage>(this, fm_mime_type, file_selection_handler);
if(!file_manager_page->set_current_directory(file_manager_start_dir))
fprintf(stderr, "Warning: Invalid directory provided with --dir\n");
auto file_manager_body = create_body();
- file_manager_page->get_files_in_directory(file_manager_body->items);
+ BodyItems body_items;
+ file_manager_page->get_files_in_directory(body_items);
+ file_manager_body->set_items(std::move(body_items));
tabs.push_back(Tab{std::move(file_manager_body), std::move(file_manager_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
} else if(strcmp(plugin_name, "stdin") == 0) {
auto pipe_body = create_body();
- PipePage::load_body_items_from_stdin(pipe_body->items);
+ BodyItems body_items;
+ PipePage::load_body_items_from_stdin(body_items);
+ pipe_body->set_items(std::move(body_items));
tabs.push_back(Tab{std::move(pipe_body), std::make_unique<PipePage>(this), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
} else if(strcmp(plugin_name, "youtube") == 0) {
if(youtube_url.empty()) {
@@ -1217,8 +1230,7 @@ namespace QuickMedia {
} else {
current_page = PageType::VIDEO_CONTENT;
auto youtube_video_page = std::make_unique<YoutubeVideoPage>(this, youtube_url);
- BodyItems body_items;
- video_content_page(nullptr, youtube_video_page.get(), "", false, nullptr, body_items, 0);
+ video_content_page(nullptr, youtube_video_page.get(), "", false, nullptr, 0);
}
} else if(strcmp(plugin_name, "pornhub") == 0) {
check_youtube_dl_installed(plugin_name);
@@ -1580,15 +1592,15 @@ namespace QuickMedia {
std::string image_url = body_item->url;
if(is_url_video(body_item->url))
image_url = body_item->thumbnail_url;
- body->items.push_back(InfoPage::add_reverse_image_search(image_url));
+ body->append_item(InfoPage::add_reverse_image_search(image_url));
}
std::vector<std::string> urls = ranges_get_strings(text, extract_urls(text));
for(const std::string &url : urls) {
- body->items.push_back(InfoPage::add_url(url));
+ body->append_item(InfoPage::add_url(url));
}
- if(body->items.empty())
+ if(body->get_num_items() == 0)
return false;
std::vector<Tab> info_tabs;
@@ -1841,7 +1853,7 @@ namespace QuickMedia {
page_stack.push(current_page);
current_page = PageType::VIDEO_CONTENT;
int selected_index = tabs[selected_tab].body->get_selected_item();
- video_content_page(tabs[selected_tab].page.get(), static_cast<VideoPage*>(new_tabs[0].page.get()), "", false, tabs[selected_tab].body.get(), tabs[selected_tab].body->items, selected_index, &tab_associated_data[selected_tab].fetched_page, tab_associated_data[selected_tab].update_search_text);
+ video_content_page(tabs[selected_tab].page.get(), static_cast<VideoPage*>(new_tabs[0].page.get()), "", false, tabs[selected_tab].body.get(), selected_index, &tab_associated_data[selected_tab].fetched_page, tab_associated_data[selected_tab].update_search_text);
} else if(new_tabs.size() == 1 && new_tabs[0].page->get_type() == PageTypez::CHAT) {
MatrixChatPage *tmp_matrix_chat_page = static_cast<MatrixChatPage*>(new_tabs[0].page.get());
MatrixRoomsPage *rooms_page = tmp_matrix_chat_page->rooms_page;
@@ -2111,7 +2123,7 @@ namespace QuickMedia {
continue;
if(associated_data.fetching_next_page_running && associated_data.next_page_future.ready()) {
- const bool body_was_empty = tabs[i].body->items.empty();
+ const bool body_was_empty = tabs[i].body->get_num_items() == 0;
BodyItems new_body_items = associated_data.next_page_future.get();
fprintf(stderr, "Finished fetching page %d, num new items: %zu\n", associated_data.fetched_page + 1, new_body_items.size());
int prev_selected_item = tabs[i].body->get_selected_item();
@@ -2163,18 +2175,18 @@ namespace QuickMedia {
if(associated_data.fetch_status == FetchStatus::LOADING && associated_data.fetch_type == FetchType::SEARCH && associated_data.fetch_future.ready()) {
if(!associated_data.search_text_updated) {
FetchResult fetch_result = associated_data.fetch_future.get();
- tabs[i].body->items = std::move(fetch_result.body_items);
+ tabs[i].body->set_items(std::move(fetch_result.body_items));
if(tabs[i].body->attach_side == AttachSide::TOP) {
tabs[i].body->select_first_item();
} else if(tabs[i].body->attach_side == AttachSide::BOTTOM) {
- std::reverse(tabs[i].body->items.begin(), tabs[i].body->items.end());
+ tabs[i].body->reverse_items();
tabs[i].body->select_last_item();
}
associated_data.fetched_page = 0;
associated_data.fetching_next_page_failed = false;
if(fetch_result.result != PluginResult::OK)
associated_data.search_result_text.setString("Search failed!");
- else if(tabs[i].body->items.empty())
+ else if(tabs[i].body->get_num_items() == 0)
associated_data.search_result_text.setString("No results found");
else
associated_data.search_result_text.setString("");
@@ -2188,19 +2200,19 @@ namespace QuickMedia {
if(associated_data.fetch_status == FetchStatus::LOADING && associated_data.fetch_type == FetchType::LAZY && associated_data.fetch_future.ready()) {
associated_data.lazy_fetch_finished = true;
FetchResult fetch_result = associated_data.fetch_future.get();
- tabs[i].body->items = std::move(fetch_result.body_items);
+ tabs[i].body->set_items(std::move(fetch_result.body_items));
if(tabs[i].search_bar && tabs[i].page->search_is_filter()) tabs[i].body->filter_search_fuzzy(tabs[i].search_bar->get_text());
if(tabs[i].body->attach_side == AttachSide::TOP) {
tabs[i].body->select_first_item();
}
if(tabs[i].body->attach_side == AttachSide::BOTTOM) {
- std::reverse(tabs[i].body->items.begin(), tabs[i].body->items.end());
+ tabs[i].body->reverse_items();
tabs[i].body->select_last_item();
}
LazyFetchPage *lazy_fetch_page = static_cast<LazyFetchPage*>(tabs[i].page.get());
if(fetch_result.result != PluginResult::OK)
associated_data.search_result_text.setString("Failed to fetch page!");
- else if(tabs[i].body->items.empty() && !lazy_fetch_page->lazy_fetch_is_loader())
+ else if(tabs[i].body->get_num_items() == 0 && !lazy_fetch_page->lazy_fetch_is_loader())
associated_data.search_result_text.setString("No results found");
else
associated_data.search_result_text.setString("");
@@ -2233,7 +2245,7 @@ namespace QuickMedia {
AsyncImageLoader::get_instance().update();
window.display();
- if(!tabs[selected_tab].body->items.empty()) {
+ if(tabs[selected_tab].body->get_num_items() > 0) {
if(tabs[selected_tab].body->attach_side == AttachSide::TOP && !tabs[selected_tab].body->is_bottom_cut_off())
on_reached_end();
else if(tabs[selected_tab].body->attach_side == AttachSide::BOTTOM && !tabs[selected_tab].body->is_top_cut_off())
@@ -2435,10 +2447,10 @@ namespace QuickMedia {
auto body = create_body();
auto options_page = std::make_unique<OptionsPage>(this, "Select download option");
- options_page->add_option(body->items, "Download video and audio", "", [&audio_only](){
+ options_page->add_option(body.get(), "Download video and audio", "", [&audio_only](){
audio_only = false;
});
- options_page->add_option(body->items, "Download only audio", "", [&audio_only](){
+ options_page->add_option(body.get(), "Download only audio", "", [&audio_only](){
audio_only = true;
});
@@ -2534,7 +2546,7 @@ namespace QuickMedia {
#define CLEANMASK(mask) ((mask) & (ShiftMask|ControlMask|Mod1Mask|Mod4Mask|Mod5Mask))
- void Program::video_content_page(Page *parent_page, VideoPage *video_page, std::string video_title, bool download_if_streaming_fails, Body *parent_body, BodyItems &next_play_items, int play_index, int *parent_body_page, const std::string &parent_page_search) {
+ void Program::video_content_page(Page *parent_page, VideoPage *video_page, std::string video_title, bool download_if_streaming_fails, Body *parent_body, int play_index, int *parent_body_page, const std::string &parent_page_search) {
PageType previous_page = pop_page_stack();
bool video_loaded = false;
@@ -2551,8 +2563,8 @@ namespace QuickMedia {
BodyItems related_videos;
bool move_in_parent = false;
- if(video_page->autoplay_next_item() && play_index + 1 >= 0 && play_index + 1 < (int)next_play_items.size()) {
- related_videos.insert(related_videos.end(), next_play_items.begin() + play_index + 1, next_play_items.end());
+ if(parent_body && video_page->autoplay_next_item() && play_index + 1 >= 0 && play_index + 1 < (int)parent_body->get_num_items()) {
+ parent_body->copy_range(play_index + 1, (size_t)-1, related_videos);
move_in_parent = true;
}
@@ -2951,7 +2963,7 @@ namespace QuickMedia {
}
if(related_videos_page) {
auto related_videos_body = create_body(false, true);
- related_videos_body->items = related_videos;
+ related_videos_body->set_items(related_videos);
tabs.push_back(Tab{std::move(related_videos_body), std::move(related_videos_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
}
if(channels_page) {
@@ -3068,8 +3080,8 @@ namespace QuickMedia {
fprintf(stderr, "Finished fetching page %d, num new items: %zu\n", fetch_page, new_body_items.size());
size_t num_new_messages = new_body_items.size();
if(num_new_messages > 0) {
- next_play_items.insert(next_play_items.end(), new_body_items.begin(), new_body_items.end());
- if(parent_body) parent_body->items_set_dirty();
+ if(parent_body)
+ parent_body->append_items(new_body_items);
(*parent_body_page)++;
related_videos = std::move(new_body_items);
find_next_video();
@@ -3487,7 +3499,7 @@ namespace QuickMedia {
if(image_index > 0) {
--image_index;
goto end_of_images_page;
- } else if(image_index == 0 && chapters_body->get_selected_item() < (int)chapters_body->items.size() - 1) {
+ } else if(image_index == 0 && chapters_body->get_selected_item() < (int)chapters_body->get_num_items() - 1) {
page_navigation = -1;
goto end_of_images_page;
}
@@ -3891,7 +3903,7 @@ namespace QuickMedia {
BodyItems next_items;
int prev_selected = thread_body->get_selected_item();
// TODO: Use real title
- video_content_page(thread_page, thread_page, "", true, thread_body, thread_body->items, thread_body->get_selected_item());
+ video_content_page(thread_page, thread_page, "", true, thread_body, thread_body->get_selected_item());
if(thread_body->get_selected_item() != prev_selected) {
comment_navigation_stack.clear();
comment_page_scroll_stack.clear();
@@ -3936,7 +3948,9 @@ namespace QuickMedia {
auto file_manager_page = std::make_unique<FileManagerPage>(this, (FileManagerMimeType)(FILE_MANAGER_MIME_TYPE_IMAGE|FILE_MANAGER_MIME_TYPE_VIDEO));
file_manager_page->set_current_directory(fm_dir.string());
auto file_manager_body = create_body();
- file_manager_page->get_files_in_directory(file_manager_body->items);
+ BodyItems body_items;
+ file_manager_page->get_files_in_directory(body_items);
+ file_manager_body->set_items(std::move(body_items));
std::vector<Tab> file_manager_tabs;
file_manager_tabs.push_back(Tab{std::move(file_manager_body), std::move(file_manager_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
@@ -3983,15 +3997,15 @@ namespace QuickMedia {
BodyItem *selected_item = thread_body->get_selected();
if(event.key.code == sf::Keyboard::Enter && selected_item && (comment_navigation_stack.empty() || thread_body->get_selected_item() != comment_navigation_stack.back()) && (!selected_item->replies_to.empty() || !selected_item->replies.empty())) {
- for(auto &body_item : thread_body->items) {
+ thread_body->for_each_item([](std::shared_ptr<BodyItem> &body_item) {
body_item->visible = false;
- }
+ });
selected_item->visible = true;
for(size_t reply_to_index : selected_item->replies_to) {
- thread_body->items[reply_to_index]->visible = true;
+ thread_body->get_item_by_index(reply_to_index)->visible = true;
}
for(size_t reply_index : selected_item->replies) {
- thread_body->items[reply_index]->visible = true;
+ thread_body->get_item_by_index(reply_index)->visible = true;
}
comment_navigation_stack.push_back(thread_body->get_selected_item());
comment_page_scroll_stack.push_back(thread_body->get_page_scroll());
@@ -4003,23 +4017,23 @@ namespace QuickMedia {
comment_navigation_stack.pop_back();
comment_page_scroll_stack.pop_back();
if(comment_navigation_stack.empty()) {
- for(auto &body_item : thread_body->items) {
- body_item->visible = true;
- }
+ thread_body->for_each_item([](std::shared_ptr<BodyItem> &body_item) {
+ body_item->visible = false;
+ });
thread_body->set_selected_item(previous_selected);
thread_body->clamp_selection();
} else {
- for(auto &body_item : thread_body->items) {
+ thread_body->for_each_item([](std::shared_ptr<BodyItem> &body_item) {
body_item->visible = false;
- }
+ });
thread_body->set_selected_item(previous_selected);
- selected_item = thread_body->items[comment_navigation_stack.back()].get();
+ selected_item = thread_body->get_item_by_index(comment_navigation_stack.back()).get();
selected_item->visible = true;
for(size_t reply_to_index : selected_item->replies_to) {
- thread_body->items[reply_to_index]->visible = true;
+ thread_body->get_item_by_index(reply_to_index)->visible = true;
}
for(size_t reply_index : selected_item->replies) {
- thread_body->items[reply_index]->visible = true;
+ thread_body->get_item_by_index(reply_index)->visible = true;
}
thread_body->clamp_selection();
}
@@ -4463,6 +4477,7 @@ namespace QuickMedia {
static const sf::Vector2i CHAT_MESSAGE_THUMBNAIL_MAX_SIZE(600, 337);
+ // TODO: Optimize
static std::shared_ptr<BodyItem> find_body_item_by_event_id(const std::shared_ptr<BodyItem> *body_items, size_t num_body_items, const std::string &event_id, size_t *index_result = nullptr) {
if(event_id.empty())
return nullptr;
@@ -4480,7 +4495,7 @@ namespace QuickMedia {
}
// Returns true if cached and loaded
- static bool load_cached_related_embedded_item(BodyItem *body_item, Message *message, UserInfo *me, const std::string &my_display_name, const std::string &my_user_id, const BodyItems &message_body_items) {
+ static bool load_cached_related_embedded_item(BodyItem *body_item, Message *message, UserInfo *me, const std::string &my_display_name, const std::string &my_user_id, const BodyItemList &message_body_items) {
// Check if we already have the referenced message as a body item, so we dont create a new one.
// TODO: Optimize from linear search to hash map
auto related_body_item = find_body_item_by_event_id(message_body_items.data(), message_body_items.size(), message->related_event_id);
@@ -4499,7 +4514,7 @@ namespace QuickMedia {
return true;
}
- static bool load_cached_related_embedded_item(BodyItem *body_item, Message *message, const std::shared_ptr<UserInfo> &me, RoomData *current_room, const BodyItems &message_body_items) {
+ static bool load_cached_related_embedded_item(BodyItem *body_item, Message *message, const std::shared_ptr<UserInfo> &me, RoomData *current_room, const BodyItemList &message_body_items) {
return load_cached_related_embedded_item(body_item, message, me.get(), current_room->get_user_display_name(me), me->user_id, message_body_items);
}
@@ -4545,7 +4560,7 @@ namespace QuickMedia {
return result_items;
}
- static void messages_load_cached_related_embedded_item(BodyItems &new_body_items, const BodyItems &all_body_items, const std::shared_ptr<UserInfo> &me, RoomData *current_room) {
+ static void messages_load_cached_related_embedded_item(BodyItems &new_body_items, const BodyItemList &all_body_items, const std::shared_ptr<UserInfo> &me, RoomData *current_room) {
std::string my_display_name = current_room->get_user_display_name(me);
for(auto &body_item : new_body_items) {
Message *message = static_cast<Message*>(body_item->userdata);
@@ -4806,7 +4821,7 @@ namespace QuickMedia {
auto me = matrix->get_me(current_room);
auto my_display_name = current_room->get_user_display_name(me);
- auto &body_items = tabs[MESSAGES_TAB_INDEX].body->items;
+ auto body_items = tabs[MESSAGES_TAB_INDEX].body->get_items();
for(auto &message : messages) {
// TODO: Make redacted/edited events as (redacted)/(edited) in the body
if(message->related_event_type == RelatedEventType::REDACTION || message->related_event_type == RelatedEventType::EDIT) {
@@ -4840,7 +4855,7 @@ namespace QuickMedia {
if(messages.empty())
return;
- auto &body_items = tabs[MESSAGES_TAB_INDEX].body->items;
+ auto body_items = tabs[MESSAGES_TAB_INDEX].body->get_items();
// TODO: Check in |messages| instead
for(auto it = unresolved_reactions.begin(); it != unresolved_reactions.end();) {
@@ -4882,20 +4897,19 @@ namespace QuickMedia {
};
auto pinned_body_items_contains_event = [&tabs, PINNED_TAB_INDEX](const std::string &event_id) {
- for(auto &body_item : tabs[PINNED_TAB_INDEX].body->items) {
- if(static_cast<PinnedEventData*>(body_item->userdata)->event_id == event_id)
- return true;
- }
- return false;
+ const int found_item_index = tabs[PINNED_TAB_INDEX].body->find_item_index([&event_id](std::shared_ptr<BodyItem> &body_item) {
+ return static_cast<PinnedEventData*>(body_item->userdata)->event_id == event_id;
+ });
+ return found_item_index != -1;
};
auto process_pinned_events = [&tabs, &ui_tabs, &pinned_body_items_contains_event, PINNED_TAB_INDEX](const std::optional<std::vector<std::string>> &pinned_events) {
if(!pinned_events)
return;
- bool empty_before = tabs[PINNED_TAB_INDEX].body->items.empty();
+ bool empty_before = tabs[PINNED_TAB_INDEX].body->get_num_items() == 0;
int selected_before = tabs[PINNED_TAB_INDEX].body->get_selected_item();
- auto prev_pinned_body_items = tabs[PINNED_TAB_INDEX].body->items;
+ auto prev_pinned_body_items = tabs[PINNED_TAB_INDEX].body->get_items_copy();
tabs[PINNED_TAB_INDEX].body->clear_items();
// TODO: Add message to rooms messages when there are new pinned events
@@ -4910,7 +4924,7 @@ namespace QuickMedia {
event_data->status = FetchStatus::NONE;
event_data->message = nullptr;
body->userdata = event_data;
- tabs[PINNED_TAB_INDEX].body->items.push_back(std::move(body));
+ tabs[PINNED_TAB_INDEX].body->append_item(std::move(body));
}
for(auto &prev_body_item : prev_pinned_body_items) {
@@ -4923,7 +4937,7 @@ namespace QuickMedia {
else
tabs[PINNED_TAB_INDEX].body->set_selected_item(selected_before);
- ui_tabs.set_text(PINNED_TAB_INDEX, "Pinned messages (" + std::to_string(tabs[PINNED_TAB_INDEX].body->items.size()) + ")");
+ ui_tabs.set_text(PINNED_TAB_INDEX, "Pinned messages (" + std::to_string(tabs[PINNED_TAB_INDEX].body->get_num_items()) + ")");
};
Body url_selection_body(BODY_THEME_MINIMAL, loading_icon, &rounded_rectangle_shader, &rounded_rectangle_mask_shader);
@@ -4946,7 +4960,7 @@ namespace QuickMedia {
}
auto me = matrix->get_me(current_room);
auto new_body_items = messages_to_body_items(current_room, all_messages, current_room->get_user_display_name(me), me->user_id);
- messages_load_cached_related_embedded_item(new_body_items, tabs[MESSAGES_TAB_INDEX].body->items, me, current_room);
+ messages_load_cached_related_embedded_item(new_body_items, tabs[MESSAGES_TAB_INDEX].body->get_items(), me, current_room);
tabs[MESSAGES_TAB_INDEX].body->insert_items_by_timestamps(std::move(new_body_items));
modify_related_messages_in_current_room(all_messages);
process_reactions(all_messages);
@@ -4999,7 +5013,7 @@ namespace QuickMedia {
auto message_set_replaced_by = [&tabs, &pending_sent_replies, MESSAGES_TAB_INDEX](std::shared_ptr<Message> message) {
if(message->related_event_type == RelatedEventType::EDIT) {
- auto body_item = find_body_item_by_event_id(tabs[MESSAGES_TAB_INDEX].body->items.data(), tabs[MESSAGES_TAB_INDEX].body->items.size(), message->related_event_id);
+ auto body_item = find_body_item_by_event_id(tabs[MESSAGES_TAB_INDEX].body->get_items().data(), tabs[MESSAGES_TAB_INDEX].body->get_items().size(), message->related_event_id);
if(body_item) {
Message *reply_to_message = static_cast<Message*>(body_item->userdata);
if(!reply_to_message) {
@@ -5203,7 +5217,7 @@ namespace QuickMedia {
message->type = MessageType::TEXT;
message->timestamp = time(NULL) * 1000;
- int num_items = tabs[MESSAGES_TAB_INDEX].body->items.size();
+ int num_items = tabs[MESSAGES_TAB_INDEX].body->get_num_items();
bool scroll_to_end = num_items == 0;
if(tabs[MESSAGES_TAB_INDEX].body->is_selected_item_last_visible_item() && selected_tab == MESSAGES_TAB_INDEX)
scroll_to_end = true;
@@ -5220,7 +5234,7 @@ namespace QuickMedia {
message->related_event_type = RelatedEventType::REACTION;
message->related_event_id = static_cast<Message*>(related_to_message)->event_id;
auto body_item = message_to_body_item(current_room, message.get(), current_room->get_user_avatar_url(me), me->user_id);
- load_cached_related_embedded_item(body_item.get(), message.get(), me, current_room, tabs[MESSAGES_TAB_INDEX].body->items);
+ load_cached_related_embedded_item(body_item.get(), message.get(), me, current_room, tabs[MESSAGES_TAB_INDEX].body->get_items());
tabs[MESSAGES_TAB_INDEX].body->insert_items_by_timestamps({body_item});
Messages messages;
messages.push_back(message);
@@ -5236,7 +5250,7 @@ namespace QuickMedia {
} else {
auto body_item = message_to_body_item(current_room, message.get(), current_room->get_user_avatar_url(me), me->user_id);
body_item->set_description_color(get_current_theme().provisional_message_color);
- load_cached_related_embedded_item(body_item.get(), message.get(), me, current_room, tabs[MESSAGES_TAB_INDEX].body->items);
+ load_cached_related_embedded_item(body_item.get(), message.get(), me, current_room, tabs[MESSAGES_TAB_INDEX].body->get_items());
tabs[MESSAGES_TAB_INDEX].body->insert_items_by_timestamps({body_item});
post_task_queue.push([this, &current_room, text, msgtype, body_item, message]() {
ProvisionalMessage provisional_message;
@@ -5261,7 +5275,7 @@ namespace QuickMedia {
message->related_event_id = static_cast<Message*>(related_to_message)->event_id;
auto body_item = message_to_body_item(current_room, message.get(), current_room->get_user_avatar_url(me), me->user_id);
body_item->set_description_color(get_current_theme().provisional_message_color);
- load_cached_related_embedded_item(body_item.get(), message.get(), me, current_room, tabs[MESSAGES_TAB_INDEX].body->items);
+ load_cached_related_embedded_item(body_item.get(), message.get(), me, current_room, tabs[MESSAGES_TAB_INDEX].body->get_items());
tabs[MESSAGES_TAB_INDEX].body->insert_items_by_timestamps({body_item});
post_task_queue.push([this, &current_room, text, related_to_message, body_item, message, transaction_id]() {
ProvisionalMessage provisional_message;
@@ -5282,15 +5296,15 @@ namespace QuickMedia {
message->related_event_type = RelatedEventType::EDIT;
message->related_event_id = static_cast<Message*>(related_to_message)->event_id;
size_t body_item_index = 0;
- auto body_item = find_body_item_by_event_id(tabs[MESSAGES_TAB_INDEX].body->items.data(), tabs[MESSAGES_TAB_INDEX].body->items.size(), message->related_event_id, &body_item_index);
+ auto body_item = find_body_item_by_event_id(tabs[MESSAGES_TAB_INDEX].body->get_items().data(), tabs[MESSAGES_TAB_INDEX].body->get_items().size(), message->related_event_id, &body_item_index);
if(body_item) {
- auto body_item_shared_ptr = tabs[MESSAGES_TAB_INDEX].body->items[body_item_index];
+ auto body_item_shared_ptr = tabs[MESSAGES_TAB_INDEX].body->get_item_by_index(body_item_index);
body_item_shared_ptr->set_description(text);
body_item_shared_ptr->set_description_color(get_current_theme().provisional_message_color);
auto edit_body_item = message_to_body_item(current_room, message.get(), current_room->get_user_avatar_url(me), me->user_id);
edit_body_item->visible = false;
- load_cached_related_embedded_item(edit_body_item.get(), message.get(), me, current_room, tabs[MESSAGES_TAB_INDEX].body->items);
+ load_cached_related_embedded_item(edit_body_item.get(), message.get(), me, current_room, tabs[MESSAGES_TAB_INDEX].body->get_items());
tabs[MESSAGES_TAB_INDEX].body->insert_items_by_timestamps({edit_body_item});
//unreferenced_events.push_back(message);
@@ -5349,7 +5363,7 @@ namespace QuickMedia {
if(event_data->message->related_event_id.empty() || event_data->message->related_event_type != RelatedEventType::REPLY || (body_item->embedded_item_status != FetchStatus::NONE && body_item->embedded_item_status != FetchStatus::QUEUED_LOADING))
return;
- if(load_cached_related_embedded_item(body_item.get(), event_data->message, me, current_room, tabs[MESSAGES_TAB_INDEX].body->items))
+ if(load_cached_related_embedded_item(body_item.get(), event_data->message, me, current_room, tabs[MESSAGES_TAB_INDEX].body->get_items()))
return;
std::string message_event_id = event_data->message->related_event_id;
@@ -5369,7 +5383,7 @@ namespace QuickMedia {
// Fetch embed message
// Check if we already have the referenced message as a body item in the messages list, so we dont create a new one.
// TODO: Optimize from linear search to hash map
- auto related_body_item = find_body_item_by_event_id(tabs[MESSAGES_TAB_INDEX].body->items.data(), tabs[MESSAGES_TAB_INDEX].body->items.size(), event_data->event_id);
+ auto related_body_item = find_body_item_by_event_id(tabs[MESSAGES_TAB_INDEX].body->get_items().data(), tabs[MESSAGES_TAB_INDEX].body->get_items().size(), event_data->event_id);
if(related_body_item) {
*body_item = *related_body_item;
body_item->reactions.clear();
@@ -5407,7 +5421,7 @@ namespace QuickMedia {
return;
}
- if(load_cached_related_embedded_item(body_item.get(), message, me, current_room, tabs[MESSAGES_TAB_INDEX].body->items))
+ if(load_cached_related_embedded_item(body_item.get(), message, me, current_room, tabs[MESSAGES_TAB_INDEX].body->get_items()))
return;
std::string message_event_id = message->related_event_id;
@@ -5431,7 +5445,7 @@ namespace QuickMedia {
Message *prev_message = static_cast<Message*>(prev_item->userdata);
if(is_system_message_type(prev_message->type) && is_system_message_type(message->type))
return true;
- else if(is_system_message_type(message->type))
+ else if(is_system_message_type(prev_message->type) || is_system_message_type(message->type))
return false;
if(is_visual_media_message_type(prev_message->type) && !prev_item->thumbnail_url.empty())
@@ -5455,7 +5469,7 @@ namespace QuickMedia {
auto fetch_more_previous_messages_if_needed = [this, &tabs, &current_room, &fetched_enough_messages, &previous_messages_future, MESSAGES_TAB_INDEX]() {
if(!fetched_enough_messages && !previous_messages_future.valid()) {
- if(tabs[MESSAGES_TAB_INDEX].body->items.size() < 30) {
+ if(tabs[MESSAGES_TAB_INDEX].body->get_num_items() < 30) {
previous_messages_future = AsyncTask<Messages>([this, &current_room]() {
Messages messages;
if(matrix->get_previous_room_messages(current_room, messages) != PluginResult::OK)
@@ -5495,7 +5509,7 @@ namespace QuickMedia {
current_page = PageType::VIDEO_CONTENT;
auto youtube_video_page = std::make_unique<YoutubeVideoPage>(this, url);
// TODO: Use real title
- video_content_page(matrix_chat_page, youtube_video_page.get(), "", false, nullptr, tabs[MESSAGES_TAB_INDEX].body->items, tabs[MESSAGES_TAB_INDEX].body->get_selected_item());
+ video_content_page(matrix_chat_page, youtube_video_page.get(), "", false, tabs[MESSAGES_TAB_INDEX].body.get(), tabs[MESSAGES_TAB_INDEX].body->get_selected_item());
redraw = true;
avatar_applied = false;
} else {
@@ -5531,7 +5545,7 @@ namespace QuickMedia {
return;
const int selected_tab = ui_tabs.get_selected();
- int num_items = tabs[MESSAGES_TAB_INDEX].body->items.size();
+ int num_items = tabs[MESSAGES_TAB_INDEX].body->get_num_items();
bool scroll_to_end = num_items == 0;
if(selected_tab == MESSAGES_TAB_INDEX && (tabs[MESSAGES_TAB_INDEX].body->is_selected_item_last_visible_item() || !tabs[MESSAGES_TAB_INDEX].body->get_selected()))
scroll_to_end = true;
@@ -5543,7 +5557,7 @@ namespace QuickMedia {
}
auto new_body_items = messages_to_body_items(current_room, messages, current_room->get_user_display_name(me), me->user_id);
- messages_load_cached_related_embedded_item(new_body_items, tabs[MESSAGES_TAB_INDEX].body->items, me, current_room);
+ messages_load_cached_related_embedded_item(new_body_items, tabs[MESSAGES_TAB_INDEX].body->get_items(), me, current_room);
tabs[MESSAGES_TAB_INDEX].body->insert_items_by_timestamps(std::move(new_body_items));
if(scroll_to_end)
tabs[MESSAGES_TAB_INDEX].body->select_last_item();
@@ -5572,8 +5586,7 @@ namespace QuickMedia {
bool prev_no_video = no_video;
no_video = is_audio;
video_page->set_url(selected->url);
- BodyItems next_items;
- video_content_page(matrix_chat_page, video_page.get(), selected_item_message->body, message_type == MessageType::VIDEO || message_type == MessageType::AUDIO, nullptr, next_items, 0);
+ video_content_page(matrix_chat_page, video_page.get(), selected_item_message->body, message_type == MessageType::VIDEO || message_type == MessageType::AUDIO, nullptr, 0);
no_video = prev_no_video;
redraw = true;
avatar_applied = false;
@@ -5598,7 +5611,7 @@ namespace QuickMedia {
url_selection_body.clear_items();
for(const std::string &url : urls) {
auto body_item = BodyItem::create(url);
- url_selection_body.items.push_back(std::move(body_item));
+ url_selection_body.append_item(std::move(body_item));
}
return true;
}
@@ -5630,50 +5643,46 @@ namespace QuickMedia {
auto update_pinned_messages_author = [&tabs, &current_room, PINNED_TAB_INDEX](const std::shared_ptr<UserInfo> &user) {
fprintf(stderr, "updated pinned messages author for user: %s\n", user->user_id.c_str());
- for(auto &pinned_body_item : tabs[PINNED_TAB_INDEX].body->items) {
+ tabs[PINNED_TAB_INDEX].body->for_each_item([&current_room, &user](std::shared_ptr<BodyItem> &pinned_body_item) {
Message *message = static_cast<PinnedEventData*>(pinned_body_item->userdata)->message;
// Its fine if we dont set it now. When the message is fetches, it will have updated user info since its fetched later
if(!message || message->user != user)
- continue;
-
+ return;
user_update_display_info(pinned_body_item.get(), current_room, message);
- }
+ });
};
auto update_messages_author = [&tabs, &current_room, MESSAGES_TAB_INDEX](const std::shared_ptr<UserInfo> &user) {
fprintf(stderr, "updated messages author for user: %s\n", user->user_id.c_str());
- for(auto &message_body_items : tabs[MESSAGES_TAB_INDEX].body->items) {
+ tabs[MESSAGES_TAB_INDEX].body->for_each_item([&current_room, &user](std::shared_ptr<BodyItem> &message_body_items) {
Message *message = static_cast<Message*>(message_body_items->userdata);
if(!message || message->user != user)
- continue;
-
+ return;
user_update_display_info(message_body_items.get(), current_room, message);
- }
+ });
};
// TODO: Optimize
auto update_pinned_messages_authors = [&tabs, &current_room, PINNED_TAB_INDEX]() {
fprintf(stderr, "updated pinned messages author for all users in room: %s\n", current_room->id.c_str());
- for(auto &pinned_body_item : tabs[PINNED_TAB_INDEX].body->items) {
+ tabs[PINNED_TAB_INDEX].body->for_each_item([&current_room](std::shared_ptr<BodyItem> &pinned_body_item) {
Message *message = static_cast<PinnedEventData*>(pinned_body_item->userdata)->message;
// Its fine if we dont set it now. When the message is fetches, it will have updated user info since its fetched later
if(!message)
- continue;
-
+ return;
user_update_display_info(pinned_body_item.get(), current_room, message);
- }
+ });
};
// TODO: Optimize
auto update_messages_authors = [&tabs, &current_room, MESSAGES_TAB_INDEX]() {
fprintf(stderr, "updated messages author for all users in room: %s\n", current_room->id.c_str());
- for(auto &message_body_items : tabs[MESSAGES_TAB_INDEX].body->items) {
+ tabs[MESSAGES_TAB_INDEX].body->for_each_item([&current_room](std::shared_ptr<BodyItem> &message_body_items) {
Message *message = static_cast<Message*>(message_body_items->userdata);
if(!message)
- continue;
-
+ return;
user_update_display_info(message_body_items.get(), current_room, message);
- }
+ });
};
auto cleanup_tasks = [&set_read_marker_future, &fetch_message_future, &fetch_users_future, &typing_state_queue, &typing_state_thread, &post_task_queue, &provisional_message_queue, &fetched_messages_set, &sent_messages, &pending_sent_replies, &post_thread, &tabs, MESSAGES_TAB_INDEX, PINNED_TAB_INDEX, USERS_TAB_INDEX]() {
@@ -5699,9 +5708,10 @@ namespace QuickMedia {
if(!tabs.empty()) {
tabs[MESSAGES_TAB_INDEX].body->clear_items();
- for(auto &body_item : tabs[PINNED_TAB_INDEX].body->items) {
- delete (PinnedEventData*)body_item->userdata;
- }
+ tabs[PINNED_TAB_INDEX].body->for_each_item([](std::shared_ptr<BodyItem> &pinned_body_item) {
+ delete (PinnedEventData*)pinned_body_item->userdata;
+ pinned_body_item->userdata = nullptr;
+ });
tabs[PINNED_TAB_INDEX].body->clear_items();
tabs[USERS_TAB_INDEX].body->clear_items();
}
@@ -5824,6 +5834,8 @@ namespace QuickMedia {
goto chat_page_end;
} else if(event.key.code == sf::Keyboard::I && event.key.control) {
BodyItem *selected_item = tabs[selected_tab].body->get_selected();
+ if(selected_item && selected_item->url.empty())
+ selected_item = selected_item->embedded_item.get();
if(selected_item && !selected_item->url.empty() && !selected_item->thumbnail_url.empty()) {
Message *selected_item_message = nullptr;
if(selected_tab == MESSAGES_TAB_INDEX) {
@@ -6051,7 +6063,9 @@ namespace QuickMedia {
auto file_manager_page = std::make_unique<FileManagerPage>(this);
file_manager_page->set_current_directory(fm_dir.string());
auto file_manager_body = create_body();
- file_manager_page->get_files_in_directory(file_manager_body->items);
+ BodyItems body_items;
+ file_manager_page->get_files_in_directory(body_items);
+ file_manager_body->set_items(std::move(body_items));
std::vector<Tab> file_manager_tabs;
file_manager_tabs.push_back(Tab{std::move(file_manager_body), std::move(file_manager_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
@@ -6226,7 +6240,7 @@ namespace QuickMedia {
modify_related_messages_in_current_room(new_messages);
process_reactions(new_messages);
// TODO: Do not loop all items, only loop the new items
- resolve_unreferenced_events_with_body_items(tabs[MESSAGES_TAB_INDEX].body->items.data(), tabs[MESSAGES_TAB_INDEX].body->items.size());
+ resolve_unreferenced_events_with_body_items(tabs[MESSAGES_TAB_INDEX].body->get_items().data(), tabs[MESSAGES_TAB_INDEX].body->get_items().size());
}
if(num_new_messages > 0 && current_room->initial_prev_messages_fetch) {
current_room->initial_prev_messages_fetch = false;
@@ -6398,7 +6412,7 @@ namespace QuickMedia {
if(selected_tab == MESSAGES_TAB_INDEX && current_room && current_room->body_item && !current_room->last_message_read && matrix->is_initial_sync_finished()) {
if(!tabs[selected_tab].body->is_bottom_cut_off() && is_window_focused && chat_state != ChatState::URL_SELECTION && !setting_read_marker && read_marker_timer.getElapsedTime().asMilliseconds() >= read_marker_timeout_ms) {
- auto &body_items = tabs[selected_tab].body->items;
+ auto body_items = tabs[selected_tab].body->get_items();
int last_timeline_message = (int)body_items.size() - 1;
for(int i = last_timeline_message - 1; i >= 0; --i) {
BodyItem *item = body_items[i].get();
@@ -6574,22 +6588,24 @@ namespace QuickMedia {
auto invites_body = create_body();
auto matrix_invites_page = std::make_unique<MatrixInvitesPage>(this, matrix, invites_body.get());
+ BodyItems room_dir_body_items;
+ add_body_item_unique_title(room_dir_body_items, matrix->get_homeserver_domain());
+ add_body_item_unique_title(room_dir_body_items, "midov.pl");
+ add_body_item_unique_title(room_dir_body_items, "matrix.org");
+ add_body_item_unique_title(room_dir_body_items, "kde.org");
+ add_body_item_unique_title(room_dir_body_items, "librem.one");
+ add_body_item_unique_title(room_dir_body_items, "maunium.net");
+ add_body_item_unique_title(room_dir_body_items, "halogen.city");
+ add_body_item_unique_title(room_dir_body_items, "gnome.org");
+ add_body_item_unique_title(room_dir_body_items, "shivering-isles.com");
+ add_body_item_unique_title(room_dir_body_items, "nerdsin.space");
+ add_body_item_unique_title(room_dir_body_items, "glowers.club");
+ add_body_item_unique_title(room_dir_body_items, "privacytools.io");
+ add_body_item_unique_title(room_dir_body_items, "linuxdelta.com");
+ add_body_item_unique_title(room_dir_body_items, "tchncs.de");
+ add_body_item_unique_title(room_dir_body_items, "jupiterbroadcasting.com");
auto room_directory_body = create_body();
- add_body_item_unique_title(room_directory_body->items, matrix->get_homeserver_domain());
- add_body_item_unique_title(room_directory_body->items, "midov.pl");
- add_body_item_unique_title(room_directory_body->items, "matrix.org");
- add_body_item_unique_title(room_directory_body->items, "kde.org");
- add_body_item_unique_title(room_directory_body->items, "librem.one");
- add_body_item_unique_title(room_directory_body->items, "maunium.net");
- add_body_item_unique_title(room_directory_body->items, "halogen.city");
- add_body_item_unique_title(room_directory_body->items, "gnome.org");
- add_body_item_unique_title(room_directory_body->items, "shivering-isles.com");
- add_body_item_unique_title(room_directory_body->items, "nerdsin.space");
- add_body_item_unique_title(room_directory_body->items, "glowers.club");
- add_body_item_unique_title(room_directory_body->items, "privacytools.io");
- add_body_item_unique_title(room_directory_body->items, "linuxdelta.com");
- add_body_item_unique_title(room_directory_body->items, "tchncs.de");
- add_body_item_unique_title(room_directory_body->items, "jupiterbroadcasting.com");
+ room_directory_body->set_items(std::move(room_dir_body_items));
auto matrix_room_directory_page = std::make_unique<MatrixRoomDirectoryPage>(this, matrix);
MatrixQuickMedia matrix_handler(this, matrix, matrix_rooms_page.get(), matrix_rooms_tag_page.get(), matrix_invites_page.get(), matrix_notifications_page.get());
@@ -6767,6 +6783,7 @@ namespace QuickMedia {
return;
}
+ string_replace_all(filename, '/', '_');
std::string output_filepath = file_save_page(filename);
if(!window.isOpen() || output_filepath.empty()) {
exit_code = 1;
@@ -6959,7 +6976,9 @@ namespace QuickMedia {
auto file_manager_page = std::make_unique<FileManagerPage>(this);
file_manager_page->set_current_directory(file_manager_start_dir);
auto file_manager_body = create_body();
- file_manager_page->get_files_in_directory(file_manager_body->items);
+ BodyItems body_items;
+ file_manager_page->get_files_in_directory(body_items);
+ file_manager_body->set_items(std::move(body_items));
auto search_bar = create_search_bar("Search...", SEARCH_DELAY_FILTER);
Tabs ui_tabs(&rounded_rectangle_shader);
@@ -6985,7 +7004,7 @@ namespace QuickMedia {
if(task_result == TaskResult::TRUE) {
if(!new_tabs.empty()) {
- file_manager_body->items = std::move(new_tabs[0].body->items);
+ new_tabs[0].body->move_items_to(file_manager_body.get());
file_manager_body->select_first_item();
search_bar->clear();
}
@@ -7038,10 +7057,10 @@ namespace QuickMedia {
auto body = create_body();
auto options_page = std::make_unique<OptionsPage>(this, "Are you sure you want to overwrite " + filename_full_path.data + "?");
- options_page->add_option(body->items, "No", "", [&overwrite](){
+ options_page->add_option(body.get(), "No", "", [&overwrite](){
overwrite = false;
});
- options_page->add_option(body->items, "Yes", "", [&overwrite](){
+ options_page->add_option(body.get(), "Yes", "", [&overwrite](){
overwrite = true;
});
diff --git a/src/Storage.cpp b/src/Storage.cpp
index 50d1ffd..9a1c492 100644
--- a/src/Storage.cpp
+++ b/src/Storage.cpp
@@ -309,7 +309,8 @@ namespace QuickMedia {
char *env = getenv("PATH");
std::unordered_set<std::string> paths;
string_split(env, ':', [&paths](const char *str, size_t size) {
- paths.insert(std::string(str, size));
+ if(size > 0)
+ paths.insert(std::string(str, size));
return true;
});
diff --git a/src/plugins/FileManager.cpp b/src/plugins/FileManager.cpp
index f78265b..295b962 100644
--- a/src/plugins/FileManager.cpp
+++ b/src/plugins/FileManager.cpp
@@ -52,7 +52,7 @@ namespace QuickMedia {
set_last_accessed_dir(current_dir);
auto body = create_body();
- body->items = std::move(result_items);
+ body->set_items(std::move(result_items));
result_tabs.push_back(Tab{std::move(body), nullptr, nullptr});
return PluginResult::OK;
}
diff --git a/src/plugins/Fourchan.cpp b/src/plugins/Fourchan.cpp
index c1d7d6a..f7c9910 100644
--- a/src/plugins/Fourchan.cpp
+++ b/src/plugins/Fourchan.cpp
@@ -365,7 +365,7 @@ namespace QuickMedia {
}
auto body = create_body(false);
- body->items = std::move(result_items);
+ body->set_items(std::move(result_items));
result_tabs.push_back(Tab{std::move(body), std::make_unique<FourchanThreadPage>(program, board_id, url), nullptr});
return PluginResult::OK;
}
diff --git a/src/plugins/HotExamples.cpp b/src/plugins/HotExamples.cpp
index 02f1217..29e0110 100644
--- a/src/plugins/HotExamples.cpp
+++ b/src/plugins/HotExamples.cpp
@@ -132,7 +132,7 @@ namespace QuickMedia {
quickmedia_html_search_deinit(&html_search);
auto body = create_body();
- body->items = std::move(result_items);
+ body->set_items(std::move(result_items));
result_tabs.push_back({ std::move(body), std::make_unique<HotExamplesCodeExamplesPage>(program, title + " code examples"), create_search_bar("Search...", SEARCH_DELAY_FILTER) });
return PluginResult::OK;
}
diff --git a/src/plugins/MangaGeneric.cpp b/src/plugins/MangaGeneric.cpp
index 4668970..90da0c2 100644
--- a/src/plugins/MangaGeneric.cpp
+++ b/src/plugins/MangaGeneric.cpp
@@ -368,7 +368,7 @@ namespace QuickMedia {
return PluginResult::ERR;
auto body = create_body();
- body->items = std::move(chapters_items);
+ body->set_items(std::move(chapters_items));
result_tabs.push_back(Tab{std::move(body), std::make_unique<MangaGenericChaptersPage>(program, title, url, manga_id_extractor, service_name, website_url, &list_page_query, fail_on_http_error), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
for(auto &it : creators) {
diff --git a/src/plugins/Mangadex.cpp b/src/plugins/Mangadex.cpp
index 477608e..98683c1 100644
--- a/src/plugins/Mangadex.cpp
+++ b/src/plugins/Mangadex.cpp
@@ -307,7 +307,9 @@ namespace QuickMedia {
PluginResult MangadexSearchPage::submit(const std::string &title, const std::string &url, std::vector<Tab> &result_tabs) {
chapter_image_urls.clear();
auto body = create_body();
- get_chapters_for_manga(this, url, 0, body->items, chapter_image_urls);
+ BodyItems body_items;
+ get_chapters_for_manga(this, url, 0, body_items, chapter_image_urls);
+ body->set_items(std::move(body_items));
result_tabs.push_back(Tab{std::move(body), std::make_unique<MangadexChaptersPage>(program, this, title, url), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
return PluginResult::OK;
}
diff --git a/src/plugins/Manganelo.cpp b/src/plugins/Manganelo.cpp
index e0517dd..d3d7bfa 100644
--- a/src/plugins/Manganelo.cpp
+++ b/src/plugins/Manganelo.cpp
@@ -93,7 +93,7 @@ namespace QuickMedia {
return PluginResult::ERR;
auto chapters_body = page->create_body();
- chapters_body->items = std::move(chapters_items);
+ chapters_body->set_items(std::move(chapters_items));
result_tabs.push_back(Tab{std::move(chapters_body), std::make_unique<ManganeloChaptersPage>(page->program, title, url), page->create_search_bar("Search...", SEARCH_DELAY_FILTER)});
// TODO: Fix. Doesn't work because manganelo changes creator url format
diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp
index 18a76eb..f072a27 100644
--- a/src/plugins/Matrix.cpp
+++ b/src/plugins/Matrix.cpp
@@ -506,26 +506,26 @@ namespace QuickMedia {
});
}
- static void insert_room_body_item_by_timestamp(BodyItems &body_items, std::shared_ptr<BodyItem> new_body_item) {
+ static void insert_room_body_item_by_timestamp(Body *body, std::shared_ptr<BodyItem> new_body_item) {
RoomData *new_room = static_cast<RoomData*>(new_body_item->userdata);
- for(auto it = body_items.begin(), end = body_items.end(); it != end; ++it) {
- RoomData *room = static_cast<RoomData*>((*it)->userdata);
- if(new_room->last_message_timestamp >= room->last_message_timestamp) {
- body_items.insert(it, std::move(new_body_item));
- return;
- }
- }
- body_items.push_back(std::move(new_body_item));
+ const int insert_index = body->find_item_index([new_room](std::shared_ptr<BodyItem> &body_item) {
+ RoomData *room = static_cast<RoomData*>(body_item->userdata);
+ return new_room->last_message_timestamp >= room->last_message_timestamp;
+ });
+
+ if(insert_index == -1)
+ body->append_item(std::move(new_body_item));
+ else
+ body->insert_item(std::move(new_body_item), insert_index);
}
+ // TODO: Optimize
void body_set_selected_item_by_url(Body *body, const std::string &url) {
- for(size_t i = 0; i < body->items.size(); ++i) {
- if(body->items[i]->url == url) {
- body->select_first_item();
- body->set_selected_item(i, false);
- return;
- }
- }
+ const int found_item_index = body->find_item_index([&url](std::shared_ptr<BodyItem> &body_item) {
+ return body_item->url == url;
+ });
+ if(found_item_index != -1)
+ body->set_selected_item(found_item_index, false);
}
void MatrixQuickMedia::clear_data() {
@@ -658,12 +658,8 @@ namespace QuickMedia {
return PluginResult::OK;
}
- void MatrixRoomsPage::on_navigate_to_page(Body *body) {
- body->items_set_dirty(true);
- }
-
void MatrixRoomsPage::add_body_item(std::shared_ptr<BodyItem> body_item) {
- insert_room_body_item_by_timestamp(body->items, body_item);
+ insert_room_body_item_by_timestamp(body, body_item);
}
void MatrixRoomsPage::move_room_to_top(RoomData *room) {
@@ -678,28 +674,25 @@ namespace QuickMedia {
if(room_body_index == selected_item)
return;
- for(size_t i = 0; i < body->items.size(); ++i) {
- RoomData *room_i = static_cast<RoomData*>(body->items[i]->userdata);
+ for(size_t i = 0; i < body->get_num_items(); ++i) {
+ RoomData *room_i = static_cast<RoomData*>(body->get_item_by_index(i)->userdata);
if((int)i == room_body_index)
return;
if((int)i != selected_item && room_i && room->last_message_timestamp >= room_i->last_message_timestamp) {
- auto body_item_to_insert = body->items[room_body_index];
- body->items.erase(body->items.begin() + room_body_index);
- if(room_body_index >= (int)i)
- body->items.insert(body->items.begin() + i, std::move(body_item_to_insert));
- else
- body->items.insert(body->items.begin() + (i - 1), std::move(body_item_to_insert));
- if((int)i < selected_item && room_body_index > selected_item && body->items.size() > 1 && i != body->items.size() - 1) {
+ body->move_item(room_body_index, i);
+ if((int)i < selected_item && room_body_index > selected_item && body->get_num_items() > 1 && i != body->get_num_items() - 1)
body->select_next_item();
- }
+
return;
}
}
}
void MatrixRoomsPage::remove_body_item_by_room_id(const std::string &room_id) {
- remove_body_item_by_url(body->items, room_id);
+ body->erase_item([&room_id](std::shared_ptr<BodyItem> &body_item) {
+ return body_item->url == room_id;
+ });
if(current_chat_page && current_chat_page->room_id == room_id) {
program->set_go_to_previous_page();
body->select_first_item();
@@ -731,9 +724,10 @@ namespace QuickMedia {
auto body = create_body(true);
Body *body_ptr = body.get();
TagData &tag_data = tag_body_items_by_name[url];
- body->items = tag_data.room_body_items;
+ BodyItems room_body_items = tag_data.room_body_items;
+ sort_room_body_items(room_body_items);
+ body->set_items(std::move(room_body_items));
//BodyItem *selected_item = body->get_selected();
- sort_room_body_items(body->items);
//body_set_selected_item(body.get(), selected_item);
auto search_bar = create_search_bar("Search...", SEARCH_DELAY_FILTER);
auto rooms_page = std::make_unique<MatrixRoomsPage>(program, body_ptr, tag_data.tag_item->get_title(), this, search_bar.get());
@@ -742,10 +736,6 @@ namespace QuickMedia {
return PluginResult::OK;
}
- void MatrixRoomTagsPage::on_navigate_to_page(Body *body) {
- body->items_set_dirty(true);
- }
-
void MatrixRoomTagsPage::add_room_body_item_to_tag(std::shared_ptr<BodyItem> body_item, const std::string &tag) {
TagData *tag_data;
auto tag_body_it = tag_body_items_by_name.find(tag);
@@ -759,7 +749,7 @@ namespace QuickMedia {
tag_body_items_by_name.insert(std::make_pair(tag, TagData{tag_body_item, {}}));
// TODO: Sort by tag priority
body->apply_search_filter_for_item(tag_body_item.get());
- body->items.push_back(tag_body_item);
+ body->append_item(tag_body_item);
tag_data = &tag_body_items_by_name[tag];
tag_data->tag_item = tag_body_item;
}
@@ -789,9 +779,10 @@ namespace QuickMedia {
tag_body_it->second.room_body_items.erase(room_body_item_it);
if(tag_body_it->second.room_body_items.empty()) {
- auto room_body_item_it = std::find(body->items.begin(), body->items.end(), tag_body_it->second.tag_item);
- if(room_body_item_it != body->items.end())
- body->items.erase(room_body_item_it);
+ const auto &tag_item = tag_body_it->second.tag_item;
+ body->erase_item([&tag_item](std::shared_ptr<BodyItem> &body_item) {
+ return body_item == tag_item;
+ });
tag_body_items_by_name.erase(tag_body_it);
}
}
@@ -831,8 +822,8 @@ namespace QuickMedia {
PluginResult MatrixInvitesPage::submit(const std::string &title, const std::string &url, std::vector<Tab> &result_tabs) {
auto body = create_body();
- body->items.push_back(BodyItem::create("Accept"));
- body->items.push_back(BodyItem::create("Decline"));
+ body->append_item(BodyItem::create("Accept"));
+ body->append_item(BodyItem::create("Decline"));
result_tabs.push_back(Tab{std::move(body), std::make_unique<MatrixInviteDetailsPage>(program, matrix, this, url, "Invite to " + title), nullptr});
return PluginResult::OK;
}
@@ -864,16 +855,19 @@ namespace QuickMedia {
// TODO: Insert in reverse order (to show the latest invite at the top?)
body->apply_search_filter_for_item(body_item.get());
body->insert_item_by_timestamp(std::move(body_item));
- if(body->items.size() != prev_invite_count) {
- prev_invite_count = body->items.size();
- title = "Invites (" + std::to_string(body->items.size()) + ")";
+ if(body->get_num_items() != prev_invite_count) {
+ prev_invite_count = body->get_num_items();
+ title = "Invites (" + std::to_string(body->get_num_items()) + ")";
}
}
void MatrixInvitesPage::remove_body_item_by_room_id(const std::string &room_id) {
- if(remove_body_item_by_url(body->items, room_id)) {
- prev_invite_count = body->items.size();
- title = "Invites (" + std::to_string(body->items.size()) + ")";
+ const bool item_removed = body->erase_item([&room_id](std::shared_ptr<BodyItem> &body_item) {
+ return body_item->url == room_id;
+ });
+ if(item_removed) {
+ prev_invite_count = body->get_num_items();
+ title = "Invites (" + std::to_string(body->get_num_items()) + ")";
}
}
@@ -908,8 +902,7 @@ namespace QuickMedia {
body_item->thumbnail_mask_type = ThumbnailMaskType::CIRCLE;
body_item->thumbnail_size = sf::Vector2i(32, 32);
- users_body->apply_search_filter_for_item(body_item.get());
- users_body->items.push_back(std::move(body_item));
+ users_body->append_item(std::move(body_item));
}
void MatrixChatPage::add_user(MatrixEventUserInfo user_info) {
@@ -924,12 +917,9 @@ namespace QuickMedia {
return;
// TODO: Optimize
- for(auto it = users_body->items.begin(), end = users_body->items.end(); it != end; ++it) {
- if((*it)->url == user_info.user_id) {
- users_body->items.erase(it);
- return;
- }
- }
+ users_body->erase_item([&user_info](std::shared_ptr<BodyItem> &it) {
+ return it->url == user_info.user_id;
+ });
}
void MatrixChatPage::set_user_info(MatrixEventUserInfo user_info) {
@@ -937,32 +927,25 @@ namespace QuickMedia {
return;
// TODO: Optimize
- for(auto it = users_body->items.begin(), end = users_body->items.end(); it != end; ++it) {
- if((*it)->url == user_info.user_id) {
- if(user_info.avatar_url)
- (*it)->thumbnail_url = user_info.avatar_url.value();
-
- if(user_info.display_name) {
- std::string display_name;
- if(user_info.display_name.value().empty())
- display_name = user_info.user_id;
- else
- display_name = user_info.display_name.value();
-
- (*it)->set_author(extract_first_line_remove_newline_elipses(display_name, AUTHOR_MAX_LENGTH));
-
- //auto user_body_item = *it;
- //users_body->items.erase(it);
-
- // TODO: extract_first_line_remove_newline_elipses(room->get_user_display_name(message->user), AUTHOR_MAX_LENGTH),
- // But that should be done in Text because we need author to be 100% the same as in the input to reorder users
- users_body->apply_search_filter_for_item(it->get());
- //size_t insert_index = get_body_item_sorted_insert_position_by_author(users_body->items, user_body_item->get_author(), 0);
- //users_body->items.insert(users_body->items.begin() + insert_index, std::move(user_body_item));
- }
+ auto user_body_item = users_body->find_item([&user_info](std::shared_ptr<BodyItem> &it) {
+ return it->url == user_info.user_id;
+ });
- return;
- }
+ if(!user_body_item)
+ return;
+
+ if(user_info.avatar_url)
+ user_body_item->thumbnail_url = user_info.avatar_url.value();
+
+ if(user_info.display_name) {
+ const std::string *display_name;
+ if(user_info.display_name.value().empty())
+ display_name = &user_info.user_id;
+ else
+ display_name = &user_info.display_name.value();
+
+ user_body_item->set_author(extract_first_line_remove_newline_elipses(*display_name, AUTHOR_MAX_LENGTH));
+ users_body->apply_search_filter_for_item(user_body_item.get());
}
}
@@ -978,7 +961,7 @@ namespace QuickMedia {
}
size_t MatrixChatPage::get_num_users_in_current_room() const {
- return users_body ? users_body->items.size() : 0;
+ return users_body ? users_body->get_num_items() : 0;
}
void MatrixChatPage::set_room_as_read(RoomData *room) {
@@ -1095,19 +1078,20 @@ namespace QuickMedia {
//int prev_selected_item = notifications_body->get_selected_item();
//notifications_body->items.push_back(notification_to_body_item(notifications_body, notification));
//notifications_body->set_selected_item(prev_selected_item - 1);
- notifications_body->items.insert(notifications_body->items.begin(), notification_to_body_item(notifications_body, notification));
+ notifications_body->prepend_item(notification_to_body_item(notifications_body, notification));
notifications_body->select_next_item();
}
+ // TODO: Only loop unread items
void MatrixNotificationsPage::set_room_as_read(RoomData *room) {
- for(auto &body_item : notifications_body->items) {
+ notifications_body->for_each_item([room](std::shared_ptr<BodyItem> &body_item) {
NotificationsExtraData *extra_data = static_cast<NotificationsExtraData*>(body_item->extra.get());
if(!extra_data->read && extra_data->room == room) {
extra_data->read = true;
body_item->set_author_color(get_current_theme().text_color);
body_item->set_description_color(get_current_theme().text_color);
}
- }
+ });
}
SearchResult MatrixInviteUserPage::search(const std::string &str, BodyItems &result_items) {
@@ -3036,8 +3020,12 @@ namespace QuickMedia {
bool is_inside_code_block = false;
bool is_first_line = true;
string_split(body, '\n', [this, room, &formatted_body, &is_inside_code_block, &is_first_line](const char *str, size_t size){
- if(!is_first_line)
- formatted_body += "<br/>";
+ if(!is_first_line) {
+ if(is_inside_code_block)
+ formatted_body += '\n';
+ else
+ formatted_body += "<br/>";
+ }
std::string line_str(str, size);
html_escape_sequences(line_str);
diff --git a/src/plugins/NyaaSi.cpp b/src/plugins/NyaaSi.cpp
index ccf027d..8e55e62 100644
--- a/src/plugins/NyaaSi.cpp
+++ b/src/plugins/NyaaSi.cpp
@@ -134,12 +134,12 @@ namespace QuickMedia {
return body_item;
}
- static void sort_page_create_body_items(BodyItems &body_items, NyaaSiSortType sort_type) {
+ static void sort_page_create_body_items(Body *body, NyaaSiSortType sort_type) {
for(size_t i = 0; i < sort_type_names.size(); ++i) {
std::string prefix = " ";
if((NyaaSiSortType)i == sort_type)
prefix = "* ";
- body_items.push_back(create_sort_body_item(prefix + sort_type_names[i], (NyaaSiSortType)i));
+ body->append_item(create_sort_body_item(prefix + sort_type_names[i], (NyaaSiSortType)i));
}
}
@@ -294,12 +294,12 @@ namespace QuickMedia {
auto search_page = std::make_unique<NyaaSiSearchPage>(program, strip(title), url, std::move(domain));
NyaaSiSearchPage *search_page_p = search_page.get();
auto body = create_body();
- body->items = std::move(result_items);
+ body->set_items(std::move(result_items));
result_tabs.push_back(Tab{std::move(body), std::move(search_page), create_search_bar("Search...", 500)});
auto sort_order_page_body = create_body();
Body *sort_order_page_body_p = sort_order_page_body.get();
- sort_page_create_body_items(sort_order_page_body->items, NyaaSiSortType::UPLOAD_DATE_DESC);
+ sort_page_create_body_items(sort_order_page_body_p, NyaaSiSortType::UPLOAD_DATE_DESC);
result_tabs.push_back(Tab{std::move(sort_order_page_body), std::make_unique<NyaaSiSortOrderPage>(program, sort_order_page_body_p, search_page_p), nullptr});
return PluginResult::OK;
}
@@ -486,7 +486,7 @@ namespace QuickMedia {
return PluginResult::ERR;
auto body = create_body();
- body->items = std::move(result_items);
+ body->set_items(std::move(result_items));
result_tabs.push_back(Tab{std::move(body), std::make_unique<NyaaSiTorrentPage>(program), nullptr});
return PluginResult::OK;
}
@@ -499,8 +499,8 @@ namespace QuickMedia {
PluginResult NyaaSiSortOrderPage::submit(const std::string&, const std::string&, std::vector<Tab>&) {
const NyaaSiSortType sort_type = (NyaaSiSortType)(size_t)submit_body_item->userdata;
- body->items.clear();
- sort_page_create_body_items(body->items, sort_type);
+ body->clear_items();
+ sort_page_create_body_items(body, sort_type);
search_page->set_sort_type(sort_type);
search_page->needs_refresh = true;
return PluginResult::OK;
diff --git a/src/plugins/Soundcloud.cpp b/src/plugins/Soundcloud.cpp
index 7079e46..f20cf7d 100644
--- a/src/plugins/Soundcloud.cpp
+++ b/src/plugins/Soundcloud.cpp
@@ -247,7 +247,7 @@ namespace QuickMedia {
if(url == "track") {
SoundcloudPlaylist *playlist = static_cast<SoundcloudPlaylist*>(submit_body_item->extra.get());
auto body = create_body(false, true);
- body->items = playlist->tracks;
+ body->set_items(playlist->tracks);
result_tabs.push_back(Tab{std::move(body), std::make_unique<SoundcloudPlaylistPage>(program, playlist, title), nullptr});
} else if(url.find("/stream/users/") != std::string::npos) {
std::string query_url = url + "?client_id=" + client_id + "&limit=20&offset=0&linked_partitioning=1&app_version=1616689516&app_locale=en";
@@ -258,7 +258,9 @@ namespace QuickMedia {
auto body = create_body(false, true);
std::string next_href;
- PluginResult pr = parse_user_page(json_root, body->items, next_href);
+ BodyItems body_items;
+ PluginResult pr = parse_user_page(json_root, body_items, next_href);
+ body->set_items(std::move(body_items));
if(pr != PluginResult::OK) return pr;
result_tabs.push_back(Tab{std::move(body), std::make_unique<SoundcloudUserPage>(program, title, url, std::move(next_href)), nullptr});