Browse Source

Added toolbar management code to C#

BearishSun 10 years ago
parent
commit
6aef09fdde

+ 2 - 2
BansheeEditor/Include/BsGUIMenuBar.h

@@ -129,7 +129,7 @@ namespace BansheeEngine
 		/**
 		 * @brief	Adds a new button to the tool bar.
 		 *
-		 * @param	name		Unique name of the button that you may use for identifiying it.
+		 * @param	name		Unique name of the button that can be used for identifiying it.
 		 * @param	content 	Content to display on the button.
 		 * @param	callback	Callback to trigger when the button is pressed.
 		 * @param	priority	Determines where is the button positioned compared to other elements on the tool bar.
@@ -140,7 +140,7 @@ namespace BansheeEngine
 		/**
 		 * @brief	Adds a new separator element to the tool bar.
 		 *
-		 * @param	name		Unique name of the separator that you may use for identifiying it.
+		 * @param	name		Unique name of the separator that can be used for identifiying it.
 		 * @param	priority	Determines where is the separator positioned compared to other elements on the tool bar.
 		 * 						Higher priority elements get placed before lower priority ones.
 		 */

+ 13 - 5
MBansheeEditor/Library/LibraryWindow.cs

@@ -30,7 +30,7 @@ namespace BansheeEditor
 
         private const int DRAG_SCROLL_HEIGHT = 20;
         private const int DRAG_SCROLL_AMOUNT_PER_SECOND = 100;
-        private const int FOLDER_BUTTON_WIDTH = 20;
+        private const int FOLDER_BUTTON_WIDTH = 30;
         private const int FOLDER_SEPARATOR_WIDTH = 10;
         
         private bool hasContentFocus = false;
@@ -159,10 +159,14 @@ namespace BansheeEditor
             searchBarLayout = contentLayout.AddLayoutX();
             searchField = new GUITextField();
             searchField.OnChanged += OnSearchChanged;
-            GUIButton clearSearchBtn = new GUIButton("C");
+
+            GUIContent clearIcon = new GUIContent(EditorBuiltin.GetLibraryWindowIcon(LibraryWindowIcon.Clear));
+            GUIButton clearSearchBtn = new GUIButton(clearIcon);
             clearSearchBtn.OnClick += ClearSearch;
             clearSearchBtn.SetWidth(40);
-            optionsButton = new GUIButton("O");
+
+            GUIContent optionsIcon = new GUIContent(EditorBuiltin.GetLibraryWindowIcon(LibraryWindowIcon.Options));
+            optionsButton = new GUIButton(optionsIcon);
             optionsButton.OnClick += OpenOptionsWindow;
             optionsButton.SetWidth(40);
             searchBarLayout.AddElement(searchField);
@@ -170,9 +174,13 @@ namespace BansheeEditor
             searchBarLayout.AddElement(optionsButton);
 
             folderBarLayout = contentLayout.AddLayoutX();
-            GUIButton homeButton = new GUIButton("H", GUIOption.FixedWidth(FOLDER_BUTTON_WIDTH));
+
+            GUIContent homeIcon = new GUIContent(EditorBuiltin.GetLibraryWindowIcon(LibraryWindowIcon.Home));
+            GUIButton homeButton = new GUIButton(homeIcon, GUIOption.FixedWidth(FOLDER_BUTTON_WIDTH));
             homeButton.OnClick += OnHomeClicked;
-            GUIButton upButton = new GUIButton("U", GUIOption.FixedWidth(FOLDER_BUTTON_WIDTH));
+
+            GUIContent upIcon = new GUIContent(EditorBuiltin.GetLibraryWindowIcon(LibraryWindowIcon.Up));
+            GUIButton upButton = new GUIButton(upIcon, GUIOption.FixedWidth(FOLDER_BUTTON_WIDTH));
             upButton.OnClick += OnUpClicked;
 
             folderBarLayout.AddElement(homeButton);

+ 1 - 0
MBansheeEditor/MBansheeEditor.csproj

@@ -134,6 +134,7 @@
     <Compile Include="Scene\ScaleHandle.cs" />
     <Compile Include="ScriptCompiler.cs" />
     <Compile Include="Selection.cs" />
