Просмотр исходного кода

Added toggle groups (i.e. radio buttons)

Marko Pintera 12 лет назад
Родитель
Сommit
5c1863956d

+ 2 - 0
BansheeEngine/BansheeEngine.vcxproj

@@ -249,6 +249,7 @@
     <ClInclude Include="Include\BsGUISpace.h" />
     <ClInclude Include="Include\BsGUITexture.h" />
     <ClInclude Include="Include\BsGUIToggle.h" />
+    <ClInclude Include="Include\BsGUIToggleGroup.h" />
     <ClInclude Include="Include\BsGUIWindowFrame.h" />
     <ClInclude Include="Include\BsGUIWindowMover.h" />
     <ClInclude Include="Include\BsPrerequisites.h" />
@@ -304,6 +305,7 @@
     <ClCompile Include="Source\BsGUISkin.cpp" />
     <ClCompile Include="Source\BsGUITexture.cpp" />
     <ClCompile Include="Source\BsGUIToggle.cpp" />
+    <ClCompile Include="Source\BsGUIToggleGroup.cpp" />
     <ClCompile Include="Source\BsGUIWidget.cpp" />
     <ClCompile Include="Source\BsGUIWindowFrame.cpp" />
     <ClCompile Include="Source\BsGUIWindowMover.cpp" />

+ 6 - 0
BansheeEngine/BansheeEngine.vcxproj.filters

@@ -189,6 +189,9 @@
     <ClInclude Include="Include\BsGUIScrollBar.h">
       <Filter>Header Files\GUI</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsGUIToggleGroup.h">
+      <Filter>Header Files\GUI</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsGUIElement.cpp">
@@ -323,5 +326,8 @@
     <ClCompile Include="Source\BsGUIScrollBar.cpp">
       <Filter>Source Files\GUI</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsGUIToggleGroup.cpp">
+      <Filter>Source Files\GUI</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 13 - 1
BansheeEngine/Include/BsGUIToggle.h

@@ -2,6 +2,7 @@
 
 #include "BsPrerequisites.h"
 #include "BsGUIElement.h"
+#include "BsGUIToggleGroup.h"
 #include "BsImageSprite.h"
 #include "boost/signal.hpp"
 
@@ -15,6 +16,16 @@ namespace BansheeEngine
 		static GUIToggle* create(GUIWidget& parent, const CM::WString& text, const GUIElementStyle* style = nullptr);
 		static GUIToggle* create(GUIWidget& parent, const GUILayoutOptions& layoutOptions, const CM::WString& text, const GUIElementStyle* style = nullptr);
 	
+		static GUIToggle* create(GUIWidget& parent, const CM::WString& text, std::shared_ptr<GUIToggleGroup> toggleGroup, const GUIElementStyle* style = nullptr);
+		static GUIToggle* create(GUIWidget& parent, const GUILayoutOptions& layoutOptions, const CM::WString& text, std::shared_ptr<GUIToggleGroup> toggleGroup, const GUIElementStyle* style = nullptr);
+
+		static std::shared_ptr<GUIToggleGroup> createToggleGroup();
+
+		void toggleOn();
+		void toggleOff();
+
+		void _setToggleGroup(std::shared_ptr<GUIToggleGroup> toggleGroup);
+
 		boost::signal<void(bool)> onToggled;
 	protected:
 		~GUIToggle();
@@ -55,6 +66,7 @@ namespace BansheeEngine
 
 		virtual CM::UINT32 _getRenderElementDepth(CM::UINT32 renderElementIdx) const;
 	private:
+		std::shared_ptr<GUIToggleGroup> mToggleGroup;
 		ImageSprite* mImageSprite;
 		TextSprite* mTextSprite;
 		CM::UINT32 mNumImageRenderElements;
@@ -63,7 +75,7 @@ namespace BansheeEngine
 		IMAGE_SPRITE_DESC mImageDesc;
 		CM::WString mText;
 
-		GUIToggle(GUIWidget& parent, const GUIElementStyle* style, const CM::WString& text, const GUILayoutOptions& layoutOptions);
+		GUIToggle(GUIWidget& parent, const GUIElementStyle* style, const CM::WString& text, std::shared_ptr<GUIToggleGroup> toggleGroup, const GUILayoutOptions& layoutOptions);
 
 		virtual bool mouseEvent(const GUIMouseEvent& ev);
 	};

+ 25 - 0
BansheeEngine/Include/BsGUIToggleGroup.h

