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