瀏覽代碼

All GUI input elements (managed and native) now have an event triggering when user confirms input
Scene tree view will now notify the external world when it modifies the scene
OnFocusChanged (C#) event is now OnFocusGained and OnFocusLost
Fixed GUIContent image+text bounds calculations

BearishSun 10 年之前
父節點
當前提交
4bb91ca22b
共有 93 個文件被更改,包括 703 次插入418 次删除
  1. 1 1
      BansheeCore/Include/BsInputFwd.h
  2. 1 1
      BansheeCore/Source/Win32/BsWin32Platform.cpp
  3. 0 5
      BansheeEditor/Include/BsGUIColor.h
  4. 5 9
      BansheeEditor/Include/BsGUIFloatField.h
  5. 5 9
      BansheeEditor/Include/BsGUIIntField.h
  6. 1 0
      BansheeEditor/Include/BsGUISceneTreeView.h
  7. 0 21
      BansheeEditor/Include/BsGUISliderField.h
  8. 5 4
      BansheeEditor/Include/BsGUITextField.h
  9. 6 0
      BansheeEditor/Include/BsGUIVector2Field.h
  10. 6 0
      BansheeEditor/Include/BsGUIVector3Field.h
  11. 6 0
      BansheeEditor/Include/BsGUIVector4Field.h
  12. 5 5
      BansheeEditor/Source/BsBuiltinEditorResources.cpp
  13. 2 2
      BansheeEditor/Source/BsEditorApplication.cpp
  14. 0 8
      BansheeEditor/Source/BsGUIColor.cpp
  15. 15 13
      BansheeEditor/Source/BsGUIFloatField.cpp
  16. 16 16
      BansheeEditor/Source/BsGUIIntField.cpp
  17. 12 0
      BansheeEditor/Source/BsGUISceneTreeView.cpp
  18. 1 21
      BansheeEditor/Source/BsGUISliderField.cpp
  19. 6 3
      BansheeEditor/Source/BsGUIStatusBar.cpp
  20. 15 8
      BansheeEditor/Source/BsGUITextField.cpp
  21. 1 1
      BansheeEditor/Source/BsGUITreeViewEditBox.cpp
  22. 7 0
      BansheeEditor/Source/BsGUIVector2Field.cpp
  23. 9 0
      BansheeEditor/Source/BsGUIVector3Field.cpp
  24. 10 0
      BansheeEditor/Source/BsGUIVector4Field.cpp
  25. 0 5
      BansheeEngine/Include/BsGUIButtonBase.h
  26. 1 1
      BansheeEngine/Include/BsGUICommandEvent.h
  27. 4 0
      BansheeEngine/Include/BsGUIContent.h
  28. 0 5
      BansheeEngine/Include/BsGUIDropDownContent.h
  29. 1 1
      BansheeEngine/Include/BsGUIElement.h
  30. 0 5
      BansheeEngine/Include/BsGUIElementContainer.h
  31. 2 7
      BansheeEngine/Include/BsGUIInputBox.h
  32. 0 4
      BansheeEngine/Include/BsGUILabel.h
  33. 0 5
      BansheeEngine/Include/BsGUITexture.h
  34. 5 8
      BansheeEngine/Source/BsGUIButtonBase.cpp
  35. 2 0
      BansheeEngine/Source/BsGUIContent.cpp
  36. 1 7
      BansheeEngine/Source/BsGUIDropDownContent.cpp
  37. 6 0
      BansheeEngine/Source/BsGUIElement.cpp
  38. 0 5
      BansheeEngine/Source/BsGUIElementContainer.cpp
  39. 4 9
      BansheeEngine/Source/BsGUIHelper.cpp
  40. 10 11
      BansheeEngine/Source/BsGUIInputBox.cpp
  41. 0 6
      BansheeEngine/Source/BsGUILabel.cpp
  42. 3 0
      BansheeEngine/Source/BsGUIManager.cpp
  43. 0 6
      BansheeEngine/Source/BsGUITexture.cpp
  44. 2 2
      BansheeUtility/Include/BsFrameAlloc.h
  45. 28 21
      MBansheeEditor/EditorApplication.cs
  46. 2 2
      MBansheeEditor/GUI/GUIDictionaryField.cs
  47. 30 16
      MBansheeEditor/GUI/GUIFloatField.cs
  48. 30 16
      MBansheeEditor/GUI/GUIIntField.cs
  49. 8 0
      MBansheeEditor/GUI/GUISceneTreeView.cs
  50. 0 15
      MBansheeEditor/GUI/GUISliderField.cs
  51. 31 17
      MBansheeEditor/GUI/GUITextField.cs
  52. 30 16
      MBansheeEditor/GUI/GUIVector2Field.cs
  53. 30 16
      MBansheeEditor/GUI/GUIVector3Field.cs
  54. 30 16
      MBansheeEditor/GUI/GUIVector4Field.cs
  55. 7 2
      MBansheeEditor/Inspector/InspectableArray.cs
  56. 7 2
      MBansheeEditor/Inspector/InspectableDictionary.cs
  57. 7 2
      MBansheeEditor/Inspector/InspectableField.cs
  58. 1 1
      MBansheeEditor/Inspector/InspectableFloat.cs
  59. 1 1
      MBansheeEditor/Inspector/InspectableInt.cs
  60. 7 2
      MBansheeEditor/Inspector/InspectableList.cs
  61. 5 3
      MBansheeEditor/Inspector/InspectableObject.cs
  62. 1 1
      MBansheeEditor/Inspector/InspectableString.cs
  63. 1 1
      MBansheeEditor/Inspector/InspectableVector2.cs
  64. 1 1
      MBansheeEditor/Inspector/InspectableVector3.cs
  65. 1 1
      MBansheeEditor/Inspector/InspectableVector4.cs
  66. 27 2
      MBansheeEditor/Inspector/InspectorWindow.cs
  67. 2 10
      MBansheeEditor/Library/LibraryWindow.cs
  68. 15 0
      MBansheeEditor/MenuItems.cs
  69. 2 0
      MBansheeEditor/Scene/DefaultHandleManager.cs
  70. 28 5
      MBansheeEditor/Scene/SceneWindow.cs
  71. 20 6
      MBansheeEngine/GUI/GUIElement.cs
  72. 16 4
      MBansheeEngine/GUI/GUITextBox.cs
  73. 3 3
      MBansheeEngine/Scene.cs
  74. 9 0
      SBansheeEditor/Include/BsScriptGUIFloatField.h
  75. 9 0
      SBansheeEditor/Include/BsScriptGUIIntField.h
  76. 12 0
      SBansheeEditor/Include/BsScriptGUISceneTreeView.h
  77. 0 1
      SBansheeEditor/Include/BsScriptGUISliderField.h
  78. 9 0
      SBansheeEditor/Include/BsScriptGUITextField.h
  79. 9 0
      SBansheeEditor/Include/BsScriptGUIVector2Field.h
  80. 9 0
      SBansheeEditor/Include/BsScriptGUIVector3Field.h
  81. 9 0
      SBansheeEditor/Include/BsScriptGUIVector4Field.h
  82. 8 1
      SBansheeEditor/Source/BsScriptGUIFloatField.cpp
  83. 8 1
      SBansheeEditor/Source/BsScriptGUIIntField.cpp
  84. 15 0
      SBansheeEditor/Source/BsScriptGUISceneTreeView.cpp
  85. 0 7
      SBansheeEditor/Source/BsScriptGUISliderField.cpp
  86. 8 1
      SBansheeEditor/Source/BsScriptGUITextField.cpp
  87. 8 1
      SBansheeEditor/Source/BsScriptGUIVector2Field.cpp
  88. 8 1
      SBansheeEditor/Source/BsScriptGUIVector3Field.cpp
  89. 8 1
      SBansheeEditor/Source/BsScriptGUIVector4Field.cpp
  90. 3 3
      SBansheeEngine/Include/BsScriptGUIElement.h
  91. 8 0
      SBansheeEngine/Include/BsScriptGUIInputBox.h
  92. 8 3
      SBansheeEngine/Source/BsScriptGUIElement.cpp
  93. 8 2
      SBansheeEngine/Source/BsScriptGUIInputBox.cpp

+ 1 - 1
BansheeCore/Include/BsInputFwd.h

@@ -347,7 +347,7 @@ namespace BansheeEngine
 	{
 		CursorMoveLeft, CursorMoveRight, CursorMoveUp, CursorMoveDown, 
 		SelectLeft, SelectRight, SelectUp, SelectDown,
-		Escape, Delete, Backspace, Return
+		Escape, Delete, Backspace, Return, Confirm
 	};
 
 	/**

+ 1 - 1
BansheeCore/Source/Win32/BsWin32Platform.cpp

@@ -460,7 +460,7 @@ namespace BansheeEngine
 			command = InputCommandType::Escape;
 			return true;
 		case VK_RETURN:
-			command = InputCommandType::Return;
+			command = isShiftPressed ? InputCommandType::Return : InputCommandType::Confirm;
 			return true;
 		case VK_BACK:
 			command = InputCommandType::Backspace;

+ 0 - 5
BansheeEditor/Include/BsGUIColor.h

@@ -85,11 +85,6 @@ namespace BansheeEngine
 		 */
 		virtual void updateRenderElementsInternal() override;
 
-		/**
-		 * @copydoc GUIElement::updateBounds()
-		 */
-		virtual void updateClippedBounds() override;
-
 		/**
 		 * @copydoc GUIElement::_mouseEvent()
 		 */

+ 5 - 9
BansheeEditor/Include/BsGUIFloatField.h

@@ -53,14 +53,10 @@ namespace BansheeEngine
 		virtual void setTint(const Color& color) override;
 
 		Event<void(float)> onValueChanged; /**< Triggers when the field value changes. */
+		Event<void()> onConfirm; /**< Triggered when the user hits the Enter key with the input box in focus. */
 	protected:
 		virtual ~GUIFloatField();
 
-		/**
-		 * @copydoc	GUIElementContainer::updateClippedBounds
-		 */
-		void updateClippedBounds() override;
-
 		/**
 		 * @copydoc	GUIElementContainer::_hasCustomCursor
 		 */
@@ -88,14 +84,14 @@ namespace BansheeEngine
 		void valueChanged(float newValue);
 
 		/**
-		 * @brief	Triggers when the input box receives keyboard focus.
+		 * @brief	Triggers when the input box receives or loses keyboard focus.
 		 */
-		void focusGained();
+		void focusChanged(bool focus);
 
 		/**
-		 * @brief	Triggers when the input box loses keyboard focus.
+		 * @brief	Triggered when the users confirms input in the input box.
 		 */
-		void focusLost();
+		void inputConfirmed();
 
 		/**
 		 * @brief	Callback that checks can the provided string be

+ 5 - 9
BansheeEditor/Include/BsGUIIntField.h

@@ -53,14 +53,10 @@ namespace BansheeEngine
 		virtual void setTint(const Color& color) override;
 
 		Event<void(INT32)> onValueChanged; /**< Triggers when the internal value changes. */
+		Event<void()> onConfirm; /**< Triggered when the user hits the Enter key with the input box in focus. */
 	protected:
 		virtual ~GUIIntField();
 
-		/**
-		 * @copydoc	GUIElement::updateClippedBounds
-		 */
-		void updateClippedBounds() override;
-
 		/**
 		 * @copydoc	GUIElement::_hasCustomCursor
 		 */
@@ -88,14 +84,14 @@ namespace BansheeEngine
 		void valueChanged(INT32 newValue);
 
 		/**
-		 * @brief	Triggers when the input box receives keyboard focus.
+		 * @brief	Triggers when the input box receives or loses keyboard focus.
 		 */
-		void focusGained();
+		void focusChanged(bool focus);
 
 		/**
-		 * @brief	Triggers when the input box loses keyboard focus.
+		 * @brief	Triggered when the users confirms input in the input box.
 		 */
-		void focusLost();
+		void inputConfirmed();
 
 		/**
 		 * @brief	Callback that checks can the provided string be

+ 1 - 0
BansheeEditor/Include/BsGUISceneTreeView.h

@@ -101,6 +101,7 @@ namespace BansheeEngine
 		void ping(const HSceneObject& object);
 
 		Event<void()> onSelectionChanged; /**< Triggered whenever the selection changes. Call ::getSelection() to retrieve new selection: */
+		Event<void()> onModified; /**< Triggered whenever the scene is modified in any way from within the scene tree view. (e.g. object is deleted, added, etc.) */
 		static const MessageId SELECTION_CHANGED_MSG;
 	protected:
 		virtual ~GUISceneTreeView();

+ 0 - 21
BansheeEditor/Include/BsGUISliderField.h

@@ -53,11 +53,6 @@ namespace BansheeEngine
 		 */
 		void setStep(float step);
 
-		/**
-		 * @brief	Checks is the input field currently active.
-		 */
-		bool hasInputFocus() const { return mHasInputFocus; }
-
 		/**
 		 * @copydoc	GUIElement::setTint
 		 */
@@ -67,11 +62,6 @@ namespace BansheeEngine
 	protected:
 		virtual ~GUISliderField();
 
-		/**
-		 * @copydoc	GUIElementContainer::updateClippedBounds
-		 */
-		void updateClippedBounds() override;
-
 		/**
 		 * @copydoc	GUIElementContainer::styleUpdated
 		 */
@@ -87,16 +77,6 @@ namespace BansheeEngine
 		 */
 		void sliderChanged(float newValue);
 
-		/**
-		 * @brief	Triggers when the input box receives keyboard focus.
-		 */
-		void focusGained();
-
-		/**
-		 * @brief	Triggers when the input box loses keyboard focus.
-		 */
-		void focusLost();
-
 		/**
 		 * @brief	Callback that checks can the provided string be
 		 *			converted to a floating point value.
@@ -105,6 +85,5 @@ namespace BansheeEngine
 
 		GUIInputBox* mInputBox;
 		GUISliderHorz* mSlider;
-		bool mHasInputFocus;
 	};
 }

+ 5 - 4
BansheeEditor/Include/BsGUITextField.h

@@ -184,6 +184,7 @@ namespace BansheeEngine
 		virtual void setTint(const Color& color) override;
 
 		Event<void(const WString&)> onValueChanged; /** Triggered when the value in the field changes. */
+		Event<void()> onConfirm; /**< Triggered when the user hits the Enter key with the input box in focus. */
 	protected:
 		static const UINT32 DEFAULT_LABEL_WIDTH;
 
@@ -210,14 +211,14 @@ namespace BansheeEngine
 		void valueChanged(const WString& newValue);
 
 		/**
-		 * @brief	Triggered when the internal input box gains focus.
+		 * @brief	Triggers when the input box receives or loses keyboard focus.
 		 */
-		void focusGained();
+		void focusChanged(bool focus);
 
 		/**
-		 * @brief	Triggered when the internal input box loses focus.
+		 * @brief	Triggered when the users confirms input in the input box.
 		 */
-		void focusLost();
+		void inputConfirmed();
 
 		GUIInputBox* mInputBox;
 		GUILayout* mLayout;

+ 6 - 0
BansheeEditor/Include/BsGUIVector2Field.h

@@ -48,6 +48,7 @@ namespace BansheeEngine
 		virtual void setTint(const Color& color) override;
 
 		Event<void(const Vector2&)> onValueChanged; /**< Triggers when the field value changes. */
+		Event<void()> onConfirm; /**< Triggered when the user hits the Enter key with the input box in focus. */
 	protected:
 		virtual ~GUIVector2Field() { }
 
@@ -64,6 +65,11 @@ namespace BansheeEngine
 		 */
 		void valueChanged(float newValue);
 
+		/**
+		 * @brief	Triggered when the users confirms input in the input box.
+		 */
+		void inputConfirmed();
+
 		GUIFloatField* mFieldX;
 		GUIFloatField* mFieldY;
 	};

