ソースを参照

Added C# Undo/redo (untested)
Added X button to ComponentFoldout (untested)
Added drag and drop functionality to Inspector (untested)
Added a way to retrieve all types in a script file (untested)

Marko Pintera 10 年 前
コミット
09bbc8848d
30 ファイル変更689 行追加29 行削除
  1. 1 1
      BansheeEditor/Include/BsCmdCloneSO.h
  2. 1 1
      BansheeEditor/Include/BsCmdCreateSO.h
  3. 1 1
      BansheeEditor/Include/BsCmdDeleteSO.h
  4. 1 1
      BansheeEditor/Include/BsCmdInputFieldValueChange.h
  5. 1 1
      BansheeEditor/Include/BsCmdInstantiateSO.h
  6. 1 1
      BansheeEditor/Include/BsCmdRecordSO.h
  7. 1 1
      BansheeEditor/Include/BsCmdReparentSO.h
  8. 1 1
      BansheeEditor/Include/BsEditorCommand.h
  9. 13 1
      BansheeEditor/Include/BsGUIComponentFoldout.h
  10. 1 1
      BansheeEditor/Include/BsUndoRedo.h
  11. 12 0
      BansheeEditor/Source/BsBuiltinEditorResources.cpp
  12. 34 7
      BansheeEditor/Source/BsGUIComponentFoldout.cpp
  13. 8 3
      MBansheeEditor/GUI/GUIComponentFoldout.cs
  14. 104 0
      MBansheeEditor/Inspector/InspectorWindow.cs
  15. 1 0
      MBansheeEditor/MBansheeEditor.csproj
  16. 153 0
      MBansheeEditor/UndoRedo.cs
  17. 10 0
      MBansheeEngine/SceneObject.cs
  18. 8 0
      MBansheeEngine/ScriptCode.cs
  19. 3 0
      SBansheeEditor/Include/BsScriptGUIComponentFoldout.h
  20. 29 0
      SBansheeEditor/Include/BsScriptUndoRedo.h
  21. 2 0
      SBansheeEditor/SBansheeEditor.vcxproj
  22. 6 0
      SBansheeEditor/SBansheeEditor.vcxproj.filters
  23. 8 0
      SBansheeEditor/Source/BsScriptGUIComponentFoldout.cpp
  24. 152 0
      SBansheeEditor/Source/BsScriptUndoRedo.cpp
  25. 6 0
      SBansheeEngine/Include/BsScriptAssemblyManager.h
  26. 0 6
      SBansheeEngine/Include/BsScriptObject.h
  27. 3 0
      SBansheeEngine/Include/BsScriptScriptCode.h
  28. 7 1
      SBansheeEngine/Source/BsScriptAssemblyManager.cpp
  29. 101 1
      SBansheeEngine/Source/BsScriptScriptCode.cpp
  30. 20 1
      TODO.txt

+ 1 - 1
BansheeEditor/Include/BsCmdCloneSO.h

@@ -11,7 +11,7 @@ namespace BansheeEngine
 	 * @brief	A command used for undo/redo purposes. Clones scene object(s)
 	 * @brief	A command used for undo/redo purposes. Clones scene object(s)
 	 *			and removes them as an undo operation.
 	 *			and removes them as an undo operation.
 	 */
 	 */
