فهرست منبع

Started work on rename functionality of TreeView
Changed how keyboard focus works in GUIManager

Marko Pintera 12 سال پیش
والد
کامیت
571341f9e5

+ 1 - 1
BansheeEngine/Include/BsGUICommandEvent.h

@@ -7,7 +7,7 @@ namespace BansheeEngine
 	enum class GUICommandEventType
 	{
 		Redraw, CursorMoveLeft, CursorMoveRight, CursorMoveUp, CursorMoveDown, 
-		SelectLeft, SelectRight, SelectUp, SelectDown, Undo, Redo,
+		SelectLeft, SelectRight, SelectUp, SelectDown, Undo, Redo, Rename,
 		Escape, Delete, Backspace, Return, SelectAll, Copy, Cut, Paste, Tab
 	};
 

+ 1 - 4
BansheeEngine/Include/BsGUIElement.h

@@ -11,7 +11,7 @@ namespace BansheeEngine
 	class BS_EXPORT GUIElement : public GUIElementBase
 	{
 	public:
-		GUIElement(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions, bool acceptsKeyboardFocus = true);
+		GUIElement(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions);
 		virtual ~GUIElement();
 
 		/**
@@ -95,7 +95,6 @@ namespace BansheeEngine
 		void _setWidth(CM::UINT32 width);
 		void _setHeight(CM::UINT32 height);
 		void _setClipRect(const CM::RectI& clipRect);
-		void _setAcceptsKeyboardFocus(bool acceptsKeyboardFocus) { mAcceptsKeyboardFocus = acceptsKeyboardFocus; }
 		virtual void _setFocus(bool focus) {}
 		virtual void _changeParentWidget(GUIWidget* widget);
 
@@ -111,7 +110,6 @@ namespace BansheeEngine
 		CM::UINT32 _getDepth() const { return mDepth; }
 		GUIWidget& _getParentWidget() const { return *mParent; }
 		virtual bool _isInBounds(const CM::Vector2I position) const;
-		bool _acceptsKeyboardFocus() const { return mAcceptsKeyboardFocus; }
 
 		virtual GUIContextMenu* getContextMenu() const { return nullptr; }
 
@@ -143,7 +141,6 @@ namespace BansheeEngine
 		GUILayoutOptions mLayoutOptions;
 		CM::RectI mClippedBounds;
 
-		bool mAcceptsKeyboardFocus;
 		CM::UINT32 mDepth;
 		CM::Vector2I mOffset;
 		CM::UINT32 mWidth, mHeight;

+ 6 - 8
BansheeEngine/Include/BsGUIInputBox.h

@@ -21,7 +21,8 @@ namespace BansheeEngine
 
 		virtual CM::Vector2I _getOptimalSize() const;
 	protected:
-		~GUIInputBox();
+		GUIInputBox(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions, bool multiline);
+		virtual ~GUIInputBox();
 
 		/**
 		 * @copydoc GUIElement::getNumRenderElements()
@@ -54,6 +55,10 @@ namespace BansheeEngine
 		 */
 		virtual void updateClippedBounds();
 
+		virtual bool mouseEvent(const GUIMouseEvent& ev);
+		virtual bool textInputEvent(const GUITextInputEvent& ev);
+		virtual bool commandEvent(const GUICommandEvent& ev);
+
 		virtual CM::Vector2I _getTextInputOffset() const;
 		virtual CM::RectI _getTextInputRect() const;
 
@@ -79,13 +84,6 @@ namespace BansheeEngine
 		bool mInputCursorSet;
 		bool mDragInProgress;
 
-		GUIInputBox(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions, bool multiline);
-
-		virtual bool mouseEvent(const GUIMouseEvent& ev);
-		virtual bool textInputEvent(const GUITextInputEvent& ev);
-
-		virtual bool commandEvent(const GUICommandEvent& ev);
-
 		Sprite* renderElemToSprite(CM::UINT32 renderElemIdx, CM::UINT32& localRenderElemIdx) const;
 		CM::Vector2I renderElemToOffset(CM::UINT32 renderElemIdx) const;
 		CM::RectI renderElemToClipRect(CM::UINT32 renderElemIdx) const;

+ 2 - 2
BansheeEngine/Include/BsGUIManager.h

@@ -147,8 +147,8 @@ namespace BansheeEngine
 		CM::Vector<ElementInfo>::type mNewActiveElements;
 
 		// Element and widget that currently have the keyboard focus
-		GUIWidget* mKeyboardFocusWidget;
-		GUIElement* mKeyboardFocusElement;
+		CM::Vector<ElementInfo>::type mElementsInFocus;
+		CM::Vector<ElementInfo>::type mNewElementsInFocus;
 
 		GUIInputCaret* mInputCaret;
 		GUIInputSelection* mInputSelection;

+ 2 - 2
BansheeEngine/Source/BsGUIElement.cpp

@@ -9,9 +9,9 @@ using namespace CamelotFramework;
 
 namespace BansheeEngine
 {
-	GUIElement::GUIElement(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions, bool acceptsKeyboardFocus)
+	GUIElement::GUIElement(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions)
 		:mParent(&parent), mLayoutOptions(layoutOptions), mWidth(0), mHeight(0), mDepth(0), mStyle(style),
-		mAcceptsKeyboardFocus(acceptsKeyboardFocus), mIsDestroyed(false)
+		mIsDestroyed(false)
 	{
 		mParent->registerElement(this);
 	}

+ 1 - 1
BansheeEngine/Source/BsGUIElementContainer.cpp

@@ -7,7 +7,7 @@ using namespace BansheeEngine;
 namespace BansheeEngine
 {
 	GUIElementContainer::GUIElementContainer(GUIWidget& parent, const GUILayoutOptions& layoutOptions)
-		:GUIElement(parent, &GUISkin::DefaultStyle, layoutOptions, false)
+		:GUIElement(parent, &GUISkin::DefaultStyle, layoutOptions)
 	{ }
 
 	GUIElementContainer::~GUIElementContainer()

+ 80 - 45
BansheeEngine/Source/BsGUIManager.cpp

@@ -62,7 +62,7 @@ namespace BansheeEngine
 	const UINT32 GUIManager::MESH_HEAP_INITIAL_NUM_INDICES = 49152;
 
 	GUIManager::GUIManager()
-		:mSeparateMeshesByWidget(true), mActiveMouseButton(GUIMouseButton::Left), mKeyboardFocusElement(nullptr), mKeyboardFocusWidget(nullptr),
+		:mSeparateMeshesByWidget(true), mActiveMouseButton(GUIMouseButton::Left),
 		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), mSelectiveInputActive(false), mDragState(DragState::NoDrag)
 	{
@@ -144,35 +144,43 @@ namespace BansheeEngine
 
 	void GUIManager::unregisterWidget(GUIWidget* widget)
 	{
-		auto findIter = std::find_if(begin(mWidgets), end(mWidgets), [=] (const WidgetInfo& x) { return x.widget == widget; } );
-
-		if(findIter != end(mWidgets))
 		{
-			mWidgets.erase(findIter);
+			auto findIter = std::find_if(begin(mWidgets), end(mWidgets), [=] (const WidgetInfo& x) { return x.widget == widget; } );
+
+			if(findIter != mWidgets.end())
+				mWidgets.erase(findIter);
 		}
 
-		auto findIter2 = std::find_if(begin(mElementsUnderCursor), end(mElementsUnderCursor), [=] (const ElementInfo& x) { return x.widget == widget; } );
+		{
+			auto findIter = std::find_if(begin(mElementsUnderCursor), end(mElementsUnderCursor), [=] (const ElementInfo& x) { return x.widget == widget; } );
 
-		if(findIter2 != mElementsUnderCursor.end())
-			mElementsUnderCursor.erase(findIter2);
+			if(findIter != mElementsUnderCursor.end())
+				mElementsUnderCursor.erase(findIter);
+		}
 
-		if(mKeyboardFocusWidget == widget)
 		{
-			mKeyboardFocusWidget = nullptr;
-			mKeyboardFocusElement = nullptr;
+			auto findIter = std::find_if(begin(mElementsInFocus), end(mElementsInFocus), [=] (const ElementInfo& x) { return x.widget == widget; } );
+
+			if(findIter != mElementsInFocus.end())
+				mElementsInFocus.erase(findIter);
 		}
 
-		auto findIter4 = std::find_if(begin(mActiveElements), end(mActiveElements), [=] (const ElementInfo& x) { return x.widget == widget; } );
+		{
+			auto findIter = std::find_if(begin(mActiveElements), end(mActiveElements), [=] (const ElementInfo& x) { return x.widget == widget; } );
 
-		if(findIter4 != mActiveElements.end())
-			mActiveElements.erase(findIter4);
+			if(findIter != mActiveElements.end())
+				mActiveElements.erase(findIter);
+		}
 
 		const Viewport* renderTarget = widget->getTarget();
 		GUIRenderData& renderData = mCachedGUIData[renderTarget];
 
-		auto findIter3 = std::find(begin(renderData.widgets), end(renderData.widgets), widget);
-		if(findIter3 != end(renderData.widgets))
-			renderData.widgets.erase(findIter3);
+		{
+			auto findIter = std::find(begin(renderData.widgets), end(renderData.widgets), widget);
+			
+			if(findIter != end(renderData.widgets))
+				renderData.widgets.erase(findIter);
+		}
 
 		if(renderData.widgets.size() == 0)
 		{
@@ -195,19 +203,19 @@ namespace BansheeEngine
 		gProfiler().endSample("UpdateLayout");
 
 		// Blink caret
-		if(mKeyboardFocusElement != nullptr)
-		{
-			float curTime = gTime().getTime();
+		float curTime = gTime().getTime();
 
-			if((curTime - mCaretLastBlinkTime) >= mCaretBlinkInterval)
-			{
-				mCaretLastBlinkTime = curTime;
-				mIsCaretOn = !mIsCaretOn;
+		if((curTime - mCaretLastBlinkTime) >= mCaretBlinkInterval)
+		{
+			mCaretLastBlinkTime = curTime;
+			mIsCaretOn = !mIsCaretOn;
 
-				mCommandEvent = GUICommandEvent();
-				mCommandEvent.setType(GUICommandEventType::Redraw);
+			mCommandEvent = GUICommandEvent();
+			mCommandEvent.setType(GUICommandEventType::Redraw);
 
-				sendCommandEvent(mKeyboardFocusWidget, mKeyboardFocusElement, mCommandEvent);
+			for(auto& elementInfo : mElementsInFocus)
+			{
+				sendCommandEvent(elementInfo.widget, elementInfo.element, mCommandEvent);
 			}
 		}
 
@@ -231,12 +239,15 @@ namespace BansheeEngine
 
 		mActiveElements.swap(mNewActiveElements);
 
-		if(mKeyboardFocusElement != nullptr && mKeyboardFocusElement->_isDestroyed())
+		mNewElementsInFocus.clear();
+		for(auto& elementInfo : mElementsInFocus)
 		{
-			mKeyboardFocusElement = nullptr;
-			mKeyboardFocusWidget = nullptr;
+			if(!elementInfo.element->_isDestroyed())
+				mNewElementsInFocus.push_back(elementInfo);
 		}
 
+		mElementsInFocus.swap(mNewElementsInFocus);
+
 		processDestroyQueue();
 	}
 
@@ -817,23 +828,41 @@ namespace BansheeEngine
 			mActiveElements.swap(mNewActiveElements);
 		}
 
+		mNewElementsInFocus.clear();
+
+		// Determine elements that gained focus
 		for(auto& elementInfo : mElementsUnderCursor)
 		{
-			if(elementInfo.element->_acceptsKeyboardFocus())
-			{
-				if(mKeyboardFocusElement != nullptr && elementInfo.element != mKeyboardFocusElement)
-					mKeyboardFocusElement->_setFocus(false);
+			mNewElementsInFocus.push_back(elementInfo);
+
+			auto iterFind = std::find_if(begin(mElementsInFocus), end(mElementsInFocus), 
+				[=] (const ElementInfo& x) { return x.element == elementInfo.element; });
 
+			if(iterFind == mElementsInFocus.end())
+			{
+				// TODO - Send FocusGained event
 				elementInfo.element->_setFocus(true);
+			}
+		}
 
-				mKeyboardFocusElement = elementInfo.element;
-				mKeyboardFocusWidget = elementInfo.widget;
+		// Determine elements that lost focus
+		for(auto& elementInfo : mElementsInFocus)
+		{
+			auto iterFind = std::find_if(begin(mNewElementsInFocus), end(mNewElementsInFocus), 
+				[=] (const ElementInfo& x) { return x.element == elementInfo.element; });
 
-				event.markAsUsed();
-				break;
+			if(iterFind == mNewElementsInFocus.end())
+			{
+				// TODO - Send FocusLost event
+				elementInfo.element->_setFocus(false);
 			}
 		}
 
+		if(mElementsUnderCursor.size() > 0)
+			event.markAsUsed();
+
+		mElementsInFocus.swap(mNewElementsInFocus);
+
 		// If right click try to open context menu
 		if(buttonStates[2] == true) 
 		{
@@ -887,7 +916,7 @@ namespace BansheeEngine
 
 	void GUIManager::onInputCommandEntered(CM::InputCommandType commandType)
 	{
-		if(mKeyboardFocusElement == nullptr)
+		if(mElementsInFocus.size() == 0)
 			return;
 
 		mCommandEvent = GUICommandEvent();
@@ -924,6 +953,9 @@ namespace BansheeEngine
 		case InputCommandType::Tab:
 			mCommandEvent.setType(GUICommandEventType::Tab);
 			break;
+		case InputCommandType::Rename:
+			mCommandEvent.setType(GUICommandEventType::Rename);
+			break;
 		case InputCommandType::SelectAll:
 			mCommandEvent.setType(GUICommandEventType::SelectAll);
 			break;
@@ -953,7 +985,10 @@ namespace BansheeEngine
 			break;
 		}
 
-		sendCommandEvent(mKeyboardFocusWidget, mKeyboardFocusElement, mCommandEvent);
+		for(auto& elementInfo : mElementsInFocus)
+		{
+			sendCommandEvent(elementInfo.widget, elementInfo.element, mCommandEvent);
+		}		
 	}
 
 	bool GUIManager::findElementUnderCursor(const CM::Vector2I& cursorScreenPos, bool buttonStates[3], bool shift, bool control, bool alt)
@@ -1127,12 +1162,12 @@ namespace BansheeEngine
 
 	void GUIManager::onTextInput(const CM::TextInputEvent& event)
 	{
-		if(mKeyboardFocusElement != nullptr)
-		{
-			mTextInputEvent = GUITextInputEvent();
+		mTextInputEvent = GUITextInputEvent();
+		mTextInputEvent.setData(event.textChar);
 
-			mTextInputEvent.setData(event.textChar);
-			if(sendTextInputEvent(mKeyboardFocusWidget, mKeyboardFocusElement, mTextInputEvent))
+		for(auto& elementInFocus : mElementsInFocus)
+		{
+			if(sendTextInputEvent(elementInFocus.widget, elementInFocus.element, mTextInputEvent))
 				event.markAsUsed();
 		}
 	}

+ 0 - 2
BansheeEngine/Source/BsGUIScrollArea.cpp

@@ -129,7 +129,6 @@ namespace BansheeEngine
 				_registerChildElement(mVertScroll);
 
 				mVertScroll->onScrollPositionChanged.connect(boost::bind(&GUIScrollArea::vertScrollUpdate, this, _1));
-				mVertScroll->_setAcceptsKeyboardFocus(false);
 			}
 
 			INT32 scrollBarOffset = (UINT32)std::max(0, (INT32)width - (INT32)ScrollBarWidth);
@@ -190,7 +189,6 @@ namespace BansheeEngine
 				_registerChildElement(mHorzScroll);
 
 				mHorzScroll->onScrollPositionChanged.connect(boost::bind(&GUIScrollArea::horzScrollUpdate, this, _1));
-				mHorzScroll->_setAcceptsKeyboardFocus(false);
 			}
 
 			INT32 scrollBarOffset = (UINT32)std::max(0, (INT32)height - (INT32)ScrollBarWidth);

+ 0 - 4
BansheeEngine/Source/BsGUIScrollBar.cpp

@@ -55,10 +55,6 @@ namespace BansheeEngine
 
 		mUpBtn->onClick.connect(boost::bind(&GUIScrollBar::upButtonClicked, this));
 		mDownBtn->onClick.connect(boost::bind(&GUIScrollBar::downButtonClicked, this));
-
-		mHandleBtn->_setAcceptsKeyboardFocus(false);
-		mUpBtn->_setAcceptsKeyboardFocus(false);
-		mDownBtn->_setAcceptsKeyboardFocus(false);
 	}
 
 	GUIScrollBar::~GUIScrollBar()

+ 2 - 0
CamelotClient/CamelotClient.vcxproj

@@ -269,6 +269,7 @@
     <ClInclude Include="Include\BsGUISceneTreeView.h" />
     <ClInclude Include="Include\BsGUITabbedTitleBar.h" />
     <ClInclude Include="Include\BsGUITabButton.h" />
+    <ClInclude Include="Include\BsGUITreeViewEditBox.h" />
     <ClInclude Include="Include\BsGUIWindowFrame.h" />
     <ClInclude Include="Include\BsGUIWindowFrameWidget.h" />
     <ClInclude Include="Include\BsGUIWindowDropArea.h" />
@@ -294,6 +295,7 @@
     <ClCompile Include="Source\BsGUISceneTreeView.cpp" />
     <ClCompile Include="Source\BsGUITabbedTitleBar.cpp" />
     <ClCompile Include="Source\BsGUITabButton.cpp" />
+    <ClCompile Include="Source\BsGUITreeViewEditBox.cpp" />
     <ClCompile Include="Source\BsGUIWindowFrame.cpp" />
     <ClCompile Include="Source\BsGUIWindowFrameWidget.cpp" />
     <ClCompile Include="Source\BsGUIWindowDropArea.cpp" />

+ 6 - 0
CamelotClient/CamelotClient.vcxproj.filters

@@ -96,6 +96,9 @@
     <ClInclude Include="Include\BsGUISceneTreeView.h">
       <Filter>Header Files\Editor</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsGUITreeViewEditBox.h">
+      <Filter>Header Files\Editor</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="stdafx.cpp">
@@ -167,5 +170,8 @@
     <ClCompile Include="Source\BsGUISceneTreeView.cpp">
       <Filter>Source Files\Editor</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsGUITreeViewEditBox.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 1 - 0
CamelotClient/Include/BsEditorPrerequisites.h

@@ -20,6 +20,7 @@ namespace BansheeEditor
 	class GUIMenuBar;
 	class GUIDockSlider;
 	class GUISceneTreeView;
+	class GUITreeViewEditBox;
 
 	enum class DragAndDropType
 	{

+ 17 - 3
CamelotClient/Include/BsGUISceneTreeView.h

@@ -47,11 +47,13 @@ namespace BansheeEditor
 
 		static GUISceneTreeView* create(BS::GUIWidget& parent,
 			BS::GUIElementStyle* backgroundStyle = nullptr, BS::GUIElementStyle* elementBtnStyle = nullptr, 
-			BS::GUIElementStyle* foldoutBtnStyle = nullptr, BS::GUIElementStyle* selectionBackgroundStyle = nullptr);
+			BS::GUIElementStyle* foldoutBtnStyle = nullptr, BS::GUIElementStyle* selectionBackgroundStyle = nullptr,
+			BS::GUIElementStyle* editBoxStyle = nullptr);
 
 		static GUISceneTreeView* create(BS::GUIWidget& parent, const BS::GUIOptions& options, 
 			BS::GUIElementStyle* backgroundStyle = nullptr, BS::GUIElementStyle* elementBtnStyle = nullptr, 
-			BS::GUIElementStyle* foldoutBtnStyle = nullptr, BS::GUIElementStyle* selectionBackgroundStyle = nullptr);
+			BS::GUIElementStyle* foldoutBtnStyle = nullptr, BS::GUIElementStyle* selectionBackgroundStyle = nullptr,
+			BS::GUIElementStyle* editBoxStyle = nullptr);
 
 		void update();
 
@@ -72,6 +74,7 @@ namespace BansheeEditor
 		const BS::GUIElementStyle* mElementBtnStyle;
 		const BS::GUIElementStyle* mFoldoutBtnStyle;
 		const BS::GUIElementStyle* mSelectionBackgroundStyle;
+		const BS::GUIElementStyle* mEditBoxStyle;
 
 		BS::GUITexture* mBackgroundImage;
 		TreeElement mRootElement;
@@ -81,14 +84,25 @@ namespace BansheeEditor
 
 		TreeElement* mSelectedElement;
 		BS::GUITexture* mSelectionBackground;
+		
+		TreeElement* mEditElement;
+		GUITreeViewEditBox* mNameEditBox;
 
 		GUISceneTreeView(BS::GUIWidget& parent, BS::GUIElementStyle* backgroundStyle, BS::GUIElementStyle* elementBtnStyle, 
-			BS::GUIElementStyle* foldoutBtnStyle, BS::GUIElementStyle* selectionBackgroundStyle, const BS::GUILayoutOptions& layoutOptions);
+			BS::GUIElementStyle* foldoutBtnStyle, BS::GUIElementStyle* selectionBackgroundStyle, BS::GUIElementStyle* editBoxStyle, 
+			const BS::GUILayoutOptions& layoutOptions);
 
 		const GUISceneTreeView::InteractableElement* findElementUnderCoord(const CM::Vector2I& coord) const;
 		GUISceneTreeView::TreeElement* GUISceneTreeView::interactableToRealElement(const GUISceneTreeView::InteractableElement& element);
 
+		void enableEdit(TreeElement* element);
+		void disableEdit(bool acceptChanges);
+
 		virtual bool mouseEvent(const BS::GUIMouseEvent& ev);
+		virtual bool commandEvent(const BS::GUICommandEvent& ev);
 		void elementToggled(TreeElement* element, bool toggled);
+
+		void onEditAccepted();
+		void onEditCanceled();
 	};
 }

