Przeglądaj źródła

A lot more work on drop down

Marko Pintera 12 lat temu
rodzic
commit
0d54df82fe

+ 2 - 0
BansheeEngine/BansheeEngine.vcxproj

@@ -235,6 +235,7 @@
     <ClInclude Include="Include\BsGUIArea.h" />
     <ClInclude Include="Include\BsGUIArea.h" />
     <ClInclude Include="Include\BsGUIButton.h" />
     <ClInclude Include="Include\BsGUIButton.h" />
     <ClInclude Include="Include\BsGUICommandEvent.h" />
     <ClInclude Include="Include\BsGUICommandEvent.h" />
+    <ClInclude Include="Include\BsGUIDropDownBox.h" />
     <ClInclude Include="Include\BsGUIDropDownList.h" />
     <ClInclude Include="Include\BsGUIDropDownList.h" />
     <ClInclude Include="Include\BsGUIElementBase.h" />
     <ClInclude Include="Include\BsGUIElementBase.h" />
     <ClInclude Include="Include\BsGUIInputTool.h" />
     <ClInclude Include="Include\BsGUIInputTool.h" />
@@ -291,6 +292,7 @@
     <ClCompile Include="Source\BsEngineGUI.cpp" />
     <ClCompile Include="Source\BsEngineGUI.cpp" />
     <ClCompile Include="Source\BsGUIArea.cpp" />
     <ClCompile Include="Source\BsGUIArea.cpp" />
     <ClCompile Include="Source\BsGUIButton.cpp" />
     <ClCompile Include="Source\BsGUIButton.cpp" />
+    <ClCompile Include="Source\BsGUIDropDownBox.cpp" />
     <ClCompile Include="Source\BsGUIDropDownList.cpp" />
     <ClCompile Include="Source\BsGUIDropDownList.cpp" />
     <ClCompile Include="Source\BsGUIElement.cpp" />
     <ClCompile Include="Source\BsGUIElement.cpp" />
     <ClCompile Include="Source\BsGUIElementBase.cpp" />
     <ClCompile Include="Source\BsGUIElementBase.cpp" />

+ 6 - 0
BansheeEngine/BansheeEngine.vcxproj.filters

@@ -201,6 +201,9 @@
     <ClInclude Include="Include\BsGUIDropDownList.h">
     <ClInclude Include="Include\BsGUIDropDownList.h">
       <Filter>Header Files\GUI</Filter>
       <Filter>Header Files\GUI</Filter>
     </ClInclude>
     </ClInclude>
+    <ClInclude Include="Include\BsGUIDropDownBox.h">
+      <Filter>Header Files\GUI</Filter>
+    </ClInclude>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsGUIElement.cpp">
     <ClCompile Include="Source\BsGUIElement.cpp">
@@ -347,5 +350,8 @@
     <ClCompile Include="Source\BsGUIDropDownList.cpp">
     <ClCompile Include="Source\BsGUIDropDownList.cpp">
       <Filter>Source Files\GUI</Filter>
       <Filter>Source Files\GUI</Filter>
     </ClCompile>
     </ClCompile>
+    <ClCompile Include="Source\BsGUIDropDownBox.cpp">
+      <Filter>Source Files\GUI</Filter>
+    </ClCompile>
   </ItemGroup>
   </ItemGroup>
 </Project>
 </Project>

+ 2 - 0
BansheeEngine/Include/BsGUIButton.h

@@ -15,6 +15,8 @@ namespace BansheeEngine
 		static GUIButton* create(GUIWidget& parent, const CM::WString& text, const GUIElementStyle* style = nullptr);
 		static GUIButton* create(GUIWidget& parent, const CM::WString& text, const GUIElementStyle* style = nullptr);
 		static GUIButton* create(GUIWidget& parent, const CM::WString& text, const GUILayoutOptions& layoutOptions, const GUIElementStyle* style = nullptr);
 		static GUIButton* create(GUIWidget& parent, const CM::WString& text, const GUILayoutOptions& layoutOptions, const GUIElementStyle* style = nullptr);
 	
 	
+		void setText(const CM::WString& text);
+
 		boost::signal<void()> onClick;
 		boost::signal<void()> onClick;
 	protected:
 	protected:
 		~GUIButton();
 		~GUIButton();

