| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472 |
- #include "BsGUIElementBase.h"
- #include "BsGUILayout.h"
- #include "BsGUILayoutX.h"
- #include "BsGUILayoutY.h"
- #include "BsGUIPanel.h"
- #include "BsGUISpace.h"
- #include "BsGUIElement.h"
- #include "BsException.h"
- #include "BsCGUIWidget.h"
- #include "BsGUILayoutUtility.h"
- #include "BsProfilerCPU.h"
- namespace BansheeEngine
- {
- GUIElementBase::GUIElementBase()
- :mIsDirty(true), mParentElement(nullptr), mIsDisabled(false),
- mParentWidget(nullptr), mAnchorParent(nullptr), mUpdateParent(nullptr)
- {
- }
- GUIElementBase::GUIElementBase(const GUIDimensions& dimensions)
- :mIsDirty(true), mParentElement(nullptr), mIsDisabled(false),
- mParentWidget(nullptr), mDimensions(dimensions),
- mAnchorParent(nullptr), mUpdateParent(nullptr)
- {
- }
- GUIElementBase::~GUIElementBase()
- {
- Vector<GUIElementBase*> childCopy = mChildren;
- for (auto& child : childCopy)
- {
- if (child->_getType() == Type::Element)
- {
- GUIElement* element = static_cast<GUIElement*>(child);
- GUIElement::destroy(element);
- }
- else if (child->_getType() == Type::Layout || child->_getType() == GUIElementBase::Type::Panel)
- {
- GUILayout* layout = static_cast<GUILayout*>(child);
- GUILayout::destroy(layout);
- }
- else if (child->_getType() == Type::FixedSpace)
- {
- GUIFixedSpace* space = static_cast<GUIFixedSpace*>(child);
- GUIFixedSpace::destroy(space);
- }
- else if (child->_getType() == Type::FlexibleSpace)
- {
- GUIFlexibleSpace* space = static_cast<GUIFlexibleSpace*>(child);
- GUIFlexibleSpace::destroy(space);
- }
- }
- }
- void GUIElementBase::setPosition(INT32 x, INT32 y)
- {
- mDimensions.x = x;
- mDimensions.y = y;
- _markMeshAsDirty();
- }
- void GUIElementBase::setWidth(UINT32 width)
- {
- bool isFixedBefore = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
- mDimensions.flags |= GUIDF_FixedWidth | GUIDF_OverWidth;
- mDimensions.minWidth = mDimensions.maxWidth = width;
- bool isFixedAfter = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
- if (isFixedBefore != isFixedAfter)
- refreshChildUpdateParents();
-
- _markLayoutAsDirty();
- }
- void GUIElementBase::setFlexibleWidth(UINT32 minWidth, UINT32 maxWidth)
- {
- if (maxWidth < minWidth)
- std::swap(minWidth, maxWidth);
- bool isFixedBefore = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
- mDimensions.flags |= GUIDF_OverWidth;
- mDimensions.flags &= ~GUIDF_FixedWidth;
- mDimensions.minWidth = minWidth;
- mDimensions.maxWidth = maxWidth;
- bool isFixedAfter = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
- if (isFixedBefore != isFixedAfter)
- refreshChildUpdateParents();
- _markLayoutAsDirty();
- }
- void GUIElementBase::setHeight(UINT32 height)
- {
- bool isFixedBefore = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
- mDimensions.flags |= GUIDF_FixedHeight | GUIDF_OverHeight;
- mDimensions.minHeight = mDimensions.maxHeight = height;
- bool isFixedAfter = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
- if (isFixedBefore != isFixedAfter)
- refreshChildUpdateParents();
- _markLayoutAsDirty();
- }
- void GUIElementBase::setFlexibleHeight(UINT32 minHeight, UINT32 maxHeight)
- {
- if (maxHeight < minHeight)
- std::swap(minHeight, maxHeight);
- bool isFixedBefore = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
- mDimensions.flags |= GUIDF_OverHeight;
- mDimensions.flags &= ~GUIDF_FixedHeight;
- mDimensions.minHeight = minHeight;
- mDimensions.maxHeight = maxHeight;
- bool isFixedAfter = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
- if (isFixedBefore != isFixedAfter)
- refreshChildUpdateParents();
- _markLayoutAsDirty();
- }
- void GUIElementBase::resetDimensions()
- {
- bool isFixedBefore = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
- mDimensions = GUIDimensions::create();
- bool isFixedAfter = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
- if (isFixedBefore != isFixedAfter)
- refreshChildUpdateParents();
- _markLayoutAsDirty();
- }
- Rect2I GUIElementBase::getBounds(GUIPanel* relativeTo)
- {
- if (relativeTo == nullptr)
- relativeTo = mAnchorParent;
- Rect2I anchorBounds;
- if (relativeTo != nullptr)
- anchorBounds = relativeTo->getGlobalBounds();
- if (mUpdateParent != nullptr && mUpdateParent->_isDirty() && mParentWidget != nullptr)
- mParentWidget->_updateLayout(mUpdateParent);
- Rect2I bounds = mLayoutData.area;
- bounds.x -= anchorBounds.x;
- bounds.y -= anchorBounds.y;
-
- return bounds;
- }
- void GUIElementBase::setBounds(const Rect2I& bounds)
- {
- setPosition(bounds.x, bounds.y);
- setWidth(bounds.width);
- setHeight(bounds.height);
- }
- Rect2I GUIElementBase::getGlobalBounds()
- {
- if (mUpdateParent != nullptr && mUpdateParent->_isDirty() && mParentWidget != nullptr)
- mParentWidget->_updateLayout(mUpdateParent);
- return mLayoutData.area;
- }
- Rect2I GUIElementBase::getVisibleBounds()
- {
- return getBounds();
- }
-
- void GUIElementBase::_markAsClean()
- {
- mIsDirty = false;
- }
- void GUIElementBase::_markLayoutAsDirty()
- {
- if(_isDisabled())
- return;
- if (mUpdateParent != nullptr)
- mUpdateParent->mIsDirty = true;
- else
- mIsDirty = true;
- }
- void GUIElementBase::_markContentAsDirty()
- {
- if (_isDisabled())
- return;
- if (mParentWidget != nullptr)
- mParentWidget->_markContentDirty(this);
- }
- void GUIElementBase::_markMeshAsDirty()
- {
- if(_isDisabled())
- return;
- if (mParentWidget != nullptr)
- mParentWidget->_markMeshDirty(this);
- }
- void GUIElementBase::enableRecursively()
- {
- if (mParentElement != nullptr && mParentElement->mIsDisabled)
- return; // Cannot enable if parent is disabled
- // Make sure to mark everything as dirty, as we didn't track any dirty flags while the element was disabled
- mIsDisabled = false;
- _markLayoutAsDirty();
- for(auto& elem : mChildren)
- {
- elem->enableRecursively();
- }
- }
- void GUIElementBase::disableRecursively()
- {
- _markMeshAsDirty(); // Just need to hide the mesh
- mIsDisabled = true;
- for(auto& elem : mChildren)
- {
- elem->disableRecursively();
- }
- }
- void GUIElementBase::_updateLayout(const GUILayoutData& data)
- {
- _updateOptimalLayoutSizes(); // We calculate optimal sizes of all layouts as a pre-processing step, as they are requested often during update
- _updateLayoutInternal(data);
- }
- void GUIElementBase::_updateOptimalLayoutSizes()
- {
- for(auto& child : mChildren)
- {
- child->_updateOptimalLayoutSizes();
- }
- }
- void GUIElementBase::_updateLayoutInternal(const GUILayoutData& data)
- {
- for(auto& child : mChildren)
- {
- child->_updateLayoutInternal(data);
- }
- }
- LayoutSizeRange GUIElementBase::_calculateLayoutSizeRange() const
- {
- if (mIsDisabled)
- return LayoutSizeRange();
- const GUIDimensions& dimensions = _getDimensions();
- return dimensions.calculateSizeRange(_getOptimalSize());
- }
- LayoutSizeRange GUIElementBase::_getLayoutSizeRange() const
- {
- return _calculateLayoutSizeRange();
- }
- void GUIElementBase::_getElementAreas(const Rect2I& layoutArea, Rect2I* elementAreas, UINT32 numElements,
- const Vector<LayoutSizeRange>& sizeRanges, const LayoutSizeRange& mySizeRange) const
- {
- assert(mChildren.size() == 0);
- }
- void GUIElementBase::_setParent(GUIElementBase* parent)
- {
- if(mParentElement != parent)
- {
- mParentElement = parent;
- _updateAUParents();
- if (parent != nullptr)
- {
- if (_getParentWidget() != parent->_getParentWidget())
- _changeParentWidget(parent->_getParentWidget());
- }
- else
- _changeParentWidget(nullptr);
- }
- }
- void GUIElementBase::_registerChildElement(GUIElementBase* element)
- {
- assert(!element->_isDestroyed());
- GUIElementBase* parentElement = element->_getParent();
- if(parentElement != nullptr)
- {
- parentElement->_unregisterChildElement(element);
- }
- element->_setParent(this);
- mChildren.push_back(element);
- if (mIsDisabled)
- element->disableRecursively();
- _markLayoutAsDirty();
- }
- void GUIElementBase::_unregisterChildElement(GUIElementBase* element)
- {
- bool foundElem = false;
- for(auto iter = mChildren.begin(); iter != mChildren.end(); ++iter)
- {
- GUIElementBase* child = *iter;
- if (child == element)
- {
- mChildren.erase(iter);
- element->_setParent(nullptr);
- foundElem = true;
- _markLayoutAsDirty();
- break;
- }
- }
- if(!foundElem)
- BS_EXCEPT(InvalidParametersException, "Provided element is not a part of this element.");
- }
- void GUIElementBase::_changeParentWidget(CGUIWidget* widget)
- {
- assert(!_isDestroyed());
- if (mParentWidget != widget)
- {
- if (mParentWidget != nullptr)
- mParentWidget->_unregisterElement(this);
- if (widget != nullptr)
- widget->_registerElement(this);
- }
- mParentWidget = widget;
- for(auto& child : mChildren)
- {
- child->_changeParentWidget(widget);
- }
- _markLayoutAsDirty();
- }
- void GUIElementBase::_updateAUParents()
- {
- GUIElementBase* updateParent = nullptr;
- if (mParentElement != nullptr)
- {
- updateParent = mParentElement->findUpdateParent();
- // If parent is a panel then we can do an optimization and only update
- // one child instead of all of them, so change parent to that child.
- if (updateParent != nullptr && updateParent->_getType() == GUIElementBase::Type::Panel)
- {
- GUIElementBase* optimizedUpdateParent = this;
- while (optimizedUpdateParent->_getParent() != updateParent)
- optimizedUpdateParent = optimizedUpdateParent->_getParent();
- updateParent = optimizedUpdateParent;
- }
- }
- GUIPanel* anchorParent = nullptr;
- GUIElementBase* currentParent = mParentElement;
- while (currentParent != nullptr)
- {
- if (currentParent->_getType() == Type::Panel)
- {
- anchorParent = static_cast<GUIPanel*>(currentParent);
- break;
- }
- currentParent = currentParent->mParentElement;
- }
- setAnchorParent(anchorParent);
- setUpdateParent(updateParent);
- }
- GUIElementBase* GUIElementBase::findUpdateParent()
- {
- GUIElementBase* currentElement = this;
- while (currentElement != nullptr)
- {
- const GUIDimensions& parentDimensions = currentElement->_getDimensions();
- bool boundsDependOnChildren = !parentDimensions.fixedHeight() || !parentDimensions.fixedWidth();
- if (!boundsDependOnChildren)
- return currentElement;
- currentElement = currentElement->mParentElement;
- }
- return nullptr;
- }
- void GUIElementBase::refreshChildUpdateParents()
- {
- GUIElementBase* updateParent = findUpdateParent();
- for (auto& child : mChildren)
- {
- GUIElementBase* childUpdateParent = updateParent;
- // If parent is a panel then we can do an optimization and only update
- // one child instead of all of them, so change parent to that child.
- if (childUpdateParent != nullptr && childUpdateParent->_getType() == GUIElementBase::Type::Panel)
- {
- GUIElementBase* optimizedUpdateParent = child;
- while (optimizedUpdateParent->_getParent() != childUpdateParent)
- optimizedUpdateParent = optimizedUpdateParent->_getParent();
- childUpdateParent = optimizedUpdateParent;
- }
- child->setUpdateParent(childUpdateParent);
- }
- }
- void GUIElementBase::setAnchorParent(GUIPanel* anchorParent)
- {
- mAnchorParent = anchorParent;
- if (_getType() == Type::Panel)
- return;
- for (auto& child : mChildren)
- child->setAnchorParent(anchorParent);
- }
- void GUIElementBase::setUpdateParent(GUIElementBase* updateParent)
- {
- mUpdateParent = updateParent;
- const GUIDimensions& dimensions = _getDimensions();
- bool boundsDependOnChildren = !dimensions.fixedHeight() || !dimensions.fixedWidth();
- if (!boundsDependOnChildren)
- return;
- for (auto& child : mChildren)
- child->setUpdateParent(updateParent);
- }
- }
|