CGUIStaticText.cpp 15 KB

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