Explorar el Código

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 hace 10 años
padre
commit
09bbc8848d
Se han modificado 30 ficheros con 689 adiciones y 29 borrados
  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)
 	 *			and removes them as an undo operation.
 	 */
-	class CmdCloneSO : public EditorCommand
+	class BS_ED_EXPORT CmdCloneSO : public EditorCommand
 	{
 	public:
 		~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 
 	 *			and removes it as an undo operation.
 	 */
-	class CmdCreateSO : public EditorCommand
+	class BS_ED_EXPORT CmdCreateSO : public EditorCommand
 	{
 	public:
 		~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
 	 *			and restores it as an undo operation.
 	 */
-	class CmdDeleteSO : public EditorCommand
+	class BS_ED_EXPORT CmdDeleteSO : public EditorCommand
 	{
 	public:
 		~CmdDeleteSO();

+ 1 - 1
BansheeEditor/Include/BsCmdInputFieldValueChange.h

@@ -12,7 +12,7 @@ namespace BansheeEngine
 	 *			as needed.
 	 */
 	template <class InputFieldType, class ValueType>
-	class CmdInputFieldValueChange : public EditorCommand
+	class BS_ED_EXPORT CmdInputFieldValueChange : public EditorCommand
 	{
 	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)
 	 *			from a prefab and removes them as an undo operation.
 	 */
-	class CmdInstantiateSO : public EditorCommand
+	class BS_ED_EXPORT CmdInstantiateSO : public EditorCommand
 	{
 	public:
 		~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
 	 *			original values as needed.
 	 */
-	class CmdRecordSO : public EditorCommand
+	class BS_ED_EXPORT CmdRecordSO : public EditorCommand
 	{
 	public:
 		~CmdRecordSO();

+ 1 - 1
BansheeEditor/Include/BsCmdReparentSO.h

@@ -11,7 +11,7 @@ namespace BansheeEngine
 	 *			parent change operations. It allows you to apply the parent change
 	 *			or revert the object to its original parent as needed.
 	 */
-	class CmdReparentSO : public EditorCommand
+	class BS_ED_EXPORT CmdReparentSO : public EditorCommand
 	{
 	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
 	 *			some object and allows you to apply or revert that change as needed.
 	 */
-	class EditorCommand
+	class BS_ED_EXPORT EditorCommand
 	{
 	public:
 		EditorCommand(const WString& description);

+ 13 - 1
BansheeEditor/Include/BsGUIComponentFoldout.h

@@ -23,10 +23,15 @@ namespace BansheeEngine
 		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();
 
+		/**
+		 * 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.
 		 *
@@ -82,6 +87,7 @@ namespace BansheeEngine
 		Vector2I _getOptimalSize() const override;
 
 		Event<void(bool)> onStateChanged;
+		Event<void()> onRemoveClicked;
 	protected:
 		virtual ~GUIComponentFoldout();
 
@@ -91,12 +97,18 @@ namespace BansheeEngine
 		 */
 		void toggleTriggered(bool value);
 
+		/**
+		 * @brief	Triggered when the remove button is clicked.
+		 */
+		void removeTriggered();
+
 		/**
 		 * @copydoc	GUIElement::styleUpdated
 		 */
 		void styleUpdated() override;
 
 		GUIToggle* mToggle;
+		GUIButton* mRemove;
 		bool mIsExpanded;
 	};
 }

+ 1 - 1
BansheeEditor/Include/BsUndoRedo.h

@@ -8,7 +8,7 @@ namespace BansheeEngine
 	/**
 	 * @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.

+ 12 - 0
BansheeEditor/Source/BsBuiltinEditorResources.cpp

@@ -1180,11 +1180,23 @@ namespace BansheeEngine
 
 		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;
 		cmpFoldoutStyle.fixedHeight = true;
 		cmpFoldoutStyle.height = 12;
 		cmpFoldoutStyle.minWidth = 30;
 		cmpFoldoutStyle.subStyles[GUIComponentFoldout::getFoldoutButtonStyleType()] = GUIComponentFoldout::getFoldoutButtonStyleType();
+		cmpFoldoutStyle.subStyles[GUIComponentFoldout::getFoldoutRemoveButtonStyleType()] = GUIComponentFoldout::getFoldoutRemoveButtonStyleType();
 
 		skin->setStyle(GUIComponentFoldout::getGUITypeName(), cmpFoldoutStyle);
 

+ 34 - 7
BansheeEditor/Source/BsGUIComponentFoldout.cpp

@@ -2,6 +2,7 @@
 #include "BsGUILayout.h"
 #include "BsGUILabel.h"
 #include "BsGUIToggle.h"
+#include "BsGUIButton.h"
 #include "BsGUITexture.h"
 #include "BsBuiltinResources.h"
 #include "BsGUIWidget.h"
@@ -13,13 +14,18 @@ namespace BansheeEngine
 {
 	GUIComponentFoldout::GUIComponentFoldout(const PrivatelyConstruct& dummy, const HString& label, const String& style,
 		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()));
+		mRemove = GUIButton::create(HEString(L""), getSubStyleName(getFoldoutRemoveButtonStyleType()));
 
 		_registerChildElement(mToggle);
+		_registerChildElement(mRemove);
 
 		mToggle->onToggled.connect(std::bind(&GUIComponentFoldout::toggleTriggered, this, _1));
+		mRemove->onClick.connect(std::bind(&GUIComponentFoldout::removeTriggered, this));
+
+		mToggle->_setElementDepth(1);
 	}
 
 	GUIComponentFoldout::~GUIComponentFoldout()
@@ -79,21 +85,35 @@ namespace BansheeEngine
 		onStateChanged(value);
 	}
 
+	void GUIComponentFoldout::removeTriggered()
+	{
+		onRemoveClicked();
+	}
+
 	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;
 			childData.area.y += yOffset;
-			childData.area.height = optimalSize.y;
+			childData.area.height = toggleOptSize.y;
 
 			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()
 	{
 		mToggle->setStyle(getSubStyleName(getFoldoutButtonStyleType()));
+		mRemove->setStyle(getSubStyleName(getFoldoutButtonStyleType()));
 	}
 
 	const String& GUIComponentFoldout::getGUITypeName()
@@ -120,4 +141,10 @@ namespace BansheeEngine
 		static String FOLDOUT_BUTTON_STYLE = "ComponentFoldoutButton";
 		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 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)
         {
@@ -53,6 +52,12 @@ namespace BansheeEditor
                 OnToggled(expanded);
         }
 
+        private void DoOnRemoveClicked()
+        {
+            if (OnRemoveClicked != null)
+                OnRemoveClicked();
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         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.Collections.Generic;
+using System.Data.Common;
 using BansheeEngine;
 
 namespace BansheeEditor
@@ -29,10 +30,13 @@ namespace BansheeEditor
             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 InspectorResource inspectorResource;
         private GUIScrollArea inspectorScrollArea;
         private GUILayout inspectorLayout;
+        private GUITexture scrollAreaHighlight;
 
         private SceneObject activeSO;
         private GUITextBox soNameInput;
@@ -48,6 +52,8 @@ namespace BansheeEditor
         private GUIFloatField soScaleY;
         private GUIFloatField soScaleZ;
 
+        private Rect2I dropBounds;
+
         private InspectorType currentType = InspectorType.None;
         private Resource activeResource;
 
@@ -94,7 +100,11 @@ namespace BansheeEditor
             activeSO = so;
 
             inspectorScrollArea = new GUIScrollArea();
+            scrollAreaHighlight = new GUITexture(Builtin.WhiteTexture);
+            scrollAreaHighlight.SetTint(HIGHLIGHT_COLOR);
+
             GUI.AddElement(inspectorScrollArea);
+            GUI.AddElement(scrollAreaHighlight);
             inspectorLayout = inspectorScrollArea.Layout;
 
             // SceneObject fields
@@ -117,6 +127,7 @@ namespace BansheeEditor
 
                 data.foldout.SetExpanded(true);
                 data.foldout.OnToggled += (bool expanded) => OnComponentFoldoutToggled(data, expanded);
+                data.foldout.OnRemoveClicked += () => OnComponentRemoveClicked(allComponents[i].GetType());
 
                 inspectorComponents.Add(data);
 
@@ -124,6 +135,9 @@ namespace BansheeEditor
             }
 
             inspectorLayout.AddFlexibleSpace();
+
+            dropBounds = inspectorScrollArea.Bounds;
+            scrollAreaHighlight.Bounds = dropBounds;
         }
 
         private void OnComponentFoldoutToggled(InspectorComponent inspectorData, bool expanded)
@@ -277,6 +291,8 @@ namespace BansheeEditor
         private void OnInitialize()
         {
             Selection.OnSelectionChanged += OnSelectionChanged;
+
+            OnSelectionChanged(new SceneObject[0], new string[0]);
         }
 
         private void OnDestroy()
@@ -319,6 +335,67 @@ namespace BansheeEditor
             {
                 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)
@@ -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()
         {
             Clear();
@@ -380,6 +466,12 @@ namespace BansheeEditor
                 inspectorScrollArea = null;
             }
 
+            if (scrollAreaHighlight != null)
+            {
+                scrollAreaHighlight.Destroy();
+                scrollAreaHighlight = null;
+            }
+
             activeSO = null;
             soNameInput = null;
             soPrefabLayout = null;
@@ -393,6 +485,7 @@ namespace BansheeEditor
             soScaleX = null;
             soScaleY = null;
             soScaleZ = null;
+            dropBounds = new Rect2I();
 
             activeResource = null;
             currentType = InspectorType.None;
@@ -446,6 +539,17 @@ namespace BansheeEditor
             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)
         {
             // TODO - Check if type has a custom inspector

+ 1 - 0
MBansheeEditor/MBansheeEditor.csproj

@@ -128,6 +128,7 @@
     <Compile Include="Scene\ScaleHandle.cs" />
     <Compile Include="ScriptCompiler.cs" />
     <Compile Include="Selection.cs" />
+    <Compile Include="UndoRedo.cs" />
     <Compile Include="UnitTests.cs" />
     <Compile Include="UnitTestTypes.cs" />
   </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));
         }
 
+        public Component AddComponent(Type type)
+        {
+            return Component.Internal_AddComponent(this, type);
+        }
+
         public T GetComponent<T>() where T : Component
         {
             return (T)Component.Internal_GetComponent(this, typeof(T));
@@ -201,6 +206,11 @@ namespace BansheeEngine
             Component.Internal_RemoveComponent(this, typeof(T));
         }
 
+        public void RemoveComponent(Type type)
+        {
+            Component.Internal_RemoveComponent(this, type);
+        }
+
         public int GetNumChildren()
         {
             int value;

+ 8 - 0
MBansheeEngine/ScriptCode.cs

@@ -17,6 +17,11 @@ namespace BansheeEngine
             set { Internal_SetEditorScript(mCachedPtr, value); }
         }
 
+        public Type[] Types
+        {
+            get { return Internal_GetTypes(mCachedPtr); }
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern string Internal_GetText(IntPtr thisPtr);
 
@@ -28,5 +33,8 @@ namespace BansheeEngine
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         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 onToggled(MonoObject* instance, bool expanded);
+		static void onRemoveClicked(MonoObject* instance);
 
 		ScriptGUIComponentFoldout(MonoObject* instance, GUIComponentFoldout* foldout);
 
 		typedef void (__stdcall *OnToggledThunkDef) (MonoObject*, bool, MonoException**);
+		typedef void(__stdcall *OnRemoveClickedThunkDef) (MonoObject*, MonoException**);
 
 		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\BsScriptSceneViewHandler.h" />
     <ClInclude Include="Include\BsScriptSelection.h" />
+    <ClInclude Include="Include\BsScriptUndoRedo.h" />
     <ClInclude Include="Include\BsScriptUnitTests.h" />
   </ItemGroup>
   <ItemGroup>
@@ -333,6 +334,7 @@
     <ClCompile Include="Source\BsScriptProjectLibrary.cpp" />
     <ClCompile Include="Source\BsScriptSceneViewHandler.cpp" />
     <ClCompile Include="Source\BsScriptSelection.cpp" />
+    <ClCompile Include="Source\BsScriptUndoRedo.cpp" />
     <ClCompile Include="Source\BsScriptUnitTests.cpp" />
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

+ 6 - 0
SBansheeEditor/SBansheeEditor.vcxproj.filters

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

+ 8 - 0
SBansheeEditor/Source/BsScriptGUIComponentFoldout.cpp

@@ -20,6 +20,7 @@ using namespace std::placeholders;
 namespace BansheeEngine
 {
 	ScriptGUIComponentFoldout::OnToggledThunkDef ScriptGUIComponentFoldout::onToggledThunk;
+	ScriptGUIComponentFoldout::OnRemoveClickedThunkDef ScriptGUIComponentFoldout::onRemoveClickedThunk;
 
 	ScriptGUIComponentFoldout::ScriptGUIComponentFoldout(MonoObject* instance, GUIComponentFoldout* foldout)
 		:TScriptGUIElement(instance, foldout)
@@ -36,6 +37,7 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_SetTint", &ScriptGUIComponentFoldout::internal_setTint);
 
 		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)
@@ -50,6 +52,7 @@ namespace BansheeEngine
 		GUIComponentFoldout* guiFoldout = GUIComponentFoldout::create(label);
 
 		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);
 	}
@@ -84,4 +87,9 @@ namespace BansheeEngine
 	{
 		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; }
 
+		/**
+		 * @brief	Gets the managed class for System.Type type.
+		 */
+		MonoClass* getSystemTypeClass() const { return mSystemTypeClass; }
+
 		/**
 		 * @brief	Gets the managed class for BansheeEngine.Component type.
 		 */
@@ -110,6 +115,7 @@ namespace BansheeEngine
 		MonoClass* mSystemArrayClass;
 		MonoClass* mSystemGenericListClass;
 		MonoClass* mSystemGenericDictionaryClass;
+		MonoClass* mSystemTypeClass;
 
 		MonoClass* mComponentClass;
 		MonoClass* mSceneObjectClass;

+ 0 - 6
SBansheeEngine/Include/BsScriptObject.h

@@ -109,12 +109,6 @@ namespace BansheeEngine
 		ScriptObject(MonoObject* 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.
 
 			if(metaData.thisPtrField != nullptr)

+ 3 - 0
SBansheeEngine/Include/BsScriptScriptCode.h

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

+ 7 - 1
SBansheeEngine/Source/BsScriptAssemblyManager.cpp

@@ -29,7 +29,8 @@ namespace BansheeEngine
 	ScriptAssemblyManager::ScriptAssemblyManager()
 		:mBaseTypesInitialized(false), mSerializeObjectAttribute(nullptr), mDontSerializeFieldAttribute(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;
 		mSystemGenericListClass = nullptr;
 		mSystemGenericDictionaryClass = nullptr;
+		mSystemTypeClass = nullptr;
 
 		mSerializeObjectAttribute = nullptr;
 		mDontSerializeFieldAttribute = nullptr;
@@ -478,6 +480,10 @@ namespace BansheeEngine
 		if(mSystemGenericDictionaryClass == nullptr)
 			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");
 		if(mSerializeObjectAttribute == nullptr)
 			BS_EXCEPT(InvalidStateException, "Cannot find SerializableObject managed class.");

+ 101 - 1
SBansheeEngine/Source/BsScriptScriptCode.cpp

@@ -5,6 +5,9 @@
 #include "BsMonoClass.h"
 #include "BsMonoManager.h"
 #include "BsMonoUtil.h"
+#include "BsScriptAssemblyManager.h"
+#include "BsManagedSerializableObjectInfo.h"
+#include <regex>
 
 using namespace std::placeholders;
 
@@ -13,7 +16,7 @@ namespace BansheeEngine
 	ScriptScriptCode::ScriptScriptCode(MonoObject* instance, const HScriptCode& scriptCode)
 		:ScriptObject(instance), mScriptCode(scriptCode)
 	{
-
+		
 	}
 
 	void ScriptScriptCode::initRuntimeData()
@@ -21,6 +24,7 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptScriptCode::internal_createInstance);
 		metaData.scriptClass->addInternalCall("Internal_GetText", &ScriptScriptCode::internal_getText);
 		metaData.scriptClass->addInternalCall("Internal_SetText", &ScriptScriptCode::internal_setText);
+		metaData.scriptClass->addInternalCall("Internal_GetTypes", &ScriptScriptCode::internal_getTypes);
 	}
 
 	void ScriptScriptCode::internal_createInstance(MonoObject* instance, MonoString* text)
@@ -45,6 +49,34 @@ namespace BansheeEngine
 
 		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()
 	{
@@ -58,4 +90,72 @@ namespace BansheeEngine
 	{
 		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
 
+Test:
+ - Component foldout X
+ - Drag and drop for components
+
  - 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
     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.
 
+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:
  - 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
@@ -72,7 +92,6 @@ Ribek use:
   - 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)
   - 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
  - (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)