BsGUIElementBase.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. #include "BsGUIElementBase.h"
  2. #include "BsGUILayout.h"
  3. #include "BsGUILayoutX.h"
  4. #include "BsGUILayoutY.h"
  5. #include "BsGUIPanel.h"
  6. #include "BsGUISpace.h"
  7. #include "BsGUIElement.h"
  8. #include "BsException.h"
  9. #include "BsCGUIWidget.h"
  10. #include "BsGUILayoutUtility.h"
  11. #include "BsProfilerCPU.h"
  12. namespace BansheeEngine
  13. {
  14. GUIElementBase::GUIElementBase()
  15. :mIsDirty(true), mParentElement(nullptr), mIsDisabled(false),
  16. mParentWidget(nullptr), mAnchorParent(nullptr), mUpdateParent(nullptr)
  17. {
  18. }
  19. GUIElementBase::GUIElementBase(const GUIDimensions& dimensions)
  20. :mIsDirty(true), mParentElement(nullptr), mIsDisabled(false),
  21. mParentWidget(nullptr), mDimensions(dimensions),
  22. mAnchorParent(nullptr), mUpdateParent(nullptr)
  23. {
  24. }
  25. GUIElementBase::~GUIElementBase()
  26. {
  27. Vector<GUIElementBase*> childCopy = mChildren;
  28. for (auto& child : childCopy)
  29. {
  30. if (child->_getType() == Type::Element)
  31. {
  32. GUIElement* element = static_cast<GUIElement*>(child);
  33. GUIElement::destroy(element);
  34. }
  35. else if (child->_getType() == Type::Layout || child->_getType() == GUIElementBase::Type::Panel)
  36. {
  37. GUILayout* layout = static_cast<GUILayout*>(child);
  38. GUILayout::destroy(layout);
  39. }
  40. else if (child->_getType() == Type::FixedSpace)
  41. {
  42. GUIFixedSpace* space = static_cast<GUIFixedSpace*>(child);
  43. GUIFixedSpace::destroy(space);
  44. }
  45. else if (child->_getType() == Type::FlexibleSpace)
  46. {
  47. GUIFlexibleSpace* space = static_cast<GUIFlexibleSpace*>(child);
  48. GUIFlexibleSpace::destroy(space);
  49. }
  50. }
  51. }
  52. void GUIElementBase::setPosition(INT32 x, INT32 y)
  53. {
  54. mDimensions.x = x;
  55. mDimensions.y = y;
  56. _markMeshAsDirty();
  57. }
  58. void GUIElementBase::setWidth(UINT32 width)
  59. {
  60. bool isFixedBefore = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
  61. mDimensions.flags |= GUIDF_FixedWidth | GUIDF_OverWidth;
  62. mDimensions.minWidth = mDimensions.maxWidth = width;
  63. bool isFixedAfter = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
  64. if (isFixedBefore != isFixedAfter)
  65. refreshChildUpdateParents();
  66. _markLayoutAsDirty();
  67. }
  68. void GUIElementBase::setFlexibleWidth(UINT32 minWidth, UINT32 maxWidth)
  69. {
  70. if (maxWidth < minWidth)
  71. std::swap(minWidth, maxWidth);
  72. bool isFixedBefore = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
  73. mDimensions.flags |= GUIDF_OverWidth;
  74. mDimensions.flags &= ~GUIDF_FixedWidth;
  75. mDimensions.minWidth = minWidth;
  76. mDimensions.maxWidth = maxWidth;
  77. bool isFixedAfter = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
  78. if (isFixedBefore != isFixedAfter)
  79. refreshChildUpdateParents();
  80. _markLayoutAsDirty();
  81. }
  82. void GUIElementBase::setHeight(UINT32 height)
  83. {
  84. bool isFixedBefore = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
  85. mDimensions.flags |= GUIDF_FixedHeight | GUIDF_OverHeight;
  86. mDimensions.minHeight = mDimensions.maxHeight = height;
  87. bool isFixedAfter = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
  88. if (isFixedBefore != isFixedAfter)
  89. refreshChildUpdateParents();
  90. _markLayoutAsDirty();
  91. }
  92. void GUIElementBase::setFlexibleHeight(UINT32 minHeight, UINT32 maxHeight)
  93. {
  94. if (maxHeight < minHeight)
  95. std::swap(minHeight, maxHeight);
  96. bool isFixedBefore = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
  97. mDimensions.flags |= GUIDF_OverHeight;
  98. mDimensions.flags &= ~GUIDF_FixedHeight;
  99. mDimensions.minHeight = minHeight;
  100. mDimensions.maxHeight = maxHeight;
  101. bool isFixedAfter = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
  102. if (isFixedBefore != isFixedAfter)
  103. refreshChildUpdateParents();
  104. _markLayoutAsDirty();
  105. }
  106. void GUIElementBase::resetDimensions()
  107. {
  108. bool isFixedBefore = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
  109. mDimensions = GUIDimensions::create();
  110. bool isFixedAfter = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
  111. if (isFixedBefore != isFixedAfter)
  112. refreshChildUpdateParents();
  113. _markLayoutAsDirty();
  114. }
  115. Rect2I GUIElementBase::getBounds(GUIPanel* relativeTo)
  116. {
  117. if (relativeTo == nullptr)
  118. relativeTo = mAnchorParent;
  119. Rect2I anchorBounds;
  120. if (relativeTo != nullptr)
  121. anchorBounds = relativeTo->getGlobalBounds();
  122. if (mUpdateParent != nullptr && mUpdateParent->_isDirty() && mParentWidget != nullptr)
  123. mParentWidget->_updateLayout(mUpdateParent);
  124. Rect2I bounds = mLayoutData.area;
  125. bounds.x -= anchorBounds.x;
  126. bounds.y -= anchorBounds.y;
  127. return bounds;
  128. }
  129. void GUIElementBase::setBounds(const Rect2I& bounds)
  130. {
  131. setPosition(bounds.x, bounds.y);
  132. setWidth(bounds.width);
  133. setHeight(bounds.height);
  134. }
  135. Rect2I GUIElementBase::getGlobalBounds()
  136. {
  137. if (mUpdateParent != nullptr && mUpdateParent->_isDirty() && mParentWidget != nullptr)
  138. mParentWidget->_updateLayout(mUpdateParent);
  139. return mLayoutData.area;
  140. }
  141. Rect2I GUIElementBase::getVisibleBounds()
  142. {
  143. return getBounds();
  144. }
  145. void GUIElementBase::_markAsClean()
  146. {
  147. mIsDirty = false;
  148. }
  149. void GUIElementBase::_markLayoutAsDirty()
  150. {
  151. if(_isDisabled())
  152. return;
  153. if (mUpdateParent != nullptr)
  154. mUpdateParent->mIsDirty = true;
  155. else
  156. mIsDirty = true;
  157. }
  158. void GUIElementBase::_markContentAsDirty()
  159. {
  160. if (_isDisabled())
  161. return;
  162. if (mParentWidget != nullptr)
  163. mParentWidget->_markContentDirty(this);
  164. }
  165. void GUIElementBase::_markMeshAsDirty()
  166. {
  167. if(_isDisabled())
  168. return;
  169. if (mParentWidget != nullptr)
  170. mParentWidget->_markMeshDirty(this);
  171. }
  172. void GUIElementBase::enableRecursively()
  173. {
  174. if (mParentElement != nullptr && mParentElement->mIsDisabled)
  175. return; // Cannot enable if parent is disabled
  176. // Make sure to mark everything as dirty, as we didn't track any dirty flags while the element was disabled
  177. mIsDisabled = false;
  178. _markLayoutAsDirty();
  179. for(auto& elem : mChildren)
  180. {
  181. elem->enableRecursively();
  182. }
  183. }
  184. void GUIElementBase::disableRecursively()
  185. {
  186. _markMeshAsDirty(); // Just need to hide the mesh
  187. mIsDisabled = true;
  188. for(auto& elem : mChildren)
  189. {
  190. elem->disableRecursively();
  191. }
  192. }
  193. void GUIElementBase::_updateLayout(const GUILayoutData& data)
  194. {
  195. _updateOptimalLayoutSizes(); // We calculate optimal sizes of all layouts as a pre-processing step, as they are requested often during update
  196. _updateLayoutInternal(data);
  197. }
  198. void GUIElementBase::_updateOptimalLayoutSizes()
  199. {
  200. for(auto& child : mChildren)
  201. {
  202. child->_updateOptimalLayoutSizes();
  203. }
  204. }
  205. void GUIElementBase::_updateLayoutInternal(const GUILayoutData& data)
  206. {
  207. for(auto& child : mChildren)
  208. {
  209. child->_updateLayoutInternal(data);
  210. }
  211. }
  212. LayoutSizeRange GUIElementBase::_calculateLayoutSizeRange() const
  213. {
  214. if (mIsDisabled)
  215. return LayoutSizeRange();
  216. const GUIDimensions& dimensions = _getDimensions();
  217. return dimensions.calculateSizeRange(_getOptimalSize());
  218. }
  219. LayoutSizeRange GUIElementBase::_getLayoutSizeRange() const
  220. {
  221. return _calculateLayoutSizeRange();
  222. }
  223. void GUIElementBase::_getElementAreas(const Rect2I& layoutArea, Rect2I* elementAreas, UINT32 numElements,
  224. const Vector<LayoutSizeRange>& sizeRanges, const LayoutSizeRange& mySizeRange) const
  225. {
  226. assert(mChildren.size() == 0);
  227. }
  228. void GUIElementBase::_setParent(GUIElementBase* parent)
  229. {
  230. if(mParentElement != parent)
  231. {
  232. mParentElement = parent;
  233. _updateAUParents();
  234. if (parent != nullptr)
  235. {
  236. if (_getParentWidget() != parent->_getParentWidget())
  237. _changeParentWidget(parent->_getParentWidget());
  238. }
  239. else
  240. _changeParentWidget(nullptr);
  241. }
  242. }
  243. void GUIElementBase::_registerChildElement(GUIElementBase* element)
  244. {
  245. assert(!element->_isDestroyed());
  246. GUIElementBase* parentElement = element->_getParent();
  247. if(parentElement != nullptr)
  248. {
  249. parentElement->_unregisterChildElement(element);
  250. }
  251. element->_setParent(this);
  252. mChildren.push_back(element);
  253. if (mIsDisabled)
  254. element->disableRecursively();
  255. _markLayoutAsDirty();
  256. }
  257. void GUIElementBase::_unregisterChildElement(GUIElementBase* element)
  258. {
  259. bool foundElem = false;
  260. for(auto iter = mChildren.begin(); iter != mChildren.end(); ++iter)
  261. {
  262. GUIElementBase* child = *iter;
  263. if (child == element)
  264. {
  265. mChildren.erase(iter);
  266. element->_setParent(nullptr);
  267. foundElem = true;
  268. _markLayoutAsDirty();
  269. break;
  270. }
  271. }
  272. if(!foundElem)
  273. BS_EXCEPT(InvalidParametersException, "Provided element is not a part of this element.");
  274. }
  275. void GUIElementBase::_changeParentWidget(CGUIWidget* widget)
  276. {
  277. assert(!_isDestroyed());
  278. if (mParentWidget != widget)
  279. {
  280. if (mParentWidget != nullptr)
  281. mParentWidget->_unregisterElement(this);
  282. if (widget != nullptr)
  283. widget->_registerElement(this);
  284. }
  285. mParentWidget = widget;
  286. for(auto& child : mChildren)
  287. {
  288. child->_changeParentWidget(widget);
  289. }
  290. _markLayoutAsDirty();
  291. }
  292. void GUIElementBase::_updateAUParents()
  293. {
  294. GUIElementBase* updateParent = nullptr;
  295. if (mParentElement != nullptr)
  296. {
  297. updateParent = mParentElement->findUpdateParent();
  298. // If parent is a panel then we can do an optimization and only update
  299. // one child instead of all of them, so change parent to that child.
  300. if (updateParent != nullptr && updateParent->_getType() == GUIElementBase::Type::Panel)
  301. {
  302. GUIElementBase* optimizedUpdateParent = this;
  303. while (optimizedUpdateParent->_getParent() != updateParent)
  304. optimizedUpdateParent = optimizedUpdateParent->_getParent();
  305. updateParent = optimizedUpdateParent;
  306. }
  307. }
  308. GUIPanel* anchorParent = nullptr;
  309. GUIElementBase* currentParent = mParentElement;
  310. while (currentParent != nullptr)
  311. {
  312. if (currentParent->_getType() == Type::Panel)
  313. {
  314. anchorParent = static_cast<GUIPanel*>(currentParent);
  315. break;
  316. }
  317. currentParent = currentParent->mParentElement;
  318. }
  319. setAnchorParent(anchorParent);
  320. setUpdateParent(updateParent);
  321. }
  322. GUIElementBase* GUIElementBase::findUpdateParent()
  323. {
  324. GUIElementBase* currentElement = this;
  325. while (currentElement != nullptr)
  326. {
  327. const GUIDimensions& parentDimensions = currentElement->_getDimensions();
  328. bool boundsDependOnChildren = !parentDimensions.fixedHeight() || !parentDimensions.fixedWidth();
  329. if (!boundsDependOnChildren)
  330. return currentElement;
  331. currentElement = currentElement->mParentElement;
  332. }
  333. return nullptr;
  334. }
  335. void GUIElementBase::refreshChildUpdateParents()
  336. {
  337. GUIElementBase* updateParent = findUpdateParent();
  338. for (auto& child : mChildren)
  339. {
  340. GUIElementBase* childUpdateParent = updateParent;
  341. // If parent is a panel then we can do an optimization and only update
  342. // one child instead of all of them, so change parent to that child.
  343. if (childUpdateParent != nullptr && childUpdateParent->_getType() == GUIElementBase::Type::Panel)
  344. {
  345. GUIElementBase* optimizedUpdateParent = child;
  346. while (optimizedUpdateParent->_getParent() != childUpdateParent)
  347. optimizedUpdateParent = optimizedUpdateParent->_getParent();
  348. childUpdateParent = optimizedUpdateParent;
  349. }
  350. child->setUpdateParent(childUpdateParent);
  351. }
  352. }
  353. void GUIElementBase::setAnchorParent(GUIPanel* anchorParent)
  354. {
  355. mAnchorParent = anchorParent;
  356. if (_getType() == Type::Panel)
  357. return;
  358. for (auto& child : mChildren)
  359. child->setAnchorParent(anchorParent);
  360. }
  361. void GUIElementBase::setUpdateParent(GUIElementBase* updateParent)
  362. {
  363. mUpdateParent = updateParent;
  364. const GUIDimensions& dimensions = _getDimensions();
  365. bool boundsDependOnChildren = !dimensions.fixedHeight() || !dimensions.fixedWidth();
  366. if (!boundsDependOnChildren)
  367. return;
  368. for (auto& child : mChildren)
  369. child->setUpdateParent(updateParent);
  370. }
  371. }