Jelajahi Sumber

Delete/Duplicate/Cut/Copy/Paste/Rename shortcuts now work globally throughout the editor without the need to focus on a specific sub-editor, for a more intuitive experience

BearishSun 9 tahun lalu
induk
melakukan
22dacdc84f

+ 323 - 333
Source/BansheeEditor/Include/BsGUITreeView.h

@@ -1,334 +1,324 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#pragma once
-
-#include "BsEditorPrerequisites.h"
-#include "BsGUIElementContainer.h"
-#include "BsVirtualInput.h"
-#include "BsEvent.h"
-
-namespace BansheeEngine
-{
-	/** @cond INTERNAL */
-	/** @addtogroup GUI-Editor
-	 *  @{
-	 */
-
-	/**
-	 * GUI element that displays some contents in a tree-view where elements are placed vertically above/beneath each other,
-	 * and different elements may be nested within other elements.
-	 *
-	 * Elements may be selected, renamed, dragged and re-parented.
-	 *
-	 * This class is abstract and meant to be extended by an implementation specific to some content type (e.g. scene object
-	 * hierarchy). 
-	 */
-	class BS_ED_EXPORT GUITreeView : public GUIElementContainer
-	{
-	protected:
-		/**	Types of possible states when the tree view is auto-scrolling. */
-		enum class ScrollState
-		{
-			None,
-			Up,
-			Down,
-			TransitioningUp,
-			TransitioningDown
-		};
-
-		/**
-		 * Contains data about a single piece of content and all its children. This element may be visible and represented
-		 * by a GUI element, but might not (e.g. its parent is collapsed).
-		 */
-		struct TreeElement
-		{
-			TreeElement();
-			virtual ~TreeElement();
-
-			TreeElement* mParent;
-			Vector<TreeElement*> mChildren;
-
-			GUIToggle* mFoldoutBtn;
-			GUILabel* mElement;
-
-			String mName;
-
-			UINT32 mSortedIdx;
-			bool mIsExpanded;
-			bool mIsSelected;
-			bool mIsHighlighted;
-			bool mIsVisible;
-			bool mIsCut;
-			bool mIsDisabled;
-			Color mTint;
-
-			bool isParentRec(TreeElement* element) const;
-		};
-
-		/**
-		 * Contains data about all visible elements in the tree view. This might be a TreeElement entry, or a separator
-		 * (empty space) between two TreeElement%s.
-		 */
-		struct InteractableElement
-		{
-			InteractableElement(TreeElement* parent, UINT32 index, const Rect2I& bounds)
-				:parent(parent), index(index), bounds(bounds)
-			{ }
-
-			bool isTreeElement() const { return index % 2 == 1; }
-			TreeElement* getTreeElement() const;
-
-			TreeElement* parent;
-			UINT32 index;
-			Rect2I bounds;
-		};
-
-		/**	Contains data about one of the currently selected tree elements. */
-		struct SelectedElement
-		{
-			SelectedElement()
-				:element(nullptr), background(nullptr)
-			{ }
-
-			SelectedElement(TreeElement* elem, GUITexture* back)
-				:element(elem), background(back)
-			{ }
-
-			TreeElement* element;
-			GUITexture* background;
-		};
-
-	public:
-		/** Returns type name of the GUI element used for finding GUI element styles. */
-		static const String& getGUITypeName();
-
-		/** Deletes all currently selected elements. */
-		void deleteSelection();
-
-		/** Duplicates the currently selected entries. */
-		virtual void duplicateSelection() { }
-
-		/**	Marks the current selection for copying. */
-		virtual void copySelection() { }
-		
-		/**	Marks the current selection for cutting. */
-		virtual void cutSelection() { }
-
-		/**	Pastes a set of entries previously marked for cut or copy. */
-		virtual void paste() { }
-
-		/** @cond INTERNAL */
-
-		/** Updates tree view if dirty, among other operations. Must be called every frame. */
-		void _update();
-
-		/** @endcond */
-
-	protected:
-		GUITreeView(const String& backgroundStyle, const String& elementBtnStyle, 
-			const String& foldoutBtnStyle, const String& highlightBackgroundStyle, const String& selectionBackgroundStyle, const String& editBoxStyle,
-			const String& dragHighlightStyle, const String& dragSepHighlightStyle, const GUIDimensions& dimensions);
-
-		virtual ~GUITreeView();
-
-		/** @copydoc GUIElement::_getOptimalSize */
-		Vector2I _getOptimalSize() const override;
-
-		/** @copydoc GUIElement::updateClippedBounds */
-		void updateClippedBounds() override;
-
-		/** @copydoc GUIElement::_updateLayoutInternal */
-		virtual void _updateLayoutInternal(const GUILayoutData& data) override;
-
-		/** @copydoc GUIElement::_mouseEvent */
-		virtual bool _mouseEvent(const GUIMouseEvent& ev) override;
-
-		/** @copydoc GUIElement::_commandEvent */
-		virtual bool _commandEvent(const GUICommandEvent& ev) override;
-
-		/** @copydoc GUIElement::_virtualButtonEvent */
-		virtual bool _virtualButtonEvent(const GUIVirtualButtonEvent& ev) override;
-
-		/**
-		 * Attempts to find an interactable element under the specified coordinates. Returns null if one cannot be found.
-		 *
-		 * @param[in]	coord	Coordinates relative to parent GUI widget.
-		 */
-		const GUITreeView::InteractableElement* findElementUnderCoord(const Vector2I& coord) const;
-
-		/**	Returns the top-most selected tree element if selection is active, null otherwise. */
-		TreeElement* getTopMostSelectedElement() const;
-
-		/** Returns the bottom-most selected tree element if selection is active, null otherwise. */
-		TreeElement* getBottomMostSelectedElement() const;
-
-		/**	Starts rename operation on the specified tree element, spawning an input box. */
-		void enableEdit(TreeElement* element);
-
-		/**
-		 * Ends rename operation if one is currently active.
-		 *
-		 * @param[in]	acceptChanges	Should the new name be accepted or discarded.
-		 */
-		void disableEdit(bool acceptChanges);
-
-		/**
-		 * Triggered when the Foldout button for a tree element was been toggled (i.e. something was expanded or collapsed).
-		 */
-		void elementToggled(TreeElement* element, bool toggled);
-
-		/**	Returns the top level TreeElement. */
-		virtual TreeElement& getRootElement() = 0;
-
-		/**	Returns the top level TreeElement that cannot be modified. */
-		virtual const TreeElement& getRootElementConst() const = 0;
-
-		/** Checks if the hierarchy needs any updates and performs those updates if needed. */
-		virtual void updateTreeElementHierarchy() = 0;
-
-		/**	Changes the name of the content associated with the provided tree element. */
-		virtual void renameTreeElement(TreeElement* element, const WString& name) = 0;
-
-		/**	Deletes the content associated with the provided tree element. */
-		virtual void deleteTreeElement(TreeElement* element) = 0;
-
-		/**	Checks whether the tree view can accept the currently active drag and drop operation. */
-		virtual bool acceptDragAndDrop() const = 0;
-
-		/**	Triggered when the user drags a tree element and starts a drag and drop operation. */
-		virtual void dragAndDropStart() = 0;
-
-		/**
-		 * Triggered when the user ends a drag and drop operation over the tree view.
-		 *
-		 * @param[in]	overTreeElement	TreeElement the drag operation ended over, if any.
-		 */
-		virtual void dragAndDropEnded(TreeElement* overTreeElement) = 0;
-
-		/**	Triggered whenever a TreeElement gets selected or deselected. */
-		virtual void selectionChanged() { }
-
-		/**	Are any tree elements currently selected. */
-		bool isSelectionActive() const;
-
-		/**	Expands the selection to the provided TreeElement. Doesn't clear previous selection. */
-		void selectElement(TreeElement* element);
-
-		/**	Unselects the provided TreeElement. */
-		void unselectElement(TreeElement* element);
-
-		/**
-		 * Unselects all selected TreeElement%s.
-		 *
-		 * @param[in]	sendEvent	Determines should the external world be notified of this change.
-		 */
-		void unselectAll(bool sendEvent = true);
-
-		/**	Starts rename operation on the currently selected element. */
-		void renameSelected();
-
-		/**	Expands all parents of the provided TreeElement making it interactable. */
-		void expandToElement(TreeElement* element);
-
-		/**	Expands the provided TreeElement making its children interactable. */
-		void expandElement(TreeElement* element);
-
-		/**	Collapses the provided TreeElement making its children hidden and not interactable. */
-		void collapseElement(TreeElement* element);
-
-		/**	Rebuilds the needed GUI elements for the provided TreeElement. */
-		void updateElementGUI(TreeElement* element);
-
-		/**	Close any elements that were temporarily expanded due to a drag operation hovering over them. */
-		void closeTemporarilyExpandedElements();
-
-		/**
-		 * Temporarily expand the provided element. Temporarily expanded elements can be closed by calling
-		 * closeTemporarilyExpandedElements().
-		 */
-		void temporarilyExpandElement(const GUITreeView::InteractableElement* mouseOverElement);
-
-		/**
-		 * Scrolls the parent GUIScrollArea (if any) so that the specified TreeElement is visible.
-		 *
-		 * @param[in]	element	Element to scroll to.
-		 * @param[in]	center	If true the element will be centered in the scroll view, otherwise it will be at the top. 
-		 */
-		void scrollToElement(TreeElement* element, bool center);
-
-		/**	Attempts to find the parent GUIScrollArea that the tree view is a child of. */
-		GUIScrollArea* findParentScrollArea() const;
-
-		/**	Scrolls the tree view to the specified element and highlights it. */
-		void ping(TreeElement* element);
-
-		/**	Clears the ping highlight on the currently highlighted element. */
-		void clearPing();
-
-		/**	Triggered when the user accepts the changes during a rename operation. */
-		void onEditAccepted();
-
-		/**	Triggered when the user rejects the changes during a rename operation. */
-		void onEditCanceled();
-
-		/**	Triggered when the user clicks outside of the edit box during a rename operation. */
-		void onEditFocusLost();
-
-		String mBackgroundStyle;
-		String mElementBtnStyle;
-		String mFoldoutBtnStyle;
-		String mSelectionBackgroundStyle;
-		String mHighlightBackgroundStyle;
-		String mEditBoxStyle;
-		String mDragHighlightStyle;
-		String mDragSepHighlightStyle;
-
-		GUITexture* mBackgroundImage;
-
-		Vector<InteractableElement> mVisibleElements;
-
-		bool mIsElementSelected;
-		Vector<SelectedElement> mSelectedElements;
-
-		bool mIsElementHighlighted;
-		SelectedElement mHighlightedElement;
-
-		TreeElement* mEditElement;
-		GUITreeViewEditBox* mNameEditBox;
-
-		Vector2I mDragStartPosition;
-		Vector2I mDragPosition;
-		bool mDragInProgress;
-		GUITexture* mDragHighlight;
-		GUITexture* mDragSepHighlight;
-
-		Rect2I mTopScrollBounds;
-		Rect2I mBottomScrollBounds;
-		ScrollState mScrollState;
-		float mLastScrollTime;
-
-		Stack<TreeElement*> mAutoExpandedElements;
-		TreeElement* mMouseOverDragElement;
-		float mMouseOverDragElementTime;
-
-		static VirtualButton mRenameVB;
-		static VirtualButton mDeleteVB;
-		static VirtualButton mDuplicateVB;
-		static VirtualButton mCutVB;
-		static VirtualButton mCopyVB;
-		static VirtualButton mPasteVB;
-
-		static const UINT32 ELEMENT_EXTRA_SPACING;
-		static const UINT32 INDENT_SIZE;
-		static const UINT32 INITIAL_INDENT_OFFSET;
-		static const UINT32 DRAG_MIN_DISTANCE;
-		static const float AUTO_EXPAND_DELAY_SEC;
-		static const float SCROLL_AREA_HEIGHT_PCT;
-		static const UINT32 SCROLL_SPEED_PX_PER_SEC;
-		static const Color CUT_COLOR;
-		static const Color DISABLED_COLOR;
-	};
-
-	/** @} */
-	/** @endcond */
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsEditorPrerequisites.h"
+#include "BsGUIElementContainer.h"
+#include "BsVirtualInput.h"
+#include "BsEvent.h"
+
+namespace BansheeEngine
+{
+	/** @cond INTERNAL */
+	/** @addtogroup GUI-Editor
+	 *  @{
+	 */
+
+	/**
+	 * GUI element that displays some contents in a tree-view where elements are placed vertically above/beneath each other,
+	 * and different elements may be nested within other elements.
+	 *
+	 * Elements may be selected, renamed, dragged and re-parented.
+	 *
+	 * This class is abstract and meant to be extended by an implementation specific to some content type (e.g. scene object
+	 * hierarchy). 
+	 */
+	class BS_ED_EXPORT GUITreeView : public GUIElementContainer
+	{
+	protected:
+		/**	Types of possible states when the tree view is auto-scrolling. */
+		enum class ScrollState
+		{
+			None,
+			Up,
+			Down,
+			TransitioningUp,
+			TransitioningDown
+		};
+
+		/**
+		 * Contains data about a single piece of content and all its children. This element may be visible and represented
+		 * by a GUI element, but might not (e.g. its parent is collapsed).
+		 */
+		struct TreeElement
+		{
+			TreeElement();
+			virtual ~TreeElement();
+
+			TreeElement* mParent;
+			Vector<TreeElement*> mChildren;
+
+			GUIToggle* mFoldoutBtn;
+			GUILabel* mElement;
+
+			String mName;
+
+			UINT32 mSortedIdx;
+			bool mIsExpanded;
+			bool mIsSelected;
+			bool mIsHighlighted;
+			bool mIsVisible;
+			bool mIsCut;
+			bool mIsDisabled;
+			Color mTint;
+
+			bool isParentRec(TreeElement* element) const;
+		};
+
+		/**
+		 * Contains data about all visible elements in the tree view. This might be a TreeElement entry, or a separator
+		 * (empty space) between two TreeElement%s.
+		 */
+		struct InteractableElement
+		{
+			InteractableElement(TreeElement* parent, UINT32 index, const Rect2I& bounds)
+				:parent(parent), index(index), bounds(bounds)
+			{ }
+
+			bool isTreeElement() const { return index % 2 == 1; }
+			TreeElement* getTreeElement() const;
+
+			TreeElement* parent;
+			UINT32 index;
+			Rect2I bounds;
+		};
+
+		/**	Contains data about one of the currently selected tree elements. */
+		struct SelectedElement
+		{
+			SelectedElement()
+				:element(nullptr), background(nullptr)
+			{ }
+
+			SelectedElement(TreeElement* elem, GUITexture* back)
+				:element(elem), background(back)
+			{ }
+
+			TreeElement* element;
+			GUITexture* background;
+		};
+
+	public:
+		/** Returns type name of the GUI element used for finding GUI element styles. */
+		static const String& getGUITypeName();
+
+		/** Deletes all currently selected elements. */
+		void deleteSelection();
+
+		/** Duplicates the currently selected entries. */
+		virtual void duplicateSelection() { }
+
+		/**	Marks the current selection for copying. */
+		virtual void copySelection() { }
+		
+		/**	Marks the current selection for cutting. */
+		virtual void cutSelection() { }
+
+		/**	Pastes a set of entries previously marked for cut or copy. */
+		virtual void paste() { }
+
+		/**	Starts rename operation on the currently selected element. */
+		void renameSelected();
+
+		/** @cond INTERNAL */
+
+		/** Updates tree view if dirty, among other operations. Must be called every frame. */
+		void _update();
+
+		/** @endcond */
+
+	protected:
+		GUITreeView(const String& backgroundStyle, const String& elementBtnStyle, 
+			const String& foldoutBtnStyle, const String& highlightBackgroundStyle, const String& selectionBackgroundStyle, const String& editBoxStyle,
+			const String& dragHighlightStyle, const String& dragSepHighlightStyle, const GUIDimensions& dimensions);
+
+		virtual ~GUITreeView();
+
+		/** @copydoc GUIElement::_getOptimalSize */
+		Vector2I _getOptimalSize() const override;
+
+		/** @copydoc GUIElement::updateClippedBounds */
+		void updateClippedBounds() override;
+
+		/** @copydoc GUIElement::_updateLayoutInternal */
+		virtual void _updateLayoutInternal(const GUILayoutData& data) override;
+
+		/** @copydoc GUIElement::_mouseEvent */
+		virtual bool _mouseEvent(const GUIMouseEvent& ev) override;
+
+		/** @copydoc GUIElement::_commandEvent */
+		virtual bool _commandEvent(const GUICommandEvent& ev) override;
+
+		/**
+		 * Attempts to find an interactable element under the specified coordinates. Returns null if one cannot be found.
+		 *
+		 * @param[in]	coord	Coordinates relative to parent GUI widget.
+		 */
+		const GUITreeView::InteractableElement* findElementUnderCoord(const Vector2I& coord) const;
+
+		/**	Returns the top-most selected tree element if selection is active, null otherwise. */
+		TreeElement* getTopMostSelectedElement() const;
+
+		/** Returns the bottom-most selected tree element if selection is active, null otherwise. */
+		TreeElement* getBottomMostSelectedElement() const;
+
+		/**	Starts rename operation on the specified tree element, spawning an input box. */
+		void enableEdit(TreeElement* element);
+
+		/**
+		 * Ends rename operation if one is currently active.
+		 *
+		 * @param[in]	acceptChanges	Should the new name be accepted or discarded.
+		 */
+		void disableEdit(bool acceptChanges);
+
+		/**
+		 * Triggered when the Foldout button for a tree element was been toggled (i.e. something was expanded or collapsed).
+		 */
+		void elementToggled(TreeElement* element, bool toggled);
+
+		/**	Returns the top level TreeElement. */
+		virtual TreeElement& getRootElement() = 0;
+
+		/**	Returns the top level TreeElement that cannot be modified. */
+		virtual const TreeElement& getRootElementConst() const = 0;
+
+		/** Checks if the hierarchy needs any updates and performs those updates if needed. */
+		virtual void updateTreeElementHierarchy() = 0;
+
+		/**	Changes the name of the content associated with the provided tree element. */
+		virtual void renameTreeElement(TreeElement* element, const WString& name) = 0;
+
+		/**	Deletes the content associated with the provided tree element. */
+		virtual void deleteTreeElement(TreeElement* element) = 0;
+
+		/**	Checks whether the tree view can accept the currently active drag and drop operation. */
+		virtual bool acceptDragAndDrop() const = 0;
+
+		/**	Triggered when the user drags a tree element and starts a drag and drop operation. */
+		virtual void dragAndDropStart() = 0;
+
+		/**
+		 * Triggered when the user ends a drag and drop operation over the tree view.
+		 *
+		 * @param[in]	overTreeElement	TreeElement the drag operation ended over, if any.
+		 */
+		virtual void dragAndDropEnded(TreeElement* overTreeElement) = 0;
+
+		/**	Triggered whenever a TreeElement gets selected or deselected. */
+		virtual void selectionChanged() { }
+
+		/**	Are any tree elements currently selected. */
+		bool isSelectionActive() const;
+
+		/**	Expands the selection to the provided TreeElement. Doesn't clear previous selection. */
+		void selectElement(TreeElement* element);
+
+		/**	Unselects the provided TreeElement. */
+		void unselectElement(TreeElement* element);
+
+		/**
+		 * Unselects all selected TreeElement%s.
+		 *
+		 * @param[in]	sendEvent	Determines should the external world be notified of this change.
+		 */
+		void unselectAll(bool sendEvent = true);
+
+		/**	Expands all parents of the provided TreeElement making it interactable. */
+		void expandToElement(TreeElement* element);
+
+		/**	Expands the provided TreeElement making its children interactable. */
+		void expandElement(TreeElement* element);
+
+		/**	Collapses the provided TreeElement making its children hidden and not interactable. */
+		void collapseElement(TreeElement* element);
+
+		/**	Rebuilds the needed GUI elements for the provided TreeElement. */
+		void updateElementGUI(TreeElement* element);
+
+		/**	Close any elements that were temporarily expanded due to a drag operation hovering over them. */
+		void closeTemporarilyExpandedElements();
+
+		/**
+		 * Temporarily expand the provided element. Temporarily expanded elements can be closed by calling
+		 * closeTemporarilyExpandedElements().
+		 */
+		void temporarilyExpandElement(const GUITreeView::InteractableElement* mouseOverElement);
+
+		/**
+		 * Scrolls the parent GUIScrollArea (if any) so that the specified TreeElement is visible.
+		 *
+		 * @param[in]	element	Element to scroll to.
+		 * @param[in]	center	If true the element will be centered in the scroll view, otherwise it will be at the top. 
+		 */
+		void scrollToElement(TreeElement* element, bool center);
+
+		/**	Attempts to find the parent GUIScrollArea that the tree view is a child of. */
+		GUIScrollArea* findParentScrollArea() const;
+
+		/**	Scrolls the tree view to the specified element and highlights it. */
+		void ping(TreeElement* element);
+
+		/**	Clears the ping highlight on the currently highlighted element. */
+		void clearPing();
+
+		/**	Triggered when the user accepts the changes during a rename operation. */
+		void onEditAccepted();
+
+		/**	Triggered when the user rejects the changes during a rename operation. */
+		void onEditCanceled();
+
+		/**	Triggered when the user clicks outside of the edit box during a rename operation. */
+		void onEditFocusLost();
+
+		String mBackgroundStyle;
+		String mElementBtnStyle;
+		String mFoldoutBtnStyle;
+		String mSelectionBackgroundStyle;
+		String mHighlightBackgroundStyle;
+		String mEditBoxStyle;
+		String mDragHighlightStyle;
+		String mDragSepHighlightStyle;
+
+		GUITexture* mBackgroundImage;
+
+		Vector<InteractableElement> mVisibleElements;
+
+		bool mIsElementSelected;
+		Vector<SelectedElement> mSelectedElements;
+
+		bool mIsElementHighlighted;
+		SelectedElement mHighlightedElement;
+
+		TreeElement* mEditElement;
+		GUITreeViewEditBox* mNameEditBox;
+
+		Vector2I mDragStartPosition;
+		Vector2I mDragPosition;
+		bool mDragInProgress;
+		GUITexture* mDragHighlight;
+		GUITexture* mDragSepHighlight;
+
+		Rect2I mTopScrollBounds;
+		Rect2I mBottomScrollBounds;
+		ScrollState mScrollState;
+		float mLastScrollTime;
+
+		Stack<TreeElement*> mAutoExpandedElements;
+		TreeElement* mMouseOverDragElement;
+		float mMouseOverDragElementTime;
+
+		static const UINT32 ELEMENT_EXTRA_SPACING;
+		static const UINT32 INDENT_SIZE;
+		static const UINT32 INITIAL_INDENT_OFFSET;
+		static const UINT32 DRAG_MIN_DISTANCE;
+		static const float AUTO_EXPAND_DELAY_SEC;
+		static const float SCROLL_AREA_HEIGHT_PCT;
+		static const UINT32 SCROLL_SPEED_PX_PER_SEC;
+		static const Color CUT_COLOR;
+		static const Color DISABLED_COLOR;
+	};
+
+	/** @} */
+	/** @endcond */
 }

+ 403 - 408
Source/BansheeEditor/Source/BsEditorApplication.cpp

@@ -1,409 +1,404 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsEditorApplication.h"
-#include "BsEditorWindowManager.h"
-#include "BsEditorWidgetManager.h"
-#include "BsMainEditorWindow.h"
-#include "BsRenderWindow.h"
-#include "BsBuiltinEditorResources.h"
-#include "BsUndoRedo.h"
-#include "BsFileSerializer.h"
-#include "BsFileSystem.h"
-#include "BsEditorWidgetLayout.h"
-#include "BsScenePicking.h"
-#include "BsSelection.h"
-#include "BsGizmoManager.h"
-#include "BsCodeEditor.h"
-#include "BsBuildManager.h"
-#include "BsScriptCodeImporter.h"
-#include "BsEditorShaderIncludeHandler.h"
-#include "BsDropDownWindowManager.h"
-#include "BsProjectLibrary.h"
-#include "BsProjectSettings.h"
-#include "BsEditorSettings.h"
-#include "BsScriptManager.h"
-#include "BsImporter.h"
-#include "BsVirtualInput.h"
-#include "BsResources.h"
-#include "BsCoreSceneManager.h"
-#include "BsSplashScreen.h"
-#include "BsDynLib.h"
-#include "BsSceneManager.h"
-
-namespace BansheeEngine
-{
-	const Path EditorApplication::WIDGET_LAYOUT_PATH = PROJECT_INTERNAL_DIR + L"Layout.asset";
-	const Path EditorApplication::BUILD_DATA_PATH = PROJECT_INTERNAL_DIR + L"BuildData.asset";
-	const Path EditorApplication::PROJECT_SETTINGS_PATH = PROJECT_INTERNAL_DIR + L"Settings.asset";
-
-	RENDER_WINDOW_DESC createRenderWindowDesc()
-	{
-		RENDER_WINDOW_DESC renderWindowDesc;
-		renderWindowDesc.videoMode = VideoMode(1920, 1080);
-		renderWindowDesc.title = "BansheeEditor";
-		renderWindowDesc.fullscreen = false;
-		renderWindowDesc.border = WindowBorder::None;
-		renderWindowDesc.hideUntilSwap = true;
-
-		return renderWindowDesc;
-	}
-
-	Vector<String> getImporters()
-	{
-		Vector<String> importers;
-
-		importers.push_back("BansheeFreeImgImporter");
-		importers.push_back("BansheeFBXImporter");
-		importers.push_back("BansheeFontImporter");
-		importers.push_back("BansheeSL");
-
-		return importers;
-	}
-
-	Path getEditorSettingsPath()
-	{
-		return Paths::getRuntimeDataPath() + L"Settings.asset";
-	}
-
-	EditorApplication::EditorApplication(EditorRenderAPI renderAPIPlugin)
-		:Application(createRenderWindowDesc(), toEngineRenderAPI(renderAPIPlugin), RendererPlugin::Default, getImporters()),
-		mActiveRAPIPlugin(toEngineRenderAPI(renderAPIPlugin)), mSBansheeEditorPlugin(nullptr), mIsProjectLoaded(false)
-	{
-
-	}
-
-	EditorApplication::~EditorApplication()
-	{
-		ProjectLibrary::shutDown();
-		BuiltinEditorResources::shutDown();
-	}
-
-	void EditorApplication::onStartUp()
-	{
-		Application::onStartUp();
-		SplashScreen::show();
-
-		// In editor we render game on a separate surface, handled in Game window
-		SceneManager::instance().setMainRenderTarget(nullptr);
-
-		loadEditorSettings();
-		mProjectSettings = bs_shared_ptr_new<ProjectSettings>();
-
-		BuiltinEditorResources::startUp();
-
-		{
-			auto inputConfig = VirtualInput::instance().getConfiguration();
-
-			inputConfig->registerButton("Rename", BC_F2);
-			inputConfig->registerButton("Undo", BC_Z, ButtonModifier::Ctrl);
-			inputConfig->registerButton("Redo", BC_Y, ButtonModifier::Ctrl);
-			inputConfig->registerButton("Copy", BC_C, ButtonModifier::Ctrl);
-			inputConfig->registerButton("Cut", BC_X, ButtonModifier::Ctrl);
-			inputConfig->registerButton("Paste", BC_V, ButtonModifier::Ctrl);
-			inputConfig->registerButton("Duplicate", BC_D, ButtonModifier::Ctrl);
-			inputConfig->registerButton("Delete", BC_DELETE);
-		}
-
-		ScriptCodeImporter* scriptCodeImporter = bs_new<ScriptCodeImporter>();
-		Importer::instance()._registerAssetImporter(scriptCodeImporter);
-
-		ProjectLibrary::startUp();
-
-		UndoRedo::startUp();
-		EditorWindowManager::startUp();
-		EditorWidgetManager::startUp();
-		DropDownWindowManager::startUp();
-
-		ScenePicking::startUp();
-		Selection::startUp();
-		GizmoManager::startUp();
-		BuildManager::startUp();
-		CodeEditorManager::startUp();
-
-		MainEditorWindow* mainWindow = MainEditorWindow::create(getPrimaryWindow());
-		ScriptManager::instance().initialize();
-	}
-
-	void EditorApplication::onShutDown()
-	{
-		unloadProject();
-
-		CodeEditorManager::shutDown();
-		BuildManager::shutDown();
-		GizmoManager::shutDown();
-		Selection::shutDown();
-		ScenePicking::shutDown();
-
-		saveEditorSettings();
-
-		DropDownWindowManager::shutDown();
-		EditorWidgetManager::shutDown();
-		EditorWindowManager::shutDown();
-		UndoRedo::shutDown();
-
-		Application::onShutDown();
-	}
-
-	void EditorApplication::loadScriptSystem()
-	{
-		loadPlugin("BansheeMono", &mMonoPlugin);
-		loadPlugin("SBansheeEngine", &mSBansheeEnginePlugin);
-		loadPlugin("SBansheeEditor", &mSBansheeEditorPlugin);
-	}
-
-	void EditorApplication::startUp(EditorRenderAPI renderAPI)
-	{
-		CoreApplication::startUp<EditorApplication>(renderAPI);
-	}
-
-	void EditorApplication::preUpdate()
-	{
-		Application::preUpdate();
-
-		EditorWidgetManager::instance().update();
-		DropDownWindowManager::instance().update();
-	}
-
-	void EditorApplication::postUpdate()
-	{
-		// Call update on editor widgets before parent's postUpdate because the parent will render the GUI and we need
-		// to ensure editor widget's GUI is updated.
-		EditorWindowManager::instance().update();
-
-		Application::postUpdate();
-
-		SplashScreen::hide();
-		setFPSLimit(mEditorSettings->getFPSLimit());
-	}
-
-	void EditorApplication::quitRequested()
-	{
-		typedef void(*QuitRequestedFunc)();
-
-		QuitRequestedFunc quitRequestedCall = (QuitRequestedFunc)mSBansheeEditorPlugin->getSymbol("quitRequested");
-
-		if (quitRequestedCall != nullptr)
-			quitRequestedCall();
-	}
-
-	Path EditorApplication::getEditorAssemblyPath() const
-	{
-		Path assemblyPath = getBuiltinAssemblyFolder();
-		assemblyPath.append(toWString(EDITOR_ASSEMBLY) + L".dll");
-
-		return assemblyPath;
-	}
-
-	Path EditorApplication::getEditorScriptAssemblyPath() const
-	{
-		Path assemblyPath = getScriptAssemblyFolder();
-		assemblyPath.append(toWString(SCRIPT_EDITOR_ASSEMBLY) + L".dll");
-
-		return assemblyPath;
-	}
-
-	Path EditorApplication::getScriptAssemblyFolder() const
-	{
-		if (!isProjectLoaded())
-			return Path::BLANK;
-
-		Path assemblyFolder = getProjectPath();
-		assemblyFolder.append(INTERNAL_ASSEMBLY_PATH);
-
-		return assemblyFolder;
-	}
-
-	void EditorApplication::saveProject()
-	{
-		if (!isProjectLoaded())
-			return;
-
-		Path buildDataPath = getProjectPath();
-		buildDataPath.append(BUILD_DATA_PATH);
-
-		BuildManager::instance().save(buildDataPath);
-		saveWidgetLayout(EditorWidgetManager::instance().getLayout());
-		saveEditorSettings();
-		saveProjectSettings();
-
-		gProjectLibrary().saveLibrary();
-	}
-
-	void EditorApplication::unloadProject()
-	{
-		if (!isProjectLoaded())
-			return;
-
-		saveProject();
-
-		mProjectSettings = bs_shared_ptr_new<ProjectSettings>();
-		BuildManager::instance().clear();
-		UndoRedo::instance().clear();
-
-		EditorWidgetManager::instance().closeAll();
-		gProjectLibrary().unloadLibrary();
-		Resources::instance().unloadAllUnused();
-		gCoreSceneManager().clearScene();
-
-		mProjectPath = Path::BLANK;
-		mProjectName = StringUtil::WBLANK;
-		mIsProjectLoaded = false;
-	}
-
-	void EditorApplication::loadProject(const Path& projectPath)
-	{
-		unloadProject();
-
-		mProjectPath = projectPath;
-		mProjectName = projectPath.getWTail();
-		mIsProjectLoaded = true;
-
-		loadProjectSettings();
-
-		Path buildDataPath = getProjectPath();
-		buildDataPath.append(BUILD_DATA_PATH);
-
-		BuildManager::instance().load(buildDataPath);
-		gProjectLibrary().loadLibrary();
-
-		// Do this before restoring windows to ensure types are loaded
-		ScriptManager::instance().reload();
-		
-		EditorWidgetLayoutPtr layout = loadWidgetLayout();
-		if (layout != nullptr)
-			EditorWidgetManager::instance().setLayout(layout);
-	}
-
-	void EditorApplication::createProject(const Path& path)
-	{
-		Path resourceDir = Path::combine(path, ProjectLibrary::RESOURCES_DIR);
-		Path internalResourcesDir = Path::combine(path, ProjectLibrary::INTERNAL_RESOURCES_DIR);
-
-		if (!FileSystem::exists(resourceDir))
-			FileSystem::createDir(resourceDir);
-
-		if (!FileSystem::exists(internalResourcesDir))
-			FileSystem::createDir(internalResourcesDir);
-
-		Path defaultLayoutPath = FileSystem::getWorkingDirectoryPath();
-		defaultLayoutPath.append(BuiltinEditorResources::getDefaultWidgetLayoutPath());
-
-		if (FileSystem::exists(defaultLayoutPath))
-		{
-			Path projectLayoutPath = Path::combine(path, WIDGET_LAYOUT_PATH);
-			FileSystem::copy(defaultLayoutPath, projectLayoutPath, false);
-		}
-	}
-
-	bool EditorApplication::isValidProjectPath(const Path& path)
-	{
-		if (!path.isAbsolute())
-			return false;
-
-		if (!FileSystem::isDirectory(path))
-			return false;
-
-		return true;
-	}
-
-	EditorWidgetLayoutPtr EditorApplication::loadWidgetLayout()
-	{
-		Path layoutPath = getProjectPath();
-		layoutPath.append(WIDGET_LAYOUT_PATH);
-
-		if(FileSystem::exists(layoutPath))
-		{
-			FileDecoder fs(layoutPath);
-			return std::static_pointer_cast<EditorWidgetLayout>(fs.decode());
-		}
-
-		return nullptr;
-	}
-
-	void EditorApplication::saveWidgetLayout(const EditorWidgetLayoutPtr& layout)
-	{
-		Path layoutPath = getProjectPath();
-		layoutPath.append(WIDGET_LAYOUT_PATH);
-
-		FileEncoder fs(layoutPath);
-		fs.encode(layout.get());
-	}
-
-	void EditorApplication::loadEditorSettings()
-	{
-		Path absoluteDataPath = FileSystem::getWorkingDirectoryPath();
-		absoluteDataPath.append(getEditorSettingsPath());
-
-		if (FileSystem::exists(absoluteDataPath))
-		{
-			FileDecoder fs(absoluteDataPath);
-			mEditorSettings = std::static_pointer_cast<EditorSettings>(fs.decode());
-		}
-		
-		if (mEditorSettings == nullptr)
-			mEditorSettings = bs_shared_ptr_new<EditorSettings>();
-	}
-
-	void EditorApplication::saveEditorSettings()
-	{
-		if (mEditorSettings == nullptr)
-			return;
-
-		Path absoluteDataPath = FileSystem::getWorkingDirectoryPath();
-		absoluteDataPath.append(getEditorSettingsPath());
-
-		FileEncoder fs(absoluteDataPath);
-		fs.encode(mEditorSettings.get());
-	}
-
-	void EditorApplication::loadProjectSettings()
-	{
-		if (isProjectLoaded())
-		{
-			Path absoluteDataPath = getProjectPath();
-			absoluteDataPath.append(PROJECT_SETTINGS_PATH);
-
-			if (FileSystem::exists(absoluteDataPath))
-			{
-				FileDecoder fs(absoluteDataPath);
-				mProjectSettings = std::static_pointer_cast<ProjectSettings>(fs.decode());
-			}
-		}
-
-		if (mProjectSettings == nullptr)
-			mProjectSettings = bs_shared_ptr_new<ProjectSettings>();
-	}
-
-	void EditorApplication::saveProjectSettings()
-	{
-		if (mProjectSettings == nullptr || !isProjectLoaded())
-			return;
-
-		Path absoluteDataPath = getProjectPath();
-		absoluteDataPath.append(PROJECT_SETTINGS_PATH);
-
-		FileEncoder fs(absoluteDataPath);
-		fs.encode(mProjectSettings.get());
-	}
-
-	ShaderIncludeHandlerPtr EditorApplication::getShaderIncludeHandler() const
-	{
-		return bs_shared_ptr_new<EditorShaderIncludeHandler>();
-	}
-
-	RenderAPIPlugin EditorApplication::toEngineRenderAPI(EditorRenderAPI renderAPI)
-	{
-		if (renderAPI == EditorRenderAPI::DX11)
-			return RenderAPIPlugin::DX11;
-		else if (renderAPI == EditorRenderAPI::OpenGL)
-			return RenderAPIPlugin::OpenGL;
-
-		BS_EXCEPT(InvalidStateException, "Unsupported render API.");
-		return RenderAPIPlugin::DX11;
-	}
-
-	EditorApplication& gEditorApplication()
-	{
-		return static_cast<EditorApplication&>(EditorApplication::instance());
-	}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsEditorApplication.h"
+#include "BsEditorWindowManager.h"
+#include "BsEditorWidgetManager.h"
+#include "BsMainEditorWindow.h"
+#include "BsRenderWindow.h"
+#include "BsBuiltinEditorResources.h"
+#include "BsUndoRedo.h"
+#include "BsFileSerializer.h"
+#include "BsFileSystem.h"
+#include "BsEditorWidgetLayout.h"
+#include "BsScenePicking.h"
+#include "BsSelection.h"
+#include "BsGizmoManager.h"
+#include "BsCodeEditor.h"
+#include "BsBuildManager.h"
+#include "BsScriptCodeImporter.h"
+#include "BsEditorShaderIncludeHandler.h"
+#include "BsDropDownWindowManager.h"
+#include "BsProjectLibrary.h"
+#include "BsProjectSettings.h"
+#include "BsEditorSettings.h"
+#include "BsScriptManager.h"
+#include "BsImporter.h"
+#include "BsVirtualInput.h"
+#include "BsResources.h"
+#include "BsCoreSceneManager.h"
+#include "BsSplashScreen.h"
+#include "BsDynLib.h"
+#include "BsSceneManager.h"
+
+namespace BansheeEngine
+{
+	const Path EditorApplication::WIDGET_LAYOUT_PATH = PROJECT_INTERNAL_DIR + L"Layout.asset";
+	const Path EditorApplication::BUILD_DATA_PATH = PROJECT_INTERNAL_DIR + L"BuildData.asset";
+	const Path EditorApplication::PROJECT_SETTINGS_PATH = PROJECT_INTERNAL_DIR + L"Settings.asset";
+
+	RENDER_WINDOW_DESC createRenderWindowDesc()
+	{
+		RENDER_WINDOW_DESC renderWindowDesc;
+		renderWindowDesc.videoMode = VideoMode(1920, 1080);
+		renderWindowDesc.title = "BansheeEditor";
+		renderWindowDesc.fullscreen = false;
+		renderWindowDesc.border = WindowBorder::None;
+		renderWindowDesc.hideUntilSwap = true;
+
+		return renderWindowDesc;
+	}
+
+	Vector<String> getImporters()
+	{
+		Vector<String> importers;
+
+		importers.push_back("BansheeFreeImgImporter");
+		importers.push_back("BansheeFBXImporter");
+		importers.push_back("BansheeFontImporter");
+		importers.push_back("BansheeSL");
+
+		return importers;
+	}
+
+	Path getEditorSettingsPath()
+	{
+		return Paths::getRuntimeDataPath() + L"Settings.asset";
+	}
+
+	EditorApplication::EditorApplication(EditorRenderAPI renderAPIPlugin)
+		:Application(createRenderWindowDesc(), toEngineRenderAPI(renderAPIPlugin), RendererPlugin::Default, getImporters()),
+		mActiveRAPIPlugin(toEngineRenderAPI(renderAPIPlugin)), mSBansheeEditorPlugin(nullptr), mIsProjectLoaded(false)
+	{
+
+	}
+
+	EditorApplication::~EditorApplication()
+	{
+		ProjectLibrary::shutDown();
+		BuiltinEditorResources::shutDown();
+	}
+
+	void EditorApplication::onStartUp()
+	{
+		Application::onStartUp();
+		SplashScreen::show();
+
+		// In editor we render game on a separate surface, handled in Game window
+		SceneManager::instance().setMainRenderTarget(nullptr);
+
+		loadEditorSettings();
+		mProjectSettings = bs_shared_ptr_new<ProjectSettings>();
+
+		BuiltinEditorResources::startUp();
+
+		{
+			auto inputConfig = VirtualInput::instance().getConfiguration();
+
+			inputConfig->registerButton("Copy", BC_C, ButtonModifier::Ctrl);
+			inputConfig->registerButton("Cut", BC_X, ButtonModifier::Ctrl);
+			inputConfig->registerButton("Paste", BC_V, ButtonModifier::Ctrl);
+		}
+
+		ScriptCodeImporter* scriptCodeImporter = bs_new<ScriptCodeImporter>();
+		Importer::instance()._registerAssetImporter(scriptCodeImporter);
+
+		ProjectLibrary::startUp();
+
+		UndoRedo::startUp();
+		EditorWindowManager::startUp();
+		EditorWidgetManager::startUp();
+		DropDownWindowManager::startUp();
+
+		ScenePicking::startUp();
+		Selection::startUp();
+		GizmoManager::startUp();
+		BuildManager::startUp();
+		CodeEditorManager::startUp();
+
+		MainEditorWindow* mainWindow = MainEditorWindow::create(getPrimaryWindow());
+		ScriptManager::instance().initialize();
+	}
+
+	void EditorApplication::onShutDown()
+	{
+		unloadProject();
+
+		CodeEditorManager::shutDown();
+		BuildManager::shutDown();
+		GizmoManager::shutDown();
+		Selection::shutDown();
+		ScenePicking::shutDown();
+
+		saveEditorSettings();
+
+		DropDownWindowManager::shutDown();
+		EditorWidgetManager::shutDown();
+		EditorWindowManager::shutDown();
+		UndoRedo::shutDown();
+
+		Application::onShutDown();
+	}
+
+	void EditorApplication::loadScriptSystem()
+	{
+		loadPlugin("BansheeMono", &mMonoPlugin);
+		loadPlugin("SBansheeEngine", &mSBansheeEnginePlugin);
+		loadPlugin("SBansheeEditor", &mSBansheeEditorPlugin);
+	}
+
+	void EditorApplication::startUp(EditorRenderAPI renderAPI)
+	{
+		CoreApplication::startUp<EditorApplication>(renderAPI);
+	}
+
+	void EditorApplication::preUpdate()
+	{
+		Application::preUpdate();
+
+		EditorWidgetManager::instance().update();
+		DropDownWindowManager::instance().update();
+	}
+
+	void EditorApplication::postUpdate()
+	{
+		// Call update on editor widgets before parent's postUpdate because the parent will render the GUI and we need
+		// to ensure editor widget's GUI is updated.
+		EditorWindowManager::instance().update();
+
+		Application::postUpdate();
+
+		SplashScreen::hide();
+		setFPSLimit(mEditorSettings->getFPSLimit());
+	}
+
+	void EditorApplication::quitRequested()
+	{
+		typedef void(*QuitRequestedFunc)();
+
+		QuitRequestedFunc quitRequestedCall = (QuitRequestedFunc)mSBansheeEditorPlugin->getSymbol("quitRequested");
+
+		if (quitRequestedCall != nullptr)
+			quitRequestedCall();
+	}
+
+	Path EditorApplication::getEditorAssemblyPath() const
+	{
+		Path assemblyPath = getBuiltinAssemblyFolder();
+		assemblyPath.append(toWString(EDITOR_ASSEMBLY) + L".dll");
+
+		return assemblyPath;
+	}
+
+	Path EditorApplication::getEditorScriptAssemblyPath() const
+	{
+		Path assemblyPath = getScriptAssemblyFolder();
+		assemblyPath.append(toWString(SCRIPT_EDITOR_ASSEMBLY) + L".dll");
+
+		return assemblyPath;
+	}
+
+	Path EditorApplication::getScriptAssemblyFolder() const
+	{
+		if (!isProjectLoaded())
+			return Path::BLANK;
+
+		Path assemblyFolder = getProjectPath();
+		assemblyFolder.append(INTERNAL_ASSEMBLY_PATH);
+
+		return assemblyFolder;
+	}
+
+	void EditorApplication::saveProject()
+	{
+		if (!isProjectLoaded())
+			return;
+
+		Path buildDataPath = getProjectPath();
+		buildDataPath.append(BUILD_DATA_PATH);
+
+		BuildManager::instance().save(buildDataPath);
+		saveWidgetLayout(EditorWidgetManager::instance().getLayout());
+		saveEditorSettings();
+		saveProjectSettings();
+
+		gProjectLibrary().saveLibrary();
+	}
+
+	void EditorApplication::unloadProject()
+	{
+		if (!isProjectLoaded())
+			return;
+
+		saveProject();
+
+		mProjectSettings = bs_shared_ptr_new<ProjectSettings>();
+		BuildManager::instance().clear();
+		UndoRedo::instance().clear();
+
+		EditorWidgetManager::instance().closeAll();
+		gProjectLibrary().unloadLibrary();
+		Resources::instance().unloadAllUnused();
+		gCoreSceneManager().clearScene();
+
+		mProjectPath = Path::BLANK;
+		mProjectName = StringUtil::WBLANK;
+		mIsProjectLoaded = false;
+	}
+
+	void EditorApplication::loadProject(const Path& projectPath)
+	{
+		unloadProject();
+
+		mProjectPath = projectPath;
+		mProjectName = projectPath.getWTail();
+		mIsProjectLoaded = true;
+
+		loadProjectSettings();
+
+		Path buildDataPath = getProjectPath();
+		buildDataPath.append(BUILD_DATA_PATH);
+
+		BuildManager::instance().load(buildDataPath);
+		gProjectLibrary().loadLibrary();
+
+		// Do this before restoring windows to ensure types are loaded
+		ScriptManager::instance().reload();
+		
+		EditorWidgetLayoutPtr layout = loadWidgetLayout();
+		if (layout != nullptr)
+			EditorWidgetManager::instance().setLayout(layout);
+	}
+
+	void EditorApplication::createProject(const Path& path)
+	{
+		Path resourceDir = Path::combine(path, ProjectLibrary::RESOURCES_DIR);
+		Path internalResourcesDir = Path::combine(path, ProjectLibrary::INTERNAL_RESOURCES_DIR);
+
+		if (!FileSystem::exists(resourceDir))
+			FileSystem::createDir(resourceDir);
+
+		if (!FileSystem::exists(internalResourcesDir))
+			FileSystem::createDir(internalResourcesDir);
+
+		Path defaultLayoutPath = FileSystem::getWorkingDirectoryPath();
+		defaultLayoutPath.append(BuiltinEditorResources::getDefaultWidgetLayoutPath());
+
+		if (FileSystem::exists(defaultLayoutPath))
+		{
+			Path projectLayoutPath = Path::combine(path, WIDGET_LAYOUT_PATH);
+			FileSystem::copy(defaultLayoutPath, projectLayoutPath, false);
+		}
+	}
+
+	bool EditorApplication::isValidProjectPath(const Path& path)
+	{
+		if (!path.isAbsolute())
+			return false;
+
+		if (!FileSystem::isDirectory(path))
+			return false;
+
+		return true;
+	}
+
+	EditorWidgetLayoutPtr EditorApplication::loadWidgetLayout()
+	{
+		Path layoutPath = getProjectPath();
+		layoutPath.append(WIDGET_LAYOUT_PATH);
+
+		if(FileSystem::exists(layoutPath))
+		{
+			FileDecoder fs(layoutPath);
+			return std::static_pointer_cast<EditorWidgetLayout>(fs.decode());
+		}
+
+		return nullptr;
+	}
+
+	void EditorApplication::saveWidgetLayout(const EditorWidgetLayoutPtr& layout)
+	{
+		Path layoutPath = getProjectPath();
+		layoutPath.append(WIDGET_LAYOUT_PATH);
+
+		FileEncoder fs(layoutPath);
+		fs.encode(layout.get());
+	}
+
+	void EditorApplication::loadEditorSettings()
+	{
+		Path absoluteDataPath = FileSystem::getWorkingDirectoryPath();
+		absoluteDataPath.append(getEditorSettingsPath());
+
+		if (FileSystem::exists(absoluteDataPath))
+		{
+			FileDecoder fs(absoluteDataPath);
+			mEditorSettings = std::static_pointer_cast<EditorSettings>(fs.decode());
+		}
+		
+		if (mEditorSettings == nullptr)
+			mEditorSettings = bs_shared_ptr_new<EditorSettings>();
+	}
+
+	void EditorApplication::saveEditorSettings()
+	{
+		if (mEditorSettings == nullptr)
+			return;
+
+		Path absoluteDataPath = FileSystem::getWorkingDirectoryPath();
+		absoluteDataPath.append(getEditorSettingsPath());
+
+		FileEncoder fs(absoluteDataPath);
+		fs.encode(mEditorSettings.get());
+	}
+
+	void EditorApplication::loadProjectSettings()
+	{
+		if (isProjectLoaded())
+		{
+			Path absoluteDataPath = getProjectPath();
+			absoluteDataPath.append(PROJECT_SETTINGS_PATH);
+
+			if (FileSystem::exists(absoluteDataPath))
+			{
+				FileDecoder fs(absoluteDataPath);
+				mProjectSettings = std::static_pointer_cast<ProjectSettings>(fs.decode());
+			}
+		}
+
+		if (mProjectSettings == nullptr)
+			mProjectSettings = bs_shared_ptr_new<ProjectSettings>();
+	}
+
+	void EditorApplication::saveProjectSettings()
+	{
+		if (mProjectSettings == nullptr || !isProjectLoaded())
+			return;
+
+		Path absoluteDataPath = getProjectPath();
+		absoluteDataPath.append(PROJECT_SETTINGS_PATH);
+
+		FileEncoder fs(absoluteDataPath);
+		fs.encode(mProjectSettings.get());
+	}
+
+	ShaderIncludeHandlerPtr EditorApplication::getShaderIncludeHandler() const
+	{
+		return bs_shared_ptr_new<EditorShaderIncludeHandler>();
+	}
+
+	RenderAPIPlugin EditorApplication::toEngineRenderAPI(EditorRenderAPI renderAPI)
+	{
+		if (renderAPI == EditorRenderAPI::DX11)
+			return RenderAPIPlugin::DX11;
+		else if (renderAPI == EditorRenderAPI::OpenGL)
+			return RenderAPIPlugin::OpenGL;
+
+		BS_EXCEPT(InvalidStateException, "Unsupported render API.");
+		return RenderAPIPlugin::DX11;
+	}
+
+	EditorApplication& gEditorApplication()
+	{
+		return static_cast<EditorApplication&>(EditorApplication::instance());
+	}
 }

+ 1345 - 1388
Source/BansheeEditor/Source/BsGUITreeView.cpp

@@ -1,1389 +1,1346 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsGUITreeView.h"
-#include "BsGUILayout.h"
-#include "BsGUITexture.h"
-#include "BsGUIButton.h"
-#include "BsGUILabel.h"
-#include "BsGUISpace.h"
-#include "BsCGUIWidget.h"
-#include "BsGUIToggle.h"
-#include "BsGUITreeViewEditBox.h"
-#include "BsGUIMouseEvent.h"
-#include "BsGUISkin.h"
-#include "BsGUICommandEvent.h"
-#include "BsGUIVirtualButtonEvent.h"
-#include "BsGUIScrollArea.h"
-#include "BsDragAndDropManager.h"
-#include "BsTime.h"
-
-using namespace std::placeholders;
-
-namespace BansheeEngine
-{
-	const UINT32 GUITreeView::ELEMENT_EXTRA_SPACING = 3;
-	const UINT32 GUITreeView::INDENT_SIZE = 10;
-	const UINT32 GUITreeView::INITIAL_INDENT_OFFSET = 16;
-	const UINT32 GUITreeView::DRAG_MIN_DISTANCE = 3;
-	const float GUITreeView::AUTO_EXPAND_DELAY_SEC = 0.5f;
-	const float GUITreeView::SCROLL_AREA_HEIGHT_PCT = 0.1f;
-	const UINT32 GUITreeView::SCROLL_SPEED_PX_PER_SEC = 100;
-	const Color GUITreeView::CUT_COLOR = Color(1.0f, 1.0f, 1.0f, 0.3f);
-	const Color GUITreeView::DISABLED_COLOR = Color(1.0f, 1.0f, 1.0f, 0.6f);
-
-	VirtualButton GUITreeView::mRenameVB = VirtualButton("Rename");
-	VirtualButton GUITreeView::mDeleteVB = VirtualButton("Delete");
-	VirtualButton GUITreeView::mDuplicateVB = VirtualButton("Duplicate");
-	VirtualButton GUITreeView::mCutVB = VirtualButton("Cut");
-	VirtualButton GUITreeView::mCopyVB = VirtualButton("Copy");
-	VirtualButton GUITreeView::mPasteVB = VirtualButton("Paste");
-
-	GUITreeView::TreeElement::TreeElement()
-		:mParent(nullptr), mFoldoutBtn(nullptr), mElement(nullptr), mIsSelected(false),
-		mIsExpanded(false), mSortedIdx(0), mIsVisible(true), mIsHighlighted(false), mIsCut(false), mIsDisabled(false)
-	{ }
-
-	GUITreeView::TreeElement::~TreeElement()
-	{
-		for(auto& child : mChildren)
-			bs_delete(child);
-
-		if(mFoldoutBtn != nullptr)
-			GUIElement::destroy(mFoldoutBtn);
-
-		if(mElement != nullptr)
-			GUIElement::destroy(mElement);
-
-		mChildren.clear();
-	}
-
-	bool GUITreeView::TreeElement::isParentRec(TreeElement* element) const
-	{
-		TreeElement* curParent = mParent;
-		while(curParent != nullptr)
-		{
-			if(curParent == element)
-				return true;
-
-			curParent = curParent->mParent;
-		}
-
-		return false;
-	}
-
-	GUITreeView::TreeElement* GUITreeView::InteractableElement::getTreeElement() const
-	{
-		if(!isTreeElement())
-			return nullptr;
-
-		UINT32 sortedIdx = (index - 1) / 2;
-
-		auto findIter = std::find_if(parent->mChildren.begin(), parent->mChildren.end(),
-			[&](const TreeElement* x) { return x->mSortedIdx == sortedIdx; });
-
-		if(findIter != parent->mChildren.end())
-			return *findIter;
-
-		return nullptr;
-	}
-
-	GUITreeView::GUITreeView(const String& backgroundStyle, const String& elementBtnStyle, 
-		const String& foldoutBtnStyle, const String& selectionBackgroundStyle, const String& highlightBackgroundStyle, 
-		const String& editBoxStyle, const String& dragHighlightStyle, const String& dragSepHighlightStyle, const GUIDimensions& dimensions)
-		:GUIElementContainer(dimensions), mBackgroundStyle(backgroundStyle),
-		mElementBtnStyle(elementBtnStyle), mFoldoutBtnStyle(foldoutBtnStyle), mEditBoxStyle(editBoxStyle), mEditElement(nullptr), mIsElementSelected(false),
-		mNameEditBox(nullptr), mHighlightBackgroundStyle(highlightBackgroundStyle), mSelectionBackgroundStyle(selectionBackgroundStyle), mDragInProgress(nullptr), 
-		mDragHighlightStyle(dragHighlightStyle), mDragSepHighlightStyle(dragSepHighlightStyle), mDragHighlight(nullptr), mDragSepHighlight(nullptr), mMouseOverDragElement(nullptr), 
-		mMouseOverDragElementTime(0.0f), mScrollState(ScrollState::None), mLastScrollTime(0.0f), mIsElementHighlighted(false)
-	{
-		if(mBackgroundStyle == StringUtil::BLANK)
-			mBackgroundStyle = "TreeViewBackground";
-
-		if(mElementBtnStyle == StringUtil::BLANK)
-			mElementBtnStyle = "TreeViewElementBtn";
-
-		if(mFoldoutBtnStyle == StringUtil::BLANK)
-			mFoldoutBtnStyle = "TreeViewFoldoutBtn";
-
-		if(mSelectionBackgroundStyle == StringUtil::BLANK)
-			mSelectionBackgroundStyle = "TreeViewSelectionBackground";
-
-		if (mHighlightBackgroundStyle == StringUtil::BLANK)
-			mHighlightBackgroundStyle = "TreeViewHighlightBackground";
-
-		if(mEditBoxStyle == StringUtil::BLANK)
-			mEditBoxStyle = "TreeViewEditBox";
-
-		if(mDragHighlightStyle == StringUtil::BLANK)
-			mDragHighlightStyle = "TreeViewElementHighlight";
-
-		if(mDragSepHighlightStyle == StringUtil::BLANK)
-			mDragSepHighlightStyle = "TreeViewElementSepHighlight";
-
-		mBackgroundImage = GUITexture::create(mBackgroundStyle);
-		mNameEditBox = GUITreeViewEditBox::create(mEditBoxStyle);
-		mNameEditBox->setVisible(false);
-
-		mNameEditBox->onInputConfirmed.connect(std::bind(&GUITreeView::onEditAccepted, this));
-		mNameEditBox->onInputCanceled.connect(std::bind(&GUITreeView::onEditCanceled, this));
-		mNameEditBox->onFocusLost.connect(std::bind(&GUITreeView::onEditFocusLost, this));
-
-		mDragHighlight = GUITexture::create(mDragHighlightStyle);
-		mDragSepHighlight = GUITexture::create(mDragSepHighlightStyle);
-
-		mDragHighlight->setVisible(false);
-		mDragSepHighlight->setVisible(false);
-
-		mDragHighlight->_setElementDepth(2);
-		mDragSepHighlight->_setElementDepth(2);
-
-		_registerChildElement(mBackgroundImage);
-		_registerChildElement(mNameEditBox);
-		_registerChildElement(mDragHighlight);
-		_registerChildElement(mDragSepHighlight);
-	}
-
-	GUITreeView::~GUITreeView()
-	{
-
-	}
-
-	void GUITreeView::_update()
-	{
-		// Attempt to auto-expand elements we are dragging over
-		if(acceptDragAndDrop())
-		{
-			const GUITreeView::InteractableElement* element = findElementUnderCoord(mDragPosition);
-			temporarilyExpandElement(element);
-		}
-
-		// NOTE - Instead of iterating through every visible element and comparing it with internal values,
-		// I might just want to add callbacks to SceneManager that notify me of any changes and then only perform
-		// update if anything is actually dirty
-
-		updateTreeElementHierarchy();
-
-		// Attempt to scroll if needed
-		if(mScrollState != ScrollState::None)
-		{
-			GUIScrollArea* scrollArea = findParentScrollArea();
-			if(scrollArea != nullptr)
-			{
-				float curTime = gTime().getTime();
-				float timeDiff = curTime - mLastScrollTime;
-				float secondsPerPixel = 1.0f / SCROLL_SPEED_PX_PER_SEC;
-
-				switch(mScrollState)
-				{
-				case ScrollState::TransitioningUp:
-					mScrollState = ScrollState::Up;
-					mLastScrollTime = curTime;
-					break;
-				case ScrollState::TransitioningDown:
-					mScrollState = ScrollState::Down;
-					mLastScrollTime = curTime;
-					break;
-				case ScrollState::Up:
-					{
-						UINT32 scrollAmount = (UINT32)Math::floorToInt(timeDiff / secondsPerPixel);
-						mLastScrollTime += scrollAmount * secondsPerPixel;
-
-						scrollArea->scrollUpPx(scrollAmount);
-					}
-					break;
-				case ScrollState::Down:
-					{
-						UINT32 scrollAmount = (UINT32)Math::floorToInt(timeDiff / secondsPerPixel);
-						mLastScrollTime += scrollAmount * secondsPerPixel;
-
-						scrollArea->scrollDownPx(scrollAmount);
-					}
-					break;
-				}
-			}
-		}
-	}
-
-	bool GUITreeView::_mouseEvent(const GUIMouseEvent& event)
-	{
-		if(event.getType() == GUIMouseEventType::MouseUp)
-		{
-			if(DragAndDropManager::instance().isDragInProgress())
-				return false;
-
-			const GUITreeView::InteractableElement* element = findElementUnderCoord(event.getPosition());
-			TreeElement* treeElement = nullptr;
-
-			if(element != nullptr && element->isTreeElement())
-			{
-				treeElement = element->getTreeElement();
-			}
-
-			if (treeElement != nullptr)
-			{
-				bool onFoldout = false;
-				if (treeElement->mFoldoutBtn != nullptr)
-					onFoldout = treeElement->mFoldoutBtn->_getClippedBounds().contains(event.getPosition());
-
-				bool onEditElement = false;
-				if (mEditElement != nullptr)
-					onEditElement = treeElement == mEditElement;
-
-				if (!onFoldout && !onEditElement)
-				{
-					if (event.getButton() == GUIMouseButton::Left)
-					{
-						if (event.isCtrlDown())
-						{
-							selectElement(treeElement);
-						}
-						else if (event.isShiftDown())
-						{
-							if (isSelectionActive())
-							{
-								TreeElement* selectionRoot = mSelectedElements[0].element;
-								unselectAll();
-
-								auto iterStartFind = std::find_if(mVisibleElements.begin(), mVisibleElements.end(),
-									[&](const InteractableElement& x) { return x.parent == selectionRoot->mParent; });
-
-								bool foundStart = false;
-								bool foundEnd = false;
-								for (; iterStartFind != mVisibleElements.end(); ++iterStartFind)
-								{
-									if (!iterStartFind->isTreeElement())
-										continue;
-
-									TreeElement* curElem = iterStartFind->getTreeElement();
-									if (curElem == selectionRoot)
-									{
-										foundStart = true;
-										break;
-									}
-								}
-
-								auto iterEndFind = std::find_if(mVisibleElements.begin(), mVisibleElements.end(),
-									[&](const InteractableElement& x) { return &x == element; });
-
-								if (iterEndFind != mVisibleElements.end())
-									foundEnd = true;
-
-								if (foundStart && foundEnd)
-								{
-									if (iterStartFind < iterEndFind)
-									{
-										for (; iterStartFind != (iterEndFind + 1); ++iterStartFind)
-										{
-											if (iterStartFind->isTreeElement())
-												selectElement(iterStartFind->getTreeElement());
-										}
-									}
-									else if (iterEndFind < iterStartFind)
-									{
-										for (; iterEndFind != (iterStartFind + 1); ++iterEndFind)
-										{
-											if (iterEndFind->isTreeElement())
-												selectElement(iterEndFind->getTreeElement());
-										}
-									}
-									else
-										selectElement(treeElement);
-								}
-
-								if (!foundStart || !foundEnd)
-									selectElement(treeElement);
-							}
-							else
-							{
-								selectElement(treeElement);
-							}
-						}
-						else
-						{
-							bool doRename = false;
-							if (isSelectionActive())
-							{
-								for (auto& selectedElem : mSelectedElements)
-								{
-									if (selectedElem.element == treeElement)
-									{
-										doRename = true;
-										break;
-									}
-								}
-							}
-
-							unselectAll();
-							selectElement(treeElement);
-
-							if (doRename)
-								renameSelected();
-						}
-
-						_markLayoutAsDirty();
-
-						return true;
-					}
-					else if (event.getButton() == GUIMouseButton::Right)
-					{
-						unselectAll();
-						selectElement(treeElement);
-						_markLayoutAsDirty();
-
-						return true;
-					}
-				}
-			}
-			else
-			{
-				unselectAll();
-
-				return true;
-			}
-		}
-		else if(event.getType() == GUIMouseEventType::MouseDragStart)
-		{
-			clearPing();
-
-			mDragStartPosition = event.getPosition();
-		}
-		else if(event.getType() == GUIMouseEventType::MouseDrag)
-		{
-			UINT32 dist = mDragStartPosition.manhattanDist(event.getPosition());
-
-			if(!DragAndDropManager::instance().isDragInProgress())
-			{
-				if(dist > DRAG_MIN_DISTANCE && mEditElement == nullptr)
-				{
-					const GUITreeView::InteractableElement* element = findElementUnderCoord(mDragStartPosition);
-					TreeElement* treeElement = nullptr;
-
-					if(element != nullptr && element->isTreeElement())
-					{
-						// If element we are trying to drag isn't selected, select it
-						TreeElement* treeElement = element->getTreeElement();
-						auto iterFind = std::find_if(mSelectedElements.begin(), mSelectedElements.end(), 
-							[&] (const SelectedElement& x) { return x.element == treeElement; });
-
-						if(iterFind == mSelectedElements.end())
-						{
-							unselectAll();
-							selectElement(element->getTreeElement());
-						}						
-					}
-
-					dragAndDropStart();
-
-					mDragPosition = event.getPosition();
-					mDragInProgress = true;
-					mScrollState = ScrollState::None;
-					_markLayoutAsDirty();
-				}
-			}
-		}
-		else if(event.getType() == GUIMouseEventType::MouseDragAndDropDragged)
-		{
-			if(acceptDragAndDrop())
-			{
-				clearPing();
-
-				mDragPosition = event.getPosition();
-				mDragInProgress = true;
-				_markLayoutAsDirty();
-
-				if(mBottomScrollBounds.contains(mDragPosition))
-				{
-					if(mScrollState != ScrollState::Down)
-						mScrollState = ScrollState::TransitioningDown;
-				}
-				else if(mTopScrollBounds.contains(mDragPosition))
-				{
-					if(mScrollState != ScrollState::Up)
-						mScrollState = ScrollState::TransitioningUp;
-				}
-				else
-					mScrollState = ScrollState::None;
-
-				return true;
-			}
-		}
-		else if(event.getType() == GUIMouseEventType::MouseDragAndDropDropped)
-		{
-			if(acceptDragAndDrop())
-			{
-				const GUITreeView::InteractableElement* element = findElementUnderCoord(event.getPosition());
-
-				TreeElement* treeElement = nullptr;
-				if(element != nullptr)
-				{
-					if(element->isTreeElement())
-						treeElement = element->getTreeElement();
-					else
-						treeElement = element->parent;
-				}
-
-				dragAndDropEnded(treeElement);
-				unselectAll();
-
-				return true;
-			}
-		}
-		else if(event.getType() == GUIMouseEventType::MouseOut)
-		{
-			mDragInProgress = false;
-			_markLayoutAsDirty();
-		}
-		else if(event.getType() == GUIMouseEventType::MouseDragAndDropLeft)
-		{
-			mDragInProgress = false;
-			_markLayoutAsDirty();
-		}
-
-		return false;
-	}
-
-	bool GUITreeView::_commandEvent(const GUICommandEvent& ev)
-	{
-		if(ev.getType() == GUICommandEventType::MoveUp || ev.getType() == GUICommandEventType::SelectUp)
-		{
-			TreeElement* topMostElement = getTopMostSelectedElement();
-			auto topMostIter = std::find_if(mVisibleElements.begin(), mVisibleElements.end(), 
-				[&] (const InteractableElement& x) { return x.getTreeElement() == topMostElement; });
-
-			if(topMostIter != mVisibleElements.end() && topMostIter != mVisibleElements.begin())
-			{
-				do 
-				{
-					topMostIter--;
-				} while (!topMostIter->isTreeElement() && topMostIter != mVisibleElements.begin());
-
-				if(topMostIter->isTreeElement())
-				{
-					if(ev.getType() == GUICommandEventType::MoveUp)
-						unselectAll();
-
-					TreeElement* treeElement = topMostIter->getTreeElement();
-					selectElement(treeElement);
-					scrollToElement(treeElement, false);
-				}
-			}
-		}
-		else if(ev.getType() == GUICommandEventType::MoveDown || ev.getType() == GUICommandEventType::SelectDown)
-		{
-			TreeElement* bottoMostElement = getBottomMostSelectedElement();
-			auto bottomMostIter = std::find_if(mVisibleElements.begin(), mVisibleElements.end(), 
-				[&] (const InteractableElement& x) { return x.getTreeElement() == bottoMostElement; });
-
-			if(bottomMostIter != mVisibleElements.end())
-			{
-				do 
-				{
-					bottomMostIter++;
-				} while (bottomMostIter != mVisibleElements.end() && !bottomMostIter->isTreeElement());
-
-				if(bottomMostIter != mVisibleElements.end() && bottomMostIter->isTreeElement())
-				{
-					if(ev.getType() == GUICommandEventType::MoveDown)
-						unselectAll();
-
-					TreeElement* treeElement = bottomMostIter->getTreeElement();
-					selectElement(treeElement);
-					scrollToElement(treeElement, false);
-				}
-			}
-		}
-
-		return GUIElementContainer::_commandEvent(ev);
-	}
-
-	bool GUITreeView::_virtualButtonEvent(const GUIVirtualButtonEvent& ev)
-	{
-		if(ev.getButton() == mRenameVB)
-		{
-			renameSelected();
-
-			return true;
-		}
-		else if(ev.getButton() == mDeleteVB)
-		{
-			deleteSelection();
-		}
-		else if (ev.getButton() == mDuplicateVB)
-		{
-			duplicateSelection();
-			return true;
-		}
-		else if (ev.getButton() == mCutVB)
-		{
-			cutSelection();
-			return true;
-		}
-		else if (ev.getButton() == mCopyVB)
-		{
-			copySelection();
-			return true;
-		}
-		else if (ev.getButton() == mPasteVB)
-		{
-			paste();
-			return true;
-		}
-
-		return false;
-	}
-
-	bool GUITreeView::isSelectionActive() const
-	{
-		return mIsElementSelected && mSelectedElements.size() > 0;
-	}
-
-	void GUITreeView::selectElement(TreeElement* element)
-	{
-		clearPing();
-
-		auto iterFind = std::find_if(mSelectedElements.begin(), mSelectedElements.end(), 
-			[&] (const SelectedElement& x) { return x.element == element; });
-
-		if(iterFind == mSelectedElements.end())
-		{
-			GUITexture* background = GUITexture::create(mSelectionBackgroundStyle);
-			background->_setElementDepth(3);
-			_registerChildElement(background);
-
-			element->mIsSelected = true;
-
-			mSelectedElements.push_back(SelectedElement(element, background));
-			mIsElementSelected = true;
-
-			selectionChanged();
-		}
-	}
-
-	void GUITreeView::unselectElement(TreeElement* element)
-	{
-		clearPing();
-
-		auto iterFind = std::find_if(mSelectedElements.begin(), mSelectedElements.end(), 
-			[&] (const SelectedElement& x) { return x.element == element; });
-
-		if(iterFind != mSelectedElements.end())
-		{
-			iterFind->element->mIsSelected = false;
-			GUIElement::destroy(iterFind->background);
-
-			mSelectedElements.erase(iterFind);
-			_markLayoutAsDirty();
-
-			selectionChanged();
-		}
-
-		mIsElementSelected = mSelectedElements.size() > 0;
-	}
-
-	void GUITreeView::unselectAll(bool sendEvent)
-	{
-		clearPing();
-
-		for(auto& selectedElem : mSelectedElements)
-		{
-			selectedElem.element->mIsSelected = false;
-			GUIElement::destroy(selectedElem.background);
-		}
-
-		mSelectedElements.clear();
-		mIsElementSelected = false;
-
-		_markLayoutAsDirty();
-
-		if (sendEvent)
-			selectionChanged();
-	}
-
-	void GUITreeView::renameSelected()
-	{
-		if (isSelectionActive() && mEditElement == nullptr)
-		{
-			clearPing();
-			enableEdit(mSelectedElements[0].element);
-			unselectAll();
-		}
-	}
-
-	void GUITreeView::deleteSelection()
-	{
-		if (isSelectionActive())
-		{
-			auto isChildOf = [&](const TreeElement* parent, const TreeElement* child)
-			{
-				const TreeElement* elem = child;
-
-				while (elem != nullptr && elem != parent)
-					elem = elem->mParent;
-
-				return elem == parent;
-			};
-
-			// Ensure we don't unnecessarily try to delete children if their
-			// parent is getting deleted anyway
-			Vector<TreeElement*> elementsToDelete;
-			for (UINT32 i = 0; i < (UINT32)mSelectedElements.size(); i++)
-			{
-				bool hasDeletedParent = false;
-				for (UINT32 j = 0; j < (UINT32)mSelectedElements.size(); j++)
-				{
-					if (i == j)
-						continue;
-
-					if (isChildOf(mSelectedElements[j].element, mSelectedElements[i].element))
-					{
-						hasDeletedParent = true;
-						break;
-					}
-				}
-
-				if (!hasDeletedParent)
-					elementsToDelete.push_back(mSelectedElements[i].element);
-			}
-
-			clearPing();
-			unselectAll();
-
-			for (auto& elem : elementsToDelete)
-				deleteTreeElement(elem);
-		}
-	}
-
-	void GUITreeView::ping(TreeElement* element)
-	{
-		clearPing();
-
-		expandToElement(element);
-		scrollToElement(element, true);
-
-		GUITexture* background = GUITexture::create(mHighlightBackgroundStyle);
-		background->_setElementDepth(2);
-		_registerChildElement(background);
-
-		element->mIsHighlighted = true;
-
-		mHighlightedElement.element = element;
-		mHighlightedElement.background = background;
-
-		mIsElementHighlighted = true;
-		_markLayoutAsDirty();
-	}
-
-	void GUITreeView::clearPing()
-	{
-		if (!mIsElementHighlighted)
-			return;
-
-		mHighlightedElement.element->mIsHighlighted = false;
-		GUIElement::destroy(mHighlightedElement.background);
-
-		mHighlightedElement.element = nullptr;
-		mHighlightedElement.background = nullptr;
-		mIsElementHighlighted = false;
-
-		_markLayoutAsDirty();
-	}
-
-	void GUITreeView::expandToElement(TreeElement* element)
-	{
-		if (element->mIsVisible || element->mParent == nullptr)
-			return;
-
-		Stack<TreeElement*> todo;
-
-		TreeElement* parent = element->mParent;
-		while (parent != nullptr)
-		{
-			if (!parent->mIsExpanded)
-				todo.push(parent);
-
-			if (parent->mIsVisible)
-				break;
-
-			parent = parent->mParent;
-		}
-
-		while (!todo.empty())
-		{
-			TreeElement* curElement = todo.top();
-			todo.pop();
-
-			expandElement(curElement);
-		}
-	}
-
-	void GUITreeView::expandElement(TreeElement* element)
-	{
-		if(element->mIsExpanded)
-			return;
-
-		element->mIsExpanded = true;
-
-		if(element->mParent == nullptr || (element->mParent->mIsVisible && element->mParent->mIsExpanded))
-		{
-			Stack<TreeElement*> todo;
-			todo.push(element);
-
-			while(!todo.empty())
-			{
-				TreeElement* curElem = todo.top();
-				todo.pop();
-
-				curElem->mIsVisible = true;
-				updateElementGUI(curElem);
-
-				if(curElem->mIsExpanded)
-				{
-					for(auto& child : curElem->mChildren)
-						todo.push(child);
-				}
-			}
-		}
-	}
-
-	void GUITreeView::collapseElement(TreeElement* element)
-	{
-		if(!element->mIsExpanded)
-			return;
-
-		element->mIsExpanded = false;
-		updateElementGUI(element);
-
-		if(element->mParent == nullptr || (element->mParent->mIsVisible && element->mParent->mIsExpanded))
-		{
-			Stack<TreeElement*> todo;
-
-			for(auto& child : element->mChildren)
-				todo.push(child);
-
-			while(!todo.empty())
-			{
-				TreeElement* curElem = todo.top();
-				todo.pop();
-
-				curElem->mIsVisible = false;
-
-				if(curElem->mIsSelected)
-					unselectElement(curElem);
-
-				updateElementGUI(curElem);
-
-				if(curElem->mIsExpanded)
-				{
-					for(auto& child : curElem->mChildren)
-						todo.push(child);
-				}
-			}
-		}
-	}
-
-	void GUITreeView::updateElementGUI(TreeElement* element)
-	{
-		if(element == &getRootElement())
-			return;
-
-		if(element->mIsVisible)
-		{
-			HString name(toWString(element->mName));
-			if(element->mElement == nullptr)
-			{
-				element->mElement = GUILabel::create(name, mElementBtnStyle);
-				_registerChildElement(element->mElement);
-			}
-
-			if (element->mIsCut)
-			{
-				Color cutTint = element->mTint;
-				cutTint.a = CUT_COLOR.a;
-
-				element->mElement->setTint(cutTint);
-			}
-			else if(element->mIsDisabled)
-			{
-				Color disabledTint = element->mTint;
-				disabledTint.a = DISABLED_COLOR.a;
-
-				element->mElement->setTint(disabledTint);
-			}
-			else
-				element->mElement->setTint(element->mTint);
-
-			if(element->mChildren.size() > 0)
-			{
-				if(element->mFoldoutBtn == nullptr)
-				{
-					element->mFoldoutBtn = GUIToggle::create(GUIContent(HString(L"")), mFoldoutBtnStyle);
-					_registerChildElement(element->mFoldoutBtn);
-
-					element->mFoldoutBtn->onToggled.connect(std::bind(&GUITreeView::elementToggled, this, element, _1));
-
-					if(element->mIsExpanded)
-						element->mFoldoutBtn->toggleOn();
-				}
-			}
-			else
-			{
-				if(element->mFoldoutBtn != nullptr)
-				{
-					GUIElement::destroy(element->mFoldoutBtn);
-					element->mFoldoutBtn = nullptr;
-				}
-			}
-
-			element->mElement->setContent(GUIContent(name));
-		}
-		else
-		{
-			if(element->mElement != nullptr)
-			{
-				GUIElement::destroy(element->mElement);
-				element->mElement = nullptr;
-			}
-
-			if(element->mFoldoutBtn != nullptr)
-			{
-				GUIElement::destroy(element->mFoldoutBtn);
-				element->mFoldoutBtn = nullptr;
-			}
-
-			if(element->mIsSelected && element->mIsExpanded)
-				unselectElement(element);
-		}
-
-		_markLayoutAsDirty();
-	}
-
-	void GUITreeView::elementToggled(TreeElement* element, bool toggled)
-	{
-		clearPing();
-
-		if(toggled)
-			expandElement(element);
-		else
-			collapseElement(element);
-	}
-
-	void GUITreeView::onEditAccepted()
-	{
-		TreeElement* elem = mEditElement;
-		disableEdit(true);
-		selectElement(elem);
-	}
-
-	void GUITreeView::onEditCanceled()
-	{
-		if (mEditElement != nullptr)
-		{
-			TreeElement* elem = mEditElement;
-			disableEdit(false);
-			selectElement(elem);
-		}
-	}
-
-	void GUITreeView::onEditFocusLost()
-	{
-		if (mEditElement != nullptr)
-			disableEdit(false);
-	}
-
-	void GUITreeView::enableEdit(TreeElement* element)
-	{
-		assert(mEditElement == nullptr);
-
-		mEditElement = element;
-		mNameEditBox->setVisible(true);
-		mNameEditBox->setText(toWString(element->mName));
-		mNameEditBox->setFocus(true);
-
-		if(element->mElement != nullptr)
-			element->mElement->setVisible(false);
-	}
-
-	void GUITreeView::disableEdit(bool applyChanges)
-	{
-		assert(mEditElement != nullptr);
-
-		if(mEditElement->mElement != nullptr)
-			mEditElement->mElement->setVisible(true);
-
-		if(applyChanges)
-		{
-			WString newName = mNameEditBox->getText();
-			renameTreeElement(mEditElement, newName);
-		}
-
-		mNameEditBox->setFocus(false);
-		mNameEditBox->setVisible(false);
-		mEditElement = nullptr;
-	}
-
-	Vector2I GUITreeView::_getOptimalSize() const
-	{
-		struct UpdateTreeElement
-		{
-			UpdateTreeElement(const TreeElement* element, UINT32 indent)
-				:element(element), indent(indent)
-			{ }
-
-			const TreeElement* element;
-			UINT32 indent;
-		};
-
-		Vector2I optimalSize;
-
-		if (_getDimensions().fixedWidth() && _getDimensions().fixedHeight())
-		{
-			optimalSize.x = _getDimensions().minWidth;
-			optimalSize.y = _getDimensions().minHeight;
-		}
-		else
-		{
-			Stack<UpdateTreeElement> todo;
-			todo.push(UpdateTreeElement(&getRootElementConst(), 0));
-
-			while(!todo.empty())
-			{
-				UpdateTreeElement currentUpdateElement = todo.top();
-				const TreeElement* current = currentUpdateElement.element;
-				todo.pop();
-
-				INT32 yOffset = 0;
-				if(current->mElement != nullptr)
-				{
-					Vector2I curOptimalSize = current->mElement->_getOptimalSize();
-					optimalSize.x = std::max(optimalSize.x, 
-						(INT32)(INITIAL_INDENT_OFFSET + curOptimalSize.x + currentUpdateElement.indent * INDENT_SIZE));
-					yOffset = curOptimalSize.y + ELEMENT_EXTRA_SPACING;
-				}
-
-				optimalSize.y += yOffset;
-
-				for(auto& child : current->mChildren)
-				{
-					if(!child->mIsVisible)
-						continue;
-
-					todo.push(UpdateTreeElement(child, currentUpdateElement.indent + 1));
-				}
-			}
-
-			if(_getDimensions().fixedWidth())
-				optimalSize.x = _getDimensions().minWidth;
-			else
-			{
-				if(_getDimensions().minWidth > 0)
-					optimalSize.x = std::max((INT32)_getDimensions().minWidth, optimalSize.x);
-
-				if(_getDimensions().maxWidth > 0)
-					optimalSize.x = std::min((INT32)_getDimensions().maxWidth, optimalSize.x);
-			}
-
-			if (_getDimensions().fixedHeight())
-				optimalSize.y = _getDimensions().minHeight;
-			else
-			{
-				if(_getDimensions().minHeight > 0)
-					optimalSize.y = std::max((INT32)_getDimensions().minHeight, optimalSize.y);
-
-				if(_getDimensions().maxHeight > 0)
-					optimalSize.y = std::min((INT32)_getDimensions().maxHeight, optimalSize.y);
-			}
-		}
-
-		return optimalSize;
-	}
-
-	void GUITreeView::updateClippedBounds()
-	{
-		mClippedBounds = mLayoutData.area;
-		mClippedBounds.clip(mLayoutData.clipRect);
-	}
-
-	void GUITreeView::_updateLayoutInternal(const GUILayoutData& data)
-	{
-		struct UpdateTreeElement
-		{
-			UpdateTreeElement(TreeElement* element, UINT32 indent)
-				:element(element), indent(indent)
-			{ }
-
-			TreeElement* element;
-			UINT32 indent;
-		};
-
-		mVisibleElements.clear();
-
-		Stack<UpdateTreeElement> todo;
-		todo.push(UpdateTreeElement(&getRootElement(), 0));
-
-		// NOTE - Instead of iterating through all elements, try to find those within the clip rect
-		// and only iterate through those. Others should somehow be marked in-active (similar to GUIElement::isDisabled()?)
-
-		Vector<TreeElement*> tempOrderedElements;
-
-		Vector2I offset(data.area.x, data.area.y);
-
-		while(!todo.empty())
-		{
-			UpdateTreeElement currentUpdateElement = todo.top();
-			TreeElement* current = currentUpdateElement.element;
-			UINT32 indent = currentUpdateElement.indent;
-			todo.pop();
-
-			INT32 btnHeight = 0;
-			INT32 yOffset = 0;
-			if(current->mElement != nullptr)
-			{
-				Vector2I elementSize = current->mElement->_getOptimalSize();
-				btnHeight = elementSize.y;
-
-				mVisibleElements.push_back(InteractableElement(current->mParent, current->mSortedIdx * 2 + 0, Rect2I(data.area.x, offset.y, data.area.width, ELEMENT_EXTRA_SPACING)));
-				mVisibleElements.push_back(InteractableElement(current->mParent, current->mSortedIdx * 2 + 1, Rect2I(data.area.x, offset.y + ELEMENT_EXTRA_SPACING, data.area.width, btnHeight)));
-
-				offset.x = data.area.x + INITIAL_INDENT_OFFSET + indent * INDENT_SIZE;
-				offset.y += ELEMENT_EXTRA_SPACING;
-
-				GUILayoutData childData = data;
-				childData.area.x = offset.x;
-				childData.area.y = offset.y;
-				childData.area.width = elementSize.x;
-				childData.area.height = elementSize.y;
-
-				current->mElement->_setLayoutData(childData);
-
-				yOffset = btnHeight;
-			}
-
-			if(current->mFoldoutBtn != nullptr)
-			{
-				Vector2I elementSize = current->mFoldoutBtn->_getOptimalSize();
-
-				offset.x -= std::min((INT32)INITIAL_INDENT_OFFSET, elementSize.x + 2);
-
-				Vector2I myOffset = offset;
-				myOffset.y += 1;
-
-				if(elementSize.y > btnHeight)
-				{
-					UINT32 diff = elementSize.y - btnHeight;
-					float half = diff * 0.5f;
-					myOffset.y -= Math::floorToInt(half);
-				}
-
-				GUILayoutData childData = data;
-				childData.area.x = myOffset.x;
-				childData.area.y = myOffset.y;
-				childData.area.width = elementSize.x;
-				childData.area.height = elementSize.y;
-
-				current->mFoldoutBtn->_setLayoutData(childData);
-			}
-
-			offset.y += yOffset;
-
-			tempOrderedElements.resize(current->mChildren.size(), nullptr);
-			for(auto& child : current->mChildren)
-			{
-				tempOrderedElements[child->mSortedIdx] = child;
-			}
-
-			for(auto iter = tempOrderedElements.rbegin(); iter != tempOrderedElements.rend(); ++iter)
-			{
-				TreeElement* child = *iter;
-
-				if(!child->mIsVisible)
-					continue;
-
-				todo.push(UpdateTreeElement(child, indent + 1));
-			}
-		}
-
-		UINT32 remainingHeight = (UINT32)std::max(0, (INT32)data.area.height - (offset.y - data.area.y));
-
-		if(remainingHeight > 0)
-			mVisibleElements.push_back(InteractableElement(&getRootElement(), (UINT32)getRootElement().mChildren.size() * 2, Rect2I(data.area.x, offset.y, data.area.width, remainingHeight)));
-
-		for(auto selectedElem : mSelectedElements)
-		{
-			GUILabel* targetElement = selectedElem.element->mElement;
-
-			GUILayoutData childData = data;
-			childData.area.y = targetElement->_getLayoutData().area.y;
-			childData.area.height = targetElement->_getLayoutData().area.height;
-
-			selectedElem.background->_setLayoutData(childData);
-		}
-
-		if (mIsElementHighlighted)
-		{
-			GUILabel* targetElement = mHighlightedElement.element->mElement;
-
-			GUILayoutData childData = data;
-			childData.area.y = targetElement->_getLayoutData().area.y;
-			childData.area.height = targetElement->_getLayoutData().area.height;
-
-			mHighlightedElement.background->_setLayoutData(childData);
-		}
-
-		if(mEditElement != nullptr)
-		{
-			GUILabel* targetElement = mEditElement->mElement;
-
-			UINT32 remainingWidth = (UINT32)std::max(0, (((INT32)data.area.width) - (offset.x - data.area.x)));
-
-			GUILayoutData childData = data;
-			childData.area = targetElement->_getLayoutData().area;
-			childData.area.width = remainingWidth;
-
-			mNameEditBox->_setLayoutData(childData);
-		}
-
-		if(mDragInProgress)
-		{
-			const InteractableElement* interactableElement = findElementUnderCoord(mDragPosition);
-
-			if(interactableElement == nullptr)
-			{
-				mDragHighlight->setVisible(false);
-				mDragSepHighlight->setVisible(false);
-			}
-			else
-			{
-				if(interactableElement->isTreeElement())
-				{
-					mDragSepHighlight->setVisible(false);
-					mDragHighlight->setVisible(true);
-
-					GUILayoutData childData = data;
-					childData.area = interactableElement->bounds;
-
-					mDragHighlight->_setLayoutData(childData);
-				}
-				else
-				{
-					mDragHighlight->setVisible(false);
-					mDragSepHighlight->setVisible(true);
-
-					GUILayoutData childData = data;
-					childData.area = interactableElement->bounds;
-
-					mDragSepHighlight->_setLayoutData(childData);
-				}
-			}
-		}
-		else
-		{
-			mDragHighlight->setVisible(false);
-			mDragSepHighlight->setVisible(false);
-		}
-
-		// Update scroll bounds
-		UINT32 scrollHeight = (UINT32)Math::roundToInt(data.clipRect.height * SCROLL_AREA_HEIGHT_PCT);
-
-		mTopScrollBounds.x = data.clipRect.x;
-		mTopScrollBounds.y = data.clipRect.y;
-		mTopScrollBounds.width = data.clipRect.width;
-		mTopScrollBounds.height = scrollHeight;
-
-		mBottomScrollBounds.x = data.clipRect.x;
-		mBottomScrollBounds.y = data.clipRect.y + data.clipRect.height - scrollHeight;
-		mBottomScrollBounds.width = data.clipRect.width;
-		mBottomScrollBounds.height = scrollHeight;
-	}
-
-	const GUITreeView::InteractableElement* GUITreeView::findElementUnderCoord(const Vector2I& coord) const
-	{
-		for(auto& element : mVisibleElements)
-		{
-			if(element.bounds.contains(coord))
-			{
-				return &element;
-			}
-		}
-
-		return nullptr;
-	}
-
-	GUITreeView::TreeElement* GUITreeView::getTopMostSelectedElement() const
-	{
-		auto topMostElement = mVisibleElements.end();
-
-		for(auto& selectedElement : mSelectedElements)
-		{
-			auto iterFind = std::find_if(mVisibleElements.begin(), mVisibleElements.end(), 
-				[&] (const InteractableElement& x) { return x.getTreeElement() == selectedElement.element; });
-
-			if(iterFind != mVisibleElements.end())
-			{
-				if(topMostElement == mVisibleElements.end())
-					topMostElement = iterFind;
-				else
-				{
-					if(iterFind->bounds.y < topMostElement->bounds.y)
-						topMostElement = iterFind;
-				}
-			}
-		}
-
-		if(topMostElement != mVisibleElements.end())
-			return topMostElement->getTreeElement();
-		else
-			return nullptr;
-	}
-
-	GUITreeView::TreeElement* GUITreeView::getBottomMostSelectedElement() const
-	{
-		auto& botMostElement = mVisibleElements.end();
-
-		for(auto& selectedElement : mSelectedElements)
-		{
-			auto iterFind = std::find_if(mVisibleElements.begin(), mVisibleElements.end(), 
-				[&] (const InteractableElement& x) { return x.getTreeElement() == selectedElement.element; });
-
-			if(iterFind != mVisibleElements.end())
-			{
-				if(botMostElement == mVisibleElements.end())
-					botMostElement = iterFind;
-				else
-				{
-					if((iterFind->bounds.y + iterFind->bounds.height) > (botMostElement->bounds.y + botMostElement->bounds.height))
-						botMostElement = iterFind;
-				}
-			}
-		}
-
-		if(botMostElement != mVisibleElements.end())
-			return botMostElement->getTreeElement();
-		else
-			return nullptr;
-	}
-
-	void GUITreeView::closeTemporarilyExpandedElements()
-	{
-		temporarilyExpandElement(nullptr);
-	}
-
-	void GUITreeView::temporarilyExpandElement(const GUITreeView::InteractableElement* mouseOverElement)
-	{
-		TreeElement* treeElement = nullptr;
-		if(mouseOverElement != nullptr && mouseOverElement->isTreeElement())
-			treeElement = mouseOverElement->getTreeElement();
-
-		if(treeElement == nullptr || treeElement != mMouseOverDragElement)
-		{
-			while(!mAutoExpandedElements.empty())
-			{
-				TreeElement* autoExpandedElement = mAutoExpandedElements.top();
-
-				bool unexpandElement = false;
-				if(mouseOverElement != nullptr && mouseOverElement->parent != nullptr)
-				{
-					if(mouseOverElement->parent != autoExpandedElement && !mouseOverElement->parent->isParentRec(autoExpandedElement))
-						unexpandElement = true;
-					else
-						break;
-				}
-				else
-					unexpandElement = true;
-
-				if(unexpandElement)
-				{
-					collapseElement(autoExpandedElement);
-
-					if(autoExpandedElement->mFoldoutBtn != nullptr)
-						autoExpandedElement->mFoldoutBtn->toggleOff();
-
-					mAutoExpandedElements.pop();
-				}
-			}
-
-			mMouseOverDragElement = treeElement;
-			mMouseOverDragElementTime = gTime().getTime();
-		}
-		else
-		{
-			if(mMouseOverDragElement != nullptr && !mMouseOverDragElement->mIsExpanded)
-			{
-				float timeDiff = gTime().getTime() - mMouseOverDragElementTime;
-				if(timeDiff >= AUTO_EXPAND_DELAY_SEC)
-				{
-					mAutoExpandedElements.push(mMouseOverDragElement);
-					expandElement(mMouseOverDragElement);
-
-					if(mMouseOverDragElement->mFoldoutBtn != nullptr)
-						mMouseOverDragElement->mFoldoutBtn->toggleOn();
-				}
-			}
-		}
-	}
-
-	void GUITreeView::scrollToElement(TreeElement* element, bool center)
-	{
-		if(element->mElement == nullptr)
-			return;
-
-		GUIScrollArea* scrollArea = findParentScrollArea();
-		if(scrollArea == nullptr)
-			return;
-
-		if(center)
-		{
-			Rect2I myBounds = _getClippedBounds();
-			INT32 clipVertCenter = myBounds.y + (INT32)Math::roundToInt(myBounds.height * 0.5f);
-			INT32 elemVertCenter = element->mElement->_getLayoutData().area.y + (INT32)Math::roundToInt(element->mElement->_getLayoutData().area.height * 0.5f);
-
-			if(elemVertCenter > clipVertCenter)
-				scrollArea->scrollDownPx(elemVertCenter - clipVertCenter);
-			else
-				scrollArea->scrollUpPx(clipVertCenter - elemVertCenter);
-		}
-		else
-		{
-			Rect2I myBounds = _getClippedBounds();
-			INT32 elemVertTop = element->mElement->_getLayoutData().area.y;
-			INT32 elemVertBottom = element->mElement->_getLayoutData().area.y + element->mElement->_getLayoutData().area.height;
-
-			INT32 top = myBounds.y;
-			INT32 bottom = myBounds.y + myBounds.height;
-
-			INT32 offset = 0;
-			if(elemVertTop < top)
-				scrollArea->scrollUpPx(top - elemVertTop);
-			else if(elemVertBottom > bottom)
-				scrollArea->scrollDownPx(elemVertBottom - bottom);
-		}
-	}
-
-	GUIScrollArea* GUITreeView::findParentScrollArea() const
-	{
-		GUIElementBase* parent = _getParent();
-		while(parent != nullptr)
-		{
-			if(parent->_getType() == GUIElementBase::Type::Element)
-			{
-				GUIElement* parentElement = static_cast<GUIElement*>(parent);
-
-				if(parentElement->_getElementType() == GUIElement::ElementType::ScrollArea)
-				{
-					GUIScrollArea* scrollArea = static_cast<GUIScrollArea*>(parentElement);
-					return scrollArea;
-
-				}
-			}
-
-			parent = parent->_getParent();
-		}
-
-		return nullptr;
-	}
-
-	const String& GUITreeView::getGUITypeName()
-	{
-		static String typeName = "SceneTreeView";
-		return typeName;
-	}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsGUITreeView.h"
+#include "BsGUILayout.h"
+#include "BsGUITexture.h"
+#include "BsGUIButton.h"
+#include "BsGUILabel.h"
+#include "BsGUISpace.h"
+#include "BsCGUIWidget.h"
+#include "BsGUIToggle.h"
+#include "BsGUITreeViewEditBox.h"
+#include "BsGUIMouseEvent.h"
+#include "BsGUISkin.h"
+#include "BsGUICommandEvent.h"
+#include "BsGUIVirtualButtonEvent.h"
+#include "BsGUIScrollArea.h"
+#include "BsDragAndDropManager.h"
+#include "BsTime.h"
+
+using namespace std::placeholders;
+
+namespace BansheeEngine
+{
+	const UINT32 GUITreeView::ELEMENT_EXTRA_SPACING = 3;
+	const UINT32 GUITreeView::INDENT_SIZE = 10;
+	const UINT32 GUITreeView::INITIAL_INDENT_OFFSET = 16;
+	const UINT32 GUITreeView::DRAG_MIN_DISTANCE = 3;
+	const float GUITreeView::AUTO_EXPAND_DELAY_SEC = 0.5f;
+	const float GUITreeView::SCROLL_AREA_HEIGHT_PCT = 0.1f;
+	const UINT32 GUITreeView::SCROLL_SPEED_PX_PER_SEC = 100;
+	const Color GUITreeView::CUT_COLOR = Color(1.0f, 1.0f, 1.0f, 0.3f);
+	const Color GUITreeView::DISABLED_COLOR = Color(1.0f, 1.0f, 1.0f, 0.6f);
+
+	GUITreeView::TreeElement::TreeElement()
+		:mParent(nullptr), mFoldoutBtn(nullptr), mElement(nullptr), mIsSelected(false),
+		mIsExpanded(false), mSortedIdx(0), mIsVisible(true), mIsHighlighted(false), mIsCut(false), mIsDisabled(false)
+	{ }
+
+	GUITreeView::TreeElement::~TreeElement()
+	{
+		for(auto& child : mChildren)
+			bs_delete(child);
+
+		if(mFoldoutBtn != nullptr)
+			GUIElement::destroy(mFoldoutBtn);
+
+		if(mElement != nullptr)
+			GUIElement::destroy(mElement);
+
+		mChildren.clear();
+	}
+
+	bool GUITreeView::TreeElement::isParentRec(TreeElement* element) const
+	{
+		TreeElement* curParent = mParent;
+		while(curParent != nullptr)
+		{
+			if(curParent == element)
+				return true;
+
+			curParent = curParent->mParent;
+		}
+
+		return false;
+	}
+
+	GUITreeView::TreeElement* GUITreeView::InteractableElement::getTreeElement() const
+	{
+		if(!isTreeElement())
+			return nullptr;
+
+		UINT32 sortedIdx = (index - 1) / 2;
+
+		auto findIter = std::find_if(parent->mChildren.begin(), parent->mChildren.end(),
+			[&](const TreeElement* x) { return x->mSortedIdx == sortedIdx; });
+
+		if(findIter != parent->mChildren.end())
+			return *findIter;
+
+		return nullptr;
+	}
+
+	GUITreeView::GUITreeView(const String& backgroundStyle, const String& elementBtnStyle, 
+		const String& foldoutBtnStyle, const String& selectionBackgroundStyle, const String& highlightBackgroundStyle, 
+		const String& editBoxStyle, const String& dragHighlightStyle, const String& dragSepHighlightStyle, const GUIDimensions& dimensions)
+		:GUIElementContainer(dimensions), mBackgroundStyle(backgroundStyle),
+		mElementBtnStyle(elementBtnStyle), mFoldoutBtnStyle(foldoutBtnStyle), mEditBoxStyle(editBoxStyle), mEditElement(nullptr), mIsElementSelected(false),
+		mNameEditBox(nullptr), mHighlightBackgroundStyle(highlightBackgroundStyle), mSelectionBackgroundStyle(selectionBackgroundStyle), mDragInProgress(nullptr), 
+		mDragHighlightStyle(dragHighlightStyle), mDragSepHighlightStyle(dragSepHighlightStyle), mDragHighlight(nullptr), mDragSepHighlight(nullptr), mMouseOverDragElement(nullptr), 
+		mMouseOverDragElementTime(0.0f), mScrollState(ScrollState::None), mLastScrollTime(0.0f), mIsElementHighlighted(false)
+	{
+		if(mBackgroundStyle == StringUtil::BLANK)
+			mBackgroundStyle = "TreeViewBackground";
+
+		if(mElementBtnStyle == StringUtil::BLANK)
+			mElementBtnStyle = "TreeViewElementBtn";
+
+		if(mFoldoutBtnStyle == StringUtil::BLANK)
+			mFoldoutBtnStyle = "TreeViewFoldoutBtn";
+
+		if(mSelectionBackgroundStyle == StringUtil::BLANK)
+			mSelectionBackgroundStyle = "TreeViewSelectionBackground";
+
+		if (mHighlightBackgroundStyle == StringUtil::BLANK)
+			mHighlightBackgroundStyle = "TreeViewHighlightBackground";
+
+		if(mEditBoxStyle == StringUtil::BLANK)
+			mEditBoxStyle = "TreeViewEditBox";
+
+		if(mDragHighlightStyle == StringUtil::BLANK)
+			mDragHighlightStyle = "TreeViewElementHighlight";
+
+		if(mDragSepHighlightStyle == StringUtil::BLANK)
+			mDragSepHighlightStyle = "TreeViewElementSepHighlight";
+
+		mBackgroundImage = GUITexture::create(mBackgroundStyle);
+		mNameEditBox = GUITreeViewEditBox::create(mEditBoxStyle);
+		mNameEditBox->setVisible(false);
+
+		mNameEditBox->onInputConfirmed.connect(std::bind(&GUITreeView::onEditAccepted, this));
+		mNameEditBox->onInputCanceled.connect(std::bind(&GUITreeView::onEditCanceled, this));
+		mNameEditBox->onFocusLost.connect(std::bind(&GUITreeView::onEditFocusLost, this));
+
+		mDragHighlight = GUITexture::create(mDragHighlightStyle);
+		mDragSepHighlight = GUITexture::create(mDragSepHighlightStyle);
+
+		mDragHighlight->setVisible(false);
+		mDragSepHighlight->setVisible(false);
+
+		mDragHighlight->_setElementDepth(2);
+		mDragSepHighlight->_setElementDepth(2);
+
+		_registerChildElement(mBackgroundImage);
+		_registerChildElement(mNameEditBox);
+		_registerChildElement(mDragHighlight);
+		_registerChildElement(mDragSepHighlight);
+	}
+
+	GUITreeView::~GUITreeView()
+	{
+
+	}
+
+	void GUITreeView::_update()
+	{
+		// Attempt to auto-expand elements we are dragging over
+		if(acceptDragAndDrop())
+		{
+			const GUITreeView::InteractableElement* element = findElementUnderCoord(mDragPosition);
+			temporarilyExpandElement(element);
+		}
+
+		// NOTE - Instead of iterating through every visible element and comparing it with internal values,
+		// I might just want to add callbacks to SceneManager that notify me of any changes and then only perform
+		// update if anything is actually dirty
+
+		updateTreeElementHierarchy();
+
+		// Attempt to scroll if needed
+		if(mScrollState != ScrollState::None)
+		{
+			GUIScrollArea* scrollArea = findParentScrollArea();
+			if(scrollArea != nullptr)
+			{
+				float curTime = gTime().getTime();
+				float timeDiff = curTime - mLastScrollTime;
+				float secondsPerPixel = 1.0f / SCROLL_SPEED_PX_PER_SEC;
+
+				switch(mScrollState)
+				{
+				case ScrollState::TransitioningUp:
+					mScrollState = ScrollState::Up;
+					mLastScrollTime = curTime;
+					break;
+				case ScrollState::TransitioningDown:
+					mScrollState = ScrollState::Down;
+					mLastScrollTime = curTime;
+					break;
+				case ScrollState::Up:
+					{
+						UINT32 scrollAmount = (UINT32)Math::floorToInt(timeDiff / secondsPerPixel);
+						mLastScrollTime += scrollAmount * secondsPerPixel;
+
+						scrollArea->scrollUpPx(scrollAmount);
+					}
+					break;
+				case ScrollState::Down:
+					{
+						UINT32 scrollAmount = (UINT32)Math::floorToInt(timeDiff / secondsPerPixel);
+						mLastScrollTime += scrollAmount * secondsPerPixel;
+
+						scrollArea->scrollDownPx(scrollAmount);
+					}
+					break;
+				}
+			}
+		}
+	}
+
+	bool GUITreeView::_mouseEvent(const GUIMouseEvent& event)
+	{
+		if(event.getType() == GUIMouseEventType::MouseUp)
+		{
+			if(DragAndDropManager::instance().isDragInProgress())
+				return false;
+
+			const GUITreeView::InteractableElement* element = findElementUnderCoord(event.getPosition());
+			TreeElement* treeElement = nullptr;
+
+			if(element != nullptr && element->isTreeElement())
+			{
+				treeElement = element->getTreeElement();
+			}
+
+			if (treeElement != nullptr)
+			{
+				bool onFoldout = false;
+				if (treeElement->mFoldoutBtn != nullptr)
+					onFoldout = treeElement->mFoldoutBtn->_getClippedBounds().contains(event.getPosition());
+
+				bool onEditElement = false;
+				if (mEditElement != nullptr)
+					onEditElement = treeElement == mEditElement;
+
+				if (!onFoldout && !onEditElement)
+				{
+					if (event.getButton() == GUIMouseButton::Left)
+					{
+						if (event.isCtrlDown())
+						{
+							selectElement(treeElement);
+						}
+						else if (event.isShiftDown())
+						{
+							if (isSelectionActive())
+							{
+								TreeElement* selectionRoot = mSelectedElements[0].element;
+								unselectAll();
+
+								auto iterStartFind = std::find_if(mVisibleElements.begin(), mVisibleElements.end(),
+									[&](const InteractableElement& x) { return x.parent == selectionRoot->mParent; });
+
+								bool foundStart = false;
+								bool foundEnd = false;
+								for (; iterStartFind != mVisibleElements.end(); ++iterStartFind)
+								{
+									if (!iterStartFind->isTreeElement())
+										continue;
+
+									TreeElement* curElem = iterStartFind->getTreeElement();
+									if (curElem == selectionRoot)
+									{
+										foundStart = true;
+										break;
+									}
+								}
+
+								auto iterEndFind = std::find_if(mVisibleElements.begin(), mVisibleElements.end(),
+									[&](const InteractableElement& x) { return &x == element; });
+
+								if (iterEndFind != mVisibleElements.end())
+									foundEnd = true;
+
+								if (foundStart && foundEnd)
+								{
+									if (iterStartFind < iterEndFind)
+									{
+										for (; iterStartFind != (iterEndFind + 1); ++iterStartFind)
+										{
+											if (iterStartFind->isTreeElement())
+												selectElement(iterStartFind->getTreeElement());
+										}
+									}
+									else if (iterEndFind < iterStartFind)
+									{
+										for (; iterEndFind != (iterStartFind + 1); ++iterEndFind)
+										{
+											if (iterEndFind->isTreeElement())
+												selectElement(iterEndFind->getTreeElement());
+										}
+									}
+									else
+										selectElement(treeElement);
+								}
+
+								if (!foundStart || !foundEnd)
+									selectElement(treeElement);
+							}
+							else
+							{
+								selectElement(treeElement);
+							}
+						}
+						else
+						{
+							bool doRename = false;
+							if (isSelectionActive())
+							{
+								for (auto& selectedElem : mSelectedElements)
+								{
+									if (selectedElem.element == treeElement)
+									{
+										doRename = true;
+										break;
+									}
+								}
+							}
+
+							unselectAll();
+							selectElement(treeElement);
+
+							if (doRename)
+								renameSelected();
+						}
+
+						_markLayoutAsDirty();
+
+						return true;
+					}
+					else if (event.getButton() == GUIMouseButton::Right)
+					{
+						unselectAll();
+						selectElement(treeElement);
+						_markLayoutAsDirty();
+
+						return true;
+					}
+				}
+			}
+			else
+			{
+				unselectAll();
+
+				return true;
+			}
+		}
+		else if(event.getType() == GUIMouseEventType::MouseDragStart)
+		{
+			clearPing();
+
+			mDragStartPosition = event.getPosition();
+		}
+		else if(event.getType() == GUIMouseEventType::MouseDrag)
+		{
+			UINT32 dist = mDragStartPosition.manhattanDist(event.getPosition());
+
+			if(!DragAndDropManager::instance().isDragInProgress())
+			{
+				if(dist > DRAG_MIN_DISTANCE && mEditElement == nullptr)
+				{
+					const GUITreeView::InteractableElement* element = findElementUnderCoord(mDragStartPosition);
+					TreeElement* treeElement = nullptr;
+
+					if(element != nullptr && element->isTreeElement())
+					{
+						// If element we are trying to drag isn't selected, select it
+						TreeElement* treeElement = element->getTreeElement();
+						auto iterFind = std::find_if(mSelectedElements.begin(), mSelectedElements.end(), 
+							[&] (const SelectedElement& x) { return x.element == treeElement; });
+
+						if(iterFind == mSelectedElements.end())
+						{
+							unselectAll();
+							selectElement(element->getTreeElement());
+						}						
+					}
+
+					dragAndDropStart();
+
+					mDragPosition = event.getPosition();
+					mDragInProgress = true;
+					mScrollState = ScrollState::None;
+					_markLayoutAsDirty();
+				}
+			}
+		}
+		else if(event.getType() == GUIMouseEventType::MouseDragAndDropDragged)
+		{
+			if(acceptDragAndDrop())
+			{
+				clearPing();
+
+				mDragPosition = event.getPosition();
+				mDragInProgress = true;
+				_markLayoutAsDirty();
+
+				if(mBottomScrollBounds.contains(mDragPosition))
+				{
+					if(mScrollState != ScrollState::Down)
+						mScrollState = ScrollState::TransitioningDown;
+				}
+				else if(mTopScrollBounds.contains(mDragPosition))
+				{
+					if(mScrollState != ScrollState::Up)
+						mScrollState = ScrollState::TransitioningUp;
+				}
+				else
+					mScrollState = ScrollState::None;
+
+				return true;
+			}
+		}
+		else if(event.getType() == GUIMouseEventType::MouseDragAndDropDropped)
+		{
+			if(acceptDragAndDrop())
+			{
+				const GUITreeView::InteractableElement* element = findElementUnderCoord(event.getPosition());
+
+				TreeElement* treeElement = nullptr;
+				if(element != nullptr)
+				{
+					if(element->isTreeElement())
+						treeElement = element->getTreeElement();
+					else
+						treeElement = element->parent;
+				}
+
+				dragAndDropEnded(treeElement);
+				unselectAll();
+
+				return true;
+			}
+		}
+		else if(event.getType() == GUIMouseEventType::MouseOut)
+		{
+			mDragInProgress = false;
+			_markLayoutAsDirty();
+		}
+		else if(event.getType() == GUIMouseEventType::MouseDragAndDropLeft)
+		{
+			mDragInProgress = false;
+			_markLayoutAsDirty();
+		}
+
+		return false;
+	}
+
+	bool GUITreeView::_commandEvent(const GUICommandEvent& ev)
+	{
+		if(ev.getType() == GUICommandEventType::MoveUp || ev.getType() == GUICommandEventType::SelectUp)
+		{
+			TreeElement* topMostElement = getTopMostSelectedElement();
+			auto topMostIter = std::find_if(mVisibleElements.begin(), mVisibleElements.end(), 
+				[&] (const InteractableElement& x) { return x.getTreeElement() == topMostElement; });
+
+			if(topMostIter != mVisibleElements.end() && topMostIter != mVisibleElements.begin())
+			{
+				do 
+				{
+					topMostIter--;
+				} while (!topMostIter->isTreeElement() && topMostIter != mVisibleElements.begin());
+
+				if(topMostIter->isTreeElement())
+				{
+					if(ev.getType() == GUICommandEventType::MoveUp)
+						unselectAll();
+
+					TreeElement* treeElement = topMostIter->getTreeElement();
+					selectElement(treeElement);
+					scrollToElement(treeElement, false);
+				}
+			}
+		}
+		else if(ev.getType() == GUICommandEventType::MoveDown || ev.getType() == GUICommandEventType::SelectDown)
+		{
+			TreeElement* bottoMostElement = getBottomMostSelectedElement();
+			auto bottomMostIter = std::find_if(mVisibleElements.begin(), mVisibleElements.end(), 
+				[&] (const InteractableElement& x) { return x.getTreeElement() == bottoMostElement; });
+
+			if(bottomMostIter != mVisibleElements.end())
+			{
+				do 
+				{
+					bottomMostIter++;
+				} while (bottomMostIter != mVisibleElements.end() && !bottomMostIter->isTreeElement());
+
+				if(bottomMostIter != mVisibleElements.end() && bottomMostIter->isTreeElement())
+				{
+					if(ev.getType() == GUICommandEventType::MoveDown)
+						unselectAll();
+
+					TreeElement* treeElement = bottomMostIter->getTreeElement();
+					selectElement(treeElement);
+					scrollToElement(treeElement, false);
+				}
+			}
+		}
+
+		return GUIElementContainer::_commandEvent(ev);
+	}
+
+	bool GUITreeView::isSelectionActive() const
+	{
+		return mIsElementSelected && mSelectedElements.size() > 0;
+	}
+
+	void GUITreeView::selectElement(TreeElement* element)
+	{
+		clearPing();
+
+		auto iterFind = std::find_if(mSelectedElements.begin(), mSelectedElements.end(), 
+			[&] (const SelectedElement& x) { return x.element == element; });
+
+		if(iterFind == mSelectedElements.end())
+		{
+			GUITexture* background = GUITexture::create(mSelectionBackgroundStyle);
+			background->_setElementDepth(3);
+			_registerChildElement(background);
+
+			element->mIsSelected = true;
+
+			mSelectedElements.push_back(SelectedElement(element, background));
+			mIsElementSelected = true;
+
+			selectionChanged();
+		}
+	}
+
+	void GUITreeView::unselectElement(TreeElement* element)
+	{
+		clearPing();
+
+		auto iterFind = std::find_if(mSelectedElements.begin(), mSelectedElements.end(), 
+			[&] (const SelectedElement& x) { return x.element == element; });
+
+		if(iterFind != mSelectedElements.end())
+		{
+			iterFind->element->mIsSelected = false;
+			GUIElement::destroy(iterFind->background);
+
+			mSelectedElements.erase(iterFind);
+			_markLayoutAsDirty();
+
+			selectionChanged();
+		}
+
+		mIsElementSelected = mSelectedElements.size() > 0;
+	}
+
+	void GUITreeView::unselectAll(bool sendEvent)
+	{
+		clearPing();
+
+		for(auto& selectedElem : mSelectedElements)
+		{
+			selectedElem.element->mIsSelected = false;
+			GUIElement::destroy(selectedElem.background);
+		}
+
+		mSelectedElements.clear();
+		mIsElementSelected = false;
+
+		_markLayoutAsDirty();
+
+		if (sendEvent)
+			selectionChanged();
+	}
+
+	void GUITreeView::renameSelected()
+	{
+		if (isSelectionActive() && mEditElement == nullptr)
+		{
+			clearPing();
+			enableEdit(mSelectedElements[0].element);
+			unselectAll();
+		}
+	}
+
+	void GUITreeView::deleteSelection()
+	{
+		if (isSelectionActive())
+		{
+			auto isChildOf = [&](const TreeElement* parent, const TreeElement* child)
+			{
+				const TreeElement* elem = child;
+
+				while (elem != nullptr && elem != parent)
+					elem = elem->mParent;
+
+				return elem == parent;
+			};
+
+			// Ensure we don't unnecessarily try to delete children if their
+			// parent is getting deleted anyway
+			Vector<TreeElement*> elementsToDelete;
+			for (UINT32 i = 0; i < (UINT32)mSelectedElements.size(); i++)
+			{
+				bool hasDeletedParent = false;
+				for (UINT32 j = 0; j < (UINT32)mSelectedElements.size(); j++)
+				{
+					if (i == j)
+						continue;
+
+					if (isChildOf(mSelectedElements[j].element, mSelectedElements[i].element))
+					{
+						hasDeletedParent = true;
+						break;
+					}
+				}
+
+				if (!hasDeletedParent)
+					elementsToDelete.push_back(mSelectedElements[i].element);
+			}
+
+			clearPing();
+			unselectAll();
+
+			for (auto& elem : elementsToDelete)
+				deleteTreeElement(elem);
+		}
+	}
+
+	void GUITreeView::ping(TreeElement* element)
+	{
+		clearPing();
+
+		expandToElement(element);
+		scrollToElement(element, true);
+
+		GUITexture* background = GUITexture::create(mHighlightBackgroundStyle);
+		background->_setElementDepth(2);
+		_registerChildElement(background);
+
+		element->mIsHighlighted = true;
+
+		mHighlightedElement.element = element;
+		mHighlightedElement.background = background;
+
+		mIsElementHighlighted = true;
+		_markLayoutAsDirty();
+	}
+
+	void GUITreeView::clearPing()
+	{
+		if (!mIsElementHighlighted)
+			return;
+
+		mHighlightedElement.element->mIsHighlighted = false;
+		GUIElement::destroy(mHighlightedElement.background);
+
+		mHighlightedElement.element = nullptr;
+		mHighlightedElement.background = nullptr;
+		mIsElementHighlighted = false;
+
+		_markLayoutAsDirty();
+	}
+
+	void GUITreeView::expandToElement(TreeElement* element)
+	{
+		if (element->mIsVisible || element->mParent == nullptr)
+			return;
+
+		Stack<TreeElement*> todo;
+
+		TreeElement* parent = element->mParent;
+		while (parent != nullptr)
+		{
+			if (!parent->mIsExpanded)
+				todo.push(parent);
+
+			if (parent->mIsVisible)
+				break;
+
+			parent = parent->mParent;
+		}
+
+		while (!todo.empty())
+		{
+			TreeElement* curElement = todo.top();
+			todo.pop();
+
+			expandElement(curElement);
+		}
+	}
+
+	void GUITreeView::expandElement(TreeElement* element)
+	{
+		if(element->mIsExpanded)
+			return;
+
+		element->mIsExpanded = true;
+
+		if(element->mParent == nullptr || (element->mParent->mIsVisible && element->mParent->mIsExpanded))
+		{
+			Stack<TreeElement*> todo;
+			todo.push(element);
+
+			while(!todo.empty())
+			{
+				TreeElement* curElem = todo.top();
+				todo.pop();
+
+				curElem->mIsVisible = true;
+				updateElementGUI(curElem);
+
+				if(curElem->mIsExpanded)
+				{
+					for(auto& child : curElem->mChildren)
+						todo.push(child);
+				}
+			}
+		}
+	}
+
+	void GUITreeView::collapseElement(TreeElement* element)
+	{
+		if(!element->mIsExpanded)
+			return;
+
+		element->mIsExpanded = false;
+		updateElementGUI(element);
+
+		if(element->mParent == nullptr || (element->mParent->mIsVisible && element->mParent->mIsExpanded))
+		{
+			Stack<TreeElement*> todo;
+
+			for(auto& child : element->mChildren)
+				todo.push(child);
+
+			while(!todo.empty())
+			{
+				TreeElement* curElem = todo.top();
+				todo.pop();
+
+				curElem->mIsVisible = false;
+
+				if(curElem->mIsSelected)
+					unselectElement(curElem);
+
+				updateElementGUI(curElem);
+
+				if(curElem->mIsExpanded)
+				{
+					for(auto& child : curElem->mChildren)
+						todo.push(child);
+				}
+			}
+		}
+	}
+
+	void GUITreeView::updateElementGUI(TreeElement* element)
+	{
+		if(element == &getRootElement())
+			return;
+
+		if(element->mIsVisible)
+		{
+			HString name(toWString(element->mName));
+			if(element->mElement == nullptr)
+			{
+				element->mElement = GUILabel::create(name, mElementBtnStyle);
+				_registerChildElement(element->mElement);
+			}
+
+			if (element->mIsCut)
+			{
+				Color cutTint = element->mTint;
+				cutTint.a = CUT_COLOR.a;
+
+				element->mElement->setTint(cutTint);
+			}
+			else if(element->mIsDisabled)
+			{
+				Color disabledTint = element->mTint;
+				disabledTint.a = DISABLED_COLOR.a;
+
+				element->mElement->setTint(disabledTint);
+			}
+			else
+				element->mElement->setTint(element->mTint);
+
+			if(element->mChildren.size() > 0)
+			{
+				if(element->mFoldoutBtn == nullptr)
+				{
+					element->mFoldoutBtn = GUIToggle::create(GUIContent(HString(L"")), mFoldoutBtnStyle);
+					_registerChildElement(element->mFoldoutBtn);
+
+					element->mFoldoutBtn->onToggled.connect(std::bind(&GUITreeView::elementToggled, this, element, _1));
+
+					if(element->mIsExpanded)
+						element->mFoldoutBtn->toggleOn();
+				}
+			}
+			else
+			{
+				if(element->mFoldoutBtn != nullptr)
+				{
+					GUIElement::destroy(element->mFoldoutBtn);
+					element->mFoldoutBtn = nullptr;
+				}
+			}
+
+			element->mElement->setContent(GUIContent(name));
+		}
+		else
+		{
+			if(element->mElement != nullptr)
+			{
+				GUIElement::destroy(element->mElement);
+				element->mElement = nullptr;
+			}
+
+			if(element->mFoldoutBtn != nullptr)
+			{
+				GUIElement::destroy(element->mFoldoutBtn);
+				element->mFoldoutBtn = nullptr;
+			}
+
+			if(element->mIsSelected && element->mIsExpanded)
+				unselectElement(element);
+		}
+
+		_markLayoutAsDirty();
+	}
+
+	void GUITreeView::elementToggled(TreeElement* element, bool toggled)
+	{
+		clearPing();
+
+		if(toggled)
+			expandElement(element);
+		else
+			collapseElement(element);
+	}
+
+	void GUITreeView::onEditAccepted()
+	{
+		TreeElement* elem = mEditElement;
+		disableEdit(true);
+		selectElement(elem);
+	}
+
+	void GUITreeView::onEditCanceled()
+	{
+		if (mEditElement != nullptr)
+		{
+			TreeElement* elem = mEditElement;
+			disableEdit(false);
+			selectElement(elem);
+		}
+	}
+
+	void GUITreeView::onEditFocusLost()
+	{
+		if (mEditElement != nullptr)
+			disableEdit(false);
+	}
+
+	void GUITreeView::enableEdit(TreeElement* element)
+	{
+		assert(mEditElement == nullptr);
+
+		mEditElement = element;
+		mNameEditBox->setVisible(true);
+		mNameEditBox->setText(toWString(element->mName));
+		mNameEditBox->setFocus(true);
+
+		if(element->mElement != nullptr)
+			element->mElement->setVisible(false);
+	}
+
+	void GUITreeView::disableEdit(bool applyChanges)
+	{
+		assert(mEditElement != nullptr);
+
+		if(mEditElement->mElement != nullptr)
+			mEditElement->mElement->setVisible(true);
+
+		if(applyChanges)
+		{
+			WString newName = mNameEditBox->getText();
+			renameTreeElement(mEditElement, newName);
+		}
+
+		mNameEditBox->setFocus(false);
+		mNameEditBox->setVisible(false);
+		mEditElement = nullptr;
+	}
+
+	Vector2I GUITreeView::_getOptimalSize() const
+	{
+		struct UpdateTreeElement
+		{
+			UpdateTreeElement(const TreeElement* element, UINT32 indent)
+				:element(element), indent(indent)
+			{ }
+
+			const TreeElement* element;
+			UINT32 indent;
+		};
+
+		Vector2I optimalSize;
+
+		if (_getDimensions().fixedWidth() && _getDimensions().fixedHeight())
+		{
+			optimalSize.x = _getDimensions().minWidth;
+			optimalSize.y = _getDimensions().minHeight;
+		}
+		else
+		{
+			Stack<UpdateTreeElement> todo;
+			todo.push(UpdateTreeElement(&getRootElementConst(), 0));
+
+			while(!todo.empty())
+			{
+				UpdateTreeElement currentUpdateElement = todo.top();
+				const TreeElement* current = currentUpdateElement.element;
+				todo.pop();
+
+				INT32 yOffset = 0;
+				if(current->mElement != nullptr)
+				{
+					Vector2I curOptimalSize = current->mElement->_getOptimalSize();
+					optimalSize.x = std::max(optimalSize.x, 
+						(INT32)(INITIAL_INDENT_OFFSET + curOptimalSize.x + currentUpdateElement.indent * INDENT_SIZE));
+					yOffset = curOptimalSize.y + ELEMENT_EXTRA_SPACING;
+				}
+
+				optimalSize.y += yOffset;
+
+				for(auto& child : current->mChildren)
+				{
+					if(!child->mIsVisible)
+						continue;
+
+					todo.push(UpdateTreeElement(child, currentUpdateElement.indent + 1));
+				}
+			}
+
+			if(_getDimensions().fixedWidth())
+				optimalSize.x = _getDimensions().minWidth;
+			else
+			{
+				if(_getDimensions().minWidth > 0)
+					optimalSize.x = std::max((INT32)_getDimensions().minWidth, optimalSize.x);
+
+				if(_getDimensions().maxWidth > 0)
+					optimalSize.x = std::min((INT32)_getDimensions().maxWidth, optimalSize.x);
+			}
+
+			if (_getDimensions().fixedHeight())
+				optimalSize.y = _getDimensions().minHeight;
+			else
+			{
+				if(_getDimensions().minHeight > 0)
+					optimalSize.y = std::max((INT32)_getDimensions().minHeight, optimalSize.y);
+
+				if(_getDimensions().maxHeight > 0)
+					optimalSize.y = std::min((INT32)_getDimensions().maxHeight, optimalSize.y);
+			}
+		}
+
+		return optimalSize;
+	}
+
+	void GUITreeView::updateClippedBounds()
+	{
+		mClippedBounds = mLayoutData.area;
+		mClippedBounds.clip(mLayoutData.clipRect);
+	}
+
+	void GUITreeView::_updateLayoutInternal(const GUILayoutData& data)
+	{
+		struct UpdateTreeElement
+		{
+			UpdateTreeElement(TreeElement* element, UINT32 indent)
+				:element(element), indent(indent)
+			{ }
+
+			TreeElement* element;
+			UINT32 indent;
+		};
+
+		mVisibleElements.clear();
+
+		Stack<UpdateTreeElement> todo;
+		todo.push(UpdateTreeElement(&getRootElement(), 0));
+
+		// NOTE - Instead of iterating through all elements, try to find those within the clip rect
+		// and only iterate through those. Others should somehow be marked in-active (similar to GUIElement::isDisabled()?)
+
+		Vector<TreeElement*> tempOrderedElements;
+
+		Vector2I offset(data.area.x, data.area.y);
+
+		while(!todo.empty())
+		{
+			UpdateTreeElement currentUpdateElement = todo.top();
+			TreeElement* current = currentUpdateElement.element;
+			UINT32 indent = currentUpdateElement.indent;
+			todo.pop();
+
+			INT32 btnHeight = 0;
+			INT32 yOffset = 0;
+			if(current->mElement != nullptr)
+			{
+				Vector2I elementSize = current->mElement->_getOptimalSize();
+				btnHeight = elementSize.y;
+
+				mVisibleElements.push_back(InteractableElement(current->mParent, current->mSortedIdx * 2 + 0, Rect2I(data.area.x, offset.y, data.area.width, ELEMENT_EXTRA_SPACING)));
+				mVisibleElements.push_back(InteractableElement(current->mParent, current->mSortedIdx * 2 + 1, Rect2I(data.area.x, offset.y + ELEMENT_EXTRA_SPACING, data.area.width, btnHeight)));
+
+				offset.x = data.area.x + INITIAL_INDENT_OFFSET + indent * INDENT_SIZE;
+				offset.y += ELEMENT_EXTRA_SPACING;
+
+				GUILayoutData childData = data;
+				childData.area.x = offset.x;
+				childData.area.y = offset.y;
+				childData.area.width = elementSize.x;
+				childData.area.height = elementSize.y;
+
+				current->mElement->_setLayoutData(childData);
+
+				yOffset = btnHeight;
+			}
+
+			if(current->mFoldoutBtn != nullptr)
+			{
+				Vector2I elementSize = current->mFoldoutBtn->_getOptimalSize();
+
+				offset.x -= std::min((INT32)INITIAL_INDENT_OFFSET, elementSize.x + 2);
+
+				Vector2I myOffset = offset;
+				myOffset.y += 1;
+
+				if(elementSize.y > btnHeight)
+				{
+					UINT32 diff = elementSize.y - btnHeight;
+					float half = diff * 0.5f;
+					myOffset.y -= Math::floorToInt(half);
+				}
+
+				GUILayoutData childData = data;
+				childData.area.x = myOffset.x;
+				childData.area.y = myOffset.y;
+				childData.area.width = elementSize.x;
+				childData.area.height = elementSize.y;
+
+				current->mFoldoutBtn->_setLayoutData(childData);
+			}
+
+			offset.y += yOffset;
+
+			tempOrderedElements.resize(current->mChildren.size(), nullptr);
+			for(auto& child : current->mChildren)
+			{
+				tempOrderedElements[child->mSortedIdx] = child;
+			}
+
+			for(auto iter = tempOrderedElements.rbegin(); iter != tempOrderedElements.rend(); ++iter)
+			{
+				TreeElement* child = *iter;
+
+				if(!child->mIsVisible)
+					continue;
+
+				todo.push(UpdateTreeElement(child, indent + 1));
+			}
+		}
+
+		UINT32 remainingHeight = (UINT32)std::max(0, (INT32)data.area.height - (offset.y - data.area.y));
+
+		if(remainingHeight > 0)
+			mVisibleElements.push_back(InteractableElement(&getRootElement(), (UINT32)getRootElement().mChildren.size() * 2, Rect2I(data.area.x, offset.y, data.area.width, remainingHeight)));
+
+		for(auto selectedElem : mSelectedElements)
+		{
+			GUILabel* targetElement = selectedElem.element->mElement;
+
+			GUILayoutData childData = data;
+			childData.area.y = targetElement->_getLayoutData().area.y;
+			childData.area.height = targetElement->_getLayoutData().area.height;
+
+			selectedElem.background->_setLayoutData(childData);
+		}
+
+		if (mIsElementHighlighted)
+		{
+			GUILabel* targetElement = mHighlightedElement.element->mElement;
+
+			GUILayoutData childData = data;
+			childData.area.y = targetElement->_getLayoutData().area.y;
+			childData.area.height = targetElement->_getLayoutData().area.height;
+
+			mHighlightedElement.background->_setLayoutData(childData);
+		}
+
+		if(mEditElement != nullptr)
+		{
+			GUILabel* targetElement = mEditElement->mElement;
+
+			UINT32 remainingWidth = (UINT32)std::max(0, (((INT32)data.area.width) - (offset.x - data.area.x)));
+
+			GUILayoutData childData = data;
+			childData.area = targetElement->_getLayoutData().area;
+			childData.area.width = remainingWidth;
+
+			mNameEditBox->_setLayoutData(childData);
+		}
+
+		if(mDragInProgress)
+		{
+			const InteractableElement* interactableElement = findElementUnderCoord(mDragPosition);
+
+			if(interactableElement == nullptr)
+			{
+				mDragHighlight->setVisible(false);
+				mDragSepHighlight->setVisible(false);
+			}
+			else
+			{
+				if(interactableElement->isTreeElement())
+				{
+					mDragSepHighlight->setVisible(false);
+					mDragHighlight->setVisible(true);
+
+					GUILayoutData childData = data;
+					childData.area = interactableElement->bounds;
+
+					mDragHighlight->_setLayoutData(childData);
+				}
+				else
+				{
+					mDragHighlight->setVisible(false);
+					mDragSepHighlight->setVisible(true);
+
+					GUILayoutData childData = data;
+					childData.area = interactableElement->bounds;
+
+					mDragSepHighlight->_setLayoutData(childData);
+				}
+			}
+		}
+		else
+		{
+			mDragHighlight->setVisible(false);
+			mDragSepHighlight->setVisible(false);
+		}
+
+		// Update scroll bounds
+		UINT32 scrollHeight = (UINT32)Math::roundToInt(data.clipRect.height * SCROLL_AREA_HEIGHT_PCT);
+
+		mTopScrollBounds.x = data.clipRect.x;
+		mTopScrollBounds.y = data.clipRect.y;
+		mTopScrollBounds.width = data.clipRect.width;
+		mTopScrollBounds.height = scrollHeight;
+
+		mBottomScrollBounds.x = data.clipRect.x;
+		mBottomScrollBounds.y = data.clipRect.y + data.clipRect.height - scrollHeight;
+		mBottomScrollBounds.width = data.clipRect.width;
+		mBottomScrollBounds.height = scrollHeight;
+	}
+
+	const GUITreeView::InteractableElement* GUITreeView::findElementUnderCoord(const Vector2I& coord) const
+	{
+		for(auto& element : mVisibleElements)
+		{
+			if(element.bounds.contains(coord))
+			{
+				return &element;
+			}
+		}
+
+		return nullptr;
+	}
+
+	GUITreeView::TreeElement* GUITreeView::getTopMostSelectedElement() const
+	{
+		auto topMostElement = mVisibleElements.end();
+
+		for(auto& selectedElement : mSelectedElements)
+		{
+			auto iterFind = std::find_if(mVisibleElements.begin(), mVisibleElements.end(), 
+				[&] (const InteractableElement& x) { return x.getTreeElement() == selectedElement.element; });
+
+			if(iterFind != mVisibleElements.end())
+			{
+				if(topMostElement == mVisibleElements.end())
+					topMostElement = iterFind;
+				else
+				{
+					if(iterFind->bounds.y < topMostElement->bounds.y)
+						topMostElement = iterFind;
+				}
+			}
+		}
+
+		if(topMostElement != mVisibleElements.end())
+			return topMostElement->getTreeElement();
+		else
+			return nullptr;
+	}
+
+	GUITreeView::TreeElement* GUITreeView::getBottomMostSelectedElement() const
+	{
+		auto& botMostElement = mVisibleElements.end();
+
+		for(auto& selectedElement : mSelectedElements)
+		{
+			auto iterFind = std::find_if(mVisibleElements.begin(), mVisibleElements.end(), 
+				[&] (const InteractableElement& x) { return x.getTreeElement() == selectedElement.element; });
+
+			if(iterFind != mVisibleElements.end())
+			{
+				if(botMostElement == mVisibleElements.end())
+					botMostElement = iterFind;
+				else
+				{
+					if((iterFind->bounds.y + iterFind->bounds.height) > (botMostElement->bounds.y + botMostElement->bounds.height))
+						botMostElement = iterFind;
+				}
+			}
+		}
+
+		if(botMostElement != mVisibleElements.end())
+			return botMostElement->getTreeElement();
+		else
+			return nullptr;
+	}
+
+	void GUITreeView::closeTemporarilyExpandedElements()
+	{
+		temporarilyExpandElement(nullptr);
+	}
+
+	void GUITreeView::temporarilyExpandElement(const GUITreeView::InteractableElement* mouseOverElement)
+	{
+		TreeElement* treeElement = nullptr;
+		if(mouseOverElement != nullptr && mouseOverElement->isTreeElement())
+			treeElement = mouseOverElement->getTreeElement();
+
+		if(treeElement == nullptr || treeElement != mMouseOverDragElement)
+		{
+			while(!mAutoExpandedElements.empty())
+			{
+				TreeElement* autoExpandedElement = mAutoExpandedElements.top();
+
+				bool unexpandElement = false;
+				if(mouseOverElement != nullptr && mouseOverElement->parent != nullptr)
+				{
+					if(mouseOverElement->parent != autoExpandedElement && !mouseOverElement->parent->isParentRec(autoExpandedElement))
+						unexpandElement = true;
+					else
+						break;
+				}
+				else
+					unexpandElement = true;
+
+				if(unexpandElement)
+				{
+					collapseElement(autoExpandedElement);
+
+					if(autoExpandedElement->mFoldoutBtn != nullptr)
+						autoExpandedElement->mFoldoutBtn->toggleOff();
+
+					mAutoExpandedElements.pop();
+				}
+			}
+
+			mMouseOverDragElement = treeElement;
+			mMouseOverDragElementTime = gTime().getTime();
+		}
+		else
+		{
+			if(mMouseOverDragElement != nullptr && !mMouseOverDragElement->mIsExpanded)
+			{
+				float timeDiff = gTime().getTime() - mMouseOverDragElementTime;
+				if(timeDiff >= AUTO_EXPAND_DELAY_SEC)
+				{
+					mAutoExpandedElements.push(mMouseOverDragElement);
+					expandElement(mMouseOverDragElement);
+
+					if(mMouseOverDragElement->mFoldoutBtn != nullptr)
+						mMouseOverDragElement->mFoldoutBtn->toggleOn();
+				}
+			}
+		}
+	}
+
+	void GUITreeView::scrollToElement(TreeElement* element, bool center)
+	{
+		if(element->mElement == nullptr)
+			return;
+
+		GUIScrollArea* scrollArea = findParentScrollArea();
+		if(scrollArea == nullptr)
+			return;
+
+		if(center)
+		{
+			Rect2I myBounds = _getClippedBounds();
+			INT32 clipVertCenter = myBounds.y + (INT32)Math::roundToInt(myBounds.height * 0.5f);
+			INT32 elemVertCenter = element->mElement->_getLayoutData().area.y + (INT32)Math::roundToInt(element->mElement->_getLayoutData().area.height * 0.5f);
+
+			if(elemVertCenter > clipVertCenter)
+				scrollArea->scrollDownPx(elemVertCenter - clipVertCenter);
+			else
+				scrollArea->scrollUpPx(clipVertCenter - elemVertCenter);
+		}
+		else
+		{
+			Rect2I myBounds = _getClippedBounds();
+			INT32 elemVertTop = element->mElement->_getLayoutData().area.y;
+			INT32 elemVertBottom = element->mElement->_getLayoutData().area.y + element->mElement->_getLayoutData().area.height;
+
+			INT32 top = myBounds.y;
+			INT32 bottom = myBounds.y + myBounds.height;
+
+			INT32 offset = 0;
+			if(elemVertTop < top)
+				scrollArea->scrollUpPx(top - elemVertTop);
+			else if(elemVertBottom > bottom)
+				scrollArea->scrollDownPx(elemVertBottom - bottom);
+		}
+	}
+
+	GUIScrollArea* GUITreeView::findParentScrollArea() const
+	{
+		GUIElementBase* parent = _getParent();
+		while(parent != nullptr)
+		{
+			if(parent->_getType() == GUIElementBase::Type::Element)
+			{
+				GUIElement* parentElement = static_cast<GUIElement*>(parent);
+
+				if(parentElement->_getElementType() == GUIElement::ElementType::ScrollArea)
+				{
+					GUIScrollArea* scrollArea = static_cast<GUIScrollArea*>(parentElement);
+					return scrollArea;
+
+				}
+			}
+
+			parent = parent->_getParent();
+		}
+
+		return nullptr;
+	}
+
+	const String& GUITreeView::getGUITypeName()
+	{
+		static String typeName = "SceneTreeView";
+		return typeName;
+	}
 }

+ 996 - 906
Source/MBansheeEditor/EditorApplication.cs

@@ -1,906 +1,996 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-using System;
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
-using System.IO;
-using BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Available tools in the scene view.
-    /// </summary>
-    public enum SceneViewTool
-    {
-        View,
-        Move,
-        Rotate,
-        Scale
-    }
-
-    /// <summary>
-    /// Pivot mode used by the scene view tools.
-    /// </summary>
-    public enum HandlePivotMode
-    {
-        Center,
-        Pivot
-    }
-
-    /// <summary>
-    /// Coordinate mode used by the scene view tools.
-    /// </summary>
-    public enum HandleCoordinateMode
-    {
-        Local,
-        World
-    }
-
-    /// <summary>
-    /// Manages various generic and global settings relating to the editor.
-    /// </summary>
-    public class EditorApplication
-    {
-        /// <summary>
-        /// Determines the active tool shown in the scene view.
-        /// </summary>
-        public static SceneViewTool ActiveSceneTool
-        {
-            get { return EditorSettings.ActiveSceneTool; }
-            set { EditorSettings.ActiveSceneTool = value; }
-        }
-
-        /// <summary>
-        /// Determines the coordinate mode used by the tools in the scene view.
-        /// </summary>
-        public static HandleCoordinateMode ActiveCoordinateMode
-        {
-            get { return EditorSettings.ActiveCoordinateMode; }
-            set { EditorSettings.ActiveCoordinateMode = value; }
-        }
-
-        /// <summary>
-        /// Determines the pivot mode used by the tools in the scene view.
-        /// </summary>
-        public static HandlePivotMode ActivePivotMode
-        {
-            get { return EditorSettings.ActivePivotMode; }
-            set { EditorSettings.ActivePivotMode = value; }
-        }
-
-        /// <summary>
-        /// Camera used for rendering the scene view.
-        /// </summary>
-        public static Camera SceneViewCamera
-        {
-            get { return EditorWindow.GetWindow<SceneWindow>().Camera; }
-        }
-
-        /// <summary>
-        /// Absolute path to the folder containing the currently open project.
-        /// </summary>
-        public static string ProjectPath { get { return Internal_GetProjectPath(); } }
-
-        /// <summary>
-        /// Name of the currently open project.
-        /// </summary>
-        public static string ProjectName { get { return Internal_GetProjectName(); } }
-
-        /// <summary>
-        /// Checks is any project currently loaded.
-        /// </summary>
-        public static bool IsProjectLoaded { get { return Internal_GetProjectLoaded(); } }
-
-        /// <summary>
-        /// Determines is the game currently running in the editor, or is it stopped or paused. Setting this value to false
-        /// will stop the game, but if you just want to pause it use <see cref="IsPaused"/> property.
-        /// </summary>
-        public static bool IsPlaying
-        {
-            get { return Internal_GetIsPlaying(); }
-            set
-            {
-                ToggleToolbarItem("Play", value);
-                ToggleToolbarItem("Pause", false);
-
-                if (!value)
-                    Selection.SceneObject = null;
-                else
-                {
-                    if (EditorSettings.GetBool(LogWindow.CLEAR_ON_PLAY_KEY, true))
-                    {
-                        Debug.Clear();
-
-                        LogWindow log = EditorWindow.GetWindow<LogWindow>();
-                        if (log != null)
-                            log.Refresh();
-                    }
-                }
-
-                Internal_SetIsPlaying(value);
-            }
-        }
-
-        /// <summary>
-        /// Determines if the game is currently running in the editor, but paused. If the game is stopped and not running
-        /// this will return false. If the game is not running and this is enabled, the game will start running but be 
-        /// immediately paused.
-        /// </summary>
-        public static bool IsPaused
-        {
-            get { return Internal_GetIsPaused(); }
-            set
-            {
-                ToggleToolbarItem("Play", !value);
-                ToggleToolbarItem("Pause", value);
-                Internal_SetIsPaused(value);
-            }
-        }
-
-        /// <summary>
-        /// Returns true if the game is currently neither running nor paused. Use <see cref="IsPlaying"/> or 
-        /// <see cref="IsPaused"/> to actually change these states.
-        /// </summary>
-        public static bool IsStopped
-        {
-            get { return !IsPlaying && !IsPaused; }
-        }
-
-        /// <summary>
-        /// Checks whether the editor currently has focus.
-        /// </summary>
-        public static bool HasFocus
-        {
-            get { return Internal_HasFocus(); }
-        }
-
-        /// <summary>
-        /// Render target that the main camera in the scene (if any) will render its view to. This generally means the main 
-        /// game window when running standalone, or the Game viewport when running in editor.
-        /// </summary>
-        internal static RenderTarget MainRenderTarget
-        {
-            set
-            {
-                IntPtr rtPtr = IntPtr.Zero;
-                if (value != null)
-                    rtPtr = value.GetCachedPtr();
-
-                Internal_SetMainRenderTarget(rtPtr);
-            }
-        }
-
-        /// <summary>
-        /// Returns the path where the script compiler is located at.
-        /// </summary>
-        internal static string CompilerPath { get { return Internal_GetCompilerPath(); } }
-
-        /// <summary>
-        /// Returns the path to the folder where the custom script assemblies are located at.
-        /// </summary>
-        internal static string ScriptAssemblyPath { get { return Internal_GetScriptAssemblyPath(); } }
-
-        /// <summary>
-        /// Returns the path to the folder where the .NET framework assemblies are located at.
-        /// </summary>
-        internal static string FrameworkAssemblyPath { get { return Internal_GetFrameworkAssemblyPath(); } }
-
-        /// <summary>
-        /// Name of the builtin assembly containing engine specific types.
-        /// </summary>
-        internal static string EngineAssemblyName { get { return Internal_GetEngineAssemblyName(); } }
-
-        /// <summary>
-        /// Name of the builtin assembly containing editor specific types.
-        /// </summary>
-        internal static string EditorAssemblyName { get { return Internal_GetEditorAssemblyName(); } }
-
-        /// <summary>
-        /// Name of the custom assembly compiled from non-editor scripts within the project.
-        /// </summary>
-        internal static string ScriptGameAssemblyName { get { return Internal_GetScriptGameAssemblyName(); } }
-
-        /// <summary>
-        /// Name of the custom assembly compiled from editor scripts within the project.
-        /// </summary>
-        internal static string ScriptEditorAssemblyName { get { return Internal_GetScriptEditorAssemblyName(); } }
-
-        /// <summary>
-        /// Returns the path to the folder where the builtin release script assemblies are located at.
-        /// </summary>
-        internal static string BuiltinReleaseAssemblyPath { get { return Internal_GetBuiltinReleaseAssemblyPath(); } }
-
-        /// <summary>
-        /// Returns the path to the folder where the builtin debug script assemblies are located at.
-        /// </summary>
-        internal static string BuiltinDebugAssemblyPath { get { return Internal_GetBuiltinDebugAssemblyPath(); } }
-
-        private static EditorApplication instance;
-        private static FolderMonitor monitor;
-        private static ScriptCodeManager codeManager;
-        private static bool sceneDirty;
-        private static bool unitTestsExecuted;
-        private static EditorPersistentData persistentData;
-
-        /// <summary>
-        /// Constructs a new editor application. Called at editor start-up by the runtime, and any time assembly refresh
-        /// occurrs.
-        /// </summary>
-        internal EditorApplication()
-        {
-            instance = this;
-            codeManager = new ScriptCodeManager();
-
-            const string soName = "EditorPersistentData";
-            SceneObject so = Scene.Root.FindChild(soName);
-            if (so == null)
-                so = new SceneObject(soName, true);
-
-            persistentData = so.GetComponent<EditorPersistentData>();
-            if (persistentData == null)
-                persistentData = so.AddComponent<EditorPersistentData>();
-
-            // Register controls
-            InputConfiguration inputConfig = VirtualInput.KeyConfig;
-
-            inputConfig.RegisterButton(SceneCamera.MoveForwardBinding, ButtonCode.W);
-            inputConfig.RegisterButton(SceneCamera.MoveBackBinding, ButtonCode.S);
-            inputConfig.RegisterButton(SceneCamera.MoveLeftBinding, ButtonCode.A);
-            inputConfig.RegisterButton(SceneCamera.MoveRightBinding, ButtonCode.D);
-            inputConfig.RegisterButton(SceneCamera.MoveUpBinding, ButtonCode.E);
-            inputConfig.RegisterButton(SceneCamera.MoveDownBinding, ButtonCode.Q);
-            inputConfig.RegisterButton(SceneCamera.MoveForwardBinding, ButtonCode.Up);
-            inputConfig.RegisterButton(SceneCamera.MoveBackBinding, ButtonCode.Down);
-            inputConfig.RegisterButton(SceneCamera.MoveLeftBinding, ButtonCode.Left);
-            inputConfig.RegisterButton(SceneCamera.MoveRightBinding, ButtonCode.Right);
-            inputConfig.RegisterButton(SceneCamera.FastMoveBinding, ButtonCode.LeftShift);
-            inputConfig.RegisterButton(SceneCamera.RotateBinding, ButtonCode.MouseRight);
-            inputConfig.RegisterButton(SceneCamera.PanBinding, ButtonCode.MouseMiddle);
-            inputConfig.RegisterAxis(SceneCamera.HorizontalAxisBinding, InputAxis.MouseX);
-            inputConfig.RegisterAxis(SceneCamera.VerticalAxisBinding, InputAxis.MouseY);
-            inputConfig.RegisterAxis(SceneCamera.ScrollAxisBinding, InputAxis.MouseZ);
-
-            inputConfig.RegisterButton(SceneWindow.ToggleProfilerOverlayBinding, ButtonCode.P, ButtonModifier.CtrlAlt);
-            inputConfig.RegisterButton(SceneWindow.ViewToolBinding, ButtonCode.Q);
-            inputConfig.RegisterButton(SceneWindow.FrameBinding, ButtonCode.F);
-            inputConfig.RegisterButton(SceneWindow.MoveToolBinding, ButtonCode.W);
-            inputConfig.RegisterButton(SceneWindow.RotateToolBinding, ButtonCode.E);
-            inputConfig.RegisterButton(SceneWindow.ScaleToolBinding, ButtonCode.R);
-            inputConfig.RegisterButton(SceneWindow.DuplicateBinding, ButtonCode.D, ButtonModifier.Ctrl);
-
-            if (IsProjectLoaded)
-            {
-                monitor = new FolderMonitor(ProjectLibrary.ResourceFolder);
-                monitor.OnAdded += OnAssetModified;
-                monitor.OnRemoved += OnAssetModified;
-                monitor.OnModified += OnAssetModified;
-            }
-        }
-
-        /// <summary>
-        /// Triggered when the folder monitor detects an asset in the monitored folder was modified.
-        /// </summary>
-        /// <param name="path">Path to the modified file or folder.</param>
-        private static void OnAssetModified(string path)
-        {
-            ProjectLibrary.Refresh(path);
-        }
-
-        /// <summary>
-        /// Called 60 times per second by the runtime.
-        /// </summary>
-        internal void OnEditorUpdate()
-        {
-            ProjectLibrary.Update();
-            codeManager.Update();
-        }
-
-        /// <summary>
-        /// Creates a new empty scene.
-        /// </summary>
-        [MenuItem("File/New Scene", 10051, true)]
-        private static void NewScene()
-        {
-            LoadScene(null);
-        }
-
-        /// <summary>
-        /// Opens a dialog that allows the user to select a new prefab to load as the current scene. If current scene
-        /// is modified the user is offered a chance to save it.
-        /// </summary>
-        [MenuItem("File/Open Scene", ButtonModifier.Ctrl, ButtonCode.L, 10050)]
-        private static void LoadScene()
-        {
-            string[] scenePaths;
-            if (BrowseDialog.OpenFile(ProjectLibrary.ResourceFolder, "", false, out scenePaths))
-            {
-                if (scenePaths.Length > 0)
-                    LoadScene(scenePaths[0]);
-            }
-        }
-
-        /// <summary>
-        /// Opens a dialog to allows the user to select a location where to save the current scene. If scene was previously
-        /// saved it is instead automatically saved at the last location.
-        /// </summary>
-        public static void SaveScene(Action onSuccess = null, Action onFailure = null)
-        {
-            if (!string.IsNullOrEmpty(Scene.ActiveSceneUUID))
-            {
-                string scenePath = ProjectLibrary.GetPath(Scene.ActiveSceneUUID);
-                if (!string.IsNullOrEmpty(scenePath))
-                {
-                    SaveScene(scenePath);
-
-                    if (onSuccess != null)
-                        onSuccess();
-                }
-                else
-                    SaveSceneAs(onSuccess, onFailure);
-            }
-            else
-                SaveSceneAs(onSuccess, onFailure);
-        }
-
-        /// <summary>
-        /// Opens a dialog to allows the user to select a location where to save the current scene.
-        /// </summary>
-        public static void SaveSceneAs(Action onSuccess = null, Action onFailure = null)
-        {
-            string scenePath = "";
-            if (BrowseDialog.SaveFile(ProjectLibrary.ResourceFolder, "*.prefab", out scenePath))
-            {
-                if (!PathEx.IsPartOf(scenePath, ProjectLibrary.ResourceFolder))
-                {
-                    DialogBox.Open("Error", "The location must be inside the Resources folder of the project.",
-                        DialogBox.Type.OK,
-                        x =>
-                        {
-                            if (onFailure != null)
-                                onFailure();
-                        });
-                }
-                else
-                {
-                    // TODO - If path points to an existing non-scene asset or folder I should delete it otherwise
-                    //        Internal_SaveScene will silently fail.
-
-                    scenePath = Path.ChangeExtension(scenePath, ".prefab");
-                    SaveScene(scenePath);
-                }
-            }
-            else
-            {
-                // User canceled, so technically a success
-                if (onSuccess != null)
-                    onSuccess();
-            }
-        }
-
-        /// <summary>
-        /// Loads a prefab as the current scene at the specified path. If current scene is modified the user is offered a 
-        /// chance to save it.
-        /// </summary>
-        /// <param name="path">Path to a valid prefab relative to the resource folder. If path is empty a brand new
-        ///                    scene will be loaded.</param>
-        public static void LoadScene(string path)
-        {
-            Action<string> continueLoad =
-                (scenePath) =>
-                {
-                    if (string.IsNullOrEmpty(path))
-                        Scene.Clear();
-                    else
-                        Scene.Load(path);
-
-                    SetSceneDirty(false);
-
-                    ProjectSettings.LastOpenScene = scenePath;
-                    ProjectSettings.Save();
-                };
-
-            Action<DialogBox.ResultType> dialogCallback =
-            (result) =>
-            {
-                if (result == DialogBox.ResultType.Yes)
-                {
-                    SaveScene();
-                    continueLoad(path);
-                }
-                else if (result == DialogBox.ResultType.No)
-                    continueLoad(path);
-            };
-
-            if (IsSceneModified())
-            {
-                DialogBox.Open("Warning", "You current scene has modifications. Do you wish to save them first?",
-                    DialogBox.Type.YesNoCancel, dialogCallback);
-            }
-            else
-                continueLoad(path);
-        }
-
-        /// <summary>
-        /// Saves the currently loaded scene to the specified path.
-        /// </summary>
-        /// <param name="path">Path relative to the resource folder. This can be the path to the existing scene
-        ///                    prefab if it just needs updating. </param>
-        public static void SaveScene(string path)
-        {
-            Prefab scene = Internal_SaveScene(path);
-            Scene.SetActive(scene);
-
-            ProjectLibrary.Refresh(true);
-            SetSceneDirty(false);
-        }
-
-        /// <summary>
-        /// Checks does the folder at the provieded path contain a valid project.
-        /// </summary>
-        /// <param name="path">Absolute path to the root project folder.</param>
-        /// <returns>True if the folder contains a valid project.</returns>
-        public static bool IsValidProject(string path)
-        {
-            return Internal_IsValidProject(path);
-        }
-
-        /// <summary>
-        /// Contains a new project in the provided folder.
-        /// </summary>
-        /// <param name="path">Absolute path to the folder to create the project in. Name of this folder will be used as the
-        ///                    project's name.</param>
-        public static void CreateProject(string path)
-        {
-            Internal_CreateProject(path);
-        }
-
-        /// <summary>
-        /// Wrapper for menu items for <see cref="SaveScene(Action, Action)"/> method
-        /// </summary>
-        [MenuItem("File/Save Scene", ButtonModifier.Ctrl, ButtonCode.S, 10049)]
-        [ToolbarItem("Save Scene", ToolbarIcon.SaveScene, "Save scene (Ctrl + S)", 1998)]
-        private static void SaveSceneMenu()
-        {
-            SaveScene();
-        }
-
-        /// <summary>
-        /// Wrapper for menu items for <see cref="SaveSceneAs(Action, Action)"/> method
-        /// </summary>
-        [MenuItem("File/Save Scene As", 10048)]
-        private static void SaveSceneAsMenu()
-        {
-            SaveSceneAs();
-        }
-
-        /// <summary>
-        /// Opens a Project Window allowing you to browse for or create a project.
-        /// </summary>
-        [MenuItem("File/Open Project", 10100)]
-        [ToolbarItem("Open Project", ToolbarIcon.OpenProject, "Project manager", 2000)]
-        public static void BrowseForProject()
-        {
-            ProjectWindow.Open();
-        }
-
-        /// <summary>
-        /// Saves all data in the currently open project.
-        /// </summary>
-        [MenuItem("File/Save Project", 10099)]
-        [ToolbarItem("Save Project", ToolbarIcon.SaveProject, "Save project", 1999)]
-        public static void SaveProject()
-        {
-            foreach (var KVP in persistentData.dirtyResources)
-            {
-                string resourceUUID = KVP.Key;
-                string path = ProjectLibrary.GetPath(resourceUUID);
-                if (!IsNative(path))
-                    continue; // Native resources can't be changed
-
-                Resource resource = ProjectLibrary.Load<Resource>(path);
-
-                if(resource != null)
-                    ProjectLibrary.Save(resource);
-            }
-
-            persistentData.dirtyResources.Clear();
-            SetStatusProject(false);
-
-            Internal_SaveProject();
-        }
-
-        /// <summary>
-        /// Loads the project at the specified path. This method executes asynchronously.
-        /// </summary>
-        /// <param name="path">Absolute path to the project's root folder.</param>
-        public static void LoadProject(string path)
-        {
-            if (IsProjectLoaded && path == ProjectPath)
-                return;
-
-            if (!Internal_IsValidProject(path))
-            {
-                Debug.LogWarning("Provided path: \"" + path + "\" is not a valid project.");
-                return;
-            }
-
-            if (IsProjectLoaded)
-                UnloadProject();
-
-            Internal_LoadProject(path); // Triggers Internal_OnProjectLoaded when done
-        }
-
-        /// <summary>
-        /// Closes the editor.
-        /// </summary>
-        public static void Quit()
-        {
-            Internal_Quit();
-        }
-
-        /// <summary>
-        /// Toggles an existing toolbar button into an on or off state which changes the visuals of the button.
-        /// </summary>
-        /// <param name="name">Name of the existing button to toggle</param>
-        /// <param name="on">True to toggle on, false to toggle off (default)</param>
-        public static void ToggleToolbarItem(string name, bool on)
-        {
-            Internal_ToggleToolbarItem(name, on);
-        }
-
-        /// <summary>
-        /// Opens a file or a folder in the default external application.
-        /// </summary>
-        /// <param name="path">Absolute path to the file or folder to open.</param>
-        public static void OpenExternally(string path)
-        {
-            Internal_OpenExternally(path);
-        }
-
-        /// <summary>
-        /// Marks a resource as dirty so that it may be saved the next time the project is saved. Optionally you may also
-        /// call <see cref="ProjectLibrary.Save"/> to save it immediately.
-        /// </summary>
-        /// <param name="resource">Resource to mark as dirty</param>
-        public static void SetDirty(Resource resource)
-        {
-            if (resource == null)
-                return;
-
-            SetStatusProject(true);
-            persistentData.dirtyResources[resource.UUID] = true;
-        }
-
-        /// <summary>
-        /// Marks the current scene as dirty.
-        /// </summary>
-        public static void SetSceneDirty()
-        {
-            SetSceneDirty(true);
-        }
-
-        /// <summary>
-        /// Marks the current scene as clean or dirty.
-        /// </summary>
-        /// <param name="dirty">Should the scene be marked as clean or dirty.</param>
-        internal static void SetSceneDirty(bool dirty)
-        {
-            sceneDirty = dirty;
-            SetStatusScene(Scene.ActiveSceneName, dirty);
-
-            if (!dirty && Scene.ActiveSceneUUID != null)
-                persistentData.dirtyResources.Remove(Scene.ActiveSceneUUID);
-        }
-
-        /// <summary>
-        /// Checks is the specific resource dirty and needs saving.
-        /// </summary>
-        /// <param name="resource">Resource to check.</param>
-        /// <returns>True if the resource requires saving, false otherwise.</returns>
-        public static bool IsDirty(Resource resource)
-        {
-            return persistentData.dirtyResources.ContainsKey(resource.UUID);
-        }
-
-        /// <summary>
-        /// Checks does the path represent a native resource.
-        /// </summary>
-        /// <param name="path">Filename or path to check.</param>
-        /// <returns>True if the path represents a native resource.</returns>
-        public static bool IsNative(string path)
-        {
-            string extension = Path.GetExtension(path);
-
-            return extension == ".asset" || extension == ".prefab";
-        }
-
-        /// <summary>
-        /// Unloads the currently loaded project. Offers the user a chance to save the current scene if it is modified.
-        /// Automatically saves all project data before unloading.
-        /// </summary>
-        private static void UnloadProject()
-        {
-            Action continueUnload =
-                () =>
-                {
-                    Scene.Clear();
-
-                    if (monitor != null)
-                    {
-                        monitor.Destroy();
-                        monitor = null;
-                    }
-
-                    LibraryWindow window = EditorWindow.GetWindow<LibraryWindow>();
-                    if(window != null)
-                        window.Reset();
-
-                    SetSceneDirty(false);
-                    Internal_UnloadProject();
-                    SetStatusProject(false);
-                };
-
-            Action<DialogBox.ResultType> dialogCallback =
-            (result) =>
-            {
-                if (result == DialogBox.ResultType.Yes)
-                    SaveScene();
-
-                continueUnload();
-            };
-
-            if (IsSceneModified())
-            {
-                DialogBox.Open("Warning", "You current scene has modifications. Do you wish to save them first?",
-                    DialogBox.Type.YesNoCancel, dialogCallback);
-            }
-            else
-                continueUnload();
-        }
-
-        /// <summary>
-        /// Reloads all script assemblies in case they were modified. This action is delayed and will be executed
-        /// at the beginning of the next frame.
-        /// </summary>
-        public static void ReloadAssemblies()
-        {
-            Internal_ReloadAssemblies();
-        }
-
-        /// <summary>
-        /// Changes the scene displayed on the status bar.
-        /// </summary>
-        /// <param name="name">Name of the scene.</param>
-        /// <param name="modified">Whether to display the scene as modified or not.</param>
-        private static void SetStatusScene(string name, bool modified)
-        {
-            Internal_SetStatusScene(name, modified);
-        }
-
-        /// <summary>
-        /// Changes the project state displayed on the status bar.
-        /// </summary>
-        /// <param name="modified">Whether to display the project as modified or not.</param>
-        private static void SetStatusProject(bool modified)
-        {
-            Internal_SetStatusProject(modified);
-        }
-
-        /// <summary>
-        /// Displays or hides the "compilation in progress" visual on the status bar.
-        /// </summary>
-        /// <param name="compiling">True to display the visual, false otherwise.</param>
-        internal static void SetStatusCompiling(bool compiling)
-        {
-            Internal_SetStatusCompiling(compiling);
-        }
-
-        /// <summary>
-        /// Checks did we make any modifications to the scene since it was last saved.
-        /// </summary>
-        /// <returns>True if the scene was never saved, or was modified after last save.</returns>
-        public static bool IsSceneModified()
-        {
-            return sceneDirty;
-        }
-
-        /// <summary>
-        /// Runs a single frame of the game and pauses it. If the game is not currently running it will be started.
-        /// </summary>
-        public static void FrameStep()
-        {
-            if (IsStopped)
-            {
-                if (EditorSettings.GetBool(LogWindow.CLEAR_ON_PLAY_KEY, true))
-                {
-                    Debug.Clear();
-
-                    LogWindow log = EditorWindow.GetWindow<LogWindow>();
-                    if (log != null)
-                        log.Refresh();
-                }
-            }
-
-            ToggleToolbarItem("Play", false);
-            ToggleToolbarItem("Pause", true);
-            Internal_FrameStep();
-        }
-
-        /// <summary>
-        /// Executes any editor-specific unit tests. This should be called after a project is loaded if possible.
-        /// </summary>
-        private static void RunUnitTests()
-        {
-#if DEBUG
-            Internal_RunUnitTests();
-#endif
-        }
-
-        /// <summary>
-        /// Triggered by the runtime when <see cref="LoadProject"/> method completes.
-        /// </summary>
-        private static void Internal_OnProjectLoaded()
-        {
-            SetStatusProject(false);
-            if (!unitTestsExecuted)
-            {
-                RunUnitTests();
-                unitTestsExecuted = true;
-            }
-
-            if (!IsProjectLoaded)
-            {
-                ProjectWindow.Open();
-                return;
-            }
-
-            string projectPath = ProjectPath;
-
-            RecentProject[] recentProjects = EditorSettings.RecentProjects;
-            bool foundPath = false;
-            for (int i = 0; i < recentProjects.Length; i++)
-            {
-                if (PathEx.Compare(recentProjects[i].path, projectPath))
-                {
-                    recentProjects[i].accessTimestamp = (ulong)DateTime.Now.Ticks;
-                    EditorSettings.RecentProjects = recentProjects;
-                    foundPath = true;
-                    break;
-                }
-            }
-
-            if (!foundPath)
-            {
-                List<RecentProject> extendedRecentProjects = new List<RecentProject>();
-                extendedRecentProjects.AddRange(recentProjects);
-
-                RecentProject newProject = new RecentProject();
-                newProject.path = projectPath;
-                newProject.accessTimestamp = (ulong)DateTime.Now.Ticks;
-
-                extendedRecentProjects.Add(newProject);
-
-                EditorSettings.RecentProjects = extendedRecentProjects.ToArray();
-            }
-
-            EditorSettings.LastOpenProject = projectPath;
-            EditorSettings.Save();
-
-            ProjectLibrary.Refresh();
-            monitor = new FolderMonitor(ProjectLibrary.ResourceFolder);
-            monitor.OnAdded += OnAssetModified;
-            monitor.OnRemoved += OnAssetModified;
-            monitor.OnModified += OnAssetModified;
-
-            if (!string.IsNullOrWhiteSpace(ProjectSettings.LastOpenScene))
-            {
-                Scene.Load(ProjectSettings.LastOpenScene);
-                SetSceneDirty(false);
-            }
-        }
-
-        /// <summary>
-        /// Triggered by the runtime when the user clicks on the status bar.
-        /// </summary>
-        private static void Internal_OnStatusBarClicked()
-        {
-            EditorWindow.OpenWindow<LogWindow>();
-        }
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetStatusScene(string name, bool modified);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetStatusProject(bool modified);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetStatusCompiling(bool compiling);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern string Internal_GetProjectPath();
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern string Internal_GetProjectName();
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern bool Internal_GetProjectLoaded();
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern string Internal_GetCompilerPath();
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern string Internal_GetBuiltinReleaseAssemblyPath();
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern string Internal_GetBuiltinDebugAssemblyPath();
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern string Internal_GetScriptAssemblyPath();
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern string Internal_GetFrameworkAssemblyPath();
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern string Internal_GetEngineAssemblyName();
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern string Internal_GetEditorAssemblyName();
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern string Internal_GetScriptGameAssemblyName();
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern string Internal_GetScriptEditorAssemblyName();
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern Prefab Internal_SaveScene(string path);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern bool Internal_IsValidProject(string path);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SaveProject();
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_LoadProject(string path);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_UnloadProject();
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_CreateProject(string path);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_ReloadAssemblies();
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_OpenExternally(string path);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_RunUnitTests();
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_Quit();
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_ToggleToolbarItem(string name, bool on);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern bool Internal_GetIsPlaying();
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetIsPlaying(bool value);
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern bool Internal_GetIsPaused();
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetIsPaused(bool value);
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_FrameStep();
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetMainRenderTarget(IntPtr rendertarget);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern bool Internal_HasFocus();
-    }
-}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.IO;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Available tools in the scene view.
+    /// </summary>
+    public enum SceneViewTool
+    {
+        View,
+        Move,
+        Rotate,
+        Scale
+    }
+
+    /// <summary>
+    /// Pivot mode used by the scene view tools.
+    /// </summary>
+    public enum HandlePivotMode
+    {
+        Center,
+        Pivot
+    }
+
+    /// <summary>
+    /// Coordinate mode used by the scene view tools.
+    /// </summary>
+    public enum HandleCoordinateMode
+    {
+        Local,
+        World
+    }
+
+    /// <summary>
+    /// Manages various generic and global settings relating to the editor.
+    /// </summary>
+    public class EditorApplication
+    {
+        internal const string CutBinding = "Cut";
+        internal const string CopyBinding = "Copy";
+        internal const string RenameBinding = "Rename";
+        internal const string DuplicateBinding = "Duplicate";
+        internal const string DeleteBinding = "Delete";
+        internal const string PasteBinding = "Paste";
+
+        /// <summary>
+        /// Determines the active tool shown in the scene view.
+        /// </summary>
+        public static SceneViewTool ActiveSceneTool
+        {
+            get { return EditorSettings.ActiveSceneTool; }
+            set { EditorSettings.ActiveSceneTool = value; }
+        }
+
+        /// <summary>
+        /// Determines the coordinate mode used by the tools in the scene view.
+        /// </summary>
+        public static HandleCoordinateMode ActiveCoordinateMode
+        {
+            get { return EditorSettings.ActiveCoordinateMode; }
+            set { EditorSettings.ActiveCoordinateMode = value; }
+        }
+
+        /// <summary>
+        /// Determines the pivot mode used by the tools in the scene view.
+        /// </summary>
+        public static HandlePivotMode ActivePivotMode
+        {
+            get { return EditorSettings.ActivePivotMode; }
+            set { EditorSettings.ActivePivotMode = value; }
+        }
+
+        /// <summary>
+        /// Camera used for rendering the scene view.
+        /// </summary>
+        public static Camera SceneViewCamera
+        {
+            get { return EditorWindow.GetWindow<SceneWindow>().Camera; }
+        }
+
+        /// <summary>
+        /// Absolute path to the folder containing the currently open project.
+        /// </summary>
+        public static string ProjectPath { get { return Internal_GetProjectPath(); } }
+
+        /// <summary>
+        /// Name of the currently open project.
+        /// </summary>
+        public static string ProjectName { get { return Internal_GetProjectName(); } }
+
+        /// <summary>
+        /// Checks is any project currently loaded.
+        /// </summary>
+        public static bool IsProjectLoaded { get { return Internal_GetProjectLoaded(); } }
+
+        /// <summary>
+        /// Determines is the game currently running in the editor, or is it stopped or paused. Setting this value to false
+        /// will stop the game, but if you just want to pause it use <see cref="IsPaused"/> property.
+        /// </summary>
+        public static bool IsPlaying
+        {
+            get { return Internal_GetIsPlaying(); }
+            set
+            {
+                ToggleToolbarItem("Play", value);
+                ToggleToolbarItem("Pause", false);
+
+                if (!value)
+                    Selection.SceneObject = null;
+                else
+                {
+                    if (EditorSettings.GetBool(LogWindow.CLEAR_ON_PLAY_KEY, true))
+                    {
+                        Debug.Clear();
+
+                        LogWindow log = EditorWindow.GetWindow<LogWindow>();
+                        if (log != null)
+                            log.Refresh();
+                    }
+                }
+
+                Internal_SetIsPlaying(value);
+            }
+        }
+
+        /// <summary>
+        /// Determines if the game is currently running in the editor, but paused. If the game is stopped and not running
+        /// this will return false. If the game is not running and this is enabled, the game will start running but be 
+        /// immediately paused.
+        /// </summary>
+        public static bool IsPaused
+        {
+            get { return Internal_GetIsPaused(); }
+            set
+            {
+                ToggleToolbarItem("Play", !value);
+                ToggleToolbarItem("Pause", value);
+                Internal_SetIsPaused(value);
+            }
+        }
+
+        /// <summary>
+        /// Returns true if the game is currently neither running nor paused. Use <see cref="IsPlaying"/> or 
+        /// <see cref="IsPaused"/> to actually change these states.
+        /// </summary>
+        public static bool IsStopped
+        {
+            get { return !IsPlaying && !IsPaused; }
+        }
+
+        /// <summary>
+        /// Checks whether the editor currently has focus.
+        /// </summary>
+        public static bool HasFocus
+        {
+            get { return Internal_HasFocus(); }
+        }
+
+        /// <summary>
+        /// Render target that the main camera in the scene (if any) will render its view to. This generally means the main 
+        /// game window when running standalone, or the Game viewport when running in editor.
+        /// </summary>
+        internal static RenderTarget MainRenderTarget
+        {
+            set
+            {
+                IntPtr rtPtr = IntPtr.Zero;
+                if (value != null)
+                    rtPtr = value.GetCachedPtr();
+
+                Internal_SetMainRenderTarget(rtPtr);
+            }
+        }
+
+        /// <summary>
+        /// Returns the path where the script compiler is located at.
+        /// </summary>
+        internal static string CompilerPath { get { return Internal_GetCompilerPath(); } }
+
+        /// <summary>
+        /// Returns the path to the folder where the custom script assemblies are located at.
+        /// </summary>
+        internal static string ScriptAssemblyPath { get { return Internal_GetScriptAssemblyPath(); } }
+
+        /// <summary>
+        /// Returns the path to the folder where the .NET framework assemblies are located at.
+        /// </summary>
+        internal static string FrameworkAssemblyPath { get { return Internal_GetFrameworkAssemblyPath(); } }
+
+        /// <summary>
+        /// Name of the builtin assembly containing engine specific types.
+        /// </summary>
+        internal static string EngineAssemblyName { get { return Internal_GetEngineAssemblyName(); } }
+
+        /// <summary>
+        /// Name of the builtin assembly containing editor specific types.
+        /// </summary>
+        internal static string EditorAssemblyName { get { return Internal_GetEditorAssemblyName(); } }
+
+        /// <summary>
+        /// Name of the custom assembly compiled from non-editor scripts within the project.
+        /// </summary>
+        internal static string ScriptGameAssemblyName { get { return Internal_GetScriptGameAssemblyName(); } }
+
+        /// <summary>
+        /// Name of the custom assembly compiled from editor scripts within the project.
+        /// </summary>
+        internal static string ScriptEditorAssemblyName { get { return Internal_GetScriptEditorAssemblyName(); } }
+
+        /// <summary>
+        /// Returns the path to the folder where the builtin release script assemblies are located at.
+        /// </summary>
+        internal static string BuiltinReleaseAssemblyPath { get { return Internal_GetBuiltinReleaseAssemblyPath(); } }
+
+        /// <summary>
+        /// Returns the path to the folder where the builtin debug script assemblies are located at.
+        /// </summary>
+        internal static string BuiltinDebugAssemblyPath { get { return Internal_GetBuiltinDebugAssemblyPath(); } }
+
+        internal static VirtualButton CutKey = new VirtualButton(CutBinding);
+        internal static VirtualButton CopyKey = new VirtualButton(CopyBinding);
+        internal static VirtualButton PasteKey = new VirtualButton(PasteBinding);
+        internal static VirtualButton RenameKey = new VirtualButton(RenameBinding);
+        internal static VirtualButton DuplicateKey = new VirtualButton(DuplicateBinding);
+        internal static VirtualButton DeleteKey = new VirtualButton(DeleteBinding);
+
+        private static EditorApplication instance;
+        private static FolderMonitor monitor;
+        private static ScriptCodeManager codeManager;
+        private static bool sceneDirty;
+        private static bool unitTestsExecuted;
+        private static EditorPersistentData persistentData;
+
+        /// <summary>
+        /// Constructs a new editor application. Called at editor start-up by the runtime, and any time assembly refresh
+        /// occurrs.
+        /// </summary>
+        internal EditorApplication()
+        {
+            instance = this;
+            codeManager = new ScriptCodeManager();
+
+            const string soName = "EditorPersistentData";
+            SceneObject so = Scene.Root.FindChild(soName);
+            if (so == null)
+                so = new SceneObject(soName, true);
+
+            persistentData = so.GetComponent<EditorPersistentData>();
+            if (persistentData == null)
+                persistentData = so.AddComponent<EditorPersistentData>();
+
+            // Register controls
+            InputConfiguration inputConfig = VirtualInput.KeyConfig;
+
+            inputConfig.RegisterButton(SceneCamera.MoveForwardBinding, ButtonCode.W);
+            inputConfig.RegisterButton(SceneCamera.MoveBackBinding, ButtonCode.S);
+            inputConfig.RegisterButton(SceneCamera.MoveLeftBinding, ButtonCode.A);
+            inputConfig.RegisterButton(SceneCamera.MoveRightBinding, ButtonCode.D);
+            inputConfig.RegisterButton(SceneCamera.MoveUpBinding, ButtonCode.E);
+            inputConfig.RegisterButton(SceneCamera.MoveDownBinding, ButtonCode.Q);
+            inputConfig.RegisterButton(SceneCamera.MoveForwardBinding, ButtonCode.Up);
+            inputConfig.RegisterButton(SceneCamera.MoveBackBinding, ButtonCode.Down);
+            inputConfig.RegisterButton(SceneCamera.MoveLeftBinding, ButtonCode.Left);
+            inputConfig.RegisterButton(SceneCamera.MoveRightBinding, ButtonCode.Right);
+            inputConfig.RegisterButton(SceneCamera.FastMoveBinding, ButtonCode.LeftShift);
+            inputConfig.RegisterButton(SceneCamera.RotateBinding, ButtonCode.MouseRight);
+            inputConfig.RegisterButton(SceneCamera.PanBinding, ButtonCode.MouseMiddle);
+            inputConfig.RegisterAxis(SceneCamera.HorizontalAxisBinding, InputAxis.MouseX);
+            inputConfig.RegisterAxis(SceneCamera.VerticalAxisBinding, InputAxis.MouseY);
+            inputConfig.RegisterAxis(SceneCamera.ScrollAxisBinding, InputAxis.MouseZ);
+
+            inputConfig.RegisterButton(SceneWindow.ToggleProfilerOverlayBinding, ButtonCode.P, ButtonModifier.CtrlAlt);
+            inputConfig.RegisterButton(SceneWindow.ViewToolBinding, ButtonCode.Q);
+            inputConfig.RegisterButton(SceneWindow.FrameBinding, ButtonCode.F);
+            inputConfig.RegisterButton(SceneWindow.MoveToolBinding, ButtonCode.W);
+            inputConfig.RegisterButton(SceneWindow.RotateToolBinding, ButtonCode.E);
+            inputConfig.RegisterButton(SceneWindow.ScaleToolBinding, ButtonCode.R);
+
+            inputConfig.RegisterButton(CutBinding, ButtonCode.X, ButtonModifier.Ctrl);
+            inputConfig.RegisterButton(CopyBinding, ButtonCode.C, ButtonModifier.Ctrl);
+            inputConfig.RegisterButton(PasteBinding, ButtonCode.V, ButtonModifier.Ctrl);
+            inputConfig.RegisterButton(DuplicateBinding, ButtonCode.D, ButtonModifier.Ctrl);
+            inputConfig.RegisterButton(DeleteBinding, ButtonCode.Delete);
+            inputConfig.RegisterButton(RenameBinding, ButtonCode.F2);
+
+            if (IsProjectLoaded)
+            {
+                monitor = new FolderMonitor(ProjectLibrary.ResourceFolder);
+                monitor.OnAdded += OnAssetModified;
+                monitor.OnRemoved += OnAssetModified;
+                monitor.OnModified += OnAssetModified;
+            }
+
+            EditorVirtualInput.OnButtonUp += OnButtonUp;
+        }
+
+        /// <summary>
+        /// Triggered when the folder monitor detects an asset in the monitored folder was modified.
+        /// </summary>
+        /// <param name="path">Path to the modified file or folder.</param>
+        private static void OnAssetModified(string path)
+        {
+            ProjectLibrary.Refresh(path);
+        }
+
+
+        /// <summary>
+        /// Triggered when the user presses a virtual button.
+        /// </summary>
+        /// <param name="btn">Button that was pressed.</param>
+        /// <param name="deviceIdx">Index of the device it was pressed on. </param>
+        private static void OnButtonUp(VirtualButton btn, int deviceIdx)
+        {
+            TriggerGlobalShortcut(btn);
+        }
+
+        /// <summary>
+        /// Called every frame by the runtime.
+        /// </summary>
+        internal void OnEditorUpdate()
+        {
+            // Update managers
+            ProjectLibrary.Update();
+            codeManager.Update();
+        }
+
+        /// <summary>
+        /// Manually triggers a global shortcut.
+        /// </summary>
+        /// <param name="btn">Button for the shortcut. If this doesn't correspond to any shortcut, it is ignored.</param>
+        internal static void TriggerGlobalShortcut(VirtualButton btn)
+        {
+            IGlobalShortcuts window = null;
+
+            if (btn != PasteKey)
+            {
+                // The system ensures elsewhere that only either a resource or a scene object is selected, but not both
+                if (Selection.ResourceUUIDs.Length > 0)
+                {
+                    window = EditorWindow.GetWindow<LibraryWindow>();
+                }
+                else if (Selection.SceneObjects.Length > 0)
+                {
+                    window = EditorWindow.GetWindow<HierarchyWindow>();
+                    if (window == null)
+                        window = EditorWindow.GetWindow<SceneWindow>();
+                }
+
+                if (window != null)
+                {
+                    if (btn == CopyKey)
+                        window.OnCopyPressed();
+                    else if (btn == CutKey)
+                        window.OnCutPressed();
+                    else if (btn == PasteKey)
+                        window.OnPastePressed();
+                    else if (btn == DuplicateKey)
+                        window.OnDuplicatePressed();
+                    else if (btn == RenameKey)
+                        window.OnRenamePressed();
+                    else if (btn == DeleteKey)
+                        window.OnDeletePressed();
+                }
+            }
+            else
+            {
+
+                HierarchyWindow hierarchy = EditorWindow.GetWindow<HierarchyWindow>();
+                if (hierarchy != null && hierarchy.HasFocus)
+                    window = hierarchy;
+                else
+                {
+                    LibraryWindow library = EditorWindow.GetWindow<LibraryWindow>();
+                    if (library != null && library.HasFocus)
+                        window = library;
+                }
+
+                if (window != null)
+                    window.OnPastePressed();
+            }
+        }
+
+        /// <summary>
+        /// Creates a new empty scene.
+        /// </summary>
+        [MenuItem("File/New Scene", 10051, true)]
+        private static void NewScene()
+        {
+            LoadScene(null);
+        }
+
+        /// <summary>
+        /// Opens a dialog that allows the user to select a new prefab to load as the current scene. If current scene
+        /// is modified the user is offered a chance to save it.
+        /// </summary>
+        [MenuItem("File/Open Scene", ButtonModifier.Ctrl, ButtonCode.L, 10050)]
+        private static void LoadScene()
+        {
+            string[] scenePaths;
+            if (BrowseDialog.OpenFile(ProjectLibrary.ResourceFolder, "", false, out scenePaths))
+            {
+                if (scenePaths.Length > 0)
+                    LoadScene(scenePaths[0]);
+            }
+        }
+
+        /// <summary>
+        /// Opens a dialog to allows the user to select a location where to save the current scene. If scene was previously
+        /// saved it is instead automatically saved at the last location.
+        /// </summary>
+        public static void SaveScene(Action onSuccess = null, Action onFailure = null)
+        {
+            if (!string.IsNullOrEmpty(Scene.ActiveSceneUUID))
+            {
+                string scenePath = ProjectLibrary.GetPath(Scene.ActiveSceneUUID);
+                if (!string.IsNullOrEmpty(scenePath))
+                {
+                    SaveScene(scenePath);
+
+                    if (onSuccess != null)
+                        onSuccess();
+                }
+                else
+                    SaveSceneAs(onSuccess, onFailure);
+            }
+            else
+                SaveSceneAs(onSuccess, onFailure);
+        }
+
+        /// <summary>
+        /// Opens a dialog to allows the user to select a location where to save the current scene.
+        /// </summary>
+        public static void SaveSceneAs(Action onSuccess = null, Action onFailure = null)
+        {
+            string scenePath = "";
+            if (BrowseDialog.SaveFile(ProjectLibrary.ResourceFolder, "*.prefab", out scenePath))
+            {
+                if (!PathEx.IsPartOf(scenePath, ProjectLibrary.ResourceFolder))
+                {
+                    DialogBox.Open("Error", "The location must be inside the Resources folder of the project.",
+                        DialogBox.Type.OK,
+                        x =>
+                        {
+                            if (onFailure != null)
+                                onFailure();
+                        });
+                }
+                else
+                {
+                    // TODO - If path points to an existing non-scene asset or folder I should delete it otherwise
+                    //        Internal_SaveScene will silently fail.
+
+                    scenePath = Path.ChangeExtension(scenePath, ".prefab");
+                    SaveScene(scenePath);
+                }
+            }
+            else
+            {
+                // User canceled, so technically a success
+                if (onSuccess != null)
+                    onSuccess();
+            }
+        }
+
+        /// <summary>
+        /// Loads a prefab as the current scene at the specified path. If current scene is modified the user is offered a 
+        /// chance to save it.
+        /// </summary>
+        /// <param name="path">Path to a valid prefab relative to the resource folder. If path is empty a brand new
+        ///                    scene will be loaded.</param>
+        public static void LoadScene(string path)
+        {
+            Action<string> continueLoad =
+                (scenePath) =>
+                {
+                    if (string.IsNullOrEmpty(path))
+                        Scene.Clear();
+                    else
+                        Scene.Load(path);
+
+                    SetSceneDirty(false);
+
+                    ProjectSettings.LastOpenScene = scenePath;
+                    ProjectSettings.Save();
+                };
+
+            Action<DialogBox.ResultType> dialogCallback =
+            (result) =>
+            {
+                if (result == DialogBox.ResultType.Yes)
+                {
+                    SaveScene();
+                    continueLoad(path);
+                }
+                else if (result == DialogBox.ResultType.No)
+                    continueLoad(path);
+            };
+
+            if (IsSceneModified())
+            {
+                DialogBox.Open("Warning", "You current scene has modifications. Do you wish to save them first?",
+                    DialogBox.Type.YesNoCancel, dialogCallback);
+            }
+            else
+                continueLoad(path);
+        }
+
+        /// <summary>
+        /// Saves the currently loaded scene to the specified path.
+        /// </summary>
+        /// <param name="path">Path relative to the resource folder. This can be the path to the existing scene
+        ///                    prefab if it just needs updating. </param>
+        public static void SaveScene(string path)
+        {
+            Prefab scene = Internal_SaveScene(path);
+            Scene.SetActive(scene);
+
+            ProjectLibrary.Refresh(true);
+            SetSceneDirty(false);
+        }
+
+        /// <summary>
+        /// Checks does the folder at the provieded path contain a valid project.
+        /// </summary>
+        /// <param name="path">Absolute path to the root project folder.</param>
+        /// <returns>True if the folder contains a valid project.</returns>
+        public static bool IsValidProject(string path)
+        {
+            return Internal_IsValidProject(path);
+        }
+
+        /// <summary>
+        /// Contains a new project in the provided folder.
+        /// </summary>
+        /// <param name="path">Absolute path to the folder to create the project in. Name of this folder will be used as the
+        ///                    project's name.</param>
+        public static void CreateProject(string path)
+        {
+            Internal_CreateProject(path);
+        }
+
+        /// <summary>
+        /// Wrapper for menu items for <see cref="SaveScene(Action, Action)"/> method
+        /// </summary>
+        [MenuItem("File/Save Scene", ButtonModifier.Ctrl, ButtonCode.S, 10049)]
+        [ToolbarItem("Save Scene", ToolbarIcon.SaveScene, "Save scene (Ctrl + S)", 1998)]
+        private static void SaveSceneMenu()
+        {
+            SaveScene();
+        }
+
+        /// <summary>
+        /// Wrapper for menu items for <see cref="SaveSceneAs(Action, Action)"/> method
+        /// </summary>
+        [MenuItem("File/Save Scene As", 10048)]
+        private static void SaveSceneAsMenu()
+        {
+            SaveSceneAs();
+        }
+
+        /// <summary>
+        /// Opens a Project Window allowing you to browse for or create a project.
+        /// </summary>
+        [MenuItem("File/Open Project", 10100)]
+        [ToolbarItem("Open Project", ToolbarIcon.OpenProject, "Project manager", 2000)]
+        public static void BrowseForProject()
+        {
+            ProjectWindow.Open();
+        }
+
+        /// <summary>
+        /// Saves all data in the currently open project.
+        /// </summary>
+        [MenuItem("File/Save Project", 10099)]
+        [ToolbarItem("Save Project", ToolbarIcon.SaveProject, "Save project", 1999)]
+        public static void SaveProject()
+        {
+            foreach (var KVP in persistentData.dirtyResources)
+            {
+                string resourceUUID = KVP.Key;
+                string path = ProjectLibrary.GetPath(resourceUUID);
+                if (!IsNative(path))
+                    continue; // Native resources can't be changed
+
+                Resource resource = ProjectLibrary.Load<Resource>(path);
+
+                if(resource != null)
+                    ProjectLibrary.Save(resource);
+            }
+
+            persistentData.dirtyResources.Clear();
+            SetStatusProject(false);
+
+            Internal_SaveProject();
+        }
+
+        /// <summary>
+        /// Loads the project at the specified path. This method executes asynchronously.
+        /// </summary>
+        /// <param name="path">Absolute path to the project's root folder.</param>
+        public static void LoadProject(string path)
+        {
+            if (IsProjectLoaded && path == ProjectPath)
+                return;
+
+            if (!Internal_IsValidProject(path))
+            {
+                Debug.LogWarning("Provided path: \"" + path + "\" is not a valid project.");
+                return;
+            }
+
+            if (IsProjectLoaded)
+                UnloadProject();
+
+            Internal_LoadProject(path); // Triggers Internal_OnProjectLoaded when done
+        }
+
+        /// <summary>
+        /// Closes the editor.
+        /// </summary>
+        public static void Quit()
+        {
+            Internal_Quit();
+        }
+
+        /// <summary>
+        /// Toggles an existing toolbar button into an on or off state which changes the visuals of the button.
+        /// </summary>
+        /// <param name="name">Name of the existing button to toggle</param>
+        /// <param name="on">True to toggle on, false to toggle off (default)</param>
+        public static void ToggleToolbarItem(string name, bool on)
+        {
+            Internal_ToggleToolbarItem(name, on);
+        }
+
+        /// <summary>
+        /// Opens a file or a folder in the default external application.
+        /// </summary>
+        /// <param name="path">Absolute path to the file or folder to open.</param>
+        public static void OpenExternally(string path)
+        {
+            Internal_OpenExternally(path);
+        }
+
+        /// <summary>
+        /// Marks a resource as dirty so that it may be saved the next time the project is saved. Optionally you may also
+        /// call <see cref="ProjectLibrary.Save"/> to save it immediately.
+        /// </summary>
+        /// <param name="resource">Resource to mark as dirty</param>
+        public static void SetDirty(Resource resource)
+        {
+            if (resource == null)
+                return;
+
+            SetStatusProject(true);
+            persistentData.dirtyResources[resource.UUID] = true;
+        }
+
+        /// <summary>
+        /// Marks the current scene as dirty.
+        /// </summary>
+        public static void SetSceneDirty()
+        {
+            SetSceneDirty(true);
+        }
+
+        /// <summary>
+        /// Marks the current scene as clean or dirty.
+        /// </summary>
+        /// <param name="dirty">Should the scene be marked as clean or dirty.</param>
+        internal static void SetSceneDirty(bool dirty)
+        {
+            sceneDirty = dirty;
+            SetStatusScene(Scene.ActiveSceneName, dirty);
+
+            if (!dirty && Scene.ActiveSceneUUID != null)
+                persistentData.dirtyResources.Remove(Scene.ActiveSceneUUID);
+        }
+
+        /// <summary>
+        /// Checks is the specific resource dirty and needs saving.
+        /// </summary>
+        /// <param name="resource">Resource to check.</param>
+        /// <returns>True if the resource requires saving, false otherwise.</returns>
+        public static bool IsDirty(Resource resource)
+        {
+            return persistentData.dirtyResources.ContainsKey(resource.UUID);
+        }
+
+        /// <summary>
+        /// Checks does the path represent a native resource.
+        /// </summary>
+        /// <param name="path">Filename or path to check.</param>
+        /// <returns>True if the path represents a native resource.</returns>
+        public static bool IsNative(string path)
+        {
+            string extension = Path.GetExtension(path);
+
+            return extension == ".asset" || extension == ".prefab";
+        }
+
+        /// <summary>
+        /// Unloads the currently loaded project. Offers the user a chance to save the current scene if it is modified.
+        /// Automatically saves all project data before unloading.
+        /// </summary>
+        private static void UnloadProject()
+        {
+            Action continueUnload =
+                () =>
+                {
+                    Scene.Clear();
+
+                    if (monitor != null)
+                    {
+                        monitor.Destroy();
+                        monitor = null;
+                    }
+
+                    LibraryWindow window = EditorWindow.GetWindow<LibraryWindow>();
+                    if(window != null)
+                        window.Reset();
+
+                    SetSceneDirty(false);
+                    Internal_UnloadProject();
+                    SetStatusProject(false);
+                };
+
+            Action<DialogBox.ResultType> dialogCallback =
+            (result) =>
+            {
+                if (result == DialogBox.ResultType.Yes)
+                    SaveScene();
+
+                continueUnload();
+            };
+
+            if (IsSceneModified())
+            {
+                DialogBox.Open("Warning", "You current scene has modifications. Do you wish to save them first?",
+                    DialogBox.Type.YesNoCancel, dialogCallback);
+            }
+            else
+                continueUnload();
+        }
+
+        /// <summary>
+        /// Reloads all script assemblies in case they were modified. This action is delayed and will be executed
+        /// at the beginning of the next frame.
+        /// </summary>
+        public static void ReloadAssemblies()
+        {
+            Internal_ReloadAssemblies();
+        }
+
+        /// <summary>
+        /// Changes the scene displayed on the status bar.
+        /// </summary>
+        /// <param name="name">Name of the scene.</param>
+        /// <param name="modified">Whether to display the scene as modified or not.</param>
+        private static void SetStatusScene(string name, bool modified)
+        {
+            Internal_SetStatusScene(name, modified);
+        }
+
+        /// <summary>
+        /// Changes the project state displayed on the status bar.
+        /// </summary>
+        /// <param name="modified">Whether to display the project as modified or not.</param>
+        private static void SetStatusProject(bool modified)
+        {
+            Internal_SetStatusProject(modified);
+        }
+
+        /// <summary>
+        /// Displays or hides the "compilation in progress" visual on the status bar.
+        /// </summary>
+        /// <param name="compiling">True to display the visual, false otherwise.</param>
+        internal static void SetStatusCompiling(bool compiling)
+        {
+            Internal_SetStatusCompiling(compiling);
+        }
+
+        /// <summary>
+        /// Checks did we make any modifications to the scene since it was last saved.
+        /// </summary>
+        /// <returns>True if the scene was never saved, or was modified after last save.</returns>
+        public static bool IsSceneModified()
+        {
+            return sceneDirty;
+        }
+
+        /// <summary>
+        /// Runs a single frame of the game and pauses it. If the game is not currently running it will be started.
+        /// </summary>
+        public static void FrameStep()
+        {
+            if (IsStopped)
+            {
+                if (EditorSettings.GetBool(LogWindow.CLEAR_ON_PLAY_KEY, true))
+                {
+                    Debug.Clear();
+
+                    LogWindow log = EditorWindow.GetWindow<LogWindow>();
+                    if (log != null)
+                        log.Refresh();
+                }
+            }
+
+            ToggleToolbarItem("Play", false);
+            ToggleToolbarItem("Pause", true);
+            Internal_FrameStep();
+        }
+
+        /// <summary>
+        /// Executes any editor-specific unit tests. This should be called after a project is loaded if possible.
+        /// </summary>
+        private static void RunUnitTests()
+        {
+#if DEBUG
+            Internal_RunUnitTests();
+#endif
+        }
+
+        /// <summary>
+        /// Triggered by the runtime when <see cref="LoadProject"/> method completes.
+        /// </summary>
+        private static void Internal_OnProjectLoaded()
+        {
+            SetStatusProject(false);
+            if (!unitTestsExecuted)
+            {
+                RunUnitTests();
+                unitTestsExecuted = true;
+            }
+
+            if (!IsProjectLoaded)
+            {
+                ProjectWindow.Open();
+                return;
+            }
+
+            string projectPath = ProjectPath;
+
+            RecentProject[] recentProjects = EditorSettings.RecentProjects;
+            bool foundPath = false;
+            for (int i = 0; i < recentProjects.Length; i++)
+            {
+                if (PathEx.Compare(recentProjects[i].path, projectPath))
+                {
+                    recentProjects[i].accessTimestamp = (ulong)DateTime.Now.Ticks;
+                    EditorSettings.RecentProjects = recentProjects;
+                    foundPath = true;
+                    break;
+                }
+            }
+
+            if (!foundPath)
+            {
+                List<RecentProject> extendedRecentProjects = new List<RecentProject>();
+                extendedRecentProjects.AddRange(recentProjects);
+
+                RecentProject newProject = new RecentProject();
+                newProject.path = projectPath;
+                newProject.accessTimestamp = (ulong)DateTime.Now.Ticks;
+
+                extendedRecentProjects.Add(newProject);
+
+                EditorSettings.RecentProjects = extendedRecentProjects.ToArray();
+            }
+
+            EditorSettings.LastOpenProject = projectPath;
+            EditorSettings.Save();
+
+            ProjectLibrary.Refresh();
+            monitor = new FolderMonitor(ProjectLibrary.ResourceFolder);
+            monitor.OnAdded += OnAssetModified;
+            monitor.OnRemoved += OnAssetModified;
+            monitor.OnModified += OnAssetModified;
+
+            if (!string.IsNullOrWhiteSpace(ProjectSettings.LastOpenScene))
+            {
+                Scene.Load(ProjectSettings.LastOpenScene);
+                SetSceneDirty(false);
+            }
+        }
+
+        /// <summary>
+        /// Triggered by the runtime when the user clicks on the status bar.
+        /// </summary>
+        private static void Internal_OnStatusBarClicked()
+        {
+            EditorWindow.OpenWindow<LogWindow>();
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetStatusScene(string name, bool modified);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetStatusProject(bool modified);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetStatusCompiling(bool compiling);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string Internal_GetProjectPath();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string Internal_GetProjectName();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool Internal_GetProjectLoaded();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string Internal_GetCompilerPath();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string Internal_GetBuiltinReleaseAssemblyPath();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string Internal_GetBuiltinDebugAssemblyPath();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string Internal_GetScriptAssemblyPath();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string Internal_GetFrameworkAssemblyPath();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string Internal_GetEngineAssemblyName();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string Internal_GetEditorAssemblyName();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string Internal_GetScriptGameAssemblyName();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string Internal_GetScriptEditorAssemblyName();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern Prefab Internal_SaveScene(string path);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool Internal_IsValidProject(string path);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SaveProject();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_LoadProject(string path);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_UnloadProject();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_CreateProject(string path);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_ReloadAssemblies();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_OpenExternally(string path);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_RunUnitTests();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_Quit();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_ToggleToolbarItem(string name, bool on);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool Internal_GetIsPlaying();
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetIsPlaying(bool value);
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool Internal_GetIsPaused();
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetIsPaused(bool value);
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_FrameStep();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetMainRenderTarget(IntPtr rendertarget);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool Internal_HasFocus();
+    }
+}

+ 180 - 169
Source/MBansheeEditor/GUI/GUISceneTreeView.cs

@@ -1,169 +1,180 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Runtime.CompilerServices;
-using BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// GUI element that displays all scene objects in the current scene as a tree view.
-    /// </summary>
-    public sealed class GUISceneTreeView : GUIElement
-    {
-        /// <summary>
-        /// Creates a new scene tree view element.
-        /// </summary>
-        /// <param name="style">Optional style to use for the element. Style controls the look of the element, as well as 
-        ///                     default layout options. Style will be retrieved from the active GUISkin. If not specified 
-        ///                     default element style is used.</param>
-        /// <param name="options">Options that allow you to control how is the element  positioned and sized. This will 
-        ///                       override any similar options set by style.</param>
-        public GUISceneTreeView(string style = "", params GUIOption[] options)
-        {
-            Internal_CreateInstance(this, style, options);
-        }
-
-        /// <summary>
-        /// Creates a new scene tree view element.
-        /// </summary>
-        /// <param name="options">Options that allow you to control how is the element  positioned and sized. This will 
-        ///                       override any similar options set by style.</param>
-        public GUISceneTreeView(params GUIOption[] options)
-        {
-            Internal_CreateInstance(this, "", options);
-        }
-
-        /// <summary>
-        /// Updates the contents of the tree view with most recent scene data. Should be called once per frame.
-        /// </summary>
-        public void Update()
-        {
-            Internal_Update(mCachedPtr);
-        }
-
-        /// <summary>
-        /// Cuts the currently selected scene object.
-        /// </summary>
-        public void CutSelection()
-        {
-            Internal_CutSelection(mCachedPtr);
-        }
-
-        /// <summary>
-        /// Copies the currently selected scene object.
-        /// </summary>
-        public void CopySelection()
-        {
-            Internal_CopySelection(mCachedPtr);
-        }
-
-        /// <summary>
-        /// Pastes the scene object(s) that were previously cut or copied.
-        /// </summary>
-        public void PasteToSelection()
-        {
-            Internal_PasteToSelection(mCachedPtr);
-        }
-
-        /// <summary>
-        /// Deletes currently selected scene objects.
-        /// </summary>
-        public void DeleteSelection()
-        {
-            Internal_DeleteSelection(mCachedPtr);
-        }
-
-        /// <summary>
-        /// Duplicates currently selected scene objects.
-        /// </summary>
-        public void DuplicateSelection()
-        {
-            Internal_DuplicateSelection(mCachedPtr);
-        }
-
-        /// <summary>
-        /// Triggered by the runtime when the scene is modified from the native scene tree view.
-        /// </summary>
-        private void Internal_DoOnModified()
-        {
-            EditorApplication.SetSceneDirty();
-        }
-
-        /// <summary>
-        /// Triggered by the runtime when a resource is dropped on the scene tree view.
-        /// </summary>
-        private void Internal_DoOnResourceDropped(SceneObject parent, string[] resourcePaths)
-        {
-            if (resourcePaths == null)
-                return;
-
-            List<SceneObject> addedObjects = new List<SceneObject>(); 
-            for (int i = 0; i < resourcePaths.Length; i++)
-            {
-                ResourceMeta meta = ProjectLibrary.GetMeta(resourcePaths[i]);
-                if (meta == null)
-                    continue;
-
-                if (meta.ResType == ResourceType.Mesh)
-                {
-                    if (!string.IsNullOrEmpty(resourcePaths[i]))
-                    {
-                        string meshName = Path.GetFileNameWithoutExtension(resourcePaths[i]);
-
-                        Mesh mesh = ProjectLibrary.Load<Mesh>(resourcePaths[i]);
-                        if (mesh == null)
-                            continue;
-
-                        SceneObject so = UndoRedo.CreateSO(meshName, "Created a new Renderable \"" + meshName + "\"");
-                        so.Parent = parent;
-
-                        Renderable renderable = so.AddComponent<Renderable>();
-                        renderable.Mesh = mesh;
-
-                        addedObjects.Add(so);
-                    }
-                }
-                else if (meta.ResType == ResourceType.Prefab)
-                {
-                    if (!string.IsNullOrEmpty(resourcePaths[i]))
-                    {
-                        Prefab prefab = ProjectLibrary.Load<Prefab>(resourcePaths[i]);
-                        SceneObject so = UndoRedo.Instantiate(prefab, "Instantiating " + prefab.Name);
-                        so.Parent = parent;
-
-                        addedObjects.Add(so);
-                    }
-                }
-            }
-
-            if(addedObjects.Count > 0)
-                EditorApplication.SetSceneDirty();
-
-            Selection.SceneObjects = addedObjects.ToArray();
-        }
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_CreateInstance(GUISceneTreeView instance, string style, GUIOption[] options);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_Update(IntPtr thisPtr);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_CutSelection(IntPtr thisPtr);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_CopySelection(IntPtr thisPtr);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_PasteToSelection(IntPtr thisPtr);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_DeleteSelection(IntPtr thisPtr);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_DuplicateSelection(IntPtr thisPtr);
-    }
-}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.CompilerServices;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// GUI element that displays all scene objects in the current scene as a tree view.
+    /// </summary>
+    public sealed class GUISceneTreeView : GUIElement
+    {
+        /// <summary>
+        /// Creates a new scene tree view element.
+        /// </summary>
+        /// <param name="style">Optional style to use for the element. Style controls the look of the element, as well as 
+        ///                     default layout options. Style will be retrieved from the active GUISkin. If not specified 
+        ///                     default element style is used.</param>
+        /// <param name="options">Options that allow you to control how is the element  positioned and sized. This will 
+        ///                       override any similar options set by style.</param>
+        public GUISceneTreeView(string style = "", params GUIOption[] options)
+        {
+            Internal_CreateInstance(this, style, options);
+        }
+
+        /// <summary>
+        /// Creates a new scene tree view element.
+        /// </summary>
+        /// <param name="options">Options that allow you to control how is the element  positioned and sized. This will 
+        ///                       override any similar options set by style.</param>
+        public GUISceneTreeView(params GUIOption[] options)
+        {
+            Internal_CreateInstance(this, "", options);
+        }
+
+        /// <summary>
+        /// Updates the contents of the tree view with most recent scene data. Should be called once per frame.
+        /// </summary>
+        public void Update()
+        {
+            Internal_Update(mCachedPtr);
+        }
+
+        /// <summary>
+        /// Cuts the currently selected scene object.
+        /// </summary>
+        public void CutSelection()
+        {
+            Internal_CutSelection(mCachedPtr);
+        }
+
+        /// <summary>
+        /// Copies the currently selected scene object.
+        /// </summary>
+        public void CopySelection()
+        {
+            Internal_CopySelection(mCachedPtr);
+        }
+
+        /// <summary>
+        /// Pastes the scene object(s) that were previously cut or copied.
+        /// </summary>
+        public void PasteToSelection()
+        {
+            Internal_PasteToSelection(mCachedPtr);
+        }
+
+        /// <summary>
+        /// Deletes currently selected scene objects.
+        /// </summary>
+        public void DeleteSelection()
+        {
+            Internal_DeleteSelection(mCachedPtr);
+        }
+
+        /// <summary>
+        /// Duplicates currently selected scene objects.
+        /// </summary>
+        public void DuplicateSelection()
+        {
+            Internal_DuplicateSelection(mCachedPtr);
+        }
+
+        /// <summary>
+        /// Starts the rename operation for the currently selected scene objects.
+        /// </summary>
+        public void RenameSelection()
+        {
+            Internal_RenameSelection(mCachedPtr);
+        }
+
+        /// <summary>
+        /// Triggered by the runtime when the scene is modified from the native scene tree view.
+        /// </summary>
+        private void Internal_DoOnModified()
+        {
+            EditorApplication.SetSceneDirty();
+        }
+
+        /// <summary>
+        /// Triggered by the runtime when a resource is dropped on the scene tree view.
+        /// </summary>
+        private void Internal_DoOnResourceDropped(SceneObject parent, string[] resourcePaths)
+        {
+            if (resourcePaths == null)
+                return;
+
+            List<SceneObject> addedObjects = new List<SceneObject>(); 
+            for (int i = 0; i < resourcePaths.Length; i++)
+            {
+                ResourceMeta meta = ProjectLibrary.GetMeta(resourcePaths[i]);
+                if (meta == null)
+                    continue;
+
+                if (meta.ResType == ResourceType.Mesh)
+                {
+                    if (!string.IsNullOrEmpty(resourcePaths[i]))
+                    {
+                        string meshName = Path.GetFileNameWithoutExtension(resourcePaths[i]);
+
+                        Mesh mesh = ProjectLibrary.Load<Mesh>(resourcePaths[i]);
+                        if (mesh == null)
+                            continue;
+
+                        SceneObject so = UndoRedo.CreateSO(meshName, "Created a new Renderable \"" + meshName + "\"");
+                        so.Parent = parent;
+
+                        Renderable renderable = so.AddComponent<Renderable>();
+                        renderable.Mesh = mesh;
+
+                        addedObjects.Add(so);
+                    }
+                }
+                else if (meta.ResType == ResourceType.Prefab)
+                {
+                    if (!string.IsNullOrEmpty(resourcePaths[i]))
+                    {
+                        Prefab prefab = ProjectLibrary.Load<Prefab>(resourcePaths[i]);
+                        SceneObject so = UndoRedo.Instantiate(prefab, "Instantiating " + prefab.Name);
+                        so.Parent = parent;
+
+                        addedObjects.Add(so);
+                    }
+                }
+            }
+
+            if(addedObjects.Count > 0)
+                EditorApplication.SetSceneDirty();
+
+            Selection.SceneObjects = addedObjects.ToArray();
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_CreateInstance(GUISceneTreeView instance, string style, GUIOption[] options);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_Update(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_CutSelection(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_CopySelection(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_PasteToSelection(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_DeleteSelection(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_DuplicateSelection(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_RenameSelection(IntPtr thisPtr);
+    }
+}

+ 79 - 83
Source/MBansheeEditor/HierarchyWindow.cs

@@ -1,83 +1,79 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-using BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Editor window that displays the scene hierarchy tree view, displaying all scene objects in the current scene.
-    /// </summary>
-    public class HierarchyWindow : EditorWindow
-    {
-        private GUISceneTreeView treeView;
-
-        /// <summary>
-        /// Opens the hierarchy window.
-        /// </summary>
-        [MenuItem("Windows/Hierarchy", ButtonModifier.CtrlAlt, ButtonCode.H, 6000)]
-        private static void OpenHierarchyWindow()
-        {
-            OpenWindow<HierarchyWindow>();
-        }
-
-        /// <inheritdoc/>
-        protected override LocString GetDisplayName()
-        {
-            return new LocEdString("Hierarchy");
-        }
-
-        /// <summary>
-        /// Cuts the currently selected scene object.
-        /// </summary>
-        public void CutSelection()
-        {
-            treeView.CutSelection();
-        }
-
-        /// <summary>
-        /// Copies the currently selected scene object.
-        /// </summary>
-        public void CopySelection()
-        {
-            treeView.CopySelection();
-        }
-
-        /// <summary>
-        /// Pastes the scene object(s) that were previously cut or copied.
-        /// </summary>
-        public void PasteToSelection()
-        {
-            treeView.PasteToSelection();
-        }
-
-        /// <summary>
-        /// Deletes currently selected scene objects.
-        /// </summary>
-        public void DeleteSelection()
-        {
-            treeView.DeleteSelection();
-        }
-
-        /// <summary>
-        /// Duplicates currently selected scene objects.
-        /// </summary>
-        public void DuplicateSelection()
-        {
-            treeView.DuplicateSelection();
-        }
-
-        private void OnInitialize()
-        {
-            GUIScrollArea scrollArea = new GUIScrollArea();
-            GUI.AddElement(scrollArea);
-
-            treeView = new GUISceneTreeView(GUIOption.FlexibleHeight(20), GUIOption.FlexibleWidth(20));
-            scrollArea.Layout.AddElement(treeView);
-        }
-
-        private void OnEditorUpdate()
-        {
-            treeView.Update();
-        }
-    }
-}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Editor window that displays the scene hierarchy tree view, displaying all scene objects in the current scene.
+    /// </summary>
+    public class HierarchyWindow : EditorWindow, IGlobalShortcuts
+    {
+        private GUISceneTreeView treeView;
+
+        /// <summary>
+        /// Opens the hierarchy window.
+        /// </summary>
+        [MenuItem("Windows/Hierarchy", ButtonModifier.CtrlAlt, ButtonCode.H, 6000)]
+        private static void OpenHierarchyWindow()
+        {
+            OpenWindow<HierarchyWindow>();
+        }
+
+        /// <inheritdoc/>
+        protected override LocString GetDisplayName()
+        {
+            return new LocEdString("Hierarchy");
+        }
+
+        /// <inheritdoc/>
+        void IGlobalShortcuts.OnDeletePressed()
+        {
+            treeView.DeleteSelection();
+        }
+
+        /// <inheritdoc/>
+        void IGlobalShortcuts.OnRenamePressed()
+        {
+            treeView.RenameSelection();
+        }
+
+        /// <inheritdoc/>
+        void IGlobalShortcuts.OnDuplicatePressed()
+        {
+            treeView.DuplicateSelection();
+        }
+
+        /// <inheritdoc/>
+        void IGlobalShortcuts.OnCopyPressed()
+        {
+            treeView.CopySelection();
+        }
+
+        /// <inheritdoc/>
+        void IGlobalShortcuts.OnCutPressed()
+        {
+            treeView.CutSelection();
+        }
+
+        /// <inheritdoc/>
+        void IGlobalShortcuts.OnPastePressed()
+        {
+            treeView.PasteToSelection();
+        }
+
+        private void OnInitialize()
+        {
+            GUIScrollArea scrollArea = new GUIScrollArea();
+            GUI.AddElement(scrollArea);
+
+            treeView = new GUISceneTreeView(GUIOption.FlexibleHeight(20), GUIOption.FlexibleWidth(20));
+            scrollArea.Layout.AddElement(treeView);
+        }
+
+        private void OnEditorUpdate()
+        {
+            treeView.Update();
+        }
+    }
+}

+ 40 - 0
Source/MBansheeEditor/IGlobalShortcuts.cs

@@ -0,0 +1,40 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Interface for editor windows that can respond to global shortcuts.
+    /// </summary>
+    internal interface IGlobalShortcuts
+    {
+        /// <summary>
+        /// Notifies the window that the delete shortcut was pressed.
+        /// </summary>
+        void OnDeletePressed();
+
+        /// <summary>
+        /// Notifies the window that the rename shortcut was pressed.
+        /// </summary>
+        void OnRenamePressed();
+
+        /// <summary>
+        /// Notifies the window that the duplicate shortcut was pressed.
+        /// </summary>
+        void OnDuplicatePressed();
+
+        /// <summary>
+        /// Notifies the window that the copy shortcut was pressed.
+        /// </summary>
+        void OnCopyPressed();
+
+        /// <summary>
+        /// Notifies the window that the cut shortcut was pressed.
+        /// </summary>
+        void OnCutPressed();
+
+        /// <summary>
+        /// Notifies the window that the paste shortcut was pressed.
+        /// </summary>
+        void OnPastePressed();
+    }
+}

+ 61 - 29
Source/MBansheeEditor/Library/LibraryWindow.cs

@@ -20,7 +20,7 @@ namespace BansheeEditor
     /// with the ability to move, cut, copy, paste resources and folders, as well as supporting drag and drop and search
     /// operations.
     /// </summary>
-    internal sealed class LibraryWindow : EditorWindow
+    internal sealed class LibraryWindow : EditorWindow, IGlobalShortcuts
     {
         /// <summary>
         /// Directions the selection cursor in library window can be moved in.
@@ -232,26 +232,6 @@ namespace BansheeEditor
             {
                 if (!isRenameInProgress)
                 {
-                    if (Input.IsButtonHeld(ButtonCode.LeftControl) || Input.IsButtonHeld(ButtonCode.RightControl))
-                    {
-                        if (Input.IsButtonUp(ButtonCode.C))
-                        {
-                            CopySelection();
-                        }
-                        else if (Input.IsButtonUp(ButtonCode.X))
-                        {
-                            CutSelection();
-                        }
-                        else if (Input.IsButtonUp(ButtonCode.D))
-                        {
-                            DuplicateSelection();
-                        }
-                        else if (Input.IsButtonUp(ButtonCode.V))
-                        {
-                            PasteToSelection();
-                        }
-                    }
-
                     if (Input.IsButtonDown(ButtonCode.Return))
                     {
                         if (selectionPaths.Count == 1)
@@ -287,14 +267,6 @@ namespace BansheeEditor
                     {
                         MoveSelection(MoveDirection.Right);
                     }
-                    else if (Input.IsButtonDown(ButtonCode.F2))
-                    {
-                        RenameSelection();
-                    }
-                    else if (Input.IsButtonDown(ButtonCode.Delete))
-                    {
-                        DeleteSelection();
-                    }
                 }
                 else
                 {
@@ -425,6 +397,66 @@ namespace BansheeEditor
             Refresh();
         }
 
+        /// <inheritdoc/>
+        void IGlobalShortcuts.OnDeletePressed()
+        {
+            bool isRenameInProgress = inProgressRenameElement != null;
+            if (isRenameInProgress)
+                return;
+
+            DeleteSelection();
+        }
+
+        /// <inheritdoc/>
+        void IGlobalShortcuts.OnRenamePressed()
+        {
+            bool isRenameInProgress = inProgressRenameElement != null;
+            if (isRenameInProgress)
+                return;
+
+            RenameSelection();
+        }
+
+        /// <inheritdoc/>
+        void IGlobalShortcuts.OnDuplicatePressed()
+        {
+            bool isRenameInProgress = inProgressRenameElement != null;
+            if (isRenameInProgress)
+                return;
+
+            DuplicateSelection();
+        }
+
+        /// <inheritdoc/>
+        void IGlobalShortcuts.OnCopyPressed()
+        {
+            bool isRenameInProgress = inProgressRenameElement != null;
+            if (isRenameInProgress)
+                return;
+
+            CopySelection();
+        }
+
+        /// <inheritdoc/>
+        void IGlobalShortcuts.OnCutPressed()
+        {
+            bool isRenameInProgress = inProgressRenameElement != null;
+            if (isRenameInProgress)
+                return;
+
+            CutSelection();
+        }
+
+        /// <inheritdoc/>
+        void IGlobalShortcuts.OnPastePressed()
+        {
+            bool isRenameInProgress = inProgressRenameElement != null;
+            if (isRenameInProgress)
+                return;
+
+            PasteToSelection();
+        }
+
         /// <summary>
         /// Deselects all selected elements.
         /// </summary>

+ 1 - 0
Source/MBansheeEditor/MBansheeEditor.csproj

@@ -47,6 +47,7 @@
     <Compile Include="CodeEditor.cs" />
     <Compile Include="ColorPicker.cs" />
     <Compile Include="EditorPersistentData.cs" />
+    <Compile Include="IGlobalShortcuts.cs" />
     <Compile Include="Inspectors\BoxColliderInspector.cs" />
     <Compile Include="Inspectors\CapsuleColliderInspector.cs" />
     <Compile Include="Inspectors\CharacterControllerInspector.cs" />

+ 5 - 61
Source/MBansheeEditor/MenuItems.cs

@@ -523,18 +523,7 @@ namespace BansheeEditor
         [MenuItem("Edit/Cut", 9450, true)]
         public static void Cut()
         {
-            if (Selection.SceneObjects != null && Selection.SceneObjects.Length > 0)
-            {
-                HierarchyWindow win = EditorWindow.GetWindow<HierarchyWindow>();
-                if (win != null)
-                    win.CutSelection();
-            }
-            else if (Selection.ResourcePaths != null && Selection.ResourcePaths.Length > 0)
-            {
-                LibraryWindow win = EditorWindow.GetWindow<LibraryWindow>();
-                if (win != null)
-                    win.CutSelection();
-            }
+            EditorApplication.TriggerGlobalShortcut(EditorApplication.CutKey);
         }
 
         /// <summary>
@@ -543,18 +532,7 @@ namespace BansheeEditor
         [MenuItem("Edit/Copy", 9449)]
         public static void Copy()
         {
-            if (Selection.SceneObjects != null && Selection.SceneObjects.Length > 0)
-            {
-                HierarchyWindow win = EditorWindow.GetWindow<HierarchyWindow>();
-                if (win != null)
-                    win.CopySelection();
-            }
-            else if (Selection.ResourcePaths != null && Selection.ResourcePaths.Length > 0)
-            {
-                LibraryWindow win = EditorWindow.GetWindow<LibraryWindow>();
-                if (win != null)
-                    win.CopySelection();
-            }
+            EditorApplication.TriggerGlobalShortcut(EditorApplication.CopyKey);
         }
 
         /// <summary>
@@ -563,19 +541,7 @@ namespace BansheeEditor
         [MenuItem("Edit/Paste", 9448)]
         public static void Paste()
         {
-            // TODO - This is slightly wrong in case both windows have something in their paste buffer (unify them?)
-
-            {
-                HierarchyWindow win = EditorWindow.GetWindow<HierarchyWindow>();
-                if (win != null)
-                    win.PasteToSelection();
-            }
-
-            {
-                LibraryWindow win = EditorWindow.GetWindow<LibraryWindow>();
-                if (win != null)
-                    win.PasteToSelection();
-            }
+            EditorApplication.TriggerGlobalShortcut(EditorApplication.PasteKey);
         }
 
         /// <summary>
@@ -584,18 +550,7 @@ namespace BansheeEditor
         [MenuItem("Edit/Delete", 9447)]
         public static void Delete()
         {
-            if (Selection.SceneObjects != null && Selection.SceneObjects.Length > 0)
-            {
-                HierarchyWindow win = EditorWindow.GetWindow<HierarchyWindow>();
-                if (win != null)
-                    win.DeleteSelection();
-            }
-            else if (Selection.ResourcePaths != null && Selection.ResourcePaths.Length > 0)
-            {
-                LibraryWindow win = EditorWindow.GetWindow<LibraryWindow>();
-                if (win != null)
-                    win.DeleteSelection();
-            }
+            EditorApplication.TriggerGlobalShortcut(EditorApplication.DeleteKey);
         }
 
         /// <summary>
@@ -604,18 +559,7 @@ namespace BansheeEditor
         [MenuItem("Edit/Duplicate", 9446)]
         public static void Duplicate()
         {
-            if (Selection.SceneObjects != null && Selection.SceneObjects.Length > 0)
-            {
-                HierarchyWindow win = EditorWindow.GetWindow<HierarchyWindow>();
-                if (win != null)
-                    win.DuplicateSelection();
-            }
-            else if (Selection.ResourcePaths != null && Selection.ResourcePaths.Length > 0)
-            {
-                LibraryWindow win = EditorWindow.GetWindow<LibraryWindow>();
-                if (win != null)
-                    win.DuplicateSelection();
-            }
+            EditorApplication.TriggerGlobalShortcut(EditorApplication.DuplicateKey);
         }
     }
 }

+ 62 - 42
Source/MBansheeEditor/Scene/SceneWindow.cs

@@ -10,15 +10,13 @@ namespace BansheeEditor
     /// <summary>
     /// Displays the scene view camera and various scene controls.
     /// </summary>
-    internal sealed class SceneWindow : EditorWindow
+    internal sealed class SceneWindow : EditorWindow, IGlobalShortcuts
     {
         internal const string ToggleProfilerOverlayBinding = "ToggleProfilerOverlay";
         internal const string ViewToolBinding = "ViewTool";
         internal const string MoveToolBinding = "MoveTool";
         internal const string RotateToolBinding = "RotateTool";
         internal const string ScaleToolBinding = "ScaleTool";
-        internal const string DuplicateBinding = "Duplicate";
-        internal const string DeleteBinding = "Delete";
         internal const string FrameBinding = "SceneFrame";
 
         private const int HeaderHeight = 20;
@@ -66,8 +64,6 @@ namespace BansheeEditor
 
         private int editorSettingsHash = int.MaxValue;
 
-        private VirtualButton duplicateKey;
-        private VirtualButton deleteKey;
         private VirtualButton frameKey;
 
         // Tool shortcuts
@@ -279,8 +275,6 @@ namespace BansheeEditor
             moveToolKey = new VirtualButton(MoveToolBinding);
             rotateToolKey = new VirtualButton(RotateToolBinding);
             scaleToolKey = new VirtualButton(ScaleToolBinding);
-            duplicateKey = new VirtualButton(DuplicateBinding);
-            deleteKey = new VirtualButton(DeleteBinding);
             frameKey = new VirtualButton(FrameBinding);
 
             UpdateRenderTexture(Width, Height - HeaderHeight);
@@ -299,6 +293,67 @@ namespace BansheeEditor
             sceneAxesGUI = null;
         }
 
+        /// <inheritdoc/>
+        void IGlobalShortcuts.OnDeletePressed()
+        {
+            SceneObject[] selectedObjects = Selection.SceneObjects;
+            CleanDuplicates(ref selectedObjects);
+
+            if (selectedObjects.Length > 0)
+            {
+                foreach (var so in selectedObjects)
+                {
+                    string message = "Deleted " + so.Name;
+                    UndoRedo.DeleteSO(so, message);
+                }
+
+                EditorApplication.SetSceneDirty();
+            }
+        }
+
+        /// <inheritdoc/>
+        void IGlobalShortcuts.OnRenamePressed()
+        {
+            // Do nothing
+        }
+
+        /// <inheritdoc/>
+        void IGlobalShortcuts.OnDuplicatePressed()
+        {
+            SceneObject[] selectedObjects = Selection.SceneObjects;
+            CleanDuplicates(ref selectedObjects);
+
+            if (selectedObjects.Length > 0)
+            {
+                string message;
+                if (selectedObjects.Length == 1)
+                    message = "Duplicated " + selectedObjects[0].Name;
+                else
+                    message = "Duplicated " + selectedObjects.Length + " elements";
+
+                UndoRedo.CloneSO(selectedObjects, message);
+                EditorApplication.SetSceneDirty();
+            }
+        }
+
+        /// <inheritdoc/>
+        void IGlobalShortcuts.OnCopyPressed()
+        {
+            // Do nothing
+        }
+
+        /// <inheritdoc/>
+        void IGlobalShortcuts.OnCutPressed()
+        {
+            // Do nothing
+        }
+
+        /// <inheritdoc/>
+        void IGlobalShortcuts.OnPastePressed()
+        {
+            // Do nothing
+        }
+
         /// <summary>
         /// Orients the camera so it looks along the provided axis.
         /// </summary>
@@ -388,41 +443,6 @@ namespace BansheeEditor
 
                     if (VirtualInput.IsButtonDown(scaleToolKey))
                         EditorApplication.ActiveSceneTool = SceneViewTool.Scale;
-
-                    if (VirtualInput.IsButtonDown(duplicateKey))
-                    {
-                        SceneObject[] selectedObjects = Selection.SceneObjects;
-                        CleanDuplicates(ref selectedObjects);
-
-                        if (selectedObjects.Length > 0)
-                        {
-                            String message;
-                            if (selectedObjects.Length == 1)
-                                message = "Duplicated " + selectedObjects[0].Name;
-                            else
-                                message = "Duplicated " + selectedObjects.Length + " elements";
-
-                            UndoRedo.CloneSO(selectedObjects, message);
-                            EditorApplication.SetSceneDirty();
-                        }
-                    }
-
-                    if (VirtualInput.IsButtonDown(deleteKey))
-                    {
-                        SceneObject[] selectedObjects = Selection.SceneObjects;
-                        CleanDuplicates(ref selectedObjects);
-
-                        if (selectedObjects.Length > 0)
-                        {
-                            foreach (var so in selectedObjects)
-                            {
-                                string message = "Deleted " + so.Name;
-                                UndoRedo.DeleteSO(so, message);
-                            }
-
-                            EditorApplication.SetSceneDirty();
-                        }
-                    }
                 }
             }
 

+ 46 - 45
Source/SBansheeEditor/Include/BsScriptGUISceneTreeView.h

@@ -1,46 +1,47 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#pragma once
-
-#include "BsScriptEditorPrerequisites.h"
-#include "BsScriptGUIElement.h"
-
-namespace BansheeEngine
-{
-	/**	Interop class between C++ & CLR for GUISceneTreeView. */
-	class BS_SCR_BED_EXPORT ScriptGUISceneTreeView : public TScriptGUIElement<ScriptGUISceneTreeView>
-	{
-	public:
-		SCRIPT_OBJ(EDITOR_ASSEMBLY, "BansheeEditor", "GUISceneTreeView")
-
-	private:
-		ScriptGUISceneTreeView(MonoObject* instance, GUISceneTreeView* treeView);
-		~ScriptGUISceneTreeView();
-
-		/** Triggered when the native scene tree view modifies the scene. */
-		void sceneModified();
-
-		/** Triggered when a resource is dragged and dropped over the native scene tree view. */
-		void resourceDropped(const HSceneObject& parent, const Vector<Path>& resourcePaths);
-
-		HEvent mOnModifiedConn;
-		HEvent mOnResourceDroppedConn;
-
-		/************************************************************************/
-		/* 								CLR HOOKS						   		*/
-		/************************************************************************/
-		static void internal_createInstance(MonoObject* instance, MonoString* style, MonoArray* guiOptions);
-		static void internal_update(ScriptGUISceneTreeView* thisPtr);
-		static void internal_cutSelection(ScriptGUISceneTreeView* thisPtr);
-		static void internal_copySelection(ScriptGUISceneTreeView* thisPtr);
-		static void internal_pasteToSelection(ScriptGUISceneTreeView* thisPtr);
-		static void internal_duplicateSelection(ScriptGUISceneTreeView* thisPtr);
-		static void internal_deleteSelection(ScriptGUISceneTreeView* thisPtr);
-
-		typedef void(__stdcall *OnModifiedThunkDef) (MonoObject*, MonoException**);
-		typedef void(__stdcall *OnResourceDroppedThunkDef) (MonoObject*, MonoObject*, MonoArray*, MonoException**);
-
-		static OnModifiedThunkDef onModifiedThunk;
-		static OnResourceDroppedThunkDef onResourceDroppedThunk;
-	};
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsScriptEditorPrerequisites.h"
+#include "BsScriptGUIElement.h"
+
+namespace BansheeEngine
+{
+	/**	Interop class between C++ & CLR for GUISceneTreeView. */
+	class BS_SCR_BED_EXPORT ScriptGUISceneTreeView : public TScriptGUIElement<ScriptGUISceneTreeView>
+	{
+	public:
+		SCRIPT_OBJ(EDITOR_ASSEMBLY, "BansheeEditor", "GUISceneTreeView")
+
+	private:
+		ScriptGUISceneTreeView(MonoObject* instance, GUISceneTreeView* treeView);
+		~ScriptGUISceneTreeView();
+
+		/** Triggered when the native scene tree view modifies the scene. */
+		void sceneModified();
+
+		/** Triggered when a resource is dragged and dropped over the native scene tree view. */
+		void resourceDropped(const HSceneObject& parent, const Vector<Path>& resourcePaths);
+
+		HEvent mOnModifiedConn;
+		HEvent mOnResourceDroppedConn;
+
+		/************************************************************************/
+		/* 								CLR HOOKS						   		*/
+		/************************************************************************/
+		static void internal_createInstance(MonoObject* instance, MonoString* style, MonoArray* guiOptions);
+		static void internal_update(ScriptGUISceneTreeView* thisPtr);
+		static void internal_cutSelection(ScriptGUISceneTreeView* thisPtr);
+		static void internal_copySelection(ScriptGUISceneTreeView* thisPtr);
+		static void internal_pasteToSelection(ScriptGUISceneTreeView* thisPtr);
+		static void internal_duplicateSelection(ScriptGUISceneTreeView* thisPtr);
+		static void internal_deleteSelection(ScriptGUISceneTreeView* thisPtr);
+		static void internal_renameSelection(ScriptGUISceneTreeView* thisPtr);
+
+		typedef void(__stdcall *OnModifiedThunkDef) (MonoObject*, MonoException**);
+		typedef void(__stdcall *OnResourceDroppedThunkDef) (MonoObject*, MonoObject*, MonoArray*, MonoException**);
+
+		static OnModifiedThunkDef onModifiedThunk;
+		static OnResourceDroppedThunkDef onResourceDroppedThunk;
+	};
 }

+ 130 - 123
Source/SBansheeEditor/Source/BsScriptGUISceneTreeView.cpp

@@ -1,124 +1,131 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsScriptGUISceneTreeView.h"
-#include "BsScriptMeta.h"
-#include "BsMonoClass.h"
-#include "BsMonoMethod.h"
-#include "BsMonoManager.h"
-#include "BsMonoUtil.h"
-#include "BsGUISceneTreeView.h"
-#include "BsGUIOptions.h"
-#include "BsScriptGameObjectManager.h"
-#include "BsScriptSceneObject.h"
-
-using namespace std::placeholders;
-
-namespace BansheeEngine
-{
-	ScriptGUISceneTreeView::OnModifiedThunkDef ScriptGUISceneTreeView::onModifiedThunk;
-	ScriptGUISceneTreeView::OnResourceDroppedThunkDef ScriptGUISceneTreeView::onResourceDroppedThunk;
-
-	ScriptGUISceneTreeView::ScriptGUISceneTreeView(MonoObject* instance, GUISceneTreeView* treeView)
-		:TScriptGUIElement(instance, treeView)
-	{
-		mOnModifiedConn = treeView->onModified.connect(std::bind(&ScriptGUISceneTreeView::sceneModified, this));
-		mOnResourceDroppedConn = treeView->onResourceDropped.connect(
-			std::bind(&ScriptGUISceneTreeView::resourceDropped, this, _1, _2));
-	}
-
-	ScriptGUISceneTreeView::~ScriptGUISceneTreeView()
-	{
-		mOnModifiedConn.disconnect();
-		mOnResourceDroppedConn.disconnect();
-	}
-
-	void ScriptGUISceneTreeView::initRuntimeData()
-	{
-		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptGUISceneTreeView::internal_createInstance);
-		metaData.scriptClass->addInternalCall("Internal_Update", &ScriptGUISceneTreeView::internal_update);
-		metaData.scriptClass->addInternalCall("Internal_CutSelection", &ScriptGUISceneTreeView::internal_cutSelection);
-		metaData.scriptClass->addInternalCall("Internal_CopySelection", &ScriptGUISceneTreeView::internal_copySelection);
-		metaData.scriptClass->addInternalCall("Internal_PasteToSelection", &ScriptGUISceneTreeView::internal_pasteToSelection);
-		metaData.scriptClass->addInternalCall("Internal_DuplicateSelection", &ScriptGUISceneTreeView::internal_duplicateSelection);
-		metaData.scriptClass->addInternalCall("Internal_DeleteSelection", &ScriptGUISceneTreeView::internal_deleteSelection);
-
-		onModifiedThunk = (OnModifiedThunkDef)metaData.scriptClass->getMethod("Internal_DoOnModified", 0)->getThunk();
-		onResourceDroppedThunk = (OnResourceDroppedThunkDef)metaData.scriptClass->getMethod("Internal_DoOnResourceDropped", 2)->getThunk();
-	}
-
-	void ScriptGUISceneTreeView::sceneModified()
-	{
-		MonoUtil::invokeThunk(onModifiedThunk, getManagedInstance());
-	}
-
-	void ScriptGUISceneTreeView::resourceDropped(const HSceneObject& parent, const Vector<Path>& resourcePaths)
-	{
-		MonoObject* sceneMonoObject = nullptr;
-
-		if (parent != nullptr)
-		{
-			ScriptSceneObject* scriptSceneObject = ScriptGameObjectManager::instance().getOrCreateScriptSceneObject(parent);
-			sceneMonoObject = scriptSceneObject->getManagedInstance();
-		}
-
-		UINT32 numPaths = (UINT32)resourcePaths.size();
-		ScriptArray array = ScriptArray::create<WString>(numPaths);
-		for (UINT32 i = 0; i < numPaths; i++)
-			array.set(i, resourcePaths[i].toWString());
-
-		MonoUtil::invokeThunk(onResourceDroppedThunk, getManagedInstance(), sceneMonoObject, array.getInternal());
-	}
-
-	void ScriptGUISceneTreeView::internal_createInstance(MonoObject* instance, MonoString* style, MonoArray* guiOptions)
-	{
-		GUIOptions options;
-
-		UINT32 arrayLen = (UINT32)mono_array_length(guiOptions);
-		for (UINT32 i = 0; i < arrayLen; i++)
-			options.addOption(mono_array_get(guiOptions, GUIOption, i));
-
-		String styleName = toString(MonoUtil::monoToWString(style));
-
-		GUISceneTreeView* treeView = GUISceneTreeView::create(options);
-		ScriptGUISceneTreeView* nativeInstance = new (bs_alloc<ScriptGUISceneTreeView>()) ScriptGUISceneTreeView(instance, treeView);
-	}
-
-	void ScriptGUISceneTreeView::internal_update(ScriptGUISceneTreeView* thisPtr)
-	{
-		if (thisPtr->mIsDestroyed)
-			return;
-
-		GUISceneTreeView* treeView = static_cast<GUISceneTreeView*>(thisPtr->getGUIElement());
-		treeView->_update();
-	}
-
-	void ScriptGUISceneTreeView::internal_cutSelection(ScriptGUISceneTreeView* thisPtr)
-	{
-		GUISceneTreeView* treeView = static_cast<GUISceneTreeView*>(thisPtr->getGUIElement());
-		treeView->cutSelection();
-	}
-
-	void ScriptGUISceneTreeView::internal_copySelection(ScriptGUISceneTreeView* thisPtr)
-	{
-		GUISceneTreeView* treeView = static_cast<GUISceneTreeView*>(thisPtr->getGUIElement());
-		treeView->copySelection();
-	}
-
-	void ScriptGUISceneTreeView::internal_pasteToSelection(ScriptGUISceneTreeView* thisPtr)
-	{
-		GUISceneTreeView* treeView = static_cast<GUISceneTreeView*>(thisPtr->getGUIElement());
-		treeView->paste();
-	}
-
-	void ScriptGUISceneTreeView::internal_duplicateSelection(ScriptGUISceneTreeView* thisPtr)
-	{
-		GUISceneTreeView* treeView = static_cast<GUISceneTreeView*>(thisPtr->getGUIElement());
-		treeView->duplicateSelection();
-	}
-
-	void ScriptGUISceneTreeView::internal_deleteSelection(ScriptGUISceneTreeView* thisPtr)
-	{
-		GUISceneTreeView* treeView = static_cast<GUISceneTreeView*>(thisPtr->getGUIElement());
-		treeView->deleteSelection();
-	}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsScriptGUISceneTreeView.h"
+#include "BsScriptMeta.h"
+#include "BsMonoClass.h"
+#include "BsMonoMethod.h"
+#include "BsMonoManager.h"
+#include "BsMonoUtil.h"
+#include "BsGUISceneTreeView.h"
+#include "BsGUIOptions.h"
+#include "BsScriptGameObjectManager.h"
+#include "BsScriptSceneObject.h"
+
+using namespace std::placeholders;
+
+namespace BansheeEngine
+{
+	ScriptGUISceneTreeView::OnModifiedThunkDef ScriptGUISceneTreeView::onModifiedThunk;
+	ScriptGUISceneTreeView::OnResourceDroppedThunkDef ScriptGUISceneTreeView::onResourceDroppedThunk;
+
+	ScriptGUISceneTreeView::ScriptGUISceneTreeView(MonoObject* instance, GUISceneTreeView* treeView)
+		:TScriptGUIElement(instance, treeView)
+	{
+		mOnModifiedConn = treeView->onModified.connect(std::bind(&ScriptGUISceneTreeView::sceneModified, this));
+		mOnResourceDroppedConn = treeView->onResourceDropped.connect(
+			std::bind(&ScriptGUISceneTreeView::resourceDropped, this, _1, _2));
+	}
+
+	ScriptGUISceneTreeView::~ScriptGUISceneTreeView()
+	{
+		mOnModifiedConn.disconnect();
+		mOnResourceDroppedConn.disconnect();
+	}
+
+	void ScriptGUISceneTreeView::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptGUISceneTreeView::internal_createInstance);
+		metaData.scriptClass->addInternalCall("Internal_Update", &ScriptGUISceneTreeView::internal_update);
+		metaData.scriptClass->addInternalCall("Internal_CutSelection", &ScriptGUISceneTreeView::internal_cutSelection);
+		metaData.scriptClass->addInternalCall("Internal_CopySelection", &ScriptGUISceneTreeView::internal_copySelection);
+		metaData.scriptClass->addInternalCall("Internal_PasteToSelection", &ScriptGUISceneTreeView::internal_pasteToSelection);
+		metaData.scriptClass->addInternalCall("Internal_DuplicateSelection", &ScriptGUISceneTreeView::internal_duplicateSelection);
+		metaData.scriptClass->addInternalCall("Internal_DeleteSelection", &ScriptGUISceneTreeView::internal_deleteSelection);
+		metaData.scriptClass->addInternalCall("Internal_RenameSelection", &ScriptGUISceneTreeView::internal_renameSelection);
+
+		onModifiedThunk = (OnModifiedThunkDef)metaData.scriptClass->getMethod("Internal_DoOnModified", 0)->getThunk();
+		onResourceDroppedThunk = (OnResourceDroppedThunkDef)metaData.scriptClass->getMethod("Internal_DoOnResourceDropped", 2)->getThunk();
+	}
+
+	void ScriptGUISceneTreeView::sceneModified()
+	{
+		MonoUtil::invokeThunk(onModifiedThunk, getManagedInstance());
+	}
+
+	void ScriptGUISceneTreeView::resourceDropped(const HSceneObject& parent, const Vector<Path>& resourcePaths)
+	{
+		MonoObject* sceneMonoObject = nullptr;
+
+		if (parent != nullptr)
+		{
+			ScriptSceneObject* scriptSceneObject = ScriptGameObjectManager::instance().getOrCreateScriptSceneObject(parent);
+			sceneMonoObject = scriptSceneObject->getManagedInstance();
+		}
+
+		UINT32 numPaths = (UINT32)resourcePaths.size();
+		ScriptArray array = ScriptArray::create<WString>(numPaths);
+		for (UINT32 i = 0; i < numPaths; i++)
+			array.set(i, resourcePaths[i].toWString());
+
+		MonoUtil::invokeThunk(onResourceDroppedThunk, getManagedInstance(), sceneMonoObject, array.getInternal());
+	}
+
+	void ScriptGUISceneTreeView::internal_createInstance(MonoObject* instance, MonoString* style, MonoArray* guiOptions)
+	{
+		GUIOptions options;
+
+		UINT32 arrayLen = (UINT32)mono_array_length(guiOptions);
+		for (UINT32 i = 0; i < arrayLen; i++)
+			options.addOption(mono_array_get(guiOptions, GUIOption, i));
+
+		String styleName = toString(MonoUtil::monoToWString(style));
+
+		GUISceneTreeView* treeView = GUISceneTreeView::create(options);
+		ScriptGUISceneTreeView* nativeInstance = new (bs_alloc<ScriptGUISceneTreeView>()) ScriptGUISceneTreeView(instance, treeView);
+	}
+
+	void ScriptGUISceneTreeView::internal_update(ScriptGUISceneTreeView* thisPtr)
+	{
+		if (thisPtr->mIsDestroyed)
+			return;
+
+		GUISceneTreeView* treeView = static_cast<GUISceneTreeView*>(thisPtr->getGUIElement());
+		treeView->_update();
+	}
+
+	void ScriptGUISceneTreeView::internal_cutSelection(ScriptGUISceneTreeView* thisPtr)
+	{
+		GUISceneTreeView* treeView = static_cast<GUISceneTreeView*>(thisPtr->getGUIElement());
+		treeView->cutSelection();
+	}
+
+	void ScriptGUISceneTreeView::internal_copySelection(ScriptGUISceneTreeView* thisPtr)
+	{
+		GUISceneTreeView* treeView = static_cast<GUISceneTreeView*>(thisPtr->getGUIElement());
+		treeView->copySelection();
+	}
+
+	void ScriptGUISceneTreeView::internal_pasteToSelection(ScriptGUISceneTreeView* thisPtr)
+	{
+		GUISceneTreeView* treeView = static_cast<GUISceneTreeView*>(thisPtr->getGUIElement());
+		treeView->paste();
+	}
+
+	void ScriptGUISceneTreeView::internal_duplicateSelection(ScriptGUISceneTreeView* thisPtr)
+	{
+		GUISceneTreeView* treeView = static_cast<GUISceneTreeView*>(thisPtr->getGUIElement());
+		treeView->duplicateSelection();
+	}
+
+	void ScriptGUISceneTreeView::internal_deleteSelection(ScriptGUISceneTreeView* thisPtr)
+	{
+		GUISceneTreeView* treeView = static_cast<GUISceneTreeView*>(thisPtr->getGUIElement());
+		treeView->deleteSelection();
+	}
+
+	void ScriptGUISceneTreeView::internal_renameSelection(ScriptGUISceneTreeView* thisPtr)
+	{
+		GUISceneTreeView* treeView = static_cast<GUISceneTreeView*>(thisPtr->getGUIElement());
+		treeView->renameSelected();
+	}
 }