+ 6 - 0
BansheeEditor/Include/BsGUIVector3Field.h

@@ -48,6 +48,7 @@ namespace BansheeEngine
 		virtual void setTint(const Color& color) override;
 
 		Event<void(const Vector3&)> onValueChanged; /**< Triggers when the field value changes. */
+		Event<void()> onConfirm; /**< Triggered when the user hits the Enter key with the input box in focus. */
 	protected:
 		virtual ~GUIVector3Field() { }
 
@@ -61,6 +62,11 @@ namespace BansheeEngine
 		 */
 		void valueChanged(float newValue);
 
+		/**
+		 * @brief	Triggered when the users confirms input in the input box.
+		 */
+		void inputConfirmed();
+
 		static const UINT32 ELEMENT_LABEL_WIDTH;
 
 		GUIFloatField* mFieldX;

+ 6 - 0
BansheeEditor/Include/BsGUIVector4Field.h

@@ -48,6 +48,7 @@ namespace BansheeEngine
 		virtual void setTint(const Color& color) override;
 
 		Event<void(const Vector4&)> onValueChanged; /**< Triggers when the field value changes. */
+		Event<void()> onConfirm; /**< Triggered when the user hits the Enter key with the input box in focus. */
 	protected:
 		virtual ~GUIVector4Field() { }
 
@@ -61,6 +62,11 @@ namespace BansheeEngine
 		 */
 		void valueChanged(float newValue);
 
+		/**
+		 * @brief	Triggered when the users confirms input in the input box.
+		 */
+		void inputConfirmed();
+
 		static const UINT32 ELEMENT_LABEL_WIDTH;
 
 		GUIFloatField* mFieldX;

+ 5 - 5
BansheeEditor/Source/BsBuiltinEditorResources.cpp

@@ -305,7 +305,7 @@ namespace BansheeEngine
 		Path absoluteDataPath = FileSystem::getWorkingDirectoryPath();
 		absoluteDataPath.append(BuiltinDataFolder);
 
-#if BS_DEBUG_MODE
+//#if BS_DEBUG_MODE
 		if (BuiltinResourcesHelper::checkForModifications(BuiltinRawDataFolder, BuiltinDataFolder + L"Timestamp.asset"))
 		{
 			mResourceManifest = ResourceManifest::create("BuiltinResources");
@@ -319,7 +319,7 @@ namespace BansheeEngine
 
 			ResourceManifest::save(mResourceManifest, ResourceManifestPath, absoluteDataPath);
 		}
-#endif
+//#endif
 
 		if (mResourceManifest == nullptr)
 		{
@@ -1053,7 +1053,7 @@ namespace BansheeEngine
 		toolBarSeparator.normal.texture = getGUITexture(ToolBarSeparatorTex);
 		toolBarSeparator.fixedWidth = true;
 		toolBarSeparator.width = 3;
-		toolBarSeparator.height = 30;
+		toolBarSeparator.height = 32;
 
 		skin->setStyle(GUIMenuBar::getToolBarSeparatorStyleType(), toolBarSeparator);
 
@@ -1067,8 +1067,8 @@ namespace BansheeEngine
 		toolBarBtnStyle.active.textColor = TextActiveColor;
 		toolBarBtnStyle.fixedHeight = true;
 		toolBarBtnStyle.fixedWidth = true;
-		toolBarBtnStyle.height = 30;
-		toolBarBtnStyle.width = 30;
+		toolBarBtnStyle.height = 32;
+		toolBarBtnStyle.width = 32;
 
 		skin->setStyle(GUIMenuBar::getToolBarButtonStyleType(), toolBarBtnStyle);
 

+ 2 - 2
BansheeEditor/Source/BsEditorApplication.cpp

@@ -77,7 +77,7 @@ namespace BansheeEngine
 
 	EditorApplication::~EditorApplication()
 	{
-#if BS_DEBUG
+#if BS_DEBUG_MODE
 		/************************************************************************/
 		/* 								DEBUG CODE                      		*/
 		/************************************************************************/
@@ -148,7 +148,7 @@ namespace BansheeEngine
 		MainEditorWindow* mainWindow = MainEditorWindow::create(getPrimaryWindow());
 		ScriptManager::instance().initialize();
 
-#if BS_DEBUG
+#if BS_DEBUG_MODE
 		/************************************************************************/
 		/* 								DEBUG CODE                      		*/
 		/************************************************************************/

+ 0 - 8
BansheeEditor/Source/BsGUIColor.cpp

@@ -108,14 +108,6 @@ namespace BansheeEngine
 		GUIElement::updateRenderElementsInternal();
 	}
 
-	void GUIColor::updateClippedBounds()
-	{
-		mClippedBounds = mLayoutData.area;
-
-		if (mLayoutData.clipRect.width > 0 && mLayoutData.clipRect.height > 0)
-			mClippedBounds.clip(mLayoutData.clipRect);
-	}
-
 	Vector2I GUIColor::_getOptimalSize() const
 	{
 		return GUIHelper::calcOptimalContentsSize(Vector2I(80, 10), *_getStyle(), _getDimensions()); // Arbitrary size

+ 15 - 13
BansheeEditor/Source/BsGUIFloatField.cpp

@@ -28,8 +28,8 @@ namespace BansheeEngine
 		mInputBox->setFilter(&GUIFloatField::floatFilter);
 
 		mInputBox->onValueChanged.connect(std::bind((void(GUIFloatField::*)(const WString&))&GUIFloatField::valueChanged, this, _1));
-		mInputBox->onFocusGained.connect(std::bind(&GUIFloatField::focusGained, this));
-		mInputBox->onFocusLost.connect(std::bind(&GUIFloatField::focusLost, this));
+		mInputBox->onFocusChanged.connect(std::bind(&GUIFloatField::focusChanged, this, _1));
+		mInputBox->onConfirm.connect(std::bind(&GUIFloatField::inputConfirmed, this));
 
 		mLayout->addElement(mInputBox);
 
@@ -161,11 +161,6 @@ namespace BansheeEngine
 		mInputBox->setTint(color);
 	}
 
-	void GUIFloatField::updateClippedBounds()
-	{
-		mClippedBounds = mLayoutData.area;
-	}
-
 	const String& GUIFloatField::getGUITypeName()
 	{
 		static String typeName = "GUIFloatField";
@@ -199,16 +194,23 @@ namespace BansheeEngine
 			onValueChanged(newValue);
 	}
 
-	void GUIFloatField::focusGained()
+	void GUIFloatField::focusChanged(bool focus)
 	{
-		UndoRedo::instance().pushGroup("InputBox");
-		mHasInputFocus = true;
+		if (focus)
+		{
+			UndoRedo::instance().pushGroup("InputBox");
+			mHasInputFocus = true;
+		}
+		else
+		{
+			UndoRedo::instance().popGroup("InputBox");
+			mHasInputFocus = false;
+		}
 	}
 
-	void GUIFloatField::focusLost()
+	void GUIFloatField::inputConfirmed()
 	{
-		UndoRedo::instance().popGroup("InputBox");
-		mHasInputFocus = false;
+		onConfirm();
 	}
 
 	bool GUIFloatField::floatFilter(const WString& str)

+ 16 - 16
BansheeEditor/Source/BsGUIIntField.cpp

@@ -29,8 +29,8 @@ namespace BansheeEngine
 		mInputBox->setFilter(&GUIIntField::intFilter);
 
 		mInputBox->onValueChanged.connect(std::bind((void(GUIIntField::*)(const WString&))&GUIIntField::valueChanged, this, _1));
-		mInputBox->onFocusGained.connect(std::bind(&GUIIntField::focusGained, this));
-		mInputBox->onFocusLost.connect(std::bind(&GUIIntField::focusLost, this));
+		mInputBox->onFocusChanged.connect(std::bind(&GUIIntField::focusChanged, this, _1));
+		mInputBox->onConfirm.connect(std::bind(&GUIIntField::inputConfirmed, this));
 
 		mLayout->addElement(mInputBox);
 
@@ -187,11 +187,6 @@ namespace BansheeEngine
 		mInputBox->setTint(color);
 	}
 
-	void GUIIntField::updateClippedBounds()
-	{
-		mClippedBounds = mLayoutData.area;
-	}
-
 	const String& GUIIntField::getGUITypeName()
 	{
 		static String typeName = "GUIIntField";
@@ -212,21 +207,26 @@ namespace BansheeEngine
 	void GUIIntField::valueChanged(INT32 newValue)
 	{
 		CmdInputFieldValueChange<GUIIntField, INT32>::execute(this, newValue);
-
-		if (!onValueChanged.empty())
-			onValueChanged(newValue);
+		onValueChanged(newValue);
 	}
 
-	void GUIIntField::focusGained()
+	void GUIIntField::focusChanged(bool focus)
 	{
-		UndoRedo::instance().pushGroup("InputBox");
-		mHasInputFocus = true;
+		if (focus)
+		{
+			UndoRedo::instance().pushGroup("InputBox");
+			mHasInputFocus = true;
+		}
+		else
+		{
+			UndoRedo::instance().popGroup("InputBox");
+			mHasInputFocus = false;
+		}
 	}
 
-	void GUIIntField::focusLost()
+	void GUIIntField::inputConfirmed()
 	{
-		UndoRedo::instance().popGroup("InputBox");
-		mHasInputFocus = false;
+		onConfirm();
 	}
 
 	bool GUIIntField::intFilter(const WString& str)

+ 12 - 0
BansheeEditor/Source/BsGUISceneTreeView.cpp

@@ -241,6 +241,8 @@ namespace BansheeEngine
 		HSceneObject so = sceneTreeElement->mSceneObject;
 		CmdRecordSO::execute(so, L"Renamed \"" + toWString(so->getName()) + L"\"");
 		so->setName(toString(name));
+
+		onModified();
 	}
 
 	void GUISceneTreeView::deleteTreeElement(TreeElement* element)
@@ -249,6 +251,8 @@ namespace BansheeEngine
 
 		HSceneObject so = sceneTreeElement->mSceneObject;
 		CmdDeleteSO::execute(so, L"Deleted \"" + toWString(so->getName()) + L"\"");
+
+		onModified();
 	}
 
 	void GUISceneTreeView::deleteTreeElementInternal(GUITreeView::TreeElement* element)
@@ -308,6 +312,7 @@ namespace BansheeEngine
 				}
 
 				CmdReparentSO::execute(sceneObjects, newParent);
+				onModified();
 			}
 		}
 		else if (dragTypeId == (UINT32)DragAndDropType::Resources)
@@ -338,6 +343,8 @@ namespace BansheeEngine
 
 							if (newParent != nullptr)
 								instance->setParent(newParent);
+
+							onModified();
 						}
 					}
 				}
@@ -486,6 +493,7 @@ namespace BansheeEngine
 			message = L"Duplicated " + toWString(duplicateList.size()) + L" elements";
 
 		CmdCloneSO::execute(duplicateList, message);
+		onModified();
 	}
 
 	void GUISceneTreeView::copySelection()
@@ -555,6 +563,8 @@ namespace BansheeEngine
 			for (auto& clone : clones)
 				clone->setParent(parent);
 		}
+
+		onModified();
 	}
 
 	void GUISceneTreeView::clearCopyList()
@@ -593,6 +603,8 @@ namespace BansheeEngine
 		expandToElement(newTreeElement);
 		setSelection({ newSO });
 		renameSelected();
+
+		onModified();
 	}
 
 	void GUISceneTreeView::cleanDuplicates(Vector<HSceneObject>& objects)

+ 1 - 21
BansheeEditor/Source/BsGUISliderField.cpp

@@ -19,8 +19,7 @@ namespace BansheeEngine
 {
 	GUISliderField::GUISliderField(const PrivatelyConstruct& dummy, const GUIContent& labelContent, UINT32 labelWidth,
 		const String& style, const GUIDimensions& dimensions, bool withLabel)
-		:TGUIField(dummy, labelContent, labelWidth, style, dimensions, withLabel), mInputBox(nullptr), mSlider(nullptr),
-		mHasInputFocus(false)
+		:TGUIField(dummy, labelContent, labelWidth, style, dimensions, withLabel), mInputBox(nullptr), mSlider(nullptr)
 	{
 		mSlider = GUISliderHorz::create(GUIOptions(GUIOption::flexibleWidth()), getSubStyleName(getSliderStyleType()));
 		mSlider->onChanged.connect(std::bind(&GUISliderField::sliderChanged, this, _1));
@@ -29,8 +28,6 @@ namespace BansheeEngine
 		mInputBox->setFilter(&GUISliderField::floatFilter);
 
 		mInputBox->onValueChanged.connect(std::bind((void(GUISliderField::*)(const WString&))&GUISliderField::valueChanged, this, _1));
-		mInputBox->onFocusGained.connect(std::bind(&GUISliderField::focusGained, this));
-		mInputBox->onFocusLost.connect(std::bind(&GUISliderField::focusLost, this));
 
 		mLayout->addElement(mSlider);
 		mLayout->addNewElement<GUIFixedSpace>(5);
@@ -84,11 +81,6 @@ namespace BansheeEngine
 		mInputBox->setTint(color);
 	}
 
-	void GUISliderField::updateClippedBounds()
-	{
-		mClippedBounds = mLayoutData.area;
-	}
-
 	const String& GUISliderField::getGUITypeName()
 	{
 		static String typeName = "GUISliderField";
@@ -130,18 +122,6 @@ namespace BansheeEngine
 		onValueChanged(mSlider->getValue());
 	}
 
-	void GUISliderField::focusGained()
-	{
-		UndoRedo::instance().pushGroup("InputBox");
-		mHasInputFocus = true;
-	}
-
-	void GUISliderField::focusLost()
-	{
-		UndoRedo::instance().popGroup("InputBox");
-		mHasInputFocus = false;
-	}
-
 	bool GUISliderField::floatFilter(const WString& str)
 	{
 		return std::regex_match(str, std::wregex(L"-?(\\d+(\\.\\d*)?)?"));

+ 6 - 3
BansheeEditor/Source/BsGUIStatusBar.cpp

@@ -28,7 +28,7 @@ namespace BansheeEngine
 
 		mBackground = GUITexture::create(GUIOptions(GUIOption::flexibleWidth()), getSubStyleName(getGUIBackgroundTypeName()));
 		mMessage = GUIButton::create(HString(L""), GUIOptions(GUIOption::flexibleWidth()), getSubStyleName(getGUIMessageTypeName()));
-		mScene = GUILabel::create(HString(L"Scene: None"), GUIOptions(GUIOption::fixedWidth(150)));
+		mScene = GUILabel::create(HString(L"Scene: Unnamed"), GUIOptions(GUIOption::fixedWidth(150)));
 		mProject = GUILabel::create(HString(L"Project: None"), GUIOptions(GUIOption::fixedWidth(150)));
 
 		GUILayoutY* vertLayout = mPanel->addNewElement<GUILayoutY>();
@@ -37,8 +37,11 @@ namespace BansheeEngine
 
 		horzLayout->addNewElement<GUIFixedSpace>(10);
 		horzLayout->addElement(mMessage);
-		horzLayout->addNewElement<GUIFixedSpace>(20);
-		horzLayout->addNewElement<GUIFixedSpace>(20);
+		horzLayout->addNewElement<GUIFlexibleSpace>();
+		horzLayout->addElement(mScene);
+		horzLayout->addNewElement<GUIFixedSpace>(10);
+		horzLayout->addElement(mProject);
+		horzLayout->addNewElement<GUIFixedSpace>(10);
 
 		mBgPanel->addElement(mBackground);
 

+ 15 - 8
BansheeEditor/Source/BsGUITextField.cpp

@@ -32,8 +32,8 @@ namespace BansheeEngine
 		mLayout->addElement(mInputBox);
 
 		mInputBox->onValueChanged.connect(std::bind(&GUITextField::valueChanged, this, _1));
-		mInputBox->onFocusGained.connect(std::bind(&GUITextField::focusGained, this));
-		mInputBox->onFocusLost.connect(std::bind(&GUITextField::focusLost, this));
+		mInputBox->onFocusChanged.connect(std::bind(&GUITextField::focusChanged, this, _1));
+		mInputBox->onConfirm.connect(std::bind(&GUITextField::inputConfirmed, this));
 	}
 
 	GUITextField::~GUITextField()
@@ -190,16 +190,23 @@ namespace BansheeEngine
 			onValueChanged(newValue);
 	}
 