+ 24 - 0
CamelotClient/Include/BsGUITreeViewEditBox.h

@@ -0,0 +1,24 @@
+#pragma once
+
+#include "BsEditorPrerequisites.h"
+#include "BsGUIInputBox.h"
+
+namespace BansheeEditor
+{
+	class GUITreeViewEditBox : public BS::GUIInputBox
+	{
+	public:
+		static const CM::String& getGUITypeName();
+
+		static GUITreeViewEditBox* create(BS::GUIWidget& parent, const BS::GUIElementStyle* style = nullptr);
+		static GUITreeViewEditBox* create(BS::GUIWidget& parent, const BS::GUIOptions& layoutOptions, const BS::GUIElementStyle* style = nullptr);
+
+		boost::signal<void()> onInputConfirmed;
+		boost::signal<void()> onInputCanceled;
+
+	private:
+		GUITreeViewEditBox(BS::GUIWidget& parent, const BS::GUIElementStyle* style, const BS::GUILayoutOptions& layoutOptions);
+
+		virtual bool commandEvent(const BS::GUICommandEvent& ev);
+	};
+}

+ 4 - 0
CamelotClient/Source/BsEditorGUI.cpp

@@ -6,6 +6,7 @@
 #include "BsGUIInputBox.h"
 #include "BsTextSprite.h"
 #include "BsSpriteTexture.h"
