浏览代码

Better handling of mouse click on a non-focused window

Marko Pintera 12 年之前
父节点
当前提交
845b629a7e

+ 5 - 2
BansheeEngine/Include/BsGUIManager.h

@@ -97,6 +97,8 @@ namespace BansheeEngine
 
 
 		CM::Stack<GUIElement*>::type mScheduledForDestruction;
 		CM::Stack<GUIElement*>::type mScheduledForDestruction;
 
 
+		CM::Int2 mMouseScreenPos;
+
 		// Element and widget mouse is currently over
 		// Element and widget mouse is currently over
 		GUIWidget* mMouseOverWidget;
 		GUIWidget* mMouseOverWidget;
 		GUIElement* mMouseOverElement;
 		GUIElement* mMouseOverElement;
@@ -154,6 +156,9 @@ namespace BansheeEngine
 		void updateTextSelectionTexture();
 		void updateTextSelectionTexture();
 		void processDestroyQueue();
 		void processDestroyQueue();
 
 
+		bool findMouseOverElement(const CM::Int2& screenMousePos);
+		bool handleMouseOver(GUIWidget* widget, GUIElement* element, const CM::Int2& screenMousePos);
+
 		void onButtonDown(const CM::ButtonEvent& event);
 		void onButtonDown(const CM::ButtonEvent& event);
 		void onButtonUp(const CM::ButtonEvent& event);
 		void onButtonUp(const CM::ButtonEvent& event);
 
 
@@ -168,8 +173,6 @@ namespace BansheeEngine
 
 
 		void onMouseLeftWindow(CM::RenderWindow* win);
 		void onMouseLeftWindow(CM::RenderWindow* win);
 
 
-		bool handleMouseOver(GUIWidget* widget, GUIElement* element, const CM::Int2& screenMousePos, float wheelScrollAmount = 0.0f);;
-
 		GUIMouseButton buttonToMouseButton(CM::ButtonCode code) const;
 		GUIMouseButton buttonToMouseButton(CM::ButtonCode code) const;
 		CM::Int2 getWidgetRelativePos(const GUIWidget& widget, const CM::Int2& screenPos) const;
 		CM::Int2 getWidgetRelativePos(const GUIWidget& widget, const CM::Int2& screenPos) const;
 
 

+ 106 - 95
BansheeEngine/Source/BsGUIManager.cpp

@@ -544,6 +544,9 @@ namespace BansheeEngine
 		if(event.isUsed())
 		if(event.isUsed())
 			return;
 			return;
 
 
+		if(findMouseOverElement(mMouseScreenPos))
+			event.markAsUsed();
+
 		bool shiftDown = gInput().isButtonDown(BC_LSHIFT) || gInput().isButtonDown(BC_RSHIFT);
 		bool shiftDown = gInput().isButtonDown(BC_LSHIFT) || gInput().isButtonDown(BC_RSHIFT);
 		bool ctrlDown = gInput().isButtonDown(BC_LCONTROL) || gInput().isButtonDown(BC_RCONTROL);
 		bool ctrlDown = gInput().isButtonDown(BC_LCONTROL) || gInput().isButtonDown(BC_RCONTROL);
 		bool altDown = gInput().isButtonDown(BC_LMENU) || gInput().isButtonDown(BC_RMENU);
 		bool altDown = gInput().isButtonDown(BC_LMENU) || gInput().isButtonDown(BC_RMENU);
@@ -646,6 +649,9 @@ namespace BansheeEngine
 		if(event.isUsed())
 		if(event.isUsed())
 			return;
 			return;
 
 
