Selaa lähdekoodia

Moved drop box methods out from GUIManager into their individual classes

Marko Pintera 12 vuotta sitten
vanhempi
sitoutus
98c3aa7686

+ 3 - 1
BansheeEngine/BansheeEngine.vcxproj

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

+ 9 - 3
BansheeEngine/BansheeEngine.vcxproj.filters

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

+ 11 - 1
BansheeEngine/Include/BsGUIContextMenu.h

@@ -7,6 +7,16 @@ namespace BansheeEngine
 {
 {
 	class BS_EXPORT GUIContextMenu : public GUIMenu
 	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 "BsPrerequisites.h"
 #include "BsGUIWidget.h"
 #include "BsGUIWidget.h"
 #include "CmInt2.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;
 		virtual bool _isInBounds(const CM::Int2 position) const;
 		bool _acceptsKeyboardFocus() const { return mAcceptsKeyboardFocus; }
 		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
 		 * @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 CM::UINT32 _getRenderElementDepth(CM::UINT32 renderElementIdx) const;
 		virtual void _setFocus(bool focus);
 		virtual void _setFocus(bool focus);
 
 
-		virtual const GUIContextMenu* getContextMenu() const;
+		virtual GUIContextMenu* getContextMenu() const;
 	private:
 	private:
 		// Sprites
 		// Sprites
 		ImageSprite* mImageSprite;
 		ImageSprite* mImageSprite;

+ 6 - 0
BansheeEngine/Include/BsGUIListBox.h

@@ -62,6 +62,7 @@ namespace BansheeEngine
 
 
 		IMAGE_SPRITE_DESC mImageDesc;
 		IMAGE_SPRITE_DESC mImageDesc;
 		CM::Vector<CM::WString>::type mElements;
 		CM::Vector<CM::WString>::type mElements;
+		bool mIsListBoxOpen;
 
 
 		GUIListBox(GUIWidget& parent, const GUIElementStyle* style, const CM::Vector<CM::WString>::type& elements, const GUILayoutOptions& layoutOptions);
 		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;
 		TEXT_SPRITE_DESC getTextDesc() const;
 
 
 		void elementSelected(CM::UINT32 idx);
 		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 update();
 		void render(CM::ViewportPtr& target, CM::RenderQueue& renderQueue) const;
 		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 queueForDestroy(GUIElement* element);
 
 
 		void setCaretColor(const CM::Color& color) { mCaretColor = color; updateCaretTexture(); }
 		void setCaretColor(const CM::Color& color) { mCaretColor = color; updateCaretTexture(); }
@@ -65,11 +60,14 @@ namespace BansheeEngine
 		GUIInputSelection* getInputSelectionTool() const { return mInputSelection; }
 		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.
 		 * @brief	Disables selective input and clears any selective input elements.
@@ -106,6 +104,7 @@ namespace BansheeEngine
 		GUIElement* mActiveElement;
 		GUIElement* mActiveElement;
 		GUIMouseButton mActiveMouseButton;
 		GUIMouseButton mActiveMouseButton;
 
 
+
 		// Element and widget that currently have the keyboard focus
 		// Element and widget that currently have the keyboard focus
 		GUIWidget* mKeyboardFocusWidget;
 		GUIWidget* mKeyboardFocusWidget;
 		GUIElement* mKeyboardFocusElement;
 		GUIElement* mKeyboardFocusElement;
@@ -129,16 +128,10 @@ namespace BansheeEngine
 		SpriteTexturePtr mTextSelectionTexture;
 		SpriteTexturePtr mTextSelectionTexture;
 		CM::Color mTextSelectionColor;
 		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
 		// Selective input
 		bool mSelectiveInputActive;
 		bool mSelectiveInputActive;
 		CM::Map<const GUIWidget*, SelectiveInputData>::type mSelectiveInputData;
 		CM::Map<const GUIWidget*, SelectiveInputData>::type mSelectiveInputData;
+		std::function<void()> mOnOutsideClickCallback;
 
 
 		boost::signals::connection mOnButtonDownConn;
 		boost::signals::connection mOnButtonDownConn;
 		boost::signals::connection mOnButtonUpConn;
 		boost::signals::connection mOnButtonUpConn;
@@ -172,12 +165,7 @@ 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);
-
-		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;
 		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;

+ 40 - 0
BansheeEngine/Source/BsGUIContextMenu.cpp

@@ -1,8 +1,48 @@
 #include "BsGUIContextMenu.h"
 #include "BsGUIContextMenu.h"
+#include "BsGUIDropDownBoxManager.h"
+#include "BsGUIManager.h"
 
 
 using namespace CamelotFramework;
 using namespace CamelotFramework;
 
 
 namespace BansheeEngine
 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;
 		mHasFocus = focus;
 	}
 	}
 
 