+#include "BsGUITreeViewEditBox.h"
 
 #include "CmFont.h"
 #include "CmFontImportOptions.h"
@@ -676,6 +677,9 @@ namespace BansheeEditor
 		treeViewSelBackgroundStyle.width = 2;
 
 		mSkin.setStyle("TreeViewSelectionBackground", treeViewSelBackgroundStyle);
+
+		// Edit box
+		mSkin.setStyle(GUITreeViewEditBox::getGUITypeName(), inputBoxStyle);
 	}
 
 	HSpriteTexture EditorGUI::getTexture(const CM::String& name)

+ 88 - 8
CamelotClient/Source/BsGUISceneTreeView.cpp

@@ -7,8 +7,10 @@
 #include "BsGUISpace.h"
 #include "BsGUIWidget.h"
 #include "BsGUIToggle.h"
+#include "BsGUITreeViewEditBox.h"
 #include "BsGUIMouseEvent.h"
 #include "BsGUISkin.h"
+#include "BsGUICommandEvent.h"
 #include "CmSceneObject.h"
 #include "CmSceneManager.h"
 
@@ -22,7 +24,7 @@ namespace BansheeEditor
 	const UINT32 GUISceneTreeView::INITIAL_INDENT_OFFSET = 16;
 
 	GUISceneTreeView::TreeElement::TreeElement()
