Browse Source

Closing an editor window no longer causes an exception in GUIManager
Fixed an issue where scene manager wasn't being notified when a component was being removed due to its parent SceneObject destruction

Marko Pintera 12 years ago
parent
commit
a3d90405e7

+ 15 - 1
BansheeEngine/Include/BsGUIManager.h

@@ -29,6 +29,17 @@ namespace BansheeEngine
 			bool isDirty;
 		};
 
+		struct WidgetInfo
+		{
+			WidgetInfo(GUIWidget* _widget, const boost::signals::connection& _onAddedConn, const boost::signals::connection& _onRemovedConn)
+				:widget(_widget), onAddedConn(_onAddedConn), onRemovedConn(_onRemovedConn)
+			{ }
+
+			GUIWidget* widget;
+			boost::signals::connection onAddedConn;
+			boost::signals::connection onRemovedConn;
+		};
+
 	public:
 		GUIManager();
 		~GUIManager();
@@ -49,7 +60,7 @@ namespace BansheeEngine
 		GUIInputSelection* getInputSelectionTool() const { return mInputSelection; }
 
 	private:
-		CM::Vector<GUIWidget*>::type mWidgets;
+		CM::Vector<WidgetInfo>::type mWidgets;
 		CM::UnorderedMap<const CM::Viewport*, GUIRenderData>::type mCachedGUIData;
 
 		// Element and widget mouse is currently over
@@ -109,6 +120,9 @@ namespace BansheeEngine
 		void onWindowFocusLost(CM::RenderWindow& win);
 		void onWindowMovedOrResized(CM::RenderWindow& win);
 
+		void onGUIElementAddedToWidget(GUIWidget* widget, GUIElement* element);
+		void onGUIElementRemovedFromWidget(GUIWidget* widget, GUIElement* element);
+
 		GUIMouseButton buttonToMouseButton(CM::ButtonCode code) const;
 		CM::Int2 getWidgetRelativePos(const GUIWidget& widget, const CM::Int2& screenPos) const;
 	};

+ 10 - 0
BansheeEngine/Include/BsGUIWidget.h

@@ -5,6 +5,7 @@
 #include "CmRect.h"
 #include "CmVector3.h"
 #include "CmQuaternion.h"
