| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346 |
- #include "BsGUIWidget.h"
- #include "BsGUIManager.h"
- #include "BsGUISkin.h"
- #include "BsGUILabel.h"
- #include "BsGUIMouseEvent.h"
- #include "BsGUIArea.h"
- #include "CmApplication.h"
- #include "CmCoreThreadAccessor.h"
- #include "CmMaterial.h"
- #include "CmPass.h"
- #include "CmMesh.h"
- #include "CmInt2.h"
- #include "BsOverlayManager.h"
- #include "BsCamera.h"
- #include "CmViewport.h"
- #include "CmSceneObject.h"
- #include "CmRenderWindow.h"
- using namespace CamelotFramework;
- namespace BansheeEngine
- {
- GUISkin GUIWidget::DefaultSkin;
- GUIWidget::GUIWidget(const HSceneObject& parent, CM::Viewport* target, CM::RenderWindow* ownerWindow)
- :Component(parent), mSkin(nullptr), mOwnerWindow(nullptr), mWidgetIsDirty(false), mTarget(nullptr), mDepth(0)
- {
- mLastFramePosition = SO()->getWorldPosition();
- mLastFrameRotation = SO()->getWorldRotation();
- mLastFrameScale = SO()->getWorldScale();
- assert(target != nullptr);
- assert(ownerWindow != nullptr);
- if(mOwnerWindow != nullptr)
- CM_EXCEPT(InvalidStateException, "Widget has already been initialized.");
- mTarget = target;
- mOwnerWindow = ownerWindow;
- mOwnerTargetResizedConn = mTarget->onResized.connect(boost::bind(&GUIWidget::ownerTargetResized, this));
- GUIManager::instance().registerWidget(this);
- }
- GUIWidget::~GUIWidget()
- {
- if(mTarget != nullptr)
- {
- GUIManager::instance().unregisterWidget(this);
- mOwnerTargetResizedConn.disconnect();
- }
- // Iterate over all elements in this way because each
- // GUIElement::destroy call internally unregisters the element
- // from the widget, and modifies the mElements array
- while(mElements.size() > 0)
- {
- GUIElement::destroy(mElements[0]);
- }
- for(auto& area : mAreas)
- {
- GUIArea::destroyInternal(area);
- }
- mElements.clear();
- }
- void GUIWidget::update()
- {
- // If the widgets parent scene object moved, we need to mark it as dirty
- // as the GUIManager batching relies on object positions, so it needs to be updated.
- const float diffEpsilon = 0.0001f;
- Vector3 position = SO()->getWorldPosition();
- Quaternion rotation = SO()->getWorldRotation();
- Vector3 scale = SO()->getWorldScale();
- if(!mWidgetIsDirty)
- {
- Vector3 posDiff = mLastFramePosition - position;
- if(Math::Abs(posDiff.x) > diffEpsilon || Math::Abs(posDiff.y) > diffEpsilon || Math::Abs(posDiff.z) > diffEpsilon)
- {
- mWidgetIsDirty = true;
- }
- else
- {
- Quaternion rotDiff = mLastFrameRotation - rotation;
- if(Math::Abs(rotDiff.x) > diffEpsilon || Math::Abs(rotDiff.y) > diffEpsilon ||
- Math::Abs(rotDiff.z) > diffEpsilon || Math::Abs(rotDiff.w) > diffEpsilon)
- {
- mWidgetIsDirty = true;
- }
- else
- {
- Vector3 scaleDiff = mLastFrameScale - scale;
- if(Math::Abs(scaleDiff.x) > diffEpsilon || Math::Abs(scaleDiff.y) > diffEpsilon || Math::Abs(scaleDiff.z) > diffEpsilon)
- {
- mWidgetIsDirty = true;
- }
- }
- }
- }
- mLastFramePosition = position;
- mLastFrameRotation = rotation;
- mLastFrameScale = scale;
- }
- void GUIWidget::_updateLayout()
- {
- for(auto& area : mAreas)
- {
- area->_update();
- }
- }
- bool GUIWidget::_mouseEvent(GUIElement* element, const GUIMouseEvent& ev)
- {
- // If an element has any parents we send the events to all parents first and only then to the children unless
- // the parents process them
- Stack<GUIElement*>::type todo;
- GUIElementBase* curElement = element;
- do
- {
- if(curElement->_getType() == GUIElementBase::Type::Element)
- todo.push(static_cast<GUIElement*>(curElement));
- curElement = curElement->_getParent();
- } while(curElement != nullptr);
-
- while(true)
- {
- GUIElement* elem = todo.top();
- todo.pop();
- if(elem->mouseEvent(ev))
- return true;
- if(todo.size() == 0)
- return false;
- }
- return false;
- }
- bool GUIWidget::_textInputEvent(GUIElement* element, const GUITextInputEvent& ev)
- {
- // If an element has any parents we send the events to all parents first and only then to the children unless
- // the parents process them
- Stack<GUIElement*>::type todo;
- GUIElementBase* curElement = element;
- do
- {
- if(curElement->_getType() == GUIElementBase::Type::Element)
- todo.push(static_cast<GUIElement*>(curElement));
- curElement = curElement->_getParent();
- } while(curElement != nullptr);
- while(true)
- {
- GUIElement* elem = todo.top();
- todo.pop();
- if(elem->textInputEvent(ev))
- return true;
- if(todo.size() == 0)
- return false;
- }
- return false;
- }
- bool GUIWidget::_commandEvent(GUIElement* element, const GUICommandEvent& ev)
- {
- // If an element has any parents we send the events to all parents first and only then to the children unless
- // the parents process them
- Stack<GUIElement*>::type todo;
- GUIElementBase* curElement = element;
- do
- {
- if(curElement->_getType() == GUIElementBase::Type::Element)
- todo.push(static_cast<GUIElement*>(curElement));
- curElement = curElement->_getParent();
- } while(curElement != nullptr);
- while(true)
- {
- GUIElement* elem = todo.top();
- todo.pop();
- if(elem->commandEvent(ev))
- return true;
- if(todo.size() == 0)
- return false;
- }
- return false;
- }
- void GUIWidget::registerElement(GUIElement* elem)
- {
- assert(elem != nullptr);
- mElements.push_back(elem);
- mWidgetIsDirty = true;
- }
- void GUIWidget::unregisterElement(GUIElement* elem)
- {
- assert(elem != nullptr);
- auto iterFind = std::find(begin(mElements), end(mElements), elem);
- if(iterFind == mElements.end())
- CM_EXCEPT(InvalidParametersException, "Cannot unregister an element that is not registered on this widget.");
- mElements.erase(iterFind);
- mWidgetIsDirty = true;
- }
- void GUIWidget::registerArea(GUIArea* area)
- {
- assert(area != nullptr);
- mAreas.push_back(area);
- mWidgetIsDirty = true;
- }
- void GUIWidget::unregisterArea(GUIArea* area)
- {
- assert(area != nullptr);
- auto iterFind = std::find(begin(mAreas), end(mAreas), area);
- if(iterFind == mAreas.end())
- CM_EXCEPT(InvalidParametersException, "Cannot unregister an area that is not registered on this widget.");
- mAreas.erase(iterFind);
- mWidgetIsDirty = true;
- }
- void GUIWidget::setSkin(const GUISkin& skin)
- {
- mSkin = &skin;
- }
- const GUISkin& GUIWidget::getSkin() const
- {
- if(mSkin != nullptr)
- return *mSkin;
- else
- return DefaultSkin;
- }
- bool GUIWidget::isDirty(bool cleanIfDirty)
- {
- if(cleanIfDirty)
- {
- bool dirty = mWidgetIsDirty;
- mWidgetIsDirty = false;
- for(auto& elem : mElements)
- {
- if(elem->_isContentDirty())
- {
- dirty = true;
- elem->updateRenderElements();
- }
- if(elem->_isMeshDirty())
- {
- dirty = true;
- elem->_markAsClean();
- }
- }
- if(dirty)
- updateBounds();
- return dirty;
- }
- else
- {
- if(mWidgetIsDirty)
- return true;
- for(auto& elem : mElements)
- {
- if(elem->_isContentDirty() || elem->_isMeshDirty())
- {
- return true;
- }
- }
- return false;
- }
- }
- bool GUIWidget::inBounds(const Int2& position) const
- {
- // Technically GUI widget bounds can be larger than the viewport, so make sure we clip to viewport first
- if(!getTarget()->getArea().contains(position))
- return false;
- const Matrix4& worldTfrm = SO()->getWorldTfrm();
- Vector3 vecPos((float)position.x, (float)position.y, 0.0f);
- vecPos = worldTfrm.inverse() * vecPos;
- Int2 localPos(Math::RoundToInt(vecPos.x), Math::RoundToInt(vecPos.y));
- return mBounds.contains(localPos);
- }
- void GUIWidget::updateBounds() const
- {
- if(mElements.size() > 0)
- mBounds = mElements[0]->_getClippedBounds();
- for(auto& elem : mElements)
- {
- Rect elemBounds = elem->_getClippedBounds();
- mBounds.encapsulate(elemBounds);
- }
- }
- void GUIWidget::ownerTargetResized()
- {
- for(auto& area : mAreas)
- {
- area->updateSizeBasedOnParent(getTarget()->getWidth(), getTarget()->getHeight());
- }
- }
- void GUIWidget::ownerWindowFocusChanged()
- {
- }
- }
|