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