text.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. // This file is part of Desktop App Toolkit,
  2. // a set of libraries for developing nice desktop applications.
  3. //
  4. // For license and copyright information please follow this link:
  5. // https://github.com/desktop-app/legal/blob/master/LEGAL
  6. //
  7. #pragma once
  8. #include "ui/text/text_entity.h"
  9. #include "ui/click_handler.h"
  10. #include "base/flags.h"
  11. #include "ui/style/style_core_types.h"
  12. #include <crl/crl_time.h>
  13. #include <any>
  14. class Painter;
  15. namespace anim {
  16. enum class type : uchar;
  17. } // namespace anim
  18. namespace style {
  19. struct TextStyle;
  20. struct TextPalette;
  21. struct QuoteStyle;
  22. } // namespace style
  23. namespace Ui {
  24. class SpoilerMessCached;
  25. extern const QString kQEllipsis;
  26. inline constexpr auto kQFixedMax = (INT_MAX / 256);
  27. } // namespace Ui
  28. struct TextParseOptions {
  29. int32 flags;
  30. int32 maxw;
  31. int32 maxh;
  32. Qt::LayoutDirection dir;
  33. };
  34. extern const TextParseOptions kDefaultTextOptions;
  35. extern const TextParseOptions kMarkupTextOptions;
  36. extern const TextParseOptions kPlainTextOptions;
  37. enum class TextSelectType {
  38. Letters = 0x01,
  39. Words = 0x02,
  40. Paragraphs = 0x03,
  41. };
  42. struct TextSelection {
  43. constexpr TextSelection() = default;
  44. constexpr TextSelection(uint16 from, uint16 to) : from(from), to(to) {
  45. }
  46. constexpr bool empty() const {
  47. return from == to;
  48. }
  49. uint16 from = 0;
  50. uint16 to = 0;
  51. };
  52. inline bool operator==(TextSelection a, TextSelection b) {
  53. return a.from == b.from && a.to == b.to;
  54. }
  55. inline bool operator!=(TextSelection a, TextSelection b) {
  56. return !(a == b);
  57. }
  58. static constexpr TextSelection AllTextSelection = { 0, 0xFFFF };
  59. namespace Ui::Text {
  60. class CustomEmoji;
  61. class AbstractBlock;
  62. class Block;
  63. class Word;
  64. struct IsolatedEmoji;
  65. struct OnlyCustomEmoji;
  66. struct SpoilerData;
  67. struct QuoteDetails;
  68. struct QuotesData;
  69. struct ExtendedData;
  70. struct MarkedContext;
  71. using CustomEmojiFactory = Fn<std::unique_ptr<CustomEmoji>(
  72. QStringView,
  73. const MarkedContext &)>;
  74. struct MarkedContext {
  75. Fn<void()> repaint;
  76. CustomEmojiFactory customEmojiFactory;
  77. std::any other;
  78. };
  79. struct Modification {
  80. int position = 0;
  81. uint16 skipped = 0;
  82. bool added = false;
  83. };
  84. struct StateRequest {
  85. enum class Flag {
  86. BreakEverywhere = (1 << 0),
  87. LookupSymbol = (1 << 1),
  88. LookupLink = (1 << 2),
  89. LookupCustomTooltip = (1 << 3),
  90. };
  91. using Flags = base::flags<Flag>;
  92. friend inline constexpr auto is_flag_type(Flag) { return true; };
  93. StateRequest() {
  94. }
  95. style::align align = style::al_left;
  96. Flags flags = Flag::LookupLink;
  97. };
  98. struct StateResult {
  99. ClickHandlerPtr link;
  100. bool uponSymbol = false;
  101. bool afterSymbol = false;
  102. uint16 symbol = 0;
  103. };
  104. struct StateRequestElided : StateRequest {
  105. StateRequestElided() {
  106. }
  107. StateRequestElided(const StateRequest &other) : StateRequest(other) {
  108. }
  109. int lines = 1;
  110. int removeFromEnd = 0;
  111. };
  112. class SpoilerMessCache {
  113. public:
  114. explicit SpoilerMessCache(int capacity);
  115. ~SpoilerMessCache();
  116. [[nodiscard]] not_null<SpoilerMessCached*> lookup(QColor color);
  117. void reset();
  118. private:
  119. struct Entry;
  120. std::vector<Entry> _cache;
  121. const int _capacity = 0;
  122. };
  123. struct SpecialColor {
  124. const QPen *pen = nullptr;
  125. const QPen *penSelected = nullptr;
  126. };
  127. struct LineGeometry {
  128. int left = 0;
  129. int width = 0;
  130. bool elided = false;
  131. };
  132. struct GeometryDescriptor {
  133. Fn<LineGeometry(int line)> layout;
  134. bool breakEverywhere = false;
  135. bool *outElided = nullptr;
  136. };
  137. [[nodiscard]] not_null<SpoilerMessCache*> DefaultSpoilerCache();
  138. [[nodiscard]] GeometryDescriptor SimpleGeometry(
  139. int availableWidth,
  140. int elisionLines,
  141. int elisionRemoveFromEnd,
  142. bool elisionBreakEverywhere);
  143. constexpr auto kMaxQuoteOutlines = 3;
  144. struct QuotePaintCache {
  145. QImage corners;
  146. QImage outline;
  147. QImage expand;
  148. QImage collapse;
  149. mutable QImage bottomCorner;
  150. mutable QImage bottomRounding;
  151. mutable QImage collapsedLine;
  152. std::array<QColor, kMaxQuoteOutlines> outlinesCached;
  153. QColor headerCached;
  154. QColor bgCached;
  155. QColor iconCached;
  156. std::array<QColor, kMaxQuoteOutlines> outlines;
  157. QColor header;
  158. QColor bg;
  159. QColor icon;
  160. };
  161. void ValidateQuotePaintCache(
  162. QuotePaintCache &cache,
  163. const style::QuoteStyle &st);
  164. struct SkipBlockPaintParts {
  165. uint32 skippedTop : 29 = 0;
  166. uint32 skipBottom : 1 = 0;
  167. uint32 expandIcon : 1 = 0;
  168. uint32 collapseIcon : 1 = 0;
  169. };
  170. void FillQuotePaint(
  171. QPainter &p,
  172. QRect rect,
  173. const QuotePaintCache &cache,
  174. const style::QuoteStyle &st,
  175. SkipBlockPaintParts parts = {});
  176. struct HighlightInfoRequest {
  177. TextSelection range;
  178. QRect interpolateTo;
  179. float64 interpolateProgress = 0.;
  180. QPainterPath *outPath = nullptr;
  181. };
  182. struct PaintContext {
  183. QPoint position;
  184. int outerWidth = 0; // For automatic RTL Ui inversion.
  185. int availableWidth = 0;
  186. GeometryDescriptor geometry; // By default is SimpleGeometry.
  187. style::align align = style::al_left;
  188. QRect clip;
  189. const style::TextPalette *palette = nullptr;
  190. QuotePaintCache *pre = nullptr;
  191. QuotePaintCache *blockquote = nullptr;
  192. std::span<SpecialColor> colors;
  193. SpoilerMessCache *spoiler = nullptr;
  194. crl::time now = 0;
  195. bool paused = false;
  196. bool pausedEmoji = false;
  197. bool pausedSpoiler = false;
  198. bool fullWidthSelection = true;
  199. TextSelection selection;
  200. HighlightInfoRequest *highlight = nullptr;
  201. int elisionHeight = 0;
  202. int elisionLines = 0;
  203. int elisionRemoveFromEnd = 0;
  204. bool elisionBreakEverywhere = false;
  205. // Elision middle works only with elisionLines = 1 and is very limited.
  206. bool elisionMiddle = false;
  207. bool useFullWidth = false; // !(width = min(availableWidth, maxWidth()))
  208. };
  209. class String {
  210. public:
  211. String(int minResizeWidth = kQFixedMax);
  212. String(
  213. const style::TextStyle &st,
  214. const QString &text,
  215. const TextParseOptions &options = kDefaultTextOptions,
  216. int minResizeWidth = kQFixedMax);
  217. String(
  218. const style::TextStyle &st,
  219. const TextWithEntities &textWithEntities,
  220. const TextParseOptions &options = kMarkupTextOptions,
  221. int minResizeWidth = kQFixedMax,
  222. const MarkedContext &context = {});
  223. String(String &&other);
  224. String &operator=(String &&other);
  225. ~String();
  226. [[nodiscard]] int countWidth(
  227. int width,
  228. bool breakEverywhere = false) const;
  229. [[nodiscard]] int countHeight(
  230. int width,
  231. bool breakEverywhere = false) const;
  232. struct LineWidthsOptions {
  233. bool breakEverywhere = false;
  234. int reserve = 0;
  235. };
  236. [[nodiscard]] std::vector<int> countLineWidths(int width) const;
  237. [[nodiscard]] std::vector<int> countLineWidths(
  238. int width,
  239. LineWidthsOptions options) const;
  240. struct DimensionsResult {
  241. int width = 0;
  242. int height = 0;
  243. std::vector<int> lineWidths;
  244. };
  245. struct DimensionsRequest {
  246. bool breakEverywhere = false;
  247. bool lineWidths = false;
  248. int reserve = 0;
  249. };
  250. [[nodiscard]] DimensionsResult countDimensions(
  251. GeometryDescriptor geometry) const;
  252. [[nodiscard]] DimensionsResult countDimensions(
  253. GeometryDescriptor geometry,
  254. DimensionsRequest request) const;
  255. void setText(
  256. const style::TextStyle &st,
  257. const QString &text,
  258. const TextParseOptions &options = kDefaultTextOptions);
  259. void setMarkedText(
  260. const style::TextStyle &st,
  261. const TextWithEntities &textWithEntities,
  262. const TextParseOptions &options = kMarkupTextOptions,
  263. const MarkedContext &context = {});
  264. [[nodiscard]] bool hasLinks() const;
  265. void setLink(uint16 index, const ClickHandlerPtr &lnk);
  266. [[nodiscard]] bool hasSpoilers() const;
  267. void setSpoilerRevealed(bool revealed, anim::type animated);
  268. void setSpoilerLinkFilter(Fn<bool(const ClickContext&)> filter);
  269. [[nodiscard]] bool hasCollapsedBlockquots() const;
  270. [[nodiscard]] bool blockquoteCollapsed(int index) const;
  271. [[nodiscard]] bool blockquoteExpanded(int index) const;
  272. void setBlockquoteExpanded(int index, bool expanded);
  273. void setBlockquoteExpandCallback(
  274. Fn<void(int index, bool expanded)> callback);
  275. [[nodiscard]] bool hasSkipBlock() const;
  276. bool updateSkipBlock(int width, int height);
  277. bool removeSkipBlock();
  278. [[nodiscard]] int maxWidth() const {
  279. return _maxWidth;
  280. }
  281. [[nodiscard]] int minHeight() const {
  282. return _minHeight;
  283. }
  284. [[nodiscard]] int countMaxMonospaceWidth() const;
  285. void draw(QPainter &p, const PaintContext &context) const;
  286. [[nodiscard]] StateResult getState(
  287. QPoint point,
  288. GeometryDescriptor geometry,
  289. StateRequest request = StateRequest()) const;
  290. void draw(Painter &p, int32 left, int32 top, int32 width, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, TextSelection selection = { 0, 0 }, bool fullWidthSelection = true) const;
  291. void drawElided(Painter &p, int32 left, int32 top, int32 width, int32 lines = 1, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, int32 removeFromEnd = 0, bool breakEverywhere = false, TextSelection selection = { 0, 0 }) const;
  292. void drawLeft(Painter &p, int32 left, int32 top, int32 width, int32 outerw, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, TextSelection selection = { 0, 0 }) const;
  293. void drawLeftElided(Painter &p, int32 left, int32 top, int32 width, int32 outerw, int32 lines = 1, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, int32 removeFromEnd = 0, bool breakEverywhere = false, TextSelection selection = { 0, 0 }) const;
  294. void drawRight(Painter &p, int32 right, int32 top, int32 width, int32 outerw, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, TextSelection selection = { 0, 0 }) const;
  295. void drawRightElided(Painter &p, int32 right, int32 top, int32 width, int32 outerw, int32 lines = 1, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, int32 removeFromEnd = 0, bool breakEverywhere = false, TextSelection selection = { 0, 0 }) const;
  296. [[nodiscard]] StateResult getState(QPoint point, int width, StateRequest request = StateRequest()) const;
  297. [[nodiscard]] StateResult getStateLeft(QPoint point, int width, int outerw, StateRequest request = StateRequest()) const;
  298. [[nodiscard]] StateResult getStateElided(QPoint point, int width, StateRequestElided request = StateRequestElided()) const;
  299. [[nodiscard]] StateResult getStateElidedLeft(QPoint point, int width, int outerw, StateRequestElided request = StateRequestElided()) const;
  300. [[nodiscard]] TextSelection adjustSelection(TextSelection selection, TextSelectType selectType) const;
  301. [[nodiscard]] bool isFullSelection(TextSelection selection) const {
  302. return (selection.from == 0) && (selection.to >= _text.size());
  303. }
  304. [[nodiscard]] bool isEmpty() const;
  305. [[nodiscard]] bool isNull() const {
  306. return !_st;
  307. }
  308. [[nodiscard]] int length() const {
  309. return _text.size();
  310. }
  311. [[nodiscard]] QString toString(
  312. TextSelection selection = AllTextSelection) const;
  313. [[nodiscard]] TextWithEntities toTextWithEntities(
  314. TextSelection selection = AllTextSelection) const;
  315. [[nodiscard]] TextForMimeData toTextForMimeData(
  316. TextSelection selection = AllTextSelection) const;
  317. [[nodiscard]] bool hasPersistentAnimation() const;
  318. void unloadPersistentAnimation();
  319. [[nodiscard]] bool isIsolatedEmoji() const;
  320. [[nodiscard]] IsolatedEmoji toIsolatedEmoji() const;
  321. [[nodiscard]] bool isOnlyCustomEmoji() const;
  322. [[nodiscard]] OnlyCustomEmoji toOnlyCustomEmoji() const;
  323. [[nodiscard]] bool hasNotEmojiAndSpaces() const;
  324. [[nodiscard]] const std::vector<Modification> &modifications() const;
  325. [[nodiscard]] const style::TextStyle *style() const {
  326. return _st;
  327. }
  328. [[nodiscard]] int lineHeight() const;
  329. void clear();
  330. private:
  331. class ExtendedWrap : public std::unique_ptr<ExtendedData> {
  332. public:
  333. ExtendedWrap() noexcept;
  334. ExtendedWrap(ExtendedWrap &&other) noexcept;
  335. ExtendedWrap &operator=(ExtendedWrap &&other) noexcept;
  336. ~ExtendedWrap();
  337. ExtendedWrap(
  338. std::unique_ptr<ExtendedData> &&other) noexcept;
  339. ExtendedWrap &operator=(
  340. std::unique_ptr<ExtendedData> &&other) noexcept;
  341. private:
  342. void adjustFrom(const ExtendedWrap *other);
  343. };
  344. [[nodiscard]] not_null<ExtendedData*> ensureExtended();
  345. [[nodiscard]] not_null<QuotesData*> ensureQuotes();
  346. [[nodiscard]] uint16 blockPosition(
  347. std::vector<Block>::const_iterator i,
  348. int fullLengthOverride = -1) const;
  349. [[nodiscard]] uint16 blockEnd(
  350. std::vector<Block>::const_iterator i,
  351. int fullLengthOverride = -1) const;
  352. [[nodiscard]] uint16 blockLength(
  353. std::vector<Block>::const_iterator i,
  354. int fullLengthOverride = -1) const;
  355. [[nodiscard]] QuoteDetails *quoteByIndex(int index) const;
  356. [[nodiscard]] const style::QuoteStyle &quoteStyle(
  357. not_null<QuoteDetails*> quote) const;
  358. [[nodiscard]] QMargins quotePadding(QuoteDetails *quote) const;
  359. [[nodiscard]] int quoteMinWidth(QuoteDetails *quote) const;
  360. [[nodiscard]] const QString &quoteHeaderText(QuoteDetails *quote) const;
  361. // Returns -1 in case there is no limit.
  362. [[nodiscard]] int quoteLinesLimit(QuoteDetails *quote) const;
  363. // Block must be either nullptr or a pointer to a NewlineBlock.
  364. [[nodiscard]] int quoteIndex(const AbstractBlock *block) const;
  365. // Template method for originalText(), originalTextWithEntities().
  366. template <
  367. typename AppendPartCallback,
  368. typename ClickHandlerStartCallback,
  369. typename ClickHandlerFinishCallback,
  370. typename FlagsChangeCallback>
  371. void enumerateText(
  372. TextSelection selection,
  373. AppendPartCallback appendPartCallback,
  374. ClickHandlerStartCallback clickHandlerStartCallback,
  375. ClickHandlerFinishCallback clickHandlerFinishCallback,
  376. FlagsChangeCallback flagsChangeCallback) const;
  377. // Template method for countWidth(), countHeight(), countLineWidths().
  378. // callback(lineWidth, lineBottom) will be called for all lines with:
  379. // QFixed lineWidth, int lineBottom
  380. template <typename Callback>
  381. void enumerateLines(
  382. int w,
  383. bool breakEverywhere,
  384. Callback &&callback) const;
  385. template <typename Callback>
  386. void enumerateLines(
  387. GeometryDescriptor geometry,
  388. Callback &&callback) const;
  389. void insertModifications(int position, int delta);
  390. void removeModificationsAfter(int size);
  391. void recountNaturalSize(
  392. bool initial,
  393. Qt::LayoutDirection optionsDir = Qt::LayoutDirectionAuto);
  394. [[nodiscard]] TextForMimeData toText(
  395. TextSelection selection,
  396. bool composeExpanded,
  397. bool composeEntities) const;
  398. const style::TextStyle *_st = nullptr;
  399. QString _text;
  400. std::vector<Block> _blocks;
  401. std::vector<Word> _words;
  402. ExtendedWrap _extended;
  403. int _minResizeWidth = 0;
  404. int _maxWidth = 0;
  405. int _minHeight = 0;
  406. uint16 _startQuoteIndex = 0;
  407. bool _startParagraphLTR : 1 = false;
  408. bool _startParagraphRTL : 1 = false;
  409. bool _hasCustomEmoji : 1 = false;
  410. bool _isIsolatedEmoji : 1 = false;
  411. bool _isOnlyCustomEmoji : 1 = false;
  412. bool _hasNotEmojiAndSpaces : 1 = false;
  413. bool _skipBlockAddedNewline : 1 = false;
  414. bool _endsWithQuoteOrOtherDirection : 1 = false;
  415. friend class BlockParser;
  416. friend class WordParser;
  417. friend class Renderer;
  418. friend class BidiAlgorithm;
  419. friend class StackEngine;
  420. };
  421. [[nodiscard]] bool IsBad(QChar ch);
  422. [[nodiscard]] bool IsWordSeparator(QChar ch);
  423. [[nodiscard]] bool IsAlmostLinkEnd(QChar ch);
  424. [[nodiscard]] bool IsLinkEnd(QChar ch);
  425. [[nodiscard]] bool IsNewline(QChar ch);
  426. [[nodiscard]] bool IsSpace(QChar ch);
  427. [[nodiscard]] bool IsDiacritic(QChar ch);
  428. [[nodiscard]] bool IsReplacedBySpace(QChar ch);
  429. [[nodiscard]] bool IsTrimmed(QChar ch);
  430. } // namespace Ui::Text
  431. inline TextSelection snapSelection(int from, int to) {
  432. return { static_cast<uint16>(std::clamp(from, 0, 0xFFFF)), static_cast<uint16>(std::clamp(to, 0, 0xFFFF)) };
  433. }
  434. inline TextSelection shiftSelection(TextSelection selection, uint16 byLength) {
  435. return snapSelection(int(selection.from) + byLength, int(selection.to) + byLength);
  436. }
  437. inline TextSelection unshiftSelection(TextSelection selection, uint16 byLength) {
  438. return snapSelection(int(selection.from) - int(byLength), int(selection.to) - int(byLength));
  439. }
  440. inline TextSelection shiftSelection(TextSelection selection, const Ui::Text::String &byText) {
  441. return shiftSelection(selection, byText.length());
  442. }
  443. inline TextSelection unshiftSelection(TextSelection selection, const Ui::Text::String &byText) {
  444. return unshiftSelection(selection, byText.length());
  445. }