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

Starting work on context menu and modifiying some wrong design choices with GUIMenu

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

+ 2 - 0
BansheeEngine/BansheeEngine.vcxproj

@@ -236,6 +236,7 @@
     <ClInclude Include="Include\BsGUIButton.h" />
     <ClInclude Include="Include\BsGUICommandEvent.h" />
     <ClInclude Include="Include\BsGUIContent.h" />
+    <ClInclude Include="Include\BsGUIContextMenu.h" />
     <ClInclude Include="Include\BsGUIDropDownBox.h" />
     <ClInclude Include="Include\BsGUIListBox.h" />
     <ClInclude Include="Include\BsGUIElementBase.h" />
@@ -284,6 +285,7 @@
     <ClInclude Include="Include\BsD3D9BuiltinMaterialFactory.h" />
     <ClInclude Include="Include\BsGLBuiltinMaterialFactory.h" />
     <ClInclude Include="Include\BsUpdateCallback.h" />
+    <ClInclude Include="Source\BsGUIContextMenu.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsApplication.cpp" />

+ 6 - 0
BansheeEngine/BansheeEngine.vcxproj.filters

@@ -210,6 +210,12 @@
     <ClInclude Include="Include\BsGUIMenu.h">
       <Filter>Header Files\GUI</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsGUIContextMenu.h">
+      <Filter>Header Files\GUI</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\BsGUIContextMenu.cpp">
+      <Filter>Source Files\GUI</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsGUIElement.cpp">

+ 12 - 0
BansheeEngine/Include/BsGUIContextMenu.h

@@ -0,0 +1,12 @@
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "BsGUIMenu.h"
+
+namespace BansheeEngine
+{
+	class BS_EXPORT GUIContextMenu : public GUIMenu
+	{
+
+	};
+}

+ 2 - 0
BansheeEngine/Include/BsGUIElement.h

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

+ 6 - 0
BansheeEngine/Include/BsGUIInputBox.h

@@ -56,6 +56,8 @@ namespace BansheeEngine
 
 		virtual CM::UINT32 _getRenderElementDepth(CM::UINT32 renderElementIdx) const;
 		virtual void _setFocus(bool focus);
+
+		virtual const GUIContextMenu* getContextMenu() const;
 	private:
 		// Sprites
 		ImageSprite* mImageSprite;
@@ -101,5 +103,9 @@ namespace BansheeEngine
 		CM::Int2 getTextOffset() const;
 		CM::Rect getTextClipRect() const;
 		TEXT_SPRITE_DESC getTextDesc() const;
+		
+		void cutText();
+		void copyText();
+		void pasteText();
 	};
 }

+ 3 - 0
BansheeEngine/Include/BsGUIManager.h

@@ -143,6 +143,9 @@ namespace BansheeEngine
 
 		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 closeContextMenu();
+
 		void closeDropDownListBox(CM::INT32 selectedIdx);
 		void closeDropDownBox();
 

+ 13 - 20
BansheeEngine/Include/BsGUIMenu.h

@@ -1,59 +1,52 @@
 #pragma once
 
 #include "BsPrerequisites.h"