+    <Compile Include="ToolbarItem.cs" />
     <Compile Include="UndoRedo.cs" />
     <Compile Include="UnitTests.cs" />
     <Compile Include="UnitTestTypes.cs" />

+ 24 - 10
MBansheeEditor/Scene/SceneWindow.cs

@@ -102,24 +102,38 @@ namespace BansheeEditor
         {
             mainLayout = GUI.AddLayoutY();
 
+            GUIContent viewIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.View));
+            GUIContent moveIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Move));
+            GUIContent rotateIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Rotate));
+            GUIContent scaleIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Scale));
+
+            GUIContent localIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Local));
+            GUIContent worldIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.World));
+
+            GUIContent pivotIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Pivot));
+            GUIContent centerIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Center));
+
+            GUIContent moveSnapIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.MoveSnap));
+            GUIContent rotateSnapIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.RotateSnap));
+
             GUIToggleGroup handlesTG = new GUIToggleGroup();
-            viewButton = new GUIToggle("V", handlesTG, EditorStyles.Button);
-            moveButton = new GUIToggle("M", handlesTG, EditorStyles.Button);
-            rotateButton = new GUIToggle("R", handlesTG, EditorStyles.Button);
-            scaleButton = new GUIToggle("S", handlesTG, EditorStyles.Button);
+            viewButton = new GUIToggle(viewIcon, handlesTG, EditorStyles.Button);
+            moveButton = new GUIToggle(moveIcon, handlesTG, EditorStyles.Button);
+            rotateButton = new GUIToggle(rotateIcon, handlesTG, EditorStyles.Button);
+            scaleButton = new GUIToggle(scaleIcon, handlesTG, EditorStyles.Button);
 
             GUIToggleGroup coordModeTG = new GUIToggleGroup();
-            localCoordButton = new GUIToggle("L", coordModeTG, EditorStyles.Button);
-            worldCoordButton = new GUIToggle("W", coordModeTG, EditorStyles.Button);
+            localCoordButton = new GUIToggle(localIcon, coordModeTG, EditorStyles.Button);
+            worldCoordButton = new GUIToggle(worldIcon, coordModeTG, EditorStyles.Button);
 
             GUIToggleGroup pivotModeTG = new GUIToggleGroup();
-            pivotButton = new GUIToggle("P", pivotModeTG, EditorStyles.Button);
-            centerButton = new GUIToggle("C", pivotModeTG, EditorStyles.Button);
+            pivotButton = new GUIToggle(pivotIcon, pivotModeTG, EditorStyles.Button);
+            centerButton = new GUIToggle(centerIcon, pivotModeTG, EditorStyles.Button);
 
-            moveSnapButton = new GUIToggle("MS", EditorStyles.Button);
+            moveSnapButton = new GUIToggle(moveSnapIcon, EditorStyles.Button);
             moveSnapInput = new GUIFloatField();
 
-            rotateSnapButton = new GUIToggle("RS", EditorStyles.Button);
+            rotateSnapButton = new GUIToggle(rotateSnapIcon, EditorStyles.Button);
             rotateSnapInput = new GUIFloatField();
 
             viewButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.View);

+ 42 - 0
MBansheeEditor/ToolbarItem.cs

@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Adds an entry to the main tool. Clicking on that entry will trigger the method the attribute is attached to.
+    /// The method must be static, have no parameters or return values otherwise the attribute will be ignored.
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Method)]
+    class ToolbarItem : Attribute
+    {
+        /// <summary>
+        /// Creates a new toolbar item attribute. Selecting the item in the toolbar will trigger the callback.
+        /// </summary>
+        /// <param name="name">Path that determines where in the menu to add the element. All path elements must be 
+        ///                    separated by /, e.g. "View/Toolbars/Find"</param>
+        /// <param name="icon">Icon to display on the toolbar entry.</param>
+        /// <param name="tooltip">Optional tooltip to display when the user hovers over the toolbar entry.</param>
+        /// <param name="priority">Priority determines the position of the toolbar item relative to its siblings.
+        ///                        Higher priority means it will be placed earlier.</param>
+        /// <param name="separator">Determines should a separator be inserted just before this element.</param>
+        public ToolbarItem(string name, SpriteTexture icon, LocString tooltip = null, int priority = 0, bool separator = false)
+        {
+            this.name = name;
+            this.icon = icon;
+            this.tooltip = tooltip;
+            this.priority = priority;
+            this.separator = separator;
+        }
+
+        private string name;
+        private SpriteTexture icon;
+        private LocString tooltip;
+        private int priority;
+        private bool separator;
+    }
+}

