]> git.lizzy.rs Git - minetest.git/blob - src/irrlicht_changes/static_text.cpp
a4749bab13256c6bbb1b62b976a89ee7d666a7c7
[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 #if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR > 8
243 video::SColor StaticText::getActiveColor() const
244 {
245         return getOverrideColor();
246 }
247 #endif
248
249 //! Sets if the static text should use the overide color or the
250 //! color in the gui skin.
251 void StaticText::enableOverrideColor(bool enable)
252 {
253         // TODO
254 }
255
256
257 bool StaticText::isOverrideColorEnabled() const
258 {
259         return true;
260 }
261
262
263 //! Enables or disables word wrap for using the static text as
264 //! multiline text control.
265 void StaticText::setWordWrap(bool enable)
266 {
267         WordWrap = enable;
268         updateText();
269 }
270
271
272 bool StaticText::isWordWrapEnabled() const
273 {
274         return WordWrap;
275 }
276
277
278 void StaticText::setRightToLeft(bool rtl)
279 {
280         if (RightToLeft != rtl)
281         {
282                 RightToLeft = rtl;
283                 updateText();
284         }
285 }
286
287
288 bool StaticText::isRightToLeft() const
289 {
290         return RightToLeft;
291 }
292
293
294 //! Breaks the single text line.
295 // Updates the font colors
296 void StaticText::updateText()
297 {
298         const EnrichedString &cText = ColoredText;
299         BrokenText.clear();
300
301         if (cText.hasBackground())
302                 setBackgroundColor(cText.getBackground());
303         else
304                 setDrawBackground(false);
305
306         if (!WordWrap) {
307                 BrokenText.push_back(cText);
308                 return;
309         }
310
311         // Update word wrap
312
313         IGUISkin* skin = Environment->getSkin();
314         IGUIFont* font = getActiveFont();
315         if (!font)
316                 return;
317
318         LastBreakFont = font;
319
320         EnrichedString line;
321         EnrichedString word;
322         EnrichedString whitespace;
323         s32 size = cText.size();
324         s32 length = 0;
325         s32 elWidth = RelativeRect.getWidth();
326         if (Border)
327                 elWidth -= 2*skin->getSize(EGDS_TEXT_DISTANCE_X);
328         wchar_t c;
329
330         //std::vector<irr::video::SColor> colors;
331
332         // We have to deal with right-to-left and left-to-right differently
333         // However, most parts of the following code is the same, it's just
334         // some order and boundaries which change.
335         if (!RightToLeft)
336         {
337                 // regular (left-to-right)
338                 for (s32 i=0; i<size; ++i)
339                 {
340                         c = cText.getString()[i];
341                         bool lineBreak = false;
342
343                         if (c == L'\r') // Mac or Windows breaks
344                         {
345                                 lineBreak = true;
346                                 //if (Text[i+1] == L'\n') // Windows breaks
347                                 //{
348                                 //      Text.erase(i+1);
349                                 //      --size;
350                                 //}
351                                 c = '\0';
352                         }
353                         else if (c == L'\n') // Unix breaks
354                         {
355                                 lineBreak = true;
356                                 c = '\0';
357                         }
358
359                         bool isWhitespace = (c == L' ' || c == 0);
360                         if ( !isWhitespace )
361                         {
362                                 // part of a word
363                                 //word += c;
364                                 word.addChar(cText, i);
365                         }
366
367                         if ( isWhitespace || i == (size-1))
368                         {
369                                 if (word.size())
370                                 {
371                                         // here comes the next whitespace, look if
372                                         // we must break the last word to the next line.
373                                         const s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
374                                         //const std::wstring sanitized = removeEscapes(word.c_str());
375                                         const s32 wordlgth = font->getDimension(word.c_str()).Width;
376
377                                         if (wordlgth > elWidth)
378                                         {
379                                                 // This word is too long to fit in the available space, look for
380                                                 // the Unicode Soft HYphen (SHY / 00AD) character for a place to
381                                                 // break the word at
382                                                 int where = core::stringw(word.c_str()).findFirst( wchar_t(0x00AD) );
383                                                 if (where != -1)
384                                                 {
385                                                         EnrichedString first = word.substr(0, where);
386                                                         EnrichedString second = word.substr(where, word.size() - where);
387                                                         first.addCharNoColor(L'-');
388                                                         BrokenText.push_back(line + first);
389                                                         const s32 secondLength = font->getDimension(second.c_str()).Width;
390
391                                                         length = secondLength;
392                                                         line = second;
393                                                 }
394                                                 else
395                                                 {
396                                                         // No soft hyphen found, so there's nothing more we can do
397                                                         // break to next line
398                                                         if (length)
399                                                                 BrokenText.push_back(line);
400                                                         length = wordlgth;
401                                                         line = word;
402                                                 }
403                                         }
404                                         else if (length && (length + wordlgth + whitelgth > elWidth))
405                                         {
406                                                 // break to next line
407                                                 BrokenText.push_back(line);
408                                                 length = wordlgth;
409                                                 line = word;
410                                         }
411                                         else
412                                         {
413                                                 // add word to line
414                                                 line += whitespace;
415                                                 line += word;
416                                                 length += whitelgth + wordlgth;
417                                         }
418
419                                         word.clear();
420                                         whitespace.clear();
421                                 }
422
423                                 if ( isWhitespace && c != 0)
424                                 {
425                                         whitespace.addChar(cText, i);
426                                 }
427
428                                 // compute line break
429                                 if (lineBreak)
430                                 {
431                                         line += whitespace;
432                                         line += word;
433                                         BrokenText.push_back(line);
434                                         line.clear();
435                                         word.clear();
436                                         whitespace.clear();
437                                         length = 0;
438                                 }
439                         }
440                 }
441
442                 line += whitespace;
443                 line += word;
444                 BrokenText.push_back(line);
445         }
446         else
447         {
448                 // right-to-left
449                 for (s32 i=size; i>=0; --i)
450                 {
451                         c = cText.getString()[i];
452                         bool lineBreak = false;
453
454                         if (c == L'\r') // Mac or Windows breaks
455                         {
456                                 lineBreak = true;
457                                 //if ((i>0) && Text[i-1] == L'\n') // Windows breaks
458                                 //{
459                                 //      Text.erase(i-1);
460                                 //      --size;
461                                 //}
462                                 c = '\0';
463                         }
464                         else if (c == L'\n') // Unix breaks
465                         {
466                                 lineBreak = true;
467                                 c = '\0';
468                         }
469
470                         if (c==L' ' || c==0 || i==0)
471                         {
472                                 if (word.size())
473                                 {
474                                         // here comes the next whitespace, look if
475                                         // we must break the last word to the next line.
476                                         const s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
477                                         const s32 wordlgth = font->getDimension(word.c_str()).Width;
478
479                                         if (length && (length + wordlgth + whitelgth > elWidth))
480                                         {
481                                                 // break to next line
482                                                 BrokenText.push_back(line);
483                                                 length = wordlgth;
484                                                 line = word;
485                                         }
486                                         else
487                                         {
488                                                 // add word to line
489                                                 line = whitespace + line;
490                                                 line = word + line;
491                                                 length += whitelgth + wordlgth;
492                                         }
493
494                                         word.clear();
495                                         whitespace.clear();
496                                 }
497
498                                 if (c != 0)
499                                 //      whitespace = core::stringw(&c, 1) + whitespace;
500                                 whitespace = cText.substr(i, 1) + whitespace;
501
502                                 // compute line break
503                                 if (lineBreak)
504                                 {
505                                         line = whitespace + line;
506                                         line = word + line;
507                                         BrokenText.push_back(line);
508                                         line.clear();
509                                         word.clear();
510                                         whitespace.clear();
511                                         length = 0;
512                                 }
513                         }
514                         else
515                         {
516                                 // yippee this is a word..
517                                 //word = core::stringw(&c, 1) + word;
518                                 word = cText.substr(i, 1) + word;
519                         }
520                 }
521
522                 line = whitespace + line;
523                 line = word + line;
524                 BrokenText.push_back(line);
525         }
526 }
527
528
529 //! Sets the new caption of this element.
530 void StaticText::setText(const wchar_t* text)
531 {
532         setText(EnrichedString(text, getOverrideColor()));
533 }
534
535 void StaticText::setText(const EnrichedString &text)
536 {
537         ColoredText = text;
538         IGUIElement::setText(ColoredText.c_str());
539         updateText();
540 }
541
542 void StaticText::updateAbsolutePosition()
543 {
544         IGUIElement::updateAbsolutePosition();
545         updateText();
546 }
547
548
549 //! Returns the height of the text in pixels when it is drawn.
550 s32 StaticText::getTextHeight() const
551 {
552         IGUIFont* font = getActiveFont();
553         if (!font)
554                 return 0;
555
556         if (WordWrap) {
557                 s32 height = font->getDimension(L"A").Height + font->getKerningHeight();
558                 return height * BrokenText.size();
559         }
560         // There may be intentional new lines without WordWrap
561         return font->getDimension(BrokenText[0].c_str()).Height;
562 }
563
564
565 s32 StaticText::getTextWidth() const
566 {
567         IGUIFont *font = getActiveFont();
568         if (!font)
569                 return 0;
570
571         s32 widest = 0;
572
573         for (const EnrichedString &line : BrokenText) {
574                 s32 width = font->getDimension(line.c_str()).Width;
575
576                 if (width > widest)
577                         widest = width;
578         }
579
580         return widest;
581 }
582
583
584 } // end namespace gui
585
586 } // end namespace irr
587
588
589 #endif // _IRR_COMPILE_WITH_GUI_