Browse Source

GUI manager mouse events implemented (not tested)

Marko Pintera 12 years ago
parent
commit
637530c1f1

+ 7 - 0
BansheeEngine/Include/BsGUIManager.h

@@ -2,6 +2,7 @@
 
 
 #include "BsPrerequisites.h"
 #include "BsPrerequisites.h"
 #include "CmModule.h"
 #include "CmModule.h"
+#include "CmInputHandler.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
@@ -20,5 +21,11 @@ namespace BansheeEngine
 		void update();
 		void update();
 	private:
 	private:
 		std::vector<GUIWidget*> mWidgets;
 		std::vector<GUIWidget*> mWidgets;
+
+		GUIWidget* mMouseOverWidget;
+		GUIElement* mMouseOverElement;
+
+		bool mLastFrameButtonState[CM::MB_Count];
+		CM::Int2 mLastCursorPos;
 	};
 	};
 }
 }

+ 16 - 2
BansheeEngine/Include/BsGUIMouseEvent.h

@@ -1,19 +1,33 @@
 #pragma once
 #pragma once
 
 
 #include "BsPrerequisites.h"
 #include "BsPrerequisites.h"
+#include "CmInputHandler.h"
 #include "CmInt2.h"
 #include "CmInt2.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
+	enum class GUIMouseEventType
+	{
+		MouseOver,
+		MouseOut,
+		MouseDown,
+		MouseUp,
+		MouseMove
+	};
+
 	class BS_EXPORT GUIMouseEvent
 	class BS_EXPORT GUIMouseEvent
 	{
 	{
 	public:
 	public:
-		GUIMouseEvent(const CM::Int2& position)
-			:mPosition(position)
+		GUIMouseEvent(GUIMouseEventType type, const CM::Int2& position, CM::MouseButton button = CM::MB_Left)
+			:mType(type), mPosition(position), mButton(button)
 		{ }
 		{ }
 
 
 		const CM::Int2& getPosition() const { return mPosition; }
 		const CM::Int2& getPosition() const { return mPosition; }
+		GUIMouseEventType getType() const { return mType; }
+		CM::MouseButton getButton() const { return mButton; }
 	private:
 	private:
 		CM::Int2 mPosition;
 		CM::Int2 mPosition;
+		GUIMouseEventType mType;
+		CM::MouseButton mButton;
 	};
 	};
 }
 }

+ 1 - 1
BansheeEngine/Include/BsGUIWidget.h

@@ -26,11 +26,11 @@ namespace BansheeEngine
 		const GUISkin* getGUISkin() const;
 		const GUISkin* getGUISkin() const;
 
 
 		bool inBounds(const CM::Int2& position) const;
 		bool inBounds(const CM::Int2& position) const;
-		bool mouseEvent(const GUIMouseEvent& ev);
 
 
 		virtual void render(CM::RenderContext& renderContext) const;
 		virtual void render(CM::RenderContext& renderContext) const;
 
 
 		const CM::RenderWindow* getOwnerWindow() const { return mOwnerWindow; }
 		const CM::RenderWindow* getOwnerWindow() const { return mOwnerWindow; }
+		const std::vector<GUIElement*>& getElements() const { return mElements; }
 	protected:
 	protected:
 		friend class CM::SceneObject;
 		friend class CM::SceneObject;
 		friend class GUIElement;
 		friend class GUIElement;

+ 104 - 7
BansheeEngine/Source/BsGUIManager.cpp

@@ -2,6 +2,7 @@
 #include "BsGUIWidget.h"
 #include "BsGUIWidget.h"
 #include "BsGUIElement.h"
 #include "BsGUIElement.h"
 #include "BsGUIMouseEvent.h"
 #include "BsGUIMouseEvent.h"
+#include "CmSceneObject.h"
 #include "CmMaterial.h"
 #include "CmMaterial.h"
 #include "CmMeshData.h"
 #include "CmMeshData.h"
 #include "CmMesh.h"
 #include "CmMesh.h"
