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