+		if(findMouseOverElement(mMouseScreenPos))
+			event.markAsUsed();
+
 		bool shiftDown = gInput().isButtonDown(BC_LSHIFT) || gInput().isButtonDown(BC_RSHIFT);
 		bool shiftDown = gInput().isButtonDown(BC_LSHIFT) || gInput().isButtonDown(BC_RSHIFT);
 		bool ctrlDown = gInput().isButtonDown(BC_LCONTROL) || gInput().isButtonDown(BC_RCONTROL);
 		bool ctrlDown = gInput().isButtonDown(BC_LCONTROL) || gInput().isButtonDown(BC_RCONTROL);
 		bool altDown = gInput().isButtonDown(BC_LMENU) || gInput().isButtonDown(BC_RMENU);
 		bool altDown = gInput().isButtonDown(BC_LMENU) || gInput().isButtonDown(BC_RMENU);
@@ -731,6 +737,67 @@ namespace BansheeEngine
 		if(event.isUsed())
 		if(event.isUsed())
 			return;
 			return;
 
 
+		if(findMouseOverElement(event.screenPos))
+			event.markAsUsed();
+
+		Int2 localPos;
+		
+		if(mMouseOverWidget != nullptr)
+			localPos = getWidgetRelativePos(*mMouseOverWidget, event.screenPos);
+
+		// If mouse is being held down send MouseDrag events
+		if(mActiveElement != nullptr && mActiveMouseButton == GUIMouseButton::Left)
+		{
+			Int2 curLocalPos = getWidgetRelativePos(*mActiveWidget, event.screenPos);
+
+			if(mLastCursorLocalPos != curLocalPos)
+			{
+				mMouseEvent.setMouseDragData(mMouseOverElement, curLocalPos, curLocalPos - mLastCursorLocalPos);
+				if(sendMouseEvent(mActiveWidget, mActiveElement, mMouseEvent))
+					event.markAsUsed();
+
+				mLastCursorLocalPos = curLocalPos;
+			}
+
+			// Also if drag is in progress send DragAndDrop events
+			if(DragAndDropManager::instance().isDragInProgress())
+			{
+				if(mMouseOverElement != nullptr)
+				{
+					mMouseEvent.setDragAndDropDraggedData(mMouseOverElement, localPos, DragAndDropManager::instance().getDragTypeId(), DragAndDropManager::instance().getDragData());
+					if(sendMouseEvent(mMouseOverWidget, mMouseOverElement, mMouseEvent))
+						event.markAsUsed();
+				}
+			}
+		}
+		else // Otherwise, send MouseMove events if we are hovering over any element
+		{
+			if(mMouseOverElement != nullptr)
+			{
+				// Send MouseMove event
+				if(mLastCursorLocalPos != localPos)
+				{
+					mMouseEvent.setMouseMoveData(mMouseOverElement, localPos);
+					if(sendMouseEvent(mMouseOverWidget, mMouseOverElement, mMouseEvent))
+						event.markAsUsed();
+
+					mLastCursorLocalPos = localPos;
+				}
+
+				if(Math::Abs(event.mouseWheelScrollAmount) > 0.00001f)
+				{
+					mMouseEvent.setMouseWheelScrollData(mMouseOverElement, event.mouseWheelScrollAmount);
+					if(sendMouseEvent(mMouseOverWidget, mMouseOverElement, mMouseEvent))
+						event.markAsUsed();
+				}
+			}
+		}
+
+		mMouseScreenPos = event.screenPos;
+	}
+
+	bool GUIManager::findMouseOverElement(const CM::Int2& screenMousePos)
+	{
 #if CM_DEBUG_MODE
 #if CM_DEBUG_MODE
 		// Checks if all referenced windows actually exist
 		// Checks if all referenced windows actually exist
 		Vector<RenderWindow*>::type activeWindows = RenderWindowManager::instance().getRenderWindows();
 		Vector<RenderWindow*>::type activeWindows = RenderWindowManager::instance().getRenderWindows();
@@ -746,34 +813,37 @@ namespace BansheeEngine
 		}
 		}
 #endif
 #endif
 
 
