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 9 #include "ui/style/style_core.h" 10 #include "ui/emoji_config.h" 11 12 #include <private/qfixed_p.h> 13 14 namespace Ui { 15 namespace Text { 16 17 enum TextBlockType { 18 TextBlockTNewline = 0x01, 19 TextBlockTText = 0x02, 20 TextBlockTEmoji = 0x03, 21 TextBlockTSkip = 0x04, 22 }; 23 24 enum TextBlockFlags { 25 TextBlockFBold = 0x01, 26 TextBlockFItalic = 0x02, 27 TextBlockFUnderline = 0x04, 28 TextBlockFStrikeOut = 0x08, 29 TextBlockFTilde = 0x10, // tilde fix in OpenSans 30 TextBlockFSemibold = 0x20, 31 TextBlockFCode = 0x40, 32 TextBlockFPre = 0x80, 33 }; 34 35 class AbstractBlock { 36 public: from()37 uint16 from() const { 38 return _from; 39 } width()40 int width() const { 41 return _width.toInt(); 42 } rpadding()43 int rpadding() const { 44 return _rpadding.toInt(); 45 } f_width()46 QFixed f_width() const { 47 return _width; 48 } f_rpadding()49 QFixed f_rpadding() const { 50 return _rpadding; 51 } 52 53 // Should be virtual, but optimized throught type() call. 54 QFixed f_rbearing() const; 55 lnkIndex()56 uint16 lnkIndex() const { 57 return (_flags >> 12) & 0xFFFF; 58 } setLnkIndex(uint16 lnkIndex)59 void setLnkIndex(uint16 lnkIndex) { 60 _flags = (_flags & ~(0xFFFF << 12)) | (lnkIndex << 12); 61 } 62 type()63 TextBlockType type() const { 64 return TextBlockType((_flags >> 8) & 0x0F); 65 } flags()66 int32 flags() const { 67 return (_flags & 0xFF); 68 } 69 70 protected: AbstractBlock(const style::font & font,const QString & str,uint16 from,uint16 length,uchar flags,uint16 lnkIndex)71 AbstractBlock( 72 const style::font &font, 73 const QString &str, 74 uint16 from, 75 uint16 length, 76 uchar flags, 77 uint16 lnkIndex) 78 : _from(from) 79 , _flags((flags & 0xFF) | ((lnkIndex & 0xFFFF) << 12)) { 80 } 81 82 uint16 _from = 0; 83 84 uint32 _flags = 0; // 4 bits empty, 16 bits lnkIndex, 4 bits type, 8 bits flags 85 86 QFixed _width = 0; 87 88 // Right padding: spaces after the last content of the block (like a word). 89 // This holds spaces after the end of the block, for example a text ending 90 // with a space before a link has started. If text block has a leading spaces 91 // (for example a text block after a link block) it is prepended with an empty 92 // word that holds those spaces as a right padding. 93 QFixed _rpadding = 0; 94 95 }; 96 97 class NewlineBlock final : public AbstractBlock { 98 public: NewlineBlock(const style::font & font,const QString & str,uint16 from,uint16 length,uchar flags,uint16 lnkIndex)99 NewlineBlock( 100 const style::font &font, 101 const QString &str, 102 uint16 from, 103 uint16 length, 104 uchar flags, 105 uint16 lnkIndex) 106 : AbstractBlock(font, str, from, length, flags, lnkIndex) { 107 _flags |= ((TextBlockTNewline & 0x0F) << 8); 108 } 109 nextDirection()110 Qt::LayoutDirection nextDirection() const { 111 return _nextDir; 112 } 113 114 private: 115 Qt::LayoutDirection _nextDir = Qt::LayoutDirectionAuto; 116 117 friend class String; 118 friend class Parser; 119 friend class Renderer; 120 121 }; 122 123 class TextWord final { 124 public: 125 TextWord() = default; 126 TextWord(uint16 from, QFixed width, QFixed rbearing, QFixed rpadding = 0) _from(from)127 : _from(from) 128 , _rbearing((rbearing.value() > 0x7FFF) 129 ? 0x7FFF 130 : (rbearing.value() < -0x7FFF ? -0x7FFF : rbearing.value())) 131 , _width(width) 132 , _rpadding(rpadding) { 133 } from()134 uint16 from() const { 135 return _from; 136 } f_rbearing()137 QFixed f_rbearing() const { 138 return QFixed::fromFixed(_rbearing); 139 } f_width()140 QFixed f_width() const { 141 return _width; 142 } f_rpadding()143 QFixed f_rpadding() const { 144 return _rpadding; 145 } add_rpadding(QFixed padding)146 void add_rpadding(QFixed padding) { 147 _rpadding += padding; 148 } 149 150 private: 151 uint16 _from = 0; 152 int16 _rbearing = 0; 153 QFixed _width, _rpadding; 154 155 }; 156 157 class TextBlock final : public AbstractBlock { 158 public: 159 TextBlock( 160 const style::font &font, 161 const QString &str, 162 QFixed minResizeWidth, 163 uint16 from, 164 uint16 length, 165 uchar flags, 166 uint16 lnkIndex); 167 168 private: real_f_rbearing()169 QFixed real_f_rbearing() const { 170 return _words.isEmpty() ? 0 : _words.back().f_rbearing(); 171 } 172 173 QVector<TextWord> _words; 174 175 friend class String; 176 friend class Parser; 177 friend class Renderer; 178 friend class BlockParser; 179 friend class AbstractBlock; 180 181 }; 182 183 class EmojiBlock final : public AbstractBlock { 184 public: 185 EmojiBlock( 186 const style::font &font, 187 const QString &str, 188 uint16 from, 189 uint16 length, 190 uchar flags, 191 uint16 lnkIndex, 192 EmojiPtr emoji); 193 194 private: 195 EmojiPtr _emoji = nullptr; 196 197 friend class String; 198 friend class Parser; 199 friend class Renderer; 200 201 }; 202 203 class SkipBlock final : public AbstractBlock { 204 public: SkipBlock(const style::font & font,const QString & str,uint16 from,int32 w,int32 h,uint16 lnkIndex)205 SkipBlock( 206 const style::font &font, 207 const QString &str, 208 uint16 from, 209 int32 w, 210 int32 h, 211 uint16 lnkIndex) 212 : AbstractBlock(font, str, from, 1, 0, lnkIndex) 213 , _height(h) { 214 _flags |= ((TextBlockTSkip & 0x0F) << 8); 215 _width = w; 216 } 217 height()218 int height() const { 219 return _height; 220 } 221 222 private: 223 int _height = 0; 224 225 friend class String; 226 friend class Parser; 227 friend class Renderer; 228 229 }; 230 231 class Block final { 232 public: Block()233 Block() { 234 Unexpected("Should not be called."); 235 } Block(const Block & other)236 Block(const Block &other) { 237 switch (other->type()) { 238 case TextBlockTNewline: 239 emplace<NewlineBlock>(other.unsafe<NewlineBlock>()); 240 break; 241 case TextBlockTText: 242 emplace<TextBlock>(other.unsafe<TextBlock>()); 243 break; 244 case TextBlockTEmoji: 245 emplace<EmojiBlock>(other.unsafe<EmojiBlock>()); 246 break; 247 case TextBlockTSkip: 248 emplace<SkipBlock>(other.unsafe<SkipBlock>()); 249 break; 250 default: 251 Unexpected("Bad text block type in Block(const Block&)."); 252 } 253 } Block(Block && other)254 Block(Block &&other) { 255 switch (other->type()) { 256 case TextBlockTNewline: 257 emplace<NewlineBlock>(std::move(other.unsafe<NewlineBlock>())); 258 break; 259 case TextBlockTText: 260 emplace<TextBlock>(std::move(other.unsafe<TextBlock>())); 261 break; 262 case TextBlockTEmoji: 263 emplace<EmojiBlock>(std::move(other.unsafe<EmojiBlock>())); 264 break; 265 case TextBlockTSkip: 266 emplace<SkipBlock>(std::move(other.unsafe<SkipBlock>())); 267 break; 268 default: 269 Unexpected("Bad text block type in Block(Block&&)."); 270 } 271 } 272 Block &operator=(const Block &other) { 273 if (&other == this) { 274 return *this; 275 } 276 destroy(); 277 switch (other->type()) { 278 case TextBlockTNewline: 279 emplace<NewlineBlock>(other.unsafe<NewlineBlock>()); 280 break; 281 case TextBlockTText: 282 emplace<TextBlock>(other.unsafe<TextBlock>()); 283 break; 284 case TextBlockTEmoji: 285 emplace<EmojiBlock>(other.unsafe<EmojiBlock>()); 286 break; 287 case TextBlockTSkip: 288 emplace<SkipBlock>(other.unsafe<SkipBlock>()); 289 break; 290 default: 291 Unexpected("Bad text block type in operator=(const Block&)."); 292 } 293 return *this; 294 } 295 Block &operator=(Block &&other) { 296 if (&other == this) { 297 return *this; 298 } 299 destroy(); 300 switch (other->type()) { 301 case TextBlockTNewline: 302 emplace<NewlineBlock>(std::move(other.unsafe<NewlineBlock>())); 303 break; 304 case TextBlockTText: 305 emplace<TextBlock>(std::move(other.unsafe<TextBlock>())); 306 break; 307 case TextBlockTEmoji: 308 emplace<EmojiBlock>(std::move(other.unsafe<EmojiBlock>())); 309 break; 310 case TextBlockTSkip: 311 emplace<SkipBlock>(std::move(other.unsafe<SkipBlock>())); 312 break; 313 default: 314 Unexpected("Bad text block type in operator=(Block&&)."); 315 } 316 return *this; 317 } ~Block()318 ~Block() { 319 destroy(); 320 } 321 Newline(const style::font & font,const QString & str,uint16 from,uint16 length,uchar flags,uint16 lnkIndex)322 [[nodiscard]] static Block Newline( 323 const style::font &font, 324 const QString &str, 325 uint16 from, 326 uint16 length, 327 uchar flags, 328 uint16 lnkIndex) { 329 return New<NewlineBlock>(font, str, from, length, flags, lnkIndex); 330 } 331 Text(const style::font & font,const QString & str,QFixed minResizeWidth,uint16 from,uint16 length,uchar flags,uint16 lnkIndex)332 [[nodiscard]] static Block Text( 333 const style::font &font, 334 const QString &str, 335 QFixed minResizeWidth, 336 uint16 from, 337 uint16 length, 338 uchar flags, 339 uint16 lnkIndex) { 340 return New<TextBlock>( 341 font, 342 str, 343 minResizeWidth, 344 from, 345 length, 346 flags, 347 lnkIndex); 348 } 349 Emoji(const style::font & font,const QString & str,uint16 from,uint16 length,uchar flags,uint16 lnkIndex,EmojiPtr emoji)350 [[nodiscard]] static Block Emoji( 351 const style::font &font, 352 const QString &str, 353 uint16 from, 354 uint16 length, 355 uchar flags, 356 uint16 lnkIndex, 357 EmojiPtr emoji) { 358 return New<EmojiBlock>( 359 font, 360 str, 361 from, 362 length, 363 flags, 364 lnkIndex, 365 emoji); 366 } 367 Skip(const style::font & font,const QString & str,uint16 from,int32 w,int32 h,uint16 lnkIndex)368 [[nodiscard]] static Block Skip( 369 const style::font &font, 370 const QString &str, 371 uint16 from, 372 int32 w, 373 int32 h, 374 uint16 lnkIndex) { 375 return New<SkipBlock>(font, str, from, w, h, lnkIndex); 376 } 377 378 template <typename FinalBlock> unsafe()379 [[nodiscard]] FinalBlock &unsafe() { 380 return *reinterpret_cast<FinalBlock*>(&_data); 381 } 382 383 template <typename FinalBlock> unsafe()384 [[nodiscard]] const FinalBlock &unsafe() const { 385 return *reinterpret_cast<const FinalBlock*>(&_data); 386 } 387 get()388 [[nodiscard]] AbstractBlock *get() { 389 return &unsafe<AbstractBlock>(); 390 } 391 get()392 [[nodiscard]] const AbstractBlock *get() const { 393 return &unsafe<AbstractBlock>(); 394 } 395 396 [[nodiscard]] AbstractBlock *operator->() { 397 return get(); 398 } 399 400 [[nodiscard]] const AbstractBlock *operator->() const { 401 return get(); 402 } 403 404 [[nodiscard]] AbstractBlock &operator*() { 405 return *get(); 406 } 407 408 [[nodiscard]] const AbstractBlock &operator*() const { 409 return *get(); 410 } 411 412 private: 413 struct Tag { 414 }; 415 Block(const Tag &)416 explicit Block(const Tag &) { 417 } 418 419 template <typename FinalType, typename ...Args> New(Args &&...args)420 [[nodiscard]] static Block New(Args &&...args) { 421 auto result = Block(Tag{}); 422 result.emplace<FinalType>(std::forward<Args>(args)...); 423 return result; 424 } 425 426 template <typename FinalType, typename ...Args> emplace(Args &&...args)427 void emplace(Args &&...args) { 428 new (&_data) FinalType(std::forward<Args>(args)...); 429 } 430 destroy()431 void destroy() { 432 switch (get()->type()) { 433 case TextBlockTNewline: 434 unsafe<NewlineBlock>().~NewlineBlock(); 435 break; 436 case TextBlockTText: 437 unsafe<TextBlock>().~TextBlock(); 438 break; 439 case TextBlockTEmoji: 440 unsafe<EmojiBlock>().~EmojiBlock(); 441 break; 442 case TextBlockTSkip: 443 unsafe<SkipBlock>().~SkipBlock(); 444 break; 445 default: 446 Unexpected("Bad text block type in Block(Block&&)."); 447 } 448 } 449 450 static_assert(sizeof(NewlineBlock) <= sizeof(TextBlock)); 451 static_assert(alignof(NewlineBlock) <= alignof(void*)); 452 static_assert(sizeof(EmojiBlock) <= sizeof(TextBlock)); 453 static_assert(alignof(EmojiBlock) <= alignof(void*)); 454 static_assert(sizeof(SkipBlock) <= sizeof(TextBlock)); 455 static_assert(alignof(SkipBlock) <= alignof(void*)); 456 457 std::aligned_storage_t<sizeof(TextBlock), alignof(void*)> _data; 458 459 }; 460 461 } // namespace Text 462 } // namespace Ui 463