UIElement.cpp 36 KB

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