UIElement.cpp 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174
  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 "ResourceCache.h"
  25. #include "UIElement.h"
  26. #include "UIEvents.h"
  27. #include "DebugNew.h"
  28. std::string UIElement::sClipBoard;
  29. UIElement::UIElement(const std::string& name) :
  30. mName(name),
  31. mParent(0),
  32. mOrigin(0),
  33. mClipBorder(IntRect::sZero),
  34. mPriority(0),
  35. mOpacity(1.0f),
  36. mBringToFront(false),
  37. mBringToBack(true),
  38. mClipChildren(false),
  39. mEnabled(false),
  40. mFocusMode(FM_NOTFOCUSABLE),
  41. mFocus(false),
  42. mSelected(false),
  43. mVisible(true),
  44. mHovering(false),
  45. mLayoutMode(LM_FREE),
  46. mLayoutSpacing(0),
  47. mLayoutBorder(IntRect::sZero),
  48. mResizeNestingLevel(0),
  49. mUpdateLayoutNestingLevel(0),
  50. mPosition(IntVector2::sZero),
  51. mSize(IntVector2::sZero),
  52. mMinSize(IntVector2::sZero),
  53. mMaxSize(M_MAX_INT, M_MAX_INT),
  54. mChildOffset(IntVector2::sZero),
  55. mHorizontalAlignment(HA_LEFT),
  56. mVerticalAlignment(VA_TOP),
  57. mScreenPositionDirty(true),
  58. mDerivedOpacityDirty(true),
  59. mHasColorGradient(false)
  60. {
  61. }
  62. UIElement::~UIElement()
  63. {
  64. // If child elements have outside references, detach them
  65. while (mChildren.size())
  66. {
  67. const SharedPtr<UIElement>& element = mChildren.back();
  68. if (element.getRefCount() > 1)
  69. {
  70. element->mParent = 0;
  71. element->markDirty();
  72. }
  73. mChildren.pop_back();
  74. }
  75. }
  76. void UIElement::setStyle(const XMLElement& element, ResourceCache* cache)
  77. {
  78. if (!cache)
  79. return;
  80. if (element.hasAttribute("name"))
  81. mName = element.getString("name");
  82. if (element.hasChildElement("position"))
  83. setPosition(element.getChildElement("position").getIntVector2("value"));
  84. if (element.hasChildElement("size"))
  85. setSize(element.getChildElement("size").getIntVector2("value"));
  86. if (element.hasChildElement("width"))
  87. setWidth(element.getChildElement("width").getInt("value"));
  88. if (element.hasChildElement("height"))
  89. setHeight(element.getChildElement("height").getInt("value"));
  90. if (element.hasChildElement("minsize"))
  91. setMinSize(element.getChildElement("minsize").getIntVector2("value"));
  92. if (element.hasChildElement("minwidth"))
  93. setMinWidth(element.getChildElement("minwidth").getInt("value"));
  94. if (element.hasChildElement("minheight"))
  95. setMinHeight(element.getChildElement("minheight").getInt("value"));
  96. if (element.hasChildElement("maxsize"))
  97. setMaxSize(element.getChildElement("maxsize").getIntVector2("value"));
  98. if (element.hasChildElement("maxwidth"))
  99. setMinWidth(element.getChildElement("maxwidth").getInt("value"));
  100. if (element.hasChildElement("maxheight"))
  101. setMinHeight(element.getChildElement("maxheight").getInt("value"));
  102. if (element.hasChildElement("fixedsize"))
  103. setFixedSize(element.getChildElement("fixedsize").getIntVector2("value"));
  104. if (element.hasChildElement("fixedwidth"))
  105. setFixedWidth(element.getChildElement("fixedwidth").getInt("value"));
  106. if (element.hasChildElement("fixedheight"))
  107. setFixedHeight(element.getChildElement("fixedheight").getInt("value"));
  108. if (element.hasChildElement("alignment"))
  109. {
  110. XMLElement alignElem = element.getChildElement("alignment");
  111. std::string horiz;
  112. std::string vert;
  113. if (alignElem.hasAttribute("h"))
  114. horiz = alignElem.getStringLower("h");
  115. if (alignElem.hasAttribute("v"))
  116. vert = alignElem.getStringLower("v");
  117. if (alignElem.hasAttribute("horizontal"))
  118. horiz = alignElem.getStringLower("horizontal");
  119. if (alignElem.hasAttribute("vertical"))
  120. vert = alignElem.getStringLower("vertical");
  121. if (horiz == "left")
  122. setHorizontalAlignment(HA_LEFT);
  123. if (horiz == "center")
  124. setHorizontalAlignment(HA_CENTER);
  125. if (horiz == "right")
  126. setHorizontalAlignment(HA_RIGHT);
  127. if (vert == "top")
  128. setVerticalAlignment(VA_TOP);
  129. if (vert == "center")
  130. setVerticalAlignment(VA_CENTER);
  131. if (vert == "bottom")
  132. setVerticalAlignment(VA_BOTTOM);
  133. }
  134. if (element.hasChildElement("clipborder"))
  135. setClipBorder(element.getChildElement("clipborder").getIntRect("value"));
  136. if (element.hasChildElement("priority"))
  137. setPriority(element.getChildElement("priority").getInt("value"));
  138. if (element.hasChildElement("opacity"))
  139. setOpacity(element.getChildElement("opacity").getFloat("value"));
  140. if (element.hasChildElement("color"))
  141. {
  142. XMLElement colorElem = element.getChildElement("color");
  143. if (colorElem.hasAttribute("value"))
  144. setColor(colorElem.getColor("value"));
  145. if (colorElem.hasAttribute("topleft"))
  146. setColor(C_TOPLEFT, colorElem.getColor("topleft"));
  147. if (colorElem.hasAttribute("topright"))
  148. setColor(C_TOPRIGHT, colorElem.getColor("topright"));
  149. if (colorElem.hasAttribute("bottomleft"))
  150. setColor(C_BOTTOMLEFT, colorElem.getColor("bottomleft"));
  151. if (colorElem.hasAttribute("bottomright"))
  152. setColor(C_BOTTOMRIGHT, colorElem.getColor("bottomright"));
  153. }
  154. if (element.hasChildElement("bringtofront"))
  155. setBringToFront(element.getChildElement("bringtofront").getBool("enable"));
  156. if (element.hasChildElement("bringtoback"))
  157. setBringToBack(element.getChildElement("bringtoback").getBool("enable"));
  158. if (element.hasChildElement("clipchildren"))
  159. setClipChildren(element.getChildElement("clipchildren").getBool("enable"));
  160. if (element.hasChildElement("enabled"))
  161. setEnabled(element.getChildElement("enabled").getBool("enable"));
  162. if (element.hasChildElement("focusmode"))
  163. {
  164. std::string focusMode = element.getChildElement("focusmode").getStringLower("value");
  165. if (focusMode == "notfocusable")
  166. setFocusMode(FM_NOTFOCUSABLE);
  167. if (focusMode == "resetfocus")
  168. setFocusMode(FM_RESETFOCUS);
  169. if (focusMode == "focusable")
  170. setFocusMode(FM_FOCUSABLE);
  171. if ((focusMode == "focusabledefocusable") || (focusMode == "defocusable"))
  172. setFocusMode(FM_FOCUSABLE_DEFOCUSABLE);
  173. }
  174. if (element.hasChildElement("selected"))
  175. setSelected(element.getChildElement("selected").getBool("enable"));
  176. if (element.hasChildElement("visible"))
  177. setVisible(element.getChildElement("visible").getBool("enable"));
  178. if (element.hasChildElement("layout"))
  179. {
  180. XMLElement layoutElem = element.getChildElement("layout");
  181. std::string mode = layoutElem.getStringLower("mode");
  182. if (mode == "free")
  183. mLayoutMode = LM_FREE;
  184. if ((mode == "horizontal") || (mode == "h"))
  185. mLayoutMode = LM_HORIZONTAL;
  186. if ((mode == "vertical") || (mode == "v"))
  187. mLayoutMode = LM_VERTICAL;
  188. if (layoutElem.hasAttribute("spacing"))
  189. mLayoutSpacing = max(layoutElem.getInt("spacing"), 0);
  190. if (layoutElem.hasAttribute("border"))
  191. setLayoutBorder(layoutElem.getIntRect("border"));
  192. else
  193. updateLayout();
  194. }
  195. }
  196. void UIElement::update(float timeStep)
  197. {
  198. }
  199. void UIElement::getBatches(std::vector<UIBatch>& batches, std::vector<UIQuad>& quads, const IntRect& currentScissor)
  200. {
  201. // Reset hovering for next frame
  202. mHovering = false;
  203. }
  204. IntVector2 UIElement::getScreenPosition()
  205. {
  206. if (mScreenPositionDirty)
  207. {
  208. IntVector2 pos = mPosition;
  209. const UIElement* parent = mParent;
  210. const UIElement* current = this;
  211. while (parent)
  212. {
  213. switch (current->mHorizontalAlignment)
  214. {
  215. case HA_LEFT:
  216. pos.mX += parent->mPosition.mX;
  217. break;
  218. case HA_CENTER:
  219. pos.mX += parent->mPosition.mX + parent->mSize.mX / 2 - current->mSize.mX / 2;
  220. break;
  221. case HA_RIGHT:
  222. pos.mX += parent->mPosition.mX + parent->mSize.mX - current->mSize.mX;
  223. break;
  224. }
  225. switch (current->mVerticalAlignment)
  226. {
  227. case VA_TOP:
  228. pos.mY += parent->mPosition.mY;
  229. break;
  230. case VA_CENTER:
  231. pos.mY += parent->mPosition.mY + parent->mSize.mY / 2 - current->mSize.mY / 2;
  232. break;
  233. case VA_BOTTOM:
  234. pos.mY += parent->mPosition.mY + parent->mSize.mY - current->mSize.mY;
  235. break;
  236. }
  237. pos += parent->mChildOffset;
  238. current = parent;
  239. parent = parent->mParent;
  240. }
  241. mScreenPosition = pos;
  242. mScreenPositionDirty = false;
  243. }
  244. return mScreenPosition;
  245. }
  246. float UIElement::getDerivedOpacity()
  247. {
  248. if (mDerivedOpacityDirty)
  249. {
  250. float opacity = mOpacity;
  251. const UIElement* parent = mParent;
  252. while (parent)
  253. {
  254. opacity *= parent->mOpacity;
  255. parent = parent->mParent;
  256. }
  257. mDerivedOpacity = opacity;
  258. mDerivedOpacityDirty = false;
  259. }
  260. return mDerivedOpacity;
  261. }
  262. void UIElement::onHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers)
  263. {
  264. mHovering = true;
  265. }
  266. void UIElement::onClick(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers)
  267. {
  268. }
  269. void UIElement::onDragStart(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers)
  270. {
  271. }
  272. void UIElement::onDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers)
  273. {
  274. }
  275. void UIElement::onDragEnd(const IntVector2& position, const IntVector2& screenPosition)
  276. {
  277. }
  278. void UIElement::onWheel(int delta, int buttons, int qualifiers)
  279. {
  280. }
  281. void UIElement::onKey(int key, int buttons, int qualifiers)
  282. {
  283. }
  284. void UIElement::onChar(unsigned char c, int buttons, int qualifiers)
  285. {
  286. }
  287. void UIElement::onResize()
  288. {
  289. }
  290. void UIElement::onFocus()
  291. {
  292. }
  293. void UIElement::onDefocus()
  294. {
  295. }
  296. void UIElement::setName(const std::string& name)
  297. {
  298. mName = name;
  299. }
  300. void UIElement::setPosition(const IntVector2& position)
  301. {
  302. if (position != mPosition)
  303. {
  304. mPosition = position;
  305. markDirty();
  306. }
  307. }
  308. void UIElement::setPosition(int x, int y)
  309. {
  310. setPosition(IntVector2(x, y));
  311. }
  312. void UIElement::setSize(const IntVector2& size)
  313. {
  314. ++mResizeNestingLevel;
  315. IntVector2 validatedSize;
  316. validatedSize.mX = clamp(size.mX, mMinSize.mX, mMaxSize.mX);
  317. validatedSize.mY = clamp(size.mY, mMinSize.mY, mMaxSize.mY);
  318. if (validatedSize != mSize)
  319. {
  320. mSize = validatedSize;
  321. if (mResizeNestingLevel == 1)
  322. {
  323. // Check if parent element's layout needs to be updated first
  324. if (mParent)
  325. mParent->updateLayout();
  326. markDirty();
  327. onResize();
  328. updateLayout();
  329. using namespace Resized;
  330. VariantMap eventData;
  331. eventData[P_ELEMENT] = (void*)this;
  332. eventData[P_WIDTH] = mSize.mX;
  333. eventData[P_HEIGHT] = mSize.mY;
  334. sendEvent(EVENT_RESIZED, eventData);
  335. }
  336. }
  337. --mResizeNestingLevel;
  338. }
  339. void UIElement::setSize(int width, int height)
  340. {
  341. setSize(IntVector2(width, height));
  342. }
  343. void UIElement::setWidth(int width)
  344. {
  345. setSize(IntVector2(width, mSize.mY));
  346. }
  347. void UIElement::setHeight(int height)
  348. {
  349. setSize(IntVector2(mSize.mX, height));
  350. }
  351. void UIElement::setMinSize(const IntVector2& minSize)
  352. {
  353. mMinSize.mX = max(minSize.mX, 0);
  354. mMinSize.mY = max(minSize.mY, 0);
  355. mMaxSize.mX = max(minSize.mX, mMaxSize.mX);
  356. mMaxSize.mY = max(minSize.mY, mMaxSize.mY);
  357. setSize(mSize);
  358. }
  359. void UIElement::setMinSize(int width, int height)
  360. {
  361. setMinSize(IntVector2(width, height));
  362. }
  363. void UIElement::setMinWidth(int width)
  364. {
  365. setMinSize(IntVector2(width, mMinSize.mY));
  366. }
  367. void UIElement::setMinHeight(int height)
  368. {
  369. setMinSize(IntVector2(mMinSize.mX, height));
  370. }
  371. void UIElement::setMaxSize(const IntVector2& maxSize)
  372. {
  373. mMaxSize.mX = max(mMinSize.mX, maxSize.mX);
  374. mMaxSize.mY = max(mMinSize.mY, maxSize.mY);
  375. setSize(mSize);
  376. }
  377. void UIElement::setMaxSize(int width, int height)
  378. {
  379. setMaxSize(IntVector2(width, height));
  380. }
  381. void UIElement::setMaxWidth(int width)
  382. {
  383. setMaxSize(IntVector2(width, mMaxSize.mY));
  384. }
  385. void UIElement::setMaxHeight(int height)
  386. {
  387. setMaxSize(IntVector2(mMaxSize.mX, height));
  388. }
  389. void UIElement::setFixedSize(const IntVector2& size)
  390. {
  391. mMinSize = mMaxSize = IntVector2(max(size.mX, 0), max(size.mY, 0));
  392. setSize(size);
  393. }
  394. void UIElement::setFixedSize(int width, int height)
  395. {
  396. setFixedSize(IntVector2(width, height));
  397. }
  398. void UIElement::setFixedWidth(int width)
  399. {
  400. mMinSize.mX = mMaxSize.mX = max(width, 0);
  401. setWidth(width);
  402. }
  403. void UIElement::setFixedHeight(int height)
  404. {
  405. mMinSize.mY = mMaxSize.mY = max(height, 0);
  406. setHeight(height);
  407. }
  408. void UIElement::setAlignment(HorizontalAlignment hAlign, VerticalAlignment vAlign)
  409. {
  410. mHorizontalAlignment = hAlign;
  411. mVerticalAlignment = vAlign;
  412. markDirty();
  413. }
  414. void UIElement::setHorizontalAlignment(HorizontalAlignment align)
  415. {
  416. mHorizontalAlignment = align;
  417. markDirty();
  418. }
  419. void UIElement::setVerticalAlignment(VerticalAlignment align)
  420. {
  421. mVerticalAlignment = align;
  422. markDirty();
  423. }
  424. void UIElement::setClipBorder(const IntRect& rect)
  425. {
  426. mClipBorder.mLeft = max(rect.mLeft, 0);
  427. mClipBorder.mTop = max(rect.mTop, 0);
  428. mClipBorder.mRight = max(rect.mRight, 0);
  429. mClipBorder.mBottom = max(rect.mBottom, 0);
  430. }
  431. void UIElement::setClipBorder(int left, int top, int right, int bottom)
  432. {
  433. setClipBorder(IntRect(left, top, right, bottom));
  434. }
  435. void UIElement::setColor(const Color& color)
  436. {
  437. for (unsigned i = 0; i < MAX_UIELEMENT_CORNERS; ++i)
  438. mColor[i] = color;
  439. mHasColorGradient = false;
  440. }
  441. void UIElement::setColor(Corner corner, const Color& color)
  442. {
  443. mColor[corner] = color;
  444. mHasColorGradient = false;
  445. for (unsigned i = 0; i < MAX_UIELEMENT_CORNERS; ++i)
  446. {
  447. if ((i != corner) && (mColor[i] != mColor[corner]))
  448. mHasColorGradient = true;
  449. }
  450. }
  451. void UIElement::setPriority(int priority)
  452. {
  453. mPriority = priority;
  454. }
  455. void UIElement::setOpacity(float opacity)
  456. {
  457. mOpacity = clamp(opacity, 0.0f, 1.0f);
  458. markDirty();
  459. }
  460. void UIElement::setBringToFront(bool enable)
  461. {
  462. mBringToFront = enable;
  463. }
  464. void UIElement::setBringToBack(bool enable)
  465. {
  466. mBringToBack = enable;
  467. }
  468. void UIElement::setClipChildren(bool enable)
  469. {
  470. mClipChildren = enable;
  471. }
  472. void UIElement::setEnabled(bool enable)
  473. {
  474. mEnabled = enable;
  475. }
  476. void UIElement::setFocusMode(FocusMode mode)
  477. {
  478. mFocusMode = mode;
  479. }
  480. void UIElement::setFocus(bool enable)
  481. {
  482. if (mFocusMode < FM_FOCUSABLE)
  483. enable = false;
  484. if (enable != mFocus)
  485. {
  486. mFocus = enable;
  487. if (enable)
  488. onFocus();
  489. else
  490. onDefocus();
  491. using namespace Focused;
  492. VariantMap eventData;
  493. eventData[P_ELEMENT] = (void*)this;
  494. sendEvent(mFocus ? EVENT_FOCUSED : EVENT_DEFOCUSED, eventData);
  495. }
  496. }
  497. void UIElement::setSelected(bool enable)
  498. {
  499. mSelected = enable;
  500. }
  501. void UIElement::setVisible(bool enable)
  502. {
  503. if (enable != mVisible)
  504. {
  505. mVisible = enable;
  506. // Parent's layout may change as a result of visibility change
  507. if (mParent)
  508. mParent->updateLayout();
  509. using namespace VisibleChanged;
  510. VariantMap eventData;
  511. eventData[P_ELEMENT] = (void*)this;
  512. eventData[P_VISIBLE] = mVisible;
  513. sendEvent(EVENT_VISIBLECHANGED, eventData);
  514. }
  515. }
  516. void UIElement::setUserData(const Variant& userData)
  517. {
  518. mUserData = userData;
  519. }
  520. void UIElement::setStyleAuto(XMLFile* file, ResourceCache* cache)
  521. {
  522. XMLElement element = getStyleElement(file);
  523. setStyle(element, cache);
  524. }
  525. void UIElement::setLayout(LayoutMode mode, int spacing, const IntRect& border)
  526. {
  527. mLayoutMode = mode;
  528. mLayoutSpacing = max(spacing, 0);
  529. mLayoutBorder = IntRect(max(border.mLeft, 0), max(border.mTop, 0), max(border.mRight, 0), max(border.mBottom, 0));
  530. updateLayout();
  531. }
  532. void UIElement::setLayoutSpacing(int spacing)
  533. {
  534. mLayoutSpacing = max(spacing, 0);
  535. updateLayout();
  536. }
  537. void UIElement::setLayoutBorder(const IntRect& border)
  538. {
  539. mLayoutBorder = IntRect(max(border.mLeft, 0), max(border.mTop, 0), max(border.mRight, 0), max(border.mBottom, 0));
  540. updateLayout();
  541. }
  542. void UIElement::updateLayout()
  543. {
  544. if ((mLayoutMode == LM_FREE) || (mUpdateLayoutNestingLevel))
  545. return;
  546. // Prevent further updates while this update happens
  547. disableLayoutUpdate();
  548. std::vector<int> positions;
  549. std::vector<int> sizes;
  550. std::vector<int> minSizes;
  551. std::vector<int> maxSizes;
  552. if (mLayoutMode == LM_HORIZONTAL)
  553. {
  554. int minChildHeight = 0;
  555. for (unsigned i = 0; i < mChildren.size(); ++i)
  556. {
  557. if (!mChildren[i]->isVisible())
  558. continue;
  559. positions.push_back(0);
  560. sizes.push_back(mChildren[i]->getWidth());
  561. minSizes.push_back(mChildren[i]->getMinWidth());
  562. maxSizes.push_back(mChildren[i]->getMaxWidth());
  563. minChildHeight = max(minChildHeight, mChildren[i]->getMinHeight());
  564. }
  565. calculateLayout(positions, sizes, minSizes, maxSizes, getWidth(), mLayoutBorder.mLeft, mLayoutBorder.mRight,
  566. mLayoutSpacing);
  567. int width = calculateLayoutParentSize(sizes, mLayoutBorder.mLeft, mLayoutBorder.mRight, mLayoutSpacing);
  568. int height = max(getHeight(), minChildHeight + mLayoutBorder.mTop + mLayoutBorder.mBottom);
  569. int minWidth = calculateLayoutParentSize(minSizes, mLayoutBorder.mLeft, mLayoutBorder.mRight, mLayoutSpacing);
  570. int minHeight = minChildHeight + mLayoutBorder.mTop + mLayoutBorder.mBottom;
  571. setMinSize(minWidth, minHeight);
  572. setSize(width, height);
  573. unsigned j = 0;
  574. for (unsigned i = 0; i < mChildren.size(); ++i)
  575. {
  576. if (!mChildren[i]->isVisible())
  577. continue;
  578. mChildren[i]->setHorizontalAlignment(HA_LEFT);
  579. mChildren[i]->setPosition(positions[j], getLayoutChildPosition(mChildren[i]).mY);
  580. mChildren[i]->setSize(sizes[j], height - mLayoutBorder.mTop - mLayoutBorder.mBottom);
  581. ++j;
  582. }
  583. }
  584. if (mLayoutMode == LM_VERTICAL)
  585. {
  586. int minChildWidth = 0;
  587. int maxChildWidth = M_MAX_INT;
  588. for (unsigned i = 0; i < mChildren.size(); ++i)
  589. {
  590. if (!mChildren[i]->isVisible())
  591. continue;
  592. positions.push_back(0);
  593. sizes.push_back(mChildren[i]->getHeight());
  594. minSizes.push_back(mChildren[i]->getMinHeight());
  595. maxSizes.push_back(mChildren[i]->getMaxHeight());
  596. minChildWidth = max(minChildWidth, mChildren[i]->getMinWidth());
  597. }
  598. calculateLayout(positions, sizes, minSizes, maxSizes, getHeight(), mLayoutBorder.mTop, mLayoutBorder.mBottom,
  599. mLayoutSpacing);
  600. int height = calculateLayoutParentSize(sizes, mLayoutBorder.mTop, mLayoutBorder.mBottom, mLayoutSpacing);
  601. int width = max(getWidth(), minChildWidth + mLayoutBorder.mLeft + mLayoutBorder.mRight);
  602. int minHeight = calculateLayoutParentSize(minSizes, mLayoutBorder.mTop, mLayoutBorder.mBottom, mLayoutSpacing);
  603. int minWidth = minChildWidth + mLayoutBorder.mLeft + mLayoutBorder.mRight;
  604. setMinSize(minWidth, minHeight);
  605. setSize(width, height);
  606. unsigned j = 0;
  607. for (unsigned i = 0; i < mChildren.size(); ++i)
  608. {
  609. if (!mChildren[i]->isVisible())
  610. continue;
  611. mChildren[i]->setVerticalAlignment(VA_TOP);
  612. mChildren[i]->setPosition(getLayoutChildPosition(mChildren[i]).mX, positions[j]);
  613. mChildren[i]->setSize(width - mLayoutBorder.mLeft - mLayoutBorder.mRight, sizes[j]);
  614. ++j;
  615. }
  616. }
  617. enableLayoutUpdate();
  618. }
  619. void UIElement::disableLayoutUpdate()
  620. {
  621. ++mUpdateLayoutNestingLevel;
  622. }
  623. void UIElement::enableLayoutUpdate()
  624. {
  625. --mUpdateLayoutNestingLevel;
  626. }
  627. void UIElement::bringToFront()
  628. {
  629. // Follow the parent chain to the top level window. If it has BringToFront mode, bring it to front now
  630. UIElement* root = getRootElement();
  631. UIElement* ptr = this;
  632. while ((ptr) && (ptr->getParent() != root))
  633. ptr = ptr->getParent();
  634. if ((!ptr) || (!ptr->getBringToFront()))
  635. return;
  636. // Get the highest priority used by all other top level elements, decrease their priority by one,
  637. // and assign that to new front element. However, take into account only active (enabled) elements
  638. // and those which have the BringToBack flag set
  639. int maxPriority = M_MIN_INT;
  640. std::vector<UIElement*> topLevelElements = root->getChildren();
  641. for (std::vector<UIElement*>::iterator i = topLevelElements.begin(); i != topLevelElements.end(); ++i)
  642. {
  643. UIElement* other = *i;
  644. if ((other->isEnabled()) && (other->getBringToBack()) && (other != ptr))
  645. {
  646. int priority = other->getPriority();
  647. maxPriority = max(priority, maxPriority);
  648. other->setPriority(priority - 1);
  649. }
  650. }
  651. ptr->setPriority(maxPriority);
  652. }
  653. void UIElement::addChild(UIElement* element)
  654. {
  655. if ((!element) || (element->mParent == this) || (mParent == element))
  656. return;
  657. // Add first, then remove from old parent, to ensure the elemen does not get deleted
  658. mChildren.push_back(SharedPtr<UIElement>(element));
  659. if (element->mParent)
  660. element->mParent->removeChild(element);
  661. element->mParent = this;
  662. element->markDirty();
  663. updateLayout();
  664. }
  665. void UIElement::removeChild(UIElement* element)
  666. {
  667. for (std::vector<SharedPtr<UIElement> >::iterator i = mChildren.begin(); i != mChildren.end(); ++i)
  668. {
  669. if ((*i) == element)
  670. {
  671. element->mParent = 0;
  672. element->markDirty();
  673. mChildren.erase(i);
  674. updateLayout();
  675. return;
  676. }
  677. }
  678. }
  679. void UIElement::removeAllChildren()
  680. {
  681. while (mChildren.size())
  682. {
  683. const SharedPtr<UIElement>& element = mChildren.back();
  684. element->mParent = 0;
  685. element->markDirty();
  686. mChildren.pop_back();
  687. }
  688. }
  689. std::vector<UIElement*> UIElement::getChildren(bool recursive) const
  690. {
  691. if (!recursive)
  692. {
  693. std::vector<UIElement*> ret;
  694. for (std::vector<SharedPtr<UIElement> >::const_iterator i = mChildren.begin(); i != mChildren.end(); ++i)
  695. ret.push_back(*i);
  696. return ret;
  697. }
  698. else
  699. {
  700. std::vector<UIElement*> allChildren;
  701. getChildrenRecursive(allChildren);
  702. return allChildren;
  703. }
  704. }
  705. unsigned UIElement::getNumChildren(bool recursive) const
  706. {
  707. if (!recursive)
  708. return mChildren.size();
  709. else
  710. {
  711. unsigned allChildren = mChildren.size();
  712. for (std::vector<SharedPtr<UIElement> >::const_iterator i = mChildren.begin(); i != mChildren.end(); ++i)
  713. allChildren += (*i)->getNumChildren(true);
  714. return allChildren;
  715. }
  716. }
  717. UIElement* UIElement::getChild(unsigned index) const
  718. {
  719. if (index >= mChildren.size())
  720. return 0;
  721. return mChildren[index];
  722. }
  723. UIElement* UIElement::getChild(const std::string& name, bool recursive) const
  724. {
  725. for (std::vector<SharedPtr<UIElement> >::const_iterator i = mChildren.begin(); i != mChildren.end(); ++i)
  726. {
  727. if ((*i)->mName == name)
  728. return *i;
  729. if (recursive)
  730. {
  731. UIElement* element = (*i)->getChild(name, true);
  732. if (element)
  733. return element;
  734. }
  735. }
  736. return 0;
  737. }
  738. UIElement* UIElement::getRootElement() const
  739. {
  740. UIElement* root = mParent;
  741. if (!root)
  742. return 0;
  743. while (root->getParent())
  744. root = root->getParent();
  745. return root;
  746. }
  747. XMLElement UIElement::getStyleElement(XMLFile* file) const
  748. {
  749. if (file)
  750. {
  751. XMLElement rootElem = file->getRootElement();
  752. XMLElement childElem = rootElem.getChildElement("element");
  753. while (childElem)
  754. {
  755. if (childElem.getString("type") == getTypeName())
  756. return childElem;
  757. childElem = childElem.getNextElement("element");
  758. }
  759. }
  760. return XMLElement();
  761. }
  762. IntVector2 UIElement::screenToElement(const IntVector2& screenPosition)
  763. {
  764. return screenPosition - getScreenPosition();
  765. }
  766. IntVector2 UIElement::elementToScreen(const IntVector2& position)
  767. {
  768. return position + getScreenPosition();
  769. }
  770. bool UIElement::isInside(IntVector2 position, bool isScreen)
  771. {
  772. if (isScreen)
  773. position = screenToElement(position);
  774. return (position.mX >= 0) && (position.mY >= 0) && (position.mX < mSize.mX) && (position.mY < mSize.mY);
  775. }
  776. bool UIElement::isInsideCombined(IntVector2 position, bool isScreen)
  777. {
  778. // If child elements are clipped, no need to expand the rect
  779. if (mClipChildren)
  780. return isInside(position, isScreen);
  781. if (!isScreen)
  782. position = elementToScreen(position);
  783. IntRect combined = getCombinedScreenRect();
  784. return (position.mX >= combined.mLeft) && (position.mY >= combined.mTop) && (position.mX < combined.mRight) &&
  785. (position.mY < combined.mBottom);
  786. }
  787. IntRect UIElement::getCombinedScreenRect()
  788. {
  789. IntVector2 screenPosition(getScreenPosition());
  790. IntRect combined(screenPosition.mX, screenPosition.mY, screenPosition.mX + mSize.mX, screenPosition.mY + mSize.mY);
  791. for (std::vector<SharedPtr<UIElement> >::iterator i = mChildren.begin(); i != mChildren.end(); ++i)
  792. {
  793. IntVector2 childPos = (*i)->getScreenPosition();
  794. const IntVector2& childSize = (*i)->getSize();
  795. if (childPos.mX < combined.mLeft)
  796. combined.mLeft = childPos.mX;
  797. if (childPos.mY < combined.mTop)
  798. combined.mTop = childPos.mY;
  799. if (childPos.mX + childSize.mX > combined.mRight)
  800. combined.mRight = childPos.mX + childSize.mX;
  801. if (childPos.mY + childSize.mY > combined.mBottom)
  802. combined.mBottom = childPos.mY + childSize.mY;
  803. }
  804. return combined;
  805. }
  806. void UIElement::setChildOffset(const IntVector2& offset)
  807. {
  808. if (offset != mChildOffset)
  809. {
  810. mChildOffset = offset;
  811. for (std::vector<SharedPtr<UIElement> >::const_iterator i = mChildren.begin(); i != mChildren.end(); ++i)
  812. (*i)->markDirty();
  813. }
  814. }
  815. void UIElement::setHovering(bool enable)
  816. {
  817. mHovering = enable;
  818. }
  819. void UIElement::setOrigin(UIElement* origin)
  820. {
  821. mOrigin = origin;
  822. }
  823. void UIElement::adjustScissor(IntRect& currentScissor)
  824. {
  825. if (mClipChildren)
  826. {
  827. IntVector2 screenPos = getScreenPosition();
  828. currentScissor.mLeft = max(currentScissor.mLeft, screenPos.mX + mClipBorder.mLeft);
  829. currentScissor.mTop = max(currentScissor.mTop, screenPos.mY + mClipBorder.mTop);
  830. currentScissor.mRight = min(currentScissor.mRight, screenPos.mX + mSize.mX - mClipBorder.mRight);
  831. currentScissor.mBottom = min(currentScissor.mBottom, screenPos.mY + mSize.mY - mClipBorder.mBottom);
  832. if (currentScissor.mRight < currentScissor.mLeft)
  833. currentScissor.mRight = currentScissor.mLeft;
  834. if (currentScissor.mBottom < currentScissor.mTop)
  835. currentScissor.mBottom = currentScissor.mTop;
  836. }
  837. }
  838. void UIElement::getBatchesWithOffset(IntVector2& offset, std::vector<UIBatch>& batches, std::vector<UIQuad>& quads, IntRect
  839. currentScissor)
  840. {
  841. unsigned initialSize = quads.size();
  842. getBatches(batches, quads, currentScissor);
  843. for (unsigned i = initialSize; i < quads.size(); ++i)
  844. {
  845. quads[i].mLeft += offset.mX;
  846. quads[i].mTop += offset.mY;
  847. quads[i].mRight += offset.mX;
  848. quads[i].mBottom += offset.mY;
  849. }
  850. adjustScissor(currentScissor);
  851. for (std::vector<SharedPtr<UIElement> >::const_iterator i = mChildren.begin(); i != mChildren.end(); ++i)
  852. {
  853. if ((*i)->isVisible())
  854. (*i)->getBatchesWithOffset(offset, batches, quads, currentScissor);
  855. }
  856. }
  857. XMLElement UIElement::getStyleElement(XMLFile* file, const std::string& typeName)
  858. {
  859. if (file)
  860. {
  861. XMLElement rootElem = file->getRootElement();
  862. XMLElement childElem = rootElem.getChildElement("element");
  863. while (childElem)
  864. {
  865. if (childElem.getString("type") == typeName)
  866. return childElem;
  867. childElem = childElem.getNextElement("element");
  868. }
  869. }
  870. return XMLElement();
  871. }
  872. void UIElement::markDirty()
  873. {
  874. mScreenPositionDirty = true;
  875. mDerivedOpacityDirty = true;
  876. for (std::vector<SharedPtr<UIElement> >::const_iterator i = mChildren.begin(); i != mChildren.end(); ++i)
  877. (*i)->markDirty();
  878. }
  879. void UIElement::getChildrenRecursive(std::vector<UIElement*>& dest) const
  880. {
  881. for (std::vector<SharedPtr<UIElement> >::const_iterator i = mChildren.begin(); i != mChildren.end(); ++i)
  882. {
  883. dest.push_back(*i);
  884. (*i)->getChildrenRecursive(dest);
  885. }
  886. }
  887. int UIElement::calculateLayoutParentSize(const std::vector<int>& sizes, int begin, int end, int spacing)
  888. {
  889. int width = begin + end;
  890. for (unsigned i = 0; i < sizes.size(); ++i)
  891. {
  892. // If we are calculating maximum size, and the default is specified, do not overflow it
  893. if (sizes[i] == M_MAX_INT)
  894. return M_MAX_INT;
  895. width += sizes[i];
  896. if (i < sizes.size() - 1)
  897. width += spacing;
  898. }
  899. return width;
  900. }
  901. void UIElement::calculateLayout(std::vector<int>& positions, std::vector<int>& sizes, const std::vector<int>& minSizes,
  902. const std::vector<int>& maxSizes, int targetSize, int begin, int end, int spacing)
  903. {
  904. int numChildren = sizes.size();
  905. if (!numChildren)
  906. return;
  907. int targetTotalSize = targetSize - begin - end - (numChildren - 1) * spacing;
  908. if (targetTotalSize < 0)
  909. targetTotalSize = 0;
  910. int targetChildSize = targetTotalSize / numChildren;
  911. int remainder = targetTotalSize % numChildren;
  912. float add = (float)remainder / numChildren;
  913. float acc = 0.0f;
  914. // Initial pass
  915. for (int i = 0; i < numChildren; ++i)
  916. {
  917. int targetSize = targetChildSize;
  918. if (remainder)
  919. {
  920. acc += add;
  921. if (acc >= 0.5f)
  922. {
  923. acc -= 1.0f;
  924. ++targetSize;
  925. --remainder;
  926. }
  927. }
  928. sizes[i] = clamp(targetSize, minSizes[i], maxSizes[i]);
  929. }
  930. // Error correction passes
  931. for (;;)
  932. {
  933. int actualTotalSize = 0;
  934. for (int i = 0; i < numChildren; ++i)
  935. actualTotalSize += sizes[i];
  936. int error = targetTotalSize - actualTotalSize;
  937. // Break if no error
  938. if (!error)
  939. break;
  940. // Check which of the children can be resized to correct the error. If none, must break
  941. static std::vector<unsigned> resizable;
  942. resizable.clear();
  943. for (int i = 0; i < numChildren; ++i)
  944. {
  945. if ((error < 0) && (sizes[i] > minSizes[i]))
  946. resizable.push_back(i);
  947. else if ((error > 0) && (sizes[i] < maxSizes[i]))
  948. resizable.push_back(i);
  949. }
  950. if (resizable.empty())
  951. break;
  952. int numResizable = resizable.size();
  953. int errorPerChild = error / numResizable;
  954. remainder = (abs(error)) % numResizable;
  955. add = (float)remainder / numResizable;
  956. acc = 0.0f;
  957. for (int i = 0; i < numResizable; ++i)
  958. {
  959. unsigned idx = resizable[i];
  960. int targetSize = sizes[idx] + errorPerChild;
  961. if (remainder)
  962. {
  963. acc += add;
  964. if (acc >= 0.5f)
  965. {
  966. acc -= 1.0f;
  967. targetSize = error < 0 ? targetSize - 1 : targetSize + 1;
  968. --remainder;
  969. }
  970. }
  971. sizes[idx] = clamp(targetSize, minSizes[idx], maxSizes[idx]);
  972. }
  973. }
  974. // Calculate final positions
  975. int position = begin;
  976. for (int i = 0; i < numChildren; ++i)
  977. {
  978. positions[i] = position;
  979. position += sizes[i];
  980. position += spacing;
  981. }
  982. }
  983. IntVector2 UIElement::getLayoutChildPosition(UIElement* child)
  984. {
  985. IntVector2 ret(IntVector2::sZero);
  986. HorizontalAlignment ha = child->getHorizontalAlignment();
  987. switch (ha)
  988. {
  989. case HA_LEFT:
  990. ret.mX = mLayoutBorder.mLeft;
  991. break;
  992. case HA_RIGHT:
  993. ret.mX = -mLayoutBorder.mRight;
  994. break;
  995. }
  996. VerticalAlignment va = child->getVerticalAlignment();
  997. switch (va)
  998. {
  999. case VA_TOP:
  1000. ret.mY = mLayoutBorder.mTop;
  1001. break;
  1002. case VA_BOTTOM:
  1003. ret.mY = -mLayoutBorder.mBottom;
  1004. break;
  1005. }
  1006. return ret;
  1007. }