-#include "BsGUIWidget.h"
 
 namespace BansheeEngine
 {
 	class BS_EXPORT GUIMenuItem
 	{
-		struct Comparer 
-		{
-			bool operator() (const GUIMenuItem* lhs, GUIMenuItem* rhs) const
-			{
-				return lhs->mName.compare(rhs->mName) < 0;
-			}
-		};
-
 	public:
 		GUIMenuItem(GUIMenuItem* parent, const CM::WString& name, std::function<void()> callback);
+		GUIMenuItem(GUIMenuItem* parent);
 		~GUIMenuItem();
 
-		void addChild(GUIMenuItem* child) { mChildren.insert(child); }
+		void addChild(GUIMenuItem* child) { mChildren.push_back(child); }
 
 		CM::UINT32 getNumChildren() const { return (CM::UINT32)mChildren.size(); }
 		const GUIMenuItem* getParent() const { return mParent; }
 		const CM::WString& getName() const { return mName; }
+		bool isSeparator() const { return mIsSeparator; }
 		const GUIMenuItem* findChild(const CM::WString& name) const;
 
-		CM::Set<GUIMenuItem*, Comparer>::type::const_iterator childBegin() const { return mChildren.cbegin(); }
-		CM::Set<GUIMenuItem*, Comparer>::type::const_iterator childEnd() const { return mChildren.cend(); }
 		void removeChild(const CM::WString& name);
 
 	private:
 		friend class GUIMenu;
 
 		GUIMenuItem* mParent;
+		bool mIsSeparator;
 		CM::WString mName;
 		std::function<void()> mCallback;
-		CM::Set<GUIMenuItem*, Comparer>::type mChildren;
+		CM::Vector<GUIMenuItem*>::type mChildren;
 
-		CM::Set<GUIMenuItem*, Comparer>::type::iterator findChildInternal(const CM::WString& name) const;
-		
+		CM::Vector<GUIMenuItem*>::type::const_iterator findChildInternal(const CM::WString& name) const;
+		CM::Vector<GUIMenuItem*>::type::iterator findChildInternal(const CM::WString& name);
 	};
 
-	class BS_EXPORT GUIMenu : public GUIWidget
+	class BS_EXPORT GUIMenu
 	{
 	public:
-		GUIMenu(const CM::HSceneObject& parent);
-		~GUIMenu();
+		GUIMenu();
+		virtual ~GUIMenu();
 
-		void addMenuItem(const CM::WString& path, std::function<void()> callback);
+		const GUIMenuItem* addMenuItem(const CM::WString& path, std::function<void()> callback);
+		const GUIMenuItem* addSeparator(const CM::WString& path);
 		const GUIMenuItem* getMenuItem(const CM::WString& path) const;
 		void removeMenuItem(const GUIMenuItem* item);
 	protected:
 		GUIMenuItem mRootElement;
 
-		void openMenu(const GUIMenuItem* menuItem, const CM::WString& subMenuName);
+		const GUIMenuItem* addMenuItemInternal(const CM::WString& path, std::function<void()> callback, bool isSeparator);
 	};
 }

+ 1 - 0
BansheeEngine/Include/BsPrerequisites.h

@@ -57,6 +57,7 @@ namespace BansheeEngine
 	class GUIDropDownBox;
 	class DragAndDropManager;
 	class GUIContent;
+	class GUIContextMenu;
 
 	// 2D
 	class TextSprite;

+ 8 - 0
BansheeEngine/Source/BsGUIContextMenu.cpp

@@ -0,0 +1,8 @@
+#include "BsGUIContextMenu.h"
+
+using namespace CamelotFramework;
+
+namespace BansheeEngine
+{
+
+}

+ 40 - 0
BansheeEngine/Source/BsGUIInputBox.cpp

@@ -16,6 +16,7 @@
 #include "BsGUIInputCaret.h"
 #include "BsGUIInputSelection.h"
 #include "BsDragAndDropManager.h"
+#include "BsGUIContextMenu.h"
 
 using namespace CamelotFramework;
 
@@ -836,4 +837,43 @@ namespace BansheeEngine
 
 		mHasFocus = focus;
 	}
