Line data Source code
1 : /**
2 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
3 :
4 : Permission is hereby granted, free of charge, to any person obtaining a copy
5 : of this software and associated documentation files (the "Software"), to deal
6 : in the Software without restriction, including without limitation the rights
7 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 : copies of the Software, and to permit persons to whom the Software is
9 : furnished to do so, subject to the following conditions:
10 :
11 : The above copyright notice and this permission notice shall be included in
12 : all copies or substantial portions of the Software.
13 :
14 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 : THE SOFTWARE.
21 : **/
22 :
23 : #include "XLFontLabelBase.h"
24 : #include "XLFontLocale.h"
25 :
26 : namespace STAPPLER_VERSIONIZED stappler::xenolith::font {
27 :
28 16448 : TextLayout::~TextLayout() { }
29 :
30 8224 : TextLayout::TextLayout(FontController *h, size_t nchars, size_t nranges)
31 8224 : : _handle(h) {
32 8224 : if (nchars) {
33 8094 : _data.chars.reserve(nchars);
34 8094 : _data.lines.reserve(nchars / 60);
35 : }
36 8224 : if (nranges) {
37 8094 : _data.ranges.reserve(nranges);
38 : }
39 8224 : }
40 :
41 260 : void TextLayout::reserve(size_t nchars, size_t nranges) {
42 260 : _data.reserve(nchars, nranges);
43 260 : }
44 :
45 8094 : void TextLayout::clear() {
46 8094 : _data.chars.clear();
47 8094 : _data.lines.clear();
48 8094 : _data.ranges.clear();
49 8094 : _data.overflow = false;
50 8094 : }
51 :
52 8848 : Rc<FontFaceSet> TextLayout::getLayout(const FontParameters &f) {
53 8848 : auto font = _handle->getLayout(f);
54 8848 : if (font) {
55 8824 : _fonts.emplace(font);
56 : }
57 8848 : return font;
58 0 : }
59 :
60 130 : RangeLineIterator TextLayout::begin() const {
61 130 : return _data.begin();
62 : }
63 :
64 1153 : RangeLineIterator TextLayout::end() const {
65 1153 : return _data.end();
66 : }
67 :
68 130 : auto TextLayout::str(bool filter) const -> WideString {
69 130 : WideString ret; ret.reserve(_data.chars.size());
70 130 : _data.str([&] (char16_t ch) {
71 11390 : ret.push_back(ch);
72 11390 : }, filter);
73 130 : return ret;
74 0 : }
75 :
76 130 : auto TextLayout::str(uint32_t s_start, uint32_t s_end, size_t maxWords, bool ellipsis, bool filter) const -> WideString {
77 130 : WideString ret; ret.reserve(s_end - s_start + 2);
78 130 : _data.str([&] (char16_t ch) {
79 55 : ret.push_back(ch);
80 55 : }, s_start, s_end, maxWords, ellipsis, filter);
81 130 : return ret;
82 0 : }
83 :
84 532 : Pair<uint32_t, CharSelectMode> TextLayout::getChar(int32_t x, int32_t y, CharSelectMode m) const {
85 532 : return _data.getChar(x, y, m);
86 : }
87 :
88 60 : const LineLayoutData *TextLayout::getLine(uint32_t charIndex) const {
89 60 : return _data.getLine(charIndex);
90 : }
91 :
92 130 : uint32_t TextLayout::getLineForChar(uint32_t charIndex) const {
93 130 : return _data.getLineForChar(charIndex);
94 : }
95 :
96 142 : Pair<uint32_t, uint32_t> TextLayout::selectWord(uint32_t origin) const {
97 142 : return _data.selectWord(origin);
98 : }
99 :
100 130 : geom::Rect TextLayout::getLineRect(uint32_t lineId, float density, const geom::Vec2 &origin) const {
101 130 : return _data.getLineRect(lineId, density, origin);
102 : }
103 :
104 1023 : geom::Rect TextLayout::getLineRect(const LineLayoutData &line, float density, const geom::Vec2 &origin) const {
105 1023 : return _data.getLineRect(line, density, origin);
106 : }
107 :
108 12 : auto TextLayout::getLabelRects(uint32_t firstCharId, uint32_t lastCharId, float density, const geom::Vec2 &origin, const geom::Padding &p) const -> Vector<geom::Rect> {
109 12 : Vector<geom::Rect> ret;
110 12 : getLabelRects(ret, firstCharId, lastCharId, density, origin, p);
111 12 : return ret;
112 0 : }
113 :
114 12 : void TextLayout::getLabelRects(Vector<geom::Rect> &ret, uint32_t firstCharId, uint32_t lastCharId, float density, const geom::Vec2 &origin, const geom::Padding &p) const {
115 12 : _data.getLabelRects([&] (const geom::Rect &rect) {
116 12 : ret.push_back(rect);
117 12 : }, firstCharId, lastCharId, density, origin, p);
118 12 : }
119 :
120 3898 : LabelBase::DescriptionStyle::DescriptionStyle() {
121 3898 : font.fontFamily = StringView("default");
122 3898 : font.fontSize = FontSize(14);
123 3898 : text.opacity = 222;
124 3898 : text.color = Color3B::BLACK;
125 3898 : text.whiteSpace = font::WhiteSpace::PreWrap;
126 3898 : }
127 :
128 130 : String LabelBase::DescriptionStyle::getConfigName(bool caps) const {
129 130 : return font.getConfigName<Interface>(caps);
130 : }
131 :
132 10148 : LabelBase::DescriptionStyle LabelBase::DescriptionStyle::merge(
133 : const Rc<font::FontController> &source, const Style &style) const {
134 10148 : DescriptionStyle ret(*this);
135 12204 : for (auto &it : style.params) {
136 2056 : switch (it.name) {
137 130 : case Style::Name::TextTransform: ret.text.textTransform = it.value.textTransform; break;
138 254 : case Style::Name::TextDecoration: ret.text.textDecoration = it.value.textDecoration; break;
139 130 : case Style::Name::Hyphens: ret.text.hyphens = it.value.hyphens; break;
140 130 : case Style::Name::VerticalAlign: ret.text.verticalAlign = it.value.verticalAlign; break;
141 130 : case Style::Name::Color: ret.text.color = it.value.color; ret.colorDirty = true; break;
142 130 : case Style::Name::Opacity: ret.text.opacity = it.value.opacity; ret.opacityDirty = true; break;
143 254 : case Style::Name::FontSize: ret.font.fontSize = it.value.fontSize; break;
144 130 : case Style::Name::FontStyle: ret.font.fontStyle = it.value.fontStyle; break;
145 254 : case Style::Name::FontWeight: ret.font.fontWeight = it.value.fontWeight; break;
146 254 : case Style::Name::FontStretch: ret.font.fontStretch = it.value.fontStretch; break;
147 130 : case Style::Name::FontFamily: ret.font.fontFamily = source->getFamilyName(it.value.fontFamily); break;
148 130 : case Style::Name::FontGrade: ret.font.fontGrade = it.value.fontGrade; break;
149 : }
150 : }
151 10148 : return ret;
152 : }
153 :
154 260 : bool LabelBase::DescriptionStyle::operator == (const DescriptionStyle &style) const {
155 260 : return font == style.font && text == style.text && colorDirty == style.colorDirty && opacityDirty == style.opacityDirty;
156 : }
157 130 : bool LabelBase::DescriptionStyle::operator != (const DescriptionStyle &style) const {
158 130 : return !((*this) == style);
159 : }
160 :
161 1858 : LabelBase::Style::Value::Value() {
162 1858 : memset(this, 0, sizeof(Value));
163 1858 : }
164 :
165 754 : void LabelBase::Style::set(const Param &p, bool force) {
166 754 : if (force) {
167 754 : auto it = params.begin();
168 1007 : while(it != params.end()) {
169 253 : if (it->name == p.name) {
170 129 : it = params.erase(it);
171 : } else {
172 124 : it ++;
173 : }
174 : }
175 : }
176 754 : params.push_back(p);
177 754 : }
178 :
179 496 : void LabelBase::Style::merge(const Style &style) {
180 992 : for (auto &it : style.params) {
181 496 : set(it, true);
182 : }
183 496 : }
184 :
185 496 : void LabelBase::Style::clear() {
186 496 : params.clear();
187 496 : }
188 :
189 130 : LabelBase::ExternalFormatter::~ExternalFormatter() { }
190 :
191 259 : bool LabelBase::ExternalFormatter::init(font::FontController *s, float w, float density) {
192 259 : if (!s) {
193 129 : return false;
194 : }
195 :
196 130 : _density = density;
197 130 : _spec = Rc<TextLayout>::alloc(s);
198 130 : _formatter.setFontCallback([this] (const FontParameters &f) {
199 260 : return _spec->getLayout(f);
200 : });
201 130 : _formatter.reset(_spec->getData());
202 130 : if (w > 0.0f) {
203 130 : _formatter.setWidth(static_cast<uint16_t>(roundf(w * _density)));
204 : }
205 130 : return true;
206 : }
207 :
208 130 : void LabelBase::ExternalFormatter::setLineHeightAbsolute(float value) {
209 130 : _formatter.setLineHeightAbsolute(static_cast<uint16_t>(value * _density));
210 130 : }
211 :
212 130 : void LabelBase::ExternalFormatter::setLineHeightRelative(float value) {
213 130 : _formatter.setLineHeightRelative(value);
214 130 : }
215 :
216 130 : void LabelBase::ExternalFormatter::reserve(size_t chars, size_t ranges) {
217 130 : if (_spec) {
218 130 : _spec->reserve(chars, ranges);
219 : }
220 130 : }
221 :
222 130 : void LabelBase::ExternalFormatter::addString(const DescriptionStyle &style, const StringView &str, bool localized) {
223 130 : addString(style, string::toUtf16<Interface>(str), localized);
224 130 : }
225 260 : void LabelBase::ExternalFormatter::addString(const DescriptionStyle &style, const WideStringView &str, bool localized) {
226 260 : if (!begin) {
227 130 : _formatter.begin(0, 0);
228 130 : begin = true;
229 : }
230 260 : if (localized && locale::hasLocaleTags(str)) {
231 129 : auto u16str = locale::resolveLocaleTags(str);
232 129 : _formatter.read(style.font, style.text, u16str.data(), u16str.size());
233 129 : } else {
234 131 : _formatter.read(style.font, style.text, str.data(), str.size());
235 : }
236 260 : }
237 :
238 259 : Size2 LabelBase::ExternalFormatter::finalize() {
239 259 : if (_spec) {
240 130 : _formatter.finalize();
241 130 : return Size2(_spec->getWidth() / _density, _spec->getHeight() / _density);
242 : }
243 129 : return Size2();
244 : }
245 :
246 258 : WideString LabelBase::getLocalizedString(const StringView &s) {
247 258 : return getLocalizedString(string::toUtf16<Interface>(s));
248 : }
249 258 : WideString LabelBase::getLocalizedString(const WideStringView &s) {
250 258 : if (locale::hasLocaleTags(s)) {
251 129 : return locale::resolveLocaleTags(s);
252 : }
253 129 : return s.str<Interface>();
254 : }
255 :
256 393 : float LabelBase::getStringWidth(font::FontController *source, const DescriptionStyle &style,
257 : const StringView &str, bool localized) {
258 393 : return getStringWidth(source, style, string::toUtf16<Interface>(str), localized);
259 : }
260 :
261 393 : float LabelBase::getStringWidth(font::FontController *source, const DescriptionStyle &style,
262 : const WideStringView &str, bool localized) {
263 393 : if (!source) {
264 129 : return 0.0f;
265 : }
266 :
267 264 : TextLayoutData<Interface> spec;
268 :
269 264 : font::Formatter fmt([source = Rc<font::FontController>(source)] (const FontParameters &f) {
270 264 : return source->getLayout(f);
271 528 : }, &spec);
272 264 : fmt.begin(0, 0);
273 :
274 264 : if (localized && locale::hasLocaleTags(str)) {
275 129 : auto u16str = locale::resolveLocaleTags(str);
276 129 : spec.reserve(u16str.size());
277 129 : fmt.read(style.font, style.text, u16str.data(), u16str.size());
278 129 : } else {
279 135 : spec.reserve(str.size());
280 135 : fmt.read(style.font, style.text, str.data(), str.size());
281 : }
282 :
283 264 : fmt.finalize();
284 264 : return spec.width / style.font.density;
285 264 : }
286 :
287 516 : Size2 LabelBase::getLabelSize(font::FontController *source, const DescriptionStyle &style,
288 : const StringView &s, float w, bool localized) {
289 516 : return getLabelSize(source, style, string::toUtf16<Interface>(s), w, localized);
290 : }
291 :
292 516 : Size2 LabelBase::getLabelSize(font::FontController *source, const DescriptionStyle &style,
293 : const WideStringView &str, float w, bool localized) {
294 516 : if (str.empty()) {
295 129 : return Size2(0.0f, 0.0f);
296 : }
297 :
298 387 : if (!source) {
299 129 : return Size2(0.0f, 0.0f);
300 : }
301 :
302 258 : TextLayoutData<Interface> spec;
303 :
304 258 : font::Formatter fmt([source = Rc<font::FontController>(source)] (const FontParameters &f) {
305 258 : return source->getLayout(f);
306 516 : }, &spec);
307 258 : fmt.setWidth(static_cast<uint16_t>(roundf(w * style.font.density)));
308 258 : fmt.begin(0, 0);
309 :
310 258 : if (localized && locale::hasLocaleTags(str)) {
311 129 : auto u16str = locale::resolveLocaleTags(str);
312 129 : spec.reserve(u16str.size());
313 129 : fmt.read(style.font, style.text, u16str.data(), u16str.size());
314 129 : } else {
315 129 : spec.reserve(str.size());
316 129 : fmt.read(style.font, style.text, str.data(), str.size());
317 : }
318 :
319 258 : fmt.finalize();
320 258 : return Size2(spec.maxAdvance / style.font.density, spec.height / style.font.density);
321 258 : }
322 :
323 2135 : void LabelBase::setAlignment(TextAlign alignment) {
324 2135 : if (_alignment != alignment) {
325 324 : _alignment = alignment;
326 324 : setLabelDirty();
327 : }
328 2135 : }
329 :
330 129 : TextAlign LabelBase::getAlignment() const {
331 129 : return _alignment;
332 : }
333 :
334 2147 : void LabelBase::setWidth(float width) {
335 2147 : if (_width != width) {
336 288 : _width = width;
337 288 : setLabelDirty();
338 : }
339 2147 : }
340 :
341 129 : float LabelBase::getWidth() const {
342 129 : return _width;
343 : }
344 :
345 8 : void LabelBase::setTextIndent(float value) {
346 8 : if (_textIndent != value) {
347 8 : _textIndent = value;
348 8 : setLabelDirty();
349 : }
350 8 : }
351 129 : float LabelBase::getTextIndent() const {
352 129 : return _textIndent;
353 : }
354 :
355 158 : void LabelBase::setTextTransform(const TextTransform &value) {
356 158 : if (value != _style.text.textTransform) {
357 62 : _style.text.textTransform = value;
358 62 : setLabelDirty();
359 : }
360 158 : }
361 :
362 129 : TextTransform LabelBase::getTextTransform() const {
363 129 : return _style.text.textTransform;
364 : }
365 :
366 7 : void LabelBase::setTextDecoration(const TextDecoration &value) {
367 7 : if (value != _style.text.textDecoration) {
368 7 : _style.text.textDecoration = value;
369 7 : setLabelDirty();
370 : }
371 7 : }
372 14 : TextDecoration LabelBase::getTextDecoration() const {
373 14 : return _style.text.textDecoration;
374 : }
375 :
376 8 : void LabelBase::setHyphens(const Hyphens &value) {
377 8 : if (value != _style.text.hyphens) {
378 8 : _style.text.hyphens = value;
379 8 : setLabelDirty();
380 : }
381 8 : }
382 129 : Hyphens LabelBase::getHyphens() const {
383 129 : return _style.text.hyphens;
384 : }
385 :
386 8 : void LabelBase::setVerticalAlign(const VerticalAlign &value) {
387 8 : if (value != _style.text.verticalAlign) {
388 8 : _style.text.verticalAlign = value;
389 8 : setLabelDirty();
390 : }
391 8 : }
392 129 : VerticalAlign LabelBase::getVerticalAlign() const {
393 129 : return _style.text.verticalAlign;
394 : }
395 :
396 1557 : void LabelBase::setFontSize(const uint16_t &value) {
397 1557 : setFontSize(FontSize(value));
398 1557 : }
399 :
400 2373 : void LabelBase::setFontSize(const FontSize &value) {
401 2373 : auto realTargetValue = value.scale(_labelDensity).get();
402 2373 : auto realSourceValue = _style.font.fontSize.scale(_labelDensity).get();
403 :
404 2373 : if (realTargetValue != realSourceValue) {
405 1019 : _style.font.fontSize = value;
406 1019 : setLabelDirty();
407 : }
408 2373 : }
409 12 : FontSize LabelBase::getFontSize() const {
410 12 : return _style.font.fontSize;
411 : }
412 :
413 393 : void LabelBase::setFontStyle(const FontStyle &value) {
414 393 : if (value != _style.font.fontStyle) {
415 369 : _style.font.fontStyle = value;
416 369 : setLabelDirty();
417 : }
418 393 : }
419 129 : FontStyle LabelBase::getFontStyle() const {
420 129 : return _style.font.fontStyle;
421 : }
422 :
423 1035 : void LabelBase::setFontWeight(const FontWeight &value) {
424 1035 : if (value != _style.font.fontWeight) {
425 843 : _style.font.fontWeight = value;
426 843 : setLabelDirty();
427 : }
428 1035 : }
429 129 : FontWeight LabelBase::getFontWeight() const {
430 129 : return _style.font.fontWeight;
431 : }
432 :
433 381 : void LabelBase::setFontStretch(const FontStretch &value) {
434 381 : if (value != _style.font.fontStretch) {
435 381 : _style.font.fontStretch = value;
436 381 : setLabelDirty();
437 : }
438 381 : }
439 129 : FontStretch LabelBase::getFontStretch() const {
440 129 : return _style.font.fontStretch;
441 : }
442 :
443 381 : void LabelBase::setFontGrade(const FontGrade &value) {
444 381 : if (value != _style.font.fontGrade) {
445 381 : _style.font.fontGrade = value;
446 381 : setLabelDirty();
447 : }
448 381 : }
449 :
450 129 : FontGrade LabelBase::getFontGrade() const {
451 129 : return _style.font.fontGrade;
452 : }
453 :
454 648 : void LabelBase::setFontFamily(const StringView &value) {
455 648 : if (value != _style.font.fontFamily) {
456 648 : _fontFamilyStorage = value.str<Interface>();
457 648 : _style.font.fontFamily = _fontFamilyStorage;
458 648 : setLabelDirty();
459 : }
460 648 : }
461 129 : StringView LabelBase::getFontFamily() const {
462 129 : return _style.font.fontFamily;
463 : }
464 :
465 8 : void LabelBase::setLineHeightAbsolute(float value) {
466 8 : if (!_isLineHeightAbsolute || _lineHeight != value) {
467 8 : _isLineHeightAbsolute = true;
468 8 : _lineHeight = value;
469 8 : setLabelDirty();
470 : }
471 8 : }
472 8 : void LabelBase::setLineHeightRelative(float value) {
473 8 : if (_isLineHeightAbsolute || _lineHeight != value) {
474 8 : _isLineHeightAbsolute = false;
475 8 : _lineHeight = value;
476 8 : setLabelDirty();
477 : }
478 8 : }
479 129 : float LabelBase::getLineHeight() const {
480 129 : return _lineHeight;
481 : }
482 129 : bool LabelBase::isLineHeightAbsolute() const {
483 129 : return _isLineHeightAbsolute;
484 : }
485 :
486 36 : void LabelBase::setMaxWidth(float value) {
487 36 : if (_maxWidth != value) {
488 12 : _maxWidth = value;
489 12 : setLabelDirty();
490 : }
491 36 : }
492 129 : float LabelBase::getMaxWidth() const {
493 129 : return _maxWidth;
494 : }
495 :
496 192 : void LabelBase::setMaxLines(size_t value) {
497 192 : if (_maxLines != value) {
498 168 : _maxLines = value;
499 168 : setLabelDirty();
500 : }
501 192 : }
502 129 : size_t LabelBase::getMaxLines() const {
503 129 : return _maxLines;
504 : }
505 :
506 8 : void LabelBase::setMaxChars(size_t value) {
507 8 : if (_maxChars != value) {
508 8 : _maxChars = value;
509 8 : setLabelDirty();
510 : }
511 8 : }
512 396 : size_t LabelBase::getMaxChars() const {
513 396 : return _maxChars;
514 : }
515 :
516 8 : void LabelBase::setOpticalAlignment(bool value) {
517 8 : if (_opticalAlignment != value) {
518 8 : _opticalAlignment = value;
519 8 : setLabelDirty();
520 : }
521 8 : }
522 129 : bool LabelBase::isOpticallyAligned() const {
523 129 : return _opticalAlignment;
524 : }
525 :
526 8 : void LabelBase::setFillerChar(char16_t c) {
527 8 : if (c != _fillerChar) {
528 8 : _fillerChar = c;
529 8 : setLabelDirty();
530 : }
531 8 : }
532 129 : char16_t LabelBase::getFillerChar() const {
533 129 : return _fillerChar;
534 : }
535 :
536 492 : void LabelBase::setLocaleEnabled(bool value) {
537 492 : if (_localeEnabled != value) {
538 471 : _localeEnabled = value;
539 471 : setLabelDirty();
540 : }
541 492 : }
542 129 : bool LabelBase::isLocaleEnabled() const {
543 129 : return _localeEnabled;
544 : }
545 :
546 48 : void LabelBase::setPersistentLayout(bool value) {
547 48 : if (_persistentLayout != value) {
548 48 : _persistentLayout = value;
549 48 : setLabelDirty();
550 : }
551 48 : }
552 :
553 129 : bool LabelBase::isPersistentLayout() const {
554 129 : return _persistentLayout;
555 : }
556 :
557 117609 : void LabelBase::setString(const StringView &newString) {
558 117609 : if (newString == _string8) {
559 109129 : return;
560 : }
561 :
562 8480 : _string8 = newString.str<Interface>();
563 8480 : _string16 = string::toUtf16<Interface>(newString);
564 8480 : if (!_localeEnabled && locale::hasLocaleTagsFast(_string16)) {
565 8 : setLocaleEnabled(true);
566 : }
567 8480 : setLabelDirty();
568 8480 : clearStyles();
569 : }
570 :
571 417 : void LabelBase::setString(const WideStringView &newString) {
572 417 : if (newString == _string16) {
573 14 : return;
574 : }
575 :
576 403 : _string8 = string::toUtf8<Interface>(newString);
577 403 : _string16 = newString.str<Interface>();
578 403 : if (!_localeEnabled && locale::hasLocaleTagsFast(_string16)) {
579 7 : setLocaleEnabled(true);
580 : }
581 403 : setLabelDirty();
582 403 : clearStyles();
583 : }
584 :
585 7 : void LabelBase::setLocalizedString(size_t idx) {
586 7 : setString(localeIndex(idx));
587 7 : setLocaleEnabled(true);
588 7 : }
589 :
590 129 : WideStringView LabelBase::getString() const {
591 129 : return _string16;
592 : }
593 :
594 129 : StringView LabelBase::getString8() const {
595 129 : return _string8;
596 : }
597 :
598 7 : void LabelBase::erase16(size_t start, size_t len) {
599 7 : if (start >= _string16.length()) {
600 0 : return;
601 : }
602 :
603 7 : _string16.erase(start, len);
604 7 : _string8 = string::toUtf8<Interface>(_string16);
605 7 : setLabelDirty();
606 : }
607 :
608 7 : void LabelBase::erase8(size_t start, size_t len) {
609 7 : if (start >= _string8.length()) {
610 0 : return;
611 : }
612 :
613 7 : _string8.erase(start, len);
614 7 : _string16 = string::toUtf16<Interface>(_string8);
615 7 : setLabelDirty();
616 : }
617 :
618 10 : void LabelBase::append(const StringView &value) {
619 10 : _string8.append(value.str<Interface>());
620 10 : _string16 = string::toUtf16<Interface>(_string8);
621 10 : setLabelDirty();
622 10 : }
623 18 : void LabelBase::append(const WideStringView &value) {
624 18 : _string16.append(value.str<Interface>());
625 18 : _string8 = string::toUtf8<Interface>(_string16);
626 18 : setLabelDirty();
627 18 : }
628 :
629 10 : void LabelBase::prepend(const StringView &value) {
630 10 : _string8 = toString(value, _string8);
631 10 : _string16 = string::toUtf16<Interface>(_string8);
632 10 : setLabelDirty();
633 10 : }
634 10 : void LabelBase::prepend(const WideStringView &value) {
635 10 : _string16 = value.str<Interface>() + _string16;
636 10 : _string8 = string::toUtf8<Interface>(_string16);
637 10 : setLabelDirty();
638 10 : }
639 :
640 40 : void LabelBase::setTextRangeStyle(size_t start, size_t length, Style &&style) {
641 40 : if (length > 0) {
642 40 : _styles.emplace_back(start, length, std::move(style));
643 40 : setLabelDirty();
644 : }
645 40 : }
646 :
647 10 : void LabelBase::appendTextWithStyle(const StringView &str, Style &&style) {
648 10 : auto start = _string16.length();
649 10 : append(str);
650 10 : setTextRangeStyle(start, _string16.length() - start, std::move(style));
651 10 : }
652 :
653 10 : void LabelBase::appendTextWithStyle(const WideStringView &str, Style &&style) {
654 10 : auto start = _string16.length();
655 10 : append(str);
656 10 : setTextRangeStyle(start, str.size(), std::move(style));
657 10 : }
658 :
659 10 : void LabelBase::prependTextWithStyle(const StringView &str, Style &&style) {
660 10 : auto len = _string16.length();
661 10 : prepend(str);
662 10 : setTextRangeStyle(0, _string16.length() - len, std::move(style));
663 10 : }
664 10 : void LabelBase::prependTextWithStyle(const WideStringView &str, Style &&style) {
665 10 : prepend(str);
666 10 : setTextRangeStyle(0, str.size(), std::move(style));
667 10 : }
668 :
669 8883 : void LabelBase::clearStyles() {
670 8883 : _styles.clear();
671 8883 : setLabelDirty();
672 8883 : }
673 :
674 136 : const LabelBase::StyleVec &LabelBase::getStyles() const {
675 136 : return _styles;
676 : }
677 :
678 129 : const LabelBase::StyleVec &LabelBase::getCompiledStyles() const {
679 129 : return _compiledStyles;
680 : }
681 :
682 7 : void LabelBase::setStyles(StyleVec &&vec) {
683 7 : _styles = std::move(vec);
684 7 : setLabelDirty();
685 7 : }
686 7 : void LabelBase::setStyles(const StyleVec &vec) {
687 7 : _styles = vec;
688 7 : setLabelDirty();
689 7 : }
690 :
691 8094 : bool LabelBase::updateFormatSpec(TextLayout *format, const StyleVec &compiledStyles, float density, uint8_t _adjustValue) {
692 8094 : bool success = true;
693 8094 : uint16_t adjustValue = maxOf<uint16_t>();
694 :
695 : do {
696 8094 : if (adjustValue == maxOf<uint16_t>()) {
697 8094 : adjustValue = 0;
698 : } else {
699 0 : adjustValue += 1;
700 : }
701 :
702 8094 : format->clear();
703 :
704 8588 : font::Formatter formatter([format] (const FontParameters &f) {
705 8588 : return format->getLayout(f);
706 8094 : }, format->getData());
707 8094 : formatter.setWidth(static_cast<uint16_t>(roundf(_width * density)));
708 8094 : formatter.setTextAlignment(_alignment);
709 8094 : formatter.setMaxWidth(static_cast<uint16_t>(roundf(_maxWidth * density)));
710 8094 : formatter.setMaxLines(_maxLines);
711 8094 : formatter.setOpticalAlignment(_opticalAlignment);
712 8094 : formatter.setFillerChar(_fillerChar);
713 8094 : formatter.setEmplaceAllChars(_emplaceAllChars);
714 :
715 8094 : if (_lineHeight != 0.0f) {
716 129 : if (_isLineHeightAbsolute) {
717 0 : formatter.setLineHeightAbsolute(static_cast<uint16_t>(_lineHeight * density));
718 : } else {
719 129 : formatter.setLineHeightRelative(_lineHeight);
720 : }
721 : }
722 :
723 8094 : formatter.begin(static_cast<uint16_t>(roundf(_textIndent * density)));
724 :
725 8094 : size_t drawedChars = 0;
726 16658 : for (auto &it : compiledStyles) {
727 8588 : DescriptionStyle params = _style.merge(dynamic_cast<font::FontController *>(format->getController()), it.style);
728 8588 : specializeStyle(params, density);
729 8588 : if (adjustValue > 0) {
730 0 : params.font.fontSize -= FontSize(adjustValue);
731 : }
732 :
733 8588 : auto start = _string16.c_str() + it.start;
734 8588 : auto len = it.length;
735 :
736 8588 : if (_localeEnabled && hasLocaleTags(WideStringView(start, len))) {
737 136 : WideString str(resolveLocaleTags(WideStringView(start, len)));
738 :
739 136 : start = str.c_str();
740 136 : len = str.length();
741 :
742 136 : if (_maxChars > 0 && drawedChars + len > _maxChars) {
743 0 : len = _maxChars - drawedChars;
744 : }
745 136 : if (!formatter.read(params.font, params.text, start, len)) {
746 0 : drawedChars += len;
747 0 : success = false;
748 0 : break;
749 : }
750 136 : } else {
751 8452 : if (_maxChars > 0 && drawedChars + len > _maxChars) {
752 0 : len = _maxChars - drawedChars;
753 : }
754 8452 : if (!formatter.read(params.font, params.text, start, len)) {
755 24 : drawedChars += len;
756 24 : success = false;
757 24 : break;
758 : }
759 : }
760 :
761 8564 : if (!format->getData()->ranges.empty()) {
762 8564 : format->getData()->ranges.back().colorDirty = params.colorDirty;
763 8564 : format->getData()->ranges.back().opacityDirty = params.opacityDirty;
764 : }
765 : }
766 8094 : formatter.finalize();
767 8094 : } while (format->isOverflow() && adjustValue < _adjustValue);
768 :
769 8094 : return success;
770 : }
771 :
772 1751 : LabelBase::~LabelBase() { }
773 :
774 129 : bool LabelBase::isLabelDirty() const {
775 129 : return _labelDirty;
776 : }
777 :
778 8712 : static void LabelBase_dumpStyle(LabelBase::StyleVec &ret, size_t pos, size_t len, const LabelBase::Style &style) {
779 8712 : if (len > 0) {
780 8588 : ret.emplace_back(pos, len, style);
781 : }
782 8712 : }
783 :
784 8094 : LabelBase::StyleVec LabelBase::compileStyle() const {
785 8094 : size_t pos = 0;
786 8094 : size_t max = _string16.length();
787 :
788 8094 : StyleVec ret;
789 8094 : StyleVec vec = _styles;
790 :
791 8094 : Style compiledStyle;
792 :
793 8094 : size_t dumpPos = 0;
794 :
795 441657 : for (pos = 0; pos < max; pos ++) {
796 433563 : auto it = vec.begin();
797 :
798 433563 : bool cleaned = false;
799 : // check for endings
800 451569 : while(it != vec.end()) {
801 18006 : if (it->start + it->length <= pos) {
802 496 : LabelBase_dumpStyle(ret, dumpPos, pos - dumpPos, compiledStyle);
803 496 : compiledStyle.clear();
804 496 : cleaned = true;
805 496 : dumpPos = pos;
806 496 : it = vec.erase(it);
807 : } else {
808 17510 : it ++;
809 : }
810 : }
811 :
812 : // checks for continuations and starts
813 433563 : it = vec.begin();
814 451073 : while(it != vec.end()) {
815 17510 : if (it->start == pos) {
816 496 : if (dumpPos != pos) {
817 122 : LabelBase_dumpStyle(ret, dumpPos, pos - dumpPos, compiledStyle);
818 122 : dumpPos = pos;
819 : }
820 496 : compiledStyle.merge(it->style);
821 17014 : } else if (it->start <= pos && it->start + it->length > pos) {
822 4960 : if (cleaned) {
823 0 : compiledStyle.merge(it->style);
824 : }
825 : }
826 17510 : it ++;
827 : }
828 : }
829 :
830 8094 : LabelBase_dumpStyle(ret, dumpPos, pos - dumpPos, compiledStyle);
831 :
832 16188 : return ret;
833 8094 : }
834 :
835 786 : bool LabelBase::hasLocaleTags(const WideStringView &str) const {
836 786 : return locale::hasLocaleTags(str);
837 : }
838 :
839 265 : WideString LabelBase::resolveLocaleTags(const WideStringView &str) const {
840 265 : return locale::resolveLocaleTags(str);
841 : }
842 :
843 8588 : void LabelBase::specializeStyle(DescriptionStyle &style, float density) const {
844 8588 : style.font.density = density;
845 8588 : style.font.persistent = _persistentLayout;
846 8588 : }
847 :
848 23425 : void LabelBase::setLabelDirty() {
849 23425 : _labelDirty = true;
850 23425 : }
851 :
852 : }
|