+ 29 - 0
BansheeEngine/Include/BsGUIDropDownBox.h

@@ -0,0 +1,29 @@
+#include "BsPrerequisites.h"
+#include "BsGUIWidget.h"
+#include <boost/signal.hpp>
+
+namespace BansheeEngine
+{
+	class BS_EXPORT GUIDropDownBox : public GUIWidget
+	{
+	public:
+		GUIDropDownBox(const CM::HSceneObject& parent);
+		~GUIDropDownBox();
+
+		void initialize(CM::Viewport* target, CM::RenderWindow* window, GUIDropDownList* parentList,
+			const CM::Vector<CM::WString>::type& elements, std::function<void(CM::UINT32)> selectedCallback);
+
+		boost::signal<void(CM::UINT32)> onEntrySelected;
+	private:
+		static const CM::UINT32 DROP_DOWN_BOX_WIDTH;
+
+		std::function<void(CM::UINT32)> mSelectedDropDownEntryCallback;
+		CM::Vector<CM::WString>::type mDropDownElements;
+		CM::Vector<GUIButton*>::type mDropDownElementButtons;
+		CM::UINT32 mDropDownStartIdx;
+
+		void scrollDown();
+		void scrollUp();
+		void entrySelected(CM::UINT32 idx);
+	};
+}

+ 7 - 0
BansheeEngine/Include/BsGUIManager.h

@@ -48,6 +48,9 @@ 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 openDropDownBox(GUIDropDownList* parentList, const CM::Vector<CM::WString>::type& elements, 
+			std::function<void(CM::UINT32)> selectedCallback);
+
 		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(); }
@@ -99,6 +102,10 @@ namespace BansheeEngine
 		SpriteTexturePtr mTextSelectionTexture;
 		SpriteTexturePtr mTextSelectionTexture;
 		CM::Color mTextSelectionColor;
 		CM::Color mTextSelectionColor;
 
 
+		// Drop down box
+		CM::HSceneObject mDropDownSO;
+		CM::GameObjectHandle<GUIDropDownBox> mDropDownBox;
+
 		boost::signals::connection mOnButtonDownConn;
 		boost::signals::connection mOnButtonDownConn;
 		boost::signals::connection mOnButtonUpConn;
 		boost::signals::connection mOnButtonUpConn;
 		boost::signals::connection mOnMouseMovedConn;
 		boost::signals::connection mOnMouseMovedConn;

+ 2 - 0
BansheeEngine/Include/BsPrerequisites.h

@@ -53,6 +53,8 @@ namespace BansheeEngine
 	class GUIInputSelection;
 	class GUIInputSelection;
 	struct GUILayoutOptions;
 	struct GUILayoutOptions;
 	class GUIToggleGroup;
 	class GUIToggleGroup;
+	class GUIDropDownList;
+	class GUIDropDownBox;
 	class DragAndDropManager;
 	class DragAndDropManager;
 
 
 	// 2D
 	// 2D

+ 4 - 0
BansheeEngine/Source/BsEngineGUI.cpp

@@ -411,6 +411,10 @@ namespace BansheeEngine
 		dropDownListStyle.fixedWidth = false;
 		dropDownListStyle.fixedWidth = false;
 		dropDownListStyle.height = 13;
 		dropDownListStyle.height = 13;
 		dropDownListStyle.width = 30;
 		dropDownListStyle.width = 30;
+		dropDownListStyle.contentOffset.left = 3;
+		dropDownListStyle.contentOffset.right = 11;
+		dropDownListStyle.contentOffset.top = 1;
+		dropDownListStyle.contentOffset.bottom = 1;
 		dropDownListStyle.border.left = 1;
 		dropDownListStyle.border.left = 1;
 		dropDownListStyle.border.right = 10;
 		dropDownListStyle.border.right = 10;
 		dropDownListStyle.border.top = 1;
 		dropDownListStyle.border.top = 1;

+ 7 - 0
BansheeEngine/Source/BsGUIButton.cpp

@@ -66,6 +66,13 @@ namespace BansheeEngine
 		return new (cm_alloc<GUIButton, PoolAlloc>()) GUIButton(parent, style, text, layoutOptions);
 		return new (cm_alloc<GUIButton, PoolAlloc>()) GUIButton(parent, style, text, layoutOptions);
 	}
 	}
 
 