-		:mParent(nullptr), mFoldoutBtn(nullptr), mElement(nullptr),
+		:mParent(nullptr), mFoldoutBtn(nullptr), mElement(nullptr), 
 		mId(0), mIsExpanded(false), mSortedIdx(0), mIsDirty(false), mIsVisible(true)
 	{ }
 
@@ -41,10 +43,11 @@ namespace BansheeEditor
 	}
 
 	GUISceneTreeView::GUISceneTreeView(GUIWidget& parent, GUIElementStyle* backgroundStyle, GUIElementStyle* elementBtnStyle, 
-		GUIElementStyle* foldoutBtnStyle, GUIElementStyle* selectionBackgroundStyle, const BS::GUILayoutOptions& layoutOptions)
+		GUIElementStyle* foldoutBtnStyle, GUIElementStyle* selectionBackgroundStyle, GUIElementStyle* editBoxStyle, 
+		const GUILayoutOptions& layoutOptions)
 		:GUIElementContainer(parent, layoutOptions), mBackgroundStyle(backgroundStyle),
-		mElementBtnStyle(elementBtnStyle), mFoldoutBtnStyle(foldoutBtnStyle),
-		mSelectedElement(nullptr), mSelectionBackground(nullptr), mSelectionBackgroundStyle(selectionBackgroundStyle)
+		mElementBtnStyle(elementBtnStyle), mFoldoutBtnStyle(foldoutBtnStyle), mEditBoxStyle(editBoxStyle), mEditElement(nullptr),
+		mSelectedElement(nullptr), mSelectionBackground(nullptr), mNameEditBox(nullptr), mSelectionBackgroundStyle(selectionBackgroundStyle)
 	{
 		if(mBackgroundStyle == nullptr)
 			mBackgroundStyle = parent.getSkin().getStyle("TreeViewBackground");
@@ -58,12 +61,21 @@ namespace BansheeEditor
 		if(mSelectionBackgroundStyle == nullptr)
 			mSelectionBackgroundStyle = parent.getSkin().getStyle("TreeViewSelectionBackground");
 
+		if(mEditBoxStyle == nullptr)
+			mEditBoxStyle = parent.getSkin().getStyle("TreeViewEditBox");
+
 		mBackgroundImage = GUITexture::create(parent, mBackgroundStyle);
 		mSelectionBackground = GUITexture::create(parent, mSelectionBackgroundStyle);
 		mSelectionBackground->disableRecursively();
+		mNameEditBox = GUITreeViewEditBox::create(parent, mEditBoxStyle);
+		mNameEditBox->disableRecursively();
+
+		mNameEditBox->onInputConfirmed.connect(boost::bind(&GUISceneTreeView::onEditAccepted, this));
+		mNameEditBox->onInputCanceled.connect(boost::bind(&GUISceneTreeView::onEditCanceled, this));
 
 		_registerChildElement(mBackgroundImage);
 		_registerChildElement(mSelectionBackground);
+		_registerChildElement(mNameEditBox);
 	}
 
 	GUISceneTreeView::~GUISceneTreeView()
