libgui 3.00
libgui is a GUI library for the GameCube, Wii, and Wii U
 
Loading...
Searching...
No Matches
GuiText.cpp
1/****************************************************************************
2 * libgui
3 * Daryl Borth 2009-2026
4 * GuiText.cpp
5 ***************************************************************************/
6
7#include "Gui.h"
8
9// The one translator. Its constructor is constexpr, so this is constant-initialized: it is complete
10// before any dynamic initializer runs, and a static GuiText/GuiButton constructed in any translation
11// unit can resolve text through it without depending on link order. The union keeps it from ever being
12// destroyed: it lives until the process ends, so nothing can look up text in a list freed at exit.
13static union TranslatorStorage
14{
15 GuiTextTranslator translator;
16 constexpr TranslatorStorage() : translator() {}
17 ~TranslatorStorage() {}
18} translatorStorage;
19GuiTextTranslator* textTranslator = &translatorStorage.translator;
20static PixelColor presetColor = (PixelColor){255, 255, 255, 255};
21static int currentSize = 0;
22static int presetSize = 0;
23static int presetMaxWidth = 0;
24static ALIGN_H presetAlignmentHor = ALIGN_H::LEFT;
25static ALIGN_V presetAlignmentVert = ALIGN_V::TOP;
26static uint16_t presetStyle = 0;
27
28#define TEXT_SCROLL_DELAY 8
29#define TEXT_SCROLL_INITIAL_DELAY 6
30
34GuiText::GuiText(const char * t, int s, PixelColor c)
35{
36 origText = nullptr;
37 text = nullptr;
38 size = s;
39 color = c;
40 alpha = c.a;
41 style = GUI_TEXT_JUSTIFY_CENTER | GUI_TEXT_ALIGN_MIDDLE;
42 maxWidth = 0;
43 wrap = false;
44 textDynNum = 0;
45 textScroll = SCROLL::NONE;
46 textScrollPos = 0;
47 textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY;
48 textScrollDelay = TEXT_SCROLL_DELAY;
49 measuredW = measuredH = 0;
50 measuredSize = fullWidthSize = -1;
51 measuredGen = fullWidthGen = 0;
52 fullWidth = 0;
53
54 alignmentHor = ALIGN_H::CENTRE;
55 alignmentVert = ALIGN_V::MIDDLE;
56
57 if(t)
58 {
59 origText = strdup(t);
60 text = getText(t);
61 }
62
63 for(int i=0; i < 20; i++)
64 textDyn[i] = nullptr;
65}
66
70GuiText::GuiText(const char * t)
71{
72 origText = nullptr;
73 text = nullptr;
74 size = presetSize;
75 color = presetColor;
76 alpha = presetColor.a;
77 style = presetStyle;
78 maxWidth = presetMaxWidth;
79 wrap = false;
80 textDynNum = 0;
81 textScroll = SCROLL::NONE;
82 textScrollPos = 0;
83 textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY;
84 textScrollDelay = TEXT_SCROLL_DELAY;
85 measuredW = measuredH = 0;
86 measuredSize = fullWidthSize = -1;
87 measuredGen = fullWidthGen = 0;
88 fullWidth = 0;
89
90 alignmentHor = presetAlignmentHor;
91 alignmentVert = presetAlignmentVert;
92
93 if(t)
94 {
95 origText = strdup(t);
96 text = getText(t);
97 }
98
99 for(int i=0; i < 20; i++)
100 textDyn[i] = nullptr;
101}
102
107{
108 if(origText)
109 free(origText);
110 if(text)
111 delete[] text;
112
113 if(textDynNum > 0)
114 {
115 for(int i=0; i < textDynNum; i++)
116 if(textDyn[i])
117 delete[] textDyn[i];
118 }
119}
120
121wchar_t* GuiText::getText(const char *t) const {
122 return GuiTextRenderer::charToWideChar(textTranslator->getText(t));
123}
124
125void GuiText::setText(const char * t)
126{
127 if(origText)
128 free(origText);
129 if(text)
130 delete[] text;
131
132 if(textDynNum > 0)
133 {
134 for(int i=0; i < textDynNum; i++)
135 if(textDyn[i])
136 delete[] textDyn[i];
137 }
138
139 origText = nullptr;
140 text = nullptr;
141 textDynNum = 0;
142 textScrollPos = 0;
143 textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY;
144 this->forgetMeasurements();
145
146 if(t)
147 {
148 origText = strdup(t);
149 text = getText(t);
150 }
151}
152
153void GuiText::setWText(wchar_t * t)
154{
155 if(origText)
156 free(origText);
157 if(text)
158 delete[] text;
159
160 if(textDynNum > 0)
161 {
162 for(int i=0; i < textDynNum; i++)
163 if(textDyn[i])
164 delete[] textDyn[i];
165 }
166
167 origText = nullptr;
168 text = nullptr;
169 textDynNum = 0;
170 textScrollPos = 0;
171 textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY;
172 this->forgetMeasurements();
173
174 if(t)
175 text = wcsdup(t);
176}
177
179{
180 if(!text)
181 return 0;
182
183 return wcslen(text);
184}
185
186void GuiText::setPresets(int sz, PixelColor c, int w, uint16_t s, ALIGN_H h, ALIGN_V v)
187{
188 presetSize = sz;
189 presetColor = c;
190 presetStyle = s;
191 presetMaxWidth = w;
192 presetAlignmentHor = h;
193 presetAlignmentVert = v;
194}
195
197{
198 size = s;
199}
200
202void GuiText::measure()
203{
204 if(measuredSize == size && measuredGen == GuiTextRenderer::getMetricsGeneration())
205 return;
206
207 fontSystem->setPixelSize(size);
208 measuredW = fontSystem->getWidth(text);
209 measuredH = fontSystem->getLineHeight(); // a line's height, not the height of these glyphs
210 measuredSize = size;
212}
213
214void GuiText::forgetMeasurements()
215{
216 measuredSize = -1;
217 fullWidthSize = -1;
218}
219
221int GuiText::getFullWidth(int atSize)
222{
223 if(fullWidthSize != atSize || fullWidthGen != GuiTextRenderer::getMetricsGeneration())
224 {
225 fullWidth = fontSystem->getWidth(text);
226 fullWidthSize = atSize;
228 }
229 return fullWidth;
230}
231
233{
234 maxWidth = width;
235
236 for(int i=0; i < textDynNum; i++)
237 {
238 if(textDyn[i])
239 {
240 delete[] textDyn[i];
241 textDyn[i] = nullptr;
242 }
243 }
244
245 textDynNum = 0;
246}
247
249{
250 if(!text)
251 return 0;
252
253 this->measure();
254 return measuredW;
255}
256
257void GuiText::setWrap(bool w, int width)
258{
259 wrap = w;
260 maxWidth = width;
261
262 for(int i=0; i < textDynNum; i++)
263 {
264 if(textDyn[i])
265 {
266 delete[] textDyn[i];
267 textDyn[i] = nullptr;
268 }
269 }
270
271 textDynNum = 0;
272}
273
275{
276 if(textScroll == s)
277 return;
278
279 for(int i=0; i < textDynNum; i++)
280 {
281 if(textDyn[i])
282 {
283 delete[] textDyn[i];
284 textDyn[i] = nullptr;
285 }
286 }
287
288 textDynNum = 0;
289
290 textScroll = s;
291 textScrollPos = 0;
292 textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY;
293 textScrollDelay = TEXT_SCROLL_DELAY;
294}
295
297{
298 color = c;
299 alpha = c.a;
300}
301
302void GuiText::setStyle(uint16_t s)
303{
304 style = s;
305}
306
308{
309 style = 0;
310
311 switch(hor)
312 {
313 case ALIGN_H::LEFT:
314 style |= GUI_TEXT_JUSTIFY_LEFT;
315 break;
316 case ALIGN_H::RIGHT:
317 style |= GUI_TEXT_JUSTIFY_RIGHT;
318 break;
319 default:
320 style |= GUI_TEXT_JUSTIFY_CENTER;
321 break;
322 }
323 switch(vert)
324 {
325 case ALIGN_V::TOP:
326 style |= GUI_TEXT_ALIGN_TOP;
327 break;
328 case ALIGN_V::BOTTOM:
329 style |= GUI_TEXT_ALIGN_BOTTOM;
330 break;
331 default:
332 style |= GUI_TEXT_ALIGN_MIDDLE;
333 break;
334 }
335
336 alignmentHor = hor;
337 alignmentVert = vert;
338}
339
341{
342 if(!origText)
343 return;
344 if(text)
345 delete[] text;
346
347 text = getText(origText);
348 this->forgetMeasurements();
349
350 for(int i=0; i < textDynNum; i++)
351 {
352 if(textDyn[i])
353 {
354 delete[] textDyn[i];
355 textDyn[i] = nullptr;
356 }
357 }
358
359 textDynNum = 0;
360 currentSize = 0;
361}
362
366bool GuiText::scrollStepDue()
367{
368 const uint32_t now = platform->getVideo()->getFrameTimer();
369
370 // Timer was reset: resync instead of stalling
371 if(now < textScrollLastTick)
372 textScrollLastTick = now;
373
374 if(now - textScrollLastTick < (uint32_t)textScrollDelay)
375 return false;
376
377 textScrollLastTick = now;
378 return true;
379}
380
385{
386 if(!text)
387 return;
388
389 if(!this->isVisible())
390 return;
391
392 PixelColor c = color;
393 c.a = this->getAlpha();
394
395 int newSize = size*this->getScale();
396
397 fontSystem->setPixelSize(newSize);
398
399 if(maxWidth == 0)
400 {
401 fontSystem->drawText(this->getLeft(), this->getTop() + nudgeY, text, c, style);
402 this->updateEffects();
403 return;
404 }
405
406 uint32_t textlen = wcslen(text);
407
408 if(wrap)
409 {
410 if(textDynNum == 0)
411 {
412 int n = 0;
413 uint32_t ch = 0;
414 int linenum = 0;
415 int lastSpace = -1;
416 int lastSpaceIndex = -1;
417
418 while(ch < textlen && linenum < 20)
419 {
420 if(n == 0)
421 textDyn[linenum] = new wchar_t[textlen + 1];
422
423 textDyn[linenum][n] = text[ch];
424 textDyn[linenum][n+1] = 0;
425
426 if(text[ch] == ' ' || ch == textlen-1)
427 {
428 if(fontSystem->getWidth(textDyn[linenum]) > maxWidth)
429 {
430 if(lastSpace >= 0)
431 {
432 textDyn[linenum][lastSpaceIndex] = 0; // discard space, and everything after
433 ch = lastSpace; // go backwards to the last space
434 lastSpace = -1; // we have used this space
435 lastSpaceIndex = -1;
436 }
437 ++linenum;
438 n = -1;
439 }
440 else if(ch == textlen-1)
441 {
442 ++linenum;
443 }
444 }
445 if(text[ch] == ' ' && n >= 0)
446 {
447 lastSpace = ch;
448 lastSpaceIndex = n;
449 }
450 ++ch;
451 ++n;
452 }
453 textDynNum = linenum;
454 }
455
456 int lineheight = newSize + lineSpacing;
457 int voffset = 0;
458
459 if(alignmentVert == ALIGN_V::MIDDLE)
460 voffset = (lineheight >> 1) * (1-textDynNum);
461
462 int left = this->getLeft();
463 int top = this->getTop() + voffset + nudgeY;
464
465 for(int i=0; i < textDynNum; ++i)
466 fontSystem->drawText(left, top+i*lineheight, textDyn[i], c, style);
467 }
468 else
469 {
470 if(textDynNum == 0)
471 {
472 textDynNum = 1;
473 textDyn[0] = wcsdup(text);
474
475 // the longest start of the text that fits, found in one pass
476 textDyn[0][fontSystem->fitCount(textDyn[0], maxWidth)] = 0;
477 }
478
479 if(textScroll == SCROLL::HORIZONTAL)
480 {
481 if(getFullWidth(newSize) > maxWidth && scrollStepDue())
482 {
483 if(textScrollInitialDelay)
484 {
485 --textScrollInitialDelay;
486 }
487 else
488 {
489 ++textScrollPos;
490 if((uint32_t)textScrollPos > textlen-1)
491 {
492 textScrollPos = 0;
493 textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY;
494 }
495
496 fontSystem->scrollWindow(text, textlen, textScrollPos, maxWidth, textDyn[0]);
497 }
498 }
499 }
500 fontSystem->drawText(this->getLeft(), this->getTop() + nudgeY, textDyn[0], c, style);
501 }
502 this->updateEffects();
503}
504
505void GuiText::getNaturalSize(int& w, int& h)
506{
507 if(!text)
508 {
509 w = 0;
510 h = 0;
511 return;
512 }
513
514 this->measure();
515 w = measuredW;
516 h = measuredH;
517 if(maxWidth > 0 && w > maxWidth)
518 w = maxWidth;
519}
Umbrella header for the platform-agnostic libgui core.
bool isVisible()
Checks whether or not the element is visible.
int getTop()
Gets the current topmost coordinate of the element Considers vertical alignment, y offset,...
int alpha
Element alpha value (0-255)
Definition GuiElement.h:283
int width
Element width.
Definition GuiElement.h:273
ALIGN_H alignmentHor
Horizontal element alignment, respective to parent element (LEFT, RIGHT, CENTRE)
Definition GuiElement.h:294
void updateEffects()
Updates the element's effects (dynamic values) Called by Draw(), used for animation purposes.
int getLeft()
Gets the current leftmost coordinate of the element Considers horizontal alignment,...
ALIGN_V alignmentVert
Horizontal element alignment, respective to parent element (TOP, BOTTOM, MIDDLE)
Definition GuiElement.h:295
float getScale()
Gets the element's current scale Considers scale, scaleDyn, and the parent element's GetScale() value...
int getAlpha()
Gets the element's alpha value Considers alpha, alphaDyn, and the parent element's GetAlpha() value.
static wchar_t * charToWideChar(const char *p)
static uint32_t getMetricsGeneration()
Changes whenever a measurement made earlier could now come out differently (a renderer was made or it...
uint16_t getWidth(const wchar_t *text)
Hashed msgid -> UTF-8 string table loaded from a single binary .lang blob (see loadLanguage()).
void setWrap(bool w, int width=0)
Enables/disables text wrapping.
Definition GuiText.cpp:257
void setColor(PixelColor c)
Sets the font color.
Definition GuiText.cpp:296
void resetText()
Updates the text to the selected language.
Definition GuiText.cpp:340
GuiText(const char *t, int s, PixelColor c)
Constructor.
Definition GuiText.cpp:34
~GuiText()
Destructor.
Definition GuiText.cpp:106
void setScroll(SCROLL s)
Enables/disables text scrolling.
Definition GuiText.cpp:274
static void setPresets(int sz, PixelColor c, int w, uint16_t s, ALIGN_H h, ALIGN_V v)
Sets up preset values to be used by GuiText(t) Useful when printing multiple text elements,...
Definition GuiText.cpp:186
void getNaturalSize(int &w, int &h) override
Gets the text's size for layout: its rendered width and height on one line (at most the maximum width...
Definition GuiText.cpp:505
void setFontSize(int s)
Sets the font size.
Definition GuiText.cpp:196
void setText(const char *t)
Sets the text of the GuiText element.
Definition GuiText.cpp:125
int getTextWidth()
Gets the width of the text when rendered.
Definition GuiText.cpp:248
void draw() override
Constantly called to draw the text.
Definition GuiText.cpp:384
void setWText(wchar_t *t)
Sets the text of the GuiText element.
Definition GuiText.cpp:153
void setMaxWidth(int w)
Sets the maximum width of the drawn texture image.
Definition GuiText.cpp:232
int getLength()
Gets the translated text length of the GuiText element.
Definition GuiText.cpp:178
void setStyle(uint16_t s)
Sets the GuiTextRenderer style attributes.
Definition GuiText.cpp:302
void setAlignment(ALIGN_H hor, ALIGN_V vert)
Sets the text alignment.
Definition GuiText.cpp:307
SCROLL
Scrolling mode of a GuiText (none or horizontal).
Definition Gui.h:39
ALIGN_V
Vertical alignment of a GuiElement relative to its parent.
Definition GuiElement.h:21
ALIGN_H
Horizontal alignment of a GuiElement relative to its parent.
Definition GuiElement.h:29
8-bit-per-channel RGBA color used by the video interfaces.
Definition Mesh.h:16
uint8_t a
Alpha component.
Definition Mesh.h:20