@@ -9,14 +10,17 @@
 #include "CmRenderWindowManager.h"
 #include "CmRenderWindowManager.h"
 #include "CmCursor.h"
 #include "CmCursor.h"
 #include "CmException.h"
 #include "CmException.h"
+#include "CmInput.h"
 
 
 using namespace CamelotFramework;
 using namespace CamelotFramework;
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
 	GUIManager::GUIManager()
 	GUIManager::GUIManager()
+		:mMouseOverElement(nullptr), mMouseOverWidget(nullptr)
 	{
 	{
-
+		for(int i = 0; i < MB_Count; i++)
+			mLastFrameButtonState[i] = false;
 	}
 	}
 
 
 	GUIManager::~GUIManager()
 	GUIManager::~GUIManager()
@@ -35,6 +39,12 @@ namespace BansheeEngine
 
 
 		if(findIter != end(mWidgets))
 		if(findIter != end(mWidgets))
 			mWidgets.erase(findIter);
 			mWidgets.erase(findIter);
+
+		if(mMouseOverWidget == widget)
+		{
+			mMouseOverWidget = nullptr;
+			mMouseOverElement = nullptr;
+		}
 	}
 	}
 
 
 	void GUIManager::update()
 	void GUIManager::update()
@@ -53,23 +63,110 @@ namespace BansheeEngine
 			}
 			}
 		}
 		}
 #endif
 #endif
+		GUIWidget* widgetInFocus = nullptr;
+		GUIElement* topMostElement = nullptr;
+		UINT32 topMostDepth = std::numeric_limits<UINT32>::max();
 
 
 		for(auto& widget : mWidgets)
 		for(auto& widget : mWidgets)
 		{
 		{
 			const RenderWindow* window = widget->getOwnerWindow();
 			const RenderWindow* window = widget->getOwnerWindow();
 
 
-			if(!window->hasFocus())
-				continue;
+			if(window->hasFocus())
+			{
+				widgetInFocus = widget;
+				break;
+			}
+		}
+
+		Int2 screenPos;
+		if(widgetInFocus != nullptr)
+		{
+			const RenderWindow* window = widgetInFocus->getOwnerWindow();
 
 
-			Int2 screenPos = Cursor::getWindowPosition(*window);
-			GUIMouseEvent mouseEvent(screenPos);
+			screenPos = Cursor::getWindowPosition(*window);
+			Vector3 vecScreenPos((float)screenPos.x, (float)screenPos.y, 0.0f);
 
 
+			GUIElement* topMostElement = nullptr;
+			INT32 topMostDepth = std::numeric_limits<INT32>::max();
 			for(auto& widget : mWidgets)
 			for(auto& widget : mWidgets)
 			{
 			{
 				if(widget->inBounds(screenPos))
 				if(widget->inBounds(screenPos))
 				{
 				{
-					if(widget->mouseEvent(mouseEvent))
-						break;
+					const Matrix4& worldTfrm = widget->SO()->getWorldTfrm();
+
+					Vector3 vecLocalPos = worldTfrm.inverse() * vecScreenPos;
+					Int2 localPos(Math::RoundToInt(vecLocalPos.x), Math::RoundToInt(vecLocalPos.y));
+
+					const std::vector<GUIElement*>& elements = widget->getElements();
+
+					// Elements with lowest depth (most to the front) get handled first
+					for(auto iter = elements.rbegin(); iter != elements.rend(); ++iter)
+					{
+						GUIElement* element = *iter;
+						const Rect& bounds = element->getBounds();
+
+						if(bounds.contains(localPos) && element->getDepth() < topMostDepth)
+						{
+							topMostElement = element;
+							topMostDepth = element->getDepth();
+
+							break;
+						}
+					}
+				}
+			}
+		}
+
+		if(topMostElement != mMouseOverElement)
+		{
+			if(mMouseOverElement != nullptr)
+			{
+				// Send MouseOut event
+				const RenderWindow* window = mMouseOverWidget->getOwnerWindow();
+				Int2 oldScreenPos = Cursor::getWindowPosition(*window);
+
+				GUIMouseEvent event(GUIMouseEventType::MouseOut, oldScreenPos); // TODO - This pos will be wrong as it might be related to completely other window
+				mMouseOverElement->mouseEvent(event);
+			}
+
+			if(topMostElement != nullptr)
+			{
+				// Send MouseOver event
+				GUIMouseEvent event(GUIMouseEventType::MouseOver, screenPos);
+				topMostElement->mouseEvent(event);
+			}
+
+			mMouseOverElement = topMostElement;
+			mMouseOverWidget = widgetInFocus;
+		}
+
+		if(mMouseOverElement != nullptr)
+		{
+			// Send MouseMove event
+			if(mLastCursorPos != screenPos)
+			{
+				GUIMouseEvent event(GUIMouseEventType::MouseMove, screenPos);
+				mMouseOverElement->mouseEvent(event);
+			}
+
+			// Send MouseDown and MouseUp events
+			for(int i = 0; i < MB_Count; i++)
+			{
+				bool buttonDown = gInput().isButtonDown((MouseButton)i);
+				if(mLastFrameButtonState[i] != buttonDown)
+				{
+					if(buttonDown)
+					{
+						GUIMouseEvent event(GUIMouseEventType::MouseDown, screenPos, (MouseButton)i);
+						mMouseOverElement->mouseEvent(event);
+					}
+					else
+					{
+						GUIMouseEvent event(GUIMouseEventType::MouseUp, screenPos, (MouseButton)i);
+						mMouseOverElement->mouseEvent(event);
+					}
+
+					mLastFrameButtonState[i] = buttonDown;
 				}
 				}
 			}
 			}
 		}
 		}