@@ -0,0 +1,25 @@
+#pragma once
+
+#include "BsPrerequisites.h"
+
+namespace BansheeEngine
+{
+	class BS_EXPORT GUIToggleGroup
+	{
+	public:
+		~GUIToggleGroup();
+
+		void add(GUIToggle* toggle);
+		void remove(GUIToggle* toggle);
+
+	private:
+		friend class GUIToggle;
+
+		GUIToggleGroup() {}
+
+		void initialize(const std::shared_ptr<GUIToggleGroup>& sharedPtr);
+
+		CM::Vector<GUIToggle*>::type mButtons;
+		std::weak_ptr<GUIToggleGroup> mThis;
+	};
+}

+ 1 - 0
BansheeEngine/Include/BsPrerequisites.h

@@ -54,6 +54,7 @@ namespace BansheeEngine
 	class GUIInputCaret;
 	class GUIInputSelection;
 	struct GUILayoutOptions;
+	class GUIToggleGroup;
 
 	// 2D
 	class TextSprite;

+ 131 - 13
BansheeEngine/Source/BsGUIToggle.cpp

@@ -6,6 +6,7 @@
 #include "BsTextSprite.h"
 #include "BsGUILayoutOptions.h"
 #include "BsGUIMouseEvent.h"
+#include "BsGUIToggleGroup.h"
 #include "CmTexture.h"
 
 using namespace CamelotFramework;
@@ -18,8 +19,8 @@ namespace BansheeEngine
 		return name;
 	}
 
-	GUIToggle::GUIToggle(GUIWidget& parent, const GUIElementStyle* style, const WString& text, const GUILayoutOptions& layoutOptions)
-		:GUIElement(parent, style, layoutOptions), mText(text), mNumImageRenderElements(0), mIsToggled(false)
+	GUIToggle::GUIToggle(GUIWidget& parent, const GUIElementStyle* style, const WString& text, std::shared_ptr<GUIToggleGroup> toggleGroup, const GUILayoutOptions& layoutOptions)
+		:GUIElement(parent, style, layoutOptions), mText(text), mNumImageRenderElements(0), mIsToggled(false), mToggleGroup(nullptr)
 	{
 		mImageSprite = cm_new<ImageSprite, PoolAlloc>();
 		mTextSprite = cm_new<TextSprite, PoolAlloc>();
@@ -36,10 +37,18 @@ namespace BansheeEngine
 		mImageDesc.borderRight = mStyle->border.right;
 		mImageDesc.borderTop = mStyle->border.top;
 		mImageDesc.borderBottom = mStyle->border.bottom;
+
+		if(toggleGroup != nullptr)
+			toggleGroup->add(this);
 	}
 
 	GUIToggle::~GUIToggle()
 	{
+		if(mToggleGroup != nullptr)
+		{
+			mToggleGroup->remove(this);
+		}
+
 		cm_delete<PoolAlloc>(mTextSprite);
 		cm_delete<PoolAlloc>(mImageSprite);
 	}
@@ -52,7 +61,7 @@ namespace BansheeEngine
 			style = skin->getStyle(getGUITypeName());
 		}
 
-		return new (cm_alloc<GUIToggle, PoolAlloc>()) GUIToggle(parent, style, text, getDefaultLayoutOptions(style));
+		return new (cm_alloc<GUIToggle, PoolAlloc>()) GUIToggle(parent, style, text, nullptr, getDefaultLayoutOptions(style));
 	}
 
 	GUIToggle* GUIToggle::create(GUIWidget& parent, const GUILayoutOptions& layoutOptions, const WString& text, const GUIElementStyle* style)
@@ -63,7 +72,123 @@ namespace BansheeEngine
 			style = skin->getStyle(getGUITypeName());
 		}
 