-	void GUITextField::focusGained()
+	void GUITextField::focusChanged(bool focus)
 	{
-		UndoRedo::instance().pushGroup("InputBox");
-		mHasInputFocus = true;
+		if (focus)
+		{
+			UndoRedo::instance().pushGroup("InputBox");
+			mHasInputFocus = true;
+		}
+		else
+		{
+			UndoRedo::instance().popGroup("InputBox");
+			mHasInputFocus = false;
+		}
 	}
 
-	void GUITextField::focusLost()
+	void GUITextField::inputConfirmed()
 	{
-		UndoRedo::instance().popGroup("InputBox");
-		mHasInputFocus = false;
+		onConfirm();
 	}
 
 	const String& GUITextField::getGUITypeName()

+ 1 - 1
BansheeEditor/Source/BsGUITreeViewEditBox.cpp

@@ -31,7 +31,7 @@ namespace BansheeEngine
 	{
 		bool processed = GUIInputBox::_commandEvent(ev);
 
-		if(ev.getType() == GUICommandEventType::Return)
+		if (ev.getType() == GUICommandEventType::Confirm)
 		{
 			if(!onInputConfirmed.empty())
 				onInputConfirmed();

+ 7 - 0
BansheeEditor/Source/BsGUIVector2Field.cpp

@@ -25,6 +25,8 @@ namespace BansheeEngine
 
 		mFieldX->onValueChanged.connect(std::bind(&GUIVector2Field::valueChanged, this, _1));
 		mFieldY->onValueChanged.connect(std::bind(&GUIVector2Field::valueChanged, this, _1));
+		mFieldX->onConfirm.connect(std::bind(&GUIVector2Field::inputConfirmed, this));
+		mFieldY->onConfirm.connect(std::bind(&GUIVector2Field::inputConfirmed, this));
 
 		mLayout->removeElement(mLabel);
 
@@ -73,6 +75,11 @@ namespace BansheeEngine
 		onValueChanged(getValue());
 	}
 
+	void GUIVector2Field::inputConfirmed()
+	{
+		onConfirm();
+	}
+
 	void GUIVector2Field::styleUpdated()
 	{
 		if (mLabel != nullptr)

+ 9 - 0
BansheeEditor/Source/BsGUIVector3Field.cpp

@@ -28,6 +28,10 @@ namespace BansheeEngine
 		mFieldY->onValueChanged.connect(std::bind(&GUIVector3Field::valueChanged, this, _1));
 		mFieldZ->onValueChanged.connect(std::bind(&GUIVector3Field::valueChanged, this, _1));
 
+		mFieldX->onConfirm.connect(std::bind(&GUIVector3Field::inputConfirmed, this));
+		mFieldY->onConfirm.connect(std::bind(&GUIVector3Field::inputConfirmed, this));
+		mFieldZ->onConfirm.connect(std::bind(&GUIVector3Field::inputConfirmed, this));
+
 		mLayout->removeElement(mLabel);
 
 		GUILayout* layout = mLayout->addNewElement<GUILayoutY>();
@@ -88,6 +92,11 @@ namespace BansheeEngine
 		onValueChanged(getValue());
 	}
 
+	void GUIVector3Field::inputConfirmed()
+	{
+		onConfirm();
+	}
+
 	const String& GUIVector3Field::getGUITypeName()
 	{
 		static String typeName = "GUIVector3Field";

+ 10 - 0
BansheeEditor/Source/BsGUIVector4Field.cpp

@@ -29,6 +29,11 @@ namespace BansheeEngine
 		mFieldZ->onValueChanged.connect(std::bind(&GUIVector4Field::valueChanged, this, _1));
 		mFieldW->onValueChanged.connect(std::bind(&GUIVector4Field::valueChanged, this, _1));
 
+		mFieldX->onConfirm.connect(std::bind(&GUIVector4Field::inputConfirmed, this));
+		mFieldY->onConfirm.connect(std::bind(&GUIVector4Field::inputConfirmed, this));
+		mFieldZ->onConfirm.connect(std::bind(&GUIVector4Field::inputConfirmed, this));
+		mFieldW->onConfirm.connect(std::bind(&GUIVector4Field::inputConfirmed, this));
+
 		mLayout->removeElement(mLabel);
 
 		GUILayout* layout = mLayout->addNewElement<GUILayoutY>();
@@ -94,6 +99,11 @@ namespace BansheeEngine
 		onValueChanged(getValue());
 	}
 
+	void GUIVector4Field::inputConfirmed()
+	{
+		onConfirm();
+	}
+
 	const String& GUIVector4Field::getGUITypeName()
 	{
 		static String typeName = "GUIVector4Field";

+ 0 - 5
BansheeEngine/Include/BsGUIButtonBase.h

@@ -113,11 +113,6 @@ namespace BansheeEngine
 		 */
 		virtual void updateRenderElementsInternal() override;
 
-		/**
-		 * @copydoc GUIElement::updateBounds
-		 */
-		virtual void updateClippedBounds() override;
-
 		/**
 		 * @copydoc GUIElement::mouseEvent
 		 */

+ 1 - 1
BansheeEngine/Include/BsGUICommandEvent.h

@@ -10,7 +10,7 @@ namespace BansheeEngine
 	enum class GUICommandEventType
 	{
 		Redraw, FocusLost, FocusGained, MoveLeft, MoveRight, MoveUp, MoveDown, 
-		SelectLeft, SelectRight, SelectUp, SelectDown, Escape, Delete, Backspace, Return
+		SelectLeft, SelectRight, SelectUp, SelectDown, Escape, Delete, Backspace, Return, Confirm
 	};
 
 	/**

+ 4 - 0
BansheeEngine/Include/BsGUIContent.h

@@ -60,6 +60,10 @@ namespace BansheeEngine
 		 */
 		const HString& getTooltip() const { return mTooltipText; }
 
+		/**
+		 * @brief	Determines the spacing between text and image content in pixels.
+		 */
+		static const UINT32 IMAGE_TEXT_SPACING;
 	private:
 		HString mText;
 		HSpriteTexture mImage;

+ 0 - 5
BansheeEngine/Include/BsGUIDropDownContent.h

@@ -91,11 +91,6 @@ namespace BansheeEngine
 		 */
 		Vector2I _getOptimalSize() const override;
 
-		/**
-		 * @copydoc	GUIElementContainer::updateClippedBounds
-		 */
-		void updateClippedBounds() override;
-
 		/**
 		 * @copydoc	GUIElementContainer::_updateLayoutInternal
 		 */

+ 1 - 1
BansheeEngine/Include/BsGUIElement.h

@@ -341,7 +341,7 @@ namespace BansheeEngine
 		 * @brief	Called whenever element clipped bounds need to be recalculated. (e.g. when
 		 *			width, height or clip rectangles changes).
 		 */
-		virtual void updateClippedBounds() = 0;
+		virtual void updateClippedBounds();
 
 		/**
 		 * @brief	Helper method that returns style name used by an element of a certain type.

+ 0 - 5
BansheeEngine/Include/BsGUIElementContainer.h

@@ -36,11 +36,6 @@ namespace BansheeEngine
 		virtual void _fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, 
 			UINT32 maxNumQuads, UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const override;
 
-		/**
-		 * @copydoc GUIElement::updateClippedBounds
-		 */
-		virtual void updateClippedBounds() override;
-
 		/**
 		 * @copydoc GUIElement::_getOptimalSize
 		 */

+ 2 - 7
BansheeEngine/Include/BsGUIInputBox.h

@@ -97,14 +97,9 @@ namespace BansheeEngine
 		Event<void(const WString&)> onValueChanged;
 
 		/**
-		 * @brief	Triggered whenever the input box receives focus.
+		 * @brief	Triggered when the user hits the Enter key with the input box in focus.
 		 */
-		Event<void()> onFocusGained;
-
-		/**
-		 * @brief	Triggered whenever the input box loses focus.
-		 */
-		Event<void()> onFocusLost;
+		Event<void()> onConfirm;
 	protected:
 		GUIInputBox(const String& styleName, const GUIDimensions& dimensions, bool multiline);
 		virtual ~GUIInputBox();

+ 0 - 4
BansheeEngine/Include/BsGUILabel.h

@@ -105,10 +105,6 @@ namespace BansheeEngine
 		 */
 		virtual void updateRenderElementsInternal() override;
 
-		/**
-		 * @copydoc GUIElement::updateBounds
-		 */
-		virtual void updateClippedBounds() override;
 	private:
 		GUILabel(const String& styleName, const GUIContent& content, const GUIDimensions& dimensions);
 

+ 0 - 5
BansheeEngine/Include/BsGUITexture.h

@@ -202,11 +202,6 @@ namespace BansheeEngine
 		 */
 		virtual void updateRenderElementsInternal() override;
 
-		/**
-		 * @copydoc GUIElement::updateBounds
-		 */
-		virtual void updateClippedBounds() override;
-
 		/**
 		 * @copydoc GUIElement::styleUpdated
 		 */

+ 5 - 8
BansheeEngine/Source/BsGUIButtonBase.cpp

@@ -169,12 +169,6 @@ namespace BansheeEngine
 		GUIElement::updateRenderElementsInternal();
 	}
 
-	void GUIButtonBase::updateClippedBounds()
-	{
-		mClippedBounds = mLayoutData.area;
-		mClippedBounds.clip(mLayoutData.clipRect);
-	}
-
 	Vector2I GUIButtonBase::_getOptimalSize() const
 	{
 		UINT32 imageWidth = 0;
@@ -241,12 +235,15 @@ namespace BansheeEngine
 		{
 			Rect2I imageBounds = mContentImageSprite->getBounds(Vector2I(), Rect2I());
 			INT32 imageXOffset = 0;
+			INT32 textImageSpacing = 0;
 			
 			if (textBounds.width == 0)
 			{
 				UINT32 freeWidth = (UINT32)std::max(0, contentBounds.width - textBounds.width - imageBounds.width);
 				imageXOffset = (INT32)(freeWidth / 2);
 			}
+			else
+				textImageSpacing = GUIContent::IMAGE_TEXT_SPACING;
 
 			if(_getStyle()->imagePosition == GUIImagePosition::Right)
 			{
@@ -256,7 +253,7 @@ namespace BansheeEngine
 				textClipRect = contentClipRect;
 				textClipRect.width = std::min(contentBounds.width - imageReservedWidth, textClipRect.width);
 
-				imageOffset = Vector2I(contentBounds.x + textBounds.width + imageXOffset, contentBounds.y);
+				imageOffset = Vector2I(contentBounds.x + textBounds.width + imageXOffset + textImageSpacing, contentBounds.y);
 				imageClipRect = contentClipRect;
 				imageClipRect.x -= textBounds.width + imageXOffset;
 			}
@@ -269,7 +266,7 @@ namespace BansheeEngine
 				imageClipRect.x -= imageXOffset;
 				imageClipRect.width = std::min(imageReservedWidth, imageClipRect.width);
 
-				textOffset = Vector2I(contentBounds.x + imageReservedWidth, contentBounds.y);
+				textOffset = Vector2I(contentBounds.x + imageReservedWidth + textImageSpacing, contentBounds.y);
 				textClipRect = contentClipRect;
 				textClipRect.x -= imageReservedWidth;
 			}

+ 2 - 0
BansheeEngine/Source/BsGUIContent.cpp

@@ -2,6 +2,8 @@
 
 namespace BansheeEngine
 {
+	const UINT32 GUIContent::IMAGE_TEXT_SPACING = 3;
+
 	GUIContent::GUIContent()
 		:mText(L"")
 	{ }

+ 1 - 7
BansheeEngine/Source/BsGUIDropDownContent.cpp

@@ -235,7 +235,7 @@ namespace BansheeEngine
 			}
 		}
 			return true;
-		case GUICommandEventType::Return:
+		case GUICommandEventType::Confirm:
 			if (mSelectedIdx == UINT_MAX)
 				selectNext(0);
 			else
@@ -384,12 +384,6 @@ namespace BansheeEngine
 		return optimalSize;
 	}
 
-	void GUIDropDownContent::updateClippedBounds()
-	{
-		mClippedBounds = mLayoutData.area;
-		mClippedBounds.clip(mLayoutData.clipRect);
-	}
-
 	void GUIDropDownContent::_updateLayoutInternal(const GUILayoutData& data)
 	{
 		GUILayoutData childData = data;

+ 6 - 0
BansheeEngine/Source/BsGUIElement.cpp

@@ -34,6 +34,12 @@ namespace BansheeEngine
 		updateClippedBounds();
 	}
 
+	void GUIElement::updateClippedBounds()
+	{
+		mClippedBounds = mLayoutData.area;
+		mClippedBounds.clip(mLayoutData.clipRect);
+	}
+
 	void GUIElement::setStyle(const String& styleName)
 	{
 		mStyleName = styleName;

+ 0 - 5
BansheeEngine/Source/BsGUIElementContainer.cpp

@@ -25,11 +25,6 @@ namespace BansheeEngine
 		return 0;
 	}
 
-	void GUIElementContainer::updateClippedBounds()
-	{
-		mClippedBounds = Rect2I(0, 0, 0, 0); // We don't want any mouse input for this element. This is just a container.
-	}
-
 	void GUIElementContainer::_fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads, 
 		UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const
 	{ }

+ 4 - 9
BansheeEngine/Source/BsGUIHelper.cpp

