UIElement.cpp 36 KB

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