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