From 6c85194c3b1baef0eaa011c4f1b8e48e11860f45 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Tue, 3 Aug 2021 15:06:57 +0200 Subject: 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. --- include/Body.hpp | 205 +++++++++---------------------------------------- include/BodyItem.hpp | 175 +++++++++++++++++++++++++++++++++++++++++ include/QuickMedia.hpp | 2 +- 3 files changed, 212 insertions(+), 170 deletions(-) create mode 100644 include/BodyItem.hpp (limited to 'include') 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 #include #include #include @@ -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; - void *userdata = nullptr; - }; + using BodyItemRenderCallback = std::function &body_item)>; + // Return true to merge + using BodyItemMergeHandler = std::function; - class BodyItem { + class BodyItemList { public: - BodyItem(std::string _title); - BodyItem(const BodyItem&) = delete; - BodyItem& operator=(const BodyItem &other); - - static std::shared_ptr create(std::string title) { return std::make_shared(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 title_text; - std::unique_ptr description_text; - std::unique_ptr author_text; - std::unique_ptr timestamp_text; // TODO: Remove - // Used by image boards for example. The elements are indices to other body items - std::vector replies_to; - // Used by image boards for example. The elements are indices to other body items - std::vector 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 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 reactions; // TODO: Move to a different body item type - std::shared_ptr extra; // TODO: Remove - - // Internal use only - int keep_alive_frames = 0; + BodyItemList(BodyItems *body_items) : body_items(body_items) {} + std::shared_ptr* data() { return body_items->data(); } + const std::shared_ptr* data() const { return body_items->data(); } + std::shared_ptr& operator[](size_t index) { return (*body_items)[index]; } + const std::shared_ptr& 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>; - using BodyItemRenderCallback = std::function &body_item)>; - // Return true to merge - using BodyItemMergeHandler = std::function; - 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 body_item); void prepend_items_reverse(BodyItems new_items); + void append_item(std::shared_ptr body_item); void append_items(BodyItems new_items); + void insert_item(std::shared_ptr 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 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&)> callback); + // Return true if the item is the match + std::shared_ptr find_item(std::function&)> callback); + // Returns -1 if no item is found + int find_item_index(std::function&)> callback); + // Return true to remove the current item. + // Returns true if the item was found. + bool erase_item(std::function&)> callback); + std::shared_ptr 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 +#include + +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; + void *userdata = nullptr; + }; + + class BodyItem { + public: + BodyItem(std::string _title); + BodyItem(const BodyItem&) = delete; + BodyItem& operator=(const BodyItem &other); + + static std::shared_ptr create(std::string title) { return std::make_shared(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 title_text; + std::unique_ptr description_text; + std::unique_ptr author_text; + std::unique_ptr timestamp_text; // TODO: Remove + // Used by image boards for example. The elements are indices to other body items + std::vector replies_to; + // Used by image boards for example. The elements are indices to other body items + std::vector 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 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 reactions; // TODO: Move to a different body item type + std::shared_ptr 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>; +} \ 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 &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); -- cgit v1.2.3