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