Browse Source

More work on drop down box
Removed the mouse up GUI hack that was used for window move/resize bug

Marko Pintera 12 năm trước cách đây
mục cha
commit
11042b9070

+ 4 - 0
BansheeEngine/Include/BsGUIDropDownList.h

@@ -14,6 +14,8 @@ namespace BansheeEngine
 
 		static GUIDropDownList* create(GUIWidget& parent, const CM::Vector<CM::WString>::type& elements, const GUIElementStyle* style = nullptr);
 		static GUIDropDownList* create(GUIWidget& parent, const CM::Vector<CM::WString>::type& elements, const GUILayoutOptions& layoutOptions, const GUIElementStyle* style = nullptr);
+	
+		boost::signal<void(CM::UINT32)> onSelectionChanged;
 	protected:
 		~GUIDropDownList();
 
@@ -64,5 +66,7 @@ namespace BansheeEngine
 		GUIDropDownList(GUIWidget& parent, const GUIElementStyle* style, const CM::Vector<CM::WString>::type& elements, const GUILayoutOptions& layoutOptions);
 
 		virtual bool mouseEvent(const GUIMouseEvent& ev);
+
+		void elementSelected(CM::UINT32 idx);
 	};
 }

+ 4 - 0
BansheeEngine/Include/BsGUIManager.h

@@ -103,8 +103,10 @@ namespace BansheeEngine
 		CM::Color mTextSelectionColor;
 
 		// Drop down box
+		bool mDropDownBoxActive;
 		CM::HSceneObject mDropDownSO;
 		CM::GameObjectHandle<GUIDropDownBox> mDropDownBox;
+		std::function<void(CM::UINT32)> mDropDownSelectionMade;
 
 		boost::signals::connection mOnButtonDownConn;
 		boost::signals::connection mOnButtonUpConn;
@@ -140,6 +142,8 @@ namespace BansheeEngine
 
 		bool handleMouseOver(GUIWidget* widget, GUIElement* element, const CM::Int2& screenMousePos, float wheelScrollAmount = 0.0f);
 
+		void closeDropDownBox(CM::INT32 selectedIdx);
+
 		GUIMouseButton buttonToMouseButton(CM::ButtonCode code) const;
 		CM::Int2 getWidgetRelativePos(const GUIWidget& widget, const CM::Int2& screenPos) const;
 

+ 18 - 6
BansheeEngine/Source/BsGUIDropDownBox.cpp

@@ -38,9 +38,21 @@ namespace BansheeEngine
 
 		Rect dropDownListBounds = parentList->getBounds();
 
-		// Determine x position
 		Int2 position;
-		position.x = dropDownListBounds.x;
+		// Determine x position and whether to align to left or right side of the drop down list
+		UINT32 availableRightwardWidth = (UINT32)std::max(0, (target->getLeft() + target->getWidth()) - dropDownListBounds.x);
+		UINT32 availableLeftwardWidth = (UINT32)std::max(0, (dropDownListBounds.x + dropDownListBounds.width) - target->getLeft());
+
+		//// Prefer right if possible
+		if(DROP_DOWN_BOX_WIDTH <= availableRightwardWidth)
+			position.x = dropDownListBounds.x;
+		else
+		{
+			if(availableRightwardWidth >= availableLeftwardWidth)
+				position.x = dropDownListBounds.x;
+			else
+				position.x = dropDownListBounds.x - std::min(DROP_DOWN_BOX_WIDTH, availableLeftwardWidth);
+		}
 
 		// Determine maximum width
 		UINT32 maxPossibleWidth = (UINT32)std::max(0, (target->getLeft() + target->getWidth()) - position.x);
@@ -48,10 +60,10 @@ namespace BansheeEngine
 		UINT32 contentWidth = (UINT32)std::max(0, (INT32)width - (INT32)dropDownBoxStyle->margins.left - (INT32)dropDownBoxStyle->margins.right);
 
 		// Determine y position and whether to open upward or downward
-		UINT32 scrollButtonUpHeight = EngineGUI::instance().getSkin().getStyle("DropDownScrollUpBtnBg")->fixedHeight;
-		UINT32 scrollButtonDownHeight = EngineGUI::instance().getSkin().getStyle("DropDownScrollDownBtnBg")->fixedHeight;
+		UINT32 scrollButtonUpHeight = EngineGUI::instance().getSkin().getStyle("DropDownScrollUpBtnBg")->height;
+		UINT32 scrollButtonDownHeight = EngineGUI::instance().getSkin().getStyle("DropDownScrollDownBtnBg")->height;
 		UINT32 helperElementHeight = scrollButtonUpHeight + scrollButtonDownHeight + dropDownBoxStyle->margins.top + dropDownBoxStyle->margins.bottom;
-		UINT32 elementButtonHeight = EngineGUI::instance().getSkin().getStyle("DropDownEntryBtn")->fixedHeight;
+		UINT32 elementButtonHeight = EngineGUI::instance().getSkin().getStyle("DropDownEntryBtn")->height;
 
 		UINT32 maxNeededHeight = elementButtonHeight * (UINT32)mDropDownElements.size() + helperElementHeight;
 		UINT32 availableDownwardHeight = (UINT32)std::max(0, (target->getTop() + target->getHeight()) - (dropDownListBounds.y + dropDownListBounds.height));
