Browse Source

Clicking on a parent of a GUI menu item now properly closes the sub menu if its currently open

Marko Pintera 10 years ago
parent
commit
9f802264bf

+ 13 - 4
BansheeEditor/Source/BsGUIMenuBar.cpp

@@ -277,13 +277,22 @@ namespace BansheeEngine
 				return;
 		}
 
-		GUIDropDownData dropDownData = subMenu->menu->getDropDownData();
 		GUIWidget* widget = subMenu->button->_getParentWidget();
 
-		DropDownAreaPlacement placement = DropDownAreaPlacement::aroundBoundsHorz(subMenu->button->_getLayoutData().area);
+		DROP_DOWN_BOX_DESC desc;
+		desc.target = widget->getTarget();
+		desc.skin = widget->getSkinResource();
+		desc.placement = DropDownAreaPlacement::aroundBoundsHorz(subMenu->button->_getLayoutData().area);
+		desc.dropDownData = subMenu->menu->getDropDownData();
 
-		GameObjectHandle<GUIDropDownMenu> dropDownBox = GUIDropDownBoxManager::instance().openDropDownBox(widget->getTarget(), 
-			placement, dropDownData, widget->getSkinResource(), GUIDropDownType::MenuBar, std::bind(&GUIMenuBar::onSubMenuClosed, this));
+		for (auto& childMenu : mChildMenus)
+		{
+			Rect2I bounds = childMenu.button->getBounds();
+			desc.additionalBounds.push_back(bounds);
+		}
+		
+		GameObjectHandle<GUIDropDownMenu> dropDownBox = GUIDropDownBoxManager::instance().openDropDownBox(
+			desc, GUIDropDownType::MenuBar, std::bind(&GUIMenuBar::onSubMenuClosed, this));
 
 		subMenu->button->_setOn(true);
 

+ 2 - 2
BansheeEngine/Include/BsDropDownAreaPlacement.h

@@ -45,6 +45,8 @@ namespace BansheeEngine
 			Up, Down
 		};
 
+		DropDownAreaPlacement() { }
+
 		/**
 		 * @brief	Drop down box will be placed at the specified position. By default the system
 		 * 			prefers the top left corner of the box to correspond to the position, but if
@@ -96,8 +98,6 @@ namespace BansheeEngine
 		Rect2I getOptimalBounds(UINT32 width, UINT32 height, const Rect2I& availableArea, HorzDir& horzDir, VertDir& vertDir) const;
 
 	private:
-		DropDownAreaPlacement() { }
-
 		Type mType;
 		Rect2I mBounds;
 		Vector2I mPosition;

+ 3 - 6
BansheeEngine/Include/BsGUIDropDownBoxManager.h

@@ -18,15 +18,12 @@ namespace BansheeEngine
 		 * @brief	Opens a new drop down box at the specified location, look and elements. This will close
 		 *			any previously open drop down box.
 		 *
-		 * @param	target				Viewport on which to open the drop down box.
-		 * @param	placement			Determines how is the drop down box positioned in the visible area.
-		 * @param	dropDownData		Data to use for initializing menu items of the drop down box.
-		 * @param	skin				Skin to use for drop down box GUI elements.
+		 * @param	desc				Various parameters for initializing the drop down box.
 		 * @param	type				Specific type of drop down box to display.
 		 * @param	onClosedCallback	Callback triggered when drop down box is closed.
 		 */