+ 66 - 0
SBansheeEditor/Include/BsToolbarItemManager.h

@@ -0,0 +1,66 @@
+#pragma once
+
+#include "BsScriptEditorPrerequisites.h"
+#include "BsModule.h"
+
+namespace BansheeEngine
+{
+	/**
+	 * @brief	Tracks toolbar items that are registered in managed code using the ToolbarItem
+	 *			attribute.
+	 */
+	class BS_SCR_BED_EXPORT ToolbarItemManager : public Module<ToolbarItemManager>
+	{
+	public:
+		ToolbarItemManager(ScriptAssemblyManager& scriptObjectManager);
+		~ToolbarItemManager();
+
+	private:
+		/**
+		 * @brief	Removes all managed toolbar items from the main menu.
+		 */
+		void clearToolbarItems();
+
+		/**
+		 * @brief	Reloads all assembly types and attempts to find uses of ToolbarItem. Old
+		 *			toolbar items are cleared and new are added.
+		 */
+		void reloadAssemblyData();
+
+		/**
+		 * @brief	Parse the provided method and detect whether it has a ToolbarItem attribute.
+		 *			If it has extract needed data from it.
+		 *
+		 * @param	method		Managed method to parse.
+		 * @param	name		Output name defined in the ToolbarItem attribute.
+		 * @param	icon		Texture defined in the ToolbarItem attribute.
+		 * @param	tooltip		Optional tooltip defined in the ToolbarItem attribute.
+		 * @param	priority	Menu item priority defined in the MenuItem attribute.
+		 * @param	separator	Should the separator be inserted before the menu item.
+		 *
+		 * @return	True if the method has a ToolbarItem attribute. If false is returned output parameters
+		 *			from this method are undefined.
+		 */
+		bool parseToolbarItemMethod(MonoMethod* method, String& name, HSpriteTexture& icon, HString& tooltip,
+			INT32& priority, bool& separator) const;
+
+		/**
+		 * @brief	Triggered when one of the managed toolbar items is clicked. 
+		 *
+		 * @param	method	Managed method that the ToolbarItem is referencing.
+		 */
+		static void toolbarItemCallback(MonoMethod* method);
+
+		ScriptAssemblyManager& mScriptObjectManager;
+		HEvent mDomainLoadedConn;
+
+		MonoClass* mToolbarItemAttribute;
+		MonoField* mNameField;
+		MonoField* mIconField;
+		MonoField* mTooltipField;
+		MonoField* mPriorityField;
+		MonoField* mSeparatorField;
+
+		Vector<String> mToolbarItems;
+	};
+}

+ 2 - 0
SBansheeEditor/SBansheeEditor.vcxproj

@@ -288,6 +288,7 @@
     <ClInclude Include="Include\BsScriptSelection.h" />
     <ClInclude Include="Include\BsScriptUndoRedo.h" />
     <ClInclude Include="Include\BsScriptUnitTests.h" />
+    <ClInclude Include="Include\BsToolbarItemManager.h" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsEditorResourceLoader.cpp" />
@@ -346,6 +347,7 @@
     <ClCompile Include="Source\BsScriptSelection.cpp" />
     <ClCompile Include="Source\BsScriptUndoRedo.cpp" />
     <ClCompile Include="Source\BsScriptUnitTests.cpp" />
+    <ClCompile Include="Source\BsToolbarItemManager.cpp" />
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">

+ 6 - 0
SBansheeEditor/SBansheeEditor.vcxproj.filters

@@ -183,6 +183,9 @@
     <ClInclude Include="Include\BsScriptGUISliderField.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsToolbarItemManager.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsScriptEditorPlugin.cpp">