-	class CmdCloneSO : public EditorCommand
+	class BS_ED_EXPORT CmdCloneSO : public EditorCommand
 	{
 	{
 	public:
 	public:
 		~CmdCloneSO();
 		~CmdCloneSO();

+ 1 - 1
BansheeEditor/Include/BsCmdCreateSO.h

@@ -11,7 +11,7 @@ namespace BansheeEngine
 	 * @brief	A command used for undo/redo purposes. Creates a scene object 
 	 * @brief	A command used for undo/redo purposes. Creates a scene object 
 	 *			and removes it as an undo operation.
 	 *			and removes it as an undo operation.
 	 */
 	 */
-	class CmdCreateSO : public EditorCommand
+	class BS_ED_EXPORT CmdCreateSO : public EditorCommand
 	{
 	{
 	public:
 	public:
 		~CmdCreateSO();
 		~CmdCreateSO();

+ 1 - 1
BansheeEditor/Include/BsCmdDeleteSO.h

@@ -11,7 +11,7 @@ namespace BansheeEngine
 	 * @brief	A command used for undo/redo purposes. Deletes a scene object
 	 * @brief	A command used for undo/redo purposes. Deletes a scene object
 	 *			and restores it as an undo operation.
 	 *			and restores it as an undo operation.
 	 */
 	 */
-	class CmdDeleteSO : public EditorCommand
+	class BS_ED_EXPORT CmdDeleteSO : public EditorCommand
 	{
 	{
 	public:
 	public:
 		~CmdDeleteSO();
 		~CmdDeleteSO();

+ 1 - 1
BansheeEditor/Include/BsCmdInputFieldValueChange.h

@@ -12,7 +12,7 @@ namespace BansheeEngine
 	 *			as needed.
 	 *			as needed.
 	 */
 	 */
 	template <class InputFieldType, class ValueType>
 	template <class InputFieldType, class ValueType>
-	class CmdInputFieldValueChange : public EditorCommand
+	class BS_ED_EXPORT CmdInputFieldValueChange : public EditorCommand
 	{
 	{
 	public:
 	public:
 		/**
 		/**

+ 1 - 1
BansheeEditor/Include/BsCmdInstantiateSO.h

@@ -11,7 +11,7 @@ namespace BansheeEngine
 	 * @brief	A command used for undo/redo purposes. Instantiates scene object(s)
 	 * @brief	A command used for undo/redo purposes. Instantiates scene object(s)
 	 *			from a prefab and removes them as an undo operation.
 	 *			from a prefab and removes them as an undo operation.
 	 */
 	 */
-	class CmdInstantiateSO : public EditorCommand
+	class BS_ED_EXPORT CmdInstantiateSO : public EditorCommand
 	{
 	{
 	public:
 	public:
 		~CmdInstantiateSO();
 		~CmdInstantiateSO();

+ 1 - 1
BansheeEditor/Include/BsCmdRecordSO.h

@@ -12,7 +12,7 @@ namespace BansheeEngine
 	 *			scene object at a specific point and allows you to restore it to its
 	 *			scene object at a specific point and allows you to restore it to its
 	 *			original values as needed.
 	 *			original values as needed.
 	 */
 	 */
-	class CmdRecordSO : public EditorCommand
+	class BS_ED_EXPORT CmdRecordSO : public EditorCommand
 	{
 	{
 	public:
 	public:
 		~CmdRecordSO();
 		~CmdRecordSO();

+ 1 - 1
BansheeEditor/Include/BsCmdReparentSO.h

@@ -11,7 +11,7 @@ namespace BansheeEngine
 	 *			parent change operations. It allows you to apply the parent change
 	 *			parent change operations. It allows you to apply the parent change
 	 *			or revert the object to its original parent as needed.
 	 *			or revert the object to its original parent as needed.
 	 */
 	 */
-	class CmdReparentSO : public EditorCommand
+	class BS_ED_EXPORT CmdReparentSO : public EditorCommand
 	{
 	{
 	public:
 	public:
 		/**
 		/**

+ 1 - 1
BansheeEditor/Include/BsEditorCommand.h

@@ -8,7 +8,7 @@ namespace BansheeEngine
 	 * @brief	A command used for undo/redo purposes. It records a change occurring on
 	 * @brief	A command used for undo/redo purposes. It records a change occurring on
 	 *			some object and allows you to apply or revert that change as needed.
 	 *			some object and allows you to apply or revert that change as needed.
 	 */
 	 */
-	class EditorCommand
+	class BS_ED_EXPORT EditorCommand
 	{
 	{
 	public:
 	public:
 		EditorCommand(const WString& description);
 		EditorCommand(const WString& description);

+ 13 - 1
BansheeEditor/Include/BsGUIComponentFoldout.h

@@ -23,10 +23,15 @@ namespace BansheeEngine
 		static const String& getGUITypeName();
 		static const String& getGUITypeName();
 
 
 		/**
 		/**
-		 * Returns the style type name of the internal toggle button.
+		 * Returns the style type name of the internal toggle button that triggers expand/collapse.
 		 */
 		 */
 		static const String& getFoldoutButtonStyleType();
 		static const String& getFoldoutButtonStyleType();
 
 
+		/**
+		 * Returns the style type name of the internal toggle button that triggers component removal.
+		 */
+		static const String& getFoldoutRemoveButtonStyleType();
+
 		/**
 		/**
 		 * @brief	Creates a new GUI component foldout element.
 		 * @brief	Creates a new GUI component foldout element.
 		 *
 		 *
@@ -82,6 +87,7 @@ namespace BansheeEngine
 		Vector2I _getOptimalSize() const override;
 		Vector2I _getOptimalSize() const override;
 
 
 		Event<void(bool)> onStateChanged;
 		Event<void(bool)> onStateChanged;
+		Event<void()> onRemoveClicked;
 	protected:
 	protected:
 		virtual ~GUIComponentFoldout();
 		virtual ~GUIComponentFoldout();
 
 
@@ -91,12 +97,18 @@ namespace BansheeEngine
 		 */
 		 */
 		void toggleTriggered(bool value);
 		void toggleTriggered(bool value);
 
 
+		/**
+		 * @brief	Triggered when the remove button is clicked.
+		 */
+		void removeTriggered();
+
 		/**
 		/**
 		 * @copydoc	GUIElement::styleUpdated
 		 * @copydoc	GUIElement::styleUpdated
 		 */
 		 */
 		void styleUpdated() override;
 		void styleUpdated() override;
 
 
 		GUIToggle* mToggle;
 		GUIToggle* mToggle;
+		GUIButton* mRemove;
 		bool mIsExpanded;
 		bool mIsExpanded;
 	};
 	};
 }
 }

+ 1 - 1
BansheeEditor/Include/BsUndoRedo.h

@@ -8,7 +8,7 @@ namespace BansheeEngine
 	/**
 	/**
 	 * @brief	Handles editor-wide undo & redo operations.
 	 * @brief	Handles editor-wide undo & redo operations.
 	 */
 	 */
-	class UndoRedo : public Module<UndoRedo>
+	class BS_ED_EXPORT UndoRedo : public Module<UndoRedo>
 	{
 	{
 		/**
 		/**
 		 * @brief	Contains data about a single undo/redo group.
 		 * @brief	Contains data about a single undo/redo group.

+ 12 - 0
BansheeEditor/Source/BsBuiltinEditorResources.cpp

@@ -1180,11 +1180,23 @@ namespace BansheeEngine
 
 
 		skin->setStyle(GUIComponentFoldout::getFoldoutButtonStyleType(), cmpFoldoutBtnStyle);
 		skin->setStyle(GUIComponentFoldout::getFoldoutButtonStyleType(), cmpFoldoutBtnStyle);
 
 
+		GUIElementStyle cmpFoldoutRemoveBtnStyle;
+		cmpFoldoutRemoveBtnStyle.normal.texture = getGUITexture(XButtonNormalTex);
+		cmpFoldoutRemoveBtnStyle.hover.texture = getGUITexture(XButtonHoverTex);
+		cmpFoldoutRemoveBtnStyle.active.texture = cmpFoldoutRemoveBtnStyle.hover.texture;
+		cmpFoldoutRemoveBtnStyle.fixedHeight = true;
+		cmpFoldoutRemoveBtnStyle.fixedWidth = true;
+		cmpFoldoutRemoveBtnStyle.height = 11;
+		cmpFoldoutRemoveBtnStyle.width = 11;
+
+		skin->setStyle(GUIComponentFoldout::getFoldoutRemoveButtonStyleType(), cmpFoldoutRemoveBtnStyle);
+
 		GUIElementStyle cmpFoldoutStyle;
 		GUIElementStyle cmpFoldoutStyle;
 		cmpFoldoutStyle.fixedHeight = true;
 		cmpFoldoutStyle.fixedHeight = true;
 		cmpFoldoutStyle.height = 12;
 		cmpFoldoutStyle.height = 12;
 		cmpFoldoutStyle.minWidth = 30;
 		cmpFoldoutStyle.minWidth = 30;
 		cmpFoldoutStyle.subStyles[GUIComponentFoldout::getFoldoutButtonStyleType()] = GUIComponentFoldout::getFoldoutButtonStyleType();
 		cmpFoldoutStyle.subStyles[GUIComponentFoldout::getFoldoutButtonStyleType()] = GUIComponentFoldout::getFoldoutButtonStyleType();
+		cmpFoldoutStyle.subStyles[GUIComponentFoldout::getFoldoutRemoveButtonStyleType()] = GUIComponentFoldout::getFoldoutRemoveButtonStyleType();
 
 
 		skin->setStyle(GUIComponentFoldout::getGUITypeName(), cmpFoldoutStyle);
 		skin->setStyle(GUIComponentFoldout::getGUITypeName(), cmpFoldoutStyle);
 
 

+ 34 - 7
BansheeEditor/Source/BsGUIComponentFoldout.cpp

@@ -2,6 +2,7 @@
 #include "BsGUILayout.h"
 #include "BsGUILayout.h"
 #include "BsGUILabel.h"
 #include "BsGUILabel.h"
 #include "BsGUIToggle.h"
 #include "BsGUIToggle.h"
+#include "BsGUIButton.h"
 #include "BsGUITexture.h"
 #include "BsGUITexture.h"
 #include "BsBuiltinResources.h"
 #include "BsBuiltinResources.h"
 #include "BsGUIWidget.h"
 #include "BsGUIWidget.h"
@@ -13,13 +14,18 @@ namespace BansheeEngine
 {
 {
 	GUIComponentFoldout::GUIComponentFoldout(const PrivatelyConstruct& dummy, const HString& label, const String& style,
 	GUIComponentFoldout::GUIComponentFoldout(const PrivatelyConstruct& dummy, const HString& label, const String& style,
 		const GUIDimensions& dimensions)
 		const GUIDimensions& dimensions)
-		:GUIElementContainer(dimensions, style), mToggle(nullptr), mIsExpanded(false)
+		:GUIElementContainer(dimensions, style), mToggle(nullptr), mRemove(nullptr), mIsExpanded(false)
 	{
 	{
 		mToggle = GUIToggle::create(label, getSubStyleName(getFoldoutButtonStyleType()));
 		mToggle = GUIToggle::create(label, getSubStyleName(getFoldoutButtonStyleType()));
+		mRemove = GUIButton::create(HEString(L""), getSubStyleName(getFoldoutRemoveButtonStyleType()));
 
 
 		_registerChildElement(mToggle);
 		_registerChildElement(mToggle);
+		_registerChildElement(mRemove);
 
 
 		mToggle->onToggled.connect(std::bind(&GUIComponentFoldout::toggleTriggered, this, _1));
 		mToggle->onToggled.connect(std::bind(&GUIComponentFoldout::toggleTriggered, this, _1));
+		mRemove->onClick.connect(std::bind(&GUIComponentFoldout::removeTriggered, this));
+
+		mToggle->_setElementDepth(1);
 	}
 	}
 
 
 	GUIComponentFoldout::~GUIComponentFoldout()
 	GUIComponentFoldout::~GUIComponentFoldout()
@@ -79,21 +85,35 @@ namespace BansheeEngine
 		onStateChanged(value);
 		onStateChanged(value);
 	}
 	}
 
 
+	void GUIComponentFoldout::removeTriggered()
+	{
+		onRemoveClicked();
+	}
+
 	void GUIComponentFoldout::_updateLayoutInternal(const GUILayoutData& data)
 	void GUIComponentFoldout::_updateLayoutInternal(const GUILayoutData& data)
 	{
 	{
-		UINT32 toggleOffset = 0;
-		
+		Vector2I toggleOptSize = mToggle->_getOptimalSize();
 		{
 		{
-			Vector2I optimalSize = mToggle->_getOptimalSize();
-			INT32 yOffset = Math::roundToInt(((INT32)data.area.height - optimalSize.y) * 0.5f);
+			INT32 yOffset = Math::roundToInt(((INT32)data.area.height - toggleOptSize.y) * 0.5f);
 
 
 			GUILayoutData childData = data;
 			GUILayoutData childData = data;
 			childData.area.y += yOffset;
 			childData.area.y += yOffset;
-			childData.area.height = optimalSize.y;
+			childData.area.height = toggleOptSize.y;
 
 
 			mToggle->_setLayoutData(childData);
 			mToggle->_setLayoutData(childData);
+		}
+
+		{
+			Vector2I optimalSize = mRemove->_getOptimalSize();
 
 
-			toggleOffset = optimalSize.x;
+			INT32 yOffset = Math::roundToInt(((INT32)data.area.height - optimalSize.y) * 0.5f);
+
+			GUILayoutData childData = data;
+			childData.area.x = data.area.x + data.area.width - optimalSize.x - 5; // 5 = arbitrary offset
+			childData.area.y += yOffset;
+			childData.area.height = optimalSize.y;
+
+			mRemove->_setLayoutData(childData);
 		}
 		}
 	}
 	}
 
 
@@ -107,6 +127,7 @@ namespace BansheeEngine
 	void GUIComponentFoldout::styleUpdated()
 	void GUIComponentFoldout::styleUpdated()
 	{
 	{
 		mToggle->setStyle(getSubStyleName(getFoldoutButtonStyleType()));
 		mToggle->setStyle(getSubStyleName(getFoldoutButtonStyleType()));
+		mRemove->setStyle(getSubStyleName(getFoldoutButtonStyleType()));
 	}
 	}
 
 
 	const String& GUIComponentFoldout::getGUITypeName()
 	const String& GUIComponentFoldout::getGUITypeName()
@@ -120,4 +141,10 @@ namespace BansheeEngine
 		static String FOLDOUT_BUTTON_STYLE = "ComponentFoldoutButton";
 		static String FOLDOUT_BUTTON_STYLE = "ComponentFoldoutButton";
 		return FOLDOUT_BUTTON_STYLE;		
 		return FOLDOUT_BUTTON_STYLE;		
 	}
 	}
+
+	const String& GUIComponentFoldout::getFoldoutRemoveButtonStyleType()
+	{
+		static String FOLDOUT_REMOVE_BUTTON_STYLE = "ComponentFoldoutRemoveButton";
+		return FOLDOUT_REMOVE_BUTTON_STYLE;
+	}
 }
 }

+ 8 - 3
MBansheeEditor/GUI/GUIComponentFoldout.cs

@@ -6,9 +6,8 @@ namespace BansheeEditor
 {
 {
     public sealed class GUIComponentFoldout : GUIElement
     public sealed class GUIComponentFoldout : GUIElement
     {
     {
-        public delegate void OnToggledDelegate(bool expanded);
-
-        public event OnToggledDelegate OnToggled;
+        public event Action<bool> OnToggled;
+        public event Action OnRemoveClicked;
 
 
         public GUIComponentFoldout(GUIContent content, string style, params GUIOption[] options)
         public GUIComponentFoldout(GUIContent content, string style, params GUIOption[] options)
         {
         {
@@ -53,6 +52,12 @@ namespace BansheeEditor
                 OnToggled(expanded);
                 OnToggled(expanded);
         }
         }
 
 
+        private void DoOnRemoveClicked()
+        {
+            if (OnRemoveClicked != null)
+                OnRemoveClicked();
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_CreateInstance(GUIComponentFoldout instance, GUIContent content, string style, GUIOption[] options);
         private static extern void Internal_CreateInstance(GUIComponentFoldout instance, GUIContent content, string style, GUIOption[] options);
 
 

+ 104 - 0
MBansheeEditor/Inspector/InspectorWindow.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Data.Common;
 using BansheeEngine;
 using BansheeEngine;
 
 
 namespace BansheeEditor
 namespace BansheeEditor
@@ -29,10 +30,13 @@ namespace BansheeEditor
             public Inspector inspector;
             public Inspector inspector;
         }
         }
 
 
+        private static readonly Color HIGHLIGHT_COLOR = new Color(1.0f, 1.0f, 1.0f, 0.5f);
+
         private List<InspectorComponent> inspectorComponents = new List<InspectorComponent>();
         private List<InspectorComponent> inspectorComponents = new List<InspectorComponent>();
         private InspectorResource inspectorResource;
         private InspectorResource inspectorResource;
         private GUIScrollArea inspectorScrollArea;
         private GUIScrollArea inspectorScrollArea;
         private GUILayout inspectorLayout;
         private GUILayout inspectorLayout;
+        private GUITexture scrollAreaHighlight;
 
 
         private SceneObject activeSO;
         private SceneObject activeSO;
         private GUITextBox soNameInput;
         private GUITextBox soNameInput;
@@ -48,6 +52,8 @@ namespace BansheeEditor
         private GUIFloatField soScaleY;
         private GUIFloatField soScaleY;
         private GUIFloatField soScaleZ;
         private GUIFloatField soScaleZ;
 
 
+        private Rect2I dropBounds;
+
         private InspectorType currentType = InspectorType.None;
         private InspectorType currentType = InspectorType.None;
         private Resource activeResource;
         private Resource activeResource;
 
 
@@ -94,7 +100,11 @@ namespace BansheeEditor
             activeSO = so;
             activeSO = so;
 
 
             inspectorScrollArea = new GUIScrollArea();
             inspectorScrollArea = new GUIScrollArea();
+            scrollAreaHighlight = new GUITexture(Builtin.WhiteTexture);
+            scrollAreaHighlight.SetTint(HIGHLIGHT_COLOR);
+
             GUI.AddElement(inspectorScrollArea);
             GUI.AddElement(inspectorScrollArea);
+            GUI.AddElement(scrollAreaHighlight);
             inspectorLayout = inspectorScrollArea.Layout;
             inspectorLayout = inspectorScrollArea.Layout;
 
 
             // SceneObject fields
             // SceneObject fields
@@ -117,6 +127,7 @@ namespace BansheeEditor
 
 
                 data.foldout.SetExpanded(true);
                 data.foldout.SetExpanded(true);
                 data.foldout.OnToggled += (bool expanded) => OnComponentFoldoutToggled(data, expanded);
                 data.foldout.OnToggled += (bool expanded) => OnComponentFoldoutToggled(data, expanded);
+                data.foldout.OnRemoveClicked += () => OnComponentRemoveClicked(allComponents[i].GetType());
 
 
                 inspectorComponents.Add(data);
                 inspectorComponents.Add(data);
 
 
@@ -124,6 +135,9 @@ namespace BansheeEditor
             }
             }
 
 
             inspectorLayout.AddFlexibleSpace();
             inspectorLayout.AddFlexibleSpace();
+
+            dropBounds = inspectorScrollArea.Bounds;
+            scrollAreaHighlight.Bounds = dropBounds;
         }
         }
 
 
         private void OnComponentFoldoutToggled(InspectorComponent inspectorData, bool expanded)
         private void OnComponentFoldoutToggled(InspectorComponent inspectorData, bool expanded)
@@ -277,6 +291,8 @@ namespace BansheeEditor
         private void OnInitialize()
         private void OnInitialize()
         {
         {
             Selection.OnSelectionChanged += OnSelectionChanged;
             Selection.OnSelectionChanged += OnSelectionChanged;
+
+            OnSelectionChanged(new SceneObject[0], new string[0]);
         }
         }
 
 
         private void OnDestroy()
         private void OnDestroy()
@@ -319,6 +335,67 @@ namespace BansheeEditor
             {
             {
                 inspectorResource.inspector.Refresh();
                 inspectorResource.inspector.Refresh();
             }
             }
+
+            // Detect drag and drop
+            bool isDraggingOver = false;
+
+            if (activeSO != null && inspectorScrollArea != null)
+            {
+                Vector2I windowPos = ScreenToWindowPos(Input.PointerPosition);
+                ScriptCode droppedCodeFile = null;
+                string droppedCodeFileName = "";
+
+                if (DragDrop.DragInProgress && DragDrop.Type == DragDropType.Resource)
+                {
+                    isDraggingOver = dropBounds.Contains(windowPos);
+                }
+                else if (DragDrop.DropInProgress && DragDrop.Type == DragDropType.Resource)
+                {
+                    ResourceDragDropData dragData = DragDrop.Data as ResourceDragDropData;
+                    if (dragData != null)
+                    {
+                        foreach (var resPath in dragData.Paths)
+                        {
+                            LibraryEntry entry = ProjectLibrary.GetEntry(resPath);
+                            FileEntry fileEntry = entry as FileEntry;
+                            if (fileEntry != null)
+                            {
+                                if (fileEntry.ResType == ResourceType.ScriptCode)
+                                {
+                                    droppedCodeFile = ProjectLibrary.Load<ScriptCode>(resPath);
+                                    droppedCodeFileName = fileEntry.Name;
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                }
+
+                if (droppedCodeFile != null)
+                {
+                    Type droppedComponentType = null;
+                    Type[] droppedTypes = droppedCodeFile.Types;
+                    foreach (var type in droppedTypes)
+                    {
+                        if (type.IsSubclassOf(typeof(Component)))
+                        {
+                            droppedComponentType = type;
+                            break;
+                        }
+                    }
+
+                    if (droppedComponentType != null)
+                    {
+                        UndoRedo.RecordSO(activeSO, "Added component " + droppedComponentType.Name);
+                        activeSO.AddComponent(droppedComponentType);
+                    }
+                    else
+                        Debug.LogWarning("Cannot find a Component in " + droppedCodeFileName);
+                }
+            }
+
+            if (scrollAreaHighlight != null)
+                scrollAreaHighlight.Visible = isDraggingOver;
         }
         }
 
 
         private void OnSelectionChanged(SceneObject[] objects, string[] paths)
         private void OnSelectionChanged(SceneObject[] objects, string[] paths)
@@ -353,6 +430,15 @@ namespace BansheeEditor
             }
             }
         }
         }
 
 
+        private void OnComponentRemoveClicked(Type componentType)
+        {
+            if (activeSO != null)
+            {
+                UndoRedo.RecordSO(activeSO, "Removed component " + componentType.Name);
+                activeSO.RemoveComponent(componentType);
+            }
+        }
+
         internal void Destroy()
         internal void Destroy()
         {
         {
             Clear();
             Clear();
@@ -380,6 +466,12 @@ namespace BansheeEditor
                 inspectorScrollArea = null;
                 inspectorScrollArea = null;
             }
             }
 
 
+            if (scrollAreaHighlight != null)
+            {
+                scrollAreaHighlight.Destroy();
+                scrollAreaHighlight = null;
+            }
+
             activeSO = null;
             activeSO = null;
             soNameInput = null;
             soNameInput = null;
             soPrefabLayout = null;
             soPrefabLayout = null;
@@ -393,6 +485,7 @@ namespace BansheeEditor
             soScaleX = null;
             soScaleX = null;
             soScaleY = null;
             soScaleY = null;
             soScaleZ = null;
             soScaleZ = null;
+            dropBounds = new Rect2I();
 
 
             activeResource = null;
             activeResource = null;
             currentType = InspectorType.None;
             currentType = InspectorType.None;
@@ -446,6 +539,17 @@ namespace BansheeEditor
             activeSO.LocalScale = scale;
             activeSO.LocalScale = scale;
         }
         }
 
 
+        protected override void WindowResized(int width, int height)
+        {
+            base.WindowResized(width, height);
+
+            if(inspectorScrollArea != null)
+                dropBounds = inspectorScrollArea.Bounds;
+
+            if (scrollAreaHighlight != null)
+                scrollAreaHighlight.Bounds = dropBounds;
+        }
+
         private Inspector GetInspector(Type type)
         private Inspector GetInspector(Type type)
         {
         {
             // TODO - Check if type has a custom inspector
             // TODO - Check if type has a custom inspector

+ 1 - 0
MBansheeEditor/MBansheeEditor.csproj

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

+ 153 - 0
MBansheeEditor/UndoRedo.cs

@@ -0,0 +1,153 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading.Tasks;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    public static class UndoRedo
+    {
+        public static void Undo()
+        {
+            Internal_Undo();
+        }
+
+        public static void Redo()
+        {
+            Internal_Redo();
+        }
+
+        public static void RecordSO(SceneObject so, string description = "")
+        {
+            if (so != null)
+                Internal_RecordSO(so.GetCachedPtr(), description);
+        }
+
+        public static SceneObject[] CloneSO(SceneObject[] so, string description = "")
+        {
+            if (so != null)
+            {
+                List<IntPtr> soPtrs = new List<IntPtr>();
+                for (int i = 0; i < so.Length; i++)
+                {
+                    if(so[i] != null)
+                        soPtrs.Add(so[i].GetCachedPtr());
+                }
+
+                return Internal_CloneSOMulti(soPtrs.ToArray(), description);
+            }
+
+            return new SceneObject[0];
+        }
+
+        public static SceneObject CloneSO(SceneObject so, string description = "")
+        {
+            if (so != null)
+                return Internal_CloneSO(so.GetCachedPtr(), description);
+
+            return null;
+        }
+
+        public static SceneObject Instantiate(Prefab prefab, string description = "")
+        {
+            if (prefab != null)
+                return Internal_Instantiate(prefab.GetCachedPtr(), description);
+
+            return null;
+        }
+
+        public static SceneObject CreateSO(string name, string description = "")
+        {
+            return Internal_CreateSO(name, description);
+        }
+
+        public static void DeleteSO(SceneObject so, string description = "")
+        {
+            if (so != null)
+                Internal_DeleteSO(so.GetCachedPtr(), description);
+        }
+
+        public static void ReparentSO(SceneObject so, SceneObject parent, string description = "")
+        {
+            if (so != null)
+            {
+                IntPtr parentPtr = IntPtr.Zero;
+                if (parent != null)
+                    parentPtr = parent.GetCachedPtr();
+
+                Internal_ReparentSO(so.GetCachedPtr(), parentPtr, description);
+            }
+        }
+
+        public static void ReparentSO(SceneObject[] so, SceneObject parent, string description = "")
+        {
+            if (so != null)
+            {
+                List<IntPtr> soPtrs = new List<IntPtr>();
+                for (int i = 0; i < so.Length; i++)
+                {
+                    if (so[i] != null)
+                        soPtrs.Add(so[i].GetCachedPtr());
+                }
+
+                if (soPtrs.Count > 0)
+                {
+                    IntPtr parentPtr = IntPtr.Zero;
+                    if (parent != null)
+                        parentPtr = parent.GetCachedPtr();
+
+                    Internal_ReparentSOMulti(soPtrs.ToArray(), parentPtr, description);
+                }
+            }
+        }
+
+        public static void PushGroup(string name)
+        {
+            Internal_PushGroup(name);
+        }
+
+        public static void PopGroup(string name)
+        {
+            Internal_PopGroup(name);
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal static extern void Internal_Undo();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal static extern void Internal_Redo();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal static extern void Internal_PushGroup(string name);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal static extern void Internal_PopGroup(string name);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal static extern void Internal_RecordSO(IntPtr soPtr, string description);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal static extern SceneObject Internal_CloneSO(IntPtr soPtr, string description);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal static extern SceneObject[] Internal_CloneSOMulti(IntPtr[] soPtr, string description);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal static extern SceneObject Internal_Instantiate(IntPtr prefabPtr, string description);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal static extern SceneObject Internal_CreateSO(string name, string description);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal static extern void Internal_DeleteSO(IntPtr soPtr, string description);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal static extern void Internal_ReparentSO(IntPtr soPtr, IntPtr parentSOPtr, string description);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal static extern void Internal_ReparentSOMulti(IntPtr[] soPtr, IntPtr parentSOPtr, string description);
+    }
+}

+ 10 - 0
MBansheeEngine/SceneObject.cs

@@ -186,6 +186,11 @@ namespace BansheeEngine
             return (T)Component.Internal_AddComponent(this, typeof (T));
             return (T)Component.Internal_AddComponent(this, typeof (T));
         }
         }
 
 
+        public Component AddComponent(Type type)
+        {
+            return Component.Internal_AddComponent(this, type);
+        }
+
         public T GetComponent<T>() where T : Component
         public T GetComponent<T>() where T : Component
         {
         {
             return (T)Component.Internal_GetComponent(this, typeof(T));
             return (T)Component.Internal_GetComponent(this, typeof(T));
@@ -201,6 +206,11 @@ namespace BansheeEngine
             Component.Internal_RemoveComponent(this, typeof(T));
             Component.Internal_RemoveComponent(this, typeof(T));
         }
         }
 
 
+        public void RemoveComponent(Type type)
+        {
+            Component.Internal_RemoveComponent(this, type);
+        }
+
         public int GetNumChildren()
         public int GetNumChildren()
         {
         {
             int value;
             int value;

+ 8 - 0
MBansheeEngine/ScriptCode.cs

@@ -17,6 +17,11 @@ namespace BansheeEngine
             set { Internal_SetEditorScript(mCachedPtr, value); }
             set { Internal_SetEditorScript(mCachedPtr, value); }
         }
         }
 
 
+        public Type[] Types
+        {
+            get { return Internal_GetTypes(mCachedPtr); }
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern string Internal_GetText(IntPtr thisPtr);
         private static extern string Internal_GetText(IntPtr thisPtr);
 
 
@@ -28,5 +33,8 @@ namespace BansheeEngine
 
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_SetEditorScript(IntPtr thisPtr, bool value);
         private static extern void Internal_SetEditorScript(IntPtr thisPtr, bool value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern Type[] Internal_GetTypes(IntPtr thisPtr);
     }
     }
 }
 }

+ 3 - 0
SBansheeEditor/Include/BsScriptGUIComponentFoldout.h

@@ -18,11 +18,14 @@ namespace BansheeEngine
 		static void internal_setTint(ScriptGUIComponentFoldout* nativeInstance, Color color);
 		static void internal_setTint(ScriptGUIComponentFoldout* nativeInstance, Color color);
 
 
 		static void onToggled(MonoObject* instance, bool expanded);
 		static void onToggled(MonoObject* instance, bool expanded);
+		static void onRemoveClicked(MonoObject* instance);
 
 
 		ScriptGUIComponentFoldout(MonoObject* instance, GUIComponentFoldout* foldout);
 		ScriptGUIComponentFoldout(MonoObject* instance, GUIComponentFoldout* foldout);
 
 
 		typedef void (__stdcall *OnToggledThunkDef) (MonoObject*, bool, MonoException**);
 		typedef void (__stdcall *OnToggledThunkDef) (MonoObject*, bool, MonoException**);
+		typedef void(__stdcall *OnRemoveClickedThunkDef) (MonoObject*, MonoException**);
 
 
 		static OnToggledThunkDef onToggledThunk;
 		static OnToggledThunkDef onToggledThunk;
+		static OnRemoveClickedThunkDef onRemoveClickedThunk;
 	};
 	};
 }
 }

+ 29 - 0
SBansheeEditor/Include/BsScriptUndoRedo.h

@@ -0,0 +1,29 @@
+#pragma once
+
+#include "BsScriptEditorPrerequisites.h"
+#include "BsScriptObject.h"
+
+namespace BansheeEngine
+{
+	class BS_SCR_BED_EXPORT ScriptUndoRedo : public ScriptObject <ScriptUndoRedo>
+	{
+	public:
+		SCRIPT_OBJ(EDITOR_ASSEMBLY, "BansheeEditor", "UndoRedo");
+
+	private:
+		ScriptUndoRedo(MonoObject* instance);
+
+		static void internal_Undo();
+		static void internal_Redo();
+		static void internal_PushGroup(MonoString* name);
+		static void internal_PopGroup(MonoString* name);
+		static void internal_RecordSO(ScriptSceneObject* soPtr, MonoString* description);
+		static MonoObject* internal_CloneSO(ScriptSceneObject* soPtr, MonoString* description);
+		static MonoArray* internal_CloneSOMulti(MonoArray* soPtrs, MonoString* description);
+		static MonoObject* internal_Instantiate(ScriptPrefab* prefabPtr, MonoString* description);
+		static MonoObject* internal_CreateSO(MonoString* name, MonoString* description);
+		static void internal_DeleteSO(ScriptSceneObject* soPtr, MonoString* description);
+		static void internal_ReparentSO(ScriptSceneObject* soPtr, ScriptSceneObject* parentSOPtr, MonoString* description);
+		static void internal_ReparentSOMulti(MonoArray* soPtrs, ScriptSceneObject* parentSOPtr, MonoString* description);
+	};
+}

+ 2 - 0
SBansheeEditor/SBansheeEditor.vcxproj

@@ -281,6 +281,7 @@
     <ClInclude Include="Include\BsScriptProjectLibrary.h" />
     <ClInclude Include="Include\BsScriptProjectLibrary.h" />
     <ClInclude Include="Include\BsScriptSceneViewHandler.h" />
     <ClInclude Include="Include\BsScriptSceneViewHandler.h" />
     <ClInclude Include="Include\BsScriptSelection.h" />
     <ClInclude Include="Include\BsScriptSelection.h" />
+    <ClInclude Include="Include\BsScriptUndoRedo.h" />
     <ClInclude Include="Include\BsScriptUnitTests.h" />
     <ClInclude Include="Include\BsScriptUnitTests.h" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
@@ -333,6 +334,7 @@
     <ClCompile Include="Source\BsScriptProjectLibrary.cpp" />
     <ClCompile Include="Source\BsScriptProjectLibrary.cpp" />
     <ClCompile Include="Source\BsScriptSceneViewHandler.cpp" />
     <ClCompile Include="Source\BsScriptSceneViewHandler.cpp" />
     <ClCompile Include="Source\BsScriptSelection.cpp" />
     <ClCompile Include="Source\BsScriptSelection.cpp" />
+    <ClCompile Include="Source\BsScriptUndoRedo.cpp" />
     <ClCompile Include="Source\BsScriptUnitTests.cpp" />
     <ClCompile Include="Source\BsScriptUnitTests.cpp" />
   </ItemGroup>
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

+ 6 - 0
SBansheeEditor/SBansheeEditor.vcxproj.filters

@@ -165,6 +165,9 @@
     <ClInclude Include="Include\BsScriptGUITextureField.h">
     <ClInclude Include="Include\BsScriptGUITextureField.h">
       <Filter>Header Files</Filter>
       <Filter>Header Files</Filter>
     </ClInclude>
     </ClInclude>
+    <ClInclude Include="Include\BsScriptUndoRedo.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsScriptEditorPlugin.cpp">
     <ClCompile Include="Source\BsScriptEditorPlugin.cpp">
@@ -317,5 +320,8 @@
     <ClCompile Include="Source\BsScriptGUITextureField.cpp">
     <ClCompile Include="Source\BsScriptGUITextureField.cpp">
       <Filter>Source Files</Filter>
       <Filter>Source Files</Filter>
     </ClCompile>
     </ClCompile>
+    <ClCompile Include="Source\BsScriptUndoRedo.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   </ItemGroup>
 </Project>
 </Project>

+ 8 - 0
SBansheeEditor/Source/BsScriptGUIComponentFoldout.cpp

@@ -20,6 +20,7 @@ using namespace std::placeholders;
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
 	ScriptGUIComponentFoldout::OnToggledThunkDef ScriptGUIComponentFoldout::onToggledThunk;
 	ScriptGUIComponentFoldout::OnToggledThunkDef ScriptGUIComponentFoldout::onToggledThunk;
+	ScriptGUIComponentFoldout::OnRemoveClickedThunkDef ScriptGUIComponentFoldout::onRemoveClickedThunk;
 
 
 	ScriptGUIComponentFoldout::ScriptGUIComponentFoldout(MonoObject* instance, GUIComponentFoldout* foldout)
 	ScriptGUIComponentFoldout::ScriptGUIComponentFoldout(MonoObject* instance, GUIComponentFoldout* foldout)
 		:TScriptGUIElement(instance, foldout)
 		:TScriptGUIElement(instance, foldout)
@@ -36,6 +37,7 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_SetTint", &ScriptGUIComponentFoldout::internal_setTint);
 		metaData.scriptClass->addInternalCall("Internal_SetTint", &ScriptGUIComponentFoldout::internal_setTint);
 
 
 		onToggledThunk = (OnToggledThunkDef)metaData.scriptClass->getMethod("DoOnToggled", 1)->getThunk();
 		onToggledThunk = (OnToggledThunkDef)metaData.scriptClass->getMethod("DoOnToggled", 1)->getThunk();
+		onRemoveClickedThunk = (OnRemoveClickedThunkDef)metaData.scriptClass->getMethod("OnRemoveClicked")->getThunk();
 	}
 	}
 
 
 	void ScriptGUIComponentFoldout::internal_createInstance(MonoObject* instance, MonoObject* content, MonoString* style, MonoArray* guiOptions)
 	void ScriptGUIComponentFoldout::internal_createInstance(MonoObject* instance, MonoObject* content, MonoString* style, MonoArray* guiOptions)
@@ -50,6 +52,7 @@ namespace BansheeEngine
 		GUIComponentFoldout* guiFoldout = GUIComponentFoldout::create(label);
 		GUIComponentFoldout* guiFoldout = GUIComponentFoldout::create(label);
 
 
 		guiFoldout->onStateChanged.connect(std::bind(&ScriptGUIComponentFoldout::onToggled, instance, _1));
 		guiFoldout->onStateChanged.connect(std::bind(&ScriptGUIComponentFoldout::onToggled, instance, _1));
+		guiFoldout->onRemoveClicked.connect(std::bind(&ScriptGUIComponentFoldout::onRemoveClicked, instance));
 
 
 		ScriptGUIComponentFoldout* nativeInstance = new (bs_alloc<ScriptGUIComponentFoldout>()) ScriptGUIComponentFoldout(instance, guiFoldout);
 		ScriptGUIComponentFoldout* nativeInstance = new (bs_alloc<ScriptGUIComponentFoldout>()) ScriptGUIComponentFoldout(instance, guiFoldout);
 	}
 	}
@@ -84,4 +87,9 @@ namespace BansheeEngine
 	{
 	{
 		MonoUtil::invokeThunk(onToggledThunk, instance, expanded);
 		MonoUtil::invokeThunk(onToggledThunk, instance, expanded);
 	}
 	}
+
+	void ScriptGUIComponentFoldout::onRemoveClicked(MonoObject* instance)
+	{
+		MonoUtil::invokeThunk(onRemoveClickedThunk, instance);
+	}
 }
 }

+ 152 - 0
SBansheeEditor/Source/BsScriptUndoRedo.cpp

@@ -0,0 +1,152 @@
+#include "BsScriptUndoRedo.h"
+#include "BsScriptMeta.h"
+#include "BsMonoClass.h"
+#include "BsMonoMethod.h"
+#include "BsScriptSceneObject.h"
+#include "BsMonoUtil.h"
+#include "BsScriptGameObjectManager.h"
+#include "BsUndoRedo.h"
+#include "BsCmdRecordSO.h"
+#include "BsCmdCloneSO.h"
+#include "BsCmdCreateSO.h"
+#include "BsCmdDeleteSO.h"
+#include "BsCmdInstantiateSO.h"
+#include "BsCmdReparentSO.h"
+#include "BsScriptPrefab.h"
+#include "BsPrefab.h"
+
+namespace BansheeEngine
+{
+	ScriptUndoRedo::ScriptUndoRedo(MonoObject* instance)
+		:ScriptObject(instance)
+	{
+
+	}
+
+	void ScriptUndoRedo::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("internal_Undo", &ScriptUndoRedo::internal_Undo);
+		metaData.scriptClass->addInternalCall("internal_Redo", &ScriptUndoRedo::internal_Redo);
+		metaData.scriptClass->addInternalCall("internal_PushGroup", &ScriptUndoRedo::internal_PushGroup);
+		metaData.scriptClass->addInternalCall("internal_PopGroup", &ScriptUndoRedo::internal_PopGroup);
+		metaData.scriptClass->addInternalCall("internal_RecordSO", &ScriptUndoRedo::internal_RecordSO);
+		metaData.scriptClass->addInternalCall("internal_CloneSO", &ScriptUndoRedo::internal_CloneSO);
+		metaData.scriptClass->addInternalCall("internal_CloneSOMulti", &ScriptUndoRedo::internal_CloneSOMulti);
+		metaData.scriptClass->addInternalCall("internal_Instantiate", &ScriptUndoRedo::internal_Instantiate);
+		metaData.scriptClass->addInternalCall("internal_CreateSO", &ScriptUndoRedo::internal_CreateSO);
+		metaData.scriptClass->addInternalCall("internal_DeleteSO", &ScriptUndoRedo::internal_DeleteSO);
+		metaData.scriptClass->addInternalCall("internal_ReparentSO", &ScriptUndoRedo::internal_ReparentSO);
+		metaData.scriptClass->addInternalCall("internal_ReparentSOMulti", &ScriptUndoRedo::internal_ReparentSOMulti);
+	}
+
+	void ScriptUndoRedo::internal_Undo()
+	{
+		UndoRedo::instance().undo();
+	}
+
+	void ScriptUndoRedo::internal_Redo()
+	{
+		UndoRedo::instance().redo();
+	}
+
+	void ScriptUndoRedo::internal_PushGroup(MonoString* name)
+	{
+		String nativeName = MonoUtil::monoToString(name);
+		UndoRedo::instance().pushGroup(nativeName);
+	}
+
+	void ScriptUndoRedo::internal_PopGroup(MonoString* name)
+	{
+		String nativeName = MonoUtil::monoToString(name);
+		UndoRedo::instance().popGroup(nativeName);
+	}
+
+	void ScriptUndoRedo::internal_RecordSO(ScriptSceneObject* soPtr, MonoString* description)
+	{
+		WString nativeDescription = MonoUtil::monoToWString(description);
+		CmdRecordSO::execute(soPtr->getNativeSceneObject(), nativeDescription);
+	}
+
+	MonoObject* ScriptUndoRedo::internal_CloneSO(ScriptSceneObject* soPtr, MonoString* description)
+	{
+		WString nativeDescription = MonoUtil::monoToWString(description);
+		HSceneObject clone = CmdCloneSO::execute(soPtr->getNativeSceneObject(), nativeDescription);
+
+		return ScriptGameObjectManager::instance().createScriptSceneObject(clone)->getManagedInstance();
+	}
+
+	MonoArray* ScriptUndoRedo::internal_CloneSOMulti(MonoArray* soPtrs, MonoString* description)
+	{
+		WString nativeDescription = MonoUtil::monoToWString(description);
+
+		ScriptArray input(soPtrs);
+		ScriptArray output = ScriptArray::create<ScriptSceneObject>(input.size());
+
+		for (UINT32 i = 0; i < input.size(); i++)
+		{
+			ScriptSceneObject* soPtr = input.get<ScriptSceneObject*>(i);
+			HSceneObject clone = CmdCloneSO::execute(soPtr->getNativeSceneObject(), nativeDescription);
+			ScriptSceneObject* cloneSoPtr = ScriptGameObjectManager::instance().createScriptSceneObject(clone);
+
+			output.set(i, cloneSoPtr->getManagedInstance());
+		}
+
+		return output.getInternal();
+	}
+
+	MonoObject* ScriptUndoRedo::internal_Instantiate(ScriptPrefab* prefabPtr, MonoString* description)
+	{
+		HPrefab prefab = prefabPtr->getPrefabHandle();
+		if (!prefab.isLoaded())
+			return nullptr;
+
+		WString nativeDescription = MonoUtil::monoToWString(description);
+		HSceneObject clone = CmdInstantiateSO::execute(prefab, nativeDescription);
+
+		return ScriptGameObjectManager::instance().createScriptSceneObject(clone)->getManagedInstance();
+	}
+
+	MonoObject* ScriptUndoRedo::internal_CreateSO(MonoString* name, MonoString* description)
+	{
+		String nativeName = MonoUtil::monoToString(name);
+		WString nativeDescription = MonoUtil::monoToWString(description);
+		HSceneObject newObj = CmdCreateSO::execute(nativeName, 0, nativeDescription);
+
+		return ScriptGameObjectManager::instance().createScriptSceneObject(newObj)->getManagedInstance();
+	}
+
+	void ScriptUndoRedo::internal_DeleteSO(ScriptSceneObject* soPtr, MonoString* description)
+	{
+		WString nativeDescription = MonoUtil::monoToWString(description);
+		CmdDeleteSO::execute(soPtr->getNativeSceneObject(), nativeDescription);
+	}
+
+	void ScriptUndoRedo::internal_ReparentSO(ScriptSceneObject* soPtr, ScriptSceneObject* parentSOPtr, MonoString* description)
+	{
+		HSceneObject parent;
+		if (parentSOPtr != nullptr)
+			parent = parentSOPtr->getNativeSceneObject();
+
+		WString nativeDescription = MonoUtil::monoToWString(description);
+
+		HSceneObject so = soPtr->getNativeSceneObject();
+		CmdReparentSO::execute(so, parent, nativeDescription);
+	}
+
+	void ScriptUndoRedo::internal_ReparentSOMulti(MonoArray* soPtrs, ScriptSceneObject* parentSOPtr, MonoString* description)
+	{
+		HSceneObject parent;
+		if (parentSOPtr != nullptr)
+			parent = parentSOPtr->getNativeSceneObject();
+
+		WString nativeDescription = MonoUtil::monoToWString(description);
+
+		ScriptArray input(soPtrs);
+		for (UINT32 i = 0; i < input.size(); i++)
+		{
+			ScriptSceneObject* soPtr = input.get<ScriptSceneObject*>(i);
+			HSceneObject so = soPtr->getNativeSceneObject();
+			CmdReparentSO::execute(so, parent, nativeDescription);
+		}
+	}
+}

+ 6 - 0
SBansheeEngine/Include/BsScriptAssemblyManager.h

@@ -72,6 +72,11 @@ namespace BansheeEngine
 		 */
 		 */
 		MonoClass* getSystemGenericDictionaryClass() const { return mSystemGenericDictionaryClass; }
 		MonoClass* getSystemGenericDictionaryClass() const { return mSystemGenericDictionaryClass; }
 
 
+		/**
+		 * @brief	Gets the managed class for System.Type type.
+		 */
+		MonoClass* getSystemTypeClass() const { return mSystemTypeClass; }
+
 		/**
 		/**
 		 * @brief	Gets the managed class for BansheeEngine.Component type.
 		 * @brief	Gets the managed class for BansheeEngine.Component type.
 		 */
 		 */
@@ -110,6 +115,7 @@ namespace BansheeEngine
 		MonoClass* mSystemArrayClass;
 		MonoClass* mSystemArrayClass;
 		MonoClass* mSystemGenericListClass;
 		MonoClass* mSystemGenericListClass;
 		MonoClass* mSystemGenericDictionaryClass;
 		MonoClass* mSystemGenericDictionaryClass;
+		MonoClass* mSystemTypeClass;
 
 
 		MonoClass* mComponentClass;
 		MonoClass* mComponentClass;
 		MonoClass* mSceneObjectClass;
 		MonoClass* mSceneObjectClass;

+ 0 - 6
SBansheeEngine/Include/BsScriptObject.h

@@ -109,12 +109,6 @@ namespace BansheeEngine
 		ScriptObject(MonoObject* instance)
 		ScriptObject(MonoObject* instance)
 			:Base(instance)
 			:Base(instance)
 		{	
 		{	
-			// Compiler will only generate code for stuff that is directly used, including static data members,
-			// so we fool it here like we're using the class directly. Otherwise compiler won't generate the code for the member
-			// and our type won't get initialized on start (Actual behavior is a bit more random)
-			// TODO - Use volatile instead?
-			initOnStart.makeSureIAmInstantiated();
-
 			Type* param = (Type*)(Base*)this; // Needed due to multiple inheritance. Safe since Type must point to an class derived from this one.
 			Type* param = (Type*)(Base*)this; // Needed due to multiple inheritance. Safe since Type must point to an class derived from this one.
 
 
 			if(metaData.thisPtrField != nullptr)
 			if(metaData.thisPtrField != nullptr)

+ 3 - 0
SBansheeEngine/Include/BsScriptScriptCode.h

@@ -18,14 +18,17 @@ namespace BansheeEngine
 		HScriptCode getScriptCodeHandle() const { return mScriptCode; }
 		HScriptCode getScriptCodeHandle() const { return mScriptCode; }
 	private:
 	private:
 		friend class ScriptResourceManager;
 		friend class ScriptResourceManager;
+		typedef std::pair<WString, WString> FullTypeName;
 
 
 		static void internal_createInstance(MonoObject* instance, MonoString* text);
 		static void internal_createInstance(MonoObject* instance, MonoString* text);
 		static MonoString* internal_getText(ScriptScriptCode* thisPtr);
 		static MonoString* internal_getText(ScriptScriptCode* thisPtr);
 		static void internal_setText(ScriptScriptCode* thisPtr, MonoString* text);
 		static void internal_setText(ScriptScriptCode* thisPtr, MonoString* text);
+		static MonoArray* internal_getTypes(ScriptScriptCode* thisPtr);
 
 
 		ScriptScriptCode(MonoObject* instance, const HScriptCode& scriptCode);
 		ScriptScriptCode(MonoObject* instance, const HScriptCode& scriptCode);
 
 
 		void _onManagedInstanceDeleted();
 		void _onManagedInstanceDeleted();
+		static Vector<FullTypeName> parseTypes(const WString& code);
 
 
 		HScriptCode mScriptCode;
 		HScriptCode mScriptCode;
 	};
 	};

+ 7 - 1
SBansheeEngine/Source/BsScriptAssemblyManager.cpp

@@ -29,7 +29,8 @@ namespace BansheeEngine
 	ScriptAssemblyManager::ScriptAssemblyManager()
 	ScriptAssemblyManager::ScriptAssemblyManager()
 		:mBaseTypesInitialized(false), mSerializeObjectAttribute(nullptr), mDontSerializeFieldAttribute(nullptr), 
 		:mBaseTypesInitialized(false), mSerializeObjectAttribute(nullptr), mDontSerializeFieldAttribute(nullptr), 
 		mComponentClass(nullptr), mSceneObjectClass(nullptr), mSerializeFieldAttribute(nullptr), mHideInInspectorAttribute(nullptr), 
 		mComponentClass(nullptr), mSceneObjectClass(nullptr), mSerializeFieldAttribute(nullptr), mHideInInspectorAttribute(nullptr), 
-		mSystemArrayClass(nullptr), mSystemGenericListClass(nullptr), mSystemGenericDictionaryClass(nullptr), mMissingComponentClass(nullptr)
+		mSystemArrayClass(nullptr), mSystemGenericListClass(nullptr), mSystemGenericDictionaryClass(nullptr), mMissingComponentClass(nullptr),
+		mSystemTypeClass(nullptr)
 	{
 	{
 
 
 	}
 	}
@@ -443,6 +444,7 @@ namespace BansheeEngine
 		mSystemArrayClass = nullptr;
 		mSystemArrayClass = nullptr;
 		mSystemGenericListClass = nullptr;
 		mSystemGenericListClass = nullptr;
 		mSystemGenericDictionaryClass = nullptr;
 		mSystemGenericDictionaryClass = nullptr;
+		mSystemTypeClass = nullptr;
 
 
 		mSerializeObjectAttribute = nullptr;
 		mSerializeObjectAttribute = nullptr;
 		mDontSerializeFieldAttribute = nullptr;
 		mDontSerializeFieldAttribute = nullptr;
@@ -478,6 +480,10 @@ namespace BansheeEngine
 		if(mSystemGenericDictionaryClass == nullptr)
 		if(mSystemGenericDictionaryClass == nullptr)
 			BS_EXCEPT(InvalidStateException, "Cannot find Dictionary<TKey, TValue> managed class.");
 			BS_EXCEPT(InvalidStateException, "Cannot find Dictionary<TKey, TValue> managed class.");
 
 
+		mSystemTypeClass = corlib->getClass("System", "Type");
+		if (mSystemTypeClass == nullptr)
+			BS_EXCEPT(InvalidStateException, "Cannot find Type managed class.");
+
 		mSerializeObjectAttribute = bansheeEngineAssembly->getClass("BansheeEngine", "SerializeObject");
 		mSerializeObjectAttribute = bansheeEngineAssembly->getClass("BansheeEngine", "SerializeObject");
 		if(mSerializeObjectAttribute == nullptr)
 		if(mSerializeObjectAttribute == nullptr)
 			BS_EXCEPT(InvalidStateException, "Cannot find SerializableObject managed class.");
 			BS_EXCEPT(InvalidStateException, "Cannot find SerializableObject managed class.");

+ 101 - 1
SBansheeEngine/Source/BsScriptScriptCode.cpp

@@ -5,6 +5,9 @@
 #include "BsMonoClass.h"
 #include "BsMonoClass.h"
 #include "BsMonoManager.h"
 #include "BsMonoManager.h"
 #include "BsMonoUtil.h"
 #include "BsMonoUtil.h"
+#include "BsScriptAssemblyManager.h"
+#include "BsManagedSerializableObjectInfo.h"
+#include <regex>
 
 
 using namespace std::placeholders;
 using namespace std::placeholders;
 
 
@@ -13,7 +16,7 @@ namespace BansheeEngine
 	ScriptScriptCode::ScriptScriptCode(MonoObject* instance, const HScriptCode& scriptCode)
 	ScriptScriptCode::ScriptScriptCode(MonoObject* instance, const HScriptCode& scriptCode)
 		:ScriptObject(instance), mScriptCode(scriptCode)
 		:ScriptObject(instance), mScriptCode(scriptCode)
 	{
 	{
-
+		
 	}
 	}
 
 
 	void ScriptScriptCode::initRuntimeData()
 	void ScriptScriptCode::initRuntimeData()
@@ -21,6 +24,7 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptScriptCode::internal_createInstance);
 		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptScriptCode::internal_createInstance);
 		metaData.scriptClass->addInternalCall("Internal_GetText", &ScriptScriptCode::internal_getText);
 		metaData.scriptClass->addInternalCall("Internal_GetText", &ScriptScriptCode::internal_getText);
 		metaData.scriptClass->addInternalCall("Internal_SetText", &ScriptScriptCode::internal_setText);
 		metaData.scriptClass->addInternalCall("Internal_SetText", &ScriptScriptCode::internal_setText);
+		metaData.scriptClass->addInternalCall("Internal_GetTypes", &ScriptScriptCode::internal_getTypes);
 	}
 	}
 
 
 	void ScriptScriptCode::internal_createInstance(MonoObject* instance, MonoString* text)
 	void ScriptScriptCode::internal_createInstance(MonoObject* instance, MonoString* text)
@@ -45,6 +49,34 @@ namespace BansheeEngine
 
 
 		scriptCode->setString(MonoUtil::monoToWString(text));
 		scriptCode->setString(MonoUtil::monoToWString(text));
 	}
 	}
+	
+	MonoArray* ScriptScriptCode::internal_getTypes(ScriptScriptCode* thisPtr)
+	{
+		Vector<FullTypeName> types;
+		if (thisPtr->getScriptCodeHandle().isLoaded())
+			types = parseTypes(thisPtr->getScriptCodeHandle()->getString());
+
+		Vector<MonoReflectionType*> validTypes;
+		for (auto& type : types)
+		{
+			ManagedSerializableObjectInfoPtr objInfo;
+			if (ScriptAssemblyManager::instance().getSerializableObjectInfo(toString(type.first), toString(type.second), objInfo))
+			{
+				MonoType* monoType = mono_class_get_type(objInfo->mTypeInfo->getMonoClass());
+				validTypes.push_back(mono_type_get_object(MonoManager::instance().getDomain(), monoType));
+			}
+		}
+
+		UINT32 numValidTypes = (UINT32)validTypes.size();
+
+		MonoClass* typeClass = ScriptAssemblyManager::instance().getSystemTypeClass();
+		MonoArray* output = mono_array_new(MonoManager::instance().getDomain(), typeClass->_getInternalClass(), numValidTypes);
+
+		for (UINT32 i = 0; i < numValidTypes; i++)
+			mono_array_set(output, MonoReflectionType*, i, validTypes[i]);
+
+		return output;
+	}
 
 
 	void ScriptScriptCode::_onManagedInstanceDeleted()
 	void ScriptScriptCode::_onManagedInstanceDeleted()
 	{
 	{
@@ -58,4 +90,72 @@ namespace BansheeEngine
 	{
 	{
 		mScriptCode = static_resource_cast<ScriptCode>(resource);
 		mScriptCode = static_resource_cast<ScriptCode>(resource);
 	}
 	}
+
+	Vector<ScriptScriptCode::FullTypeName> ScriptScriptCode::parseTypes(const WString& code)
+	{
+		struct NamespaceData
+		{
+			WString ns;
+			INT32 bracketIdx;
+		};
+
+		Vector<FullTypeName> output;
+		Stack<NamespaceData> namespaces;
+
+		// Note: Won't match unicode escape sequences
+		WString identifierPattern = LR"([_@a-zA-Z][\p{Lu}\p{Ll}\p{Lt}\p{Lm}\p{Lo}\p{Nl}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\p{Cf}]*)";
+		std::wregex identifierRegex(identifierPattern, std::regex_constants::ECMAScript | std::regex_constants::icase);
+
+		WString nsToken = L"namespace";
+		WString classToken = L"class";
+
+		UINT32 idx = 0;
+		INT32 bracketIdx = 0;
+		for (auto iter = code.begin(); iter != code.end(); ++iter)
+		{
+			wchar_t ch = *iter;
+			
+			if (code.compare(idx, classToken.size(), classToken))
+			{
+				std::wsmatch results;
+				if (std::regex_match(iter + classToken.size(), code.end(), results, identifierRegex))
+				{
+					WString ns = L"";
+					if (!namespaces.empty())
+						ns = namespaces.top().ns;
+
+					std::wstring tempStr = results[0];
+					WString typeName = tempStr.c_str();
+
+					output.push_back(FullTypeName());
+					FullTypeName& nsTypePair = output.back();
+					nsTypePair.first = ns;
+					nsTypePair.second = typeName;
+				}
+			}
+			else if (code.compare(idx, nsToken.size(), nsToken))
+			{
+				std::wsmatch results;
+				if (std::regex_match(iter + nsToken.size(), code.end(), results, identifierRegex))
+				{
+					std::wstring tempStr = results[0];
+					WString ns = tempStr.c_str();
+
+					namespaces.push({ ns, bracketIdx + 1 });
+				}
+			}
+			else if (ch == '{')
+			{
+				bracketIdx++;
+			}
+			else if (ch == '}')
+			{
+				bracketIdx--;
+			}
+
+			idx++;
+		}
+
+		return output;
+	}
 }
 }

+ 20 - 1
TODO.txt

@@ -54,11 +54,31 @@ Code quality improvements:
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 Polish
 Polish
 
 
+Test:
+ - Component foldout X
+ - Drag and drop for components
+
  - Duplicating a mesh doesn't properly render the mesh
  - Duplicating a mesh doesn't properly render the mesh
   - It seems to be due to renderables sharing materials. I generate a separate per-object buffer per each
   - It seems to be due to renderables sharing materials. I generate a separate per-object buffer per each
     renderable (but maybe I shouldn't? Just generate the one and update it before rendering) and then assign
     renderable (but maybe I shouldn't? Just generate the one and update it before rendering) and then assign
 	it to the same MaterialCore which means whichever one was applied last was the one that sticks.
 	it to the same MaterialCore which means whichever one was applied last was the one that sticks.
 
 
+Maybe:
+Make current material not a resource
+Store material in Renderable by value (so its copied when cloning happens)
+ - TODO - Does cloning even properly clone Renderable or do they point to the same RenderableHandler? (If not, switching RTTI to non-ptr might be okay)
+  - Same issue will be with CameraHandler and LightInternal
+
+Add a new MaterialPrefab type that is a resource
+ - Internally it stores a Material and it can be instantiated (copied) for use on Renderable
+POTENTIALLY - When serializing Renderable don't serialize a handle to the resource but the material directly. Then cloning it will ensure it has an unique instance.
+  - Same would go for assignment of material to Renderable, it would need to be cloned
+  - A pointer to the original resource could still be held
+BE AWARE - That in Unity when accessed from inspector materials aren't instantiated (they all share parameters)
+ - However sometimes it will be useful to modify the materials without cloning & reassigning them (i.e. I'd still like to keep the original reference)
+  - But I think this will only happen at runtime, and it's probably fine to lose the original reference
+   - or maybe I don't lose the original reference but just don't persist (i.e. serialize) the instantiated ones
+
 Ribek use:
 Ribek use:
  - Camera, Renderable, Material, Texture inspector
  - Camera, Renderable, Material, Texture inspector
   - Material/Shader has no color type so I cannot know when to display normal vector and when color in inspector
   - Material/Shader has no color type so I cannot know when to display normal vector and when color in inspector
@@ -72,7 +92,6 @@ Ribek use:
   - Components adding should be only done by drag and dropping scripts to inspector (undoable)
   - Components adding should be only done by drag and dropping scripts to inspector (undoable)
   - COmponent removal should be done by clicking X in inspector (undoable)
   - COmponent removal should be done by clicking X in inspector (undoable)
   - Adding scene objects should be doable from context menu in Hierarchy, by dropping a Prefab or by main Menu (undoable)
   - Adding scene objects should be doable from context menu in Hierarchy, by dropping a Prefab or by main Menu (undoable)
-  - Deleting them should be doable by context menu in Hierarchy and Del keystroke (undoable)
  - Hook up color picker to guicolor field
  - Hook up color picker to guicolor field
  - (Optionally, needed for GUI editing) GUISkin resource inspector & a way to inspect and save the default editor skin
  - (Optionally, needed for GUI editing) GUISkin resource inspector & a way to inspect and save the default editor skin
    - Will need C# wrapper for GUISkin (and a way to assign the current skin to a window)
    - Will need C# wrapper for GUISkin (and a way to assign the current skin to a window)