+
+	const GUIContextMenu* GUIInputBox::getContextMenu() const
+	{
+		static bool initialized = false;
+		static GUIContextMenu mContextMenu;
+
+		if(!initialized)
+		{
+			mContextMenu.addMenuItem(L"Cut", boost::bind(&GUIInputBox::cutText, const_cast<GUIInputBox*>(this)));
+			mContextMenu.addMenuItem(L"Copy", boost::bind(&GUIInputBox::copyText, const_cast<GUIInputBox*>(this)));
+			mContextMenu.addMenuItem(L"Paste", boost::bind(&GUIInputBox::pasteText, const_cast<GUIInputBox*>(this)));
+
+			// DEBUG ONLY
+			
+			mContextMenu.addSeparator(L"");
+			mContextMenu.addMenuItem(L"DebugBox/Test1", boost::bind(&GUIInputBox::pasteText, const_cast<GUIInputBox*>(this)));
+			mContextMenu.addMenuItem(L"DebugBox/Test2", boost::bind(&GUIInputBox::pasteText, const_cast<GUIInputBox*>(this)));
+			mContextMenu.addMenuItem(L"Zzzz/Test1", boost::bind(&GUIInputBox::pasteText, const_cast<GUIInputBox*>(this)));
+
+			initialized = true;
+		}
+
+		return &mContextMenu;
+	}
+
+	void GUIInputBox::cutText()
+	{
+		// TODO
+	}
+
+	void GUIInputBox::copyText()
+	{
+		// TODO
+	}
+
+	void GUIInputBox::pasteText()
+	{
+		// TODO
+	}
 }

+ 29 - 0
BansheeEngine/Source/BsGUIManager.cpp

@@ -542,6 +542,26 @@ namespace BansheeEngine
 		mDropDownBoxActive = false;
 	}
 
+	void GUIManager::openContextMenu(const GUIContextMenu* menu, const Int2& position, GUIWidget& widget)
+	{
+		if(mDropDownBoxOpenScheduled || mDropDownBoxActive)
+			closeContextMenu();
+
+		mDropDownSO = SceneObject::create("DropDownBox");
+		mDropDownBox = mDropDownSO->addComponent<GUIDropDownBox>();
+
+		//Vector<GUIDropDownData>::type dropDownData = menu->getDropDownData();
+
+		//mDropDownBox->initialize(widget.getTarget(), widget.getOwnerWindow(), parentList, dropDownData, widget.getSkin(), GUIDropDownType::ContextMenu);
+
+		mDropDownBoxOpenScheduled = true;
+	}
+
+	void GUIManager::closeContextMenu()
+	{
+		closeDropDownBox();
+	}
+
 	void GUIManager::updateCaretTexture()
 	{
 		if(mCaretTexture == nullptr)
@@ -644,6 +664,15 @@ namespace BansheeEngine
 				event.markAsUsed();
 			}
 
+			// If right click try to open context menu
+			if(mMouseOverElement != nullptr && buttonStates[2] == true) 
+			{
+				const GUIContextMenu* menu = mMouseOverElement->getContextMenu();
+
+				if(menu != nullptr)
+					openContextMenu(menu, gInput().getMousePosition(), *mMouseOverWidget);
+			}
+
 			// Close drop down box(es) if user clicks outside of one
 			if(mDropDownBoxActive)
 			{

+ 39 - 19
BansheeEngine/Source/BsGUIMenu.cpp

@@ -5,7 +5,13 @@ using namespace CamelotFramework;
 namespace BansheeEngine
 {
 	GUIMenuItem::GUIMenuItem(GUIMenuItem* parent, const CM::WString& name, std::function<void()> callback)
-		:mParent(parent), mName(name), mCallback(callback)
+		:mParent(parent), mName(name), mCallback(callback), mIsSeparator(false)
+	{
+
+	}
+
+	GUIMenuItem::GUIMenuItem(GUIMenuItem* parent)
+		:mParent(parent), mCallback(nullptr), mIsSeparator(true)
 	{
 
 	}
@@ -37,14 +43,18 @@ namespace BansheeEngine
 		}
 	}
 
-	CM::Set<GUIMenuItem*, GUIMenuItem::Comparer>::type::iterator GUIMenuItem::findChildInternal(const CM::WString& name) const
+	CM::Vector<GUIMenuItem*>::type::const_iterator GUIMenuItem::findChildInternal(const CM::WString& name) const
+	{
+		return std::find_if(begin(mChildren), end(mChildren), [&] (GUIMenuItem* x) { return x->getName() == name; });
+	}
+
+	CM::Vector<GUIMenuItem*>::type::iterator GUIMenuItem::findChildInternal(const CM::WString& name)
 	{
-		GUIMenuItem tempItem(nullptr, name, nullptr);
-		return mChildren.find(&tempItem);
+		return std::find_if(begin(mChildren), end(mChildren), [&] (GUIMenuItem* x) { return x->getName() == name; });
 	}
 