@@ -72,17 +84,17 @@ namespace BansheeEditor
 	}
 
 	GUISceneTreeView* GUISceneTreeView::create(GUIWidget& parent, GUIElementStyle* backgroundStyle, GUIElementStyle* elementBtnStyle, 
-		GUIElementStyle* foldoutBtnStyle, GUIElementStyle* selectionBackgroundStyle)
+		GUIElementStyle* foldoutBtnStyle, GUIElementStyle* selectionBackgroundStyle, GUIElementStyle* editBoxStyle)
 	{
 		return new (cm_alloc<GUISceneTreeView, PoolAlloc>()) GUISceneTreeView(parent, backgroundStyle, elementBtnStyle, foldoutBtnStyle, 
-			selectionBackgroundStyle, GUILayoutOptions::create(&GUISkin::DefaultStyle));
+			selectionBackgroundStyle, editBoxStyle, GUILayoutOptions::create(&GUISkin::DefaultStyle));
 	}
 
 	GUISceneTreeView* GUISceneTreeView::create(GUIWidget& parent, const GUIOptions& options, GUIElementStyle* backgroundStyle,
-		GUIElementStyle* elementBtnStyle, GUIElementStyle* foldoutBtnStyle, GUIElementStyle* selectionBackgroundStyle)
+		GUIElementStyle* elementBtnStyle, GUIElementStyle* foldoutBtnStyle, GUIElementStyle* selectionBackgroundStyle, GUIElementStyle* editBoxStyle)
 	{
 		return new (cm_alloc<GUISceneTreeView, PoolAlloc>()) GUISceneTreeView(parent, backgroundStyle, elementBtnStyle, 
-			foldoutBtnStyle, selectionBackgroundStyle, GUILayoutOptions::create(options, &GUISkin::DefaultStyle));
+			foldoutBtnStyle, selectionBackgroundStyle, editBoxStyle, GUILayoutOptions::create(options, &GUISkin::DefaultStyle));
 	}
 
 	void GUISceneTreeView::update()
