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