-	GUIMenu::GUIMenu(const CM::HSceneObject& parent)
-		:GUIWidget(parent), mRootElement(nullptr, L"", nullptr)
+	GUIMenu::GUIMenu()
+		:mRootElement(nullptr, L"", nullptr)
 	{
 
 	}
@@ -54,7 +64,17 @@ namespace BansheeEngine
 
 	}
 
-	void GUIMenu::addMenuItem(const CM::WString& path, std::function<void()> callback)
+	const GUIMenuItem* GUIMenu::addMenuItem(const CM::WString& path, std::function<void()> callback)
+	{
+		return addMenuItemInternal(path, callback, false);
+	}
+
+	const GUIMenuItem* GUIMenu::addSeparator(const CM::WString& path)
+	{
+		return addMenuItemInternal(path, nullptr, true);
+	}
+
+	const GUIMenuItem* GUIMenu::addMenuItemInternal(const CM::WString& path, std::function<void()> callback, bool isSeparator)
 	{
 		Vector<WString>::type pathElements = StringUtil::split(path, L"/");
 
@@ -76,7 +96,7 @@ namespace BansheeEngine
 					const WString& nextPathElem = *(pathElements.begin() + i);
 
 					existingItem = cm_alloc<GUIMenuItem, PoolAlloc>();
-					existingItem = new (existingItem) GUIMenuItem(curSubMenu, pathElem, boost::bind(&GUIMenu::openMenu, this, existingItem, nextPathElem));
+					existingItem = new (existingItem) GUIMenuItem(curSubMenu, pathElem, nullptr);
 				}
 
 				curSubMenu->addChild(existingItem);
@@ -84,6 +104,16 @@ namespace BansheeEngine
 
 			curSubMenu = existingItem;
 		}
+
+		if(isSeparator)
+		{
+			GUIMenuItem* separatorItem = cm_new<GUIMenuItem, PoolAlloc>(curSubMenu);
+			curSubMenu->addChild(separatorItem);
+
+			return separatorItem;
+		}
+
+		return curSubMenu;
 	}
 
 	const GUIMenuItem* GUIMenu::getMenuItem(const CM::WString& path) const
@@ -97,7 +127,7 @@ namespace BansheeEngine
 			auto foundChild = curSubMenu->findChildInternal(pathElem);
 			GUIMenuItem* existingItem = *foundChild;
 
-			if(existingItem == nullptr)
+			if(existingItem == nullptr || existingItem->isSeparator())
 				return nullptr;
 
 			curSubMenu = existingItem;
@@ -113,14 +143,4 @@ namespace BansheeEngine
 
 		parent->removeChild(item->getName());
 	}
-
-	void GUIMenu::openMenu(const GUIMenuItem* menuItem, const WString& subMenuName)
-	{
-		assert(menuItem != nullptr);
-
-		const GUIMenuItem* subMenu = menuItem->findChild(subMenuName);
-		assert(subMenu != nullptr);
-
-		// TODO
-	}
 }

+ 2 - 5
DropDown.txt

@@ -5,11 +5,8 @@ Doesn't belong here but as an additional reminder: Remove Component::initialize
 GUIDropDownBox::openSubMenu
  - I need to provide a parent element, also I probably need a way to distinguish between original drop down box, and sub-menu one
 
-GUIContextMenu
- - GUIElements may specify a context menu, which is retrieved by GUIManager using GUIElement::getContextMenu
- - Menu is opened at the current mouse position (determined by GUIManager)
- - GUIContextMenu::open(x, y)
-   - GUIDropDownBox is created with the root elements data
+GUIContextMenu::getDropDownData needs to be implemented
+GUIDropDownBox only accepts a GUIElement, while GUIContextMenu only specifies a position
 
 GUIMenuBar
  - It has a specific position, width and height