]> git.lizzy.rs Git - minetest.git/blob - src/irrlicht_changes/static_text.cpp
4dc4523491152cea12a7021426907baa06046467
[minetest.git] / src / irrlicht_changes / static_text.cpp
1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
2 // Copyright (C) 2016 NathanaĆ«lle Courant:
3 //   Modified the functions to use EnrichedText instead of string.
4 // This file is part of the "Irrlicht Engine".
5 // For conditions of distribution and use, see copyright notice in irrlicht.h
6
7 #include "static_text.h"
8 #ifdef _IRR_COMPILE_WITH_GUI_
9
10 #include <IGUIFont.h>
11 #include <IVideoDriver.h>
12 #include <rect.h>
13 #include <SColor.h>
14
15 #include "CGUITTFont.h"
16 #include "util/string.h"
17
18 namespace irr
19 {
20
21 namespace gui
22 {
23 //! constructor
24 StaticText::StaticText(const EnrichedString &text, bool border,
25                         IGUIEnvironment* environment, IGUIElement* parent,
26                         s32 id, const core::rect<s32>& rectangle,
27                         bool background)
28 : IGUIStaticText(environment, parent, id, rectangle),
29         HAlign(EGUIA_UPPERLEFT), VAlign(EGUIA_UPPERLEFT),
30         Border(border), WordWrap(false), Background(background),
31         RestrainTextInside(true), RightToLeft(false),
32         OverrideFont(0), LastBreakFont(0)
33 {
34         #ifdef _DEBUG
35         setDebugName("StaticText");
36         #endif
37
38         setText(text);
39 }
40
41
42 //! destructor
43 StaticText::~StaticText()
44 {
45         if (OverrideFont)
46                 OverrideFont->drop();
47 }
48
49 //! draws the element and its children
50 void StaticText::draw()
51 {
52         if (!IsVisible)
53                 return;
54
55         IGUISkin* skin = Environment->getSkin();
56         if (!skin)
57                 return;
58         video::IVideoDriver* driver = Environment->getVideoDriver();
59
60         core::rect<s32> frameRect(AbsoluteRect);
61
62         // draw background
63
64         if (Background)
65                 driver->draw2DRectangle(getBackgroundColor(), frameRect, &AbsoluteClippingRect);
66
67         // draw the border
68
69         if (Border)
70         {
71                 skin->draw3DSunkenPane(this, 0, true, false, frameRect, &AbsoluteClippingRect);
72                 frameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X);
73         }
74
75         // draw the text
76         IGUIFont *font = getActiveFont();
77         if (font && BrokenText.size()) {
78                 if (font != LastBreakFont)
79                         updateText();
80
81                 core::rect<s32> r = frameRect;
82                 s32 height_line = font->getDimension(L"A").Height + font->getKerningHeight();
83                 s32 height_total = height_line * BrokenText.size();
84                 if (VAlign == EGUIA_CENTER && WordWrap)
85                 {
86                         r.UpperLeftCorner.Y = r.getCenter().Y - (height_total / 2);
87                 }
88                 else if (VAlign == EGUIA_LOWERRIGHT)
89                 {
90                         r.UpperLeftCorner.Y = r.LowerRightCorner.Y - height_total;
91                 }
92                 if (HAlign == EGUIA_LOWERRIGHT)
93                 {
94                         r.UpperLeftCorner.X = r.LowerRightCorner.X -
95                                 getTextWidth();
96                 }
97
98                 irr::video::SColor previous_color(255, 255, 255, 255);
99                 for (const EnrichedString &str : BrokenText) {
100                         if (HAlign == EGUIA_LOWERRIGHT)
101                         {
102                                 r.UpperLeftCorner.X = frameRect.LowerRightCorner.X -
103                                         font->getDimension(str.c_str()).Width;
104                         }
105
106                         if (font->getType() == irr::gui::EGFT_CUSTOM) {
107                                 CGUITTFont *tmp = static_cast<CGUITTFont*>(font);
108                                 tmp->draw(str,
109                                         r, HAlign == EGUIA_CENTER, VAlign == EGUIA_CENTER,
110                                         (RestrainTextInside ? &AbsoluteClippingRect : NULL));
111                         } else
112                         {
113                                 // Draw non-colored text
114                                 font->draw(str.c_str(),
115                                         r, str.getDefaultColor(), // TODO: Implement colorization
116                                         HAlign == EGUIA_CENTER, VAlign == EGUIA_CENTER,
117                                         (RestrainTextInside ? &AbsoluteClippingRect : NULL));
118                         }
119
120
121                         r.LowerRightCorner.Y += height_line;
122                         r.UpperLeftCorner.Y += height_line;
123                 }
124         }
125
126         IGUIElement::draw();
127 }
128
129
130 //! Sets another skin independent font.
131 void StaticText::setOverrideFont(IGUIFont* font)
132 {
133         if (OverrideFont == font)
134                 return;
135
136         if (OverrideFont)
137                 OverrideFont->drop();
138
139         OverrideFont = font;
140
141         if (OverrideFont)
142                 OverrideFont->grab();
143
144         updateText();
145 }
146
147 //! Gets the override font (if any)
148 IGUIFont * StaticText::getOverrideFont() const
149 {
150         return OverrideFont;
151 }
152
153 //! Get the font which is used right now for drawing
154 IGUIFont* StaticText::getActiveFont() const
155 {
156         if ( OverrideFont )
157                 return OverrideFont;
158         IGUISkin* skin = Environment->getSkin();
159         if (skin)
160                 return skin->getFont();
161         return 0;
162 }
163
164 //! Sets another color for the text.
165 void StaticText::setOverrideColor(video::SColor color)
166 {
167         ColoredText.setDefaultColor(color);
168         updateText();
169 }
170
171
172 //! Sets another color for the text.
173 void StaticText::setBackgroundColor(video::SColor color)
174 {
175         ColoredText.setBackground(color);
176         Background = true;
177 }
178
179
180 //! Sets whether to draw the background
181 void StaticText::setDrawBackground(bool draw)
182 {
183         Background = draw;
184 }
185
186
187 //! Gets the background color
188 video::SColor StaticText::getBackgroundColor() const
189 {
190         IGUISkin *skin = Environment->getSkin();
191
192         return (ColoredText.hasBackground() || !skin) ?
193                 ColoredText.getBackground() : skin->getColor(gui::EGDC_3D_FACE);
194 }
195
196
197 //! Checks if background drawing is enabled
198 bool StaticText::isDrawBackgroundEnabled() const
199 {
200         return Background;
201 }
202
203
204 //! Sets whether to draw the border
205 void StaticText::setDrawBorder(bool draw)
206 {
207         Border = draw;
208 }
209
210
211 //! Checks if border drawing is enabled
212 bool StaticText::isDrawBorderEnabled() const
213 {
214         return Border;
215 }
216
217
218 void StaticText::setTextRestrainedInside(bool restrainTextInside)
219 {
220         RestrainTextInside = restrainTextInside;
221 }
222
223
224 bool StaticText::isTextRestrainedInside() const
225 {
226         return RestrainTextInside;
227 }
228
229
230 void StaticText::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical)
231 {
232         HAlign = horizontal;
233         VAlign = vertical;
234 }
235
236
237 video::SColor StaticText::getOverrideColor() const
238 {
239         return ColoredText.getDefaultColor();
240 }
241
242 video::SColor StaticText::getActiveColor() const
243 {
244         return getOverrideColor();
245 }
246
247 //! Sets if the static text should use the overide color or the
248 //! color in the gui skin.
249 void StaticText::enableOverrideColor(bool enable)
250 {
251         // TODO
252 }
253
254
255 bool StaticText::isOverrideColorEnabled() const
256 {
257         return true;
258 }
259
260
261 //! Enables or disables word wrap for using the static text as
262 //! multiline text control.
263 void StaticText::setWordWrap(bool enable)
264 {
265         WordWrap = enable;
266         updateText();
267 }
268
269
270 bool StaticText::isWordWrapEnabled() const
271 {
272         return WordWrap;
273 }
274
275
276 void StaticText::setRightToLeft(bool rtl)
277 {
278         if (RightToLeft != rtl)
279         {
280                 RightToLeft = rtl;
281                 updateText();
282         }
283 }
284
285
286 bool StaticText::isRightToLeft() const
287 {
288         return RightToLeft;
289 }
290
291
292 //! Breaks the single text line.
293 // Updates the font colors
294 void StaticText::updateText()
295 {
296         const EnrichedString &cText = ColoredText;
297         BrokenText.clear();
298
299         if (cText.hasBackground())
300                 setBackgroundColor(cText.getBackground());
301         else
302                 setDrawBackground(false);
303
304         if (!WordWrap) {
305                 BrokenText.push_back(cText);
306                 return;
307         }
308
309         // Update word wrap
310
311         IGUISkin* skin = Environment->getSkin();
312         IGUIFont* font = getActiveFont();
313         if (!font)
314                 return;
315
316         LastBreakFont = font;
317
318         EnrichedString line;
319         EnrichedString word;
320         EnrichedString whitespace;
321         s32 size = cText.size();
322         s32 length = 0;
323         s32 elWidth = RelativeRect.getWidth();
324         if (Border)
325                 elWidth -= 2*skin->getSize(EGDS_TEXT_DISTANCE_X);
326         wchar_t c;
327
328         //std::vector<irr::video::SColor> colors;
329
330         // We have to deal with right-to-left and left-to-right differently
331         // However, most parts of the following code is the same, it's just
332         // some order and boundaries which change.
333         if (!RightToLeft)
334         {
335                 // regular (left-to-right)
336                 for (s32 i=0; i<size; ++i)
337                 {
338                         c = cText.getString()[i];
339                         bool lineBreak = false;
340
341                         if (c == L'\r') // Mac or Windows breaks
342                         {
343                                 lineBreak = true;
344                                 //if (Text[i+1] == L'\n') // Windows breaks
345                                 //{
346                                 //      Text.erase(i+1);
347                                 //      --size;
348                                 //}
349                                 c = '\0';
350                         }
351                         else if (c == L'\n') // Unix breaks
352                         {
353                                 lineBreak = true;
354                                 c = '\0';
355                         }
356
357                         bool isWhitespace = (c == L' ' || c == 0);
358                         if ( !isWhitespace )
359                         {
360                                 // part of a word
361                                 //word += c;
362                                 word.addChar(cText, i);
363                         }
364
365                         if ( isWhitespace || i == (size-1))
366                         {
367                                 if (word.size())
368                                 {
369                                         // here comes the next whitespace, look if
370                                         // we must break the last word to the next line.
371                                         const s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
372                                         //const std::wstring sanitized = removeEscapes(word.c_str());
373                                         const s32 wordlgth = font->getDimension(word.c_str()).Width;
374
375                                         if (wordlgth > elWidth)
376                                         {
377                                                 // This word is too long to fit in the available space, look for
378                                                 // the Unicode Soft HYphen (SHY / 00AD) character for a place to
379                                                 // break the word at
380                                                 int where = core::stringw(word.c_str()).findFirst( wchar_t(0x00AD) );
381                                                 if (where != -1)
382                                                 {
383                                                         EnrichedString first = word.substr(0, where);
384                                                         EnrichedString second = word.substr(where, word.size() - where);
385                                                         first.addCharNoColor(L'-');
386                                                         BrokenText.push_back(line + first);
387                                                         const s32 secondLength = font->getDimension(second.c_str()).Width;
388
389                                                         length = secondLength;
390                                                         line = second;
391                                                 }
392                                                 else
393                                                 {
394                                                         // No soft hyphen found, so there's nothing more we can do
395                                                         // break to next line
396                                                         if (length)
397                                                                 BrokenText.push_back(line);
398                                                         length = wordlgth;
399                                                         line = word;
400                                                 }
401                                         }
402                                         else if (length && (length + wordlgth + whitelgth > elWidth))
403                                         {
404                                                 // break to next line
405                                                 BrokenText.push_back(line);
406                                                 length = wordlgth;
407                                                 line = word;
408                                         }
409                                         else
410                                         {
411                                                 // add word to line
412                                                 line += whitespace;
413                                                 line += word;
414                                                 length += whitelgth + wordlgth;
415                                         }
416
417                                         word.clear();
418                                         whitespace.clear();
419                                 }
420
421                                 if ( isWhitespace && c != 0)
422                                 {
423                                         whitespace.addChar(cText, i);
424                                 }
425
426                                 // compute line break
427                                 if (lineBreak)
428                                 {
429                                         line += whitespace;
430                                         line += word;
431                                         BrokenText.push_back(line);
432                                         line.clear();
433                                         word.clear();
434                                         whitespace.clear();
435                                         length = 0;
436                                 }
437                         }
438                 }
439
440                 line += whitespace;
441                 line += word;
442                 BrokenText.push_back(line);
443         }
444         else
445         {
446                 // right-to-left
447                 for (s32 i=size; i>=0; --i)
448                 {
449                         c = cText.getString()[i];
450                         bool lineBreak = false;
451
452                         if (c == L'\r') // Mac or Windows breaks
453                         {
454                                 lineBreak = true;
455                                 //if ((i>0) && Text[i-1] == L'\n') // Windows breaks
456                                 //{
457                                 //      Text.erase(i-1);
458                                 //      --size;
459                                 //}
460                                 c = '\0';
461                         }
462                         else if (c == L'\n') // Unix breaks
463                         {
464                                 lineBreak = true;
465                                 c = '\0';
466                         }
467
468                         if (c==L' ' || c==0 || i==0)
469                         {
470                                 if (word.size())
471                                 {
472                                         // here comes the next whitespace, look if
473                                         // we must break the last word to the next line.
474                                         const s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
475                                         const s32 wordlgth = font->getDimension(word.c_str()).Width;
476
477                                         if (length && (length + wordlgth + whitelgth > elWidth))
478                                         {
479                                                 // break to next line
480                                                 BrokenText.push_back(line);
481                                                 length = wordlgth;
482                                                 line = word;
483                                         }
484                                         else
485                                         {
486                                                 // add word to line
487                                                 line = whitespace + line;
488                                                 line = word + line;
489                                                 length += whitelgth + wordlgth;
490                                         }
491
492                                         word.clear();
493                                         whitespace.clear();
494                                 }
495
496                                 if (c != 0)
497                                 //      whitespace = core::stringw(&c, 1) + whitespace;
498                                 whitespace = cText.substr(i, 1) + whitespace;
499
500                                 // compute line break
501                                 if (lineBreak)
502                                 {
503                                         line = whitespace + line;
504                                         line = word + line;
505                                         BrokenText.push_back(line);
506                                         line.clear();
507                                         word.clear();
508                                         whitespace.clear();
509                                         length = 0;
510                                 }
511                         }
512                         else
513                         {
514                                 // yippee this is a word..
515                                 //word = core::stringw(&c, 1) + word;
516                                 word = cText.substr(i, 1) + word;
517                         }
518                 }
519
520                 line = whitespace + line;
521                 line = word + line;
522                 BrokenText.push_back(line);
523         }
524 }
525
526
527 //! Sets the new caption of this element.
528 void StaticText::setText(const wchar_t* text)
529 {
530         setText(EnrichedString(text, getOverrideColor()));
531 }
532
533 void StaticText::setText(const EnrichedString &text)
534 {
535         ColoredText = text;
536         IGUIElement::setText(ColoredText.c_str());
537         updateText();
538 }
539
540 void StaticText::updateAbsolutePosition()
541 {
542         IGUIElement::updateAbsolutePosition();
543         updateText();
544 }
545
546
547 //! Returns the height of the text in pixels when it is drawn.
548 s32 StaticText::getTextHeight() const
549 {
550         IGUIFont* font = getActiveFont();
551         if (!font)
552                 return 0;
553
554         if (WordWrap) {
555                 s32 height = font->getDimension(L"A").Height + font->getKerningHeight();
556                 return height * BrokenText.size();
557         }
558         // There may be intentional new lines without WordWrap
559         return font->getDimension(BrokenText[0].c_str()).Height;
560 }
561
562
563 s32 StaticText::getTextWidth() const
564 {
565         IGUIFont *font = getActiveFont();
566         if (!font)
567                 return 0;
568
569         s32 widest = 0;
570
571         for (const EnrichedString &line : BrokenText) {
572                 s32 width = font->getDimension(line.c_str()).Width;
573
574                 if (width > widest)
575                         widest = width;
576         }
577
578         return widest;
579 }
580
581
582 } // end namespace gui
583
584 } // end namespace irr
585
586
587 #endif // _IRR_COMPILE_WITH_GUI_