-	const GUIContextMenu* GUIInputBox::getContextMenu() const
+	GUIContextMenu* GUIInputBox::getContextMenu() const
 	{
 	{
 		static bool initialized = false;
 		static bool initialized = false;
 		static GUIContextMenu mContextMenu;
 		static GUIContextMenu mContextMenu;

+ 48 - 3
BansheeEngine/Source/BsGUIListBox.cpp

@@ -8,6 +8,7 @@
 #include "BsGUIMouseEvent.h"
 #include "BsGUIMouseEvent.h"
 #include "BsGUIManager.h"
 #include "BsGUIManager.h"
 #include "BsGUIHelper.h"
 #include "BsGUIHelper.h"
+#include "BsGUIDropDownBoxManager.h"
 #include "CmTexture.h"
 #include "CmTexture.h"
 
 
 using namespace CamelotFramework;
 using namespace CamelotFramework;
@@ -21,7 +22,7 @@ namespace BansheeEngine
 	}
 	}
 
 
 	GUIListBox::GUIListBox(GUIWidget& parent, const GUIElementStyle* style, const Vector<WString>::type& elements, const GUILayoutOptions& layoutOptions)
 	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>();
 		mImageSprite = cm_new<ImageSprite, PoolAlloc>();
 		mTextSprite = cm_new<TextSprite, PoolAlloc>();
 		mTextSprite = cm_new<TextSprite, PoolAlloc>();
@@ -44,6 +45,8 @@ namespace BansheeEngine
 	{
 	{
 		cm_delete<PoolAlloc>(mTextSprite);
 		cm_delete<PoolAlloc>(mTextSprite);
 		cm_delete<PoolAlloc>(mImageSprite);
 		cm_delete<PoolAlloc>(mImageSprite);
+
+		closeListBox();
 	}
 	}
 
 
 	GUIListBox* GUIListBox::create(GUIWidget& parent, const Vector<WString>::type& elements, const GUIElementStyle* style)
 	GUIListBox* GUIListBox::create(GUIWidget& parent, const Vector<WString>::type& elements, const GUIElementStyle* style)
@@ -178,8 +181,7 @@ namespace BansheeEngine
 			mImageDesc.texture = mStyle->active.texture;
 			mImageDesc.texture = mStyle->active.texture;
 			markContentAsDirty();
 			markContentAsDirty();
 
 
-			GUIManager::instance().openDropDownListBox(this, mElements, boost::bind(&GUIListBox::elementSelected, this, _1), _getParentWidget().getSkin());
-			GUIManager::instance().addSelectiveInputElement(this);
+			openListBox();
 
 
 			return true;
 			return true;
 		}
 		}
@@ -201,9 +203,52 @@ namespace BansheeEngine
 
 
 		mSelectedIdx = idx;
 		mSelectedIdx = idx;
 
 
+		closeListBox();
+
 		markContentAsDirty();
 		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 GUIListBox::getTextDesc() const
 	{
 	{
 		TEXT_SPRITE_DESC textDesc;
 		TEXT_SPRITE_DESC textDesc;

+ 17 - 108
BansheeEngine/Source/BsGUIManager.cpp

@@ -25,6 +25,8 @@
 #include "BsGUIDropDownBox.h"
 #include "BsGUIDropDownBox.h"
 #include "BsGUIContextMenu.h"
 #include "BsGUIContextMenu.h"
 #include "BsDragAndDropManager.h"
 #include "BsDragAndDropManager.h"
+#include "BsGUIDropDownBoxManager.h"
+#include "BsGUIContextMenu.h"
 
 
 using namespace CamelotFramework;
 using namespace CamelotFramework;
 namespace BansheeEngine
 namespace BansheeEngine
@@ -55,8 +57,7 @@ namespace BansheeEngine
 		:mMouseOverElement(nullptr), mMouseOverWidget(nullptr), mSeparateMeshesByWidget(true), mActiveElement(nullptr), 
 		:mMouseOverElement(nullptr), mMouseOverWidget(nullptr), mSeparateMeshesByWidget(true), mActiveElement(nullptr), 
 		mActiveWidget(nullptr), mActiveMouseButton(GUIMouseButton::Left), mKeyboardFocusElement(nullptr), mKeyboardFocusWidget(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),
 		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));
 		mOnButtonDownConn = gInput().onButtonDown.connect(boost::bind(&GUIManager::onButtonDown, this, _1));
 		mOnButtonUpConn = gInput().onButtonUp.connect(boost::bind(&GUIManager::onButtonUp, this, _1));
 		mOnButtonUpConn = gInput().onButtonUp.connect(boost::bind(&GUIManager::onButtonUp, this, _1));
@@ -75,6 +76,8 @@ namespace BansheeEngine
 		DragAndDropManager::startUp(cm_new<DragAndDropManager>());
 		DragAndDropManager::startUp(cm_new<DragAndDropManager>());
 		mMouseDragEndedConn = DragAndDropManager::instance().onDragEnded.connect(boost::bind(&GUIManager::onMouseDragEnded, this, _1));
 		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
 		// 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::updateCaretTexture, this));
 		deferredCall(std::bind(&GUIManager::updateTextSelectionTexture, this));
 		deferredCall(std::bind(&GUIManager::updateTextSelectionTexture, this));