-		GameObjectHandle<GUIDropDownMenu> openDropDownBox(Viewport* target, const DropDownAreaPlacement& placement,
-			const GUIDropDownData& dropDownData, const HGUISkin& skin, GUIDropDownType type, std::function<void()> onClosedCallback);
+		GameObjectHandle<GUIDropDownMenu> openDropDownBox(const DROP_DOWN_BOX_DESC& desc, 
+			GUIDropDownType type, std::function<void()> onClosedCallback);
 
 		/**
 		 * @brief	Closes the currently active drop down box (if any).

+ 18 - 7
BansheeEngine/Include/BsGUIDropDownMenu.h

@@ -3,6 +3,7 @@
 #include "BsPrerequisites.h"
 #include "BsGUIWidget.h"
 #include "BsVector2I.h"
+#include "BsRect2I.h"
 #include "BsEvent.h"
 #include "BsDropDownAreaPlacement.h"
 
@@ -17,6 +18,18 @@ namespace BansheeEngine
 		UnorderedMap<WString, HString> localizedNames;
 	};
 
+	/**
+	 * @brief	A set of parameters used for initializing a drop down box.
+	 */
+	struct DROP_DOWN_BOX_DESC
+	{
+		Viewport* target; /**< Viewport on which to open the drop down box. */
+		DropDownAreaPlacement placement; /**< Determines how is the drop down box positioned in the visible area. */
+		GUIDropDownData dropDownData; /**< Data to use for initializing menu items of the drop down box. */
+		HGUISkin skin; /**< Skin to use for drop down box GUI elements. */
+		Vector<Rect2I> additionalBounds; /**< Additional bounds that control what is considered the inside or the outside of the drop down box. */
+	};
+
 	/**
 	 * @brief	Represents a single entry in a drop down box.
 	 */
@@ -107,14 +120,10 @@ namespace BansheeEngine
 		 * @brief	Creates a new drop down box widget.
 		 *
 		 * @param	parent			Parent scene object to attach the drop down box to.
-		 * @param	target			Viewport on which to open the drop down box.
-		 * @param	placement		Determines how is the drop down box positioned in the visible area.
-		 * @param	dropDownData	Data to use for initializing menu items of the drop down box.
-		 * @param	skin			Skin to use for drop down box GUI elements.
+		 * @param	desc			Various parameters that control the drop down menu features and content.
 		 * @param	type			Specific type of drop down box to display.
 		 */
-		GUIDropDownMenu(const HSceneObject& parent, Viewport* target, const DropDownAreaPlacement& placement,
-			const GUIDropDownData& dropDownData, const HGUISkin& skin, GUIDropDownType type);
+		GUIDropDownMenu(const HSceneObject& parent, const DROP_DOWN_BOX_DESC& desc, GUIDropDownType type);
 		~GUIDropDownMenu();
 
 	private:
@@ -283,6 +292,8 @@ namespace BansheeEngine
 		// Captures mouse clicks so that we don't trigger elements outside the drop down box when we just want to close it.
 		// (Particular example is clicking on the button that opened the drop down box in the first place. Clicking will cause
 		// the drop down to lose focus and close, but if the button still processes the mouse click it will be immediately opened again)
-		GUIDropDownHitBox* mCaptureHitBox; 
+		GUIDropDownHitBox* mCaptureHitBox;
+
+		Vector<Rect2I> mAdditionalCaptureBounds;
 	};
 }

+ 8 - 4
BansheeEngine/Source/BsGUIContextMenu.cpp

@@ -17,10 +17,14 @@ namespace BansheeEngine
 
 	void GUIContextMenu::open(const Vector2I& position, GUIWidget& widget)
 	{
-		DropDownAreaPlacement placement = DropDownAreaPlacement::aroundPosition(position);
-
-		GameObjectHandle<GUIDropDownMenu> dropDownBox = GUIDropDownBoxManager::instance().openDropDownBox(widget.getTarget(), 
-			placement, getDropDownData(), widget.getSkinResource(), GUIDropDownType::ContextMenu, std::bind(&GUIContextMenu::onMenuClosed, this));
+		DROP_DOWN_BOX_DESC desc;
+		desc.target = widget.getTarget();
+		desc.skin = widget.getSkinResource();
+		desc.placement = DropDownAreaPlacement::aroundPosition(position);
+		desc.dropDownData = getDropDownData();
+
+		GameObjectHandle<GUIDropDownMenu> dropDownBox = GUIDropDownBoxManager::instance().openDropDownBox(
+			desc, GUIDropDownType::ContextMenu, std::bind(&GUIContextMenu::onMenuClosed, this));
 
 		mContextMenuOpen = true;
 	}

+ 3 - 3
BansheeEngine/Source/BsGUIDropDownBoxManager.cpp

@@ -8,13 +8,13 @@ namespace BansheeEngine
 		closeDropDownBox();
 	}
 