@@ -16,20 +16,15 @@ namespace BansheeEngine
 
 	Vector2I GUIHelper::calcOptimalContentsSize(const GUIContent& content, const GUIElementStyle& style, const GUIDimensions& dimensions)
 	{
-		Vector2I textContentBounds = calcOptimalContentsSize((const WString&)content.getText(), style, dimensions);
+		Vector2I contentBounds = calcOptimalContentsSize((const WString&)content.getText(), style, dimensions);
 
-		Vector2I imageSize;
-		imageSize.x = style.margins.left + style.margins.right + style.contentOffset.left + style.contentOffset.right;
-		imageSize.y = style.margins.top + style.margins.bottom + style.contentOffset.top + style.contentOffset.bottom;
 		if(content.getImage() != nullptr)
 		{
-			imageSize.x += content.getImage()->getWidth();
-			imageSize.y += content.getImage()->getHeight();
-
-			imageSize = dimensions.calculateSizeRange(imageSize).optimal;
+			contentBounds.x += content.getImage()->getWidth() + GUIContent::IMAGE_TEXT_SPACING;
+			contentBounds.y = std::max(content.getImage()->getHeight(), (UINT32)contentBounds.y);
 		}
 
-		return Vector2I(std::max((UINT32)textContentBounds.x, (UINT32)imageSize.x), std::max((UINT32)textContentBounds.y, (UINT32)imageSize.y));
+		return contentBounds;
 	}
 
 	Vector2I GUIHelper::calcOptimalContentsSize(const WString& text, const GUIElementStyle& style, const GUIDimensions& dimensions)

+ 10 - 11
BansheeEngine/Source/BsGUIInputBox.cpp

@@ -606,9 +606,6 @@ namespace BansheeEngine
 			else
 				_markContentAsDirty();
 
-			if(!onFocusGained.empty())
-				onFocusGained();
-
 			return true;
 		}
 		
@@ -628,9 +625,6 @@ namespace BansheeEngine
 			else
 				_markContentAsDirty();
 
-			if(!onFocusLost.empty())
-				onFocusLost();
-
 			return true;
 		}
 		
@@ -865,17 +859,17 @@ namespace BansheeEngine
 
 		if(ev.getType() == GUICommandEventType::Return)
 		{
-			if(mIsMultiline)
+			if (mIsMultiline)
 			{
 				Vector2I origSize = mDimensions.calculateSizeRange(_getOptimalSize()).optimal;
 
-				if(mSelectionShown)
+				if (mSelectionShown)
 					deleteSelectedText();
 
 				UINT32 charIdx = gGUIManager().getInputCaretTool()->getCharIdxAtCaretPos();
 
 				bool filterOkay = true;
-				if(mFilter != nullptr)
+				if (mFilter != nullptr)
 				{
 					WString newText = mText;
 					newText.insert(newText.begin() + charIdx, '\n');
@@ -883,14 +877,14 @@ namespace BansheeEngine
 					filterOkay = mFilter(newText);
 				}
 
-				if(filterOkay)
+				if (filterOkay)
 				{
 					insertChar(charIdx, '\n');
 
 					gGUIManager().getInputCaretTool()->moveCaretRight();
 					scrollTextToCaret();
 
-					if(!onValueChanged.empty())
+					if (!onValueChanged.empty())
 						onValueChanged(mText);
 				}
 
@@ -902,7 +896,12 @@ namespace BansheeEngine
 
 				return true;
 			}
+		}
 
+		if (ev.getType() == GUICommandEventType::Confirm)
+		{
+			onConfirm();
+			return true;
 		}
 
 		return baseReturn;

+ 0 - 6
BansheeEngine/Source/BsGUILabel.cpp

@@ -52,12 +52,6 @@ namespace BansheeEngine
 		GUIElement::updateRenderElementsInternal();
 	}
 
-	void GUILabel::updateClippedBounds()
-	{
-		mClippedBounds = mLayoutData.area;
-		mClippedBounds.clip(mLayoutData.clipRect);
-	}
-
 	Vector2I GUILabel::_getOptimalSize() const
 	{
 		return GUIHelper::calcOptimalContentsSize(mContent, *_getStyle(), _getDimensions());

+ 3 - 0
BansheeEngine/Source/BsGUIManager.cpp

@@ -1096,6 +1096,9 @@ namespace BansheeEngine
 		case InputCommandType::Return:
 			mCommandEvent.setType(GUICommandEventType::Return);
 			break;
+		case InputCommandType::Confirm:
+			mCommandEvent.setType(GUICommandEventType::Confirm);
+			break;
 		case InputCommandType::Escape:
 			mCommandEvent.setType(GUICommandEventType::Escape);
 			break;

+ 0 - 6
BansheeEngine/Source/BsGUITexture.cpp

@@ -203,12 +203,6 @@ namespace BansheeEngine
 		GUIElement::updateRenderElementsInternal();
 	}
 
-	void GUITexture::updateClippedBounds()
-	{
-		mClippedBounds = mLayoutData.area;
-		mClippedBounds.clip(mLayoutData.clipRect);
-	}
-
 	void GUITexture::styleUpdated()
 	{
 		if (mUsingStyleTexture)

+ 2 - 2
BansheeUtility/Include/BsFrameAlloc.h

@@ -8,8 +8,8 @@ namespace BansheeEngine
 	 * @brief	Frame allocator. Performs very fast allocations but can only free all of its memory at once.
 	 * 			Perfect for allocations that last just a single frame.
 	 * 			
-	 * @note	Not thread safe with an exception. "alloc" and "clear" methods need to be called from the same thread.
-	 * 			"dealloc" is thread safe and can be called from any thread.
+	 * @note	Not thread safe with an exception. ::alloc and ::clear methods need to be called from the same thread.
+	 * 			::dealloc is thread safe and can be called from any thread.
 	 */
 	class BS_UTILITY_EXPORT FrameAlloc
 	{

+ 28 - 21
MBansheeEditor/EditorApplication.cs

@@ -132,7 +132,8 @@ namespace BansheeEditor
 
         private static EditorApplication instance;
         private static FolderMonitor monitor;
-        private static HashSet<string> dirtyResources;
+        private static HashSet<string> dirtyResources = new HashSet<string>();
+        private static bool sceneDirty;
 
         /// <summary>
         /// Constructs a new editor application. Called at editor start-up by the runtime.
@@ -250,7 +251,7 @@ namespace BansheeEditor
                 (scenePath) =>
                 {
                     Scene.Load(path);
-                    SetStatusScene(Scene.ActiveSceneName, false);
+                    SetSceneDirty(false);
 
                     ProjectSettings.LastOpenScene = scenePath;
                     ProjectSettings.Save();
@@ -285,8 +286,7 @@ namespace BansheeEditor
         public static void SaveScene(string path)
         {
             Internal_SaveScene(path);
-            SetStatusScene(Scene.ActiveSceneName, false);
-            dirtyResources.Remove(path);
+            SetSceneDirty(false);
         }
 
         /// <summary>
@@ -361,7 +361,6 @@ namespace BansheeEditor
                 UnloadProject();
 
             Internal_LoadProject(path); // Triggers OnProjectLoaded when done
-            SetStatusProject(false);
         }
 
         /// <summary>
@@ -383,33 +382,39 @@ namespace BansheeEditor
             if (resource == null)
                 return;
 
-            if (Scene.ActiveSceneUUID == resource.UUID)
-            {
-                SetStatusScene(Scene.ActiveSceneName, true);
-            }
-
             SetStatusProject(true);
             dirtyResources.Add(resource.UUID);
         }
 
         /// <summary>
-        /// Checks is the specific resource dirty and needs saving.
+        /// Marks the current scene as dirty.
         /// </summary>
-        /// <param name="resource">Resource to check.</param>
-        /// <returns>True if the resource requires saving, false otherwise.</returns>
-        public static bool IsDirty(Resource resource)
+        public static void SetSceneDirty()
         {
-            return dirtyResources.Contains(resource.UUID);
+            SetSceneDirty(true);
         }
 
         /// <summary>
-        /// Checks is the resource with the specified UUID dirty and needs saving.
+        /// Marks the current scene as clean or dirty.
         /// </summary>
-        /// <param name="uuid">Identifier of the resource to check.</param>
+        /// <param name="dirty">Should the scene be marked as clean or dirty.</param>
+        internal static void SetSceneDirty(bool dirty)
+        {
+            sceneDirty = dirty;
+            SetStatusScene(Scene.ActiveSceneName, dirty);
+
+            if (!dirty)
+                dirtyResources.Remove(Scene.ActiveSceneUUID);
+        }
+
+        /// <summary>
+        /// Checks is the specific resource dirty and needs saving.
+        /// </summary>
+        /// <param name="resource">Resource to check.</param>
         /// <returns>True if the resource requires saving, false otherwise.</returns>
-        public static bool IsDirty(string uuid)
+        public static bool IsDirty(Resource resource)
         {
-            return dirtyResources.Contains(uuid);
+            return dirtyResources.Contains(resource.UUID);
         }
 
         /// <summary>
@@ -417,6 +422,8 @@ namespace BansheeEditor
         /// </summary>
         private static void OnProjectLoaded()
         {
+            SetStatusProject(false);
+
             if (!IsProjectLoaded)
             {
                 ProjectWindow.Open();
@@ -486,8 +493,8 @@ namespace BansheeEditor
                     if(window != null)
                         window.Reset();
 
+                    SetSceneDirty(false);
                     Internal_UnloadProject();
-                    SetStatusScene("None", false);
                     SetStatusProject(false);
                 };
 
@@ -534,7 +541,7 @@ namespace BansheeEditor
         /// <returns>True if the scene was never saved, or was modified after last save.</returns>
         public static bool IsSceneModified()
         {
-            return IsDirty(Scene.ActiveSceneUUID);
+            return sceneDirty;
         }
 
         [MethodImpl(MethodImplOptions.InternalCall)]

+ 2 - 2
MBansheeEditor/GUI/GUIDictionaryField.cs

@@ -33,7 +33,7 @@ namespace BansheeEditor
         /// <summary>
         /// Constructs a new GUI dictionary.
         /// </summary>
-        ///  <param name="title">Label to display on the dictionary GUI title.</param>
+        /// <param name="title">Label to display on the dictionary GUI title.</param>
         /// <param name="layout">Layout to which to append the list GUI elements to.</param>
         /// <param name="depth">Determines at which depth to render the background. Useful when you have multiple
         ///                     nested containers whose backgrounds are overlaping. Also determines background style,
@@ -736,7 +736,7 @@ namespace BansheeEditor
                     }
                 }
 
-                for (int i = newNumRows; i < oldNumRows; i++)
+                for (int i = oldNumRows; i >= newNumRows; i--)
                 {
                     rows[i].Destroy();
                     rows.Remove(i);

+ 30 - 16
MBansheeEditor/GUI/GUIFloatField.cs

@@ -9,12 +9,15 @@ namespace BansheeEditor
     /// </summary>
     public sealed class GUIFloatField : GUIElement
     {
-        public delegate void OnChangedDelegate(float newValue);
-
         /// <summary>
         /// Triggered when the value in the field changes.
         /// </summary>
-        public event OnChangedDelegate OnChanged;
+        public event Action<float> OnChanged;
+
+        /// <summary>
+        /// Triggered whenever user confirms input.
+        /// </summary>
+        public event Action OnConfirmed;
 
         /// <summary>
         /// Value displayed by the field input box.
@@ -31,6 +34,20 @@ namespace BansheeEditor
             set { Internal_SetValue(mCachedPtr, value); }
         }
 
+        /// <summary>
+        /// Checks does the element currently has input focus. Input focus means the element has an input caret displayed
+        /// and will accept input from the keyboard.
+        /// </summary>
+        public bool HasInputFocus
+        {
+            get
+            {
+                bool value;
+                Internal_HasInputFocus(mCachedPtr, out value);
+                return value;
+            }
+        }
+
         /// <summary>
         /// Creates a new float field element with a label.
         /// </summary>
@@ -59,18 +76,6 @@ namespace BansheeEditor
             Internal_CreateInstance(this, null, 0, style, options, false);
         }
 
-        /// <summary>
-        /// Checks does the element currently has input focus. Input focus means the element has an input caret displayed
-        /// and will accept input from the keyboard.
-        /// </summary>
-        /// <returns>True if the element has input focus.</returns>
-        public bool HasInputFocus()
-        {
-            bool value;
-            Internal_HasInputFocus(mCachedPtr, out value);
-            return value;
-        }
-
         /// <summary>
         /// Colors the element with a specific tint.
         /// </summary>
@@ -95,12 +100,21 @@ namespace BansheeEditor
         /// Triggered by the runtime when the value of the float field changes.
         /// </summary>
         /// <param name="newValue">New value of the float field.</param>
-        private void DoOnChanged(float newValue)
+        private void Internal_DoOnChanged(float newValue)
         {
             if (OnChanged != null)
                 OnChanged(newValue);
         }
 
+        /// <summary>
+        /// Triggered by the native interop object when the user confirms the input.
+        /// </summary>
+        private void Internal_DoOnConfirmed()
+        {
+            if (OnConfirmed != null)
+                OnConfirmed();
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_CreateInstance(GUIFloatField instance, GUIContent title, int titleWidth,
             string style, GUIOption[] options, bool withTitle);

+ 30 - 16
MBansheeEditor/GUI/GUIIntField.cs

@@ -9,12 +9,15 @@ namespace BansheeEditor
     /// </summary>
     public sealed class GUIIntField : GUIElement
     {
-        public delegate void OnChangedDelegate(int newValue);
-
         /// <summary>
         /// Triggered when the value in the field changes.
         /// </summary>
-        public event OnChangedDelegate OnChanged;
+        public event Action<int> OnChanged;
+
+        /// <summary>
+        /// Triggered whenever user confirms input.
+        /// </summary>
+        public event Action OnConfirmed;
 
         /// <summary>
         /// Value displayed by the field input box.
@@ -31,6 +34,20 @@ namespace BansheeEditor
             set { Internal_SetValue(mCachedPtr, value); }
         }
 
+        /// <summary>
+        /// Checks does the element currently has input focus. Input focus means the element has an input caret displayed
+        /// and will accept input from the keyboard.
+        /// </summary>
+        public bool HasInputFocus
+        {
+            get
+            {
+                bool value;
+                Internal_HasInputFocus(mCachedPtr, out value);
+                return value;
+            }
+        }
+
         /// <summary>
         /// Creates a new integer field element with a label.
         /// </summary>
@@ -59,18 +76,6 @@ namespace BansheeEditor
             Internal_CreateInstance(this, null, 0, style, options, false);
         }
 
-        /// <summary>
-        /// Checks does the element currently has input focus. Input focus means the element has an input caret displayed
-        /// and will accept input from the keyboard.
-        /// </summary>
-        /// <returns>True if the element has input focus.</returns>
-        public bool HasInputFocus()
-        {
-            bool value;
-            Internal_HasInputFocus(mCachedPtr, out value);
-            return value;
-        }
-
         /// <summary>
         /// Sets a range that will input field values will be clamped to. Set to large negative/positive values if clamping
         /// is not required.
@@ -95,12 +100,21 @@ namespace BansheeEditor
         /// Triggered by the runtime when the value of the float field changes.
         /// </summary>
         /// <param name="newValue">New value of the float field.</param>
