Przeglądaj źródła

Added support for selective input in GUIManager

Marko Pintera 12 lat temu
rodzic
commit
469ea333ec

+ 29 - 1
BansheeEngine/Include/BsGUIManager.h

@@ -64,9 +64,34 @@ namespace BansheeEngine
 		GUIInputCaret* getInputCaretTool() const { return mInputCaret; }
 		GUIInputSelection* getInputSelectionTool() const { return mInputSelection; }
 
+		/**
+		 * @brief	Selective input allows you to limit input only to certain GUI elements.
+		 * 			After enabling selective input use addSelectiveInput* methods to add specific
+		 * 			elements that will be allowed to receive input.
+		 */
+		void enableSelectiveInput();
+
+		/**
+		 * @brief	Disables selective input and clears any selective input elements.
+		 */
+		void disableSelectiveInput();
+
+		void addSelectiveInputWidget(const GUIWidget* widget);
+		void addSelectiveInputElement(const GUIElement* element);
+
 		boost::signal<void(GUIWidget*, GUIElement*, const GUIMouseEvent&)> mouseEventFilter;
 		boost::signal<void(GUIWidget*, GUIElement*, const GUIKeyEvent&)> keyEventFilter;
 	private:
+		struct SelectiveInputData
+		{
+			SelectiveInputData()
+				:acceptAllElements(false)
+			{ }
+
+			CM::Set<const GUIElement*>::type elements;
+			bool acceptAllElements;
+		};
+
 		CM::Vector<WidgetInfo>::type mWidgets;
 		CM::UnorderedMap<const CM::Viewport*, GUIRenderData>::type mCachedGUIData;
 
@@ -107,11 +132,14 @@ namespace BansheeEngine
 		// Drop down box
 		bool mDropDownBoxOpenScheduled;
 		bool mDropDownBoxActive;
-		bool mDropDownBoxCloseScheduled;
 		CM::HSceneObject mDropDownSO;
 		CM::GameObjectHandle<GUIDropDownBox> mDropDownBox;
 		std::function<void(CM::UINT32)> mListBoxSelectionMade;
 
+		// Selective input
+		bool mSelectiveInputActive;
+		CM::Map<const GUIWidget*, SelectiveInputData>::type mSelectiveInputData;
+
 		boost::signals::connection mOnButtonDownConn;
 		boost::signals::connection mOnButtonUpConn;
 		boost::signals::connection mOnMouseMovedConn;

+ 1 - 0
BansheeEngine/Source/BsGUIListBox.cpp

@@ -179,6 +179,7 @@ namespace BansheeEngine
 			markContentAsDirty();
 
 			GUIManager::instance().openDropDownListBox(this, mElements, boost::bind(&GUIListBox::elementSelected, this, _1), _getParentWidget().getSkin());
+			GUIManager::instance().addSelectiveInputElement(this);
 
 			return true;
 		}

+ 60 - 23
BansheeEngine/Source/BsGUIManager.cpp

@@ -56,7 +56,7 @@ namespace BansheeEngine
 		mActiveWidget(nullptr), mActiveMouseButton(GUIMouseButton::Left), mKeyboardFocusElement(nullptr), mKeyboardFocusWidget(nullptr),
 		mCaretTexture(nullptr), mCaretBlinkInterval(0.5f), mCaretLastBlinkTime(0.0f), mCaretColor(1.0f, 0.6588f, 0.0f), mIsCaretOn(false),
 		mTextSelectionColor(1.0f, 0.6588f, 0.0f), mInputCaret(nullptr), mInputSelection(nullptr), mDropDownBoxActive(false), mDropDownBoxOpenScheduled(false),
-		mDropDownBoxCloseScheduled(false)
+		mSelectiveInputActive(false)
 	{
 		mOnButtonDownConn = gInput().onButtonDown.connect(boost::bind(&GUIManager::onButtonDown, this, _1));
 		mOnButtonUpConn = gInput().onButtonUp.connect(boost::bind(&GUIManager::onButtonUp, this, _1));
@@ -175,12 +175,6 @@ namespace BansheeEngine
 			mDropDownBoxOpenScheduled = false;
 		}
 
