aboutsummaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/Body.hpp205
-rw-r--r--include/BodyItem.hpp175
-rw-r--r--include/QuickMedia.hpp2
3 files changed, 212 insertions, 170 deletions
diff --git a/include/Body.hpp b/include/Body.hpp
index 43fdf22..b44a386 100644
--- a/include/Body.hpp
+++ b/include/Body.hpp
@@ -1,9 +1,8 @@
#pragma once
-#include "Text.hpp"
+#include "BodyItem.hpp"
#include "AsyncImageLoader.hpp"
#include "RoundedRectangle.hpp"
-#include <SFML/Graphics/Text.hpp>
#include <SFML/Graphics/RectangleShape.hpp>
#include <SFML/Graphics/Sprite.hpp>
#include <json/value.h>
@@ -18,171 +17,22 @@ namespace sf {
}
namespace QuickMedia {
- class Program;
-
- enum class FetchStatus {
- NONE,
- QUEUED_LOADING,
- LOADING,
- FINISHED_LOADING,
- FAILED_TO_LOAD
- };
-
- enum class ThumbnailMaskType {
- NONE,
- CIRCLE,
- ROUNDED_RECTANGLE
- };
-
- enum BodyTheme : int {
- BODY_THEME_MINIMAL,
- BODY_THEME_MODERN_SPACIOUS
- };
-
- // TODO: Remove and create an Userdata class instead to replace the void* userdata in BodyItem
- class BodyItemExtra {
- public:
- virtual ~BodyItemExtra() = default;
- };
-
- struct Reaction {
- std::unique_ptr<Text> text;
- void *userdata = nullptr;
- };
+ using BodyItemRenderCallback = std::function<void(std::shared_ptr<BodyItem> &body_item)>;
+ // Return true to merge
+ using BodyItemMergeHandler = std::function<bool(BodyItem *prev_item, BodyItem *this_item)>;
- class BodyItem {
+ class BodyItemList {
public:
- BodyItem(std::string _title);
- BodyItem(const BodyItem&) = delete;
- BodyItem& operator=(const BodyItem &other);
-
- static std::shared_ptr<BodyItem> create(std::string title) { return std::make_shared<BodyItem>(std::move(title)); }
-
- void set_title(std::string new_title) {
- if(title == new_title)
- return;
- title = std::move(new_title);
- dirty = true;
- }
-
- void set_description(std::string new_description) {
- if(description == new_description)
- return;
- description = std::move(new_description);
- dirty_description = true;
- }
-
- void set_author(std::string new_author) {
- if(author == new_author)
- return;
- author = std::move(new_author);
- dirty_author = true;
- }
-
- // |new_timestamp| is in milliseconds
- void set_timestamp(time_t new_timestamp) {
- if(new_timestamp == timestamp)
- return;
- timestamp = new_timestamp;
- dirty_timestamp = true;
- }
-
- void set_title_color(sf::Color new_color) {
- if(new_color == title_color)
- return;
- title_color = new_color;
- dirty = true;
- }
-
- void set_description_color(sf::Color new_color) {
- if(new_color == description_color)
- return;
- description_color = new_color;
- dirty_description = true;
- }
-
- void set_author_color(sf::Color new_color) {
- if(new_color == author_color)
- return;
- author_color = new_color;
- dirty_author = true;
- }
-
- void add_reaction(std::string text, void *userdata);
-
- // Returns true if reaction is found
- bool remove_reaction_by_userdata(void *userdata) {
- for(auto it = reactions.begin(); it != reactions.end(); ++it) {
- if(it->userdata == userdata) {
- reactions.erase(it);
- return true;
- }
- }
- return false;
- }
-
- const std::string& get_title() const { return title; }
- const std::string& get_description() const { return description; }
- const std::string& get_author() const { return author; }
- // In milliseconds
- time_t get_timestamp() const { return timestamp; }
-
- sf::Color get_title_color() const { return title_color; }
- sf::Color get_description_color() const { return description_color; }
- sf::Color get_author_color() const { return author_color; }
-
- // TODO: Use a list of strings instead, not all plugins need all of these fields
- std::string url;
- std::string thumbnail_url;
- bool visible;
- bool dirty;
- bool dirty_description;
- bool dirty_author;
- bool dirty_timestamp;
- // TODO: Remove this and instead if |thumbnail_url| starts with file://, then its a local file
- bool thumbnail_is_local;
- std::unique_ptr<Text> title_text;
- std::unique_ptr<Text> description_text;
- std::unique_ptr<Text> author_text;
- std::unique_ptr<sf::Text> timestamp_text; // TODO: Remove
- // Used by image boards for example. The elements are indices to other body items
- std::vector<size_t> replies_to;
- // Used by image boards for example. The elements are indices to other body items
- std::vector<size_t> replies;
- std::string post_number;
- void *userdata; // Not managed, should be deallocated by whoever sets this
- float loaded_height = 0.0f;
- sf::Vector2f loaded_image_size;
- float loaded_content_height = 0.0f;
- FetchStatus embedded_item_status = FetchStatus::NONE;
- // Important! Should refer to a new BodyItem, not one that already exists in the body.
- // TODO: Allow referring to an existing body item. This doesn't work properly at the moment because max width of text and line count calculation getting messed up
- // if an embedded item wraps but not the original body item.
- std::shared_ptr<BodyItem> embedded_item; // Used by matrix for example to display reply message body. Note: only the first level of embedded items is rendered (not recursive, this is done on purpose)
- ThumbnailMaskType thumbnail_mask_type = ThumbnailMaskType::NONE;
- sf::Vector2i thumbnail_size;
- std::vector<Reaction> reactions; // TODO: Move to a different body item type
- std::shared_ptr<BodyItemExtra> extra; // TODO: Remove
-
- // Internal use only
- int keep_alive_frames = 0;
+ BodyItemList(BodyItems *body_items) : body_items(body_items) {}
+ std::shared_ptr<BodyItem>* data() { return body_items->data(); }
+ const std::shared_ptr<BodyItem>* data() const { return body_items->data(); }
+ std::shared_ptr<BodyItem>& operator[](size_t index) { return (*body_items)[index]; }
+ const std::shared_ptr<BodyItem>& operator[](size_t index) const { return (*body_items)[index]; }
+ size_t size() const { return body_items->size(); }
private:
- // TODO: Clean up these strings when set in text, and get_title for example should return |title_text.getString()|
- // TODO: Use sf::String instead, removes the need to convert to utf32 every time the text is dirty (for example when resizing window)
- std::string title;
- std::string description;
- std::string author;
- time_t timestamp;
- sf::Color title_color;
- sf::Color author_color;
- sf::Color description_color;
+ BodyItems *body_items;
};
- using BodyItems = std::vector<std::shared_ptr<BodyItem>>;
- using BodyItemRenderCallback = std::function<void(std::shared_ptr<BodyItem> &body_item)>;
- // Return true to merge
- using BodyItemMergeHandler = std::function<bool(BodyItem *prev_item, BodyItem *this_item)>;
-
enum class AttachSide {
TOP,
BOTTOM
@@ -212,13 +62,34 @@ namespace QuickMedia {
void select_first_item(bool reset_page_scroll = true);
void select_last_item();
+ void set_items(BodyItems items);
void clear_items();
+ void prepend_item(std::shared_ptr<BodyItem> body_item);
void prepend_items_reverse(BodyItems new_items);
+ void append_item(std::shared_ptr<BodyItem> body_item);
void append_items(BodyItems new_items);
+ void insert_item(std::shared_ptr<BodyItem> body_item, int index);
+ void move_items_to(Body *other_body);
+ void move_item(size_t src_index, size_t dst_index);
// Returns the inserted position
size_t insert_item_by_timestamp(std::shared_ptr<BodyItem> body_item);
// Note: keeps the selected item (moving the selected_item index if necessary)
void insert_items_by_timestamps(BodyItems new_items);
+ void for_each_item(std::function<void(std::shared_ptr<BodyItem>&)> callback);
+ // Return true if the item is the match
+ std::shared_ptr<BodyItem> find_item(std::function<bool(std::shared_ptr<BodyItem>&)> callback);
+ // Returns -1 if no item is found
+ int find_item_index(std::function<bool(std::shared_ptr<BodyItem>&)> callback);
+ // Return true to remove the current item.
+ // Returns true if the item was found.
+ bool erase_item(std::function<bool(std::shared_ptr<BodyItem>&)> callback);
+ std::shared_ptr<BodyItem> get_item_by_index(size_t index);
+ BodyItemList get_items();
+ BodyItems get_items_copy();
+ // If |end_index| is (size_t)-1, then all items starting from |start_index| until the end are copied
+ void copy_range(size_t start_index, size_t end_index, BodyItems &target);
+ size_t get_num_items() const;
+ void reverse_items();
void clear_cache();
void clear_text_cache();
@@ -255,17 +126,12 @@ namespace QuickMedia {
// This is the item we can see the end of
bool is_bottom_cut_off() const { return bottom_cut_off; }
int get_num_visible_items() const { return num_visible_items; };
-
- // Call this once after adding new items
- void items_set_dirty(bool force = false);
void apply_search_filter_for_item(BodyItem *body_item);
bool can_move_left() const { return selected_column > 0; }
bool can_move_right() const { return selected_column + 1 < num_columns; }
- // TODO: Prevent items from being removed when render is in progress. That would invalidate references and cause a crash
- BodyItems items;
bool draw_thumbnails;
// Set to {0, 0} to disable resizing
sf::Vector2i thumbnail_max_size;
@@ -315,6 +181,9 @@ namespace QuickMedia {
BOTTOM
};
+ // TODO: Prevent items from being removed when render is in progress. That would invalidate references and cause a crash
+ BodyItems items;
+
BodyTheme body_theme;
int selected_item;
@@ -376,8 +245,6 @@ namespace QuickMedia {
sf::Vector2f item_background_target_size;
float item_background_target_height = 0.0f;
TargetSetState target_set = TargetSetState::NOT_SET;
- // TODO: Instead of using this, add functions for modifying |items| and apply the filter on those new items
- DirtyState items_dirty = DirtyState::FALSE;
std::string current_filter;
bool using_filter = false;
sf::Shader *rounded_rectangle_mask_shader;
diff --git a/include/BodyItem.hpp b/include/BodyItem.hpp
new file mode 100644
index 0000000..ed53908
--- /dev/null
+++ b/include/BodyItem.hpp
@@ -0,0 +1,175 @@
+#pragma once
+
+#include "Text.hpp"
+#include <memory>
+#include <SFML/Graphics/Text.hpp>
+
+namespace sf {
+ class RenderTarget;
+}
+
+namespace QuickMedia {
+ class Body;
+
+ enum class FetchStatus {
+ NONE,
+ QUEUED_LOADING,
+ LOADING,
+ FINISHED_LOADING,
+ FAILED_TO_LOAD
+ };
+
+ enum class ThumbnailMaskType {
+ NONE,
+ CIRCLE,
+ ROUNDED_RECTANGLE
+ };
+
+ enum BodyTheme : int {
+ BODY_THEME_MINIMAL,
+ BODY_THEME_MODERN_SPACIOUS
+ };
+
+ // TODO: Remove and create an Userdata class instead to replace the void* userdata in BodyItem
+ class BodyItemExtra {
+ public:
+ virtual ~BodyItemExtra() = default;
+ };
+
+ struct Reaction {
+ std::unique_ptr<Text> text;
+ void *userdata = nullptr;
+ };
+
+ class BodyItem {
+ public:
+ BodyItem(std::string _title);
+ BodyItem(const BodyItem&) = delete;
+ BodyItem& operator=(const BodyItem &other);
+
+ static std::shared_ptr<BodyItem> create(std::string title) { return std::make_shared<BodyItem>(std::move(title)); }
+
+ void set_title(std::string new_title) {
+ if(title == new_title)
+ return;
+ title = std::move(new_title);
+ dirty = true;
+ }
+
+ void set_description(std::string new_description) {
+ if(description == new_description)
+ return;
+ description = std::move(new_description);
+ dirty_description = true;
+ }
+
+ void set_author(std::string new_author) {
+ if(author == new_author)
+ return;
+ author = std::move(new_author);
+ dirty_author = true;
+ }
+
+ // |new_timestamp| is in milliseconds
+ void set_timestamp(time_t new_timestamp) {
+ if(new_timestamp == timestamp)
+ return;
+ timestamp = new_timestamp;
+ dirty_timestamp = true;
+ }
+
+ void set_title_color(sf::Color new_color) {
+ if(new_color == title_color)
+ return;
+ title_color = new_color;
+ dirty = true;
+ }
+
+ void set_description_color(sf::Color new_color) {
+ if(new_color == description_color)
+ return;
+ description_color = new_color;
+ dirty_description = true;
+ }
+
+ void set_author_color(sf::Color new_color) {
+ if(new_color == author_color)
+ return;
+ author_color = new_color;
+ dirty_author = true;
+ }
+
+ void add_reaction(std::string text, void *userdata);
+
+ // Returns true if reaction is found
+ bool remove_reaction_by_userdata(void *userdata) {
+ for(auto it = reactions.begin(); it != reactions.end(); ++it) {
+ if(it->userdata == userdata) {
+ reactions.erase(it);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ const std::string& get_title() const { return title; }
+ const std::string& get_description() const { return description; }
+ const std::string& get_author() const { return author; }
+ // In milliseconds
+ time_t get_timestamp() const { return timestamp; }
+
+ sf::Color get_title_color() const { return title_color; }
+ sf::Color get_description_color() const { return description_color; }
+ sf::Color get_author_color() const { return author_color; }
+
+ void draw_list(Body *body, sf::RenderTarget &render_target);
+
+ // TODO: Use a list of strings instead, not all plugins need all of these fields
+ std::string url;
+ std::string thumbnail_url;
+ bool visible;
+ bool dirty;
+ bool dirty_description;
+ bool dirty_author;
+ bool dirty_timestamp;
+ // TODO: Remove this and instead if |thumbnail_url| starts with file://, then its a local file
+ bool thumbnail_is_local;
+ std::unique_ptr<Text> title_text;
+ std::unique_ptr<Text> description_text;
+ std::unique_ptr<Text> author_text;
+ std::unique_ptr<sf::Text> timestamp_text; // TODO: Remove
+ // Used by image boards for example. The elements are indices to other body items
+ std::vector<size_t> replies_to;
+ // Used by image boards for example. The elements are indices to other body items
+ std::vector<size_t> replies;
+ std::string post_number;
+ void *userdata; // Not managed, should be deallocated by whoever sets this
+ float loaded_height = 0.0f;
+ sf::Vector2f loaded_image_size;
+ float loaded_content_height = 0.0f;
+ FetchStatus embedded_item_status = FetchStatus::NONE;
+ // Important! Should refer to a new BodyItem, not one that already exists in the body.
+ // TODO: Allow referring to an existing body item. This doesn't work properly at the moment because max width of text and line count calculation getting messed up
+ // if an embedded item wraps but not the original body item.
+ std::shared_ptr<BodyItem> embedded_item; // Used by matrix for example to display reply message body. Note: only the first level of embedded items is rendered (not recursive, this is done on purpose)
+ ThumbnailMaskType thumbnail_mask_type = ThumbnailMaskType::NONE;
+ sf::Vector2i thumbnail_size;
+ std::vector<Reaction> reactions; // TODO: Move to a different body item type
+ std::shared_ptr<BodyItemExtra> extra; // TODO: Remove
+
+ // Internal use only
+ int keep_alive_frames = 0;
+ private:
+ // TODO: Clean up these strings when set in text, and get_title for example should return |title_text.getString()|
+ // TODO: Use sf::String instead, removes the need to convert to utf32 every time the text is dirty (for example when resizing window)
+ std::string title;
+ std::string description;
+ std::string author;
+ time_t timestamp;
+ sf::Color title_color;
+ sf::Color author_color;
+ sf::Color description_color;
+ };
+
+ using BodyItems = std::vector<std::shared_ptr<BodyItem>>;
+} \ No newline at end of file
diff --git a/include/QuickMedia.hpp b/include/QuickMedia.hpp
index 0cd65b4..4399c3d 100644
--- a/include/QuickMedia.hpp
+++ b/include/QuickMedia.hpp
@@ -115,7 +115,7 @@ namespace QuickMedia {
bool page_loop(std::vector<Tab> &tabs, int start_tab_index = 0, PageLoopSubmitHandler after_submit_handler = nullptr);
void video_page_download_video(const std::string &url, sf::WindowHandle video_player_window = None);
bool video_download_if_non_streamable(std::string &video_url, std::string &audio_url, bool &is_audio_only, bool &has_embedded_audio, PageType previous_page);
- void video_content_page(Page *parent_page, VideoPage *video_page, std::string video_title, bool download_if_streaming_fails, Body *parent_body, BodyItems &next_play_items, int play_index, int *parent_body_page = nullptr, const std::string &parent_page_search = "");
+ void video_content_page(Page *parent_page, VideoPage *video_page, std::string video_title, bool download_if_streaming_fails, Body *parent_body, int play_index, int *parent_body_page = nullptr, const std::string &parent_page_search = "");
// Returns -1 to go to previous chapter, 0 to stay on same chapter and 1 to go to next chapter
int image_page(MangaImagesPage *images_page, Body *chapters_body);
void image_continuous_page(MangaImagesPage *images_page);