-		return new (cm_alloc<GUIToggle, PoolAlloc>()) GUIToggle(parent, style, text, layoutOptions);
+		return new (cm_alloc<GUIToggle, PoolAlloc>()) GUIToggle(parent, style, text, nullptr, layoutOptions);
+	}
+
+	GUIToggle* GUIToggle::create(GUIWidget& parent, const WString& text, std::shared_ptr<GUIToggleGroup> toggleGroup, const GUIElementStyle* style)
+	{
+		if(style == nullptr)
+		{
+			const GUISkin* skin = parent.getSkin();
+			style = skin->getStyle(getGUITypeName());
+		}
+
+		return new (cm_alloc<GUIToggle, PoolAlloc>()) GUIToggle(parent, style, text, toggleGroup, getDefaultLayoutOptions(style));
+	}
+
+	GUIToggle* GUIToggle::create(GUIWidget& parent, const GUILayoutOptions& layoutOptions, const WString& text, std::shared_ptr<GUIToggleGroup> toggleGroup, const GUIElementStyle* style)
+	{
+		if(style == nullptr)
+		{
+			const GUISkin* skin = parent.getSkin();
+			style = skin->getStyle(getGUITypeName());
+		}
+
+		return new (cm_alloc<GUIToggle, PoolAlloc>()) GUIToggle(parent, style, text, toggleGroup, layoutOptions);
+	}
+
+	std::shared_ptr<GUIToggleGroup> GUIToggle::createToggleGroup()
+	{
+		std::shared_ptr<GUIToggleGroup> toggleGroup = std::shared_ptr<GUIToggleGroup>(new GUIToggleGroup());
+		toggleGroup->initialize(toggleGroup);
+
+		return toggleGroup;
+	}
+
+	void GUIToggle::_setToggleGroup(std::shared_ptr<GUIToggleGroup> toggleGroup)
+	{
+		mToggleGroup = toggleGroup;
+
+		bool isToggled = false;
+		if(mToggleGroup != nullptr) // If in group ensure at least one element is toggled on
+		{
+			for(auto& toggleElem : mToggleGroup->mButtons)
+			{
+				if(isToggled)
+				{
+					if(toggleElem->mIsToggled)
+						toggleElem->toggleOff();
+				}
+				else
+				{
+					if(toggleElem->mIsToggled)
+						isToggled = true;
+				}
+
+			}
+
+			if(!isToggled)
+				toggleOn();
+		}
+	}
+
+	void GUIToggle::toggleOn()
+	{
+		if(mIsToggled)
+			return;
+
+		mIsToggled = true;
+
+		if(!onToggled.empty())
+			onToggled(mIsToggled);
+
+		if(mToggleGroup != nullptr)
+		{
+			for(auto& toggleElem : mToggleGroup->mButtons)
+			{
+				if(toggleElem != this)
+					toggleElem->toggleOff();
+			}
+		}
+
+		mImageDesc.texture = mStyle->normalOn.texture;
+		markContentAsDirty();
+	}
+
+	void GUIToggle::toggleOff()
+	{
+		if(!mIsToggled)
+			return;
+
+		bool canBeToggledOff = false;
+		if(mToggleGroup != nullptr) // If in group ensure at least one element is toggled on
+		{
+			for(auto& toggleElem : mToggleGroup->mButtons)
+			{
+				if(toggleElem != this)
+				{
+					if(toggleElem->mIsToggled)
+					{
+						canBeToggledOff = true;
+						break;
+					}
+				}
+
+			}
+		}
+		else
+			canBeToggledOff = true;
+
+		if(canBeToggledOff)
+		{
+			mIsToggled = false;
+
+			if(!onToggled.empty())
+				onToggled(mIsToggled);
+
+			mImageDesc.texture = mStyle->normal.texture;
+			markContentAsDirty();
+		}
 	}
 
 	UINT32 GUIToggle::getNumRenderElements() const
@@ -204,17 +329,10 @@ namespace BansheeEngine
 		}
 		else if(ev.getType() == GUIMouseEventType::MouseUp)
 		{
-			mIsToggled = !mIsToggled;
-
 			if(mIsToggled)
-				mImageDesc.texture = mStyle->normalOn.texture;
+				toggleOff();
 			else
-				mImageDesc.texture = mStyle->normal.texture;
-
-			markContentAsDirty();
-
-			if(!onToggled.empty())
-				onToggled(mIsToggled);
+				toggleOn();
 
 			return true;
 		}

+ 40 - 0
BansheeEngine/Source/BsGUIToggleGroup.cpp

@@ -0,0 +1,40 @@
+#include "BsGUIToggleGroup.h"
+#include "BsGUIToggle.h"
+
+using namespace CamelotFramework;
+
+namespace BansheeEngine
+{
+	GUIToggleGroup::~GUIToggleGroup()
+	{
+		for(auto& button : mButtons)
+		{
+			button->_setToggleGroup(nullptr);
+		}
+	}
+
+	void GUIToggleGroup::initialize(const std::shared_ptr<GUIToggleGroup>& sharedPtr)
+	{
+		mThis = sharedPtr;
+	}
+
+	void GUIToggleGroup::add(GUIToggle* toggle)
+	{
+		auto iterFind = std::find(begin(mButtons), end(mButtons), toggle);
+		if(iterFind != end(mButtons))
+			return;
+
+		mButtons.push_back(toggle);
+		toggle->_setToggleGroup(mThis.lock());
+	}
+
+	void GUIToggleGroup::remove(GUIToggle* toggle)
+	{
+		auto iterFind = std::find(begin(mButtons), end(mButtons), toggle);
+		if(iterFind == end(mButtons))
+			return;
+
+		(*iterFind)->_setToggleGroup(nullptr);
+		mButtons.erase(iterFind);
+	}
+}

+ 2 - 0
CamelotClient/CamelotClient.cpp

@@ -28,6 +28,7 @@
 #include "CmDebugCamera.h"
 #include "CmTestTextSprite.h"
 #include "DbgEditorWidget1.h"
+#include "DbgEditorWidget2.h"
 #include "CmRTTIType.h"
 #include "CmCursor.h"
 