-        private void DoOnChanged(int newValue)
+        private void Internal_DoOnChanged(int newValue)
         {
             if (OnChanged != null)
                 OnChanged(newValue);
         }
 
+        /// <summary>
+        /// Triggered by the native interop object when the user confirms the input.
+        /// </summary>
+        private void Internal_DoOnConfirmed()
+        {
+            if (OnConfirmed != null)
+                OnConfirmed();
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_CreateInstance(GUIIntField instance, GUIContent title, int titleWidth, 
             string style, GUIOption[] options, bool withTitle);

+ 8 - 0
MBansheeEditor/GUI/GUISceneTreeView.cs

@@ -40,6 +40,14 @@ namespace BansheeEditor
             Internal_Update(mCachedPtr);
         }
 
+        /// <summary>
+        /// Triggered by the runtime when the scene is modified from the native scene tree view.
+        /// </summary>
+        private void Internal_DoOnModified()
+        {
+            EditorApplication.SetSceneDirty();
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_CreateInstance(GUISceneTreeView instance, string style, GUIOption[] options);
 

+ 0 - 15
MBansheeEditor/GUI/GUISliderField.cs

@@ -58,18 +58,6 @@ namespace BansheeEditor
             Internal_CreateInstance(this, min, max, null, 0, style, options, false);
         }
 
-        /// <summary>
-        /// Checks does the element currently has input focus. Input focus means the element has an input caret displayed
-        /// and will accept input from the keyboard.
-        /// </summary>
-        /// <returns>True if the element has input focus.</returns>
-        public bool HasInputFocus()
-        {
-            bool value;
-            Internal_HasInputFocus(mCachedPtr, out value);
-            return value;
-        }
-
         /// <summary>
         /// Colors the element with a specific tint.
         /// </summary>
@@ -119,9 +107,6 @@ namespace BansheeEditor
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_SetValue(IntPtr nativeInstance, float value);
 
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_HasInputFocus(IntPtr nativeInstance, out bool value);
-
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_SetTint(IntPtr nativeInstance, Color color);
 

+ 31 - 17
MBansheeEditor/GUI/GUITextField.cs

@@ -9,17 +9,20 @@ namespace BansheeEditor
     /// </summary>
     public sealed class GUITextField : GUIElement
     {
-        public delegate void OnChangedDelegate(String newValue);
-
         /// <summary>
         /// Triggered when the value in the field changes.
         /// </summary>
-        public event OnChangedDelegate OnChanged;
+        public event Action<string> OnChanged;
+
+        /// <summary>
+        /// Triggered whenever user confirms input.
+        /// </summary>
+        public event Action OnConfirmed;
 
         /// <summary>
         /// Value displayed by the field input box.
         /// </summary>
-        public String Value
+        public string Value
         {
             get
             {
@@ -31,6 +34,20 @@ namespace BansheeEditor
             set { Internal_SetValue(mCachedPtr, value); }
         }
 
+        /// <summary>
+        /// Checks does the element currently has input focus. Input focus means the element has an input caret displayed
+        /// and will accept input from the keyboard.
+        /// </summary>
+        public bool HasInputFocus
+        {
+            get
+            {
+                bool value;
+                Internal_HasInputFocus(mCachedPtr, out value);
+                return value;
+            }
+        }
+
         /// <summary>
         /// Creates a new text field element with a label.
         /// </summary>
@@ -61,18 +78,6 @@ namespace BansheeEditor
             Internal_CreateInstance(this, multiline, null, 0, style, options, false);
         }
 
-        /// <summary>
-        /// Checks does the element currently has input focus. Input focus means the element has an input caret displayed
-        /// and will accept input from the keyboard.
-        /// </summary>
-        /// <returns>True if the element has input focus.</returns>
-        public bool HasInputFocus()
-        {
-            bool value;
-            Internal_HasInputFocus(mCachedPtr, out value);
-            return value;
-        }
-
         /// <summary>
         /// Colors the element with a specific tint.
         /// </summary>
@@ -86,12 +91,21 @@ namespace BansheeEditor
         /// Triggered by the runtime when the value of the text field changes.
         /// </summary>
         /// <param name="newValue">New value of the text field.</param>
-        private void DoOnChanged(String newValue)
+        private void Internal_DoOnChanged(String newValue)
         {
             if (OnChanged != null)
                 OnChanged(newValue);
         }
 
+        /// <summary>
+        /// Triggered by the native interop object when the user confirms the input.
+        /// </summary>
+        private void Internal_DoOnConfirmed()
+        {
+            if (OnConfirmed != null)
+                OnConfirmed();
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_CreateInstance(GUITextField instance, bool multiline, GUIContent title, int titleWidth,
             string style, GUIOption[] options, bool withTitle);

+ 30 - 16
MBansheeEditor/GUI/GUIVector2Field.cs

@@ -9,12 +9,15 @@ namespace BansheeEditor
     /// </summary>
     public sealed class GUIVector2Field : GUIElement
     {
-        public delegate void OnChangedDelegate(Vector2 newValue);
-
         /// <summary>
         /// Triggered when the value in the field changes.
         /// </summary>
-        public event OnChangedDelegate OnChanged;
+        public event Action<Vector2> OnChanged;
+
+        /// <summary>
+        /// Triggered whenever user confirms input.
+        /// </summary>
+        public event Action OnConfirmed;
 
         /// <summary>
         /// Value displayed by the field input box.
@@ -31,6 +34,20 @@ namespace BansheeEditor
             set { Internal_SetValue(mCachedPtr, value); }
         }
 
+        /// <summary>
+        /// Checks does the element currently has input focus. Input focus means the element has an input caret displayed
+        /// and will accept input from the keyboard.
+        /// </summary>
+        public bool HasInputFocus
+        {
+            get
+            {
+                bool value;
+                Internal_HasInputFocus(mCachedPtr, out value);
+                return value;
+            }
+        }
+
         /// <summary>
         /// Creates a new 2D vector field element with a label.
         /// </summary>
@@ -59,18 +76,6 @@ namespace BansheeEditor
             Internal_CreateInstance(this, null, 0, style, options, false);
         }
 
-        /// <summary>
-        /// Checks does the element currently has input focus. Input focus means the element has an input caret displayed
-        /// and will accept input from the keyboard.
-        /// </summary>
-        /// <returns>True if the element has input focus.</returns>
-        public bool HasInputFocus()
-        {
-            bool value;
-            Internal_HasInputFocus(mCachedPtr, out value);
-            return value;
-        }
-
         /// <summary>
         /// Colors the element with a specific tint.
         /// </summary>
@@ -84,12 +89,21 @@ namespace BansheeEditor
         /// Triggered by the runtime when the value of the field changes.
         /// </summary>
         /// <param name="newValue">New value of the field.</param>
-        private void DoOnChanged(Vector2 newValue)
+        private void Internal_DoOnChanged(Vector2 newValue)
         {
             if (OnChanged != null)
                 OnChanged(newValue);
         }
 
+        /// <summary>
+        /// Triggered by the native interop object when the user confirms the input.
+        /// </summary>
+        private void Internal_DoOnConfirmed()
+        {
+            if (OnConfirmed != null)
+                OnConfirmed();
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_CreateInstance(GUIVector2Field instance, GUIContent title, int titleWidth,
             string style, GUIOption[] options, bool withTitle);

+ 30 - 16
MBansheeEditor/GUI/GUIVector3Field.cs

@@ -9,12 +9,15 @@ namespace BansheeEditor
     /// </summary>
     public sealed class GUIVector3Field : GUIElement
     {
-        public delegate void OnChangedDelegate(Vector3 newValue);
-
         /// <summary>
         /// Triggered when the value in the field changes.
         /// </summary>
-        public event OnChangedDelegate OnChanged;
+        public event Action<Vector3> OnChanged;
+
+        /// <summary>
+        /// Triggered whenever user confirms input.
+        /// </summary>
+        public event Action OnConfirmed;
 
         /// <summary>
         /// Value displayed by the field input box.
@@ -31,6 +34,20 @@ namespace BansheeEditor
             set { Internal_SetValue(mCachedPtr, value); }
         }
 
+        /// <summary>
+        /// Checks does the element currently has input focus. Input focus means the element has an input caret displayed
+        /// and will accept input from the keyboard.
+        /// </summary>
+        public bool HasInputFocus
+        {
+            get
+            {
+                bool value;
+                Internal_HasInputFocus(mCachedPtr, out value);
+                return value;
+            }
+        }
+
         /// <summary>
         /// Creates a new 3D vector field element with a label.
         /// </summary>
@@ -59,18 +76,6 @@ namespace BansheeEditor
             Internal_CreateInstance(this, null, 0, style, options, false);
         }
 
-        /// <summary>
-        /// Checks does the element currently has input focus. Input focus means the element has an input caret displayed
-        /// and will accept input from the keyboard.
-        /// </summary>
-        /// <returns>True if the element has input focus.</returns>
-        public bool HasInputFocus()
-        {
-            bool value;
-            Internal_HasInputFocus(mCachedPtr, out value);
-            return value;
-        }
-
         /// <summary>
         /// Colors the element with a specific tint.
         /// </summary>
@@ -84,12 +89,21 @@ namespace BansheeEditor
         /// Triggered by the runtime when the value of the field changes.
         /// </summary>
         /// <param name="newValue">New value of the field.</param>
-        private void DoOnChanged(Vector3 newValue)
+        private void Internal_DoOnChanged(Vector3 newValue)
         {
             if (OnChanged != null)
                 OnChanged(newValue);
         }
 
+        /// <summary>
+        /// Triggered by the native interop object when the user confirms the input.
+        /// </summary>
+        private void Internal_DoOnConfirmed()
+        {
+            if (OnConfirmed != null)
+                OnConfirmed();
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_CreateInstance(GUIVector3Field instance, GUIContent title, int titleWidth,
             string style, GUIOption[] options, bool withTitle);

+ 30 - 16
MBansheeEditor/GUI/GUIVector4Field.cs

@@ -9,12 +9,15 @@ namespace BansheeEditor
     /// </summary>
     public sealed class GUIVector4Field : GUIElement
     {
-        public delegate void OnChangedDelegate(Vector4 newValue);
-
         /// <summary>
         /// Triggered when the value in the field changes.
         /// </summary>
-        public event OnChangedDelegate OnChanged;
+        public event Action<Vector4> OnChanged;
+
+        /// <summary>
+        /// Triggered whenever user confirms input.
+        /// </summary>
+        public event Action OnConfirmed;
 
         /// <summary>
         /// Value displayed by the field input box.
@@ -31,6 +34,20 @@ namespace BansheeEditor
             set { Internal_SetValue(mCachedPtr, value); }
         }
 
+        /// <summary>
+        /// Checks does the element currently has input focus. Input focus means the element has an input caret displayed
+        /// and will accept input from the keyboard.
+        /// </summary>
+        public bool HasInputFocus
+        {
+            get
+            {
+                bool value;
+                Internal_HasInputFocus(mCachedPtr, out value);
+                return value;
+            }
+        }
+
         /// <summary>
         /// Creates a new 4D vector field element with a label.
         /// </summary>
@@ -59,18 +76,6 @@ namespace BansheeEditor
             Internal_CreateInstance(this, null, 0, style, options, false);
         }
 
-        /// <summary>
-        /// Checks does the element currently has input focus. Input focus means the element has an input caret displayed
-        /// and will accept input from the keyboard.
-        /// </summary>
-        /// <returns>True if the element has input focus.</returns>
-        public bool HasInputFocus()
-        {
-            bool value;
-            Internal_HasInputFocus(mCachedPtr, out value);
-            return value;
-        }
-
         /// <summary>
         /// Colors the element with a specific tint.
         /// </summary>
@@ -84,12 +89,21 @@ namespace BansheeEditor
         /// Triggered by the runtime when the value of the field changes.
         /// </summary>
         /// <param name="newValue">New value of the field.</param>
-        private void DoOnChanged(Vector4 newValue)
+        private void Internal_DoOnChanged(Vector4 newValue)
         {
             if (OnChanged != null)
                 OnChanged(newValue);
         }
 
+        /// <summary>
+        /// Triggered by the native interop object when the user confirms the input.
+        /// </summary>
+        private void Internal_DoOnConfirmed()
+        {
+            if (OnConfirmed != null)
+                OnConfirmed();
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_CreateInstance(GUIVector4Field instance, GUIContent title, int titleWidth,
             string style, GUIOption[] options, bool withTitle);

+ 7 - 2
MBansheeEditor/Inspector/InspectableArray.cs

@@ -51,12 +51,17 @@ namespace BansheeEditor
         }
 
         /// <inheritdoc/>
-        public override void Refresh(int layoutIndex)
+        public override bool Refresh(int layoutIndex)
         {
-            if (IsModified())
+            bool isModified = IsModified();
+
+            if (isModified)
                 Update(layoutIndex);
 
             arrayGUIField.Refresh();
+            //isModified |= arrayGUIField.Refresh();
+
+            return isModified;
         }
 
         /// <inheritdoc/>

+ 7 - 2
MBansheeEditor/Inspector/InspectableDictionary.cs

@@ -53,12 +53,17 @@ namespace BansheeEditor
         }
 
         /// <inheritdoc/>
-        public override void Refresh(int layoutIndex)
+        public override bool Refresh(int layoutIndex)
         {
-            if (IsModified())
+            bool isModified = IsModified();
+
+            if (isModified)
                 Update(layoutIndex);
 
             dictionaryGUIField.Refresh();
+            //isModified |= dictionaryGUIField.Refresh();
+
+            return true;
         }
 
         /// <inheritdoc/>

+ 7 - 2
MBansheeEditor/Inspector/InspectableField.cs

@@ -37,10 +37,15 @@ namespace BansheeEditor
         /// </summary>
         /// <param name="layoutIndex">Index in the parent's layout at which to insert the GUI elements for this field.
         ///                           </param>
-        public virtual void Refresh(int layoutIndex)
+        /// <returns>True if the field or any of its children were modified.</returns>
+        public virtual bool Refresh(int layoutIndex)
         {
-            if (IsModified())
+            bool isModified = IsModified();
+
+            if (isModified)
                 Update(layoutIndex);
+
+            return isModified;
         }
 
         /// <summary>

+ 1 - 1
MBansheeEditor/Inspector/InspectableFloat.cs

@@ -52,7 +52,7 @@ namespace BansheeEditor
             propertyValue = property.GetValue<float>();
             if (guiFloatField != null)
             {
-                if (guiFloatField.HasInputFocus())
+                if (guiFloatField.HasInputFocus)
                     return;
 
                 guiFloatField.Value = propertyValue;

+ 1 - 1
MBansheeEditor/Inspector/InspectableInt.cs

@@ -52,7 +52,7 @@ namespace BansheeEditor
             propertyValue = property.GetValue<int>();
             if (guiIntField != null)
             {
-                if (guiIntField.HasInputFocus())
+                if (guiIntField.HasInputFocus)
                     return;
 
                 guiIntField.Value = propertyValue;

+ 7 - 2
MBansheeEditor/Inspector/InspectableList.cs

@@ -53,12 +53,17 @@ namespace BansheeEditor
         }
 
         /// <inheritdoc/>
-        public override void Refresh(int layoutIndex)
+        public override bool Refresh(int layoutIndex)
         {
-            if (IsModified())
+            bool isModified = IsModified();
+
+            if (isModified)
                 Update(layoutIndex);
 
             listGUIField.Refresh();
+            //isModified |= listGUIField.Refresh();
+
+            return isModified;
         }
 
         /// <inheritdoc/>

+ 5 - 3
MBansheeEditor/Inspector/InspectableObject.cs

@@ -56,16 +56,18 @@ namespace BansheeEditor
         }
 
         /// <inheritdoc/>