@@ -318,11 +330,61 @@ namespace BansheeEditor
 		return false;
 	}
 
+	bool GUISceneTreeView::commandEvent(const GUICommandEvent& ev)
+	{
+		if(ev.getType() == GUICommandEventType::Rename)
+		{
+			if(mSelectedElement != nullptr && mEditElement == nullptr)
+				enableEdit(mSelectedElement);
+
+			return true;
+		}
+
+		return false;
+	}
+
 	void GUISceneTreeView::elementToggled(TreeElement* element, bool toggled)
 	{
 		element->mIsExpanded = toggled;
 	}
 
+	void GUISceneTreeView::onEditAccepted()
+	{
+		disableEdit(true);
+	}
+
+	void GUISceneTreeView::onEditCanceled()
+	{
+		disableEdit(false);
+	}
+
+	void GUISceneTreeView::enableEdit(TreeElement* element)
+	{
+		assert(mEditElement == nullptr);
+
+		mEditElement = element;
+		mNameEditBox->enableRecursively();
+
+		if(element->mElement != nullptr)
+			element->mElement->disableRecursively();
+	}
+
+	void GUISceneTreeView::disableEdit(bool applyChanges)
+	{
+		assert(mEditElement != nullptr);
+		
+		if(mEditElement->mElement != nullptr)
+			mEditElement->mElement->enableRecursively();
+
+		if(applyChanges)
+		{
+			// TODO - Actually rename GameObject. Don't forget Undo/Redo
+		}
+
+		mNameEditBox->disableRecursively();
+		mEditElement = nullptr;
+	}
+
 	Vector2I GUISceneTreeView::_getOptimalSize() const
 	{
 		struct UpdateTreeElement
@@ -527,6 +589,24 @@ namespace BansheeEditor
 			RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
 			mSelectionBackground->_setClipRect(elemClipRect);
 		}
