1 #pragma once
2 
3 #include "common/FlagsEnum.hpp"
4 #include "messages/ImageSet.hpp"
5 #include "messages/Link.hpp"
6 #include "messages/MessageColor.hpp"
7 #include "singletons/Fonts.hpp"
8 
9 #include <QRect>
10 #include <QString>
11 #include <QTime>
12 #include <boost/noncopyable.hpp>
13 #include <cstdint>
14 #include <memory>
15 #include <pajlada/signals/signalholder.hpp>
16 #include <vector>
17 
18 namespace chatterino {
19 class Channel;
20 struct MessageLayoutContainer;
21 class MessageLayoutElement;
22 
23 class Image;
24 using ImagePtr = std::shared_ptr<Image>;
25 
26 struct Emote;
27 using EmotePtr = std::shared_ptr<const Emote>;
28 
29 enum class MessageElementFlag : int64_t {
30     None = 0LL,
31     Misc = (1LL << 0),
32     Text = (1LL << 1),
33 
34     Username = (1LL << 2),
35     Timestamp = (1LL << 3),
36 
37     TwitchEmoteImage = (1LL << 4),
38     TwitchEmoteText = (1LL << 5),
39     TwitchEmote = TwitchEmoteImage | TwitchEmoteText,
40     BttvEmoteImage = (1LL << 6),
41     BttvEmoteText = (1LL << 7),
42     BttvEmote = BttvEmoteImage | BttvEmoteText,
43 
44     ChannelPointReward = (1LL << 8),
45     ChannelPointRewardImage = ChannelPointReward | TwitchEmoteImage,
46 
47     FfzEmoteImage = (1LL << 9),
48     FfzEmoteText = (1LL << 10),
49     FfzEmote = FfzEmoteImage | FfzEmoteText,
50     EmoteImages = TwitchEmoteImage | BttvEmoteImage | FfzEmoteImage,
51     EmoteText = TwitchEmoteText | BttvEmoteText | FfzEmoteText,
52 
53     BitsStatic = (1LL << 11),
54     BitsAnimated = (1LL << 12),
55 
56     // Slot 1: Twitch
57     // - Staff badge
58     // - Admin badge
59     // - Global Moderator badge
60     BadgeGlobalAuthority = (1LL << 13),
61 
62     // Slot 2: Twitch
63     // - Predictions badge
64     BadgePredictions = (1LL << 14),
65 
66     // Slot 3: Twitch
67     // - VIP badge
68     // - Moderator badge
69     // - Broadcaster badge
70     BadgeChannelAuthority = (1LL << 15),
71 
72     // Slot 4: Twitch
73     // - Subscription badges
74     BadgeSubscription = (1LL << 16),
75 
76     // Slot 5: Twitch
77     // - Turbo badge
78     // - Prime badge
79     // - Bit badges
80     // - Game badges
81     BadgeVanity = (1LL << 17),
82 
83     // Slot 6: Chatterino
84     // - Chatterino developer badge
85     // - Chatterino contributor badge
86     // - Chatterino donator badge
87     // - Chatterino top donator badge
88     // - Chatterino special pepe badge
89     // - Chatterino gnome badge
90     BadgeChatterino = (1LL << 18),
91 
92     // Slot 7: FrankerFaceZ
93     // - FFZ developer badge
94     // - FFZ bot badge
95     // - FFZ donator badge
96     BadgeFfz = (1LL << 19),
97 
98     Badges = BadgeGlobalAuthority | BadgePredictions | BadgeChannelAuthority |
99              BadgeSubscription | BadgeVanity | BadgeChatterino | BadgeFfz,
100 
101     ChannelName = (1LL << 20),
102 
103     BitsAmount = (1LL << 21),
104 
105     ModeratorTools = (1LL << 22),
106 
107     EmojiImage = (1LL << 23),
108     EmojiText = (1LL << 24),
109     EmojiAll = EmojiImage | EmojiText,
110 
111     AlwaysShow = (1LL << 25),
112 
113     // used in the ChannelView class to make the collapse buttons visible if
114     // needed
115     Collapsed = (1LL << 26),
116 
117     // used for dynamic bold usernames
118     BoldUsername = (1LL << 27),
119     NonBoldUsername = (1LL << 28),
120 
121     // for links
122     LowercaseLink = (1LL << 29),
123     OriginalLink = (1LL << 30),
124 
125     // ZeroWidthEmotes are emotes that are supposed to overlay over any pre-existing emotes
126     // e.g. BTTV's SoSnowy during christmas season
127     ZeroWidthEmote = (1LL << 31),
128 
129     Default = Timestamp | Badges | Username | BitsStatic | FfzEmoteImage |
130               BttvEmoteImage | TwitchEmoteImage | BitsAmount | Text |
131               AlwaysShow,
132 };
133 using MessageElementFlags = FlagsEnum<MessageElementFlag>;
134 
135 class MessageElement : boost::noncopyable
136 {
137 public:
138     enum UpdateFlags : char {
139         Update_Text = 1,
140         Update_Emotes = 2,
141         Update_Images = 4,
142         Update_All = Update_Text | Update_Emotes | Update_Images
143     };
144     enum ThumbnailType : char {
145         Link_Thumbnail = 1,
146     };
147 
148     virtual ~MessageElement();
149 
150     MessageElement *setLink(const Link &link);
151     MessageElement *setText(const QString &text);
152     MessageElement *setTooltip(const QString &tooltip);
153     MessageElement *setThumbnailType(const ThumbnailType type);
154     MessageElement *setThumbnail(const ImagePtr &thumbnail);
155 
156     MessageElement *setTrailingSpace(bool value);
157     const QString &getTooltip() const;
158     const ImagePtr &getThumbnail() const;
159     const ThumbnailType &getThumbnailType() const;
160 
161     const Link &getLink() const;
162     bool hasTrailingSpace() const;
163     MessageElementFlags getFlags() const;
164     MessageElement *updateLink();
165 
166     virtual void addToContainer(MessageLayoutContainer &container,
167                                 MessageElementFlags flags) = 0;
168 
169     pajlada::Signals::NoArgSignal linkChanged;
170 
171 protected:
172     MessageElement(MessageElementFlags flags);
173     bool trailingSpace = true;
174 
175 private:
176     QString text_;
177     Link link_;
178     QString tooltip_;
179     ImagePtr thumbnail_;
180     ThumbnailType thumbnailType_;
181     MessageElementFlags flags_;
182 };
183 
184 // used when layout element doesn't have a creator
185 class EmptyElement : public MessageElement
186 {
187 public:
188     EmptyElement();
189 
190     void addToContainer(MessageLayoutContainer &container,
191                         MessageElementFlags flags) override;
192 
193     static EmptyElement &instance();
194 
195 private:
196     ImagePtr image_;
197 };
198 
199 // contains a simple image
200 class ImageElement : public MessageElement
201 {
202 public:
203     ImageElement(ImagePtr image, MessageElementFlags flags);
204 
205     void addToContainer(MessageLayoutContainer &container,
206                         MessageElementFlags flags) override;
207 
208 private:
209     ImagePtr image_;
210 };
211 
212 // contains a text, it will split it into words
213 class TextElement : public MessageElement
214 {
215 public:
216     TextElement(const QString &text, MessageElementFlags flags,
217                 const MessageColor &color = MessageColor::Text,
218                 FontStyle style = FontStyle::ChatMedium);
219     ~TextElement() override = default;
220 
221     void addToContainer(MessageLayoutContainer &container,
222                         MessageElementFlags flags) override;
223 
224 private:
225     MessageColor color_;
226     FontStyle style_;
227 
228     struct Word {
229         QString text;
230         int width = -1;
231     };
232     std::vector<Word> words_;
233 };
234 
235 // contains emote data and will pick the emote based on :
236 //   a) are images for the emote type enabled
237 //   b) which size it wants
238 class EmoteElement : public MessageElement
239 {
240 public:
241     EmoteElement(const EmotePtr &data, MessageElementFlags flags_);
242 
243     void addToContainer(MessageLayoutContainer &container,
244                         MessageElementFlags flags_) override;
245     EmotePtr getEmote() const;
246 
247 protected:
248     virtual MessageLayoutElement *makeImageLayoutElement(const ImagePtr &image,
249                                                          const QSize &size);
250 
251 private:
252     std::unique_ptr<TextElement> textElement_;
253     EmotePtr emote_;
254 };
255 
256 class BadgeElement : public MessageElement
257 {
258 public:
259     BadgeElement(const EmotePtr &data, MessageElementFlags flags_);
260 
261     void addToContainer(MessageLayoutContainer &container,
262                         MessageElementFlags flags_) override;
263 
264     EmotePtr getEmote() const;
265 
266 protected:
267     virtual MessageLayoutElement *makeImageLayoutElement(const ImagePtr &image,
268                                                          const QSize &size);
269 
270 private:
271     EmotePtr emote_;
272 };
273 
274 class ModBadgeElement : public BadgeElement
275 {
276 public:
277     ModBadgeElement(const EmotePtr &data, MessageElementFlags flags_);
278 
279 protected:
280     MessageLayoutElement *makeImageLayoutElement(const ImagePtr &image,
281                                                  const QSize &size) override;
282 };
283 
284 class VipBadgeElement : public BadgeElement
285 {
286 public:
287     VipBadgeElement(const EmotePtr &data, MessageElementFlags flags_);
288 
289 protected:
290     MessageLayoutElement *makeImageLayoutElement(const ImagePtr &image,
291                                                  const QSize &size) override;
292 };
293 
294 class FfzBadgeElement : public BadgeElement
295 {
296 public:
297     FfzBadgeElement(const EmotePtr &data, MessageElementFlags flags_,
298                     QColor &color);
299 
300 protected:
301     MessageLayoutElement *makeImageLayoutElement(const ImagePtr &image,
302                                                  const QSize &size) override;
303     QColor color;
304 };
305 
306 // contains a text, formated depending on the preferences
307 class TimestampElement : public MessageElement
308 {
309 public:
310     TimestampElement(QTime time_ = QTime::currentTime());
311     ~TimestampElement() override = default;
312 
313     void addToContainer(MessageLayoutContainer &container,
314                         MessageElementFlags flags) override;
315 
316     TextElement *formatTime(const QTime &time);
317 
318 private:
319     QTime time_;
320     std::unique_ptr<TextElement> element_;
321     QString format_;
322 };
323 
324 // adds all the custom moderation buttons, adds a variable amount of items
325 // depending on settings fourtf: implement
326 class TwitchModerationElement : public MessageElement
327 {
328 public:
329     TwitchModerationElement();
330 
331     void addToContainer(MessageLayoutContainer &container,
332                         MessageElementFlags flags) override;
333 };
334 
335 // contains a full message string that's split into words on space and parses irc colors that are then put into segments
336 // these segments are later passed to "MultiColorTextLayoutElement" elements to be rendered :)
337 class IrcTextElement : public MessageElement
338 {
339 public:
340     IrcTextElement(const QString &text, MessageElementFlags flags,
341                    FontStyle style = FontStyle::ChatMedium);
342     ~IrcTextElement() override = default;
343 
344     void addToContainer(MessageLayoutContainer &container,
345                         MessageElementFlags flags) override;
346 
347 private:
348     FontStyle style_;
349 
350     struct Segment {
351         QString text;
352         int fg = -1;
353         int bg = -1;
354     };
355 
356     struct Word {
357         QString text;
358         int width = -1;
359         std::vector<Segment> segments;
360     };
361 
362     std::vector<Word> words_;
363 };
364 
365 // Forces a linebreak
366 class LinebreakElement : public MessageElement
367 {
368 public:
369     LinebreakElement(MessageElementFlags flags);
370 
371     void addToContainer(MessageLayoutContainer &container,
372                         MessageElementFlags flags) override;
373 };
374 
375 // Image element which will pick the quality of the image based on ui scale
376 class ScalingImageElement : public MessageElement
377 {
378 public:
379     ScalingImageElement(ImageSet images, MessageElementFlags flags);
380 
381     void addToContainer(MessageLayoutContainer &container,
382                         MessageElementFlags flags) override;
383 
384 private:
385     ImageSet images_;
386 };
387 }  // namespace chatterino
388