UIElement.cpp 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240
  1. //
  2. // Urho3D Engine
  3. // Copyright (c) 2008-2011 Lasse Öörni
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. #include "Precompiled.h"
  24. #include "Context.h"
  25. #include "ResourceCache.h"
  26. #include "StringUtils.h"
  27. #include "UIElement.h"
  28. #include "UIEvents.h"
  29. #include "DebugNew.h"
  30. static const std::string horizontalAlignments[] =
  31. {
  32. "left",
  33. "center",
  34. "right"
  35. };
  36. static const std::string verticalAlignments[] =
  37. {
  38. "top",
  39. "center",
  40. "bottom"
  41. };
  42. static const std::string focusModes[] =
  43. {
  44. "notfocusable",
  45. "resetfocus",
  46. "focusable",
  47. "focusabledefocusable"
  48. };
  49. static const std::string dragDropModes[] =
  50. {
  51. "disabled",
  52. "source",
  53. "target",
  54. "sourceandtarget"
  55. };
  56. OBJECTTYPESTATIC(UIElement);
  57. UIElement::UIElement(Context* context) :
  58. Object(context),
  59. parent_(0),
  60. clipBorder_(IntRect::ZERO),
  61. priority_(0),
  62. opacity_(1.0f),
  63. bringToFront_(false),
  64. bringToBack_(true),
  65. clipChildren_(false),
  66. active_(false),
  67. focusMode_(FM_NOTFOCUSABLE),
  68. focus_(false),
  69. selected_(false),
  70. visible_(true),
  71. hovering_(false),
  72. dragDropMode_(DD_DISABLED),
  73. layoutMode_(LM_FREE),
  74. layoutSpacing_(0),
  75. layoutBorder_(IntRect::ZERO),
  76. resizeNestingLevel_(0),
  77. layoutNestingLevel_(0),
  78. position_(IntVector2::ZERO),
  79. size_(IntVector2::ZERO),
  80. minSize_(IntVector2::ZERO),
  81. maxSize_(M_MAX_INT, M_MAX_INT),
  82. childOffset_(IntVector2::ZERO),
  83. horizontalAlignment_(HA_LEFT),
  84. verticalAlignment_(VA_TOP),
  85. positionDirty_(true),
  86. opacityDirty_(true),
  87. derivedColorDirty_(true),
  88. colorGradient_(false)
  89. {
  90. }
  91. UIElement::~UIElement()
  92. {
  93. // If child elements have outside references, detach them
  94. while (children_.size())
  95. {
  96. const SharedPtr<UIElement>& element = children_.back();
  97. if (element.GetRefCount() > 1)
  98. {
  99. element->parent_ = 0;
  100. element->MarkDirty();
  101. }
  102. children_.pop_back();
  103. }
  104. }
  105. void UIElement::RegisterObject(Context* context)
  106. {
  107. context->RegisterFactory<UIElement>();
  108. }
  109. void UIElement::SetStyle(const XMLElement& element)
  110. {
  111. if (element.HasAttribute("name"))
  112. name_ = element.GetString("name");
  113. if (element.HasChildElement("position"))
  114. SetPosition(element.GetChildElement("position").GetIntVector2("value"));
  115. if (element.HasChildElement("size"))
  116. SetSize(element.GetChildElement("size").GetIntVector2("value"));
  117. if (element.HasChildElement("width"))
  118. SetWidth(element.GetChildElement("width").GetInt("value"));
  119. if (element.HasChildElement("height"))
  120. SetHeight(element.GetChildElement("height").GetInt("value"));
  121. if (element.HasChildElement("minsize"))
  122. SetMinSize(element.GetChildElement("minsize").GetIntVector2("value"));
  123. if (element.HasChildElement("minwidth"))
  124. SetMinWidth(element.GetChildElement("minwidth").GetInt("value"));
  125. if (element.HasChildElement("minheight"))
  126. SetMinHeight(element.GetChildElement("minheight").GetInt("value"));
  127. if (element.HasChildElement("maxsize"))
  128. SetMaxSize(element.GetChildElement("maxsize").GetIntVector2("value"));
  129. if (element.HasChildElement("maxwidth"))
  130. SetMinWidth(element.GetChildElement("maxwidth").GetInt("value"));
  131. if (element.HasChildElement("maxheight"))
  132. SetMinHeight(element.GetChildElement("maxheight").GetInt("value"));
  133. if (element.HasChildElement("fixedsize"))
  134. SetFixedSize(element.GetChildElement("fixedsize").GetIntVector2("value"));
  135. if (element.HasChildElement("fixedwidth"))
  136. SetFixedWidth(element.GetChildElement("fixedwidth").GetInt("value"));
  137. if (element.HasChildElement("fixedheight"))
  138. SetFixedHeight(element.GetChildElement("fixedheight").GetInt("value"));
  139. if (element.HasChildElement("alignment"))
  140. {
  141. XMLElement alignElem = element.GetChildElement("alignment");
  142. std::string horiz;
  143. std::string vert;
  144. if (alignElem.HasAttribute("horizontal"))
  145. horiz = alignElem.GetStringLower("horizontal");
  146. if (alignElem.HasAttribute("vertical"))
  147. vert = alignElem.GetStringLower("vertical");
  148. if (alignElem.HasAttribute("h"))
  149. horiz = alignElem.GetStringLower("h");
  150. if (alignElem.HasAttribute("v"))
  151. vert = alignElem.GetStringLower("v");
  152. if (!horiz.empty())
  153. SetHorizontalAlignment((HorizontalAlignment)GetStringListIndex(horiz, horizontalAlignments, 3, 0));
  154. if (!vert.empty())
  155. SetVerticalAlignment((VerticalAlignment)GetStringListIndex(vert, verticalAlignments, 3, 0));
  156. }
  157. if (element.HasChildElement("clipborder"))
  158. SetClipBorder(element.GetChildElement("clipborder").GetIntRect("value"));
  159. if (element.HasChildElement("priority"))
  160. SetPriority(element.GetChildElement("priority").GetInt("value"));
  161. if (element.HasChildElement("opacity"))
  162. SetOpacity(element.GetChildElement("opacity").GetFloat("value"));
  163. if (element.HasChildElement("color"))
  164. {
  165. XMLElement colorElem = element.GetChildElement("color");
  166. if (colorElem.HasAttribute("value"))
  167. SetColor(colorElem.GetColor("value"));
  168. if (colorElem.HasAttribute("topleft"))
  169. SetColor(C_TOPLEFT, colorElem.GetColor("topleft"));
  170. if (colorElem.HasAttribute("topright"))
  171. SetColor(C_TOPRIGHT, colorElem.GetColor("topright"));
  172. if (colorElem.HasAttribute("bottomleft"))
  173. SetColor(C_BOTTOMLEFT, colorElem.GetColor("bottomleft"));
  174. if (colorElem.HasAttribute("bottomright"))
  175. SetColor(C_BOTTOMRIGHT, colorElem.GetColor("bottomright"));
  176. }
  177. if (element.HasChildElement("bringtofront"))
  178. SetBringToFront(element.GetChildElement("bringtofront").GetBool("enable"));
  179. if (element.HasChildElement("bringtoback"))
  180. SetBringToBack(element.GetChildElement("bringtoback").GetBool("enable"));
  181. if (element.HasChildElement("clipchildren"))
  182. SetClipChildren(element.GetChildElement("clipchildren").GetBool("enable"));
  183. if (element.HasChildElement("enabled"))
  184. SetActive(element.GetChildElement("enabled").GetBool("enable"));
  185. if (element.HasChildElement("selected"))
  186. SetSelected(element.GetChildElement("selected").GetBool("enable"));
  187. if (element.HasChildElement("visible"))
  188. SetVisible(element.GetChildElement("visible").GetBool("enable"));
  189. if (element.HasChildElement("focusmode"))
  190. {
  191. std::string focusMode = element.GetChildElement("focusmode").GetStringLower("value");
  192. SetFocusMode((FocusMode)GetStringListIndex(focusMode, focusModes, 4, 0));
  193. if (focusMode == "defocusable")
  194. SetFocusMode(FM_FOCUSABLE_DEFOCUSABLE);
  195. }
  196. if (element.HasChildElement("dragdropmode"))
  197. {
  198. std::string dragDropMode = element.GetChildElement("dragdropmode").GetStringLower("value");
  199. SetDragDropMode(GetStringListIndex(dragDropMode, dragDropModes, 4, 0));
  200. }
  201. if (element.HasChildElement("layout"))
  202. {
  203. XMLElement layoutElem = element.GetChildElement("layout");
  204. std::string mode = layoutElem.GetStringLower("mode");
  205. if (mode == "free")
  206. layoutMode_ = LM_FREE;
  207. if ((mode == "horizontal") || (mode == "h"))
  208. layoutMode_ = LM_HORIZONTAL;
  209. if ((mode == "vertical") || (mode == "v"))
  210. layoutMode_ = LM_VERTICAL;
  211. if (layoutElem.HasAttribute("spacing"))
  212. layoutSpacing_ = Max(layoutElem.GetInt("spacing"), 0);
  213. if (layoutElem.HasAttribute("border"))
  214. SetLayoutBorder(layoutElem.GetIntRect("border"));
  215. else
  216. UpdateLayout();
  217. }
  218. if (element.HasChildElement("userdata"))
  219. SetUserData(element.GetChildElement("userdat").GetVariantMap());
  220. }
  221. void UIElement::Update(float timeStep)
  222. {
  223. }
  224. void UIElement::GetBatches(std::vector<UIBatch>& batches, std::vector<UIQuad>& quads, const IntRect& currentScissor)
  225. {
  226. // Reset hovering for next frame
  227. hovering_ = false;
  228. }
  229. void UIElement::OnHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
  230. {
  231. hovering_ = true;
  232. }
  233. void UIElement::OnClick(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
  234. {
  235. }
  236. void UIElement::OnDragStart(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
  237. {
  238. }
  239. void UIElement::OnDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
  240. {
  241. }
  242. void UIElement::OnDragEnd(const IntVector2& position, const IntVector2& screenPosition, Cursor* cursor)
  243. {
  244. }
  245. bool UIElement::OnDragDropTest(UIElement* source)
  246. {
  247. return true;
  248. }
  249. bool UIElement::OnDragDropFinish(UIElement* source)
  250. {
  251. return true;
  252. }
  253. void UIElement::OnWheel(int delta, int buttons, int qualifiers)
  254. {
  255. }
  256. void UIElement::OnKey(int key, int buttons, int qualifiers)
  257. {
  258. }
  259. void UIElement::OnChar(unsigned char c, int buttons, int qualifiers)
  260. {
  261. }
  262. void UIElement::OnResize()
  263. {
  264. }
  265. void UIElement::OnFocus()
  266. {
  267. }
  268. void UIElement::OnDefocus()
  269. {
  270. }
  271. void UIElement::SetName(const std::string& name)
  272. {
  273. name_ = name;
  274. }
  275. void UIElement::SetPosition(const IntVector2& position)
  276. {
  277. if (position != position_)
  278. {
  279. position_ = position;
  280. MarkDirty();
  281. }
  282. }
  283. void UIElement::SetPosition(int x, int y)
  284. {
  285. SetPosition(IntVector2(x, y));
  286. }
  287. void UIElement::SetSize(const IntVector2& size)
  288. {
  289. ++resizeNestingLevel_;
  290. IntVector2 validatedSize;
  291. validatedSize.x_ = Clamp(size.x_, minSize_.x_, maxSize_.x_);
  292. validatedSize.y_ = Clamp(size.y_, minSize_.y_, maxSize_.y_);
  293. if (validatedSize != size_)
  294. {
  295. size_ = validatedSize;
  296. if (resizeNestingLevel_ == 1)
  297. {
  298. // Check if parent element's layout needs to be updated first
  299. if (parent_)
  300. parent_->UpdateLayout();
  301. MarkDirty();
  302. OnResize();
  303. UpdateLayout();
  304. using namespace Resized;
  305. VariantMap eventData;
  306. eventData[P_ELEMENT] = (void*)this;
  307. eventData[P_WIDTH] = size_.x_;
  308. eventData[P_HEIGHT] = size_.y_;
  309. SendEvent(E_RESIZED, eventData);
  310. }
  311. }
  312. --resizeNestingLevel_;
  313. }
  314. void UIElement::SetSize(int width, int height)
  315. {
  316. SetSize(IntVector2(width, height));
  317. }
  318. void UIElement::SetWidth(int width)
  319. {
  320. SetSize(IntVector2(width, size_.y_));
  321. }
  322. void UIElement::SetHeight(int height)
  323. {
  324. SetSize(IntVector2(size_.x_, height));
  325. }
  326. void UIElement::SetMinSize(const IntVector2& minSize)
  327. {
  328. minSize_.x_ = Max(minSize.x_, 0);
  329. minSize_.y_ = Max(minSize.y_, 0);
  330. SetSize(size_);
  331. }
  332. void UIElement::SetMinSize(int width, int height)
  333. {
  334. SetMinSize(IntVector2(width, height));
  335. }
  336. void UIElement::SetMinWidth(int width)
  337. {
  338. SetMinSize(IntVector2(width, minSize_.y_));
  339. }
  340. void UIElement::SetMinHeight(int height)
  341. {
  342. SetMinSize(IntVector2(minSize_.x_, height));
  343. }
  344. void UIElement::SetMaxSize(const IntVector2& maxSize)
  345. {
  346. maxSize_.x_ = Max(maxSize.x_, 0);
  347. maxSize_.y_ = Max(maxSize.y_, 0);
  348. SetSize(size_);
  349. }
  350. void UIElement::SetMaxSize(int width, int height)
  351. {
  352. SetMaxSize(IntVector2(width, height));
  353. }
  354. void UIElement::SetMaxWidth(int width)
  355. {
  356. SetMaxSize(IntVector2(width, maxSize_.y_));
  357. }
  358. void UIElement::SetMaxHeight(int height)
  359. {
  360. SetMaxSize(IntVector2(maxSize_.x_, height));
  361. }
  362. void UIElement::SetFixedSize(const IntVector2& size)
  363. {
  364. minSize_ = maxSize_ = IntVector2(Max(size.x_, 0), Max(size.y_, 0));
  365. SetSize(size);
  366. }
  367. void UIElement::SetFixedSize(int width, int height)
  368. {
  369. SetFixedSize(IntVector2(width, height));
  370. }
  371. void UIElement::SetFixedWidth(int width)
  372. {
  373. minSize_.x_ = maxSize_.x_ = Max(width, 0);
  374. SetWidth(width);
  375. }
  376. void UIElement::SetFixedHeight(int height)
  377. {
  378. minSize_.y_ = maxSize_.y_ = Max(height, 0);
  379. SetHeight(height);
  380. }
  381. void UIElement::SetAlignment(HorizontalAlignment hAlign, VerticalAlignment vAlign)
  382. {
  383. horizontalAlignment_ = hAlign;
  384. verticalAlignment_ = vAlign;
  385. MarkDirty();
  386. }
  387. void UIElement::SetHorizontalAlignment(HorizontalAlignment align)
  388. {
  389. horizontalAlignment_ = align;
  390. MarkDirty();
  391. }
  392. void UIElement::SetVerticalAlignment(VerticalAlignment align)
  393. {
  394. verticalAlignment_ = align;
  395. MarkDirty();
  396. }
  397. void UIElement::SetClipBorder(const IntRect& rect)
  398. {
  399. clipBorder_.left_ = Max(rect.left_, 0);
  400. clipBorder_.top_ = Max(rect.top_, 0);
  401. clipBorder_.right_ = Max(rect.right_, 0);
  402. clipBorder_.bottom_ = Max(rect.bottom_, 0);
  403. }
  404. void UIElement::SetColor(const Color& color)
  405. {
  406. for (unsigned i = 0; i < MAX_UIELEMENT_CORNERS; ++i)
  407. color_[i] = color;
  408. colorGradient_ = false;
  409. derivedColorDirty_ = true;
  410. }
  411. void UIElement::SetColor(Corner corner, const Color& color)
  412. {
  413. color_[corner] = color;
  414. colorGradient_ = false;
  415. derivedColorDirty_ = true;
  416. for (unsigned i = 0; i < MAX_UIELEMENT_CORNERS; ++i)
  417. {
  418. if ((i != corner) && (color_[i] != color_[corner]))
  419. colorGradient_ = true;
  420. }
  421. }
  422. void UIElement::SetPriority(int priority)
  423. {
  424. priority_ = priority;
  425. }
  426. void UIElement::SetOpacity(float opacity)
  427. {
  428. opacity_ = Clamp(opacity, 0.0f, 1.0f);
  429. MarkDirty();
  430. }
  431. void UIElement::SetBringToFront(bool enable)
  432. {
  433. bringToFront_ = enable;
  434. }
  435. void UIElement::SetBringToBack(bool enable)
  436. {
  437. bringToBack_ = enable;
  438. }
  439. void UIElement::SetClipChildren(bool enable)
  440. {
  441. clipChildren_ = enable;
  442. }
  443. void UIElement::SetActive(bool enable)
  444. {
  445. active_ = enable;
  446. }
  447. void UIElement::SetFocusMode(FocusMode mode)
  448. {
  449. focusMode_ = mode;
  450. }
  451. void UIElement::SetFocus(bool enable)
  452. {
  453. if (focusMode_ < FM_FOCUSABLE)
  454. enable = false;
  455. if (enable != focus_)
  456. {
  457. focus_ = enable;
  458. if (enable)
  459. OnFocus();
  460. else
  461. OnDefocus();
  462. using namespace Focused;
  463. VariantMap eventData;
  464. eventData[P_ELEMENT] = (void*)this;
  465. SendEvent(focus_ ? E_FOCUSED : E_DEFOCUSED, eventData);
  466. }
  467. }
  468. void UIElement::SetSelected(bool enable)
  469. {
  470. selected_ = enable;
  471. }
  472. void UIElement::SetVisible(bool enable)
  473. {
  474. if (enable != visible_)
  475. {
  476. visible_ = enable;
  477. // Parent's layout may change as a result of visibility change
  478. if (parent_)
  479. parent_->UpdateLayout();
  480. using namespace VisibleChanged;
  481. VariantMap eventData;
  482. eventData[P_ELEMENT] = (void*)this;
  483. eventData[P_VISIBLE] = visible_;
  484. SendEvent(E_VISIBLECHANGED, eventData);
  485. }
  486. }
  487. void UIElement::SetDragDropMode(unsigned mode)
  488. {
  489. dragDropMode_ = mode;
  490. }
  491. void UIElement::SetStyle(XMLFile* file, const std::string& typeName)
  492. {
  493. if (!file)
  494. return;
  495. XMLElement rootElem = file->GetRootElement();
  496. XMLElement childElem = rootElem.GetChildElement("element");
  497. while (childElem)
  498. {
  499. if (childElem.GetString("type") == typeName)
  500. {
  501. SetStyle(childElem);
  502. return;
  503. }
  504. childElem = childElem.GetNextElement("element");
  505. }
  506. }
  507. void UIElement::SetStyleAuto(XMLFile* file)
  508. {
  509. SetStyle(file, GetTypeName());
  510. }
  511. void UIElement::SetLayout(LayoutMode mode, int spacing, const IntRect& border)
  512. {
  513. layoutMode_ = mode;
  514. layoutSpacing_ = Max(spacing, 0);
  515. layoutBorder_ = IntRect(Max(border.left_, 0), Max(border.top_, 0), Max(border.right_, 0), Max(border.bottom_, 0));
  516. UpdateLayout();
  517. }
  518. void UIElement::SetLayoutMode(LayoutMode mode)
  519. {
  520. layoutMode_ = mode;
  521. UpdateLayout();
  522. }
  523. void UIElement::SetLayoutSpacing(int spacing)
  524. {
  525. layoutSpacing_ = Max(spacing, 0);
  526. UpdateLayout();
  527. }
  528. void UIElement::SetLayoutBorder(const IntRect& border)
  529. {
  530. layoutBorder_ = IntRect(Max(border.left_, 0), Max(border.top_, 0), Max(border.right_, 0), Max(border.bottom_, 0));
  531. UpdateLayout();
  532. }
  533. void UIElement::SetUserData(const VariantMap& userData)
  534. {
  535. userData_ = userData;
  536. }
  537. void UIElement::UpdateLayout()
  538. {
  539. if ((layoutMode_ == LM_FREE) || (layoutNestingLevel_))
  540. return;
  541. // Prevent further updates while this update happens
  542. DisableLayoutUpdate();
  543. std::vector<int> positions;
  544. std::vector<int> sizes;
  545. std::vector<int> minSizes;
  546. std::vector<int> maxSizes;
  547. if (layoutMode_ == LM_HORIZONTAL)
  548. {
  549. int minChildHeight = 0;
  550. for (unsigned i = 0; i < children_.size(); ++i)
  551. {
  552. if (!children_[i]->IsVisible())
  553. continue;
  554. positions.push_back(0);
  555. sizes.push_back(children_[i]->GetWidth());
  556. minSizes.push_back(children_[i]->GetMinWidth());
  557. maxSizes.push_back(children_[i]->GetMaxWidth());
  558. minChildHeight = Max(minChildHeight, children_[i]->GetMinHeight());
  559. }
  560. CalculateLayout(positions, sizes, minSizes, maxSizes, GetWidth(), layoutBorder_.left_, layoutBorder_.right_,
  561. layoutSpacing_);
  562. int width = CalculateLayoutParentSize(sizes, layoutBorder_.left_, layoutBorder_.right_, layoutSpacing_);
  563. int height = Max(GetHeight(), minChildHeight + layoutBorder_.top_ + layoutBorder_.bottom_);
  564. int minWidth = Min(CalculateLayoutParentSize(minSizes, layoutBorder_.left_, layoutBorder_.right_, layoutSpacing_), maxSize_.x_);
  565. int minHeight = Min(minChildHeight + layoutBorder_.top_ + layoutBorder_.bottom_, maxSize_.y_);
  566. // Respect fixed size if already set
  567. if (minSize_.x_ != maxSize_.x_)
  568. minSize_.x_ = minWidth;
  569. if (minSize_.y_ != maxSize_.y_)
  570. minSize_.y_ = minHeight;
  571. SetSize(width, height);
  572. // Validate the size before resizing child elements, in case of min/max limits
  573. width = size_.x_;
  574. height = size_.y_;
  575. unsigned j = 0;
  576. for (unsigned i = 0; i < children_.size(); ++i)
  577. {
  578. if (!children_[i]->IsVisible())
  579. continue;
  580. children_[i]->SetHorizontalAlignment(HA_LEFT);
  581. children_[i]->SetPosition(positions[j], GetLayoutChildPosition(children_[i]).y_);
  582. children_[i]->SetSize(sizes[j], height - layoutBorder_.top_ - layoutBorder_.bottom_);
  583. ++j;
  584. }
  585. }
  586. if (layoutMode_ == LM_VERTICAL)
  587. {
  588. int minChildWidth = 0;
  589. int maxChildWidth = M_MAX_INT;
  590. for (unsigned i = 0; i < children_.size(); ++i)
  591. {
  592. if (!children_[i]->IsVisible())
  593. continue;
  594. positions.push_back(0);
  595. sizes.push_back(children_[i]->GetHeight());
  596. minSizes.push_back(children_[i]->GetMinHeight());
  597. maxSizes.push_back(children_[i]->GetMaxHeight());
  598. minChildWidth = Max(minChildWidth, children_[i]->GetMinWidth());
  599. }
  600. CalculateLayout(positions, sizes, minSizes, maxSizes, GetHeight(), layoutBorder_.top_, layoutBorder_.bottom_,
  601. layoutSpacing_);
  602. int height = CalculateLayoutParentSize(sizes, layoutBorder_.top_, layoutBorder_.bottom_, layoutSpacing_);
  603. int width = Max(GetWidth(), minChildWidth + layoutBorder_.left_ + layoutBorder_.right_);
  604. int minHeight = Min(CalculateLayoutParentSize(minSizes, layoutBorder_.top_, layoutBorder_.bottom_, layoutSpacing_), maxSize_.y_);
  605. int minWidth = Min(minChildWidth + layoutBorder_.left_ + layoutBorder_.right_, maxSize_.x_);
  606. if (minSize_.x_ != maxSize_.x_)
  607. minSize_.x_ = minWidth;
  608. if (minSize_.y_ != maxSize_.y_)
  609. minSize_.y_ = minHeight;
  610. SetSize(width, height);
  611. width = size_.x_;
  612. height = size_.y_;
  613. unsigned j = 0;
  614. for (unsigned i = 0; i < children_.size(); ++i)
  615. {
  616. if (!children_[i]->IsVisible())
  617. continue;
  618. children_[i]->SetVerticalAlignment(VA_TOP);
  619. children_[i]->SetPosition(GetLayoutChildPosition(children_[i]).x_, positions[j]);
  620. children_[i]->SetSize(width - layoutBorder_.left_ - layoutBorder_.right_, sizes[j]);
  621. ++j;
  622. }
  623. }
  624. EnableLayoutUpdate();
  625. }
  626. void UIElement::DisableLayoutUpdate()
  627. {
  628. ++layoutNestingLevel_;
  629. }
  630. void UIElement::EnableLayoutUpdate()
  631. {
  632. --layoutNestingLevel_;
  633. }
  634. void UIElement::BringToFront()
  635. {
  636. // Follow the parent chain to the top level window. If it has BringToFront mode, bring it to front now
  637. UIElement* root = GetRootElement();
  638. UIElement* ptr = this;
  639. while ((ptr) && (ptr->GetParent() != root))
  640. ptr = ptr->GetParent();
  641. if ((!ptr) || (!ptr->GetBringToFront()))
  642. return;
  643. // Get the highest priority used by all other top level elements, assign that to the new front element
  644. // and decrease others' priority by one. However, take into account only active (enabled) elements
  645. // and those which have the BringToBack flag set
  646. int maxPriority = M_MIN_INT;
  647. std::vector<UIElement*> topLevelElements = root->GetChildren();
  648. for (std::vector<UIElement*>::iterator i = topLevelElements.begin(); i != topLevelElements.end(); ++i)
  649. {
  650. UIElement* other = *i;
  651. if ((other->IsActive()) && (other->bringToBack_) && (other != ptr))
  652. {
  653. int priority = other->GetPriority();
  654. maxPriority = Max(priority, maxPriority);
  655. other->SetPriority(priority - 1);
  656. }
  657. }
  658. if (maxPriority != M_MIN_INT)
  659. ptr->SetPriority(maxPriority);
  660. }
  661. void UIElement::AddChild(UIElement* element)
  662. {
  663. InsertChild(children_.size(), element);
  664. }
  665. void UIElement::InsertChild(unsigned index, UIElement* element)
  666. {
  667. // Check for illegal parent assignments
  668. if ((!element) || (element == this) || (element->parent_ == this) || (parent_ == element))
  669. return;
  670. // Add first, then remove from old parent, to ensure the element does not get deleted
  671. if (index >= children_.size())
  672. children_.push_back(SharedPtr<UIElement>(element));
  673. else
  674. children_.insert(children_.begin() + index, SharedPtr<UIElement>(element));
  675. if (element->parent_)
  676. element->parent_->RemoveChild(element);
  677. element->parent_ = this;
  678. element->MarkDirty();
  679. UpdateLayout();
  680. }
  681. void UIElement::RemoveChild(UIElement* element)
  682. {
  683. for (std::vector<SharedPtr<UIElement> >::iterator i = children_.begin(); i != children_.end(); ++i)
  684. {
  685. if ((*i) == element)
  686. {
  687. element->parent_ = 0;
  688. element->MarkDirty();
  689. children_.erase(i);
  690. UpdateLayout();
  691. return;
  692. }
  693. }
  694. }
  695. void UIElement::RemoveAllChildren()
  696. {
  697. while (children_.size())
  698. {
  699. const SharedPtr<UIElement>& element = children_.back();
  700. element->parent_ = 0;
  701. element->MarkDirty();
  702. children_.pop_back();
  703. }
  704. }
  705. void UIElement::Remove()
  706. {
  707. if (parent_)
  708. parent_->RemoveChild(this);
  709. }
  710. void UIElement::SetParent(UIElement* parent)
  711. {
  712. if (parent)
  713. parent->AddChild(this);
  714. }
  715. IntVector2 UIElement::GetScreenPosition()
  716. {
  717. if (positionDirty_)
  718. {
  719. IntVector2 pos = position_;
  720. const UIElement* parent = parent_;
  721. const UIElement* current = this;
  722. while (parent)
  723. {
  724. switch (current->horizontalAlignment_)
  725. {
  726. case HA_LEFT:
  727. pos.x_ += parent->position_.x_;
  728. break;
  729. case HA_CENTER:
  730. pos.x_ += parent->position_.x_ + parent->size_.x_ / 2 - current->size_.x_ / 2;
  731. break;
  732. case HA_RIGHT:
  733. pos.x_ += parent->position_.x_ + parent->size_.x_ - current->size_.x_;
  734. break;
  735. }
  736. switch (current->verticalAlignment_)
  737. {
  738. case VA_TOP:
  739. pos.y_ += parent->position_.y_;
  740. break;
  741. case VA_CENTER:
  742. pos.y_ += parent->position_.y_ + parent->size_.y_ / 2 - current->size_.y_ / 2;
  743. break;
  744. case VA_BOTTOM:
  745. pos.y_ += parent->position_.y_ + parent->size_.y_ - current->size_.y_;
  746. break;
  747. }
  748. pos += parent->childOffset_;
  749. current = parent;
  750. parent = parent->parent_;
  751. }
  752. screenPosition_ = pos;
  753. positionDirty_ = false;
  754. }
  755. return screenPosition_;
  756. }
  757. float UIElement::GetDerivedOpacity()
  758. {
  759. if (opacityDirty_)
  760. {
  761. float opacity = opacity_;
  762. const UIElement* parent = parent_;
  763. while (parent)
  764. {
  765. opacity *= parent->opacity_;
  766. parent = parent->parent_;
  767. }
  768. derivedOpacity_ = opacity;
  769. opacityDirty_ = false;
  770. }
  771. return derivedOpacity_;
  772. }
  773. std::vector<UIElement*> UIElement::GetChildren(bool recursive) const
  774. {
  775. if (!recursive)
  776. {
  777. std::vector<UIElement*> ret;
  778. for (std::vector<SharedPtr<UIElement> >::const_iterator i = children_.begin(); i != children_.end(); ++i)
  779. ret.push_back(*i);
  780. return ret;
  781. }
  782. else
  783. {
  784. std::vector<UIElement*> allChildren;
  785. GetChildrenRecursive(allChildren);
  786. return allChildren;
  787. }
  788. }
  789. unsigned UIElement::GetNumChildren(bool recursive) const
  790. {
  791. if (!recursive)
  792. return children_.size();
  793. else
  794. {
  795. unsigned allChildren = children_.size();
  796. for (std::vector<SharedPtr<UIElement> >::const_iterator i = children_.begin(); i != children_.end(); ++i)
  797. allChildren += (*i)->GetNumChildren(true);
  798. return allChildren;
  799. }
  800. }
  801. UIElement* UIElement::GetChild(unsigned index) const
  802. {
  803. return index < children_.size() ? children_[index] : (UIElement*)0;
  804. }
  805. UIElement* UIElement::GetChild(const std::string& name, bool recursive) const
  806. {
  807. for (std::vector<SharedPtr<UIElement> >::const_iterator i = children_.begin(); i != children_.end(); ++i)
  808. {
  809. if ((*i)->name_ == name)
  810. return *i;
  811. if (recursive)
  812. {
  813. UIElement* element = (*i)->GetChild(name, true);
  814. if (element)
  815. return element;
  816. }
  817. }
  818. return 0;
  819. }
  820. UIElement* UIElement::GetRootElement() const
  821. {
  822. UIElement* root = parent_;
  823. if (!root)
  824. return 0;
  825. while (root->GetParent())
  826. root = root->GetParent();
  827. return root;
  828. }
  829. unsigned UIElement::GetUIntColor()
  830. {
  831. if (derivedColorDirty_)
  832. {
  833. Color color = color_[C_TOPLEFT];
  834. color.a_ *= GetDerivedOpacity();
  835. uintColor_ = color.ToUInt();
  836. derivedColorDirty_ = false;
  837. }
  838. return uintColor_;
  839. }
  840. IntVector2 UIElement::ScreenToElement(const IntVector2& screenPosition)
  841. {
  842. return screenPosition - GetScreenPosition();
  843. }
  844. IntVector2 UIElement::ElementToScreen(const IntVector2& position)
  845. {
  846. return position + GetScreenPosition();
  847. }
  848. bool UIElement::IsInside(IntVector2 position, bool isScreen)
  849. {
  850. if (isScreen)
  851. position = ScreenToElement(position);
  852. return (position.x_ >= 0) && (position.y_ >= 0) && (position.x_ < size_.x_) && (position.y_ < size_.y_);
  853. }
  854. bool UIElement::IsInsideCombined(IntVector2 position, bool isScreen)
  855. {
  856. // If child elements are clipped, no need to expand the rect
  857. if (clipChildren_)
  858. return IsInside(position, isScreen);
  859. if (!isScreen)
  860. position = ElementToScreen(position);
  861. IntRect combined = GetCombinedScreenRect();
  862. return (position.x_ >= combined.left_) && (position.y_ >= combined.top_) && (position.x_ < combined.right_) &&
  863. (position.y_ < combined.bottom_);
  864. }
  865. IntRect UIElement::GetCombinedScreenRect()
  866. {
  867. IntVector2 screenPosition(GetScreenPosition());
  868. IntRect combined(screenPosition.x_, screenPosition.y_, screenPosition.x_ + size_.x_, screenPosition.y_ + size_.y_);
  869. for (std::vector<SharedPtr<UIElement> >::iterator i = children_.begin(); i != children_.end(); ++i)
  870. {
  871. IntVector2 childPos = (*i)->GetScreenPosition();
  872. const IntVector2& childSize = (*i)->GetSize();
  873. if (childPos.x_ < combined.left_)
  874. combined.left_ = childPos.x_;
  875. if (childPos.y_ < combined.top_)
  876. combined.top_ = childPos.y_;
  877. if (childPos.x_ + childSize.x_ > combined.right_)
  878. combined.right_ = childPos.x_ + childSize.x_;
  879. if (childPos.y_ + childSize.y_ > combined.bottom_)
  880. combined.bottom_ = childPos.y_ + childSize.y_;
  881. }
  882. return combined;
  883. }
  884. void UIElement::SetChildOffset(const IntVector2& offset)
  885. {
  886. if (offset != childOffset_)
  887. {
  888. childOffset_ = offset;
  889. for (std::vector<SharedPtr<UIElement> >::const_iterator i = children_.begin(); i != children_.end(); ++i)
  890. (*i)->MarkDirty();
  891. }
  892. }
  893. void UIElement::SetHovering(bool enable)
  894. {
  895. hovering_ = enable;
  896. }
  897. void UIElement::AdjustScissor(IntRect& currentScissor)
  898. {
  899. if (clipChildren_)
  900. {
  901. IntVector2 screenPos = GetScreenPosition();
  902. currentScissor.left_ = Max(currentScissor.left_, screenPos.x_ + clipBorder_.left_);
  903. currentScissor.top_ = Max(currentScissor.top_, screenPos.y_ + clipBorder_.top_);
  904. currentScissor.right_ = Min(currentScissor.right_, screenPos.x_ + size_.x_ - clipBorder_.right_);
  905. currentScissor.bottom_ = Min(currentScissor.bottom_, screenPos.y_ + size_.y_ - clipBorder_.bottom_);
  906. if (currentScissor.right_ < currentScissor.left_)
  907. currentScissor.right_ = currentScissor.left_;
  908. if (currentScissor.bottom_ < currentScissor.top_)
  909. currentScissor.bottom_ = currentScissor.top_;
  910. }
  911. }
  912. void UIElement::GetBatchesWithOffset(IntVector2& offset, std::vector<UIBatch>& batches, std::vector<UIQuad>& quads, IntRect
  913. currentScissor)
  914. {
  915. unsigned initialSize = quads.size();
  916. GetBatches(batches, quads, currentScissor);
  917. for (unsigned i = initialSize; i < quads.size(); ++i)
  918. {
  919. quads[i].left_ += offset.x_;
  920. quads[i].top_ += offset.y_;
  921. quads[i].right_ += offset.x_;
  922. quads[i].bottom_ += offset.y_;
  923. }
  924. AdjustScissor(currentScissor);
  925. for (std::vector<SharedPtr<UIElement> >::const_iterator i = children_.begin(); i != children_.end(); ++i)
  926. {
  927. if ((*i)->IsVisible())
  928. (*i)->GetBatchesWithOffset(offset, batches, quads, currentScissor);
  929. }
  930. }
  931. void UIElement::MarkDirty()
  932. {
  933. positionDirty_ = true;
  934. opacityDirty_ = true;
  935. derivedColorDirty_ = true;
  936. for (std::vector<SharedPtr<UIElement> >::const_iterator i = children_.begin(); i != children_.end(); ++i)
  937. (*i)->MarkDirty();
  938. }
  939. void UIElement::GetChildrenRecursive(std::vector<UIElement*>& dest) const
  940. {
  941. for (std::vector<SharedPtr<UIElement> >::const_iterator i = children_.begin(); i != children_.end(); ++i)
  942. {
  943. dest.push_back(*i);
  944. (*i)->GetChildrenRecursive(dest);
  945. }
  946. }
  947. int UIElement::CalculateLayoutParentSize(const std::vector<int>& sizes, int begin, int end, int spacing)
  948. {
  949. int width = begin + end;
  950. for (unsigned i = 0; i < sizes.size(); ++i)
  951. {
  952. // If calculating maximum size, and the default is specified, do not overflow it
  953. if (sizes[i] == M_MAX_INT)
  954. return M_MAX_INT;
  955. width += sizes[i];
  956. if (i < sizes.size() - 1)
  957. width += spacing;
  958. }
  959. return width;
  960. }
  961. void UIElement::CalculateLayout(std::vector<int>& positions, std::vector<int>& sizes, const std::vector<int>& minSizes,
  962. const std::vector<int>& maxSizes, int targetSize, int begin, int end, int spacing)
  963. {
  964. int numChildren = sizes.size();
  965. if (!numChildren)
  966. return;
  967. int targetTotalSize = targetSize - begin - end - (numChildren - 1) * spacing;
  968. if (targetTotalSize < 0)
  969. targetTotalSize = 0;
  970. int targetChildSize = targetTotalSize / numChildren;
  971. int remainder = targetTotalSize % numChildren;
  972. float add = (float)remainder / numChildren;
  973. float acc = 0.0f;
  974. // Initial pass
  975. for (int i = 0; i < numChildren; ++i)
  976. {
  977. int targetSize = targetChildSize;
  978. if (remainder)
  979. {
  980. acc += add;
  981. if (acc >= 0.5f)
  982. {
  983. acc -= 1.0f;
  984. ++targetSize;
  985. --remainder;
  986. }
  987. }
  988. sizes[i] = Clamp(targetSize, minSizes[i], maxSizes[i]);
  989. }
  990. // Error correction passes
  991. for (;;)
  992. {
  993. int actualTotalSize = 0;
  994. for (int i = 0; i < numChildren; ++i)
  995. actualTotalSize += sizes[i];
  996. int error = targetTotalSize - actualTotalSize;
  997. // Break if no error
  998. if (!error)
  999. break;
  1000. // Check which of the children can be resized to correct the error. If none, must break
  1001. std::vector<unsigned> resizable;
  1002. for (int i = 0; i < numChildren; ++i)
  1003. {
  1004. if ((error < 0) && (sizes[i] > minSizes[i]))
  1005. resizable.push_back(i);
  1006. else if ((error > 0) && (sizes[i] < maxSizes[i]))
  1007. resizable.push_back(i);
  1008. }
  1009. if (resizable.empty())
  1010. break;
  1011. int numResizable = resizable.size();
  1012. int errorPerChild = error / numResizable;
  1013. remainder = (abs(error)) % numResizable;
  1014. add = (float)remainder / numResizable;
  1015. acc = 0.0f;
  1016. for (int i = 0; i < numResizable; ++i)
  1017. {
  1018. unsigned idx = resizable[i];
  1019. int targetSize = sizes[idx] + errorPerChild;
  1020. if (remainder)
  1021. {
  1022. acc += add;
  1023. if (acc >= 0.5f)
  1024. {
  1025. acc -= 1.0f;
  1026. targetSize = error < 0 ? targetSize - 1 : targetSize + 1;
  1027. --remainder;
  1028. }
  1029. }
  1030. sizes[idx] = Clamp(targetSize, minSizes[idx], maxSizes[idx]);
  1031. }
  1032. }
  1033. // Calculate final positions
  1034. int position = begin;
  1035. for (int i = 0; i < numChildren; ++i)
  1036. {
  1037. positions[i] = position;
  1038. position += sizes[i];
  1039. position += spacing;
  1040. }
  1041. }
  1042. IntVector2 UIElement::GetLayoutChildPosition(UIElement* child)
  1043. {
  1044. IntVector2 ret(IntVector2::ZERO);
  1045. HorizontalAlignment ha = child->GetHorizontalAlignment();
  1046. switch (ha)
  1047. {
  1048. case HA_LEFT:
  1049. ret.x_ = layoutBorder_.left_;
  1050. break;
  1051. case HA_RIGHT:
  1052. ret.x_ = -layoutBorder_.right_;
  1053. break;
  1054. }
  1055. VerticalAlignment va = child->GetVerticalAlignment();
  1056. switch (va)
  1057. {
  1058. case VA_TOP:
  1059. ret.y_ = layoutBorder_.top_;
  1060. break;
  1061. case VA_BOTTOM:
  1062. ret.y_ = -layoutBorder_.bottom_;
  1063. break;
  1064. }
  1065. return ret;
  1066. }