+	void GUIButton::setText(const CM::WString& text)
+	{
+		mText = text;
+
+		markContentAsDirty();
+	}
+
 	UINT32 GUIButton::getNumRenderElements() const
 	UINT32 GUIButton::getNumRenderElements() const
 	{
 	{
 		UINT32 numElements = mImageSprite->getNumRenderElements();
 		UINT32 numElements = mImageSprite->getNumRenderElements();

+ 178 - 0
BansheeEngine/Source/BsGUIDropDownBox.cpp

@@ -0,0 +1,178 @@
+#include "BsGUIDropDownBox.h"
+#include "BsGUIArea.h"
+#include "BsGUILayout.h"
+#include "BsGUITexture.h"
+#include "BsGUIButton.h"
+#include "BsEngineGUI.h"
+#include "CmViewport.h"
+#include "BsGUIDropDownList.h"
+
+using namespace CamelotFramework;
+
+namespace BansheeEngine
+{
+	const UINT32 GUIDropDownBox::DROP_DOWN_BOX_WIDTH = 150;
+
+	GUIDropDownBox::GUIDropDownBox(const HSceneObject& parent)
+		:GUIWidget(parent), mDropDownStartIdx(0)
+	{
+
+	}
+
+	GUIDropDownBox::~GUIDropDownBox()
+	{
+
+	}
+
+	void GUIDropDownBox::initialize(Viewport* target, RenderWindow* window, GUIDropDownList* parentList, 
+		const CM::Vector<WString>::type& elements, std::function<void(CM::UINT32)> selectedCallback)
+	{
+		GUIWidget::initialize(target, window);
+
+		mDropDownElements = elements;
+		mSelectedDropDownEntryCallback = selectedCallback;
+
+		setDepth(0); // Needs to be in front of everything
+
+		const GUIElementStyle* dropDownBoxStyle = EngineGUI::instance().getSkin().getStyle("DropDownBox");
+
+		Rect dropDownListBounds = parentList->getBounds();
+
+		// Determine x position
+		Int2 position;
+		position.x = dropDownListBounds.x;
+
+		// Determine maximum width
+		UINT32 maxPossibleWidth = (UINT32)std::max(0, (target->getLeft() + target->getWidth()) - position.x);
+		UINT32 width = std::min(DROP_DOWN_BOX_WIDTH, maxPossibleWidth);
+		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 helperElementHeight = scrollButtonUpHeight + scrollButtonDownHeight + dropDownBoxStyle->margins.top + dropDownBoxStyle->margins.bottom;
+		UINT32 elementButtonHeight = EngineGUI::instance().getSkin().getStyle("DropDownEntryBtn")->fixedHeight;
+
+		UINT32 maxNeededHeight = elementButtonHeight * (UINT32)mDropDownElements.size() + helperElementHeight;
+		UINT32 availableDownwardHeight = (UINT32)std::max(0, (target->getTop() + target->getHeight()) - (dropDownListBounds.y + dropDownListBounds.height));
+
+		UINT32 availableUpwardHeight = (UINT32)std::max(0, dropDownListBounds.y - target->getTop());
+
+		//// Prefer down if possible
+		if(maxNeededHeight <= availableDownwardHeight)
+			position.y = dropDownListBounds.y + dropDownListBounds.height;
+		else
+		{
+			if(availableDownwardHeight > availableUpwardHeight)
+				position.y = dropDownListBounds.y + dropDownListBounds.height;
+			else
+				position.y = dropDownListBounds.y - std::min(maxNeededHeight, availableUpwardHeight);
+		}
+
+		// Determine height of the box, and how many visible elements we can fit in it
+		UINT32 maxPossibleHeight = (UINT32)std::max(0, (target->getTop() + target->getHeight()) - position.y);
+		UINT32 heightAvailableForContent = (UINT32)std::max(0, (INT32)maxPossibleHeight - (INT32)helperElementHeight);
+		UINT32 numVisElements = 0;
+		
+		UINT32 contentAreaHeight = 0;
+		for(UINT32 i = 0; i < (UINT32)mDropDownElements.size(); i++)
+		{
+			if(contentAreaHeight >= heightAvailableForContent)
+				break;
+
+			contentAreaHeight += elementButtonHeight;
+			numVisElements++;
+		}
+
+		UINT32 totalHeight = contentAreaHeight + helperElementHeight; 
+		
+		// Scroll up buttons
+		GUIArea* scrollUpBtnBgArea = GUIArea::create(*this, position.x + dropDownBoxStyle->margins.left, position.y, contentWidth, scrollButtonUpHeight);
+		scrollUpBtnBgArea->setDepth(101);
+		scrollUpBtnBgArea->getLayout().addElement(GUITexture::create(*this, GUIImageScaleMode::ScaleToFit, 
+			EngineGUI::instance().getSkin().getStyle("DropDownScrollUpBtnBg")));
+
+		GUIArea* scrollUpBtnArea = GUIArea::create(*this, position.x + dropDownBoxStyle->margins.left, position.y, contentWidth, scrollButtonUpHeight);
+		scrollUpBtnArea->setDepth(100);
+		GUIButton* scrollUpBtn = GUIButton::create(*this, L"", EngineGUI::instance().getSkin().getStyle("DropDownScrollUpBtn"));
+		scrollUpBtnArea->getLayout().addElement(scrollUpBtn);
+
+		// Entry buttons
+		// TODO - It may happen there is not enough place for even a single element, in which case we just don't render any.
+		//  Some smart solution to deal with that case might be employed, but I think it's a fairly rare case and can be deal with in other ways.
+
+		UINT32 contentAreaYOffset = scrollButtonUpHeight + dropDownBoxStyle->margins.top;
+
+		GUIArea* dropDownEntriesArea = GUIArea::create(*this, position.x + dropDownBoxStyle->margins.left, 
+			position.y + contentAreaYOffset, contentWidth, contentAreaHeight);
+		GUILayout* dropDownEntriesLayout = &dropDownEntriesArea->getLayout().addLayoutY();
+
+		for(UINT32 i = 0; i < numVisElements; i++)
+		{
+			GUIButton* button = GUIButton::create(*this, mDropDownElements[i], EngineGUI::instance().getSkin().getStyle("DropDownEntryBtn"));
+			button->onClick.connect(boost::bind(&GUIDropDownBox::entrySelected, this, i));
+
+			dropDownEntriesLayout->addElement(button);
+			mDropDownElementButtons.push_back(button);
+		}
+
+		// Scroll down buttons
+		UINT32 scrollBtnDownOffset = position.y + contentAreaYOffset + contentAreaHeight;
+		GUIArea* scrollDownBtnBgArea = GUIArea::create(*this, position.x + dropDownBoxStyle->margins.left, 
+			scrollBtnDownOffset, contentWidth, scrollButtonDownHeight);
+		scrollDownBtnBgArea->setDepth(101);
+		scrollDownBtnBgArea->getLayout().addElement(GUITexture::create(*this, GUIImageScaleMode::ScaleToFit, 
+			EngineGUI::instance().getSkin().getStyle("DropDownScrollDownBtnBg")));
+
+		GUIArea* scrollDownBtnArea = GUIArea::create(*this, position.x + dropDownBoxStyle->margins.left, 
+			scrollBtnDownOffset, contentWidth, scrollButtonDownHeight);
+		scrollDownBtnArea->setDepth(100);
+		GUIButton* scrollDownBtn = GUIButton::create(*this, L"", EngineGUI::instance().getSkin().getStyle("DropDownScrollDownBtn"));
+		scrollDownBtnArea->getLayout().addElement(scrollDownBtn);
+		
+		// Background frame
+		GUIArea* dropDownBoxArea = GUIArea::create(*this, position.x, position.y, width, totalHeight);
+		dropDownBoxArea->setDepth(102);
+		dropDownBoxArea->getLayout().addElement(GUITexture::create(*this, GUIImageScaleMode::ScaleToFit, 
+			EngineGUI::instance().getSkin().getStyle("DropDownBox")));
+	}
+
+	void GUIDropDownBox::scrollDown()
+	{
+		INT32 maxNumElements = (INT32)mDropDownElements.size();
+		INT32 numVisibleElements = (INT32)mDropDownElementButtons.size();
+
+		INT32 newIdx = mDropDownStartIdx + numVisibleElements;
+		INT32 clampedNewIdx = std::min(newIdx, maxNumElements - numVisibleElements);
+		mDropDownStartIdx = (UINT32)std::min(newIdx, clampedNewIdx);
+
+		UINT32 i = mDropDownStartIdx;
+		for(auto& button : mDropDownElementButtons)
+		{
+			button->setText(mDropDownElements[i]);
+			i++;
+		}
+	}
+
+	void GUIDropDownBox::scrollUp()
+	{
+		INT32 numVisibleElements = (INT32)mDropDownElementButtons.size();
+
+		INT32 newIdx = mDropDownStartIdx - numVisibleElements;
+		INT32 clampedNewIdx = std::max(newIdx, 0);
+		mDropDownStartIdx = (UINT32)std::min(newIdx, clampedNewIdx);
+
+		UINT32 i = mDropDownStartIdx;
+		for(auto& button : mDropDownElementButtons)
+		{
+			button->setText(mDropDownElements[i]);
+			i++;
+		}
+	}
+
+	void GUIDropDownBox::entrySelected(UINT32 idx)
+	{
+		if(!onEntrySelected.empty())
+			onEntrySelected(mDropDownStartIdx + idx);
+	}
+}

+ 12 - 0
BansheeEngine/Source/BsGUIManager.cpp

@@ -20,6 +20,8 @@
 #include "CmRenderQueue.h"
 #include "CmRenderQueue.h"
 #include "BsGUIInputCaret.h"
 #include "BsGUIInputCaret.h"
 #include "BsGUIInputSelection.h"
 #include "BsGUIInputSelection.h"
+#include "BsGUIDropDownList.h"
+#include "BsGUIDropDownBox.h"
 #include "BsDragAndDropManager.h"
 #include "BsDragAndDropManager.h"
 
 
 using namespace CamelotFramework;
 using namespace CamelotFramework;
@@ -492,6 +494,16 @@ namespace BansheeEngine
 		}
 		}
 	}
 	}
 
 