@@ -63,7 +75,7 @@ namespace BansheeEngine
 			position.y = dropDownListBounds.y + dropDownListBounds.height;
 		else
 		{
-			if(availableDownwardHeight > availableUpwardHeight)
+			if(availableDownwardHeight >= availableUpwardHeight)
 				position.y = dropDownListBounds.y + dropDownListBounds.height;
 			else
 				position.y = dropDownListBounds.y - std::min(maxNeededHeight, availableUpwardHeight);

+ 9 - 0
BansheeEngine/Source/BsGUIDropDownList.cpp

@@ -6,6 +6,7 @@
 #include "BsTextSprite.h"
 #include "BsGUILayoutOptions.h"
 #include "BsGUIMouseEvent.h"
+#include "BsGUIManager.h"
 #include "CmTexture.h"
 
 using namespace CamelotFramework;
@@ -191,6 +192,8 @@ namespace BansheeEngine
 			mImageDesc.texture = mStyle->active.texture;
 			markContentAsDirty();
 
+			GUIManager::instance().openDropDownBox(this, mElements, boost::bind(&GUIDropDownList::elementSelected, this, _1));
+
 			return true;
 		}
 		else if(ev.getType() == GUIMouseEventType::MouseUp)
@@ -203,4 +206,10 @@ namespace BansheeEngine
 
 		return false;
 	}
+
+	void GUIDropDownList::elementSelected(CM::UINT32 idx)
+	{
+		if(!onSelectionChanged.empty())
+			onSelectionChanged(idx);
+	}
 }

+ 25 - 12
BansheeEngine/Source/BsGUIManager.cpp

@@ -53,7 +53,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)
+		mTextSelectionColor(1.0f, 0.6588f, 0.0f), mInputCaret(nullptr), mInputSelection(nullptr), mDropDownBoxActive(false)
 	{
 		mOnButtonDownConn = gInput().onButtonDown.connect(boost::bind(&GUIManager::onButtonDown, this, _1));
 		mOnButtonUpConn = gInput().onButtonUp.connect(boost::bind(&GUIManager::onButtonUp, this, _1));
@@ -501,7 +501,21 @@ namespace BansheeEngine
 		mDropDownBox = mDropDownSO->addComponent<GUIDropDownBox>();
 
 		GUIWidget& widget = parentList->_getParentWidget();
-		mDropDownBox->initialize(widget.getTarget(), widget.getOwnerWindow(), parentList, elements, selectedCallback);
+		mDropDownBox->initialize(widget.getTarget(), widget.getOwnerWindow(), parentList, elements, 
+			boost::bind(&GUIManager::closeDropDownBox, this, _1));
+
+		mDropDownBoxActive = true;
+		mDropDownSelectionMade = selectedCallback;
+	}
+
+	void GUIManager::closeDropDownBox(INT32 selectedIdx)
+	{
+		if(selectedIdx != -1)
+			mDropDownSelectionMade(selectedIdx);
+
+		mDropDownBoxActive = false;
+
+		mDropDownSO->destroy();
 	}
 
 	void GUIManager::updateCaretTexture()
@@ -572,16 +586,6 @@ namespace BansheeEngine
 
 			GUIMouseButton guiButton = buttonToMouseButton(event.buttonCode);
 
-			// HACK: This should never happen, as MouseUp was meant to happen before another MouseDown,
-			// and MouseUp will clear the active element. HOWEVER Windows doesn't send a MouseUp message when resizing
-			// a window really fast. My guess is that the cursor gets out of bounds and message is sent to another window.
-			if(mActiveMouseButton == guiButton && mActiveElement != nullptr) 
-			{
-				mActiveElement = nullptr;
-				mActiveWidget = nullptr;
-				mActiveMouseButton = GUIMouseButton::Left;
-			}
-
 			// We only check for mouse down if mouse isn't already being held down, and we are hovering over an element
 			bool acceptMouseDown = mActiveElement == nullptr && mMouseOverElement != nullptr;
 			if(acceptMouseDown)
@@ -615,6 +619,15 @@ namespace BansheeEngine
 
 				event.markAsUsed();
 			}
+
+			// Close drop down box if user clicks outside the drop down box
+			if(mDropDownBoxActive)
+			{
+				if(mMouseOverElement == nullptr || (&mMouseOverElement->_getParentWidget() != mDropDownBox.get()))
+				{
+					closeDropDownBox(-1);
+				}
+			}
 		}
 	}
 

+ 2 - 16
DropDown.txt

@@ -1,16 +1,2 @@
-DropDownBox
- - Like ScrollArea, has 0 width and contains just buttons
-   - Set up three button types, plus bg for top/bottom buttons
- - Sets up callbacks to all of the buttons
-   - Clicking on entry buttons closes it and notifies parent DropDownList
-   - Clicking on scroll buttons updates the data
-
-GUIManager::createDropDownBox(GUIWidget parent, Int2 position, vector<String> data, function selectedCallback)
- - Creates a GUIArea in the specified widget
-   - Positions it under the wanted position, with height determined by widget window parent
-   - Width is some fixed value predefined in GUIManager
- - Automatically closes the area when the user changes focus to non-drop down box element
-
-Same approach can be used for Menus
- GUIManager::createDropDownMenu
-   - Except they can cascade so multiple menus may be open at the same time
+Add support for images on buttons. Replace dropdownbox scroll buttons with that.
+Drop down box gets closed the same frame it was opened on! Need to delay the close one frame.