-        public override void Refresh(int layoutIndex)
+        public override bool Refresh(int layoutIndex)
         {
-            base.Refresh(layoutIndex);
+            bool isModified = base.Refresh(layoutIndex);
 
             int currentIndex = 0;
             for (int i = 0; i < children.Count; i++)
             {
-                children[i].Refresh(currentIndex);
+                isModified |= children[i].Refresh(currentIndex);
                 currentIndex += children[i].GetNumLayoutElements();
             }
+
+            return isModified;
         }
 
         /// <inheritdoc/>

+ 1 - 1
MBansheeEditor/Inspector/InspectableString.cs

@@ -52,7 +52,7 @@ namespace BansheeEditor
             propertyValue = property.GetValue<string>();
             if (guiField != null)
             {
-                if (guiField.HasInputFocus())
+                if (guiField.HasInputFocus)
                     return;
 
                 guiField.Value = propertyValue;

+ 1 - 1
MBansheeEditor/Inspector/InspectableVector2.cs

@@ -52,7 +52,7 @@ namespace BansheeEditor
             propertyValue = property.GetValue<Vector2>();
             if (guiField != null)
             {
-                if (guiField.HasInputFocus())
+                if (guiField.HasInputFocus)
                     return;
 
                 guiField.Value = propertyValue;

+ 1 - 1
MBansheeEditor/Inspector/InspectableVector3.cs

@@ -52,7 +52,7 @@ namespace BansheeEditor
             propertyValue = property.GetValue<Vector3>();
             if (guiField != null)
             {
-                if (guiField.HasInputFocus())
+                if (guiField.HasInputFocus)
                     return;
 
                 guiField.Value = propertyValue;

+ 1 - 1
MBansheeEditor/Inspector/InspectableVector4.cs

@@ -52,7 +52,7 @@ namespace BansheeEditor
             propertyValue = property.GetValue<Vector4>();
             if (guiField != null)
             {
-                if (guiField.HasInputFocus())
+                if (guiField.HasInputFocus)
                     return;
 
                 guiField.Value = propertyValue;

+ 27 - 2
MBansheeEditor/Inspector/InspectorWindow.cs

@@ -309,6 +309,8 @@ namespace BansheeEditor
                 return;
 
             soNameInput.Text = activeSO.Name;
+            soNameInput.OnConfirmed += OnSceneObjectRename;
+            soNameInput.OnFocusLost += OnSceneObjectRename;
 
             bool hasPrefab = PrefabUtility.IsPrefabInstance(activeSO);
             if (soHasPrefab != hasPrefab || forceUpdate)
@@ -327,8 +329,8 @@ namespace BansheeEditor
                     GUIButton btnBreakPrefab = new GUIButton(new LocEdString("Break"), GUIOption.FixedWidth(60));
 
                     btnApplyPrefab.OnClick += () => PrefabUtility.ApplyPrefab(activeSO);
-                    btnRevertPrefab.OnClick += () => PrefabUtility.RevertPrefab(activeSO);
-                    btnBreakPrefab.OnClick += () => PrefabUtility.BreakPrefab(activeSO);
+                    btnRevertPrefab.OnClick += () => { PrefabUtility.RevertPrefab(activeSO); EditorApplication.SetSceneDirty(); };
+                    btnBreakPrefab.OnClick += () => { PrefabUtility.BreakPrefab(activeSO); EditorApplication.SetSceneDirty(); };
 
                     soPrefabLayout.AddElement(btnApplyPrefab);
                     soPrefabLayout.AddElement(btnRevertPrefab);
@@ -492,6 +494,8 @@ namespace BansheeEditor
                         {
                             UndoRedo.RecordSO(activeSO, "Added component " + draggedComponentType.Name);
                             activeSO.AddComponent(draggedComponentType);
+
+                            EditorApplication.SetSceneDirty();
                         }
                     }
                 }               
@@ -569,6 +573,8 @@ namespace BansheeEditor
             {
                 UndoRedo.RecordSO(activeSO, "Removed component " + componentType.Name);
                 activeSO.RemoveComponent(componentType);
+
+                EditorApplication.SetSceneDirty();
             }
         }
 
@@ -638,6 +644,19 @@ namespace BansheeEditor
             return new Rect2I(0, 0, Width, 115);
         }
 
+        /// <summary>
+        /// Triggered when the user changes the name of the currently active scene object.
+        /// </summary>
+        private void OnSceneObjectRename()
+        {
+            if (activeSO != null)
+            {
+                UndoRedo.RecordSO(activeSO, "Renamed scene object \"" + soNameInput.Text + "\"");
+                activeSO.Name = soNameInput.Text;
+                EditorApplication.SetSceneDirty();
+            }
+        }
+
         /// <summary>
         /// Triggered when the position value in the currently active <see cref="SceneObject"/> changes. Updates the 
         /// necessary GUI elements.
@@ -661,6 +680,8 @@ namespace BansheeEditor
                 position[idx] = value;
                 activeSO.LocalPosition = position;
             }
+
+            EditorApplication.SetSceneDirty();
         }
 
         /// <summary>
@@ -686,6 +707,8 @@ namespace BansheeEditor
                 angles[idx] = value;
                 activeSO.LocalRotation = Quaternion.FromEuler(angles);
             }
+
+            EditorApplication.SetSceneDirty();
         }
 
         /// <summary>
@@ -702,6 +725,8 @@ namespace BansheeEditor
             Vector3 scale = activeSO.LocalScale;
             scale[idx] = value;
             activeSO.LocalScale = scale;
+
+            EditorApplication.SetSceneDirty();
         }
 
         /// <inheritdoc/>

+ 2 - 10
MBansheeEditor/Library/LibraryWindow.cs

@@ -798,7 +798,8 @@ namespace BansheeEditor
             focusBounds.height += folderBarBounds.height;
 
             GUIButton focusCatcher = new GUIButton("", EditorStyles.Blank);
-            focusCatcher.OnFocusChanged += OnContentsFocusChanged;
+            focusCatcher.OnFocusGained += () => hasContentFocus = true;
+            focusCatcher.OnFocusLost += () => hasContentFocus = false;
             focusCatcher.Bounds = focusBounds;
 
             GUIPanel focusPanel = GUI.AddPanel(3);
@@ -1399,15 +1400,6 @@ namespace BansheeEditor
             EnterDirectory(path);
         }
 
-        /// <summary>
-        /// Triggered when the content area receives or loses keyboard focus.
-        /// </summary>
-        /// <param name="focus">True if focus was received, false otherwise.</param>
-        private void OnContentsFocusChanged(bool focus)
-        {
-            hasContentFocus = focus;
-        }
-
         /// <summary>
         /// Triggered when the user clicks on empty space between elements.
         /// </summary>

+ 15 - 0
MBansheeEditor/MenuItems.cs

@@ -24,6 +24,7 @@ namespace BansheeEditor
 
             UndoRedo.RecordSO(so, "Added a Camera component");
             so.AddComponent<Camera>();
+            EditorApplication.SetSceneDirty();
         }
 
         /// <summary>
@@ -38,6 +39,7 @@ namespace BansheeEditor
 
             UndoRedo.RecordSO(so, "Added a Renderable component");
             so.AddComponent<Renderable>();
+            EditorApplication.SetSceneDirty();
         }
 
         /// <summary>
@@ -53,6 +55,7 @@ namespace BansheeEditor
             UndoRedo.RecordSO(so, "Added a Light component");
             Light light = so.AddComponent<Light>();
             light.Type = LightType.Point;
+            EditorApplication.SetSceneDirty();
         }
 
         /// <summary>
@@ -68,6 +71,7 @@ namespace BansheeEditor
             UndoRedo.RecordSO(so, "Added a Light component");
             Light light = so.AddComponent<Light>();
             light.Type = LightType.Spot;
+            EditorApplication.SetSceneDirty();
         }
 
         /// <summary>
@@ -83,6 +87,7 @@ namespace BansheeEditor
             UndoRedo.RecordSO(so, "Added a Light component");
             Light light = so.AddComponent<Light>();
             light.Type = LightType.Directional;
+            EditorApplication.SetSceneDirty();
         }
 
         /// <summary>
@@ -96,6 +101,7 @@ namespace BansheeEditor
             so.AddComponent<Camera>();
 
             Selection.SceneObject = so;
+            EditorApplication.SetSceneDirty();
         }
 
         /// <summary>
@@ -109,6 +115,7 @@ namespace BansheeEditor
             so.AddComponent<Renderable>();
 
             Selection.SceneObject = so;
+            EditorApplication.SetSceneDirty();
         }
 
         /// <summary>
@@ -123,6 +130,7 @@ namespace BansheeEditor
             light.Type = LightType.Point;
 
             Selection.SceneObject = so;
+            EditorApplication.SetSceneDirty();
         }
 
         /// <summary>
@@ -137,6 +145,7 @@ namespace BansheeEditor
             light.Type = LightType.Spot;
 
             Selection.SceneObject = so;
+            EditorApplication.SetSceneDirty();
         }
 
         /// <summary>
@@ -151,6 +160,7 @@ namespace BansheeEditor
             light.Type = LightType.Directional;
 
             Selection.SceneObject = so;
+            EditorApplication.SetSceneDirty();
         }
 
         /// <summary>
@@ -165,6 +175,7 @@ namespace BansheeEditor
             renderable.Mesh = Builtin.Box;
 
             Selection.SceneObject = so;
+            EditorApplication.SetSceneDirty();
         }
 
         /// <summary>
@@ -179,6 +190,7 @@ namespace BansheeEditor
             renderable.Mesh = Builtin.Sphere;
 
             Selection.SceneObject = so;
+            EditorApplication.SetSceneDirty();
         }
 
         /// <summary>
@@ -193,6 +205,7 @@ namespace BansheeEditor
             renderable.Mesh = Builtin.Cone;
 
             Selection.SceneObject = so;
+            EditorApplication.SetSceneDirty();
         }
 
         /// <summary>
@@ -207,6 +220,7 @@ namespace BansheeEditor
             renderable.Mesh = Builtin.Quad;
 
             Selection.SceneObject = so;
+            EditorApplication.SetSceneDirty();
         }
 
         /// <summary>
@@ -220,6 +234,7 @@ namespace BansheeEditor
             renderable.Mesh = Builtin.Disc;
 
             Selection.SceneObject = so;
+            EditorApplication.SetSceneDirty();
         }
     }
 }

+ 2 - 0
MBansheeEditor/Scene/DefaultHandleManager.cs

@@ -207,6 +207,8 @@ namespace BansheeEditor
                         }
                             break;
                     }
+
+                    EditorApplication.SetSceneDirty();
                 }
             }
             else

+ 28 - 5
MBansheeEditor/Scene/SceneWindow.cs

@@ -14,11 +14,12 @@ namespace BansheeEditor
     internal sealed class SceneWindow : EditorWindow
     {
         internal const string ToggleProfilerOverlayBinding = "ToggleProfilerOverlay";
-        internal const string ViewToolBinding = "EdViewTool";
-        internal const string MoveToolBinding = "EdMoveTool";
-        internal const string RotateToolBinding = "EdRotateTool";
-        internal const string ScaleToolBinding = "EdScaleTool";
-        internal const string DuplicateBinding = "EdDuplicate";
+        internal const string ViewToolBinding = "ViewTool";
+        internal const string MoveToolBinding = "MoveTool";
+        internal const string RotateToolBinding = "RotateTool";
+        internal const string ScaleToolBinding = "ScaleTool";
+        internal const string DuplicateBinding = "Duplicate";
+        internal const string DeleteBinding = "Delete";
 
         private const int HeaderHeight = 20;
         private const float DefaultPlacementDepth = 5.0f;
@@ -53,6 +54,7 @@ namespace BansheeEditor
         private int editorSettingsHash = int.MaxValue;
 
         private VirtualButton duplicateKey;
+        private VirtualButton deleteKey;
 
         // Tool shortcuts
         private VirtualButton viewToolKey;
@@ -177,6 +179,7 @@ namespace BansheeEditor
             rotateToolKey = new VirtualButton(RotateToolBinding);
             scaleToolKey = new VirtualButton(ScaleToolBinding);
             duplicateKey = new VirtualButton(DuplicateBinding);
+            deleteKey = new VirtualButton(DeleteBinding);
 
             UpdateRenderTexture(Width, Height - HeaderHeight);
             UpdateProfilerOverlay();
@@ -250,6 +253,24 @@ namespace BansheeEditor
                                 message = "Duplicated " + selectedObjects.Length + " elements";
 
                             UndoRedo.CloneSO(selectedObjects, message);
+                            EditorApplication.SetSceneDirty();
+                        }
+                    }
+
+                    if (VirtualInput.IsButtonUp(deleteKey))
+                    {
+                        SceneObject[] selectedObjects = Selection.SceneObjects;
+                        CleanDuplicates(ref selectedObjects);
+
+                        if (selectedObjects.Length > 0)
+                        {
+                            foreach (var so in selectedObjects)
+                            {
+                                string message = "Deleted " + so.Name;
+                                UndoRedo.DeleteSO(so, message);
+                            }
+
+                            EditorApplication.SetSceneDirty();
                         }
                     }
                 }
@@ -325,6 +346,8 @@ namespace BansheeEditor
                             Renderable renderable = draggedSO.AddComponent<Renderable>();
                             renderable.Mesh = mesh;
                             renderable.SetMaterial(material);
+
+                            EditorApplication.SetSceneDirty();
                         }
                     }
 

+ 20 - 6
MBansheeEngine/GUI/GUIElement.cs

@@ -11,9 +11,14 @@ namespace BansheeEngine
     public abstract class GUIElement : ScriptObject
     {
         /// <summary>
-        /// Triggered when a GUI element receives or loses keyboard focus.
+        /// Triggered when a GUI element receives keyboard focus.
         /// </summary>
-        public Action<bool> OnFocusChanged;
+        public Action OnFocusGained;
+
+        /// <summary>
+        /// Triggered when a GUI element loses keyboard focus.
+        /// </summary>
+        public Action OnFocusLost;
 
         /// <summary>
         /// Returns the layout this element belongs to, if any.
@@ -164,12 +169,21 @@ namespace BansheeEngine
         }
 
         /// <summary>
-        /// Triggered by the native interop object when the keyboard focus of this element changes.
+        /// Triggered by the native interop object when the element gains keyboard focus.
+        /// </summary>
+        private void Internal_OnFocusGained()
+        {
+            if (OnFocusGained != null)
+                OnFocusGained();
+        }
+
+        /// <summary>
+        /// Triggered by the native interop object when the element loses keyboard focus.
         /// </summary>
