diff options
author | dec05eba <dec05eba@protonmail.com> | 2021-08-03 15:06:57 +0200 |
---|---|---|
committer | dec05eba <dec05eba@protonmail.com> | 2021-08-05 05:50:23 +0200 |
commit | 6c85194c3b1baef0eaa011c4f1b8e48e11860f45 (patch) | |
tree | 61f3eb4304091fd2203519702a4f4daf184c5a59 /src | |
parent | cbc6997c0a5659239d2cd971f2fa77eeda53550b (diff) |
Make body items private, add accessor functions
This allows body to automatically update dirty state (and other states).
Correctly format newlines in codeblocks in matrix.
Diffstat (limited to 'src')
-rw-r--r-- | src/Body.cpp | 192 | ||||
-rw-r--r-- | src/BodyItem.cpp | 83 | ||||
-rw-r--r-- | src/QuickMedia.cpp | 291 | ||||
-rw-r--r-- | src/Storage.cpp | 3 | ||||
-rw-r--r-- | src/plugins/FileManager.cpp | 2 | ||||
-rw-r--r-- | src/plugins/Fourchan.cpp | 2 | ||||
-rw-r--r-- | src/plugins/HotExamples.cpp | 2 | ||||
-rw-r--r-- | src/plugins/MangaGeneric.cpp | 2 | ||||
-rw-r--r-- | src/plugins/Mangadex.cpp | 4 | ||||
-rw-r--r-- | src/plugins/Manganelo.cpp | 2 | ||||
-rw-r--r-- | src/plugins/Matrix.cpp | 166 | ||||
-rw-r--r-- | src/plugins/NyaaSi.cpp | 14 | ||||
-rw-r--r-- | src/plugins/Soundcloud.cpp | 6 |
13 files changed, 440 insertions, 329 deletions
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, ¤t_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, ¤t_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, ¤t_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, ¤t_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, ¤t_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([¤t_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, ¤t_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([¤t_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, ¤t_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([¤t_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, ¤t_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([¤t_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}); |