+#include <boost/signal.hpp>
 
 namespace BansheeEngine
 {
@@ -56,6 +57,13 @@ namespace BansheeEngine
 		 * 			must be a child of this widget.
 		 */
 		virtual bool _keyEvent(GUIElement* element, const GUIKeyEvent& ev);
+
+		// Note: These are shared_ptrs because of boosts non_copyable limitation on signals
+		// (which triggers a compile error although the copy constructor is private and I don't make any copies.
+		// Presumably containers like vector or map trigger it). So instead of keeping signals by value
+		// I use a shared_ptr.
+		std::shared_ptr<boost::signal<void(GUIElement*)>> onElementAdded;
+		std::shared_ptr<boost::signal<void(GUIElement*)>> onElementRemoved;
 	protected:
 		friend class CM::SceneObject;
 		friend class GUIElement;
@@ -70,6 +78,8 @@ namespace BansheeEngine
 		void registerArea(GUIArea* area);
 		void unregisterArea(GUIArea* area);
 	private:
+		GUIWidget(const GUIWidget& other) { }
+
 		void updateBounds() const;
 
 		virtual void ownerWindowResized();

+ 67 - 18
BansheeEngine/Source/BsGUIManager.cpp

@@ -72,9 +72,9 @@ namespace BansheeEngine
 	{
 		// Make a copy of widgets, since destroying them will remove them from mWidgets and
 		// we can't iterate over an array thats getting modified
-		Vector<GUIWidget*>::type widgetCopy = mWidgets;
+		Vector<WidgetInfo>::type widgetCopy = mWidgets;
 		for(auto& widget : widgetCopy)
-			widget->destroy();
+			widget.widget->destroy();
 
 		mOnButtonDownConn.disconnect();
 		mOnButtonUpConn.disconnect();
@@ -91,7 +91,10 @@ namespace BansheeEngine
 
 	void GUIManager::registerWidget(GUIWidget* widget)
 	{
-		mWidgets.push_back(widget);
+		boost::signals::connection onElementAddedConn = widget->onElementAdded->connect(boost::bind(&GUIManager::onGUIElementAddedToWidget, this, widget, _1));
+		boost::signals::connection onElementRemovedConn = widget->onElementRemoved->connect(boost::bind(&GUIManager::onGUIElementRemovedFromWidget, this, widget, _1));
+
+		mWidgets.push_back(WidgetInfo(widget, onElementAddedConn, onElementRemovedConn));
 
 		const Viewport* renderTarget = widget->getTarget();
 
@@ -107,10 +110,14 @@ namespace BansheeEngine
 
 	void GUIManager::unregisterWidget(GUIWidget* widget)
 	{
-		auto findIter = std::find(begin(mWidgets), end(mWidgets), widget);
+		auto findIter = std::find_if(begin(mWidgets), end(mWidgets), [=] (const WidgetInfo& x) { return x.widget == widget; } );
 
 		if(findIter != end(mWidgets))
+		{
+			findIter->onAddedConn.disconnect();
+			findIter->onRemovedConn.disconnect();
 			mWidgets.erase(findIter);
+		}
 
 		if(mMouseOverWidget == widget)
 		{
@@ -118,12 +125,24 @@ namespace BansheeEngine
 			mMouseOverElement = nullptr;
 		}
 
+		if(mKeyboardFocusWidget == widget)
+		{
+			mKeyboardFocusWidget = nullptr;
+			mKeyboardFocusElement = nullptr;
+		}
+
+		if(mActiveWidget == widget)
+		{
+			mActiveWidget = nullptr;
+			mActiveElement = nullptr;
+		}
+
 		const Viewport* renderTarget = widget->getTarget();
 		GUIRenderData& renderData = mCachedGUIData[renderTarget];
 
-		findIter = std::find(begin(renderData.widgets), end(renderData.widgets), widget);
-		if(findIter != end(renderData.widgets))
-			renderData.widgets.erase(findIter);
+		auto findIter2 = std::find(begin(renderData.widgets), end(renderData.widgets), widget);
+		if(findIter2 != end(renderData.widgets))
+			renderData.widgets.erase(findIter2);
 
 		if(renderData.widgets.size() == 0)
 			mCachedGUIData.erase(renderTarget);
@@ -134,9 +153,9 @@ namespace BansheeEngine
 	void GUIManager::update()
 	{
 		// Update layouts
-		for(auto& widget : mWidgets)
+		for(auto& widgetInfo : mWidgets)
 		{
-			widget->_updateLayout();
+			widgetInfo.widget->_updateLayout();
 		}
 
 		// Blink caret
@@ -640,9 +659,9 @@ namespace BansheeEngine
 #if CM_DEBUG_MODE
 		// Checks if all referenced windows actually exist
 		Vector<RenderWindow*>::type activeWindows = RenderWindowManager::instance().getRenderWindows();
-		for(auto& widget : mWidgets)
+		for(auto& widgetInfo : mWidgets)
 		{
-			auto iterFind = std::find(begin(activeWindows), end(activeWindows), widget->getOwnerWindow());
+			auto iterFind = std::find(begin(activeWindows), end(activeWindows), widgetInfo.widget->getOwnerWindow());
 
 			if(iterFind == activeWindows.end())
 			{
@@ -669,13 +688,13 @@ namespace BansheeEngine
 		Int2 screenPos;
 		Int2 localPos;
 
-		for(auto& widget : mWidgets)
+		for(auto& widgetInfo : mWidgets)
 		{
-			const RenderWindow* window = widget->getOwnerWindow();
+			const RenderWindow* window = widgetInfo.widget->getOwnerWindow();
 
 			if(window->hasFocus())
 			{
-				widgetInFocus = widget;
+				widgetInFocus = widgetInfo.widget;
 				break;
 			}
 		}
@@ -688,8 +707,9 @@ namespace BansheeEngine
 			Vector4 vecScreenPos((float)screenPos.x, (float)screenPos.y, 0.0f, 1.0f);
 
 			UINT32 topMostDepth = std::numeric_limits<UINT32>::max();
-			for(auto& widget : mWidgets)
+			for(auto& widgetInfo : mWidgets)
 			{
+				GUIWidget* widget = widgetInfo.widget;
 				if(widget->getOwnerWindow() == window && widget->inBounds(screenPos))
 				{
 					const Matrix4& worldTfrm = widget->SO()->getWorldTfrm();
@@ -810,8 +830,9 @@ namespace BansheeEngine
 
 	void GUIManager::onWindowFocusGained(RenderWindow& win)
 	{
-		for(auto& widget : mWidgets)
+		for(auto& widgetInfo : mWidgets)
 		{
+			GUIWidget* widget = widgetInfo.widget;
 			if(widget->getOwnerWindow() == &win)
 				widget->ownerWindowFocusChanged();
 		}
@@ -819,8 +840,9 @@ namespace BansheeEngine
 
 	void GUIManager::onWindowFocusLost(RenderWindow& win)
 	{
-		for(auto& widget : mWidgets)
+		for(auto& widgetInfo : mWidgets)
 		{
+			GUIWidget* widget = widgetInfo.widget;
 			if(widget->getOwnerWindow() == &win)
 				widget->ownerWindowFocusChanged();
 		}
@@ -828,13 +850,40 @@ namespace BansheeEngine
 
 	void GUIManager::onWindowMovedOrResized(RenderWindow& win)
 	{
-		for(auto& widget : mWidgets)
+		for(auto& widgetInfo : mWidgets)
 		{
+			GUIWidget* widget = widgetInfo.widget;
 			if(widget->getOwnerWindow() == &win)
 				widget->ownerWindowResized();
 		}
 	}
 
+	void GUIManager::onGUIElementRemovedFromWidget(GUIWidget* widget, GUIElement* element)
+	{
+		if(mMouseOverElement == element)
+		{
+			mMouseOverElement = nullptr;
+			mMouseOverWidget = nullptr;
+		}
+
+		if(mActiveElement == element)
+		{
+			mActiveElement = nullptr;
+			mActiveWidget = nullptr;
+		}
+
+		if(mKeyboardFocusElement == element)
+		{
+			mKeyboardFocusElement = nullptr;
+			mKeyboardFocusWidget = nullptr;
+		}
+	}
+
+	void GUIManager::onGUIElementAddedToWidget(GUIWidget* widget, GUIElement* element)
+	{
+
+	}
+
 	GUIMouseButton GUIManager::buttonToMouseButton(ButtonCode code) const
 	{
 		if(code == BC_MOUSE_LEFT)

+ 3 - 0
BansheeEngine/Source/BsGUIToggleGroup.cpp

@@ -30,6 +30,9 @@ namespace BansheeEngine
 
 	void GUIToggleGroup::remove(GUIToggle* toggle)
 	{
+		auto sharedPtr = mThis.lock(); // Make sure we keep a reference because calling _setToggleGroup(nullptr) 
+		                               // may otherwise clear the last reference and cause us to destruct
+
 		auto iterFind = std::find(begin(mButtons), end(mButtons), toggle);
 		if(iterFind == end(mButtons))
 			return;

+ 9 - 0
BansheeEngine/Source/BsGUIWidget.cpp

@@ -28,6 +28,9 @@ namespace BansheeEngine
 		mLastFramePosition = SO()->getWorldPosition();
 		mLastFrameRotation = SO()->getWorldRotation();
 		mLastFrameScale = SO()->getWorldScale();
+
+		onElementAdded = std::shared_ptr<boost::signal<void(GUIElement*)>>(cm_new<boost::signal<void(GUIElement*)>>());
+		onElementRemoved = std::shared_ptr<boost::signal<void(GUIElement*)>>(cm_new<boost::signal<void(GUIElement*)>>());
 	}
 
 	GUIWidget::~GUIWidget()
@@ -179,6 +182,9 @@ namespace BansheeEngine
 		mElements.push_back(elem);
 
 		mWidgetIsDirty = true;
+
+		if(!onElementAdded->empty())
+			(*onElementAdded)(elem);
 	}
 
 	void GUIWidget::unregisterElement(GUIElement* elem)
@@ -192,6 +198,9 @@ namespace BansheeEngine
 
 		mElements.erase(iterFind);
 		mWidgetIsDirty = true;
+
+		if(!onElementRemoved->empty())
+			(*onElementRemoved)(elem);
 	}
 
 	void GUIWidget::registerArea(GUIArea* area)

+ 2 - 0
CamelotCore/Include/CmComponent.h

@@ -37,6 +37,8 @@ namespace CamelotFramework
 		virtual ~Component();
 
 		HSceneObject mParent;
+	private:
+		Component(const Component& other) { }
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/

+ 1 - 0
CamelotCore/Source/CmSceneObject.cpp

@@ -65,6 +65,7 @@ namespace CamelotFramework
 
 		for(auto iter = mComponents.begin(); iter != mComponents.end(); ++iter)
 		{
+			gSceneManager().notifyComponentRemoved((*iter));
 			(*iter).destroy();
 		}
 

+ 0 - 1
EditorWindowDock.txt

@@ -57,7 +57,6 @@ Implementation plan:
  - Continue with DockManager (flesh it out later)
 
  GUIManager holds a ptr to GUIElements, and when elements are destroyed, GUIManager holds an invalid ptr
-Tabs need toggle groups
 Hook up DbgEditorWidget1::close()
  - In EditorWidget instead of having a onClose callback, instead keep EditorWidgetContainer parent and then notify it before it is closed. And if the widget is closed by parent container, 
    then the container can remove parent and then close it so it doesn't receive the callback.