@@ -353,5 +356,8 @@
     <ClCompile Include="Source\BsScriptGUISliderField.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsToolbarItemManager.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 3 - 0
SBansheeEditor/Source/BsEditorScriptManager.cpp

@@ -11,6 +11,7 @@
 #include "BsScriptDragDropManager.h"
 #include "BsScriptProjectLibrary.h"
 #include "BsMenuItemManager.h"
+#include "BsToolbarItemManager.h"
 #include "BsScriptFolderMonitor.h"
 #include "BsScriptEditorTestSuite.h"
 #include "BsTime.h"
@@ -42,6 +43,7 @@ namespace BansheeEngine
 		ScriptDragDropManager::startUp();
 		ScriptProjectLibrary::startUp();
 		MenuItemManager::startUp(ScriptAssemblyManager::instance());
+		ToolbarItemManager::startUp(ScriptAssemblyManager::instance());
 		ScriptFolderMonitorManager::startUp();
 		ScriptSelection::startUp();
 		ScriptInspectorUtility::startUp();
@@ -74,6 +76,7 @@ namespace BansheeEngine
 		ScriptInspectorUtility::shutDown();
 		ScriptSelection::shutDown();
 		ScriptFolderMonitorManager::shutDown();
+		ToolbarItemManager::shutDown();
 		MenuItemManager::shutDown();
 		ScriptProjectLibrary::shutDown();
 		ScriptDragDropManager::shutDown();

+ 1 - 1
SBansheeEditor/Source/BsMenuItemManager.cpp

