HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
attributed_glyph_line.hpp
1// Copyright Take Vos 2020.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "attributed_glyph.hpp"
8#include <vector>
9#include <optional>
10
11namespace tt {
12
15 using iterator = vector_type::iterator;
16 using const_iterator = vector_type::const_iterator;
17 using value_type = vector_type::value_type;
18
19 vector_type line;
20 float width;
21 float ascender;
22 float descender;
23 float lineGap;
24 float capHeight;
25 float xHeight;
26 float y;
27
30 attributed_glyph_line(iterator first, iterator last) noexcept :
31 line(), width(0.0f), ascender(0.0f), descender(0.0f), lineGap(0.0f), capHeight(0.0f), xHeight(0.0f)
32 {
33 tt_axiom(std::distance(first, last) > 0);
34
35 line.reserve(std::distance(first, last));
36 std::move(first, last, std::back_inserter(line));
37 calculateLineMetrics();
38 }
39
40 [[nodiscard]] bool shouldWrap(float maximum_width) noexcept {
41 tt_axiom(std::ssize(line) >= 1);
42 return
43 width > maximum_width &&
44 std::ssize(line) >= (line.back().isParagraphSeparator() ? 3 : 2);
45 }
46
47 [[nodiscard]] attributed_glyph_line wrap(float maximum_width) noexcept {
48 tt_axiom(shouldWrap(maximum_width));
49
50 auto word_end = line.begin();
51 auto line_width = 0.0f;
52 auto line_valid_width = 0.0f;
53
54 auto i = line.begin();
55 for (; i != line.end(); ++i) {
56 line_width += i->metrics.advance.x();
57 if (i->isVisible()) {
58 line_valid_width = line_width;
59 }
60
61 if (line_valid_width > maximum_width) {
62 // Found position where to wrap.
63 break;
64
65 } else if (i->isWhiteSpace()) {
66 // Include the whitespace in the word, as it should belong at the end of the line.
67 word_end = i + 1;
68 }
69 }
70
71 auto split_position =
72 (word_end != line.begin()) ? word_end : // Wrap at word boundary
73 (i != line.begin()) ? i : // Wrap at character boundary
74 i + 1; // Include at least one character.
75
76 auto reset_of_line = attributed_glyph_line(split_position, line.end());
77 line.erase(split_position, line.cend());
78 calculateLineMetrics();
79 return reset_of_line;
80 }
81
82 [[nodiscard]] aarect boundingBox() const noexcept {
83 tt_axiom(std::ssize(line) >= 1);
84
85 ttlet p0 = f32x4::point(
86 line.front().position.x(),
87 line.front().position.y() - descender
88 );
89
90 ttlet p3 = f32x4::point(
91 line.back().position.x() + line.back().metrics.advance.x(),
92 line.back().position.y() + ascender
93 );
94
95 return aarect::p0p3(p0, p3);
96 }
97
98 [[nodiscard]] bool contains(f32x4 coordinate) const noexcept {
99 return boundingBox().contains(coordinate);
100 }
101
102 [[nodiscard]] const_iterator find(f32x4 coordinate) const noexcept {
103 auto bbox = boundingBox();
104
105 if (coordinate.y() < bbox.y() || coordinate.y() > bbox.p3().y()) {
106 return cend();
107 }
108
109 if (coordinate.x() < bbox.x()) {
110 return cbegin();
111 }
112
113 if (coordinate.x() > bbox.p3().x()) {
114 return cend() - 1;
115 }
116
117 return std::lower_bound(cbegin(), cend(), coordinate.x(), [](ttlet &a, ttlet &b) {
118 return (a.position.x() + a.metrics.advance.x()) < b;
119 });
120 }
121
122 [[nodiscard]] size_t size() const noexcept { return line.size(); }
123
124 [[nodiscard]] iterator begin() noexcept { return line.begin(); }
125 [[nodiscard]] const_iterator begin() const noexcept { return line.cbegin(); }
126 [[nodiscard]] const_iterator cbegin() const noexcept { return line.cbegin(); }
127
128 [[nodiscard]] iterator end() noexcept { return line.end(); }
129 [[nodiscard]] const_iterator end() const noexcept { return line.cend(); }
130 [[nodiscard]] const_iterator cend() const noexcept { return line.cend(); }
131
132 void positionGlyphs(f32x4 position) noexcept {
133 tt_axiom(position.is_point());
134 y = position.y();
135 for (auto &&g: line) {
136 g.position = position;
137 position += g.metrics.advance;
138 }
139 }
140
141private:
142 void calculateLineMetrics() noexcept {
143 ascender = 0.0f;
144 descender = 0.0f;
145 lineGap = 0.0f;
146 capHeight = 0.0f;
147 xHeight = 0.0f;
148
149 auto totalWidth = 0.0f;
150 auto validWidth = 0.0f;
151 for (ttlet &g: line) {
152 totalWidth += g.metrics.advance.x();
153 ascender = std::max(ascender, g.metrics.ascender);
154 descender = std::max(descender, g.metrics.descender);
155 lineGap = std::max(lineGap, g.metrics.lineGap);
156 capHeight += g.metrics.capHeight;
157 xHeight += g.metrics.xHeight;
158
159 if (g.isVisible()) {
160 // Don't include trailing whitespace in the width.
161 validWidth = totalWidth;
162 }
163 }
164 capHeight /= narrow_cast<float>(std::ssize(line));
165 xHeight /= narrow_cast<float>(std::ssize(line));
166
167 width = validWidth;
168 }
169
170};
171
172}
static axis_aligned_rectangle p0p3(numeric_array< float, 4 > const &v) noexcept
Create axis_aligned_rectangle from packed p0p3 coordinates.
Definition aarect.hpp:86
bool contains(numeric_array< T, 4 > const &rhs) const noexcept
Check if a 2D coordinate is inside the rectangle.
Definition aarect.hpp:300
static constexpr numeric_array point() noexcept
Get a point at the origin.
Definition numeric_array.hpp:90
Definition attributed_glyph_line.hpp:13
attributed_glyph_line(iterator first, iterator last) noexcept
This constructor will move the data from first to last.
Definition attributed_glyph_line.hpp:30
T back(T... args)
T back_inserter(T... args)
T begin(T... args)
T distance(T... args)
T end(T... args)
T erase(T... args)
T front(T... args)
T lower_bound(T... args)
T max(T... args)
T move(T... args)
T reserve(T... args)
T size(T... args)