+ 2 - 26
BansheeEngine/Source/BsGUIWidget.cpp

@@ -63,30 +63,6 @@ namespace BansheeEngine
 			return &DefaultSkin;
 			return &DefaultSkin;
 	}
 	}
 
 
-	bool GUIWidget::mouseEvent(const GUIMouseEvent& ev)
-	{
-		const Matrix4& worldTfrm = SO()->getWorldTfrm();
-		Vector3 vecPos((float)ev.getPosition().x, (float)ev.getPosition().y, 0.0f);
-		vecPos = worldTfrm.inverse() * vecPos;
-
-		Int2 localPos(Math::RoundToInt(vecPos.x), Math::RoundToInt(vecPos.y));
-
-		// Elements with lowest depth (most to the front) get handled first
-		for(auto iter = mElements.rbegin(); iter != mElements.rend(); ++iter)
-		{
-			GUIElement* element = *iter;
-			const Rect& bounds = element->getBounds();
-
-			if(bounds.contains(localPos))
-			{
-				if(element->mouseEvent(ev))
-					return true;
-			}
-		}
-
-		return false;
-	}
-
 	void GUIWidget::updateMeshes() const
 	void GUIWidget::updateMeshes() const
 	{
 	{
 		struct TempMeshData
 		struct TempMeshData
@@ -159,7 +135,7 @@ namespace BansheeEngine
 		std::sort(sortedElements.begin(), sortedElements.end(), 
 		std::sort(sortedElements.begin(), sortedElements.end(), 
 			[](GUIElement* a, GUIElement* b)
 			[](GUIElement* a, GUIElement* b)
 		{
 		{
-			return a->getDepth() > b->getDepth();
+			return a->getDepth() < b->getDepth();
 		});
 		});
 
 
 		// Fill buffers for each group
 		// Fill buffers for each group
@@ -180,7 +156,7 @@ namespace BansheeEngine
 				UINT32 maxNumQuads = meshDataPerRenderElement[meshGroup].numQuads;
 				UINT32 maxNumQuads = meshDataPerRenderElement[meshGroup].numQuads;
 				UINT32 vertexStride = meshData->getVertexStride();
 				UINT32 vertexStride = meshData->getVertexStride();
 				UINT32 indexStride = meshData->getIndexElementSize();
 				UINT32 indexStride = meshData->getIndexElementSize();
-
+				
 				elem->fillBuffer(vertices, uvs, indices, startingQuad, maxNumQuads, vertexStride, indexStride, i);
 				elem->fillBuffer(vertices, uvs, indices, startingQuad, maxNumQuads, vertexStride, indexStride, i);
 				elem->markAsClean();
 				elem->markAsClean();
 
 

+ 1 - 0
CamelotClient/CmEditorWindow.cpp

@@ -47,6 +47,7 @@ namespace BansheeEditor
 		//// DEBUG
 		//// DEBUG
 		mGUI->setSkin(&EngineGUI::instance().getSkin());
 		mGUI->setSkin(&EngineGUI::instance().getSkin());
 		mDbgLabel = GUILabel::create(mGUI.get(), "Testing test", renderWindowDesc.width);
 		mDbgLabel = GUILabel::create(mGUI.get(), "Testing test", renderWindowDesc.width);
+		mDbgLabel->setDepth(-1);
 		GUIWindowFrame::create(mGUI.get());
 		GUIWindowFrame::create(mGUI.get());
 	}
 	}
 
 