-        private void InternalOnFocusChanged(bool focus)
+        private void Internal_OnFocusLost()
         {
-            if (OnFocusChanged != null)
-                OnFocusChanged(focus);
+            if (OnFocusLost != null)
+                OnFocusLost();
         }
 
         [MethodImpl(MethodImplOptions.InternalCall)]

+ 16 - 4
MBansheeEngine/GUI/GUITextBox.cs

@@ -9,12 +9,15 @@ namespace BansheeEngine
     /// </summary>
     public sealed class GUITextBox : GUIElement
     {
-        public delegate void OnChangedDelegate(string newValue);
-
         /// <summary>
         /// Triggered whenever input text has changed.
         /// </summary>
-        public event OnChangedDelegate OnChanged;
+        public event Action<string> OnChanged;
+
+        /// <summary>
+        /// Triggered whenever user confirms input.
+        /// </summary>
+        public event Action OnConfirmed;
 
         /// <summary>
         /// Creates a new text box element.
@@ -86,12 +89,21 @@ namespace BansheeEngine
         /// Triggered by the native interop object when the text box value is changed.
         /// </summary>
         /// <param name="newValue">New value in the text box.</param>
-        private void DoOnChanged(string newValue)
+        private void Internal_DoOnChanged(string newValue)
         {
             if (OnChanged != null)
                 OnChanged(newValue);
         }
 
+        /// <summary>
+        /// Triggered by the native interop object when the user confirms the input.
+        /// </summary>
+        private void Internal_DoOnConfirmed()
+        {
+            if (OnConfirmed != null)
+                OnConfirmed();
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_CreateInstance(GUITextBox instance, bool multiline, string style, GUIOption[] options);
 

+ 3 - 3
MBansheeEngine/Scene.cs

@@ -17,8 +17,8 @@ namespace BansheeEngine
         /// </summary>
         internal static string ActiveSceneUUID { get { return activeSceneUUID; } }
 
-        private static string activeSceneName;
-        private static string activeSceneUUID;
+        private static string activeSceneName = "Unnamed";
+        private static string activeSceneUUID = "";
 
         /// <summary>
         /// Clears all scene objects from the current scene.
@@ -27,7 +27,7 @@ namespace BansheeEngine
         {
             Internal_ClearScene();
             activeSceneUUID = null;
-            activeSceneName = "None";
+            activeSceneName = "Unnamed";
         }
 
         /// <summary>

+ 9 - 0
SBansheeEditor/Include/BsScriptGUIFloatField.h

@@ -24,6 +24,13 @@ namespace BansheeEngine
 		 */
 		static void onChanged(MonoObject* instance, float newValue);
 
+		/**
+		 * @brief	Triggered when the user confirms input in the native float field.
+		 *
+		 * @param	instance	Managed GUIFloatField instance.
+		 */
+		static void onConfirmed(MonoObject* instance);
+
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
@@ -37,7 +44,9 @@ namespace BansheeEngine
 		static void internal_setRange(ScriptGUIFloatField* nativeInstance, float min, float max);
 
 		typedef void(__stdcall *OnChangedThunkDef) (MonoObject*, float, MonoException**);
+		typedef void(__stdcall *OnConfirmedThunkDef) (MonoObject*, MonoException**);
 
 		static OnChangedThunkDef onChangedThunk;
+		static OnConfirmedThunkDef onConfirmedThunk;
 	};
 }

+ 9 - 0
SBansheeEditor/Include/BsScriptGUIIntField.h

@@ -22,6 +22,13 @@ namespace BansheeEngine
 		 */
 		static void onChanged(MonoObject* instance, INT32 newValue);
 
+		/**
+		 * @brief	Triggered when the user confirms input in the native int field.
+		 *
+		 * @param	instance	Managed GUIIntField instance.
+		 */
+		static void onConfirmed(MonoObject* instance);
+
 		ScriptGUIIntField(MonoObject* instance, GUIIntField* intField);
 
 		/************************************************************************/
@@ -37,7 +44,9 @@ namespace BansheeEngine
 		static void internal_setTint(ScriptGUIIntField* nativeInstance, Color color);
 
 		typedef void (__stdcall *OnChangedThunkDef) (MonoObject*, INT32, MonoException**);
+		typedef void(__stdcall *OnConfirmedThunkDef) (MonoObject*, MonoException**);
 
 		static OnChangedThunkDef onChangedThunk;
+		static OnConfirmedThunkDef onConfirmedThunk;
 	};
 }

+ 12 - 0
SBansheeEditor/Include/BsScriptGUISceneTreeView.h

@@ -15,11 +15,23 @@ namespace BansheeEngine
 
 	private:
 		ScriptGUISceneTreeView(MonoObject* instance, GUISceneTreeView* treeView);
+		~ScriptGUISceneTreeView();
+
+		/**
+		 * @brief	Triggered when the native scene tree view modifies the scene.
+		 */
+		void sceneModified();
+
+		HEvent mOnModifiedConn;
 
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
 		static void internal_createInstance(MonoObject* instance, MonoString* style, MonoArray* guiOptions);
 		static void internal_update(ScriptGUISceneTreeView* thisPtr);
+
+		typedef void(__stdcall *OnModifiedThunkDef) (MonoObject*, MonoException**);
+
+		static OnModifiedThunkDef onModifiedThunk;
 	};
 }

+ 0 - 1
SBansheeEditor/Include/BsScriptGUISliderField.h

@@ -32,7 +32,6 @@ namespace BansheeEngine
 
 		static float internal_getValue(ScriptGUISliderField* nativeInstance);
 		static void internal_setValue(ScriptGUISliderField* nativeInstance, float value);
-		static void internal_hasInputFocus(ScriptGUISliderField* nativeInstance, bool* output);
 		static void internal_setTint(ScriptGUISliderField* nativeInstance, Color color);
 		static void internal_setRange(ScriptGUISliderField* nativeInstance, float min, float max);
 		static void internal_setStep(ScriptGUISliderField* nativeInstance, float step);

+ 9 - 0
SBansheeEditor/Include/BsScriptGUITextField.h

@@ -22,6 +22,13 @@ namespace BansheeEngine
 		 */
 		static void onChanged(MonoObject* instance, const WString& newValue);
 
+		/**
+		 * @brief	Triggered when the user confirms input in the native text field.
+		 *
+		 * @param	instance	Managed GUITextField instance.
+		 */
+		static void onConfirmed(MonoObject* instance);
+
 		ScriptGUITextField(MonoObject* instance, GUITextField* textField);
 
 		/************************************************************************/
@@ -36,7 +43,9 @@ namespace BansheeEngine
 		static void internal_setTint(ScriptGUITextField* nativeInstance, Color color);
 
 		typedef void(__stdcall *OnChangedThunkDef) (MonoObject*, MonoString*, MonoException**);
+		typedef void(__stdcall *OnConfirmedThunkDef) (MonoObject*, MonoException**);
 
 		static OnChangedThunkDef onChangedThunk;
+		static OnConfirmedThunkDef onConfirmedThunk;
 	};
 }

+ 9 - 0
SBansheeEditor/Include/BsScriptGUIVector2Field.h

@@ -22,6 +22,13 @@ namespace BansheeEngine
 		 */
 		static void onChanged(MonoObject* instance, Vector2 newValue);
 
+		/**
+		 * @brief	Triggered when the user confirms input in the native vector field.
+		 *
+		 * @param	instance	Managed GUIVector2Field instance.
+		 */
+		static void onConfirmed(MonoObject* instance);
+
 		ScriptGUIVector2Field(MonoObject* instance, GUIVector2Field* vector2Field);
 
 		/************************************************************************/
@@ -36,7 +43,9 @@ namespace BansheeEngine
 		static void internal_setTint(ScriptGUIVector2Field* nativeInstance, Color color);
 
 		typedef void(__stdcall *OnChangedThunkDef) (MonoObject*, Vector2, MonoException**);
+		typedef void(__stdcall *OnConfirmedThunkDef) (MonoObject*, MonoException**);
 
 		static OnChangedThunkDef onChangedThunk;
+		static OnConfirmedThunkDef onConfirmedThunk;
 	};
 }

+ 9 - 0
SBansheeEditor/Include/BsScriptGUIVector3Field.h

@@ -22,6 +22,13 @@ namespace BansheeEngine
 		 */
 		static void onChanged(MonoObject* instance, Vector3 newValue);
 
+		/**
+		 * @brief	Triggered when the user confirms input in the native vector field.
+		 *
+		 * @param	instance	Managed GUIVector3Field instance.
+		 */
+		static void onConfirmed(MonoObject* instance);
+
 		ScriptGUIVector3Field(MonoObject* instance, GUIVector3Field* vector3Field);
 
 		/************************************************************************/
@@ -36,7 +43,9 @@ namespace BansheeEngine
 		static void internal_setTint(ScriptGUIVector3Field* nativeInstance, Color color);
 
 		typedef void(__stdcall *OnChangedThunkDef) (MonoObject*, Vector3, MonoException**);
+		typedef void(__stdcall *OnConfirmedThunkDef) (MonoObject*, MonoException**);
 
 		static OnChangedThunkDef onChangedThunk;
+		static OnConfirmedThunkDef onConfirmedThunk;
 	};
 }

+ 9 - 0
SBansheeEditor/Include/BsScriptGUIVector4Field.h

@@ -22,6 +22,13 @@ namespace BansheeEngine
 		 */
 		static void onChanged(MonoObject* instance, Vector4 newValue);
 
+		/**
+		 * @brief	Triggered when the user confirms input in the native vector field.
+		 *
+		 * @param	instance	Managed GUIVector4Field instance.
+		 */
+		static void onConfirmed(MonoObject* instance);
+
 		ScriptGUIVector4Field(MonoObject* instance, GUIVector4Field* vector4Field);
 
 		/************************************************************************/
@@ -36,7 +43,9 @@ namespace BansheeEngine
 		static void internal_setTint(ScriptGUIVector4Field* nativeInstance, Color color);
 
 		typedef void(__stdcall *OnChangedThunkDef) (MonoObject*, Vector4, MonoException**);
+		typedef void(__stdcall *OnConfirmedThunkDef) (MonoObject*, MonoException**);
 
 		static OnChangedThunkDef onChangedThunk;
+		static OnConfirmedThunkDef onConfirmedThunk;
 	};
 }

+ 8 - 1
SBansheeEditor/Source/BsScriptGUIFloatField.cpp

@@ -20,6 +20,7 @@ using namespace std::placeholders;
 namespace BansheeEngine
 {
 	ScriptGUIFloatField::OnChangedThunkDef ScriptGUIFloatField::onChangedThunk;
+	ScriptGUIFloatField::OnConfirmedThunkDef ScriptGUIFloatField::onConfirmedThunk;
 
 	ScriptGUIFloatField::ScriptGUIFloatField(MonoObject* instance, GUIFloatField* floatField)
 		:TScriptGUIElement(instance, floatField)
@@ -36,7 +37,8 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_SetTint", &ScriptGUIFloatField::internal_setTint);
 		metaData.scriptClass->addInternalCall("Internal_SetRange", &ScriptGUIFloatField::internal_setRange);
 
-		onChangedThunk = (OnChangedThunkDef)metaData.scriptClass->getMethod("DoOnChanged", 1)->getThunk();
+		onChangedThunk = (OnChangedThunkDef)metaData.scriptClass->getMethod("Internal_DoOnChanged", 1)->getThunk();
+		onConfirmedThunk = (OnConfirmedThunkDef)metaData.scriptClass->getMethod("Internal_DoOnConfirmed", 0)->getThunk();
 	}
 
 	void ScriptGUIFloatField::internal_createInstance(MonoObject* instance, MonoObject* title, UINT32 titleWidth,
@@ -100,4 +102,9 @@ namespace BansheeEngine
 	{
 		MonoUtil::invokeThunk(onChangedThunk, instance, newValue);
 	}
+
+	void ScriptGUIFloatField::onConfirmed(MonoObject* instance)
+	{
+		MonoUtil::invokeThunk(onConfirmedThunk, instance);
+	}
 }

+ 8 - 1
SBansheeEditor/Source/BsScriptGUIIntField.cpp

@@ -20,6 +20,7 @@ using namespace std::placeholders;
 namespace BansheeEngine
 {
 	ScriptGUIIntField::OnChangedThunkDef ScriptGUIIntField::onChangedThunk;
+	ScriptGUIIntField::OnConfirmedThunkDef ScriptGUIIntField::onConfirmedThunk;
 
 	ScriptGUIIntField::ScriptGUIIntField(MonoObject* instance, GUIIntField* intField)
 		:TScriptGUIElement(instance, intField)
@@ -36,7 +37,8 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_SetRange", &ScriptGUIIntField::internal_setRange);
 		metaData.scriptClass->addInternalCall("Internal_SetTint", &ScriptGUIIntField::internal_setTint);
 
-		onChangedThunk = (OnChangedThunkDef)metaData.scriptClass->getMethod("DoOnChanged", 1)->getThunk();
+		onChangedThunk = (OnChangedThunkDef)metaData.scriptClass->getMethod("Internal_DoOnChanged", 1)->getThunk();
+		onConfirmedThunk = (OnConfirmedThunkDef)metaData.scriptClass->getMethod("Internal_DoOnConfirmed", 0)->getThunk();
 	}
 
 	void ScriptGUIIntField::internal_createInstance(MonoObject* instance, MonoObject* title, UINT32 titleWidth, 
@@ -100,4 +102,9 @@ namespace BansheeEngine
 	{
 		MonoUtil::invokeThunk(onChangedThunk, instance, newValue);
 	}
+
+	void ScriptGUIIntField::onConfirmed(MonoObject* instance)
+	{
+		MonoUtil::invokeThunk(onConfirmedThunk, instance);
+	}
 }

+ 15 - 0
SBansheeEditor/Source/BsScriptGUISceneTreeView.cpp

@@ -1,6 +1,7 @@
 #include "BsScriptGUISceneTreeView.h"
 #include "BsScriptMeta.h"
 #include "BsMonoClass.h"
+#include "BsMonoMethod.h"
 #include "BsMonoManager.h"
 #include "BsMonoUtil.h"
 #include "BsGUISceneTreeView.h"
@@ -10,16 +11,30 @@ using namespace std::placeholders;
 
 namespace BansheeEngine
 {
+	ScriptGUISceneTreeView::OnModifiedThunkDef ScriptGUISceneTreeView::onModifiedThunk;
+
 	ScriptGUISceneTreeView::ScriptGUISceneTreeView(MonoObject* instance, GUISceneTreeView* treeView)
 		:TScriptGUIElement(instance, treeView)
 	{
+		mOnModifiedConn = treeView->onModified.connect(std::bind(&ScriptGUISceneTreeView::sceneModified, this));
+	}
 
+	ScriptGUISceneTreeView::~ScriptGUISceneTreeView()
+	{
+		mOnModifiedConn.disconnect();
 	}
 
 	void ScriptGUISceneTreeView::initRuntimeData()
 	{
 		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptGUISceneTreeView::internal_createInstance);
 		metaData.scriptClass->addInternalCall("Internal_Update", &ScriptGUISceneTreeView::internal_update);
+
+		onModifiedThunk = (OnModifiedThunkDef)metaData.scriptClass->getMethod("Internal_DoOnModified", 0)->getThunk();
+	}
+
+	void ScriptGUISceneTreeView::sceneModified()
+	{
+		MonoUtil::invokeThunk(onModifiedThunk, getManagedInstance());
 	}
 
 	void ScriptGUISceneTreeView::internal_createInstance(MonoObject* instance, MonoString* style, MonoArray* guiOptions)

