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