-		GUIWidget* widgetInFocus = nullptr;
-		GUIElement* topMostElement = nullptr;
-		Int2 screenPos;
-		Int2 localPos;
+		GUIWidget* mouseOverWidget = nullptr;
+		GUIElement* mouseOverElement = nullptr;
+
+		const RenderWindow* windowUnderCursor = nullptr;
+		UnorderedSet<const RenderWindow*>::type uniqueWindows;
 
 
 		for(auto& widgetInfo : mWidgets)
 		for(auto& widgetInfo : mWidgets)
 		{
 		{
 			const RenderWindow* window = widgetInfo.widget->getOwnerWindow();
 			const RenderWindow* window = widgetInfo.widget->getOwnerWindow();
+			uniqueWindows.insert(window);
+		}
 
 
-			if(window->hasFocus())
+		for(auto& window : uniqueWindows)
+		{
+			if(Platform::isPointOverWindow(*window, screenMousePos))
 			{
 			{
-				widgetInFocus = widgetInfo.widget;
+				windowUnderCursor = window;
 				break;
 				break;
 			}
 			}
 		}
 		}
 
 
-		if(widgetInFocus != nullptr)
+		if(windowUnderCursor != nullptr)
 		{
 		{
-			const RenderWindow* window = widgetInFocus->getOwnerWindow();
-
-			Int2 screenPos = window->screenToWindowPos(event.screenPos);
-			Vector4 vecScreenPos((float)screenPos.x, (float)screenPos.y, 0.0f, 1.0f);
+			Int2 windowPos = windowUnderCursor->screenToWindowPos(screenMousePos);
+			Vector4 vecWindowPos((float)windowPos.x, (float)windowPos.y, 0.0f, 1.0f);
 
 
 			UINT32 topMostDepth = std::numeric_limits<UINT32>::max();
 			UINT32 topMostDepth = std::numeric_limits<UINT32>::max();
 			for(auto& widgetInfo : mWidgets)
 			for(auto& widgetInfo : mWidgets)
 			{
 			{
 				GUIWidget* widget = widgetInfo.widget;
 				GUIWidget* widget = widgetInfo.widget;
-				if(widget->getOwnerWindow() == window && widget->inBounds(screenPos))
+				if(widget->getOwnerWindow() == windowUnderCursor && widget->inBounds(windowPos))
 				{
 				{
 					SelectiveInputData* selectiveInputData = nullptr;
 					SelectiveInputData* selectiveInputData = nullptr;
 					if(mSelectiveInputActive)
 					if(mSelectiveInputActive)
@@ -788,8 +858,8 @@ namespace BansheeEngine
 
 
 					const Matrix4& worldTfrm = widget->SO()->getWorldTfrm();
 					const Matrix4& worldTfrm = widget->SO()->getWorldTfrm();
 
 
-					Vector4 vecLocalPos = worldTfrm.inverse() * vecScreenPos;
-					localPos = Int2(Math::RoundToInt(vecLocalPos.x), Math::RoundToInt(vecLocalPos.y));
+					Vector4 vecLocalPos = worldTfrm.inverse() * vecWindowPos;
+					Int2 localPos = Int2(Math::RoundToInt(vecLocalPos.x), Math::RoundToInt(vecLocalPos.y));
 
 
 					Vector<GUIElement*>::type sortedElements = widget->getElements();
 					Vector<GUIElement*>::type sortedElements = widget->getElements();
 					std::sort(sortedElements.begin(), sortedElements.end(), 
 					std::sort(sortedElements.begin(), sortedElements.end(), 
@@ -811,9 +881,9 @@ namespace BansheeEngine
 									continue;
 									continue;
 							}
 							}
 
 
-							topMostElement = element;
+							mouseOverElement = element;
 							topMostDepth = element->_getDepth();
 							topMostDepth = element->_getDepth();
-							widgetInFocus = widget;
+							mouseOverWidget = widget;
 
 
 							break;
 							break;
 						}
 						}
@@ -822,46 +892,16 @@ namespace BansheeEngine
 			}
 			}
 		}
 		}
 
 