-		if(mDropDownBoxCloseScheduled)
-		{
-			closeDropDownBox();
-			mDropDownBoxCloseScheduled = false;
-		}
-
 		// Update layouts
 		for(auto& widgetInfo : mWidgets)
 		{
@@ -520,6 +514,9 @@ namespace BansheeEngine
 		mDropDownSO = SceneObject::create("DropDownBox");
 		mDropDownBox = mDropDownSO->addComponent<GUIDropDownBox>();
 
+		enableSelectiveInput();
+		addSelectiveInputWidget(mDropDownBox.get());
+
 		GUIWidget& widget = parentList->_getParentWidget();
 
 		Vector<GUIDropDownData>::type dropDownData;
@@ -547,6 +544,7 @@ namespace BansheeEngine
 
 	void GUIManager::closeDropDownBox()
 	{
+		disableSelectiveInput();
 		mDropDownSO->destroy();
 
 		mDropDownBoxActive = false;
@@ -560,6 +558,9 @@ namespace BansheeEngine
 		mDropDownSO = SceneObject::create("DropDownBox");
 		mDropDownBox = mDropDownSO->addComponent<GUIDropDownBox>();
 
+		enableSelectiveInput();
+		addSelectiveInputWidget(mDropDownBox.get());
+
 		Vector<GUIDropDownData>::type dropDownData = menu->getDropDownData();
 		GUIWidget& widget = parentButton->_getParentWidget();
 
@@ -577,6 +578,9 @@ namespace BansheeEngine
 		mDropDownSO = SceneObject::create("DropDownBox");
 		mDropDownBox = mDropDownSO->addComponent<GUIDropDownBox>();
 
+		enableSelectiveInput();
+		addSelectiveInputWidget(mDropDownBox.get());
+
 		Vector<GUIDropDownData>::type dropDownData = menu->getDropDownData();
 
 		mDropDownBox->initialize(widget.getTarget(), widget.getOwnerWindow(), 
@@ -663,8 +667,7 @@ namespace BansheeEngine
 
 				if(!clickedOnDropDownBox)
 				{
-					mDropDownBoxCloseScheduled = true;
-					return;
+					closeDropDownBox();
 				}
 			}
 
@@ -746,13 +749,6 @@ namespace BansheeEngine
 
 			mMouseEvent = GUIMouseEvent(buttonStates, shiftDown, ctrlDown, altDown);
 
-			// Ignore input if drop box is active and we're not interacting with its elements
-			if(mDropDownBoxActive)
-			{
-				if(mMouseOverElement == nullptr || (&mMouseOverElement->_getParentWidget() != mDropDownBox.get()))
-					return;	
-			}
-
 			Int2 localPos;
 			if(mMouseOverWidget != nullptr)
 			{
@@ -854,6 +850,17 @@ namespace BansheeEngine
 				GUIWidget* widget = widgetInfo.widget;
 				if(widget->getOwnerWindow() == window && widget->inBounds(screenPos))
 				{
+					SelectiveInputData* selectiveInputData = nullptr;
+					if(mSelectiveInputActive)
+					{
+						auto selectionIterFind = mSelectiveInputData.find(widget);
+
+						if(selectionIterFind == mSelectiveInputData.end())
+							continue;
+						else
+							selectiveInputData = &selectionIterFind->second;
+					}
+
 					const Matrix4& worldTfrm = widget->SO()->getWorldTfrm();
 
 					Vector4 vecLocalPos = worldTfrm.inverse() * vecScreenPos;
@@ -873,6 +880,12 @@ namespace BansheeEngine
 
 						if(!element->_isDisabled() && element->_isInBounds(localPos) && element->_getDepth() < topMostDepth)
 						{
+							if(mSelectiveInputActive && !selectiveInputData->acceptAllElements)
+							{
+								if(selectiveInputData->elements.find(element) == selectiveInputData->elements.end())
+									continue;
+							}
+
 							topMostElement = element;
 							topMostDepth = element->_getDepth();
 							widgetInFocus = widget;
@@ -911,13 +924,6 @@ namespace BansheeEngine
 	{
 		bool eventProcessed = false;
 
-		// Ignore input if drop box is active and we're not interacting with its elements
-		if(mDropDownBoxActive)
-		{
-			if(element == nullptr || (&element->_getParentWidget() != mDropDownBox.get()))
-				return false;	
-		}
-
 		Int2 localPos;
 		if(widget != nullptr)
 		{
@@ -1078,6 +1084,37 @@ namespace BansheeEngine
 		}
 	}
 
+	void GUIManager::enableSelectiveInput()
+	{
+		mSelectiveInputActive = true;
+	}
+
+	void GUIManager::disableSelectiveInput()
+	{
+		mSelectiveInputData.clear();
+		mSelectiveInputActive = false;
+	}
+
+	void GUIManager::addSelectiveInputWidget(const GUIWidget* widget)
+	{
+		auto findIter = mSelectiveInputData.find(widget);
+
+		if(findIter == mSelectiveInputData.end())
+		{
+			SelectiveInputData& data = mSelectiveInputData[widget];
+			data.acceptAllElements = true;
+		}
+		else
+		{
+			mSelectiveInputData[widget].acceptAllElements = true;
+		}
+	}
+
+	void GUIManager::addSelectiveInputElement(const GUIElement* element)
+	{
+		mSelectiveInputData[&element->_getParentWidget()].elements.insert(element);
+	}
+
 	GUIMouseButton GUIManager::buttonToMouseButton(ButtonCode code) const
 	{
 		if(code == BC_MOUSE_LEFT)

+ 5 - 0
CamelotClient/Source/BsGUIMenuBar.cpp

@@ -205,5 +205,10 @@ namespace BansheeEditor
 			return;
 
 		GUIManager::instance().openMenuBarMenu(subMenu->button, subMenu->menu);
+
+		for(auto& childMenu : mChildMenus)
+		{
+			GUIManager::instance().addSelectiveInputElement(childMenu.button);
+		}
 	}
 }

+ 5 - 2
DropDown.txt

@@ -2,8 +2,11 @@ GUI ignores image in GUIContent for most elements.
 
 Doesn't belong here but as an additional reminder: Remove Component::initialize and instead make multi paramter addComponent
 
-There seems to be a depth issue between menu items and GUIRenderTexture
-When I have a sub-menu open, mousing over any other entry button should close the sub-menu
+Once drop down menu is open the initial button (for listbox and menubar) should remain in active state
+Mousing over other buttons in menu bar should open their menus
+ - GUIManager currently blocks input to anything but currently open drop down box. Maybe also allow input
+    on the drop down box owner (Might be tricky since MenuBar consists of multiple different elements).
+MouseOver over sub-menu buttons doesn't trigger hover style
 
 Add min/max/close buttons to GUIMenuBar
 Setup move areas in GUIMenuBar