+
+		if(mEditElement != nullptr)
+		{
+			GUILabel* targetElement = mEditElement->mElement;
+
+			Vector2I offset = targetElement->_getOffset();
+			UINT32 remainingWidth = (UINT32)std::max(0, (((INT32)width) - (offset.x - x)));
+
+			mNameEditBox->_setOffset(offset);
+			mNameEditBox->_setWidth(remainingWidth);
+			mNameEditBox->_setHeight(targetElement->_getHeight());
+			mNameEditBox->_setAreaDepth(areaDepth);
+			mNameEditBox->_setWidgetDepth(widgetDepth);
+
+			RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
+			mNameEditBox->_setClipRect(elemClipRect);
+
+		}
 	}
 
 	const GUISceneTreeView::InteractableElement* GUISceneTreeView::findElementUnderCoord(const CM::Vector2I& coord) const

+ 66 - 0
CamelotClient/Source/BsGUITreeViewEditBox.cpp

@@ -0,0 +1,66 @@
+#include "BsGUITreeViewEditBox.h"
+#include "BsGUICommandEvent.h"
+#include "BsGUIWidget.h"
+#include "BsGUISkin.h"
+
+using namespace CamelotFramework;
+using namespace BansheeEngine;
+
+namespace BansheeEditor
+{
+	const CM::String& GUITreeViewEditBox::getGUITypeName()
+	{
+		static String name = "TreeViewEditBox";
+		return name;
+	}
+
+	GUITreeViewEditBox* GUITreeViewEditBox::create(BS::GUIWidget& parent, const BS::GUIElementStyle* style)
+	{
+		if(style == nullptr)
+		{
+			const GUISkin& skin = parent.getSkin();
+			style = skin.getStyle(getGUITypeName());
+		}
+
+		return new (cm_alloc<GUITreeViewEditBox, PoolAlloc>()) GUITreeViewEditBox(parent, style, GUILayoutOptions::create(style));
+	}
+
+	GUITreeViewEditBox* GUITreeViewEditBox::create(BS::GUIWidget& parent, const BS::GUIOptions& layoutOptions, const BS::GUIElementStyle* style)
+	{
+		if(style == nullptr)
+		{
+			const GUISkin& skin = parent.getSkin();
+			style = skin.getStyle(getGUITypeName());
+		}
+
+		return new (cm_alloc<GUITreeViewEditBox, PoolAlloc>()) GUITreeViewEditBox(parent, style, GUILayoutOptions::create(layoutOptions, style));
+	}
+
+	GUITreeViewEditBox::GUITreeViewEditBox(BS::GUIWidget& parent, const BS::GUIElementStyle* style, const BS::GUILayoutOptions& layoutOptions)
+		:GUIInputBox(parent, style, layoutOptions, false)
+	{
+
+	}
+
+	bool GUITreeViewEditBox::commandEvent(const BS::GUICommandEvent& ev)
+	{
+		bool processed = GUIInputBox::commandEvent(ev);
+
+		if(ev.getType() == GUICommandEventType::Return)
+		{
+			if(!onInputConfirmed.empty())
+				onInputConfirmed();
+
+			return true;
+		}
+		else if(ev.getType() == GUICommandEventType::Escape)
+		{
+			if(!onInputCanceled.empty())
+				onInputCanceled();
+
+			return true;
+		}
+
+		return processed;
+	}
+};