-	GameObjectHandle<GUIDropDownMenu> GUIDropDownBoxManager::openDropDownBox(Viewport* target, const DropDownAreaPlacement& placement,
-		const GUIDropDownData& dropDownData, const HGUISkin& skin, GUIDropDownType type, std::function<void()> onClosedCallback)
+	GameObjectHandle<GUIDropDownMenu> GUIDropDownBoxManager::openDropDownBox(const DROP_DOWN_BOX_DESC& desc,
+		GUIDropDownType type, std::function<void()> onClosedCallback)
 	{
 		closeDropDownBox();
 
 		mDropDownSO = SceneObject::create("DropDownBox", SOF_Internal | SOF_Persistent | SOF_DontSave);
-		mDropDownBox = mDropDownSO->addComponent<GUIDropDownMenu>(target, placement, dropDownData, skin, type);
+		mDropDownBox = mDropDownSO->addComponent<GUIDropDownMenu>(desc, type);
 		mOnClosedCallback = onClosedCallback;
 
 		return mDropDownBox;

+ 20 - 11
BansheeEngine/Source/BsGUIDropDownMenu.cpp

@@ -52,9 +52,8 @@ namespace BansheeEngine
 		return dataEntry;
 	}
 
-	GUIDropDownMenu::GUIDropDownMenu(const HSceneObject& parent, Viewport* target, const DropDownAreaPlacement& placement,
-		const GUIDropDownData& dropDownData, const HGUISkin& skin, GUIDropDownType type)
-		:GUIWidget(parent, target), mRootMenu(nullptr), mFrontHitBox(nullptr), mCaptureHitBox(nullptr), mBackHitBox(nullptr)
+	GUIDropDownMenu::GUIDropDownMenu(const HSceneObject& parent, const DROP_DOWN_BOX_DESC& desc, GUIDropDownType type)
+		:GUIWidget(parent, desc.target), mRootMenu(nullptr), mFrontHitBox(nullptr), mCaptureHitBox(nullptr), mBackHitBox(nullptr)
 	{
 		String stylePrefix = "";
 		switch(type)
@@ -75,11 +74,11 @@ namespace BansheeEngine
 		mBackgroundStyle = stylePrefix + "Frame";
 		mContentStyle = stylePrefix + "Content";
 
-		mScrollUpBtnArrow = skin->getStyle(stylePrefix + "ScrollUpBtnArrow")->normal.texture;
-		mScrollDownBtnArrow = skin->getStyle(stylePrefix + "ScrollDownBtnArrow")->normal.texture;
+		mScrollUpBtnArrow = desc.skin->getStyle(stylePrefix + "ScrollUpBtnArrow")->normal.texture;
+		mScrollDownBtnArrow = desc.skin->getStyle(stylePrefix + "ScrollDownBtnArrow")->normal.texture;
 
 		setDepth(0); // Needs to be in front of everything
-		setSkin(skin);
+		setSkin(desc.skin);
 
 		mFrontHitBox = GUIDropDownHitBox::create(false, false);
 		mFrontHitBox->onFocusLost.connect(std::bind(&GUIDropDownMenu::dropDownFocusLost, this));
@@ -100,7 +99,7 @@ namespace BansheeEngine
 		mBackHitBox->_markContentAsDirty();
 
 		mCaptureHitBox = GUIDropDownHitBox::create(true, false);
-		mCaptureHitBox->setBounds(Rect2I(0, 0, target->getWidth(), target->getHeight()));
+		mCaptureHitBox->setBounds(Rect2I(0, 0, desc.target->getWidth(), desc.target->getHeight()));
 		GUILayoutData captureHitboxLayoutData = mCaptureHitBox->_getLayoutData();
 		captureHitboxLayoutData.setWidgetDepth(0);
 		captureHitboxLayoutData.setPanelDepth(std::numeric_limits<INT16>::max());
@@ -108,8 +107,10 @@ namespace BansheeEngine
 		mCaptureHitBox->_changeParentWidget(this);
 		mCaptureHitBox->_markContentAsDirty();
 
-		Rect2I availableBounds(target->getX(), target->getY(), target->getWidth(), target->getHeight());
-		mRootMenu = bs_new<DropDownSubMenu>(this, nullptr, placement, availableBounds, dropDownData, type, 0);
+		mAdditionalCaptureBounds = desc.additionalBounds;
+
+		Rect2I availableBounds(desc.target->getX(), desc.target->getY(), desc.target->getWidth(), desc.target->getHeight());
+		mRootMenu = bs_new<DropDownSubMenu>(this, nullptr, desc.placement, availableBounds, desc.dropDownData, type, 0);
 	}
 
 	GUIDropDownMenu::~GUIDropDownMenu()