@@ -18,7 +18,7 @@ namespace BansheeEngine
 {
 	MenuItemManager::MenuItemManager(ScriptAssemblyManager& scriptObjectManager)
 		:mScriptObjectManager(scriptObjectManager), mMenuItemAttribute(nullptr), mPathField(nullptr), 
-		mShortcutField(nullptr), mPriorityField(nullptr)
+		mShortcutField(nullptr), mPriorityField(nullptr), mSeparatorField(nullptr)
 	{
 		mDomainLoadedConn = ScriptObjectManager::instance().onRefreshDomainLoaded.connect(std::bind(&MenuItemManager::reloadAssemblyData, this));
 		reloadAssemblyData();

+ 147 - 0
SBansheeEditor/Source/BsToolbarItemManager.cpp

@@ -0,0 +1,147 @@
+#include "BsToolbarItemManager.h"
+#include "BsScriptAssemblyManager.h"
+#include "BsMonoAssembly.h"
+#include "BsMonoClass.h"
+#include "BsMonoMethod.h"
+#include "BsMonoField.h"
+#include "BsMonoManager.h"
+#include "BsMonoUtil.h"
+#include "BsScriptObjectManager.h"
+#include "BsEditorWindowManager.h"
+#include "BsMainEditorWindow.h"
+#include "BsGUIMenuBar.h"
+#include "BsGUIContent.h"
+#include "BsScriptSpriteTexture.h"
+#include "BsScriptHString.h"
+
+using namespace std::placeholders;
+
+namespace BansheeEngine
+{
+	ToolbarItemManager::ToolbarItemManager(ScriptAssemblyManager& scriptObjectManager)
+		:mScriptObjectManager(scriptObjectManager), mToolbarItemAttribute(nullptr), mNameField(nullptr),
+		mIconField(nullptr), mPriorityField(nullptr), mTooltipField(nullptr), mSeparatorField(nullptr)
+	{
+		mDomainLoadedConn = ScriptObjectManager::instance().onRefreshDomainLoaded.connect(std::bind(&ToolbarItemManager::reloadAssemblyData, this));
+		reloadAssemblyData();
+	}
+
+	ToolbarItemManager::~ToolbarItemManager()
+	{
+		mDomainLoadedConn.disconnect();
+	}
+
+	void ToolbarItemManager::clearToolbarItems()
+	{
+		MainEditorWindow* mainWindow = EditorWindowManager::instance().getMainWindow();
+
+		for (auto& toolbarItem : mToolbarItems)
+			mainWindow->getMenuBar().removeToolBarButton(toolbarItem);
+	}
+
+	void ToolbarItemManager::reloadAssemblyData()
+	{
+		clearToolbarItems();
+
+		// Reload MenuItem attribute from editor assembly
+		MonoAssembly* editorAssembly = MonoManager::instance().getAssembly(EDITOR_ASSEMBLY);
+		mToolbarItemAttribute = editorAssembly->getClass("BansheeEditor", "ToolbarItem");
+		if (mToolbarItemAttribute == nullptr)
+			BS_EXCEPT(InvalidStateException, "Cannot find ToolbarItem managed class.");
+
+		mNameField = mToolbarItemAttribute->getField("name");
+		mIconField = mToolbarItemAttribute->getField("icon");
+		mTooltipField = mToolbarItemAttribute->getField("tooltip");
+		mPriorityField = mToolbarItemAttribute->getField("priority");
+		mSeparatorField = mToolbarItemAttribute->getField("separator");
+
+		MainEditorWindow* mainWindow = EditorWindowManager::instance().getMainWindow();
+
+		Vector<String> scriptAssemblyNames = mScriptObjectManager.getScriptAssemblies();
+		for (auto& assemblyName : scriptAssemblyNames)
+		{
+			MonoAssembly* assembly = MonoManager::instance().getAssembly(assemblyName);
+
+			// Find new menu item methods
+			const Vector<MonoClass*>& allClasses = assembly->getAllClasses();
+			for (auto curClass : allClasses)
+			{
+				const Vector<MonoMethod*>& methods = curClass->getAllMethods();
+				for (auto& curMethod : methods)
+				{
+					String name;
+					HSpriteTexture icon;
+					HString tooltip;
+					INT32 priority = 0;
+					bool separator = false;
+					if (parseToolbarItemMethod(curMethod, name, icon, tooltip, priority, separator))
+					{
+						std::function<void()> callback = std::bind(&ToolbarItemManager::toolbarItemCallback, curMethod);
+
+						if (separator)
+						{
+							String sepName = "s__" + name;
+
+							mainWindow->getMenuBar().addToolBarSeparator(sepName, priority);
+							mToolbarItems.push_back(sepName);
+						}
+
+						GUIContent content(icon, tooltip);
+						mainWindow->getMenuBar().addToolBarButton(name, content, callback, priority);
+						mToolbarItems.push_back(name);
+					}
+				}
+			}
+		}
+	}
+
+	bool ToolbarItemManager::parseToolbarItemMethod(MonoMethod* method, String& name, HSpriteTexture& icon, HString& tooltip,
+		INT32& priority, bool& separator) const
+	{
+		if (!method->hasAttribute(mToolbarItemAttribute))
+			return false;
+
+		if (method->getNumParameters() != 0)
+			return false;
+
+		if (!method->isStatic())
+			return false;
+
+		MonoObject* toolbarItemAttrib = method->getAttribute(mToolbarItemAttribute);
+
+		MonoString* monoName;
+		mNameField->getValue(toolbarItemAttrib, &monoName);
+		name = MonoUtil::monoToString(monoName);
+
+		MonoObject* monoTexture;
+		mIconField->getValue(toolbarItemAttrib, &monoTexture);
+
+		if (monoTexture != nullptr)
+		{
+			ScriptSpriteTexture* scriptTexture = ScriptSpriteTexture::toNative(monoTexture);
+			if (scriptTexture != nullptr)
+				icon = scriptTexture->getInternalValue();
+		}
+
+		MonoObject* tooltipMono;
+		mTooltipField->getValue(toolbarItemAttrib, &tooltipMono);
+
+		if (tooltipMono == nullptr)
+			tooltip = HString::dummy();
+		else
+		{
+			ScriptHString* tooltipScript = ScriptHString::toNative(tooltipMono);
+			tooltip = tooltipScript->getInternalValue();
+		}
+		
+		mPriorityField->getValue(toolbarItemAttrib, &priority);
+		mSeparatorField->getValue(toolbarItemAttrib, &separator);
+
+		return true;
+	}
+
+	void ToolbarItemManager::toolbarItemCallback(MonoMethod* method)
+	{
+		method->invoke(nullptr, nullptr);
+	}
+}