2
0
Эх сурвалжийг харах

Moved drop box methods out from GUIManager into their individual classes

Marko Pintera 12 жил өмнө
parent
commit
98c3aa7686

+ 3 - 1
BansheeEngine/BansheeEngine.vcxproj

@@ -238,6 +238,7 @@
     <ClInclude Include="Include\BsGUIContent.h" />
     <ClInclude Include="Include\BsGUIContextMenu.h" />
     <ClInclude Include="Include\BsGUIDropDownBox.h" />
+    <ClInclude Include="Include\BsGUIDropDownBoxManager.h" />
     <ClInclude Include="Include\BsGUIHelper.h" />
     <ClInclude Include="Include\BsGUIListBox.h" />
     <ClInclude Include="Include\BsGUIElementBase.h" />
@@ -286,7 +287,7 @@
     <ClInclude Include="Include\BsD3D9BuiltinMaterialFactory.h" />
     <ClInclude Include="Include\BsGLBuiltinMaterialFactory.h" />
     <ClInclude Include="Include\BsUpdateCallback.h" />
-    <ClInclude Include="Source\BsGUIContextMenu.cpp" />
+    <ClCompile Include="Source\BsGUIContextMenu.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsApplication.cpp" />
@@ -299,6 +300,7 @@
     <ClCompile Include="Source\BsGUIButton.cpp" />
     <ClCompile Include="Source\BsGUIContent.cpp" />
     <ClCompile Include="Source\BsGUIDropDownBox.cpp" />
+    <ClCompile Include="Source\BsGUIDropDownBoxManager.cpp" />
     <ClCompile Include="Source\BsGUIHelper.cpp" />
     <ClCompile Include="Source\BsGUIListBox.cpp" />
     <ClCompile Include="Source\BsGUIElement.cpp" />

+ 9 - 3
BansheeEngine/BansheeEngine.vcxproj.filters

@@ -213,12 +213,12 @@
     <ClInclude Include="Include\BsGUIContextMenu.h">
       <Filter>Header Files\GUI</Filter>
     </ClInclude>
-    <ClInclude Include="Source\BsGUIContextMenu.cpp">
-      <Filter>Source Files\GUI</Filter>
-    </ClInclude>
     <ClInclude Include="Include\BsGUIHelper.h">
       <Filter>Header Files\GUI</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsGUIDropDownBoxManager.h">
+      <Filter>Header Files\GUI</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsGUIElement.cpp">
@@ -377,5 +377,11 @@
     <ClCompile Include="Source\BsGUIHelper.cpp">
       <Filter>Source Files\GUI</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsGUIDropDownBoxManager.cpp">
+      <Filter>Source Files\GUI</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsGUIContextMenu.cpp">
+      <Filter>Source Files\GUI</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 11 - 1
BansheeEngine/Include/BsGUIContextMenu.h

@@ -7,6 +7,16 @@ namespace BansheeEngine
 {
 	class BS_EXPORT GUIContextMenu : public GUIMenu
 	{
-		
+	public:
+		GUIContextMenu();
+		~GUIContextMenu();
+
+		void open(const CM::Int2& position, GUIWidget& widget);
+
+	private:
+		bool mContextMenuOpen;
+
+		void close();
+		void onMenuClosed();
 	};
 }

+ 2 - 0
BansheeEngine/Include/BsGUIDropDownBox.h

@@ -1,3 +1,5 @@
+#pragma once
+
 #include "BsPrerequisites.h"
 #include "BsGUIWidget.h"
 #include "CmInt2.h"

+ 23 - 0
BansheeEngine/Include/BsGUIDropDownBoxManager.h

