UIElement.cpp 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239
  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 String horizontalAlignments[] =
  31. {
  32. "left",
  33. "center",
  34. "right",
  35. ""
  36. };
  37. static const String verticalAlignments[] =
  38. {
  39. "top",
  40. "center",
  41. "bottom",
  42. ""
  43. };
  44. static const String focusModes[] =
  45. {
  46. "notfocusable",
  47. "resetfocus",
  48. "focusable",
  49. "focusabledefocusable",
  50. ""
  51. };
  52. static const String dragDropModes[] =
  53. {
  54. "disabled",
  55. "source",
  56. "target",
  57. "sourceandtarget",
  58. ""
  59. };
  60. OBJECTTYPESTATIC(UIElement);
  61. UIElement::UIElement(Context* context) :
  62. Object(context),
  63. parent_(0),
  64. clipBorder_(IntRect::ZERO),
  65. priority_(0),
  66. opacity_(1.0f),
  67. bringToFront_(false),
  68. bringToBack_(true),
  69. clipChildren_(false),
  70. active_(false),
  71. focusMode_(FM_NOTFOCUSABLE),
  72. focus_(false),
  73. selected_(false),
  74. visible_(true),
  75. hovering_(false),
  76. dragDropMode_(DD_DISABLED),
  77. layoutMode_(LM_FREE),
  78. layoutSpacing_(0),
  79. layoutBorder_(IntRect::ZERO),
  80. resizeNestingLevel_(0),
  81. layoutNestingLevel_(0),
  82. position_(IntVector2::ZERO),
  83. size_(IntVector2::ZERO),
  84. minSize_(IntVector2::ZERO),
  85. maxSize_(M_MAX_INT, M_MAX_INT),
  86. childOffset_(IntVector2::ZERO),
  87. horizontalAlignment_(HA_LEFT),
  88. verticalAlignment_(VA_TOP),
  89. positionDirty_(true),
  90. opacityDirty_(true),
  91. derivedColorDirty_(true),
  92. colorGradient_(false)
  93. {
  94. }
  95. UIElement::~UIElement()
  96. {
  97. // If child elements have outside references, detach them
  98. while (children_.Size())
  99. {
  100. const SharedPtr<UIElement>& element = children_.Back();
  101. if (element.GetRefCount() > 1)
  102. {
  103. element->parent_ = 0;
  104. element->MarkDirty();
  105. }
  106. children_.Pop();
  107. }
  108. }
  109. void UIElement::RegisterObject(Context* context)
  110. {
  111. context->RegisterFactory<UIElement>();
  112. }
  113. void UIElement::SetStyle(const XMLElement& element)
  114. {
  115. if (element.HasAttribute("name"))
  116. name_ = element.GetString("name");
  117. if (element.HasChildElement("position"))
  118. SetPosition(element.GetChildElement("position").GetIntVector2("value"));
  119. if (element.HasChildElement("size"))
  120. SetSize(element.GetChildElement("size").GetIntVector2("value"));
  121. if (element.HasChildElement("width"))
  122. SetWidth(element.GetChildElement("width").GetInt("value"));
  123. if (element.HasChildElement("height"))
  124. SetHeight(element.GetChildElement("height").GetInt("value"));
  125. if (element.HasChildElement("minsize"))
  126. SetMinSize(element.GetChildElement("minsize").GetIntVector2("value"));
  127. if (element.HasChildElement("minwidth"))
  128. SetMinWidth(element.GetChildElement("minwidth").GetInt("value"));
  129. if (element.HasChildElement("minheight"))
  130. SetMinHeight(element.GetChildElement("minheight").GetInt("value"));
  131. if (element.HasChildElement("maxsize"))
  132. SetMaxSize(element.GetChildElement("maxsize").GetIntVector2("value"));
  133. if (element.HasChildElement("maxwidth"))
  134. SetMinWidth(element.GetChildElement("maxwidth").GetInt("value"));
  135. if (element.HasChildElement("maxheight"))
  136. SetMinHeight(element.GetChildElement("maxheight").GetInt("value"));
  137. if (element.HasChildElement("fixedsize"))
  138. SetFixedSize(element.GetChildElement("fixedsize").GetIntVector2("value"));
  139. if (element.HasChildElement("fixedwidth"))
  140. SetFixedWidth(element.GetChildElement("fixedwidth").GetInt("value"));
  141. if (element.HasChildElement("fixedheight"))
  142. SetFixedHeight(element.GetChildElement("fixedheight").GetInt("value"));
  143. if (element.HasChildElement("alignment"))
  144. {
  145. XMLElement alignElem = element.GetChildElement("alignment");
  146. String horiz;
  147. String vert;
  148. if (alignElem.HasAttribute("horizontal"))
  149. horiz = alignElem.GetStringLower("horizontal");
  150. if (alignElem.HasAttribute("vertical"))
  151. vert = alignElem.GetStringLower("vertical");
  152. if (alignElem.HasAttribute("h"))
  153. horiz = alignElem.GetStringLower("h");
  154. if (alignElem.HasAttribute("v"))
  155. vert = alignElem.GetStringLower("v");
  156. if (!horiz.Empty())
  157. SetHorizontalAlignment((HorizontalAlignment)GetStringListIndex(horiz, horizontalAlignments, HA_LEFT));
  158. if (!vert.Empty())
  159. SetVerticalAlignment((VerticalAlignment)GetStringListIndex(vert, verticalAlignments, VA_TOP));
  160. }
  161. if (element.HasChildElement("clipborder"))
  162. SetClipBorder(element.GetChildElement("clipborder").GetIntRect("value"));
  163. if (element.HasChildElement("priority"))
  164. SetPriority(element.GetChildElement("priority").GetInt("value"));
  165. if (element.HasChildElement("opacity"))
  166. SetOpacity(element.GetChildElement("opacity").GetFloat("value"));
  167. if (element.HasChildElement("color"))
  168. {
  169. XMLElement colorElem = element.GetChildElement("color");
  170. if (colorElem.HasAttribute("value"))
  171. SetColor(colorElem.GetColor("value"));
  172. if (colorElem.HasAttribute("topleft"))
  173. SetColor(C_TOPLEFT, colorElem.GetColor("topleft"));
  174. if (colorElem.HasAttribute("topright"))
  175. SetColor(C_TOPRIGHT, colorElem.GetColor("topright"));
  176. if (colorElem.HasAttribute("bottomleft"))
  177. SetColor(C_BOTTOMLEFT, colorElem.GetColor("bottomleft"));
  178. if (colorElem.HasAttribute("bottomright"))
  179. SetColor(C_BOTTOMRIGHT, colorElem.GetColor("bottomright"));
  180. }
  181. if (element.HasChildElement("bringtofront"))
  182. SetBringToFront(element.GetChildElement("bringtofront").GetBool("enable"));
  183. if (element.HasChildElement("bringtoback"))
  184. SetBringToBack(element.GetChildElement("bringtoback").GetBool("enable"));
  185. if (element.HasChildElement("clipchildren"))
  186. SetClipChildren(element.GetChildElement("clipchildren").GetBool("enable"));
  187. if (element.HasChildElement("enabled"))
  188. SetActive(element.GetChildElement("enabled").GetBool("enable"));
  189. if (element.HasChildElement("selected"))
  190. SetSelected(element.GetChildElement("selected").GetBool("enable"));
  191. if (element.HasChildElement("visible"))
  192. SetVisible(element.GetChildElement("visible").GetBool("enable"));
  193. if (element.HasChildElement("focusmode"))
  194. {
  195. String focusMode = element.GetChildElement("focusmode").GetStringLower("value");
  196. SetFocusMode((FocusMode)GetStringListIndex(focusMode, focusModes, FM_NOTFOCUSABLE));
  197. if (focusMode == "defocusable")
  198. SetFocusMode(FM_FOCUSABLE_DEFOCUSABLE);
  199. }
  200. if (element.HasChildElement("dragdropmode"))
  201. {
  202. String dragDropMode = element.GetChildElement("dragdropmode").GetStringLower("value");
  203. SetDragDropMode(GetStringListIndex(dragDropMode, dragDropModes, DD_DISABLED));
  204. }
  205. if (element.HasChildElement("layout"))
  206. {
  207. XMLElement layoutElem = element.GetChildElement("layout");
  208. String mode = layoutElem.GetStringLower("mode");
  209. if (mode == "free")
  210. layoutMode_ = LM_FREE;
  211. if ((mode == "horizontal") || (mode == "h"))
  212. layoutMode_ = LM_HORIZONTAL;
  213. if ((mode == "vertical") || (mode == "v"))
  214. layoutMode_ = LM_VERTICAL;
  215. if (layoutElem.HasAttribute("spacing"))
  216. layoutSpacing_ = Max(layoutElem.GetInt("spacing"), 0);
  217. if (layoutElem.HasAttribute("border"))
  218. SetLayoutBorder(layoutElem.GetIntRect("border"));
  219. else
  220. UpdateLayout();
  221. }
  222. if (element.HasChildElement("vars"))
  223. vars_ = element.GetChildElement("vars").GetVariantMap();
  224. }
  225. void UIElement::Update(float timeStep)
  226. {
  227. }
  228. void UIElement::GetBatches(PODVector<UIBatch>& batches, PODVector<UIQuad>& quads, const IntRect& currentScissor)
  229. {
  230. // Reset hovering for next frame
  231. hovering_ = false;
  232. }
  233. void UIElement::OnHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
  234. {
  235. hovering_ = true;
  236. }
  237. void UIElement::OnClick(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
  238. {
  239. }
  240. void UIElement::OnDragStart(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
  241. {
  242. }
  243. void UIElement::OnDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
  244. {
  245. }
  246. void UIElement::OnDragEnd(const IntVector2& position, const IntVector2& screenPosition, Cursor* cursor)
  247. {
  248. }
  249. bool UIElement::OnDragDropTest(UIElement* source)
  250. {
  251. return true;
  252. }
  253. bool UIElement::OnDragDropFinish(UIElement* source)
  254. {
  255. return true;
  256. }
  257. void UIElement::OnWheel(int delta, int buttons, int qualifiers)
  258. {
  259. }
  260. void UIElement::OnKey(int key, int buttons, int qualifiers)
  261. {
  262. }
  263. void UIElement::OnChar(unsigned char c, int buttons, int qualifiers)
  264. {
  265. }
  266. void UIElement::OnResize()
  267. {
  268. }
  269. void UIElement::OnFocus()
  270. {
  271. }
  272. void UIElement::OnDefocus()
  273. {
  274. }
  275. void UIElement::SetName(const String& name)
  276. {
  277. name_ = name;
  278. }
  279. void UIElement::SetPosition(const IntVector2& position)
  280. {
  281. if (position != position_)
  282. {
  283. position_ = position;
  284. MarkDirty();
  285. }
  286. }
  287. void UIElement::SetPosition(int x, int y)
  288. {
  289. SetPosition(IntVector2(x, y));
  290. }
  291. void UIElement::SetSize(const IntVector2& size)
  292. {
  293. ++resizeNestingLevel_;
  294. IntVector2 validatedSize;
  295. validatedSize.x_ = Clamp(size.x_, minSize_.x_, maxSize_.x_);
  296. validatedSize.y_ = Clamp(size.y_, minSize_.y_, maxSize_.y_);
  297. if (validatedSize != size_)
  298. {
  299. size_ = validatedSize;
  300. if (resizeNestingLevel_ == 1)
  301. {
  302. // Check if parent element's layout needs to be updated first
  303. if (parent_)
  304. parent_->UpdateLayout();
  305. MarkDirty();
  306. OnResize();
  307. UpdateLayout();
  308. using namespace Resized;
  309. VariantMap eventData;
  310. eventData[P_ELEMENT] = (void*)this;
  311. eventData[P_WIDTH] = size_.x_;
  312. eventData[P_HEIGHT] = size_.y_;
  313. SendEvent(E_RESIZED, eventData);
  314. }
  315. }
  316. --resizeNestingLevel_;
  317. }
  318. void UIElement::SetSize(int width, int height)
  319. {
  320. SetSize(IntVector2(width, height));
  321. }
  322. void UIElement::SetWidth(int width)
  323. {
  324. SetSize(IntVector2(width, size_.y_));
  325. }
  326. void UIElement::SetHeight(int height)
  327. {
  328. SetSize(IntVector2(size_.x_, height));
  329. }
  330. void UIElement::SetMinSize(const IntVector2& minSize)
  331. {
  332. minSize_.x_ = Max(minSize.x_, 0);
  333. minSize_.y_ = Max(minSize.y_, 0);
  334. SetSize(size_);
  335. }
  336. void UIElement::SetMinSize(int width, int height)
  337. {
  338. SetMinSize(IntVector2(width, height));
  339. }
  340. void UIElement::SetMinWidth(int width)
  341. {
  342. SetMinSize(IntVector2(width, minSize_.y_));
  343. }
  344. void UIElement::SetMinHeight(int height)
  345. {
  346. SetMinSize(IntVector2(minSize_.x_, height));
  347. }
  348. void UIElement::SetMaxSize(const IntVector2& maxSize)
  349. {
  350. maxSize_.x_ = Max(maxSize.x_, 0);
  351. maxSize_.y_ = Max(maxSize.y_, 0);
  352. SetSize(size_);
  353. }
  354. void UIElement::SetMaxSize(int width, int height)
  355. {
  356. SetMaxSize(IntVector2(width, height));
  357. }
  358. void UIElement::SetMaxWidth(int width)
  359. {
  360. SetMaxSize(IntVector2(width, maxSize_.y_));
  361. }
  362. void UIElement::SetMaxHeight(int height)
  363. {
  364. SetMaxSize(IntVector2(maxSize_.x_, height));
  365. }
  366. void UIElement::SetFixedSize(const IntVector2& size)
  367. {
  368. minSize_ = maxSize_ = IntVector2(Max(size.x_, 0), Max(size.y_, 0));
  369. SetSize(size);
  370. }
  371. void UIElement::SetFixedSize(int width, int height)
  372. {
  373. SetFixedSize(IntVector2(width, height));
  374. }
  375. void UIElement::SetFixedWidth(int width)
  376. {
  377. minSize_.x_ = maxSize_.x_ = Max(width, 0);
  378. SetWidth(width);
  379. }
  380. void UIElement::SetFixedHeight(int height)
  381. {
  382. minSize_.y_ = maxSize_.y_ = Max(height, 0);
  383. SetHeight(height);
  384. }
  385. void UIElement::SetAlignment(HorizontalAlignment hAlign, VerticalAlignment vAlign)
  386. {
  387. horizontalAlignment_ = hAlign;
  388. verticalAlignment_ = vAlign;
  389. MarkDirty();
  390. }
  391. void UIElement::SetHorizontalAlignment(HorizontalAlignment align)
  392. {
  393. horizontalAlignment_ = align;
  394. MarkDirty();
  395. }
  396. void UIElement::SetVerticalAlignment(VerticalAlignment align)
  397. {
  398. verticalAlignment_ = align;
  399. MarkDirty();
  400. }
  401. void UIElement::SetClipBorder(const IntRect& rect)
  402. {
  403. clipBorder_.left_ = Max(rect.left_, 0);
  404. clipBorder_.top_ = Max(rect.top_, 0);
  405. clipBorder_.right_ = Max(rect.right_, 0);
  406. clipBorder_.bottom_ = Max(rect.bottom_, 0);
  407. }
  408. void UIElement::SetColor(const Color& color)
  409. {
  410. for (unsigned i = 0; i < MAX_UIELEMENT_CORNERS; ++i)
  411. color_[i] = color;
  412. colorGradient_ = false;
  413. derivedColorDirty_ = true;
  414. }
  415. void UIElement::SetColor(Corner corner, const Color& color)
  416. {
  417. color_[corner] = color;
  418. colorGradient_ = false;
  419. derivedColorDirty_ = true;
  420. for (unsigned i = 0; i < MAX_UIELEMENT_CORNERS; ++i)
  421. {
  422. if ((i != corner) && (color_[i] != color_[corner]))
  423. colorGradient_ = true;
  424. }
  425. }
  426. void UIElement::SetPriority(int priority)
  427. {
  428. priority_ = priority;
  429. }
  430. void UIElement::SetOpacity(float opacity)
  431. {
  432. opacity_ = Clamp(opacity, 0.0f, 1.0f);
  433. MarkDirty();
  434. }
  435. void UIElement::SetBringToFront(bool enable)
  436. {
  437. bringToFront_ = enable;
  438. }
  439. void UIElement::SetBringToBack(bool enable)
  440. {
  441. bringToBack_ = enable;
  442. }
  443. void UIElement::SetClipChildren(bool enable)
  444. {
  445. clipChildren_ = enable;
  446. }
  447. void UIElement::SetActive(bool enable)
  448. {
  449. active_ = enable;
  450. }
  451. void UIElement::SetFocusMode(FocusMode mode)
  452. {
  453. focusMode_ = mode;
  454. }
  455. void UIElement::SetFocus(bool enable)
  456. {
  457. if (focusMode_ < FM_FOCUSABLE)
  458. enable = false;
  459. if (enable != focus_)
  460. {
  461. focus_ = enable;
  462. if (enable)
  463. OnFocus();
  464. else
  465. OnDefocus();
  466. using namespace Focused;
  467. VariantMap eventData;
  468. eventData[P_ELEMENT] = (void*)this;
  469. SendEvent(focus_ ? E_FOCUSED : E_DEFOCUSED, eventData);
  470. }
  471. }
  472. void UIElement::SetSelected(bool enable)
  473. {
  474. selected_ = enable;
  475. }
  476. void UIElement::SetVisible(bool enable)
  477. {
  478. if (enable != visible_)
  479. {
  480. visible_ = enable;
  481. // Parent's layout may change as a result of visibility change
  482. if (parent_)
  483. parent_->UpdateLayout();
  484. using namespace VisibleChanged;
  485. VariantMap eventData;
  486. eventData[P_ELEMENT] = (void*)this;
  487. eventData[P_VISIBLE] = visible_;
  488. SendEvent(E_VISIBLECHANGED, eventData);
  489. }
  490. }
  491. void UIElement::SetDragDropMode(unsigned mode)
  492. {
  493. dragDropMode_ = mode;
  494. }
  495. void UIElement::SetStyle(XMLFile* file, const String& typeName)
  496. {
  497. if (!file)
  498. return;
  499. XMLElement rootElem = file->GetRootElement();
  500. XMLElement childElem = rootElem.GetChildElement("element");
  501. while (childElem)
  502. {
  503. if (childElem.GetString("type") == typeName)
  504. {
  505. SetStyle(childElem);
  506. return;
  507. }
  508. childElem = childElem.GetNextElement("element");
  509. }
  510. }
  511. void UIElement::SetStyleAuto(XMLFile* file)
  512. {
  513. SetStyle(file, GetTypeName());
  514. }
  515. void UIElement::SetLayout(LayoutMode mode, int spacing, const IntRect& border)
  516. {
  517. layoutMode_ = mode;
  518. layoutSpacing_ = Max(spacing, 0);
  519. layoutBorder_ = IntRect(Max(border.left_, 0), Max(border.top_, 0), Max(border.right_, 0), Max(border.bottom_, 0));
  520. UpdateLayout();
  521. }
  522. void UIElement::SetLayoutMode(LayoutMode mode)
  523. {
  524. layoutMode_ = mode;
  525. UpdateLayout();
  526. }
  527. void UIElement::SetLayoutSpacing(int spacing)
  528. {
  529. layoutSpacing_ = Max(spacing, 0);
  530. UpdateLayout();
  531. }
  532. void UIElement::SetLayoutBorder(const IntRect& border)
  533. {
  534. layoutBorder_ = IntRect(Max(border.left_, 0), Max(border.top_, 0), Max(border.right_, 0), Max(border.bottom_, 0));
  535. UpdateLayout();
  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. PODVector<int> positions;
  544. PODVector<int> sizes;
  545. PODVector<int> minSizes;
  546. PODVector<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(0);
  555. sizes.Push(children_[i]->GetWidth());
  556. minSizes.Push(children_[i]->GetMinWidth());
  557. maxSizes.Push(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(0);
  595. sizes.Push(children_[i]->GetHeight());
  596. minSizes.Push(children_[i]->GetMinHeight());
  597. maxSizes.Push(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. PODVector<UIElement*> topLevelElements = root->GetChildren();
  648. for (PODVector<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(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 (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();
  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. PODVector<UIElement*> UIElement::GetChildren(bool recursive) const
  774. {
  775. if (!recursive)
  776. {
  777. PODVector<UIElement*> ret;
  778. for (Vector<SharedPtr<UIElement> >::ConstIterator i = children_.Begin(); i != children_.End(); ++i)
  779. ret.Push(*i);
  780. return ret;
  781. }
  782. else
  783. {
  784. PODVector<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 (Vector<SharedPtr<UIElement> >::ConstIterator 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 String& name, bool recursive) const
  806. {
  807. for (Vector<SharedPtr<UIElement> >::ConstIterator 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 (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 (Vector<SharedPtr<UIElement> >::ConstIterator 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, PODVector<UIBatch>& batches, PODVector<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 (Vector<SharedPtr<UIElement> >::ConstIterator 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 (Vector<SharedPtr<UIElement> >::ConstIterator i = children_.Begin(); i != children_.End(); ++i)
  937. (*i)->MarkDirty();
  938. }
  939. void UIElement::GetChildrenRecursive(PODVector<UIElement*>& dest) const
  940. {
  941. for (Vector<SharedPtr<UIElement> >::ConstIterator i = children_.Begin(); i != children_.End(); ++i)
  942. {
  943. dest.Push(*i);
  944. (*i)->GetChildrenRecursive(dest);
  945. }
  946. }
  947. int UIElement::CalculateLayoutParentSize(const PODVector<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(PODVector<int>& positions, PODVector<int>& sizes, const PODVector<int>& minSizes,
  962. const PODVector<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. PODVector<unsigned> resizable;
  1002. for (int i = 0; i < numChildren; ++i)
  1003. {
  1004. if ((error < 0) && (sizes[i] > minSizes[i]))
  1005. resizable.Push(i);
  1006. else if ((error > 0) && (sizes[i] < maxSizes[i]))
  1007. resizable.Push(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. }