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 "XL2dLabel.h"
24 : #include "XL2dFrameContext.h"
25 : #include "XLFrameInfo.h"
26 : #include "XLEventListener.h"
27 : #include "XLDirector.h"
28 : #include "XLFontExtension.h"
29 :
30 : namespace STAPPLER_VERSIONIZED stappler::xenolith::basic2d {
31 :
32 5860 : Label::Selection::~Selection() { }
33 :
34 2930 : bool Label::Selection::init() {
35 2930 : if (!Sprite::init()) {
36 0 : return false;
37 : }
38 :
39 2930 : return true;
40 : }
41 :
42 13906 : void Label::Selection::clear() {
43 13906 : _vertexes.clear();
44 13906 : }
45 :
46 10 : void Label::Selection::emplaceRect(const Rect &rect) {
47 20 : _vertexes.addQuad().setGeometry(
48 10 : Vec4(rect.origin.x, _contentSize.height - rect.origin.y - rect.size.height, 0.0f, 0.0f), rect.size);
49 10 : }
50 :
51 70056 : void Label::Selection::updateColor() {
52 70056 : Sprite::updateColor();
53 70056 : }
54 :
55 10 : void Label::Selection::updateVertexes() {
56 : //_vertexes.clear();
57 10 : }
58 :
59 : template <typename Interface>
60 6430 : static size_t Label_getQuadsCount(const font::TextLayoutData<Interface> *format) {
61 6430 : size_t ret = 0;
62 :
63 6430 : const font::RangeLayoutData *targetRange = nullptr;
64 :
65 28011 : for (auto it = format->begin(); it != format->end(); ++ it) {
66 21559 : if (&(*it.range) != targetRange) {
67 6588 : targetRange = &(*it.range);
68 : }
69 :
70 21559 : const auto start = it.start();
71 21557 : auto end = start + it.count();
72 21562 : if (it.line->start + it.line->count == end) {
73 21447 : const font::CharLayoutData &c = format->chars[end - 1];
74 21448 : if (!chars::isspace(c.charID) && c.charID != char16_t(0x0A)) {
75 6592 : ++ ret;
76 : }
77 21449 : end -= 1;
78 : }
79 :
80 355023 : for (auto charIdx = start; charIdx < end; ++ charIdx) {
81 333442 : const font::CharLayoutData &c = format->chars[charIdx];
82 333425 : if (!chars::isspace(c.charID) && c.charID != char16_t(0x0A) && c.charID != char16_t(0x00AD)) {
83 290380 : ++ ret;
84 : }
85 : }
86 : }
87 :
88 6443 : return ret;
89 : }
90 :
91 296199 : static void Label_pushColorMap(const font::RangeLayoutData &range, Vector<ColorMask> &cMap) {
92 296199 : ColorMask mask = ColorMask::None;
93 296199 : if (!range.colorDirty) {
94 296244 : mask |= ColorMask::Color;
95 : }
96 296192 : if (!range.opacityDirty) {
97 296259 : mask |= ColorMask::A;
98 : }
99 296151 : cMap.push_back(mask);
100 296196 : }
101 :
102 296034 : static void Label_writeTextureQuad(float height, const font::Metrics &m, const font::CharLayoutData &c,
103 : const font::CharShape &l, const font::RangeLayoutData &range, const font::LineLayoutData &line, VertexArray::Quad &quad) {
104 296034 : switch (range.align) {
105 0 : case font::VerticalAlign::Sub:
106 0 : quad.drawChar(m, l, c.pos, height - line.pos + m.descender / 2, range.color, range.decoration, c.face);
107 0 : break;
108 0 : case font::VerticalAlign::Super:
109 0 : quad.drawChar(m, l, c.pos, height - line.pos + m.ascender / 2, range.color, range.decoration, c.face);
110 0 : break;
111 296034 : default:
112 296034 : quad.drawChar(m, l, c.pos, height - line.pos, range.color, range.decoration, c.face);
113 296194 : break;
114 : }
115 296194 : }
116 :
117 : template <typename Interface>
118 6430 : static void Label_writeQuads(VertexArray &vertexes, const font::TextLayoutData<Interface> *format, Vector<ColorMask> &colorMap) {
119 6430 : auto quadsCount = Label_getQuadsCount(format);
120 6443 : colorMap.clear();
121 6440 : colorMap.reserve(quadsCount);
122 :
123 6434 : const font::RangeLayoutData *targetRange = nullptr;
124 6434 : font::Metrics metrics;
125 :
126 6434 : vertexes.clear();
127 :
128 28003 : for (auto it = format->begin(); it != format->end(); ++ it) {
129 21561 : if (it.count() == 0) {
130 0 : continue;
131 : }
132 :
133 21561 : if (&(*it.range) != targetRange) {
134 6590 : targetRange = &(*it.range);
135 6590 : metrics = targetRange->layout->getMetrics();
136 : }
137 :
138 21561 : const auto start = it.start();
139 21561 : auto end = start + it.count();
140 :
141 375595 : for (auto charIdx = start; charIdx < end; ++ charIdx) {
142 354233 : const font::CharLayoutData &c = format->chars[charIdx];
143 354147 : if (!chars::isspace(c.charID) && c.charID != char16_t(0x0A) && c.charID != char16_t(0x00AD)) {
144 :
145 296251 : uint16_t face = 0;
146 296251 : auto ch = targetRange->layout->getChar(c.charID, face);
147 :
148 296379 : if (ch.charID == c.charID) {
149 296433 : auto quad = vertexes.addQuad();
150 296154 : Label_pushColorMap(*it.range, colorMap);
151 296088 : Label_writeTextureQuad(format->height, metrics, c, ch, *it.range, *it.line, quad);
152 : }
153 : }
154 : }
155 :
156 21362 : if (it.line->start + it.line->count == end) {
157 21453 : const font::CharLayoutData &c = format->chars[end - 1];
158 21452 : if (c.charID == char16_t(0x00AD)) {
159 0 : uint16_t face = 0;
160 0 : auto ch = targetRange->layout->getChar('-', face);
161 :
162 0 : if (ch.charID == '-') {
163 0 : auto quad = vertexes.addQuad();
164 0 : Label_pushColorMap(*it.range, colorMap);
165 0 : Label_writeTextureQuad(format->height, metrics, c, ch, *it.range, *it.line, quad);
166 : }
167 : }
168 21453 : end -= 1;
169 : }
170 :
171 21362 : if (it.count() > 0 && it.range->decoration != font::TextDecoration::None) {
172 75 : auto chstart = it.start();
173 75 : auto chend = it.end();
174 75 : while (chstart < chend && chars::isspace(format->chars[chstart].charID)) {
175 0 : ++ chstart;
176 : }
177 :
178 75 : if (chstart == chend) {
179 0 : return;
180 : }
181 :
182 75 : const font::CharLayoutData &firstChar = format->chars[chstart];
183 75 : const font::CharLayoutData &lastChar = format->chars[chend - 1];
184 :
185 75 : auto color = it.range->color;
186 75 : color.a = uint8_t(0.75f * color.a);
187 75 : auto layoutMetrics = it.range->layout->getMetrics();
188 :
189 75 : float offset = 0.0f;
190 75 : switch (it.range->decoration) {
191 0 : case font::TextDecoration::None: break;
192 0 : case font::TextDecoration::Overline:
193 0 : offset = layoutMetrics.height;
194 0 : break;
195 35 : case font::TextDecoration::LineThrough:
196 35 : offset = (layoutMetrics.height * 11.0f) / 24.0f;
197 35 : break;
198 40 : case font::TextDecoration::Underline:
199 40 : offset = layoutMetrics.height / 8.0f;
200 40 : break;
201 : }
202 :
203 75 : const float width = layoutMetrics.height / 16.0f;
204 75 : const float base = floorf(width);
205 75 : const float frac = width - base;
206 :
207 75 : const auto underlineBase = uint16_t(base);
208 75 : const auto underlineX = firstChar.pos;
209 75 : const auto underlineWidth = lastChar.pos + lastChar.advance - firstChar.pos;
210 75 : const auto underlineY = format->height - it.line->pos + offset - underlineBase / 2;
211 75 : const auto underlineHeight = underlineBase;
212 :
213 75 : auto quad = vertexes.addQuad();
214 75 : Label_pushColorMap(*it.range, colorMap);
215 75 : quad.drawUnderlineRect(underlineX, underlineY, underlineWidth, underlineHeight, color);
216 75 : if (frac > 0.1) {
217 0 : color.a *= frac;
218 :
219 0 : auto uquad = vertexes.addQuad();
220 0 : Label_pushColorMap(*it.range, colorMap);
221 0 : uquad.drawUnderlineRect(underlineX, underlineY - 1, underlineWidth, 1, color);
222 : }
223 : }
224 : }
225 : }
226 :
227 6436 : void Label::writeQuads(VertexArray &vertexes, const font::TextLayoutData<memory::StandartInterface> *format, Vector<ColorMask> &colorMap) {
228 6436 : Label_writeQuads(vertexes, format, colorMap);
229 6440 : }
230 :
231 0 : void Label::writeQuads(VertexArray &vertexes, const font::TextLayoutData<memory::PoolInterface> *format, Vector<ColorMask> &colorMap) {
232 0 : Label_writeQuads(vertexes, format, colorMap);
233 0 : }
234 :
235 6434 : Rc<LabelResult> Label::writeResult(TextLayout *format, const Color4F &color) {
236 6434 : auto result = Rc<LabelResult>::alloc();
237 6435 : VertexArray array;
238 6433 : array.init(format->getData()->chars.size() * 4, format->getData()->chars.size() * 6);
239 :
240 6433 : writeQuads(array, format->getData(), result->colorMap);
241 6441 : result->data.transform = Mat4::IDENTITY;
242 6441 : result->data.data = array.pop();
243 12881 : return result;
244 6440 : }
245 :
246 2399 : Label::~Label() {
247 1465 : _format = nullptr;
248 2399 : }
249 :
250 1385 : bool Label::init() {
251 1385 : return init(nullptr);
252 : }
253 :
254 70 : bool Label::init(StringView str) {
255 70 : return init(nullptr, DescriptionStyle(), str, 0.0f, TextAlign::Left);
256 : }
257 :
258 0 : bool Label::init(StringView str, float w, TextAlign a) {
259 0 : return init(nullptr, DescriptionStyle(), str, w, a);
260 : }
261 :
262 1465 : bool Label::init(font::FontController *source, const DescriptionStyle &style,
263 : StringView str, float width, TextAlign alignment) {
264 1465 : if (!Sprite::init()) {
265 0 : return false;
266 : }
267 :
268 1465 : if (!source) {
269 1455 : source = Application::getInstance()->getExtension<font::FontController>();
270 : }
271 :
272 1465 : _source = source;
273 1465 : _style = style;
274 1465 : setNormalized(true);
275 :
276 1465 : setColorMode(core::ColorMode::AlphaChannel);
277 1465 : setRenderingLevel(RenderingLevel::Surface);
278 :
279 1465 : auto el = Rc<EventListener>::create();
280 1465 : el->onEventWithObject(font::FontController::onFontSourceUpdated, source, std::bind(&Label::onFontSourceUpdated, this));
281 :
282 1465 : if (_source->isLoaded()) {
283 1415 : setTexture(Rc<Texture>(_source->getTexture()));
284 : } else {
285 50 : el->onEventWithObject(font::FontController::onLoaded, source, std::bind(&Label::onFontSourceLoaded, this), true);
286 : }
287 :
288 1465 : _listener = addComponent(el);
289 :
290 1465 : _selection = addChild(Rc<Selection>::create());
291 1465 : _selection->setAnchorPoint(Vec2(0.0f, 0.0f));
292 1465 : _selection->setPosition(Vec2(0.0f, 0.0f));
293 1465 : _selection->setColor(Color::BlueGrey_500);
294 1465 : _selection->setOpacity(OpacityValue(64));
295 1465 : _selection->setVisible(false);
296 :
297 1465 : _marked = addChild(Rc<Selection>::create());
298 1465 : _marked->setAnchorPoint(Vec2(0.0f, 0.0f));
299 1465 : _marked->setPosition(Vec2(0.0f, 0.0f));
300 1465 : _marked->setColor(Color::Green_500);
301 1465 : _marked->setOpacity(OpacityValue(64));
302 1465 : _marked->setVisible(false);
303 :
304 1465 : setColor(Color4F(_style.text.color, _style.text.opacity), true);
305 :
306 1465 : setString(str);
307 1465 : setWidth(width);
308 1465 : setAlignment(alignment);
309 :
310 1465 : return true;
311 1465 : }
312 :
313 0 : bool Label::init(const DescriptionStyle &style, StringView str, float w, TextAlign a) {
314 0 : return init(nullptr, style, str, w, a);
315 : }
316 :
317 340 : void Label::tryUpdateLabel() {
318 340 : if (_parent) {
319 340 : updateLabelScale(_parent->getNodeToWorldTransform());
320 : }
321 340 : if (_labelDirty) {
322 20 : updateLabel();
323 : }
324 340 : }
325 :
326 230 : void Label::setStyle(const DescriptionStyle &style) {
327 230 : _style = style;
328 :
329 230 : setColor(Color4F(_style.text.color, _style.text.opacity), true);
330 :
331 230 : setLabelDirty();
332 230 : }
333 :
334 115 : const Label::DescriptionStyle &Label::getStyle() const {
335 115 : return _style;
336 : }
337 :
338 6449 : Rc<LabelDeferredResult> Label::runDeferred(thread::TaskQueue &queue, TextLayout *format, const Color4F &color) {
339 6449 : auto result = new std::promise<Rc<LabelResult>>;
340 6449 : Rc<LabelDeferredResult> ret = Rc<LabelDeferredResult>::create(result->get_future());
341 6449 : queue.perform([queue = Rc<thread::TaskQueue>(&queue), format = Rc<Label::TextLayout>(format), color, ret, result] () mutable {
342 6436 : auto res = Label::writeResult(format, color);
343 6440 : result->set_value(res);
344 :
345 6438 : queue->onMainThread([ret = move(ret), res = move(res), result] () mutable {
346 6449 : ret->handleReady(move(res));
347 6449 : delete result;
348 6449 : }, queue);
349 6448 : }, ret);
350 6449 : return ret;
351 0 : }
352 :
353 7768 : void Label::applyLayout(TextLayout *layout) {
354 7768 : _format = layout;
355 :
356 7768 : if (_format) {
357 6783 : if (_format->empty()) {
358 0 : setContentSize(Size2(0.0f, getFontHeight() / _labelDensity));
359 : } else {
360 6783 : setContentSize(Size2(_format->getWidth() / _labelDensity, _format->getHeight() / _labelDensity));
361 : }
362 :
363 6783 : setSelectionCursor(getSelectionCursor());
364 6783 : setMarkedCursor(getMarkedCursor());
365 :
366 6783 : _labelDirty = false;
367 6783 : _vertexColorDirty = false;
368 6783 : _vertexesDirty = true;
369 : } else {
370 985 : _vertexesDirty = true;
371 : }
372 7768 : }
373 :
374 7788 : void Label::updateLabel() {
375 7788 : if (!_source) {
376 1005 : return;
377 : }
378 :
379 7788 : if (_string16.empty()) {
380 985 : applyLayout(nullptr);
381 985 : setContentSize(Size2(0.0f, getFontHeight() / _labelDensity));
382 985 : return;
383 : }
384 :
385 6803 : auto spec = Rc<font::TextLayout>::alloc(_source, _string16.size(), _compiledStyles.size() + 1);
386 :
387 6803 : _compiledStyles = compileStyle();
388 6803 : _style.text.color = _displayedColor.getColor();
389 6803 : _style.text.opacity = _displayedColor.getOpacity();
390 6803 : _style.text.whiteSpace = font::WhiteSpace::PreWrap;
391 :
392 6803 : if (!updateFormatSpec(spec, _compiledStyles, _labelDensity, _adjustValue)) {
393 20 : return;
394 : }
395 :
396 6783 : applyLayout(spec);
397 6803 : }
398 :
399 3164 : void Label::onContentSizeDirty() {
400 3164 : Sprite::onContentSizeDirty();
401 :
402 3164 : _selection->setContentSize(_contentSize);
403 3164 : _marked->setContentSize(_contentSize);
404 3164 : }
405 :
406 3271 : void Label::onTransformDirty(const Mat4 &parent) {
407 3271 : updateLabelScale(parent);
408 3271 : Sprite::onTransformDirty(parent);
409 3271 : }
410 :
411 4873 : void Label::onGlobalTransformDirty(const Mat4 &parent) {
412 4873 : if (!_transformDirty) {
413 1602 : updateLabelScale(parent);
414 : }
415 :
416 4873 : Sprite::onGlobalTransformDirty(parent);
417 4873 : }
418 :
419 131127 : void Label::updateColor() {
420 131127 : if (_format) {
421 183024 : for (auto &it : _format->getData()->ranges) {
422 92027 : if (!it.colorDirty) {
423 92027 : it.color.r = uint8_t(_displayedColor.r * 255.0f);
424 92027 : it.color.g = uint8_t(_displayedColor.g * 255.0f);
425 92027 : it.color.b = uint8_t(_displayedColor.b * 255.0f);
426 : }
427 92027 : if (!it.opacityDirty) {
428 92027 : it.color.a = uint8_t(_displayedColor.a * 255.0f);
429 : }
430 : }
431 : }
432 131127 : _vertexColorDirty = true;
433 131127 : }
434 :
435 23795 : void Label::updateVertexesColor() {
436 23795 : if (_deferredResult) {
437 20699 : _deferredResult->updateColor(_displayedColor);
438 : } else {
439 3096 : if (!_colorMap.empty()) {
440 0 : _vertexes.updateColorQuads(_displayedColor, _colorMap);
441 : }
442 : }
443 23795 : }
444 :
445 0 : void Label::updateQuadsForeground(font::FontController *controller, TextLayout *format, Vector<ColorMask> &colorMap) {
446 0 : writeQuads(_vertexes, format->getData(), colorMap);
447 0 : }
448 :
449 44873 : bool Label::checkVertexDirty() const {
450 44873 : return _vertexesDirty || _labelDirty;
451 : }
452 :
453 89787 : NodeFlags Label::processParentFlags(FrameInfo &info, NodeFlags parentFlags) {
454 89787 : if (_labelDirty) {
455 6940 : updateLabel();
456 : }
457 :
458 89787 : return Sprite::processParentFlags(info, parentFlags);
459 : }
460 :
461 44873 : void Label::pushCommands(FrameInfo &frame, NodeFlags flags) {
462 44873 : if (_deferred) {
463 44873 : if (!_deferredResult || (_deferredResult->isReady() && _deferredResult->getResult()->data.empty())) {
464 7240 : return;
465 : }
466 :
467 37633 : FrameContextHandle2d *handle = static_cast<FrameContextHandle2d *>(frame.currentContext);
468 :
469 112899 : handle->commands->pushDeferredVertexResult(_deferredResult, frame.viewProjectionStack.back(), frame.modelTransformStack.back(),
470 75266 : _normalized, frame.zPath, _materialId, handle->getCurrentState(), _realRenderingLevel, frame.depthStack.back(), _commandFlags);
471 : } else {
472 0 : Sprite::pushCommands(frame, flags);
473 : }
474 : }
475 :
476 5213 : void Label::updateLabelScale(const Mat4 &parent) {
477 5213 : Vec3 scale;
478 5213 : parent.decompose(&scale, nullptr, nullptr);
479 :
480 5213 : if (_scale.x != 1.f) { scale.x *= _scale.x; }
481 5213 : if (_scale.y != 1.f) { scale.y *= _scale.y; }
482 5213 : if (_scale.z != 1.f) { scale.z *= _scale.z; }
483 :
484 5213 : auto density = std::min(std::min(scale.x, scale.y), scale.z);
485 5213 : if (density != _labelDensity) {
486 0 : _labelDensity = density;
487 0 : setLabelDirty();
488 : }
489 :
490 5213 : if (_labelDirty) {
491 584 : updateLabel();
492 : }
493 5213 : }
494 :
495 166 : void Label::setAdjustValue(uint8_t val) {
496 166 : if (_adjustValue != val) {
497 142 : _adjustValue = val;
498 142 : setLabelDirty();
499 : }
500 166 : }
501 12 : uint8_t Label::getAdjustValue() const {
502 12 : return _adjustValue;
503 : }
504 :
505 12 : bool Label::isOverflow() const {
506 12 : if (_format) {
507 12 : return _format->isOverflow();
508 : }
509 0 : return false;
510 : }
511 :
512 12 : size_t Label::getCharsCount() const {
513 12 : return _format?_format->getData()->chars.size():0;
514 : }
515 12 : size_t Label::getLinesCount() const {
516 12 : return _format?_format->getData()->lines.size():0;
517 : }
518 24 : Label::LineLayout Label::getLine(uint32_t num) const {
519 24 : if (_format) {
520 24 : if (num < _format->getData()->lines.size()) {
521 12 : return _format->getData()->lines[num];
522 : }
523 : }
524 12 : return LineLayout();
525 : }
526 :
527 995 : uint16_t Label::getFontHeight() const {
528 995 : auto l = _source->getLayout(_style.font);
529 995 : if (l.get()) {
530 995 : return l->getFontHeight();
531 : }
532 0 : return 0;
533 995 : }
534 :
535 6655 : void Label::updateVertexes() {
536 6655 : if (!_source) {
537 0 : return;
538 : }
539 :
540 6655 : if (_labelDirty) {
541 244 : updateLabel();
542 : }
543 :
544 6655 : if (!_format || _format->getData()->chars.size() == 0 || _string16.empty()) {
545 206 : _vertexes.clear();
546 206 : _labelDirty = false;
547 206 : _deferredResult = nullptr;
548 206 : return;
549 : }
550 :
551 13056 : for (auto &it : _format->getData()->ranges) {
552 6607 : auto dep = _source->addTextureChars(it.layout, SpanView<font::CharLayoutData>(_format->getData()->chars, it.start, it.count));
553 6607 : if (dep) {
554 962 : emplace_ordered(_pendingDependencies, move(dep));
555 : }
556 6607 : }
557 :
558 6449 : if (_deferred) {
559 6449 : _deferredResult = runDeferred(*_director->getApplication()->getQueue(), _format, _displayedColor);
560 6449 : _vertexes.clear();
561 6449 : _vertexColorDirty = false;
562 : } else {
563 0 : _deferredResult = nullptr;
564 0 : updateQuadsForeground(_source, _format, _colorMap);
565 0 : _vertexColorDirty = true;
566 : }
567 : }
568 :
569 0 : void Label::onFontSourceUpdated() {
570 0 : setLabelDirty();
571 0 : _vertexesDirty = true;
572 0 : }
573 :
574 50 : void Label::onFontSourceLoaded() {
575 50 : if (_source) {
576 50 : setTexture(Rc<Texture>(_source->getTexture()));
577 50 : _vertexesDirty = true;
578 50 : setLabelDirty();
579 : }
580 50 : }
581 :
582 0 : void Label::onLayoutUpdated() {
583 0 : _labelDirty = false;
584 0 : }
585 :
586 250 : Vec2 Label::getCursorPosition(uint32_t charIndex, bool front) const {
587 250 : if (_format) {
588 230 : auto d = _format->getData();
589 230 : if (charIndex < d->chars.size()) {
590 50 : auto &c = d->chars[charIndex];
591 50 : auto line = _format->getLine(charIndex);
592 50 : if (line) {
593 50 : return Vec2( (front ? c.pos : c.pos + c.advance) / _labelDensity, _contentSize.height - line->pos / _labelDensity);
594 : }
595 180 : } else if (charIndex >= d->chars.size() && charIndex != 0) {
596 180 : auto &c = d->chars.back();
597 180 : auto &l = d->lines.back();
598 180 : if (c.charID == char16_t(0x0A)) {
599 0 : return getCursorOrigin();
600 : } else {
601 180 : return Vec2( (c.pos + c.advance) / _labelDensity, _contentSize.height - l.pos / _labelDensity);
602 : }
603 : }
604 : }
605 :
606 20 : return Vec2::ZERO;
607 : }
608 :
609 12 : Vec2 Label::getCursorOrigin() const {
610 12 : switch (_alignment) {
611 12 : case TextAlign::Left:
612 : case TextAlign::Justify:
613 12 : return Vec2( 0.0f / _labelDensity, _contentSize.height - _format->getHeight() / _labelDensity);
614 : break;
615 0 : case TextAlign::Center:
616 0 : return Vec2( _contentSize.width * 0.5f / _labelDensity, _contentSize.height - _format->getHeight() / _labelDensity);
617 : break;
618 0 : case TextAlign::Right:
619 0 : return Vec2( _contentSize.width / _labelDensity, _contentSize.height - _format->getHeight() / _labelDensity);
620 : break;
621 : }
622 0 : return Vec2::ZERO;
623 : }
624 :
625 12 : Pair<uint32_t, bool> Label::getCharIndex(const Vec2 &pos, font::CharSelectMode mode) const {
626 12 : if (!_format) {
627 0 : return pair(0, false);
628 : }
629 :
630 12 : auto ret = _format->getChar(pos.x * _labelDensity, _format->getHeight() - pos.y * _labelDensity, mode);
631 12 : if (ret.first == maxOf<uint32_t>()) {
632 0 : return pair(maxOf<uint32_t>(), false);
633 12 : } else if (ret.second == font::CharSelectMode::Prefix) {
634 0 : return pair(ret.first, false);
635 : } else {
636 12 : return pair(ret.first, true);
637 : }
638 : }
639 :
640 12 : core::TextCursor Label::selectWord(uint32_t chIdx) const {
641 12 : auto ret = _format->selectWord(chIdx);
642 12 : return core::TextCursor(ret.first, ret.second);
643 : }
644 :
645 12 : float Label::getMaxLineX() const {
646 12 : if (_format) {
647 12 : return _format->getMaxAdvance() / _labelDensity;
648 : }
649 0 : return 0.0f;
650 : }
651 :
652 0 : void Label::setDeferred(bool val) {
653 0 : if (val != _deferred) {
654 0 : _deferred = val;
655 0 : _vertexesDirty = true;
656 : }
657 0 : }
658 :
659 7123 : void Label::setSelectionCursor(core::TextCursor c) {
660 7123 : _selection->clear();
661 7123 : _selection->setVisible(c != core::TextCursor::InvalidCursor && c.length > 0);
662 7123 : if (_format && c != core::TextCursor::InvalidCursor && c.length > 0) {
663 10 : auto rects = _format->getLabelRects(c.start, c.start + c.length - 1, _labelDensity);
664 20 : for (auto &rect: rects) {
665 10 : _selection->emplaceRect(rect);
666 : }
667 10 : _selection->updateColor();
668 10 : }
669 7123 : _selection->setTextCursor(c);
670 7123 : }
671 :
672 6783 : core::TextCursor Label::getSelectionCursor() const {
673 6783 : return _selection->getTextCursor();
674 : }
675 :
676 5779 : void Label::setSelectionColor(const Color4F &c) {
677 5779 : _selection->setColor(c, false);
678 5779 : }
679 :
680 12 : Color4F Label::getSelectionColor() const {
681 12 : return _selection->getColor();
682 : }
683 :
684 6783 : void Label::setMarkedCursor(core::TextCursor c) {
685 6783 : _marked->clear();
686 6783 : _marked->setVisible(c != core::TextCursor::InvalidCursor && c.length > 0);
687 6783 : if (c != core::TextCursor::InvalidCursor && c.length > 0) {
688 0 : auto rects = _format->getLabelRects(c.start, c.start + c.length, _labelDensity);
689 0 : for (auto &rect: rects) {
690 0 : _marked->emplaceRect(rect);
691 : }
692 0 : _marked->updateColor();
693 0 : }
694 6783 : _marked->setTextCursor(c);
695 6783 : }
696 :
697 6783 : core::TextCursor Label::getMarkedCursor() const {
698 6783 : return _marked->getTextCursor();
699 : }
700 :
701 6 : void Label::setMarkedColor(const Color4F &c) {
702 6 : _marked->setColor(c, false);
703 6 : }
704 :
705 6 : Color4F Label::getMarkedColor() const {
706 6 : return _marked->getColor();
707 : }
708 :
709 :
710 12898 : LabelDeferredResult::~LabelDeferredResult() {
711 6449 : if (_future) {
712 0 : delete _future;
713 0 : _future = nullptr;
714 : }
715 12898 : }
716 :
717 6449 : bool LabelDeferredResult::init(std::future<Rc<LabelResult>> &&future) {
718 6449 : _future = new std::future<Rc<LabelResult>>(move(future));
719 6449 : return true;
720 : }
721 :
722 37623 : SpanView<TransformVertexData> LabelDeferredResult::getData() {
723 37623 : std::unique_lock<Mutex> lock(_mutex);
724 37623 : if (_future) {
725 1229 : _result = _future->get();
726 1229 : delete _future;
727 1229 : _future = nullptr;
728 1229 : DeferredVertexResult::handleReady();
729 : }
730 75246 : return makeSpanView(&_result->data, 1);
731 37623 : }
732 :
733 6449 : void LabelDeferredResult::handleReady(Rc<LabelResult> &&res) {
734 6449 : std::unique_lock<Mutex> lock(_mutex);
735 6449 : if (_future) {
736 5220 : delete _future;
737 5220 : _future = nullptr;
738 : }
739 6449 : _result = move(res);
740 6449 : DeferredVertexResult::handleReady();
741 6449 : }
742 :
743 20699 : void LabelDeferredResult::updateColor(const Color4F &color) {
744 20699 : getResult(); // ensure rendering was complete
745 :
746 20699 : std::unique_lock<Mutex> lock(_mutex);
747 20699 : if (_result) {
748 20699 : VertexArray arr;
749 20699 : arr.init(_result->data.data);
750 20699 : arr.updateColorQuads(color, _result->colorMap);
751 20699 : _result->data.data = arr.pop();
752 20699 : }
753 20699 : }
754 :
755 51883 : Rc<VertexData> LabelDeferredResult::getResult() const {
756 51883 : std::unique_lock<Mutex> lock(_mutex);
757 103766 : return _result->data.data;
758 51883 : }
759 :
760 : }
|