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