aboutsummaryrefslogtreecommitdiff
path: root/include/BodyItem.hpp
blob: 819b730ef11851accd74d3b360c04951a025c5c7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
#pragma once

#include "Text.hpp"
#include <memory>
#include <optional>
#include <mglpp/graphics/Text.hpp>

namespace mgl {
    class Window;
}

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
    };

    struct ThumbnailWidget {
        mgl::vec2f position;
        mgl::vec2f size;
    };

    struct Widgets {
        std::optional<ThumbnailWidget> thumbnail;
    };

    // TODO: Remove and create an Userdata class instead to replace the void* userdata in BodyItem
    class BodyItemExtra {
    public:
        virtual ~BodyItemExtra() = default;

        virtual void draw_overlay(mgl::Window &window, const Widgets &widgets) {
            (void)window;
            (void)widgets;
        }
    };

    struct Reaction {
        std::string text_str;
        std::unique_ptr<Text> text;
        void *userdata = nullptr;
        mgl::vec2f size;
        mgl::Color text_color;
        int num_lines = 1;
    };

    class BodyItem {
    public:
        BodyItem(const BodyItem&) = delete;
        BodyItem& operator=(const BodyItem &other);

        static std::shared_ptr<BodyItem> create(std::string title, bool selectable = true);

        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(int64_t new_timestamp) {
            if(new_timestamp == timestamp)
                return;
            timestamp = new_timestamp;
            dirty_timestamp = true;
        }

        void set_title_color(mgl::Color new_color) {
            if(new_color == title_color)
                return;
            title_color = new_color;
            dirty = true;
        }

        void set_description_color(mgl::Color new_color) {
            if(new_color == description_color)
                return;
            description_color = new_color;
            dirty_description = true;
        }

        void set_author_color(mgl::Color new_color) {
            if(new_color == author_color)
                return;
            author_color = new_color;
            dirty_author = true;
        }

        void add_reaction(std::string text, void *userdata, mgl::Color text_color);
        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
        int64_t get_timestamp() const { return timestamp; }

        mgl::Color get_title_color() const { return title_color; }
        mgl::Color get_description_color() const { return description_color; }
        mgl::Color get_author_color() const { return author_color; }

        bool is_selectable() const { return selectable; }

        void draw_list(Body *body, mgl::Window &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; // TODO: Make private and when set by user, set a |visible_force| variable to true or false which makes the item invisible even after filtering
        bool dirty;
        bool dirty_description;
        bool dirty_author;
        bool dirty_timestamp;
        bool dirty_reactions;
        // 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<mgl::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;
        float height = 0.0f;
        float prev_height = 0.0f;
        mgl::vec2f 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;
        mgl::vec2i 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:
        BodyItem(std::string _title, bool selectable);
    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;
        int64_t timestamp;
        mgl::Color title_color;
        mgl::Color author_color;
        mgl::Color description_color;
        bool selectable;
    };

    using BodyItems = std::vector<std::shared_ptr<BodyItem>>;
}