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