// This file is part of Desktop App Toolkit, // a set of libraries for developing nice desktop applications. // // For license and copyright information please follow this link: // https://github.com/desktop-app/legal/blob/master/LEGAL // #pragma once #include "ui/style/style_core.h" #include "ui/emoji_config.h" #include #include namespace Ui { namespace Text { enum TextBlockType { TextBlockTNewline = 0x01, TextBlockTText = 0x02, TextBlockTEmoji = 0x03, TextBlockTCustomEmoji = 0x04, TextBlockTSkip = 0x05, }; enum TextBlockFlags { TextBlockFBold = 0x01, TextBlockFItalic = 0x02, TextBlockFUnderline = 0x04, TextBlockFStrikeOut = 0x08, TextBlockFTilde = 0x10, // tilde fix in OpenSans TextBlockFSemibold = 0x20, TextBlockFCode = 0x40, TextBlockFPre = 0x80, TextBlockFPlainLink = 0x100, }; class AbstractBlock { public: uint16 from() const; int width() const; int rpadding() const; QFixed f_width() const; QFixed f_rpadding() const; // Should be virtual, but optimized throught type() call. QFixed f_rbearing() const; uint16 lnkIndex() const; void setLnkIndex(uint16 lnkIndex); uint16 spoilerIndex() const; void setSpoilerIndex(uint16 spoilerIndex); TextBlockType type() const; int32 flags() const; protected: AbstractBlock( const style::font &font, const QString &str, uint16 from, uint16 length, uint16 flags, uint16 lnkIndex, uint16 spoilerIndex); uint32 _flags = 0; // 2 bits empty, 16 bits lnkIndex, 4 bits type, 10 bits flags uint16 _from = 0; uint16 _spoilerIndex = 0; QFixed _width = 0; // Right padding: spaces after the last content of the block (like a word). // This holds spaces after the end of the block, for example a text ending // with a space before a link has started. If text block has a leading spaces // (for example a text block after a link block) it is prepended with an empty // word that holds those spaces as a right padding. QFixed _rpadding = 0; }; class NewlineBlock final : public AbstractBlock { public: NewlineBlock( const style::font &font, const QString &str, uint16 from, uint16 length, uint16 flags, uint16 lnkIndex, uint16 spoilerIndex); Qt::LayoutDirection nextDirection() const; private: Qt::LayoutDirection _nextDir = Qt::LayoutDirectionAuto; friend class String; friend class Parser; friend class Renderer; }; class TextWord final { public: TextWord() = default; TextWord(uint16 from, QFixed width, QFixed rbearing, QFixed rpadding = 0); uint16 from() const; QFixed f_rbearing() const; QFixed f_width() const; QFixed f_rpadding() const; void add_rpadding(QFixed padding); private: uint16 _from = 0; int16 _rbearing = 0; QFixed _width, _rpadding; }; class TextBlock final : public AbstractBlock { public: TextBlock( const style::font &font, const QString &str, QFixed minResizeWidth, uint16 from, uint16 length, uint16 flags, uint16 lnkIndex, uint16 spoilerIndex); private: QFixed real_f_rbearing() const; QVector _words; friend class String; friend class Parser; friend class Renderer; friend class BlockParser; friend class AbstractBlock; }; class EmojiBlock final : public AbstractBlock { public: EmojiBlock( const style::font &font, const QString &str, uint16 from, uint16 length, uint16 flags, uint16 lnkIndex, uint16 spoilerIndex, EmojiPtr emoji); private: EmojiPtr _emoji = nullptr; friend class String; friend class Parser; friend class Renderer; }; class CustomEmoji { public: virtual ~CustomEmoji() = default; [[nodiscard]] virtual QString entityData() = 0; virtual void paint( QPainter &p, int x, int y, crl::time now, const QColor &preview, bool paused) = 0; virtual void unload() = 0; }; class CustomEmojiBlock final : public AbstractBlock { public: CustomEmojiBlock( const style::font &font, const QString &str, uint16 from, uint16 length, uint16 flags, uint16 lnkIndex, uint16 spoilerIndex, std::unique_ptr custom); private: std::unique_ptr _custom; friend class String; friend class Parser; friend class Renderer; }; class SkipBlock final : public AbstractBlock { public: SkipBlock( const style::font &font, const QString &str, uint16 from, int32 w, int32 h, uint16 lnkIndex, uint16 spoilerIndex); int height() const; private: int _height = 0; friend class String; friend class Parser; friend class Renderer; }; class Block final { public: Block(); Block(Block &&other); Block &operator=(Block &&other); ~Block(); [[nodiscard]] static Block Newline( const style::font &font, const QString &str, uint16 from, uint16 length, uint16 flags, uint16 lnkIndex, uint16 spoilerIndex); [[nodiscard]] static Block Text( const style::font &font, const QString &str, QFixed minResizeWidth, uint16 from, uint16 length, uint16 flags, uint16 lnkIndex, uint16 spoilerIndex); [[nodiscard]] static Block Emoji( const style::font &font, const QString &str, uint16 from, uint16 length, uint16 flags, uint16 lnkIndex, uint16 spoilerIndex, EmojiPtr emoji); [[nodiscard]] static Block CustomEmoji( const style::font &font, const QString &str, uint16 from, uint16 length, uint16 flags, uint16 lnkIndex, uint16 spoilerIndex, std::unique_ptr custom); [[nodiscard]] static Block Skip( const style::font &font, const QString &str, uint16 from, int32 w, int32 h, uint16 lnkIndex, uint16 spoilerIndex); template [[nodiscard]] FinalBlock &unsafe() { return *reinterpret_cast(&_data); } template [[nodiscard]] const FinalBlock &unsafe() const { return *reinterpret_cast(&_data); } [[nodiscard]] AbstractBlock *get(); [[nodiscard]] const AbstractBlock *get() const; [[nodiscard]] AbstractBlock *operator->(); [[nodiscard]] const AbstractBlock *operator->() const; [[nodiscard]] AbstractBlock &operator*(); [[nodiscard]] const AbstractBlock &operator*() const; private: struct Tag { }; explicit Block(const Tag &) { } template [[nodiscard]] static Block New(Args &&...args) { auto result = Block(Tag{}); result.emplace(std::forward(args)...); return result; } template void emplace(Args &&...args) { new (&_data) FinalType(std::forward(args)...); } void destroy(); static_assert(sizeof(NewlineBlock) <= sizeof(TextBlock)); static_assert(alignof(NewlineBlock) <= alignof(void*)); static_assert(sizeof(EmojiBlock) <= sizeof(TextBlock)); static_assert(alignof(EmojiBlock) <= alignof(void*)); static_assert(sizeof(CustomEmojiBlock) <= sizeof(TextBlock)); static_assert(alignof(CustomEmojiBlock) <= alignof(void*)); static_assert(sizeof(SkipBlock) <= sizeof(TextBlock)); static_assert(alignof(SkipBlock) <= alignof(void*)); std::aligned_storage_t _data; }; } // namespace Text } // namespace Ui