+ 1 - 1
CamelotClient/Source/CmTestTextSprite.cpp

@@ -54,7 +54,7 @@ namespace BansheeEditor
 		sceneTreeViewLayout.addElement(mSceneTreeView);
 		sceneTreeViewLayout.addFlexibleSpace();
 
-		area->getLayout().addElement(GUIRenderTexture::create(*this, sceneView, GUIOptions(GUIOption::fixedWidth(800), GUIOption::fixedHeight(600))));
+		//area->getLayout().addElement(GUIRenderTexture::create(*this, sceneView, GUIOptions(GUIOption::fixedWidth(800), GUIOption::fixedHeight(600))));
 		//mLabel = GUILabel::create(*this, HString(L""));
 		//area->getLayout().addElement(mLabel);
 

+ 1 - 1
CamelotCore/Include/CmInputFwd.h

@@ -302,7 +302,7 @@ namespace CamelotFramework
 	enum class InputCommandType
 	{
 		CursorMoveLeft, CursorMoveRight, CursorMoveUp, CursorMoveDown, 
-		SelectLeft, SelectRight, SelectUp, SelectDown, Undo, Redo,
+		SelectLeft, SelectRight, SelectUp, SelectDown, Undo, Redo, Rename,
 		Escape, Delete, Backspace, Return, SelectAll, Copy, Cut, Paste, Tab
 	};
 

+ 3 - 0
CamelotCore/Source/CmPlatformWndProc.cpp

@@ -455,6 +455,9 @@ namespace CamelotFramework
 		case VK_TAB:
 			command = InputCommandType::Tab;
 			return true;
+		case VK_F2:
+			command = InputCommandType::Rename;
+			return true;
 		case 0x41: // A
 			if(isCtrlPressed)
 			{

+ 44 - 3
TreeView.txt

@@ -9,8 +9,18 @@ TreeView
   - Deleting
       - Simply track currently selected element and Delete event
 
-IMMEDIATE:
- - Selection sprite seems to be one pixel too high even if its on the same level as the text sprite. I think this is a discrepancy on sprite level.
+TODO:
+ - Add better TreeViewEditBox texture
+ - In disableEdit make sure I actually apply name change
+   - Use Undo/Redo system (TODO: Not implemented yet)
+ - Clicking on an already selectecd element starts rename 
+    - Will likely need some kind of check to ignore double-clicks?
+ - Pressing F2 starts rename
+ - Context menu with rename
+ - Detect when I click outside of input box and disable edit (TODO: Need a way to easily detect this)
+ - Delete
+    - Needs Undo/Redo support
+
 
 Implementation steps:
  - Selection
@@ -40,4 +50,35 @@ Detecting external clicks:
  Other:
  - When dragging in tree view automatically expand mouse over elements
  - Ability to select and drag multiple elements
- - "Ping" effect
+ - "Ping" effect
+ - All operations should be undo-redoable
+ - Context menu with copy/paste
+ - Copy/Paste/Duplicate
+   - This is more of a problem with actual copying of SceneObjects (It's not implemented). I should be able to serialize, and then de-serialize with a new parent as a form of copy.
+
+
+ ---------------------
+
+ SHORTCUT BRAINSTORM:
+
+ Remove pre-defined GUICommandEvents and InputCommandType. They cannot be easily extended and I will need their functionality when I add support for shortcuts! 
+INSTEAD:
+ - Add GUIShortcutManager to BansheeEngine
+ - Platform forwards all input commands to ShortcutManager instead of gInput (via a callback)
+ - ShortcutManager checks if it has any registered shortcuts and if any are triggered it notifies GUIManager
+ - Shortcuts change depending on active GUIElement
+   - Each GUIELement can provide a list of shortcuts it listens to
+   - OnFocusGained/OnFocusLost adds/removes items from the list
+   - If multiple elements are in focus, all of their shortcuts are registered but the callbacks just don't get triggered on those elements that don't have the triggered shortcut
+     - If two elements have the same shortcut, only one of them will trigger (undefined which)
+ - (Obviously this functionality will need focusGained/focusLost functionality first)
+
+
+TODO: Can I integrate this with the input manager somehow?
+ - NO - Shortcut system should be GUI only, while in-game input and in-game shortcuts will be handled manually.
+
+When I implement focus gained/lost system, send keyboard input to all elements with focus. Remove "acceptsKeyboardFocus". Elements can choose to ignore keyboard focus by ignoring keyboard events.
+Remove Cut/Copy/Paste/Undo/Redo/Rename/SelectAll and similar shortcuts from InputCommands. Instead send generic shortcut commands whenever user presses some key (together with shift/ctrl/alt state).
+  - Some shift/ctrl combinations might not be general shortcuts, like shift + arrows for selection, in which case we send them as specific shortcuts instead of generic ones
+  - I should also add a GUIShortcutManager, as a centralized place for dealing with shortcuts (so that I my edit them at a later date)
+   - TODO - Figure out how would this work