+	void GUIManager::openDropDownBox(GUIDropDownList* parentList, const CM::Vector<WString>::type& elements, 
+		std::function<void(CM::UINT32)> selectedCallback)
+	{
+		mDropDownSO = SceneObject::create("DropDownBox");
+		mDropDownBox = mDropDownSO->addComponent<GUIDropDownBox>();
+
+		GUIWidget& widget = parentList->_getParentWidget();
+		mDropDownBox->initialize(widget.getTarget(), widget.getOwnerWindow(), parentList, elements, selectedCallback);
+	}
+
 	void GUIManager::updateCaretTexture()
 	void GUIManager::updateCaretTexture()
 	{
 	{
 		if(mCaretTexture == nullptr)
 		if(mCaretTexture == nullptr)

+ 1 - 1
CamelotClient/Source/CmTestTextSprite.cpp

@@ -46,7 +46,7 @@ namespace CamelotFramework
 		area->getLayout().addElement(mLabel);
 		area->getLayout().addElement(mLabel);
 
 
 		Vector<WString>::type dropDownElements;
 		Vector<WString>::type dropDownElements;
-		dropDownElements.push_back(L"Element #1");
+		dropDownElements.push_back(L"Ejlement #1");
 		dropDownElements.push_back(L"Element #2");
 		dropDownElements.push_back(L"Element #2");
 		dropDownElements.push_back(L"Element #3");
 		dropDownElements.push_back(L"Element #3");
 		area->getLayout().addElement(GUIDropDownList::create(*this, dropDownElements, GUILayoutOptions::fixed(50, 13)));
 		area->getLayout().addElement(GUIDropDownList::create(*this, dropDownElements, GUILayoutOptions::fixed(50, 13)));

+ 14 - 10
DropDown.txt

@@ -1,12 +1,16 @@
-DropDownList
- - 2 states
-   - Closed - Pretty much a normal button with a drop down arrow
-   - Open - Creates a new GUIArea in the current widget
-        - The area is always sized so it doesn't go outside the widget
-        - Area is full of GUIButtons with callbacks set up so that the GUIArea gets destroyed when selection is made
-        - If GUIButtons don't fully fit in the area then top and bottom display arrow buttons you may press to scroll through them
-             - Arrows change pages, not element-by-element
+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
 
 
-IMMEDIATE:
- - Add up/down drop down arrows (need Textures)
+Same approach can be used for Menus
+ GUIManager::createDropDownMenu
+   - Except they can cascade so multiple menus may be open at the same time