-		if(handleMouseOver(widgetInFocus, topMostElement, event.screenPos, event.mouseWheelScrollAmount))
-			event.markAsUsed();
-	}
-
-	void GUIManager::onTextInput(const CM::TextInputEvent& event)
-	{
-		if(mKeyboardFocusElement != nullptr)
-		{
-			bool shiftDown = gInput().isButtonDown(BC_LSHIFT) || gInput().isButtonDown(BC_RSHIFT);
-			bool ctrlDown = gInput().isButtonDown(BC_LCONTROL) || gInput().isButtonDown(BC_RCONTROL);
-			bool altDown = gInput().isButtonDown(BC_LMENU) || gInput().isButtonDown(BC_RMENU);
-
-			if(ctrlDown || altDown) // Ignore text input because key characters + alt/ctrl usually correspond to certain commands
-				return;
-
-			mKeyEvent = GUIKeyEvent(shiftDown, ctrlDown, altDown);
-
-			mKeyEvent.setTextInputData(event.textChar);
-			if(sendKeyEvent(mKeyboardFocusWidget, mKeyboardFocusElement, mKeyEvent))
-				event.markAsUsed();
-		}
+		return handleMouseOver(mouseOverWidget, mouseOverElement, screenMousePos);
 	}
 	}
 
 
-	bool GUIManager::handleMouseOver(GUIWidget* widget, GUIElement* element, const CM::Int2& screenMousePos, float wheelScrollAmount)
+	bool GUIManager::handleMouseOver(GUIWidget* widget, GUIElement* element, const CM::Int2& screenMousePos)
 	{
 	{
 		bool eventProcessed = false;
 		bool eventProcessed = false;
 
 
 		Int2 localPos;
 		Int2 localPos;
 		if(widget != nullptr)
 		if(widget != nullptr)
-		{
-			const RenderWindow* window = widget->getOwnerWindow();
-
-			Int2 screenPos = window->screenToWindowPos(screenMousePos);
-			Vector4 vecScreenPos((float)screenPos.x, (float)screenPos.y, 0.0f, 1.0f);
-
-			const Matrix4& worldTfrm = widget->SO()->getWorldTfrm();
-
-			Vector4 vecLocalPos = worldTfrm.inverse() * vecScreenPos;
-			localPos = Int2(Math::RoundToInt(vecLocalPos.x), Math::RoundToInt(vecLocalPos.y));
-		}
+			localPos = getWidgetRelativePos(*widget, screenMousePos);
 
 
 		bool shiftDown = gInput().isButtonDown(BC_LSHIFT) || gInput().isButtonDown(BC_RSHIFT);
 		bool shiftDown = gInput().isButtonDown(BC_LSHIFT) || gInput().isButtonDown(BC_RSHIFT);
 		bool ctrlDown = gInput().isButtonDown(BC_LCONTROL) || gInput().isButtonDown(BC_RCONTROL);
 		bool ctrlDown = gInput().isButtonDown(BC_LCONTROL) || gInput().isButtonDown(BC_RCONTROL);
@@ -904,58 +944,29 @@ namespace BansheeEngine
 			}
 			}
 		}
 		}
 
 