@@ -0,0 +1,23 @@
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "BsGUIDropDownBox.h"
+#include "CmModule.h"
+
+namespace BansheeEngine
+{
+	class BS_EXPORT GUIDropDownBoxManager : public CM::Module<GUIDropDownBoxManager>
+	{
+	public:
+		~GUIDropDownBoxManager();
+
+		CM::GameObjectHandle<GUIDropDownBox> openDropDownBox(CM::Viewport* target, CM::RenderWindow* window, const GUIDropDownAreaPlacement& placement,
+			const CM::Vector<GUIDropDownData>::type& elements, const GUISkin& skin, GUIDropDownType type, std::function<void()> onClosedCallback);
+		void closeDropDownBox();
+
+	private:
+		CM::HSceneObject mDropDownSO;
+		CM::GameObjectHandle<GUIDropDownBox> mDropDownBox;
+		std::function<void()> mOnClosedCallback;
+	};
+}

+ 1 - 1
BansheeEngine/Include/BsGUIElement.h

@@ -112,7 +112,7 @@ namespace BansheeEngine
 		virtual bool _isInBounds(const CM::Int2 position) const;
 		bool _acceptsKeyboardFocus() const { return mAcceptsKeyboardFocus; }
 
-		virtual const GUIContextMenu* getContextMenu() const { return nullptr; }
+		virtual GUIContextMenu* getContextMenu() const { return nullptr; }
 
 		/**
 		 * @brief	Returns a clip rectangle relative to the element, used for offsetting

+ 1 - 1
BansheeEngine/Include/BsGUIInputBox.h

@@ -56,7 +56,7 @@ namespace BansheeEngine
 		virtual CM::UINT32 _getRenderElementDepth(CM::UINT32 renderElementIdx) const;
 		virtual void _setFocus(bool focus);
 
-		virtual const GUIContextMenu* getContextMenu() const;
+		virtual GUIContextMenu* getContextMenu() const;
 	private:
 		// Sprites
 		ImageSprite* mImageSprite;

+ 6 - 0
BansheeEngine/Include/BsGUIListBox.h

@@ -62,6 +62,7 @@ namespace BansheeEngine
 
 		IMAGE_SPRITE_DESC mImageDesc;
 		CM::Vector<CM::WString>::type mElements;
+		bool mIsListBoxOpen;
 
 		GUIListBox(GUIWidget& parent, const GUIElementStyle* style, const CM::Vector<CM::WString>::type& elements, const GUILayoutOptions& layoutOptions);
 
@@ -69,5 +70,10 @@ namespace BansheeEngine
 		TEXT_SPRITE_DESC getTextDesc() const;
 
 		void elementSelected(CM::UINT32 idx);
+
+		void openListBox();
+		void closeListBox();
+
+		void onListBoxClosed();
 	};
 }

+ 10 - 22
BansheeEngine/Include/BsGUIManager.h

@@ -48,11 +48,6 @@ namespace BansheeEngine
 		void update();
 		void render(CM::ViewportPtr& target, CM::RenderQueue& renderQueue) const;
 
-		void openDropDownListBox(const GUIListBox* parentList, const CM::Vector<CM::WString>::type& elements, 
-			std::function<void(CM::UINT32)> selectedCallback, const GUISkin& skin);
-
-		void openMenuBarMenu(GUIButton* parentButton, const GUIMenu* menu);
-
 		void queueForDestroy(GUIElement* element);
 
 		void setCaretColor(const CM::Color& color) { mCaretColor = color; updateCaretTexture(); }
@@ -65,11 +60,14 @@ namespace BansheeEngine
 		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.
+		 * @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.
+		 *
+		 * @param	onOutsideClickCallback	Callback that gets triggered when the user
+		 * 									presses a mouse button outside of the selective input area.
 		 */