@@ -144,8 +145,12 @@ namespace BansheeEngine
 			subMenu = subMenu->mSubMenu;
 		}
 
-		mFrontHitBox->setBounds(bounds);
 		mBackHitBox->setBounds(bounds);
+
+		for (auto& additionalBound : mAdditionalCaptureBounds)
+			bounds.push_back(additionalBound);
+
+		mFrontHitBox->setBounds(bounds);
 	}
 
 	void GUIDropDownMenu::notifySubMenuClosed(DropDownSubMenu* subMenu)
@@ -159,8 +164,12 @@ namespace BansheeEngine
 			subMenu = subMenu->mSubMenu;
 		}
 
-		mFrontHitBox->setBounds(bounds);
 		mBackHitBox->setBounds(bounds);
+		
+		for (auto& additionalBound : mAdditionalCaptureBounds)
+			bounds.push_back(additionalBound);
+
+		mFrontHitBox->setBounds(bounds);
 	}
 
 	GUIDropDownMenu::DropDownSubMenu::DropDownSubMenu(GUIDropDownMenu* owner, DropDownSubMenu* parent, const DropDownAreaPlacement& placement,

+ 10 - 6
BansheeEngine/Source/BsGUIListBox.cpp

@@ -95,21 +95,25 @@ namespace BansheeEngine
 	{
 		closeListBox();
 
-		GUIDropDownData dropDownData;
+		DROP_DOWN_BOX_DESC desc;
+
 		UINT32 i = 0;
 		for(auto& elem : mElements)
 		{
 			WString identifier = toWString(i);
-			dropDownData.entries.push_back(GUIDropDownDataEntry::button(identifier, std::bind(&GUIListBox::elementSelected, this, i)));
-			dropDownData.localizedNames[identifier] = elem;
+			desc.dropDownData.entries.push_back(GUIDropDownDataEntry::button(identifier, std::bind(&GUIListBox::elementSelected, this, i)));
+			desc.dropDownData.localizedNames[identifier] = elem;
 			i++;
 		}
 
 		GUIWidget* widget = _getParentWidget();
-		DropDownAreaPlacement placement = DropDownAreaPlacement::aroundBoundsHorz(_getLayoutData().area);
 
-		GameObjectHandle<GUIDropDownMenu> dropDownBox = GUIDropDownBoxManager::instance().openDropDownBox(widget->getTarget(), 
-			placement, dropDownData, widget->getSkinResource(), GUIDropDownType::MenuBar, std::bind(&GUIListBox::onListBoxClosed, this));
+		desc.target = widget->getTarget();
+		desc.skin = widget->getSkinResource();
+		desc.placement = DropDownAreaPlacement::aroundBoundsHorz(_getLayoutData().area);
+		
+		GameObjectHandle<GUIDropDownMenu> dropDownBox = GUIDropDownBoxManager::instance().openDropDownBox(
+			desc, GUIDropDownType::MenuBar, std::bind(&GUIListBox::onListBoxClosed, this));
 
 		_setOn(true);
 		mIsListBoxOpen = true;

+ 1 - 2
TODO.txt

@@ -62,8 +62,7 @@ Code quality improvements:
 ----------------------------------------------------------------------
 Polish stage 1
 
-Click on empty part of the menu bar doesn't close the menu bar (ignoring NC area clicks?)
-Clicking on a parent of a menu item just reopens that item but I would expect it to close it
+Hovering over a menu item doesn't open its sub-menu
 Docking a window to another windows title bar doesn't seem to work
 Decent looking default layout
 Fix DX11 (and possibly DX9) rendering