@@ -82,6 +85,7 @@ namespace BansheeEngine
 
 
 	GUIManager::~GUIManager()
 	GUIManager::~GUIManager()
 	{
 	{
+		GUIDropDownBoxManager::shutDown();
 		DragAndDropManager::shutDown();
 		DragAndDropManager::shutDown();
 
 
 		// Make a copy of widgets, since destroying them will remove them from mWidgets and
 		// Make a copy of widgets, since destroying them will remove them from mWidgets and
@@ -167,14 +171,6 @@ namespace BansheeEngine
 	{
 	{
 		DragAndDropManager::instance().update();
 		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
 		// Update layouts
 		for(auto& widgetInfo : mWidgets)
 		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()
 	void GUIManager::updateCaretTexture()
 	{
 	{
 		if(mCaretTexture == nullptr)
 		if(mCaretTexture == nullptr)
@@ -657,18 +569,11 @@ namespace BansheeEngine
 
 
 			GUIMouseButton guiButton = buttonToMouseButton(event.buttonCode);
 			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
 			// 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 right click try to open context menu
 			if(mMouseOverElement != nullptr && buttonStates[2] == true) 
 			if(mMouseOverElement != nullptr && buttonStates[2] == true) 
 			{
 			{
-				const GUIContextMenu* menu = mMouseOverElement->getContextMenu();
+				GUIContextMenu* menu = mMouseOverElement->getContextMenu();
 
 
 				if(menu != nullptr)
 				if(menu != nullptr)
 				{
 				{
-					openContextMenu(menu, gInput().getMousePosition(), *mMouseOverWidget);
+					menu->open(gInput().getMousePosition(), *mMouseOverWidget);
 					event.markAsUsed();
 					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;
 		mSelectiveInputActive = true;
+		mOnOutsideClickCallback = onOutsideClickCallback;
 	}
 	}
 
 
 	void GUIManager::disableSelectiveInput()
 	void GUIManager::disableSelectiveInput()
 	{
 	{
 		mSelectiveInputData.clear();
 		mSelectiveInputData.clear();
 		mSelectiveInputActive = false;
 		mSelectiveInputActive = false;
+		mOnOutsideClickCallback = nullptr;
 	}
 	}
 
 
 	void GUIManager::addSelectiveInputWidget(const GUIWidget* widget)
 	void GUIManager::addSelectiveInputWidget(const GUIWidget* widget)

+ 5 - 0
CamelotClient/Include/BsGUIMenuBar.h

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

+ 37 - 2
CamelotClient/Source/BsGUIMenuBar.cpp

@@ -8,6 +8,8 @@
 #include "BsGUIMenu.h"
 #include "BsGUIMenu.h"
 #include "BsGUIManager.h"
 #include "BsGUIManager.h"
 #include "BsEngineGUI.h"
 #include "BsEngineGUI.h"
+#include "BsGUIDropDownBoxManager.h"
+#include "CmSceneObject.h"
 
 
 using namespace CamelotFramework;
 using namespace CamelotFramework;
 using namespace BansheeEngine;
 using namespace BansheeEngine;
@@ -15,7 +17,7 @@ using namespace BansheeEngine;
 namespace BansheeEditor
 namespace BansheeEditor
 {
 {
 	GUIMenuBar::GUIMenuBar(BS::GUIWidget* parent)
 	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);
 		mBackgroundArea = GUIArea::create(*parent, 0, 0, 1, 13, 9900);
 		mMainArea = GUIArea::create(*parent, 0, 0, 1, 13, 9899);
 		mMainArea = GUIArea::create(*parent, 0, 0, 1, 13, 9899);
@@ -31,6 +33,8 @@ namespace BansheeEditor
 
 
 	GUIMenuBar::~GUIMenuBar()
 	GUIMenuBar::~GUIMenuBar()
 	{
 	{
+		closeSubMenu();
+
 		for(auto& menu : mChildMenus)
 		for(auto& menu : mChildMenus)
 		{
 		{
 			cm_delete<PoolAlloc>(menu.menu);
 			cm_delete<PoolAlloc>(menu.menu);
@@ -204,11 +208,42 @@ namespace BansheeEditor
 		if(subMenu == nullptr)
 		if(subMenu == nullptr)
 			return;
 			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)
 		for(auto& childMenu : mChildMenus)
 		{
 		{
 			GUIManager::instance().addSelectiveInputElement(childMenu.button);
 			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();
 			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 
 		T* get() const 
 		{ 
 		{ 
 			throwIfDestroyed();
 			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
 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
 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
 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
 Add min/max/close buttons to GUIMenuBar
 Setup move areas in 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
 Test scrollUp/scrollDown