+ 6 - 1
CamelotCore/Include/CmInput.h

@@ -20,7 +20,6 @@ namespace CamelotFramework
 		boost::signal<void(const MouseEvent&, MouseButton)> onMouseDown;
 		boost::signal<void(const MouseEvent&, MouseButton)> onMouseDown;
 		boost::signal<void(const MouseEvent&, MouseButton)> onMouseUp;
 		boost::signal<void(const MouseEvent&, MouseButton)> onMouseUp;
 
 
-
 		void initClipRect(Rect& clipRect);
 		void initClipRect(Rect& clipRect);
 		void registerInputHandler(InputHandlerPtr inputHandler);
 		void registerInputHandler(InputHandlerPtr inputHandler);
 
 
@@ -43,6 +42,9 @@ namespace CamelotFramework
 		 */
 		 */
 		float getVerticalAxis() const;
 		float getVerticalAxis() const;
 
 
+		bool isButtonDown(MouseButton button) const;
+		bool isKeyDown(KeyCode keyCode) const;
+
 	private:
 	private:
 		InputHandlerPtr mInputHandler;
 		InputHandlerPtr mInputHandler;
 
 
@@ -58,6 +60,9 @@ namespace CamelotFramework
 		Rect mClipRect;
 		Rect mClipRect;
 		bool mUsingClipRect;
 		bool mUsingClipRect;
 
 
+		bool mMouseButtonState[MB_Count];
+		bool mKeyState[KC_Count];
+
 		void keyDown(KeyCode keyCode);
 		void keyDown(KeyCode keyCode);
 		void keyUp(KeyCode keyCode);
 		void keyUp(KeyCode keyCode);
 
 

+ 3 - 2
CamelotCore/Include/CmInputHandler.h

@@ -152,13 +152,14 @@ namespace CamelotFramework
 		KC_WEBBACK     = 0xEA,    // Web Back
 		KC_WEBBACK     = 0xEA,    // Web Back
 		KC_MYCOMPUTER  = 0xEB,    // My Computer
 		KC_MYCOMPUTER  = 0xEB,    // My Computer
 		KC_MAIL        = 0xEC,    // Mail
 		KC_MAIL        = 0xEC,    // Mail
-		KC_MEDIASELECT = 0xED     // Media Select
+		KC_MEDIASELECT = 0xED,     // Media Select
+		KC_Count
 	};
 	};
 
 
 	enum MouseButton
 	enum MouseButton
 	{
 	{
 		MB_Left = 0, MB_Right, MB_Middle,
 		MB_Left = 0, MB_Right, MB_Middle,
-		MB_Button3, MB_Button4,	MB_Button5, MB_Button6,	MB_Button7
+		MB_Button3, MB_Button4,	MB_Button5, MB_Button6,	MB_Button7, MB_Count
 	};
 	};
 
 
 	struct MouseEvent
 	struct MouseEvent

