UIElement.cpp 36 KB

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