-		// If mouse is being held down send MouseDrag events
-		if(mActiveElement != nullptr && mActiveMouseButton == GUIMouseButton::Left)
-		{
-			Int2 curLocalPos = getWidgetRelativePos(*mActiveWidget, screenMousePos);
-
-			if(mLastCursorLocalPos != curLocalPos)
-			{
-				mMouseEvent.setMouseDragData(element, curLocalPos, curLocalPos - mLastCursorLocalPos);
-				if(sendMouseEvent(mActiveWidget, mActiveElement, mMouseEvent))
-					eventProcessed = true;
+		mMouseOverElement = element;
+		mMouseOverWidget = widget;
 
 
-				mLastCursorLocalPos = curLocalPos;
-			}
+		return eventProcessed;
+	}
 
 
-			// Also if drag is in progress send DragAndDrop events
-			if(DragAndDropManager::instance().isDragInProgress())
-			{
-				if(element != nullptr)
-				{
-					mMouseEvent.setDragAndDropDraggedData(element, localPos, DragAndDropManager::instance().getDragTypeId(), DragAndDropManager::instance().getDragData());
-					if(sendMouseEvent(widget, element, mMouseEvent))
-						eventProcessed = true;
-				}
-			}
-		}
-		else // Otherwise, send MouseMove events if we are hovering over any element
+	void GUIManager::onTextInput(const CM::TextInputEvent& event)
+	{
+		if(mKeyboardFocusElement != nullptr)
 		{
 		{
-			if(element != nullptr)
-			{
-				// Send MouseMove event
-				if(mLastCursorLocalPos != localPos)
-				{
-					mMouseEvent.setMouseMoveData(element, localPos);
-					if(sendMouseEvent(widget, element, mMouseEvent))
-						eventProcessed = true;
-
-					mLastCursorLocalPos = localPos;
-				}
+			bool shiftDown = gInput().isButtonDown(BC_LSHIFT) || gInput().isButtonDown(BC_RSHIFT);
+			bool ctrlDown = gInput().isButtonDown(BC_LCONTROL) || gInput().isButtonDown(BC_RCONTROL);
+			bool altDown = gInput().isButtonDown(BC_LMENU) || gInput().isButtonDown(BC_RMENU);
 
 
-				if(Math::Abs(wheelScrollAmount) > 0.00001f)
-				{
-					mMouseEvent.setMouseWheelScrollData(element, wheelScrollAmount);
-					if(sendMouseEvent(widget, element, mMouseEvent))
-						eventProcessed = true;
-				}
-			}
-		}
+			if(ctrlDown || altDown) // Ignore text input because key characters + alt/ctrl usually correspond to certain commands
+				return;
 
 
-		mMouseOverElement = element;
-		mMouseOverWidget = widget;
+			mKeyEvent = GUIKeyEvent(shiftDown, ctrlDown, altDown);
 
 
-		return false;
+			mKeyEvent.setTextInputData(event.textChar);
+			if(sendKeyEvent(mKeyboardFocusWidget, mKeyboardFocusElement, mKeyEvent))
+				event.markAsUsed();
+		}
 	}
 	}
 
 
 	void GUIManager::onWindowFocusGained(RenderWindow& win)
 	void GUIManager::onWindowFocusGained(RenderWindow& win)

+ 5 - 0
CamelotCore/Include/Win32/CmPlatformImpl.h

@@ -72,6 +72,11 @@ namespace CamelotFramework
 		 */
 		 */
 		static void releaseMouseCapture();
 		static void releaseMouseCapture();
 
 