@@ -296,6 +297,7 @@ int CALLBACK WinMain(
 	/************************************************************************/
 
 	DbgEditorWidget1::open();
+	DbgEditorWidget2::open();
 
 	gBansheeApp().runMainLoop();
 

+ 16 - 0
CamelotClient/Include/DbgEditorWidget2.h

@@ -1,8 +1,24 @@
 #pragma once
 
 #include "BsEditorPrerequisites.h"
+#include "BsEditorWidget.h"
 
 namespace BansheeEditor
 {
+	class DbgEditorWidget2 : public EditorWidget
+	{
+	public:
+		virtual ~DbgEditorWidget2();
 
+		static DbgEditorWidget2* instance();
+		static DbgEditorWidget2* open();
+		static void close();
+
+	protected:
+		DbgEditorWidget2();
+
+		void initialize();
+	private:
+		static DbgEditorWidget2* Instance;
+	};
 }

+ 70 - 0
CamelotClient/Source/DbgEditorWidget2.cpp

@@ -1,9 +1,79 @@
+#include "DbgEditorWidget2.h"
+#include "BsEditorWindow.h"
+#include "BsGUIToggle.h"
+#include "BsGUIScrollArea.h"
+#include "BsGUIArea.h"
+#include "BsGUILayout.h"
+#include "BsEditorWindow.h"
 #include "BsEditorWidgetContainer.h"
+#include "BsEngineGUI.h"
 
 using namespace CamelotFramework;
 using namespace BansheeEngine;
 
 namespace BansheeEditor
 {
+	DbgEditorWidget2* DbgEditorWidget2::Instance = nullptr;
 
+	DbgEditorWidget2::DbgEditorWidget2()
+		:EditorWidget(L"DbgEditorWidget2")
+	{
+
+	}
+
+	DbgEditorWidget2::~DbgEditorWidget2()
+	{
+
+	}
+
+	void DbgEditorWidget2::initialize()
+	{
+		GUILayout& layout = mContent->getLayout();
+
+		GUIScrollArea* scrollArea = GUIScrollArea::create(*mParentWidget);
+		layout.addElement(scrollArea);
+
+		GUILayout& scrollLayout = scrollArea->getLayout().addLayoutY();
+
+		std::shared_ptr<GUIToggleGroup> toggleGroup = GUIToggle::createToggleGroup();
+
+		scrollLayout.addElement(GUIToggle::create(*mParentWidget, L"Test A", toggleGroup, EngineGUI::instance().getSkin().getStyle("Button")));
+		scrollLayout.addElement(GUIToggle::create(*mParentWidget, L"Test B", toggleGroup, EngineGUI::instance().getSkin().getStyle("Button")));
+		scrollLayout.addElement(GUIToggle::create(*mParentWidget, L"Test C", toggleGroup, EngineGUI::instance().getSkin().getStyle("Button")));
+		scrollLayout.addElement(GUIToggle::create(*mParentWidget, L"Test D", toggleGroup, EngineGUI::instance().getSkin().getStyle("Button")));
+		scrollLayout.addElement(GUIToggle::create(*mParentWidget, L"Test E", toggleGroup, EngineGUI::instance().getSkin().getStyle("Button")));
+	}
+
+	DbgEditorWidget2* DbgEditorWidget2::instance()
+	{
+		return Instance;
+	}
+
+	DbgEditorWidget2* DbgEditorWidget2::open()
+	{
+		if(Instance != nullptr)
+			return Instance;
+
+		EditorWindow* newWindow = EditorWindow::create();
+
+		DbgEditorWidget2* newWidget = new (cm_alloc<DbgEditorWidget2>()) DbgEditorWidget2();
+		newWindow->getWidgets().add(*newWidget);
+		newWidget->initialize();
+		Instance = newWidget;
+
+		return newWidget;
+	}
+
+	void DbgEditorWidget2::close()
+	{
+		if(Instance != nullptr)
+		{
+			if(!Instance->onClosed.empty()) // TODO - This is very hackish
+				Instance->onClosed(Instance);
+
+			EditorWidget::destroy(Instance);
+		}
+
+		Instance = nullptr; 
+	}
 }

+ 2 - 0
EditorWindowDock.txt

@@ -59,6 +59,8 @@ Implementation plan:
  GUIManager holds a ptr to GUIElements, and when elements are destroyed, GUIManager holds an invalid ptr
 Tabs need toggle groups
 Hook up DbgEditorWidget1::close()
+ - In EditorWidget instead of having a onClose callback, instead keep EditorWidgetContainer parent and then notify it before it is closed. And if the widget is closed by parent container, 
+   then the container can remove parent and then close it so it doesn't receive the callback.
 Test tab switch/tab close/window close
 
 ------------------------