+ 20 - 0
CamelotCore/Source/CmInput.cpp

@@ -25,6 +25,12 @@ namespace CamelotFramework
 			mVerticalHistoryBuffer[i] = 0.0f;
 			mVerticalHistoryBuffer[i] = 0.0f;
 			mTimesHistoryBuffer[i] = 0.0f;
 			mTimesHistoryBuffer[i] = 0.0f;
 		}
 		}
+
+		for(int i = 0; i < MB_Count; i++)
+			mMouseButtonState[i] = false;
+
+		for(int i = 0; i < KC_Count; i++)
+			mKeyState[i] = false;
 	}
 	}
 
 
 	Input::~Input()
 	Input::~Input()
@@ -74,11 +80,13 @@ namespace CamelotFramework
 
 
 	void Input::keyDown(KeyCode keyCode)
 	void Input::keyDown(KeyCode keyCode)
 	{
 	{
+		mKeyState[keyCode] = true;
 		onKeyDown(keyCode);
 		onKeyDown(keyCode);
 	}
 	}
 
 
 	void Input::keyUp(KeyCode keyCode)
 	void Input::keyUp(KeyCode keyCode)
 	{
 	{
+		mKeyState[keyCode] = false;
 		onKeyUp(keyCode);
 		onKeyUp(keyCode);
 	}
 	}
 
 
@@ -103,6 +111,7 @@ namespace CamelotFramework
 				return;
 				return;
 		}
 		}
 
 
+		mMouseButtonState[buttonID] = true;
 		onMouseDown(event, buttonID);
 		onMouseDown(event, buttonID);
 	}
 	}
 
 
@@ -114,6 +123,7 @@ namespace CamelotFramework
 				return;
 				return;
 		}
 		}
 
 
+		mMouseButtonState[buttonID] = false;
 		onMouseUp(event, buttonID);
 		onMouseUp(event, buttonID);
 	}
 	}
 
 
@@ -127,6 +137,16 @@ namespace CamelotFramework
 		return mSmoothVerticalAxis;
 		return mSmoothVerticalAxis;
 	}
 	}
 
 
+	bool Input::isButtonDown(MouseButton button) const
+	{
+		return mMouseButtonState[button];
+	}
+
+	bool Input::isKeyDown(KeyCode keyCode) const
+	{
+		return mKeyState[keyCode];
+	}
+
 	void Input::updateSmoothInput()
 	void Input::updateSmoothInput()
 	{
 	{
 		float currentTime = gTime().getTime();
 		float currentTime = gTime().getTime();

+ 3 - 0
TODO.txt

@@ -19,6 +19,9 @@ I still re-create GUIWidget mesh every frame instead of just updating it.
 MAJOR ISSUE: writeSubresource/readSubresoure doesn't require a shared ptr to GpuResourceData which means it could get destroyed while still in command queue. Right now it only works because I block right after I call those methods, which ensures nothing is destroyed.
 MAJOR ISSUE: writeSubresource/readSubresoure doesn't require a shared ptr to GpuResourceData which means it could get destroyed while still in command queue. Right now it only works because I block right after I call those methods, which ensures nothing is destroyed.
 GUIWidget::updateMeshes leaks. If I leave the game running I can see memory continously going up
 GUIWidget::updateMeshes leaks. If I leave the game running I can see memory continously going up
 
 
+GUI element grouping is wrong as it doesn't account for transparency
+ - Elements sharing same material cannot be batched unless they are at a nearby depth with no elements between them. I need an algorithm to handle that.
+
  - My test model is rendering back faces. I need to flip them.
  - My test model is rendering back faces. I need to flip them.
   - Although more than likely I am loading the model incorrectly since it works in Unity?
   - Although more than likely I am loading the model incorrectly since it works in Unity?
   - I probably want to determine front faces based on normals
   - I probably want to determine front faces based on normals