-		void enableSelectiveInput();
+		void enableSelectiveInput(std::function<void()> onOutsideClickCallback);
 
 		/**
 		 * @brief	Disables selective input and clears any selective input elements.
@@ -106,6 +104,7 @@ namespace BansheeEngine
 		GUIElement* mActiveElement;
 		GUIMouseButton mActiveMouseButton;
 
+
 		// Element and widget that currently have the keyboard focus
 		GUIWidget* mKeyboardFocusWidget;
 		GUIElement* mKeyboardFocusElement;
@@ -129,16 +128,10 @@ namespace BansheeEngine
 		SpriteTexturePtr mTextSelectionTexture;
 		CM::Color mTextSelectionColor;
 
-		// Drop down box
-		bool mDropDownBoxOpenScheduled;
-		bool mDropDownBoxActive;
-		CM::HSceneObject mDropDownSO;
-		CM::GameObjectHandle<GUIDropDownBox> mDropDownBox;
-		std::function<void(CM::UINT32)> mListBoxSelectionMade;
-
 		// Selective input
 		bool mSelectiveInputActive;
 		CM::Map<const GUIWidget*, SelectiveInputData>::type mSelectiveInputData;
+		std::function<void()> mOnOutsideClickCallback;
 
 		boost::signals::connection mOnButtonDownConn;
 		boost::signals::connection mOnButtonUpConn;
@@ -172,12 +165,7 @@ namespace BansheeEngine
 
 		void onMouseLeftWindow(CM::RenderWindow* win);
 
-		bool handleMouseOver(GUIWidget* widget, GUIElement* element, const CM::Int2& screenMousePos, float wheelScrollAmount = 0.0f);
-
-		void openContextMenu(const GUIContextMenu* menu, const CM::Int2& position, GUIWidget& widget);
-
-		void closeDropDownListBox(CM::INT32 selectedIdx);
-		void closeDropDownBox();
+		bool handleMouseOver(GUIWidget* widget, GUIElement* element, const CM::Int2& screenMousePos, float wheelScrollAmount = 0.0f);;
 
 		GUIMouseButton buttonToMouseButton(CM::ButtonCode code) const;
 		CM::Int2 getWidgetRelativePos(const GUIWidget& widget, const CM::Int2& screenPos) const;

+ 40 - 0
BansheeEngine/Source/BsGUIContextMenu.cpp

@@ -1,8 +1,48 @@
 #include "BsGUIContextMenu.h"
+#include "BsGUIDropDownBoxManager.h"
+#include "BsGUIManager.h"
 
 using namespace CamelotFramework;
 
 namespace BansheeEngine
 {
+	GUIContextMenu::GUIContextMenu()
+		:GUIMenu(), mContextMenuOpen(false)
+	{
 
+	}
+
+	GUIContextMenu::~GUIContextMenu()
+	{
+		close();
+	}
+
+	void GUIContextMenu::open(const Int2& position, GUIWidget& widget)
+	{
+		GUIDropDownAreaPlacement placement = GUIDropDownAreaPlacement::aroundPosition(position);
+
+		GameObjectHandle<GUIDropDownBox> dropDownBox = GUIDropDownBoxManager::instance().openDropDownBox(widget.getTarget(), widget.getOwnerWindow(), 
+			placement, getDropDownData(), widget.getSkin(), GUIDropDownType::MenuBar, boost::bind(&GUIContextMenu::onMenuClosed, this));
+
+		GUIManager::instance().enableSelectiveInput(boost::bind(&GUIContextMenu::close, this));
+		GUIManager::instance().addSelectiveInputWidget(dropDownBox.get());
+
+		mContextMenuOpen = true;
+	}
+
+	void GUIContextMenu::close()
+	{
+		if(mContextMenuOpen)
+		{
+			GUIDropDownBoxManager::instance().closeDropDownBox();
+			GUIManager::instance().disableSelectiveInput();
+			mContextMenuOpen = false;
+		}
+	}
+
+	void GUIContextMenu::onMenuClosed()
+	{
+		GUIManager::instance().disableSelectiveInput();
+		mContextMenuOpen = false;
+	}
 }

+ 40 - 0
BansheeEngine/Source/BsGUIDropDownBoxManager.cpp

@@ -0,0 +1,40 @@
+#include "BsGUIDropDownBoxManager.h"
+#include "CmSceneObject.h"
+
+using namespace CamelotFramework;
+
+namespace BansheeEngine
+{
+	GUIDropDownBoxManager::~GUIDropDownBoxManager()
+	{
+		closeDropDownBox();
+	}
+
+	CM::GameObjectHandle<GUIDropDownBox> GUIDropDownBoxManager::openDropDownBox(CM::Viewport* target, CM::RenderWindow* window, const GUIDropDownAreaPlacement& placement,
+		const CM::Vector<GUIDropDownData>::type& elements, const GUISkin& skin, GUIDropDownType type, std::function<void()> onClosedCallback)
+	{
+		closeDropDownBox();
+
+		mDropDownSO = SceneObject::create("DropDownBox");
+		mDropDownBox = mDropDownSO->addComponent<GUIDropDownBox>();
+		mOnClosedCallback = onClosedCallback;
+
+		mDropDownBox->initialize(target, window, placement, elements, skin, type);
+
+		return mDropDownBox;
+	}
+
+	void GUIDropDownBoxManager::closeDropDownBox()
+	{
+		if(mDropDownSO != nullptr)
+		{
+			mDropDownSO->destroy();
+			mDropDownSO = nullptr;
+
+			if(mOnClosedCallback != nullptr)
+				mOnClosedCallback();
+
+			mOnClosedCallback = nullptr;
+		}
+	}
+}

+ 1 - 1
BansheeEngine/Source/BsGUIInputBox.cpp

@@ -836,7 +836,7 @@ namespace BansheeEngine
 		mHasFocus = focus;
 	}
 
-	const GUIContextMenu* GUIInputBox::getContextMenu() const
+	GUIContextMenu* GUIInputBox::getContextMenu() const
 	{
 		static bool initialized = false;
 		static GUIContextMenu mContextMenu;

+ 48 - 3
BansheeEngine/Source/BsGUIListBox.cpp

@@ -8,6 +8,7 @@
 #include "BsGUIMouseEvent.h"
 #include "BsGUIManager.h"
 #include "BsGUIHelper.h"
+#include "BsGUIDropDownBoxManager.h"
 #include "CmTexture.h"
 
 using namespace CamelotFramework;
@@ -21,7 +22,7 @@ namespace BansheeEngine
 	}
 
 	GUIListBox::GUIListBox(GUIWidget& parent, const GUIElementStyle* style, const Vector<WString>::type& elements, const GUILayoutOptions& layoutOptions)
-		:GUIElement(parent, style, layoutOptions), mElements(elements), mNumImageRenderElements(0), mSelectedIdx(0)
+		:GUIElement(parent, style, layoutOptions), mElements(elements), mNumImageRenderElements(0), mSelectedIdx(0), mIsListBoxOpen(false)
 	{
 		mImageSprite = cm_new<ImageSprite, PoolAlloc>();
 		mTextSprite = cm_new<TextSprite, PoolAlloc>();
@@ -44,6 +45,8 @@ namespace BansheeEngine
 	{
 		cm_delete<PoolAlloc>(mTextSprite);
 		cm_delete<PoolAlloc>(mImageSprite);
+
+		closeListBox();
 	}
 
 	GUIListBox* GUIListBox::create(GUIWidget& parent, const Vector<WString>::type& elements, const GUIElementStyle* style)
@@ -178,8 +181,7 @@ namespace BansheeEngine
 			mImageDesc.texture = mStyle->active.texture;
 			markContentAsDirty();
 
-			GUIManager::instance().openDropDownListBox(this, mElements, boost::bind(&GUIListBox::elementSelected, this, _1), _getParentWidget().getSkin());
-			GUIManager::instance().addSelectiveInputElement(this);
+			openListBox();
 
 			return true;
 		}
@@ -201,9 +203,52 @@ namespace BansheeEngine
 
 		mSelectedIdx = idx;
 
+		closeListBox();
+
 		markContentAsDirty();
 	}
 
+	void GUIListBox::openListBox()
+	{
+		closeListBox();
+
+		Vector<GUIDropDownData>::type dropDownData;
+		UINT32 i = 0;
+		for(auto& elem : mElements)
+		{
+			dropDownData.push_back(GUIDropDownData::button(elem, boost::bind(&GUIListBox::elementSelected, this, i)));
+			i++;
+		}
+
+		GUIWidget& widget = _getParentWidget();
+		GUIDropDownAreaPlacement placement = GUIDropDownAreaPlacement::aroundBoundsHorz(getBounds());
+
+		GameObjectHandle<GUIDropDownBox> dropDownBox = GUIDropDownBoxManager::instance().openDropDownBox(widget.getTarget(), widget.getOwnerWindow(), 
+			placement, dropDownData, widget.getSkin(), GUIDropDownType::MenuBar, boost::bind(&GUIListBox::onListBoxClosed, this));
+
+		GUIManager::instance().enableSelectiveInput(boost::bind(&GUIListBox::closeListBox, this));
+		GUIManager::instance().addSelectiveInputWidget(dropDownBox.get());
+		GUIManager::instance().addSelectiveInputElement(this);
+
+		mIsListBoxOpen = true;
+	}
+
+	void GUIListBox::closeListBox()
+	{
+		if(mIsListBoxOpen)
+		{
+			GUIDropDownBoxManager::instance().closeDropDownBox();
+			GUIManager::instance().disableSelectiveInput();
+			mIsListBoxOpen = false;
+		}
+	}
+
+	void GUIListBox::onListBoxClosed()
+	{
+		GUIManager::instance().disableSelectiveInput();
+		mIsListBoxOpen = false;
+	}
+
 	TEXT_SPRITE_DESC GUIListBox::getTextDesc() const
 	{
 		TEXT_SPRITE_DESC textDesc;

+ 17 - 108
BansheeEngine/Source/BsGUIManager.cpp

@@ -25,6 +25,8 @@
 #include "BsGUIDropDownBox.h"
 #include "BsGUIContextMenu.h"
 #include "BsDragAndDropManager.h"
+#include "BsGUIDropDownBoxManager.h"
+#include "BsGUIContextMenu.h"
 
 using namespace CamelotFramework;
 namespace BansheeEngine
@@ -55,8 +57,7 @@ namespace BansheeEngine
 		:mMouseOverElement(nullptr), mMouseOverWidget(nullptr), mSeparateMeshesByWidget(true), mActiveElement(nullptr), 
 		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),
-		mSelectiveInputActive(false)
+		mTextSelectionColor(1.0f, 0.6588f, 0.0f), mInputCaret(nullptr), mInputSelection(nullptr), mSelectiveInputActive(false)
 	{
 		mOnButtonDownConn = gInput().onButtonDown.connect(boost::bind(&GUIManager::onButtonDown, this, _1));
 		mOnButtonUpConn = gInput().onButtonUp.connect(boost::bind(&GUIManager::onButtonUp, this, _1));
@@ -75,6 +76,8 @@ namespace BansheeEngine
 		DragAndDropManager::startUp(cm_new<DragAndDropManager>());
 		mMouseDragEndedConn = DragAndDropManager::instance().onDragEnded.connect(boost::bind(&GUIManager::onMouseDragEnded, this, _1));
 
+		GUIDropDownBoxManager::startUp(cm_new<GUIDropDownBoxManager>());
+
 		// Need to defer this call because I want to make sure all managers are initialized first
 		deferredCall(std::bind(&GUIManager::updateCaretTexture, this));
 		deferredCall(std::bind(&GUIManager::updateTextSelectionTexture, this));
@@ -82,6 +85,7 @@ namespace BansheeEngine
 
 	GUIManager::~GUIManager()
 	{
+		GUIDropDownBoxManager::shutDown();
 		DragAndDropManager::shutDown();
 
 		// Make a copy of widgets, since destroying them will remove them from mWidgets and
@@ -167,14 +171,6 @@ namespace BansheeEngine
 	{
 		DragAndDropManager::instance().update();
 
-		// We only activate the drop down box the next frame so it isn't
-		// accidentally destroyed the same frame it was created
-		if(mDropDownBoxOpenScheduled)
-		{
-			mDropDownBoxActive = true;
-			mDropDownBoxOpenScheduled = false;
-		}
-
 		// Update layouts
 		for(auto& widgetInfo : mWidgets)
 		{
@@ -505,90 +501,6 @@ namespace BansheeEngine
 		}
 	}
 
-	void GUIManager::openDropDownListBox(const GUIListBox* parentList, const CM::Vector<WString>::type& elements, 
-		std::function<void(CM::UINT32)> selectedCallback, const GUISkin& skin)
-	{
-		if(mDropDownBoxOpenScheduled || mDropDownBoxActive)
-			closeDropDownListBox(-1);
-
-		mDropDownSO = SceneObject::create("DropDownBox");
-		mDropDownBox = mDropDownSO->addComponent<GUIDropDownBox>();
-
-		enableSelectiveInput();
-		addSelectiveInputWidget(mDropDownBox.get());
-
-		GUIWidget& widget = parentList->_getParentWidget();
-
-		Vector<GUIDropDownData>::type dropDownData;
-		UINT32 i = 0;
-		for(auto& elem : elements)
-		{
-			dropDownData.push_back(GUIDropDownData::button(elem, boost::bind(&GUIManager::closeDropDownListBox, this, i)));
-			i++;
-		}
-
-		mDropDownBox->initialize(widget.getTarget(), widget.getOwnerWindow(), 
-			GUIDropDownAreaPlacement::aroundBoundsHorz(parentList->getBounds()), dropDownData, skin, GUIDropDownType::ListBox);
-
-		mDropDownBoxOpenScheduled = true;
-		mListBoxSelectionMade = selectedCallback;
-	}
-
-	void GUIManager::closeDropDownListBox(INT32 selectedIdx)
-	{
-		if(selectedIdx != -1)
-			mListBoxSelectionMade(selectedIdx);
-
-		closeDropDownBox();
-	}
-
-	void GUIManager::closeDropDownBox()
-	{
-		disableSelectiveInput();
-		mDropDownSO->destroy();
-
-		mDropDownBoxActive = false;
-	}
-
-	void GUIManager::openMenuBarMenu(GUIButton* parentButton, const GUIMenu* menu)
-	{
-		if(mDropDownBoxOpenScheduled || mDropDownBoxActive)
-			closeDropDownBox();
-
-		mDropDownSO = SceneObject::create("DropDownBox");
-		mDropDownBox = mDropDownSO->addComponent<GUIDropDownBox>();
-
-		enableSelectiveInput();
-		addSelectiveInputWidget(mDropDownBox.get());
-
-		Vector<GUIDropDownData>::type dropDownData = menu->getDropDownData();
-		GUIWidget& widget = parentButton->_getParentWidget();
-
-		mDropDownBox->initialize(widget.getTarget(), widget.getOwnerWindow(), 
-			GUIDropDownAreaPlacement::aroundBoundsHorz(parentButton->getBounds()), dropDownData, widget.getSkin(), GUIDropDownType::MenuBar);
-
-		mDropDownBoxOpenScheduled = true;
-	}
-
-	void GUIManager::openContextMenu(const GUIContextMenu* menu, const Int2& position, GUIWidget& widget)
-	{
-		if(mDropDownBoxOpenScheduled || mDropDownBoxActive)
-			closeDropDownBox();
-
-		mDropDownSO = SceneObject::create("DropDownBox");
-		mDropDownBox = mDropDownSO->addComponent<GUIDropDownBox>();
-
-		enableSelectiveInput();
-		addSelectiveInputWidget(mDropDownBox.get());
-
-		Vector<GUIDropDownData>::type dropDownData = menu->getDropDownData();
-
-		mDropDownBox->initialize(widget.getTarget(), widget.getOwnerWindow(), 
-			GUIDropDownAreaPlacement::aroundPosition(position), dropDownData, widget.getSkin(), GUIDropDownType::ContextMenu);
-
-		mDropDownBoxOpenScheduled = true;
-	}
-
 	void GUIManager::updateCaretTexture()
 	{
 		if(mCaretTexture == nullptr)
@@ -657,18 +569,11 @@ namespace BansheeEngine
 
 			GUIMouseButton guiButton = buttonToMouseButton(event.buttonCode);
 
-			// Close drop down box(es) if user clicks outside of one
-			if(mDropDownBoxActive)
+			// Send out selective input callback when user clicks on non-selectable element
+			if(mSelectiveInputActive && mMouseOverElement == nullptr)
 			{
-				bool clickedOnDropDownBox = false;
-
-				if(mMouseOverElement != nullptr && (&mMouseOverElement->_getParentWidget() == mDropDownBox.get()))
-					clickedOnDropDownBox = true;
-
-				if(!clickedOnDropDownBox)
-				{
-					closeDropDownBox();
-				}
+				if(mOnOutsideClickCallback != nullptr)
+					mOnOutsideClickCallback();
 			}
 
 			// We only check for mouse down if mouse isn't already being held down, and we are hovering over an element
@@ -708,11 +613,11 @@ namespace BansheeEngine
 			// If right click try to open context menu
 			if(mMouseOverElement != nullptr && buttonStates[2] == true) 
 			{
-				const GUIContextMenu* menu = mMouseOverElement->getContextMenu();
+				GUIContextMenu* menu = mMouseOverElement->getContextMenu();
 
 				if(menu != nullptr)
 				{
-					openContextMenu(menu, gInput().getMousePosition(), *mMouseOverWidget);
+					menu->open(gInput().getMousePosition(), *mMouseOverWidget);
 					event.markAsUsed();
 				}
 			}
@@ -1084,15 +989,19 @@ namespace BansheeEngine
 		}
 	}
 
-	void GUIManager::enableSelectiveInput()
+	// HACK - This callback is very hackish and very specific. Attempt to replace it with a more
+	// general purpose solution
+	void GUIManager::enableSelectiveInput(std::function<void()> onOutsideClickCallback)
 	{
 		mSelectiveInputActive = true;
+		mOnOutsideClickCallback = onOutsideClickCallback;
 	}
 
 	void GUIManager::disableSelectiveInput()
 	{
 		mSelectiveInputData.clear();
 		mSelectiveInputActive = false;
+		mOnOutsideClickCallback = nullptr;
 	}
 
 	void GUIManager::addSelectiveInputWidget(const GUIWidget* widget)

+ 5 - 0
CamelotClient/Include/BsGUIMenuBar.h

@@ -32,6 +32,8 @@ namespace BansheeEditor
 
 		CM::Vector<GUIMenuBarData>::type mChildMenus;
 
+		bool mSubMenuOpen;
+
 		const GUIMenuBarData* getSubMenu(const CM::WString& name) const;
 
 		/**
@@ -44,5 +46,8 @@ namespace BansheeEditor
 		bool stripPath(CM::WString& path, CM::WString& pathRoot) const;
 
 		void openSubMenu(const CM::WString& name);
+		void closeSubMenu();
+
+		void onSubMenuClosed();
 	};
 }

+ 37 - 2
CamelotClient/Source/BsGUIMenuBar.cpp

@@ -8,6 +8,8 @@
 #include "BsGUIMenu.h"
 #include "BsGUIManager.h"
 #include "BsEngineGUI.h"
+#include "BsGUIDropDownBoxManager.h"
+#include "CmSceneObject.h"
 
 using namespace CamelotFramework;
 using namespace BansheeEngine;
@@ -15,7 +17,7 @@ using namespace BansheeEngine;
 namespace BansheeEditor
 {
 	GUIMenuBar::GUIMenuBar(BS::GUIWidget* parent)
-		:mParentWidget(parent), mMainArea(nullptr), mBackgroundArea(nullptr), mBgTexture(nullptr), mLogoTexture(nullptr)
+		:mParentWidget(parent), mMainArea(nullptr), mBackgroundArea(nullptr), mBgTexture(nullptr), mLogoTexture(nullptr), mSubMenuOpen(false)
 	{
 		mBackgroundArea = GUIArea::create(*parent, 0, 0, 1, 13, 9900);
 		mMainArea = GUIArea::create(*parent, 0, 0, 1, 13, 9899);
@@ -31,6 +33,8 @@ namespace BansheeEditor
 
 	GUIMenuBar::~GUIMenuBar()
 	{
+		closeSubMenu();
+
 		for(auto& menu : mChildMenus)
 		{
 			cm_delete<PoolAlloc>(menu.menu);
@@ -204,11 +208,42 @@ namespace BansheeEditor
 		if(subMenu == nullptr)
 			return;
 
-		GUIManager::instance().openMenuBarMenu(subMenu->button, subMenu->menu);
+		closeSubMenu();
+
+		Vector<GUIDropDownData>::type dropDownData = subMenu->menu->getDropDownData();
+		GUIWidget& widget = subMenu->button->_getParentWidget();
+
+		GUIDropDownAreaPlacement placement = GUIDropDownAreaPlacement::aroundBoundsHorz(subMenu->button->getBounds());
+
+		GameObjectHandle<GUIDropDownBox> dropDownBox = GUIDropDownBoxManager::instance().openDropDownBox(widget.getTarget(), widget.getOwnerWindow(), 
+			placement, dropDownData, widget.getSkin(), GUIDropDownType::MenuBar, boost::bind(&GUIMenuBar::onSubMenuClosed, this));
+
+		GUIManager::instance().enableSelectiveInput(boost::bind(&GUIMenuBar::closeSubMenu, this));
+		GUIManager::instance().addSelectiveInputWidget(dropDownBox.get());
 
 		for(auto& childMenu : mChildMenus)
 		{
 			GUIManager::instance().addSelectiveInputElement(childMenu.button);
 		}
+
+		mSubMenuOpen = true;
+	}
+
+	void GUIMenuBar::closeSubMenu()
+	{
+		if(mSubMenuOpen)
+		{
+			GUIDropDownBoxManager::instance().closeDropDownBox();
+			GUIManager::instance().disableSelectiveInput();
+			
+			mSubMenuOpen = false;
+		}		
+	}
+
+	void GUIMenuBar::onSubMenuClosed()
+	{
+		GUIManager::instance().disableSelectiveInput();
+
+		mSubMenuOpen = false;
 	}
 }

+ 8 - 0
CamelotCore/Include/CmGameObjectHandle.h

@@ -85,6 +85,14 @@ namespace CamelotFramework
 			mData = ptr.getHandleData();
 		}
 
+		inline GameObjectHandle<T>& operator=(std::nullptr_t ptr)
+		{ 	
+			mData = cm_shared_ptr<GameObjectHandleData, PoolAlloc>();
+			mData->mPtr = nullptr;
+
+			return *this;
+		}
+
 		T* get() const 
 		{ 
 			throwIfDestroyed();

+ 9 - 5
DropDown.txt

@@ -2,15 +2,19 @@ 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
 
+Attempt to get rid of selective input click callback. It's too specific.
+ - Instead of using selective input I might be able to block all input using event filter (by adding return values to them)
+   - Some kind of GUISelectiveInput class? I think it makes sense to move it out of GUIManager
+
+I will need a GUIDropDownBoxManager because otherwise I have no way of ensuring only one drop down box is active at a time
+ Principle remains the same, I call GUIDropDownBox::open and internally it calls the manager
+
 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
+
+ListBox: Clicking on ListBox once the DropDownBox is open should close the drop down box (I assume it does, it just re-opens it immediately again?)
 
 Add min/max/close buttons to GUIMenuBar
 Setup move areas in GUIMenuBar
 
-ListBox: Clicking on ListBox once the DropDownBox is open should close the drop down box (I assume it does, it just re-opens it immediately again?)
-
 Test scrollUp/scrollDown