+		/**
+		 * @brief	Checks if provided over screen position is over the specified window.
+		 */
+		static bool isPointOverWindow(const RenderWindow& window, const Int2& screenPos);
+
 		/**
 		/**
 		 * @brief	Limit cursor movement to the specified window.
 		 * @brief	Limit cursor movement to the specified window.
 		 *
 		 *

+ 15 - 0
CamelotCore/Source/Win32/CmPlatformImpl.cpp

@@ -65,6 +65,21 @@ namespace CamelotFramework
 		PostMessage(hwnd, WM_CM_RELEASECAPTURE, WPARAM(hwnd), 0);
 		PostMessage(hwnd, WM_CM_RELEASECAPTURE, WPARAM(hwnd), 0);
 	}
 	}
 
 
+	bool Platform::isPointOverWindow(const RenderWindow& window, const Int2& screenPos)
+	{
+		RenderWindowPtr primaryWindow = gApplication().getPrimaryWindow();
+
+		POINT point;
+		point.x = screenPos.x;
+		point.y = screenPos.y;
+
+		HWND hwndToCheck;
+		window.getCustomAttribute("WINDOW", &hwndToCheck);
+
+		HWND hwndUnderPos = WindowFromPoint(point);
+		return hwndUnderPos == hwndToCheck;
+	}
+
 	void Platform::hideCursor()
 	void Platform::hideCursor()
 	{
 	{
 		mIsCursorHidden = true;
 		mIsCursorHidden = true;

+ 2 - 8
TODO.txt

@@ -13,18 +13,17 @@ GUI SYSTEM WRAP UP:
  GUIManager drag events should only trigger after a mouse moves a few pixels
  GUIManager drag events should only trigger after a mouse moves a few pixels
   - Right now it is breaking double click as start/end drag events get sent out twice
   - Right now it is breaking double click as start/end drag events get sent out twice
 
 
- Writing a long word so that text scrolls, then deleting it will not scroll back unless the cursor is out of view
-
  Key repeat
  Key repeat
+  - Add special text input commands, which will get repeatedly sent as long as their corresponding key is set in GUIManager
  Double click (Input box select all)
  Double click (Input box select all)
  Windows drag and drop detect
  Windows drag and drop detect
   - http://www.codeguru.com/cpp/misc/misc/draganddrop/article.php/c349/Drag-And-Drop-between-Window-Controls.htm
   - http://www.codeguru.com/cpp/misc/misc/draganddrop/article.php/c349/Drag-And-Drop-between-Window-Controls.htm
   - http://www.catch22.net/tuts/drop-target
   - http://www.catch22.net/tuts/drop-target
   - http://msdn.microsoft.com/en-us/library/windows/desktop/bb776902(v=vs.85).aspx
   - http://msdn.microsoft.com/en-us/library/windows/desktop/bb776902(v=vs.85).aspx
  Click when unfocused actually effects the elements in that window/widget
  Click when unfocused actually effects the elements in that window/widget
+  - This is bugged in many different ways, sometimes it works sometimes it doesn't.
 
 
 IMMEDIATE:
 IMMEDIATE:
- - Clicking on a window to focus and immediately trying to drag/resize it, doesn't work. I first need to click, then click again to drag/resize.
  - OpenGL rendering slows to extremely with time (seems to be related to rendering, possibly GUI, possibly in general Pass/Material/Shader/PassParams)
  - OpenGL rendering slows to extremely with time (seems to be related to rendering, possibly GUI, possibly in general Pass/Material/Shader/PassParams)
  - Update debug camera so it uses callbacks
  - Update debug camera so it uses callbacks
  
  
@@ -37,15 +36,10 @@ IMMEDIATE:
 - Hover colors of the scroll bar are wrong
 - Hover colors of the scroll bar are wrong
 
 
 TextBox needed elements:
 TextBox needed elements:
- - Key-repeat? Pressing left/right/up/down arrows doesn't repeat the keys (also delete/backspace)
-  - Add special text input commands, which will get repeatedly sent as long as their corresponding key is set in GUIManager
  - Get DebugCamera to ignore input if GUI has already processed it
  - Get DebugCamera to ignore input if GUI has already processed it
- - Cut/Copy/Paste
- - Clicking on the InputBox doesn't display the cursor. I need to click another time and then it shows up.
 
 
  - LATER
  - LATER
   - TAB between input elements
   - TAB between input elements
-  - Context menu with copy/cut/paste
   - Remove updateText calls from updateRenderElementsInternal and instead call it whenever offsets change
   - Remove updateText calls from updateRenderElementsInternal and instead call it whenever offsets change
   - I might consider not rendering caret from within input sprite to avoid redrawing it while, and draw it directly from GUIManager
   - I might consider not rendering caret from within input sprite to avoid redrawing it while, and draw it directly from GUIManager