AUI Framework  master
Cross-platform module-based framework for developing C++20 desktop applications
ATextBase.h
1/*
2 * AUI Framework - Declarative UI toolkit for modern C++20
3 * Copyright (C) 2020-2024 Alex2772 and Contributors
4 *
5 * SPDX-License-Identifier: MPL-2.0
6 *
7 * This Source Code Form is subject to the terms of the Mozilla Public
8 * License, v. 2.0. If a copy of the MPL was not distributed with this
9 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 */
11
12#pragma once
13
14
15#include <AUI/Util/AWordWrappingEngine.h>
16#include "AViewContainer.h"
17#include "AUI/Font/IFontView.h"
18#include <initializer_list>
19#include <variant>
20#include <AUI/Enum/WordBreak.h>
21
22namespace aui::detail {
24 public:
25 virtual size_t getCharacterCount() = 0;
26 virtual glm::ivec2 getPosByIndex(size_t characterIndex) = 0;
27 virtual void appendTo(AString& dst) = 0;
28 virtual void erase(size_t begin, AOptional<size_t> end) {}
29
31 using HitTestResult = std::variant<std::nullopt_t, size_t, StopLineScanningHint>;
32 virtual HitTestResult hitTest(glm::ivec2 position) {
33 return std::nullopt;
34 }
35 };
36
37 class CharEntry: public TextBaseEntry {
38 private:
39 IFontView* mText;
40 char32_t mChar;
41 glm::ivec2 mPosition;
42
43 public:
44 CharEntry(IFontView* text, char32_t ch)
45 : mText(text), mChar(ch) {}
46
47 glm::ivec2 getSize() override {
48 return { mText->getFontStyle().getCharacter(mChar).advanceX, mText->getFontStyle().size };
49 }
50
51 void setPosition(glm::ivec2 position) override {
52 mPosition = position;
53 }
54
55 const glm::ivec2& getPosition() const {
56 return mPosition;
57 }
58
59 char32_t getChar() const {
60 return mChar;
61 }
62
63 size_t getCharacterCount() override {
64 return 1;
65 }
66
67 glm::ivec2 getPosByIndex(size_t characterIndex) override {
68 return mPosition + glm::ivec2{characterIndex * mText->getFontStyle().getCharacter(mChar).advanceX, 0};
69 }
70
71 void appendTo(AString& dst) override {
72 dst += mChar;
73 }
74 };
75 class WordEntry: public TextBaseEntry {
76 protected:
77 IFontView* mText;
78 AString mWord;
79 glm::ivec2 mPosition{};
80
81 public:
83 : mText(text), mWord(std::move(word)){}
84
85 glm::ivec2 getSize() override {
86 return { mText->getFontStyle().getWidth(mWord), mText->getFontStyle().size };
87 }
88
89 const AString& getWord() const {
90 return mWord;
91 }
92
93 AString& getWord() {
94 return mWord;
95 }
96
97 [[nodiscard]]
98 glm::ivec2 getPosition() const {
99 return mPosition;
100 }
101
102 void setPosition(glm::ivec2 position) override {
103 TextBaseEntry::setPosition(position);
104 mPosition = position;
105 }
106
107 size_t getCharacterCount() override {
108 return mWord.size();
109 }
110
111 glm::ivec2 getPosByIndex(size_t characterIndex) override {
112 return mPosition + glm::ivec2{mText->getFontStyle().getWidth(mWord.begin(), mWord.begin() + long(characterIndex)), 0};
113 }
114
115 void appendTo(AString& dst) override {
116 dst += mWord;
117 }
118
119 void erase(size_t begin, AOptional<size_t> end) override {
120 mWord.erase(mWord.begin() + long(begin), mWord.begin() + long(end.valueOr(mWord.length())));
121 }
122 };
123
125 private:
126 IFontView* mText;
127
128 public:
129 WhitespaceEntry(IFontView* text) : mText(text) {}
130
131 glm::ivec2 getSize() override {
132 return { mText->getFontStyle().getSpaceWidth(), mText->getFontStyle().size };
133 }
134
135 bool escapesEdges() override {
136 return true;
137 }
138
139 ~WhitespaceEntry() override = default;
140
141 size_t getCharacterCount() override {
142 return 1;
143 }
144
145 glm::ivec2 getPosByIndex(size_t characterIndex) override {
146 throw AException("unimplemented");
147 }
148
149 void appendTo(AString& dst) override {
150 dst += ' ';
151 }
152 };
153
155 private:
156 IFontView* mText;
157
158 public:
159 NextLineEntry(IFontView* text) : mText(text) {}
160
161 bool forcesNextLine() const override {
162 return true;
163 }
164
165 glm::ivec2 getSize() override {
166 return {0, mText->getFontStyle().size};
167 }
168
169 ~NextLineEntry() override = default;
170
171 size_t getCharacterCount() override {
172 return 1;
173 }
174
175 glm::ivec2 getPosByIndex(size_t characterIndex) override {
176 throw AException("unimplemented");
177 }
178
179 void appendTo(AString& dst) override {
180 dst += '\n';
181 }
182 };
183}
184
188template<typename WordWrappingEngine = AWordWrappingEngine<>>
189class API_AUI_VIEWS ATextBase: public AViewContainerBase, public IFontView {
190public:
191 using Entries = typename WordWrappingEngine::Entries;
192
193 ATextBase() = default;
194 ~ATextBase() override = default;
195 void render(ARenderContext context) override {
197
198 doDrawString(context);
199 }
200
201 void doDrawString(ARenderContext& context) {
202 if (!mPrerenderedString) {
203 prerenderString(context);
204 }
205 if (mPrerenderedString) {
206 RenderHints::PushColor c(context.render);
207 context.render.setColor(getTextColor());
208 mPrerenderedString->draw();
209 }
210 }
211
212 void setSize(glm::ivec2 size) override {
213 bool widthDiffers = size.x != getWidth();
214 AViewContainerBase::setSize(size);
215 if (widthDiffers) {
216 mPrerenderedString = nullptr;
217 markMinContentSizeInvalid();
218 }
219 }
220
221 int getContentMinimumWidth() override {
222 if (mExpanding.x != 0 || mFixedSize.x != 0) {
223 // there's no need to calculate min size because width is defined.
224 return 0;
225 }
226
227 // if width is not defined, when we try to occupy as much space as possible, only restricted by max size.
228 // basically, it is drastically simplified version of perform layout.
229 int max = 0;
230 int accumulator = 0;
231 const auto paddedMaxSize = mMaxSize.x - mPadding.horizontal();
232 for (const auto& e : mEngine.entries()) {
233 if (e->forcesNextLine()) {
234 max = glm::max(max, accumulator);
235 accumulator = 0;
236 continue;
237 }
238 if (accumulator + e->getSize().x > paddedMaxSize) {
239 if (accumulator == 0) {
240 return mMaxSize.x;
241 }
242 // there's no need to calculate min size further.
243 goto ret;
244 }
245 accumulator += e->getSize().x;
246 }
247 ret:
248 return glm::max(max, accumulator);
249 }
250 int getContentMinimumHeight() override {
251 if (!mPrerenderedString) {
252 performLayout();
253 }
254
255 if (auto engineHeight = mEngine.height()) {
256 return *engineHeight;
257 }
258
259 return 0;
260 }
261
262 void invalidateFont() override {
263 mPrerenderedString.reset();
264 markMinContentSizeInvalid();
265 }
266
267protected:
268 void commitStyle() override {
269 AView::commitStyle();
270 commitStyleFont();
271 }
272
273 void invalidateAllStyles() override {
274 invalidateAllStylesFont();
276 }
277
278 virtual void fillStringCanvas(const _<IRenderer::IMultiStringCanvas>& canvas) = 0;
279
280 void prerenderString(ARenderContext ctx) {
281 performLayout();
282 {
283 auto multiStringCanvas = ctx.render.newMultiStringCanvas(getFontStyle());
284 fillStringCanvas(multiStringCanvas);
285 /*
286 */
287 mPrerenderedString = multiStringCanvas->finalize();
288 }
289 }
290
291 virtual void clearContent() {
293 mPrerenderedString = nullptr;
294 }
295
296
297protected:
298 WordWrappingEngine mEngine;
299
300 _<IRenderer::IPrerenderedString> mPrerenderedString;
301
302
303 void performLayout() {
304 APerformanceSection s("ATextBase::performLayout");
305 mEngine.setTextAlign(getFontStyle().align);
306 mEngine.setLineHeight(getFontStyle().lineSpacing);
307 mEngine.performLayout({mPadding.left, mPadding.top }, getSize() - glm::ivec2{mPadding.horizontal(), mPadding.vertical()});
308 }
309};
Abstract AUI exception.
Definition: AException.h:29
Utility wrapper implementing the stack-allocated (fast) optional idiom.
Definition: AOptional.h:32
T valueOr(F &&alternative) const
value or alternative (either value or callback)
Definition: AOptional.h:233
Defines performance profiling named (and colored) span within RAII range.
Definition: APerformanceSection.h:27
Represents a Unicode character string.
Definition: AString.h:37
Base class for AText without public APIs.
Definition: ATextBase.h:189
void render(ARenderContext context) override
Draws this AView. Noone should call this function except rendering routine.
Definition: ATextBase.h:195
int getContentMinimumWidth() override
Definition: ATextBase.h:221
void invalidateAllStyles() override
Invalidates all styles, causing to iterate over all rules in global and parent stylesheets.
Definition: ATextBase.h:273
int getContentMinimumHeight() override
Definition: ATextBase.h:250
A view that represents a set of views.
Definition: AViewContainerBase.h:68
void removeAllViews()
Remove all views from container.
Definition: AViewContainerBase.cpp:493
void invalidateAllStyles() override
Invalidates all styles, causing to iterate over all rules in global and parent stylesheets.
Definition: AViewContainerBase.cpp:455
void render(ARenderContext context) override
Draws this AView. Noone should call this function except rendering routine.
Definition: AViewContainerBase.cpp:233
glm::ivec2 getSize() const noexcept
Size, including content area, border and padding.
Definition: AView.h:370
Definition: AWordWrappingEngine.h:23
Interface of a AView that works with fonts (i.e., ALabel, ATextField, AText, etc.....
Definition: IFontView.h:19
virtual _< IMultiStringCanvas > newMultiStringCanvas(const AFontStyle &style)=0
Creates new canvas for batching multiple prerender string calls.
void setColor(const AColor &color)
Definition: IRenderer.h:430
An std::weak_ptr with AUI extensions.
Definition: SharedPtrTypes.h:177
Definition: ATextBase.h:37
Definition: ATextBase.h:154
Definition: ATextBase.h:23
Definition: ATextBase.h:124
Definition: ATextBase.h:75
Render context passed to AView::render.
Definition: ARenderContext.h:43
Definition: RenderHints.h:79
Definition: Text.h:21