+ 0 - 7
SBansheeEditor/Source/BsScriptGUISliderField.cpp

@@ -27,7 +27,6 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptGUISliderField::internal_createInstance);
 		metaData.scriptClass->addInternalCall("Internal_GetValue", &ScriptGUISliderField::internal_getValue);
 		metaData.scriptClass->addInternalCall("Internal_SetValue", &ScriptGUISliderField::internal_setValue);
-		metaData.scriptClass->addInternalCall("Internal_HasInputFocus", &ScriptGUISliderField::internal_hasInputFocus);
 		metaData.scriptClass->addInternalCall("Internal_SetTint", &ScriptGUISliderField::internal_setTint);
 		metaData.scriptClass->addInternalCall("Internal_SetRange", &ScriptGUISliderField::internal_setRange);
 		metaData.scriptClass->addInternalCall("Internal_SetStep", &ScriptGUISliderField::internal_setStep);
@@ -75,12 +74,6 @@ namespace BansheeEngine
 		return sliderField->setValue(value);
 	}
 
-	void ScriptGUISliderField::internal_hasInputFocus(ScriptGUISliderField* nativeInstance, bool* output)
-	{
-		GUISliderField* sliderField = static_cast<GUISliderField*>(nativeInstance->getGUIElement());
-		*output = sliderField->hasInputFocus();
-	}
-
 	void ScriptGUISliderField::internal_setTint(ScriptGUISliderField* nativeInstance, Color color)
 	{
 		GUISliderField* sliderField = (GUISliderField*)nativeInstance->getGUIElement();

+ 8 - 1
SBansheeEditor/Source/BsScriptGUITextField.cpp

@@ -20,6 +20,7 @@ using namespace std::placeholders;
 namespace BansheeEngine
 {
 	ScriptGUITextField::OnChangedThunkDef ScriptGUITextField::onChangedThunk;
+	ScriptGUITextField::OnConfirmedThunkDef ScriptGUITextField::onConfirmedThunk;
 
 	ScriptGUITextField::ScriptGUITextField(MonoObject* instance, GUITextField* textField)
 		:TScriptGUIElement(instance, textField)
@@ -35,7 +36,8 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_HasInputFocus", &ScriptGUITextField::internal_hasInputFocus);
 		metaData.scriptClass->addInternalCall("Internal_SetTint", &ScriptGUITextField::internal_setTint);
 
-		onChangedThunk = (OnChangedThunkDef)metaData.scriptClass->getMethod("DoOnChanged", 1)->getThunk();
+		onChangedThunk = (OnChangedThunkDef)metaData.scriptClass->getMethod("Internal_DoOnChanged", 1)->getThunk();
+		onConfirmedThunk = (OnConfirmedThunkDef)metaData.scriptClass->getMethod("Internal_DoOnConfirmed", 0)->getThunk();
 	}
 
 	void ScriptGUITextField::internal_createInstance(MonoObject* instance, bool multiline, MonoObject* title, UINT32 titleWidth,
@@ -94,4 +96,9 @@ namespace BansheeEngine
 		MonoString* monoNewValue = MonoUtil::wstringToMono(MonoManager::instance().getDomain(), newValue);
 		MonoUtil::invokeThunk(onChangedThunk, instance, monoNewValue);
 	}
+
+	void ScriptGUITextField::onConfirmed(MonoObject* instance)
+	{
+		MonoUtil::invokeThunk(onConfirmedThunk, instance);
+	}
 }

+ 8 - 1
SBansheeEditor/Source/BsScriptGUIVector2Field.cpp

@@ -20,6 +20,7 @@ using namespace std::placeholders;
 namespace BansheeEngine
 {
 	ScriptGUIVector2Field::OnChangedThunkDef ScriptGUIVector2Field::onChangedThunk;
+	ScriptGUIVector2Field::OnConfirmedThunkDef ScriptGUIVector2Field::onConfirmedThunk;
 
 	ScriptGUIVector2Field::ScriptGUIVector2Field(MonoObject* instance, GUIVector2Field* vector2Field)
 		:TScriptGUIElement(instance, vector2Field)
@@ -35,7 +36,8 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_HasInputFocus", &ScriptGUIVector2Field::internal_hasInputFocus);
 		metaData.scriptClass->addInternalCall("Internal_SetTint", &ScriptGUIVector2Field::internal_setTint);
 
-		onChangedThunk = (OnChangedThunkDef)metaData.scriptClass->getMethod("DoOnChanged", 1)->getThunk();
+		onChangedThunk = (OnChangedThunkDef)metaData.scriptClass->getMethod("Internal_DoOnChanged", 1)->getThunk();
+		onConfirmedThunk = (OnConfirmedThunkDef)metaData.scriptClass->getMethod("Internal_DoOnConfirmed", 0)->getThunk();
 	}
 
 	void ScriptGUIVector2Field::internal_createInstance(MonoObject* instance, MonoObject* title, UINT32 titleWidth,
@@ -93,4 +95,9 @@ namespace BansheeEngine
 	{
 		MonoUtil::invokeThunk(onChangedThunk, instance, newValue);
 	}
+
+	void ScriptGUIVector2Field::onConfirmed(MonoObject* instance)
+	{
+		MonoUtil::invokeThunk(onConfirmedThunk, instance);
+	}
 }

+ 8 - 1
SBansheeEditor/Source/BsScriptGUIVector3Field.cpp

@@ -20,6 +20,7 @@ using namespace std::placeholders;
 namespace BansheeEngine
 {
 	ScriptGUIVector3Field::OnChangedThunkDef ScriptGUIVector3Field::onChangedThunk;
+	ScriptGUIVector3Field::OnConfirmedThunkDef ScriptGUIVector3Field::onConfirmedThunk;
 
 	ScriptGUIVector3Field::ScriptGUIVector3Field(MonoObject* instance, GUIVector3Field* vector3Field)
 		:TScriptGUIElement(instance, vector3Field)
@@ -35,7 +36,8 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_HasInputFocus", &ScriptGUIVector3Field::internal_hasInputFocus);
 		metaData.scriptClass->addInternalCall("Internal_SetTint", &ScriptGUIVector3Field::internal_setTint);
 
-		onChangedThunk = (OnChangedThunkDef)metaData.scriptClass->getMethod("DoOnChanged", 1)->getThunk();
+		onChangedThunk = (OnChangedThunkDef)metaData.scriptClass->getMethod("Internal_DoOnChanged", 1)->getThunk();
+		onConfirmedThunk = (OnConfirmedThunkDef)metaData.scriptClass->getMethod("Internal_DoOnConfirmed", 0)->getThunk();
 	}
 
 	void ScriptGUIVector3Field::internal_createInstance(MonoObject* instance, MonoObject* title, UINT32 titleWidth,
@@ -93,4 +95,9 @@ namespace BansheeEngine
 	{
 		MonoUtil::invokeThunk(onChangedThunk, instance, newValue);
 	}
+
+	void ScriptGUIVector3Field::onConfirmed(MonoObject* instance)
+	{
+		MonoUtil::invokeThunk(onConfirmedThunk, instance);
+	}
 }

+ 8 - 1
SBansheeEditor/Source/BsScriptGUIVector4Field.cpp

@@ -20,6 +20,7 @@ using namespace std::placeholders;
 namespace BansheeEngine
 {
 	ScriptGUIVector4Field::OnChangedThunkDef ScriptGUIVector4Field::onChangedThunk;
+	ScriptGUIVector4Field::OnConfirmedThunkDef ScriptGUIVector4Field::onConfirmedThunk;
 
 	ScriptGUIVector4Field::ScriptGUIVector4Field(MonoObject* instance, GUIVector4Field* vector4Field)
 		:TScriptGUIElement(instance, vector4Field)
@@ -35,7 +36,8 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_HasInputFocus", &ScriptGUIVector4Field::internal_hasInputFocus);
 		metaData.scriptClass->addInternalCall("Internal_SetTint", &ScriptGUIVector4Field::internal_setTint);
 
-		onChangedThunk = (OnChangedThunkDef)metaData.scriptClass->getMethod("DoOnChanged", 1)->getThunk();
+		onChangedThunk = (OnChangedThunkDef)metaData.scriptClass->getMethod("Internal_DoOnChanged", 1)->getThunk();
+		onConfirmedThunk = (OnConfirmedThunkDef)metaData.scriptClass->getMethod("Internal_DoOnConfirmed", 0)->getThunk();
 	}
 
 	void ScriptGUIVector4Field::internal_createInstance(MonoObject* instance, MonoObject* title, UINT32 titleWidth,
@@ -93,4 +95,9 @@ namespace BansheeEngine
 	{
 		MonoUtil::invokeThunk(onChangedThunk, instance, newValue);
 	}
+
+	void ScriptGUIVector4Field::onConfirmed(MonoObject* instance)
+	{
+		MonoUtil::invokeThunk(onConfirmedThunk, instance);
+	}
 }

+ 3 - 3
SBansheeEngine/Include/BsScriptGUIElement.h

@@ -126,10 +126,10 @@ namespace BansheeEngine
 	public:
 		SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "GUIElement")
 
-		typedef void(__stdcall *OnFocusChangedThunkDef) (MonoObject*, bool, MonoException**);
-
-		static OnFocusChangedThunkDef onFocusChangedThunk;
+		typedef void(__stdcall *OnFocusChangedThunkDef) (MonoObject*, MonoException**);
 
+		static OnFocusChangedThunkDef onFocusGainedThunk;
+		static OnFocusChangedThunkDef onFocusLostThunk;
 	private:
 		ScriptGUIElement(MonoObject* instance);
 

+ 8 - 0
SBansheeEngine/Include/BsScriptGUIInputBox.h

@@ -22,6 +22,11 @@ namespace BansheeEngine
 		 */
 		static void onChanged(MonoObject* instance, const WString& newValue);
 
+		/**
+		 * @brief	Triggered when the user confirms input in the native input box.
+		 */
+		static void onConfirmed(MonoObject* instance);
+
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
@@ -31,6 +36,9 @@ namespace BansheeEngine
 		static void internal_setTint(ScriptGUIInputBox* nativeInstance, Color color);
 
 		typedef void(__stdcall *OnChangedThunkDef) (MonoObject*, MonoString*, MonoException**);
+		typedef void(__stdcall *OnConfirmedThunkDef) (MonoObject*, MonoException**);
+
 		static OnChangedThunkDef onChangedThunk;
+		static OnConfirmedThunkDef onConfirmedThunk;
 	};
 }

+ 8 - 3
SBansheeEngine/Source/BsScriptGUIElement.cpp

@@ -31,7 +31,10 @@ namespace BansheeEngine
 
 	void ScriptGUIElementBaseTBase::onFocusChanged(MonoObject* instance, bool focus)
 	{
-		MonoUtil::invokeThunk(ScriptGUIElement::onFocusChangedThunk, instance, focus);
+		if (focus)
+			MonoUtil::invokeThunk(ScriptGUIElement::onFocusGainedThunk, instance);
+		else
+			MonoUtil::invokeThunk(ScriptGUIElement::onFocusLostThunk, instance);
 	}
 
 	void ScriptGUIElementBaseTBase::_onManagedInstanceDeleted()
@@ -64,7 +67,8 @@ namespace BansheeEngine
 		}
 	}
 
-	ScriptGUIElement::OnFocusChangedThunkDef ScriptGUIElement::onFocusChangedThunk;
+	ScriptGUIElement::OnFocusChangedThunkDef ScriptGUIElement::onFocusGainedThunk;
+	ScriptGUIElement::OnFocusChangedThunkDef ScriptGUIElement::onFocusLostThunk;
 
 	ScriptGUIElement::ScriptGUIElement(MonoObject* instance)
 		:ScriptObject(instance)
@@ -91,7 +95,8 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_ResetDimensions", &ScriptGUIElement::internal_ResetDimensions);
 		metaData.scriptClass->addInternalCall("Internal_SetContextMenu", &ScriptGUIElement::internal_SetContextMenu);
 
-		onFocusChangedThunk = (OnFocusChangedThunkDef)metaData.scriptClass->getMethod("InternalOnFocusChanged", 1)->getThunk();
+		onFocusGainedThunk = (OnFocusChangedThunkDef)metaData.scriptClass->getMethod("Internal_OnFocusGained", 0)->getThunk();
+		onFocusLostThunk = (OnFocusChangedThunkDef)metaData.scriptClass->getMethod("Internal_OnFocusLost", 0)->getThunk();
 	}
 
 	void ScriptGUIElement::internal_destroy(ScriptGUIElementBaseTBase* nativeInstance)

+ 8 - 2
SBansheeEngine/Source/BsScriptGUIInputBox.cpp

@@ -12,6 +12,7 @@ using namespace std::placeholders;
 namespace BansheeEngine
 {
 	ScriptGUIInputBox::OnChangedThunkDef ScriptGUIInputBox::onChangedThunk;
+	ScriptGUIInputBox::OnConfirmedThunkDef ScriptGUIInputBox::onConfirmedThunk;
 
 	ScriptGUIInputBox::ScriptGUIInputBox(MonoObject* instance, GUIInputBox* inputBox)
 		:TScriptGUIElement(instance, inputBox)
@@ -26,7 +27,8 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_SetText", &ScriptGUIInputBox::internal_setText);
 		metaData.scriptClass->addInternalCall("Internal_SetTint", &ScriptGUIInputBox::internal_setTint);
 
-		onChangedThunk = (OnChangedThunkDef)metaData.scriptClass->getMethod("DoOnChanged", 1)->getThunk();
+		onChangedThunk = (OnChangedThunkDef)metaData.scriptClass->getMethod("Internal_DoOnChanged", 1)->getThunk();
+		onConfirmedThunk = (OnConfirmedThunkDef)metaData.scriptClass->getMethod("Internal_DoOnConfirmed", 0)->getThunk();
 	}
 
 	void ScriptGUIInputBox::internal_createInstance(MonoObject* instance, bool multiline, MonoString* style, MonoArray* guiOptions)
@@ -40,7 +42,6 @@ namespace BansheeEngine
 		GUIInputBox* guiInputBox = GUIInputBox::create(multiline, options, toString(MonoUtil::monoToWString(style)));
 		guiInputBox->onValueChanged.connect(std::bind(&ScriptGUIInputBox::onChanged, instance, _1));
 
-
 		ScriptGUIInputBox* nativeInstance = new (bs_alloc<ScriptGUIInputBox>()) ScriptGUIInputBox(instance, guiInputBox);
 	}
 
@@ -67,4 +68,9 @@ namespace BansheeEngine
 		MonoString* monoValue = MonoUtil::wstringToMono(MonoManager::instance().getDomain(), newValue);
 		MonoUtil::invokeThunk(onChangedThunk, instance, monoValue);
 	}
+
+	void ScriptGUIInputBox::onConfirmed(MonoObject* instance)
+	{
+		MonoUtil::invokeThunk(onConfirmedThunk, instance);
+	}
 }