Просмотр исходного кода

Fixed task scheduler so it properly counts active tasks
Fixed an issue with setting physics materials on a collider
GUI elements can now eat focus events to prevent the elements under them receiving the events
Added a way to manually set focus on editor widgets

BearishSun 9 лет назад
Родитель
Сommit
c8f96b70d6
24 измененных файлов с 10071 добавлено и 9902 удалено
  1. 211 208
      Source/BansheeEditor/Include/BsEditorWidget.h
  2. 190 171
      Source/BansheeEditor/Source/BsEditorWidget.cpp
  3. 315 305
      Source/BansheeEngine/Include/BsGUIElement.h
  4. 43 40
      Source/BansheeEngine/Include/BsGUIElementContainer.h
  5. 464 451
      Source/BansheeEngine/Include/BsGUIManager.h
  6. 434 432
      Source/BansheeEngine/Source/BsGUIButtonBase.cpp
  7. 133 133
      Source/BansheeEngine/Source/BsGUIDropDownHitBox.cpp
  8. 263 257
      Source/BansheeEngine/Source/BsGUIElement.cpp
  9. 51 39
      Source/BansheeEngine/Source/BsGUIElementContainer.cpp
  10. 1797 1787
      Source/BansheeEngine/Source/BsGUIManager.cpp
  11. 313 310
      Source/BansheePhysX/Include/BsPhysX.h
  12. 1 1
      Source/BansheePhysX/Source/BsFPhysXCollider.cpp
  13. 1170 1150
      Source/BansheePhysX/Source/BsPhysX.cpp
  14. 139 141
      Source/BansheeUtility/Include/BsTaskScheduler.h
  15. 187 189
      Source/BansheeUtility/Source/BsTaskScheduler.cpp
  16. 173 165
      Source/MBansheeEditor/EditorWindow.cs
  17. 405 410
      Source/MBansheeEditor/Library/LibraryGUIEntry.cs
  18. 1550 1549
      Source/MBansheeEditor/Library/LibraryWindow.cs
  19. 859 847
      Source/MBansheeEditor/Scene/SceneWindow.cs
  20. 292 276
      Source/MBansheeEngine/GUI/GUIElement.cs
  21. 234 233
      Source/SBansheeEditor/Include/BsScriptEditorWindow.h
  22. 502 495
      Source/SBansheeEditor/Source/BsScriptEditorWindow.cpp
  23. 2 0
      Source/SBansheeEngine/Include/BsScriptGUIElement.h
  24. 343 313
      Source/SBansheeEngine/Source/BsScriptGUIElement.cpp

+ 211 - 208
Source/BansheeEditor/Include/BsEditorWidget.h

@@ -1,209 +1,212 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#pragma once
-
-#include "BsEditorPrerequisites.h"
-#include "BsEditorWidgetManager.h"
-#include "BsEvent.h"
-#include "BsRect2I.h"
-
-namespace BansheeEngine
-{
-	/** @addtogroup Implementation
-	 *  @{
-	 */
-
-	/**
-	 * Editor widget represents a single "window" in the editor. It may be dragged,	docked and can share space with multiple
-	 * other widgets by using tabs.
-	 *
-	 * Each widget has its own position, size, GUI and update method. Widget is always docked inside an 
-	 * EditorWidgetContainer unless it's being dragged and is not visible.
-	 */
-	class BS_ED_EXPORT EditorWidgetBase
-	{
-	public:
-		/** Gets a unique name for this widget. This name will be used for referencing the widget by other systems. */
-		const String& getName() const { return mName; }
-
-		/** Gets the display name for the widget. This is what editor users will see in the widget title bar. */
-		const HString& getDisplayName() const { return mDisplayName; }
-
-		/**	Returns the X position of the widget inside of its container. */
-		INT32 getX() const { return mX; }
-
-		/**	Returns the Y position of the widget inside of its container. */
-		INT32 getY() const { return mY; }
-
-		/**	Returns the width of the widget in pixels. */
-		UINT32 getWidth() const { return mWidth; }
-
-		/**	Returns the height of the widget in pixels. */
-		UINT32 getHeight() const { return mHeight; }
-
-		/**	Returns the width of the widget when initially created, in pixels. */
-		UINT32 getDefaultWidth() const { return mDefaultWidth; }
-
-		/**	Returns the height of the widget when initially created, in pixels. */
-		UINT32 getDefaultHeight() const { return mDefaultHeight; }
-
-		/**	Returns the bounds of the widget in pixels, relative to its parent container. */
-		Rect2I getBounds() const { return Rect2I(mX, mY, mWidth, mHeight); }
-
-		/**	Checks if the widget has focus (usually means user clicked on it last). */
-		bool hasFocus() const { return mHasFocus; }
-
-		/** 
-		 * Checks is the widget the currently active widget in its container. This means the widget's tab is active or
-		 * the widget is the only one in its container. 
-		 */
-		bool isActive() const { return mIsActive; }
-
-		/**
-		 * Gets the parent editor window this widget is docked in. Can be null (e.g. when widget is in the process of
-		 * dragging and not visible).
-		 */
-		EditorWindowBase* getParentWindow() const;
-
-		/**
-		 * Returns the parent widget container. Can be null (e.g. when widget is in the process of dragging and not 
-		 * visible). 
-		 */
-		EditorWidgetContainer* _getParent() const { return mParent; }
-
-		/**	Converts screen coordinates to coordinates relative to the widget. */
-		Vector2I screenToWidgetPos(const Vector2I& screenPos) const;
-
-		/**	Converts widget relative coordinates to screen coordiantes. */
-		Vector2I widgetToScreenPos(const Vector2I& widgetPos) const;
-
-		/**	Closes the widget, undocking it from its container and freeing any resources related to it. */
-		void close();
-
-		/** Internal method. Called once per frame.	 */
-		virtual void update() { }
-
-		Event<void(UINT32, UINT32)> onResized; /**< Triggered whenever widget size changes. */
-		Event<void(INT32, INT32)> onMoved; /**< Triggered whenever widget position changes. */
-		Event<void(EditorWidgetContainer*)> onParentChanged; /**< Triggered whenever widget parent container changes. */
-		Event<void(bool)> onFocusChanged; /**< Triggered whenever widget receives or loses focus. */
-
-		/** @cond INTERNAL */
-
-		/**	Changes the size of the widget (and its internal GUI panel). */
-		void _setSize(UINT32 width, UINT32 height);
-
-		/**	Changes the position of the widget (and its internal GUI panel), relative to its parent container. */
-		void _setPosition(INT32 x, INT32 y);
-
-		/**
-		 * Changes the parent container of the widget (e.g. when re-docking or moving a widget to another window). Parent
-		 * can be null (e.g. when widget is in the process of dragging and not visible).
-		 */
-		void _changeParent(EditorWidgetContainer* parent);
-
-		/**	Sets or removes focus for this widget. */
-		void _setHasFocus(bool focus);
-
-		/** Disables the widget making its GUI contents not visible. The widget	remains docked in its container. */
-		void _disable();
-
-		/**	Enables the widget making its previously hidden GUI contents visible. */
-		void _enable();
-
-		/** @endcond */
-	protected:
-		friend class EditorWidgetManager;
-
-		EditorWidgetBase(const HString& displayName, const String& name, UINT32 defaultWidth,
-			UINT32 defaultHeight, EditorWidgetContainer& parentContainer);
-		virtual ~EditorWidgetBase();
-
-		/**	Triggered whenever widget position changes. */
-		virtual void doOnMoved(INT32 x, INT32 y);
-
-		/**	Triggered whenever widget size changes. */
-		virtual void doOnResized(UINT32 width, UINT32 height);
-
-		/**	Triggered whenever widget parent container changes. */
-		virtual void doOnParentChanged();
-
-		/**
-		 * Returns the parent GUI widget. Before calling this you must ensure the widget has a container parent otherwise
-		 * this method will fail.
-		 */
-		GUIWidget& getParentWidget() const;
-
-		/**	Frees widget resources and deletes the instance. */
-		static void destroy(EditorWidgetBase* widget);
-
-		String mName;
-		HString mDisplayName;
-		EditorWidgetContainer* mParent;
-		INT32 mX, mY;
-		UINT32 mWidth, mHeight;
-		UINT32 mDefaultWidth, mDefaultHeight;
-		GUIPanel* mContent;
-		bool mHasFocus;
-		bool mIsActive;
-	};
-
-	/** @} */
-
-	/** @cond INTERNAL */
-	/** @addtogroup EditorWindow
-	 *  @{
-	 */
-
-	/**
-	 * Helper class that registers a widget creation callback with the widget manager. The creation callback allows the
-	 * runtime to open widgets just by their name without knowing the actual type.
-	 */
-	template<typename Type>
-	struct RegisterWidgetOnStart
-	{
-	public:
-		RegisterWidgetOnStart()
-		{
-			EditorWidgetManager::preRegisterWidget(Type::getTypeName(), &create);
-		}
-
-		/**	Creates a new widget of a specific type and adds it to the provided container. */
-		static EditorWidgetBase* create(EditorWidgetContainer& parentContainer)
-		{
-			return bs_new<Type>(EditorWidget<Type>::ConstructPrivately(), parentContainer);
-		}
-	};
-
-	/**
-	 * Editor widget template class that widgets can inherit from. Ensures that	all widget implementations are automatically
-	 * registered with the widget manager.
-	 *
-	 * @see		EditorWidgetBase
-	 */
-	template <class Type>
-	class EditorWidget : public EditorWidgetBase
-	{
-		static volatile RegisterWidgetOnStart<Type> RegisterOnStart;
-
-	protected:
-		friend struct RegisterWidgetOnStart<Type>;
-
-		struct ConstructPrivately {};
-
-		EditorWidget(const HString& displayName, UINT32 defaultWidth, UINT32 defaultHeight, 
-			EditorWidgetContainer& parentContainer)
-			:EditorWidgetBase(displayName, Type::getTypeName(), defaultWidth, defaultHeight, parentContainer)
-		{ }
-
-	public:
-
-		virtual ~EditorWidget() { }
-	};
-
-	template <typename Type>
-	volatile RegisterWidgetOnStart<Type> EditorWidget<Type>::RegisterOnStart;
-
-	/** @} */
-	/** @endcond */
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsEditorPrerequisites.h"
+#include "BsEditorWidgetManager.h"
+#include "BsEvent.h"
+#include "BsRect2I.h"
+
+namespace BansheeEngine
+{
+	/** @addtogroup Implementation
+	 *  @{
+	 */
+
+	/**
+	 * Editor widget represents a single "window" in the editor. It may be dragged,	docked and can share space with multiple
+	 * other widgets by using tabs.
+	 *
+	 * Each widget has its own position, size, GUI and update method. Widget is always docked inside an 
+	 * EditorWidgetContainer unless it's being dragged and is not visible.
+	 */
+	class BS_ED_EXPORT EditorWidgetBase
+	{
+	public:
+		/** Gets a unique name for this widget. This name will be used for referencing the widget by other systems. */
+		const String& getName() const { return mName; }
+
+		/** Gets the display name for the widget. This is what editor users will see in the widget title bar. */
+		const HString& getDisplayName() const { return mDisplayName; }
+
+		/**	Returns the X position of the widget inside of its container. */
+		INT32 getX() const { return mX; }
+
+		/**	Returns the Y position of the widget inside of its container. */
+		INT32 getY() const { return mY; }
+
+		/**	Returns the width of the widget in pixels. */
+		UINT32 getWidth() const { return mWidth; }
+
+		/**	Returns the height of the widget in pixels. */
+		UINT32 getHeight() const { return mHeight; }
+
+		/**	Returns the width of the widget when initially created, in pixels. */
+		UINT32 getDefaultWidth() const { return mDefaultWidth; }
+
+		/**	Returns the height of the widget when initially created, in pixels. */
+		UINT32 getDefaultHeight() const { return mDefaultHeight; }
+
+		/**	Returns the bounds of the widget in pixels, relative to its parent container. */
+		Rect2I getBounds() const { return Rect2I(mX, mY, mWidth, mHeight); }
+
+		/** Makes the widget in or out focus. Widget can only be made in focus if it is active. */
+		void setHasFocus(bool focus);
+
+		/**	Checks if the widget has focus (usually means user clicked on it last). */
+		bool hasFocus() const { return mHasFocus; }
+
+		/** 
+		 * Checks is the widget the currently active widget in its container. This means the widget's tab is active or
+		 * the widget is the only one in its container. 
+		 */
+		bool isActive() const { return mIsActive; }
+
+		/**
+		 * Gets the parent editor window this widget is docked in. Can be null (e.g. when widget is in the process of
+		 * dragging and not visible).
+		 */
+		EditorWindowBase* getParentWindow() const;
+
+		/**
+		 * Returns the parent widget container. Can be null (e.g. when widget is in the process of dragging and not 
+		 * visible). 
+		 */
+		EditorWidgetContainer* _getParent() const { return mParent; }
+
+		/**	Converts screen coordinates to coordinates relative to the widget. */
+		Vector2I screenToWidgetPos(const Vector2I& screenPos) const;
+
+		/**	Converts widget relative coordinates to screen coordiantes. */
+		Vector2I widgetToScreenPos(const Vector2I& widgetPos) const;
+
+		/**	Closes the widget, undocking it from its container and freeing any resources related to it. */
+		void close();
+
+		/** Internal method. Called once per frame.	 */
+		virtual void update() { }
+
+		Event<void(UINT32, UINT32)> onResized; /**< Triggered whenever widget size changes. */
+		Event<void(INT32, INT32)> onMoved; /**< Triggered whenever widget position changes. */
+		Event<void(EditorWidgetContainer*)> onParentChanged; /**< Triggered whenever widget parent container changes. */
+		Event<void(bool)> onFocusChanged; /**< Triggered whenever widget receives or loses focus. */
+
+		/** @cond INTERNAL */
+
+		/**	Changes the size of the widget (and its internal GUI panel). */
+		void _setSize(UINT32 width, UINT32 height);
+
+		/**	Changes the position of the widget (and its internal GUI panel), relative to its parent container. */
+		void _setPosition(INT32 x, INT32 y);
+
+		/**
+		 * Changes the parent container of the widget (e.g. when re-docking or moving a widget to another window). Parent
+		 * can be null (e.g. when widget is in the process of dragging and not visible).
+		 */
+		void _changeParent(EditorWidgetContainer* parent);
+
+		/**	Sets or removes focus for this widget. */
+		void _setHasFocus(bool focus);
+
+		/** Disables the widget making its GUI contents not visible. The widget	remains docked in its container. */
+		void _disable();
+
+		/**	Enables the widget making its previously hidden GUI contents visible. */
+		void _enable();
+
+		/** @endcond */
+	protected:
+		friend class EditorWidgetManager;
+
+		EditorWidgetBase(const HString& displayName, const String& name, UINT32 defaultWidth,
+			UINT32 defaultHeight, EditorWidgetContainer& parentContainer);
+		virtual ~EditorWidgetBase();
+
+		/**	Triggered whenever widget position changes. */
+		virtual void doOnMoved(INT32 x, INT32 y);
+
+		/**	Triggered whenever widget size changes. */
+		virtual void doOnResized(UINT32 width, UINT32 height);
+
+		/**	Triggered whenever widget parent container changes. */
+		virtual void doOnParentChanged();
+
+		/**
+		 * Returns the parent GUI widget. Before calling this you must ensure the widget has a container parent otherwise
+		 * this method will fail.
+		 */
+		GUIWidget& getParentWidget() const;
+
+		/**	Frees widget resources and deletes the instance. */
+		static void destroy(EditorWidgetBase* widget);
+
+		String mName;
+		HString mDisplayName;
+		EditorWidgetContainer* mParent;
+		INT32 mX, mY;
+		UINT32 mWidth, mHeight;
+		UINT32 mDefaultWidth, mDefaultHeight;
+		GUIPanel* mContent;
+		bool mHasFocus;
+		bool mIsActive;
+	};
+
+	/** @} */
+
+	/** @cond INTERNAL */
+	/** @addtogroup EditorWindow
+	 *  @{
+	 */
+
+	/**
+	 * Helper class that registers a widget creation callback with the widget manager. The creation callback allows the
+	 * runtime to open widgets just by their name without knowing the actual type.
+	 */
+	template<typename Type>
+	struct RegisterWidgetOnStart
+	{
+	public:
+		RegisterWidgetOnStart()
+		{
+			EditorWidgetManager::preRegisterWidget(Type::getTypeName(), &create);
+		}
+
+		/**	Creates a new widget of a specific type and adds it to the provided container. */
+		static EditorWidgetBase* create(EditorWidgetContainer& parentContainer)
+		{
+			return bs_new<Type>(EditorWidget<Type>::ConstructPrivately(), parentContainer);
+		}
+	};
+
+	/**
+	 * Editor widget template class that widgets can inherit from. Ensures that	all widget implementations are automatically
+	 * registered with the widget manager.
+	 *
+	 * @see		EditorWidgetBase
+	 */
+	template <class Type>
+	class EditorWidget : public EditorWidgetBase
+	{
+		static volatile RegisterWidgetOnStart<Type> RegisterOnStart;
+
+	protected:
+		friend struct RegisterWidgetOnStart<Type>;
+
+		struct ConstructPrivately {};
+
+		EditorWidget(const HString& displayName, UINT32 defaultWidth, UINT32 defaultHeight, 
+			EditorWidgetContainer& parentContainer)
+			:EditorWidgetBase(displayName, Type::getTypeName(), defaultWidth, defaultHeight, parentContainer)
+		{ }
+
+	public:
+
+		virtual ~EditorWidget() { }
+	};
+
+	template <typename Type>
+	volatile RegisterWidgetOnStart<Type> EditorWidget<Type>::RegisterOnStart;
+
+	/** @} */
+	/** @endcond */
 }

+ 190 - 171
Source/BansheeEditor/Source/BsEditorWidget.cpp

@@ -1,178 +1,197 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsEditorWidget.h"
-#include "BsGUIManager.h"
-#include "BsGUIWidget.h"
-#include "BsGUITexture.h"
-#include "BsGUILayout.h"
-#include "BsGUIPanel.h"
-#include "BsEditorWidgetContainer.h"
-#include "BsEditorWidgetManager.h"
-#include "BsEditorWindow.h"
-#include "BsRenderWindow.h"
-
-namespace BansheeEngine
-{
-	EditorWidgetBase::EditorWidgetBase(const HString& displayName, const String& name, UINT32 defaultWidth,
-		UINT32 defaultHeight, EditorWidgetContainer& parentContainer)
-		:mDisplayName(displayName), mName(name), mParent(nullptr), mContent(nullptr), mX(0), mY(0), mWidth(0), 
-		mHeight(0), mHasFocus(false), mDefaultWidth(defaultWidth), mDefaultHeight(defaultHeight), mIsActive(true)
-	{
-		parentContainer.add(*this);
-	}
-
-	EditorWidgetBase::~EditorWidgetBase()
-	{
-		if (mContent != nullptr)
-			GUILayout::destroy(mContent);
-	}
-
-	EditorWindowBase* EditorWidgetBase::getParentWindow() const
-	{
-		if (mParent != nullptr)
-			return mParent->getParentWindow();
-		else
-			return nullptr;
-	}
-
-	void EditorWidgetBase::close()
-	{
-		EditorWidgetManager::instance().close(this);
-	}
-
-	void EditorWidgetBase::destroy(EditorWidgetBase* widget)
-	{
-		widget->~EditorWidgetBase();
-		bs_free(widget);
-	}
-
-	void EditorWidgetBase::_setPosition(INT32 x, INT32 y)
-	{
-		if (mX == x && mY == y)
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsEditorWidget.h"
+#include "BsGUIManager.h"
+#include "BsGUIWidget.h"
+#include "BsGUITexture.h"
+#include "BsGUILayout.h"
+#include "BsGUIPanel.h"
+#include "BsEditorWidgetContainer.h"
+#include "BsEditorWidgetManager.h"
+#include "BsEditorWindow.h"
+#include "BsRenderWindow.h"
+
+namespace BansheeEngine
+{
+	EditorWidgetBase::EditorWidgetBase(const HString& displayName, const String& name, UINT32 defaultWidth,
+		UINT32 defaultHeight, EditorWidgetContainer& parentContainer)
+		:mDisplayName(displayName), mName(name), mParent(nullptr), mContent(nullptr), mX(0), mY(0), mWidth(0), 
+		mHeight(0), mHasFocus(false), mDefaultWidth(defaultWidth), mDefaultHeight(defaultHeight), mIsActive(true)
+	{
+		parentContainer.add(*this);
+	}
+
+	EditorWidgetBase::~EditorWidgetBase()
+	{
+		if (mContent != nullptr)
+			GUILayout::destroy(mContent);
+	}
+
+	EditorWindowBase* EditorWidgetBase::getParentWindow() const
+	{
+		if (mParent != nullptr)
+			return mParent->getParentWindow();
+		else
+			return nullptr;
+	}
+
+	void EditorWidgetBase::setHasFocus(bool focus)
+	{
+		EditorWidgetContainer* parentContainer = _getParent();
+		if (parentContainer == nullptr)
 			return;
 
-		mX = x;
-		mY = y;
+		EditorWindowBase* parentWindow = parentContainer->getParentWindow();
+		RenderWindowPtr parentRenderWindow = parentWindow->getRenderWindow();
+		const RenderWindowProperties& props = parentRenderWindow->getProperties();
 
-		if(mContent != nullptr)
-			mContent->setPosition(x, y);
-
-		doOnMoved(x, y);
-	}
-
-	void EditorWidgetBase::_setSize(UINT32 width, UINT32 height)
-	{
-		if (mWidth == width && mHeight == height)
+		if (!props.hasFocus())
 			return;
 
-		mWidth = width;
-		mHeight = height;
-
-		if (mContent != nullptr)
-		{
-			mContent->setWidth(width);
-			mContent->setHeight(height);
-		}
-
-		doOnResized(width, height);
-	}
-
-	void EditorWidgetBase::_setHasFocus(bool focus) 
-	{ 
-		if (mHasFocus != focus)
-		{
-			mHasFocus = focus;
-
-			onFocusChanged(focus);
-		}
-	}
-
-	Vector2I EditorWidgetBase::screenToWidgetPos(const Vector2I& screenPos) const
-	{
-		if (mParent == nullptr)
-			return screenPos;
-
-		EditorWindowBase* parentEditorWindow = mParent->getParentWindow();
-		RenderWindowPtr parentRenderWindow = parentEditorWindow->getRenderWindow();
-
-		Vector2I windowPos = parentRenderWindow->screenToWindowPos(screenPos);
-		windowPos.x -= mX;
-		windowPos.y -= mY;
-
-		return windowPos;
-	}
-
-	Vector2I EditorWidgetBase::widgetToScreenPos(const Vector2I& widgetPos) const
-	{
-		if (mParent == nullptr)
-			return widgetPos;
-
-		EditorWindowBase* parentEditorWindow = mParent->getParentWindow();
-		RenderWindowPtr parentRenderWindow = parentEditorWindow->getRenderWindow();
-
-		Vector2I windowPos = widgetPos;
-		windowPos.x += mX;
-		windowPos.y += mY;
-
-		return parentRenderWindow->windowToScreenPos(windowPos);
-	}
-
-	void EditorWidgetBase::doOnMoved(INT32 x, INT32 y)
-	{
-		onMoved(x, y);
-	}
-
-	void EditorWidgetBase::doOnResized(UINT32 width, UINT32 height)
-	{
-		onResized(width, height);
-	}
-
-	void EditorWidgetBase::doOnParentChanged()
-	{
-
-	}
-
-	// Note: Must not be virtual as parent container uses it in constructor
-	void EditorWidgetBase::_changeParent(EditorWidgetContainer* parent)
-	{
-		if(mParent != parent) 
-		{
-			if(parent != nullptr)
-			{
-				if (mContent == nullptr)
-					mContent = parent->getParentWidget().getPanel()->addNewElement<GUIPanel>();
-				else
-					parent->getParentWidget().getPanel()->addElement(mContent);
-			}
-			else
-			{
-				if (mContent != nullptr)
-					mParent->getParentWidget().getPanel()->removeElement(mContent);
-			}
-
-			mParent = parent;
-
-			doOnParentChanged();
-
-			if(!onParentChanged.empty())
-				onParentChanged(mParent);
-		}
-	}
-
-	void EditorWidgetBase::_disable()
-	{
-		mContent->setVisible(false);
-		mIsActive = false;
-	}
-
-	void EditorWidgetBase::_enable()
-	{
-		mContent->setVisible(true);
-		mIsActive = true;
-	}
+		if (parentContainer->getActiveWidget() != this)
+			return;
 
-	GUIWidget& EditorWidgetBase::getParentWidget() const
-	{
-		return mParent->getParentWidget();
-	}
+		_setHasFocus(focus);
+	}
+
+	void EditorWidgetBase::close()
+	{
+		EditorWidgetManager::instance().close(this);
+	}
+
+	void EditorWidgetBase::destroy(EditorWidgetBase* widget)
+	{
+		widget->~EditorWidgetBase();
+		bs_free(widget);
+	}
+
+	void EditorWidgetBase::_setPosition(INT32 x, INT32 y)
+	{
+		if (mX == x && mY == y)
+			return;
+
+		mX = x;
+		mY = y;
+
+		if(mContent != nullptr)
+			mContent->setPosition(x, y);
+
+		doOnMoved(x, y);
+	}
+
+	void EditorWidgetBase::_setSize(UINT32 width, UINT32 height)
+	{
+		if (mWidth == width && mHeight == height)
+			return;
+
+		mWidth = width;
+		mHeight = height;
+
+		if (mContent != nullptr)
+		{
+			mContent->setWidth(width);
+			mContent->setHeight(height);
+		}
+
+		doOnResized(width, height);
+	}
+
+	void EditorWidgetBase::_setHasFocus(bool focus) 
+	{ 
+		if (mHasFocus != focus)
+		{
+			mHasFocus = focus;
+
+			onFocusChanged(focus);
+		}
+	}
+
+	Vector2I EditorWidgetBase::screenToWidgetPos(const Vector2I& screenPos) const
+	{
+		if (mParent == nullptr)
+			return screenPos;
+
+		EditorWindowBase* parentEditorWindow = mParent->getParentWindow();
+		RenderWindowPtr parentRenderWindow = parentEditorWindow->getRenderWindow();
+
+		Vector2I windowPos = parentRenderWindow->screenToWindowPos(screenPos);
+		windowPos.x -= mX;
+		windowPos.y -= mY;
+
+		return windowPos;
+	}
+
+	Vector2I EditorWidgetBase::widgetToScreenPos(const Vector2I& widgetPos) const
+	{
+		if (mParent == nullptr)
+			return widgetPos;
+
+		EditorWindowBase* parentEditorWindow = mParent->getParentWindow();
+		RenderWindowPtr parentRenderWindow = parentEditorWindow->getRenderWindow();
+
+		Vector2I windowPos = widgetPos;
+		windowPos.x += mX;
+		windowPos.y += mY;
+
+		return parentRenderWindow->windowToScreenPos(windowPos);
+	}
+
+	void EditorWidgetBase::doOnMoved(INT32 x, INT32 y)
+	{
+		onMoved(x, y);
+	}
+
+	void EditorWidgetBase::doOnResized(UINT32 width, UINT32 height)
+	{
+		onResized(width, height);
+	}
+
+	void EditorWidgetBase::doOnParentChanged()
+	{
+
+	}
+
+	// Note: Must not be virtual as parent container uses it in constructor
+	void EditorWidgetBase::_changeParent(EditorWidgetContainer* parent)
+	{
+		if(mParent != parent) 
+		{
+			if(parent != nullptr)
+			{
+				if (mContent == nullptr)
+					mContent = parent->getParentWidget().getPanel()->addNewElement<GUIPanel>();
+				else
+					parent->getParentWidget().getPanel()->addElement(mContent);
+			}
+			else
+			{
+				if (mContent != nullptr)
+					mParent->getParentWidget().getPanel()->removeElement(mContent);
+			}
+
+			mParent = parent;
+
+			doOnParentChanged();
+
+			if(!onParentChanged.empty())
+				onParentChanged(mParent);
+		}
+	}
+
+	void EditorWidgetBase::_disable()
+	{
+		mContent->setVisible(false);
+		mIsActive = false;
+	}
+
+	void EditorWidgetBase::_enable()
+	{
+		mContent->setVisible(true);
+		mIsActive = true;
+	}
+
+	GUIWidget& EditorWidgetBase::getParentWidget() const
+	{
+		return mParent->getParentWidget();
+	}
 }

+ 315 - 305
Source/BansheeEngine/Include/BsGUIElement.h

@@ -1,306 +1,316 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#pragma once
-
-#include "BsPrerequisites.h"
-#include "BsGUIElementBase.h"
-#include "BsGUIOptions.h"
-#include "BsRect2I.h"
-#include "BsVector2I.h"
-#include "BsColor.h"
-
-namespace BansheeEngine
-{
-	/** @addtogroup Implementation
-	 *  @{
-	 */
-
-	/**
-	 * Represents parent class for all visible GUI elements. Contains methods needed for positioning, rendering and
-	 * handling input.
-	 */
-	class BS_EXPORT GUIElement : public GUIElementBase
-	{
-	public:
-		/**	Different sub-types of GUI elements. */
-		enum class ElementType
-		{
-			Label,
-			Button,
-			Toggle,
-			Texture,
-			InputBox,
-			ListBox,
-			ScrollArea,
-			Layout,
-			Undefined
-		};
-
-	public:
-		GUIElement(const String& styleName, const GUIDimensions& dimensions);
-		virtual ~GUIElement();
-
-		/**	Sets or removes focus from an element. Will change element style. */
-		void setFocus(bool enabled);
-
-		/**	Sets the tint of the GUI element. */
-		virtual void setTint(const Color& color);
-
-		/** @copydoc GUIElementBase::resetDimensions */
-		virtual void resetDimensions() override;
-
-		/**	Sets new style to be used by the element. */
-		void setStyle(const String& styleName);
-
-		/**	Returns the name of the style used by this element. */
-		const String& getStyleName() const { return mStyleName; }
-
-		/**
-		 * Assigns a new context menu that will be opened when the element is right clicked. Null is allowed in case no
-		 * context menu is wanted.
-		 */
-		void setContextMenu(const GUIContextMenuPtr& menu) { mContextMenu = menu; }
-
-		/** @copydoc GUIElementBase::getVisibleBounds */
-		Rect2I getVisibleBounds() override;
-
-		/**
-		 * Destroy the element. Removes it from parent and widget, and queues it for deletion. Element memory will be 
-		 * released delayed, next frame.
-		 */	
-		static void destroy(GUIElement* element);
-
-		/**	Triggered when the element loses or gains focus. */
-		Event<void(bool)> onFocusChanged;
-
-	public: // ***** INTERNAL ******
-		/** @cond INTERNAL */
-
-		/**
-		 * Returns the number of separate render elements in the GUI element.
-		 * 			
-		 * @return	The number render elements.
-		 *
-		 * @note	
-		 * GUI system attempts to reduce the number of GUI meshes so it will group sprites based on their material and 
-		 * textures. One render elements represents a group of such sprites that share a material/texture.
-		 */
-		virtual UINT32 _getNumRenderElements() const = 0;
-
-		/**
-		 * Gets a material for the specified render element index.
-		 * 		
-		 * @return	Handle to the material.
-		 *
-		 * @see		_getNumRenderElements()
-		 */
-		virtual const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx) const = 0;
-
-		/**
-		 * Returns the number of quads that the specified render element will use. You will need this value when creating
-		 * the buffers before calling _fillBuffer().
-		 * 			
-		 * @return	Number of quads for the specified render element. 
-		 *
-		 * @see		_getNumRenderElements()
-		 * @see		_fillBuffer()
-		 * 		
-		 * @note	
-		 * Number of vertices = Number of quads * 4
-		 * Number of indices = Number of quads * 6	
-		 */
-		virtual UINT32 _getNumQuads(UINT32 renderElementIdx) const = 0;
-
-		/**
-		 * Fill the pre-allocated vertex, uv and index buffers with the mesh data for the specified render element.
-		 * 			
-		 * @param[out]	vertices			Previously allocated buffer where to store the vertices.
-		 * @param[out]	uv					Previously allocated buffer where to store the uv coordinates.
-		 * @param[out]	indices				Previously allocated buffer where to store the indices.
-		 * @param[in]	startingQuad		At which quad should the method start filling the buffer.
-		 * @param[in]	maxNumQuads			Total number of quads the buffers were allocated for. Used only for memory 
-		 *									safety.
-		 * @param[in]	vertexStride		Number of bytes between of vertices in the provided vertex and uv data.
-		 * @param[in]	indexStride			Number of bytes between two indexes in the provided index data.
-		 * @param[in]	renderElementIdx	Zero-based index of the render element.
-		 *
-		 * @see		_getNumRenderElements()
-		 * @see		_getNumQuads()
-		 */
-		virtual void _fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, 
-			UINT32 maxNumQuads, UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const = 0;
-
-		/**
-		 * Recreates the internal render elements. Must be called before fillBuffer if element is dirty. Marks the element
-		 * as non dirty.
-		 */
-		void _updateRenderElements();
-
-		/** Gets internal element style representing the exact type of GUI element in this object. */
-		virtual ElementType _getElementType() const { return ElementType::Undefined; }
-
-		/**
-		 * Called when a mouse event is received on any GUI element the mouse is interacting with. Return true if you have
-		 * processed the event and don't want other elements to process it.
-		 */
-		virtual bool _mouseEvent(const GUIMouseEvent& ev);
-
-		/**
-		 * Called when some text is input and the GUI element has input focus. Return true if you have processed the event
-		 * and don't want other elements to process it.
-		 */	
-		virtual bool _textInputEvent(const GUITextInputEvent& ev);
-
-		/**
-		 * Called when a command event is triggered. Return true if you have processed the event and don't want other 
-		 * elements to process it.
-		 */
-		virtual bool _commandEvent(const GUICommandEvent& ev);
-
-		/**
-		 * Called when a virtual button is pressed/released and the GUI element has input focus. Return true if you have
-		 * processed the event and don't want other elements to process it.
-		 */
-		virtual bool _virtualButtonEvent(const GUIVirtualButtonEvent& ev);
-
-		/** Set element part of element depth. Less significant than both widget and area depth. */
-		void _setElementDepth(UINT8 depth);
-
-		/** Retrieve element part of element depth. Less significant than both widget and area depth. */
-		UINT8 _getElementDepth() const;
-
-		/** @copydoc GUIElementBase::_setLayoutData */
-		virtual void _setLayoutData(const GUILayoutData& data) override;
-
-		/** @copydoc GUIElementBase::_changeParentWidget */
-		virtual void _changeParentWidget(GUIWidget* widget) override;
-
-		/**
-		 * Returns depth for a specific render element. This contains a combination of widget depth (8 bit(, area depth
-		 * (16 bit) and render element depth (8 bit).
-		 *
-		 * @see		_getNumRenderElements
-		 */
-		virtual UINT32 _getRenderElementDepth(UINT32 renderElementIdx) const { return _getDepth(); }
-
-		/**
-		 * Returns the range of depths that the child elements can be rendered it.
-		 *
-		 * @note	
-		 * e.g. if you are rendering a button with an image and a text you will want the text to be rendered in front of
-		 * the image at a different depth, which means the depth range is 2 (0 for text, 1 for background image).
-		 */
-		virtual UINT32 _getRenderElementDepthRange() const { return 1; }
-
-		/** Gets internal element style representing the exact type of GUI element in this object. */
-		Type _getType() const override { return GUIElementBase::Type::Element; }
-
-		/** Checks if element has been destroyed and is queued for deletion. */
-		bool _isDestroyed() const override { return mIsDestroyed; }
-
-		/** Update element style based on active GUI skin and style name. */
-		void _refreshStyle();
-
-		/**	Gets the currently active element style. */
-		const GUIElementStyle* _getStyle() const { return mStyle; }
-
-		/**	Gets GUI element bounds relative to parent widget, clipped by specified clip rect. */
-		const Rect2I& _getClippedBounds() const { return mClippedBounds; }
-
-		/** 
-		 * Returns GUI element padding. Padding is modified by changing element style and determines minimum distance 
-		 * between different GUI elements. 
-		 */
-		const RectOffset& _getPadding() const override;
-
-		/**
-		 * Returns GUI element depth. This includes widget and area depth, but does not include specific per-render-element
-		 * depth.
-		 */
-		UINT32 _getDepth() const { return mLayoutData.depth; }
-
-		/** Checks is the specified position within GUI element bounds. Position is relative to parent GUI widget. */
-		virtual bool _isInBounds(const Vector2I position) const;
-
-		/**	Checks if the GUI element has a custom cursor and outputs the cursor type if it does. */
-		virtual bool _hasCustomCursor(const Vector2I position, CursorType& type) const { return false; }
-
-		/**	Checks if the GUI element accepts a drag and drop operation of the specified type. */
-		virtual bool _acceptDragAndDrop(const Vector2I position, UINT32 typeId) const { return false; }
-
-		/**	Returns a context menu if a GUI element has one. Otherwise returns nullptr. */
-		virtual GUIContextMenuPtr _getContextMenu() const;
-
-		/**	Returns text to display when hovering over the element. Returns empty string if no tooltip. */
-		virtual WString _getTooltip() const { return StringUtil::WBLANK; }
-
-		/**	Returns a clip rectangle relative to the element, used for offsetting the input text. */
-		virtual Vector2I _getTextInputOffset() const { return Vector2I(); }
-
-		/**	Returns a clip rectangle relative to the element, used for clipping	the input text. */
-		virtual Rect2I _getTextInputRect() const { return Rect2I(); }
-
-		/** @endcond */
-
-	protected:
-		/**	Called whenever render elements are dirty and need to be rebuilt. */
-		virtual void updateRenderElementsInternal();
-
-		/**
-		 * Called whenever element clipped bounds need to be recalculated. (e.g. when width, height or clip rectangles 
-		 * changes).
-		 */
-		virtual void updateClippedBounds();
-
-		/**
-		 * Helper method that returns style name used by an element of a certain type. If override style is empty, default
-		 * style for that type is returned.
-		 */
-		template<class T>
-		static const String& getStyleName(const String& overrideStyle)
-		{
-			if(overrideStyle == StringUtil::BLANK)
-				return T::getGUITypeName();
-
-			return overrideStyle;
-		}
-
-		/**
-		 * Attempts to find a sub-style for the specified type in the currently set GUI element style. If one cannot be
-		 * found empty string is returned.
-		 */
-		const String& getSubStyleName(const String& subStyleTypeName) const;
-
-		/**	Method that gets triggered whenever element style changes. */
-		virtual void styleUpdated() { }
-
-		/**	Returns clipped bounds excluding the margins. Relative to parent widget. */
-		Rect2I getCachedVisibleBounds() const;
-
-		/**	Returns bounds of the content contained within the GUI element. Relative to parent widget. */
-		Rect2I getCachedContentBounds() const;
-
-		/**
-		 * Returns a clip rectangle that can be used for clipping the contents of this GUI element. Clip rect is relative
-		 * to GUI element origin.
-		 */
-		Rect2I getCachedContentClipRect() const;
-
-		/**	Returns the tint that is applied to the GUI element. */
-		Color getTint() const;
-
-		bool mIsDestroyed;
-		Rect2I mClippedBounds;
-		
-	private:
-		static const Color DISABLED_COLOR;
-
-		const GUIElementStyle* mStyle;
-		String mStyleName;
-
-		GUIContextMenuPtr mContextMenu;
-		Color mColor;
-	};
-
-	/** @} */
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "BsGUIElementBase.h"
+#include "BsGUIOptions.h"
+#include "BsRect2I.h"
+#include "BsVector2I.h"
+#include "BsColor.h"
+
+namespace BansheeEngine
+{
+	/** @addtogroup Implementation
+	 *  @{
+	 */
+
+	/**
+	 * Represents parent class for all visible GUI elements. Contains methods needed for positioning, rendering and
+	 * handling input.
+	 */
+	class BS_EXPORT GUIElement : public GUIElementBase
+	{
+	public:
+		/**	Different sub-types of GUI elements. */
+		enum class ElementType
+		{
+			Label,
+			Button,
+			Toggle,
+			Texture,
+			InputBox,
+			ListBox,
+			ScrollArea,
+			Layout,
+			Undefined
+		};
+
+	public:
+		GUIElement(const String& styleName, const GUIDimensions& dimensions);
+		virtual ~GUIElement();
+
+		/**	Sets or removes focus from an element. Will change element style. */
+		void setFocus(bool enabled);
+
+		/**	Sets the tint of the GUI element. */
+		virtual void setTint(const Color& color);
+
+		/** @copydoc GUIElementBase::resetDimensions */
+		virtual void resetDimensions() override;
+
+		/**	Sets new style to be used by the element. */
+		void setStyle(const String& styleName);
+
+		/**	Returns the name of the style used by this element. */
+		const String& getStyleName() const { return mStyleName; }
+
+		/** 
+		 * Determines will this element block elements underneath it from receiving pointer events (clicks, focus 
+		 * gain/lost, hover on/off, etc.). Enabled by default.
+		 */
+		void setBlockPointerEvents(bool block) { mBlockPointerEvents = block; }
+
+		/** @copydoc setBlockPointerEvents */
+		bool getBlockPointerEvents() const { return mBlockPointerEvents; }
+
+		/**
+		 * Assigns a new context menu that will be opened when the element is right clicked. Null is allowed in case no
+		 * context menu is wanted.
+		 */
+		void setContextMenu(const GUIContextMenuPtr& menu) { mContextMenu = menu; }
+
+		/** @copydoc GUIElementBase::getVisibleBounds */
+		Rect2I getVisibleBounds() override;
+
+		/**
+		 * Destroy the element. Removes it from parent and widget, and queues it for deletion. Element memory will be 
+		 * released delayed, next frame.
+		 */	
+		static void destroy(GUIElement* element);
+
+		/**	Triggered when the element loses or gains focus. */
+		Event<void(bool)> onFocusChanged;
+
+	public: // ***** INTERNAL ******
+		/** @cond INTERNAL */
+
+		/**
+		 * Returns the number of separate render elements in the GUI element.
+		 * 			
+		 * @return	The number render elements.
+		 *
+		 * @note	
+		 * GUI system attempts to reduce the number of GUI meshes so it will group sprites based on their material and 
+		 * textures. One render elements represents a group of such sprites that share a material/texture.
+		 */
+		virtual UINT32 _getNumRenderElements() const = 0;
+
+		/**
+		 * Gets a material for the specified render element index.
+		 * 		
+		 * @return	Handle to the material.
+		 *
+		 * @see		_getNumRenderElements()
+		 */
+		virtual const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx) const = 0;
+
+		/**
+		 * Returns the number of quads that the specified render element will use. You will need this value when creating
+		 * the buffers before calling _fillBuffer().
+		 * 			
+		 * @return	Number of quads for the specified render element. 
+		 *
+		 * @see		_getNumRenderElements()
+		 * @see		_fillBuffer()
+		 * 		
+		 * @note	
+		 * Number of vertices = Number of quads * 4
+		 * Number of indices = Number of quads * 6	
+		 */
+		virtual UINT32 _getNumQuads(UINT32 renderElementIdx) const = 0;
+
+		/**
+		 * Fill the pre-allocated vertex, uv and index buffers with the mesh data for the specified render element.
+		 * 			
+		 * @param[out]	vertices			Previously allocated buffer where to store the vertices.
+		 * @param[out]	uv					Previously allocated buffer where to store the uv coordinates.
+		 * @param[out]	indices				Previously allocated buffer where to store the indices.
+		 * @param[in]	startingQuad		At which quad should the method start filling the buffer.
+		 * @param[in]	maxNumQuads			Total number of quads the buffers were allocated for. Used only for memory 
+		 *									safety.
+		 * @param[in]	vertexStride		Number of bytes between of vertices in the provided vertex and uv data.
+		 * @param[in]	indexStride			Number of bytes between two indexes in the provided index data.
+		 * @param[in]	renderElementIdx	Zero-based index of the render element.
+		 *
+		 * @see		_getNumRenderElements()
+		 * @see		_getNumQuads()
+		 */
+		virtual void _fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, 
+			UINT32 maxNumQuads, UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const = 0;
+
+		/**
+		 * Recreates the internal render elements. Must be called before fillBuffer if element is dirty. Marks the element
+		 * as non dirty.
+		 */
+		void _updateRenderElements();
+
+		/** Gets internal element style representing the exact type of GUI element in this object. */
+		virtual ElementType _getElementType() const { return ElementType::Undefined; }
+
+		/**
+		 * Called when a mouse event is received on any GUI element the mouse is interacting with. Return true if you have
+		 * processed the event and don't want other elements to process it.
+		 */
+		virtual bool _mouseEvent(const GUIMouseEvent& ev);
+
+		/**
+		 * Called when some text is input and the GUI element has input focus. Return true if you have processed the event
+		 * and don't want other elements to process it.
+		 */	
+		virtual bool _textInputEvent(const GUITextInputEvent& ev);
+
+		/**
+		 * Called when a command event is triggered. Return true if you have processed the event and don't want other 
+		 * elements to process it.
+		 */
+		virtual bool _commandEvent(const GUICommandEvent& ev);
+
+		/**
+		 * Called when a virtual button is pressed/released and the GUI element has input focus. Return true if you have
+		 * processed the event and don't want other elements to process it.
+		 */
+		virtual bool _virtualButtonEvent(const GUIVirtualButtonEvent& ev);
+
+		/** Set element part of element depth. Less significant than both widget and area depth. */
+		void _setElementDepth(UINT8 depth);
+
+		/** Retrieve element part of element depth. Less significant than both widget and area depth. */
+		UINT8 _getElementDepth() const;
+
+		/** @copydoc GUIElementBase::_setLayoutData */
+		virtual void _setLayoutData(const GUILayoutData& data) override;
+
+		/** @copydoc GUIElementBase::_changeParentWidget */
+		virtual void _changeParentWidget(GUIWidget* widget) override;
+
+		/**
+		 * Returns depth for a specific render element. This contains a combination of widget depth (8 bit(, area depth
+		 * (16 bit) and render element depth (8 bit).
+		 *
+		 * @see		_getNumRenderElements
+		 */
+		virtual UINT32 _getRenderElementDepth(UINT32 renderElementIdx) const { return _getDepth(); }
+
+		/**
+		 * Returns the range of depths that the child elements can be rendered it.
+		 *
+		 * @note	
+		 * e.g. if you are rendering a button with an image and a text you will want the text to be rendered in front of
+		 * the image at a different depth, which means the depth range is 2 (0 for text, 1 for background image).
+		 */
+		virtual UINT32 _getRenderElementDepthRange() const { return 1; }
+
+		/** Gets internal element style representing the exact type of GUI element in this object. */
+		Type _getType() const override { return GUIElementBase::Type::Element; }
+
+		/** Checks if element has been destroyed and is queued for deletion. */
+		bool _isDestroyed() const override { return mIsDestroyed; }
+
+		/** Update element style based on active GUI skin and style name. */
+		void _refreshStyle();
+
+		/**	Gets the currently active element style. */
+		const GUIElementStyle* _getStyle() const { return mStyle; }
+
+		/**	Gets GUI element bounds relative to parent widget, clipped by specified clip rect. */
+		const Rect2I& _getClippedBounds() const { return mClippedBounds; }
+
+		/** 
+		 * Returns GUI element padding. Padding is modified by changing element style and determines minimum distance 
+		 * between different GUI elements. 
+		 */
+		const RectOffset& _getPadding() const override;
+
+		/**
+		 * Returns GUI element depth. This includes widget and area depth, but does not include specific per-render-element
+		 * depth.
+		 */
+		UINT32 _getDepth() const { return mLayoutData.depth; }
+
+		/** Checks is the specified position within GUI element bounds. Position is relative to parent GUI widget. */
+		virtual bool _isInBounds(const Vector2I position) const;
+
+		/**	Checks if the GUI element has a custom cursor and outputs the cursor type if it does. */
+		virtual bool _hasCustomCursor(const Vector2I position, CursorType& type) const { return false; }
+
+		/**	Checks if the GUI element accepts a drag and drop operation of the specified type. */
+		virtual bool _acceptDragAndDrop(const Vector2I position, UINT32 typeId) const { return false; }
+
+		/**	Returns a context menu if a GUI element has one. Otherwise returns nullptr. */
+		virtual GUIContextMenuPtr _getContextMenu() const;
+
+		/**	Returns text to display when hovering over the element. Returns empty string if no tooltip. */
+		virtual WString _getTooltip() const { return StringUtil::WBLANK; }
+
+		/**	Returns a clip rectangle relative to the element, used for offsetting the input text. */
+		virtual Vector2I _getTextInputOffset() const { return Vector2I(); }
+
+		/**	Returns a clip rectangle relative to the element, used for clipping	the input text. */
+		virtual Rect2I _getTextInputRect() const { return Rect2I(); }
+
+		/** @endcond */
+
+	protected:
+		/**	Called whenever render elements are dirty and need to be rebuilt. */
+		virtual void updateRenderElementsInternal();
+
+		/**
+		 * Called whenever element clipped bounds need to be recalculated. (e.g. when width, height or clip rectangles 
+		 * changes).
+		 */
+		virtual void updateClippedBounds();
+
+		/**
+		 * Helper method that returns style name used by an element of a certain type. If override style is empty, default
+		 * style for that type is returned.
+		 */
+		template<class T>
+		static const String& getStyleName(const String& overrideStyle)
+		{
+			if(overrideStyle == StringUtil::BLANK)
+				return T::getGUITypeName();
+
+			return overrideStyle;
+		}
+
+		/**
+		 * Attempts to find a sub-style for the specified type in the currently set GUI element style. If one cannot be
+		 * found empty string is returned.
+		 */
+		const String& getSubStyleName(const String& subStyleTypeName) const;
+
+		/**	Method that gets triggered whenever element style changes. */
+		virtual void styleUpdated() { }
+
+		/**	Returns clipped bounds excluding the margins. Relative to parent widget. */
+		Rect2I getCachedVisibleBounds() const;
+
+		/**	Returns bounds of the content contained within the GUI element. Relative to parent widget. */
+		Rect2I getCachedContentBounds() const;
+
+		/**
+		 * Returns a clip rectangle that can be used for clipping the contents of this GUI element. Clip rect is relative
+		 * to GUI element origin.
+		 */
+		Rect2I getCachedContentClipRect() const;
+
+		/**	Returns the tint that is applied to the GUI element. */
+		Color getTint() const;
+
+		bool mIsDestroyed;
+		bool mBlockPointerEvents;
+		Rect2I mClippedBounds;
+		
+	private:
+		static const Color DISABLED_COLOR;
+
+		const GUIElementStyle* mStyle;
+		String mStyleName;
+
+		GUIContextMenuPtr mContextMenu;
+		Color mColor;
+	};
+
+	/** @} */
 }

+ 43 - 40
Source/BansheeEngine/Include/BsGUIElementContainer.h

@@ -1,41 +1,44 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#pragma once
-
-#include "BsPrerequisites.h"
-#include "BsGUIElement.h"
-
-namespace BansheeEngine
-{
-	/** @cond INTERNAL */
-	/** @addtogroup GUI
-	 *  @{
-	 */
-
-	/** Base for GUI elements that combine multiple GUI elements into one more complex element. */
-	class BS_EXPORT GUIElementContainer : public GUIElement
-	{
-	protected:
-		GUIElementContainer(const GUIDimensions& dimensions, const String& style = StringUtil::BLANK);
-		virtual ~GUIElementContainer();
-
-		/** @copydoc GUIElement::_getNumRenderElements */
-		virtual UINT32 _getNumRenderElements() const override;
-
-		/** @copydoc GUIElement::_getMaterial */
-		virtual const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx) const override;
-
-		/** @copydoc GUIElement::_getNumQuads */
-		virtual UINT32 _getNumQuads(UINT32 renderElementIdx) const override;
-
-		/** @copydoc GUIElement::_fillBuffer */
-		virtual void _fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, 
-			UINT32 maxNumQuads, UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const override;
-
-		/** @copydoc GUIElement::_getOptimalSize */
-		virtual Vector2I _getOptimalSize() const override;
-	};
-
-	/** @} */
-	/** @endcond */
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "BsGUIElement.h"
+
+namespace BansheeEngine
+{
+	/** @cond INTERNAL */
+	/** @addtogroup GUI
+	 *  @{
+	 */
+
+	/** Base for GUI elements that combine multiple GUI elements into one more complex element. */
+	class BS_EXPORT GUIElementContainer : public GUIElement
+	{
+	protected:
+		GUIElementContainer(const GUIDimensions& dimensions, const String& style = StringUtil::BLANK);
+		virtual ~GUIElementContainer();
+
+		/** @copydoc GUIElement::_getNumRenderElements */
+		virtual UINT32 _getNumRenderElements() const override;
+
+		/** @copydoc GUIElement::_getMaterial */
+		virtual const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx) const override;
+
+		/** @copydoc GUIElement::_getNumQuads */
+		virtual UINT32 _getNumQuads(UINT32 renderElementIdx) const override;
+
+		/** @copydoc GUIElement::_fillBuffer */
+		virtual void _fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, 
+			UINT32 maxNumQuads, UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const override;
+
+		/** @copydoc GUIElement::_getOptimalSize */
+		virtual Vector2I _getOptimalSize() const override;
+
+		/** @copydoc GUIElementBase::_commandEvent */
+		bool _commandEvent(const GUICommandEvent& event) override;
+	};
+
+	/** @} */
+	/** @endcond */
 }

+ 464 - 451
Source/BansheeEngine/Include/BsGUIManager.h

@@ -1,452 +1,465 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#pragma once
-
-#include "BsPrerequisites.h"
-#include "BsGUIMouseEvent.h"
-#include "BsGUITextInputEvent.h"
-#include "BsGUICommandEvent.h"
-#include "BsGUIVirtualButtonEvent.h"
-#include "BsSprite.h"
-#include "BsModule.h"
-#include "BsColor.h"
-#include "BsMatrix4.h"
-#include "BsEvent.h"
-#include "BsMaterialParam.h"
-
-namespace BansheeEngine
-{
-	/** @cond INTERNAL */
-	/** @addtogroup GUI
-	 *  @{
-	 */
-
-	class GUIManagerCore;
-
-	/**
-	 * Manages the rendering and input of all GUI widgets in the scene. 
-	 * 			
-	 * @note	
-	 * If adding or modifying GUIManager functionality ensure that GUIManager data never gets modified outside of update()
-	 * method or Input callbacks. If you need such functionality add temporary variables that store you changes and then
-	 * execute them delayed in update().
-	 * @par
-	 * This ensures that GUIElements don't recursively modify GUIManager while GUIManager is still using that data.
-	 * @par
-	 * e.g. setFocus() usually gets called from within GUIElements, however we don't want elements in focus be modified 
-	 * immediately since that setFocus() call could have originated in sendCommandEvent and elements in focus array would
-	 * be modified while still being iterated upon.
-	 * @note
-	 * Internal class. Unless modifying internal engine systems you should have no need to access this class.
-	 */
-	class BS_EXPORT GUIManager : public Module<GUIManager>
-	{
-		/**	Valid states of a drag and drop operation. */
-		enum class DragState
-		{
-			NoDrag,
-			HeldWithoutDrag,
-			Dragging
-		};
-
-		/**	GUI render data for a single viewport. */
-		struct GUIRenderData
-		{
-			GUIRenderData()
-				:isDirty(true)
-			{ }
-
-			Vector<TransientMeshPtr> cachedMeshes;
-			Vector<SpriteMaterialInfo> cachedMaterials;
-			Vector<GUIWidget*> cachedWidgetsPerMesh;
-			Vector<GUIWidget*> widgets;
-			bool isDirty;
-		};
-
-		/**	Render data for a single GUI group used for notifying the core GUI renderer. */
-		struct GUICoreRenderData
-		{
-			SPtr<TransientMeshCore> mesh;
-			SPtr<TextureCore> texture;
-			SpriteMaterial materialType;
-			Color tint;
-			Matrix4 worldTransform;
-		};
-
-		/**	Container for a GUI widget. */
-		struct WidgetInfo
-		{
-			WidgetInfo(GUIWidget* _widget)
-				:widget(_widget)
-			{ }
-
-			GUIWidget* widget;
-		};
-
-		/**	Container for data about a single GUI element and its widget. */
-		struct ElementInfo
-		{
-			ElementInfo(GUIElement* element, GUIWidget* widget)
-				:element(element), widget(widget)
-			{ }
-
-			GUIElement* element;
-			GUIWidget* widget;
-		};
-
-		/**	Container for data about a single GUI element and its widget currently under the pointer. */
-		struct ElementInfoUnderPointer
-		{
-			ElementInfoUnderPointer(GUIElement* element, GUIWidget* widget)
-				:element(element), widget(widget), usesMouseOver(false), 
-				receivedMouseOver(false), isHovering(false)
-			{ }
-
-			GUIElement* element;
-			GUIWidget* widget;
-			bool usesMouseOver;
-			bool receivedMouseOver;
-			bool isHovering;
-		};
-
-		/**	Container for GUI element in focus. */
-		struct ElementFocusInfo
-		{
-			GUIElement* element;
-			bool focus;
-		};
-
-	public:
-		GUIManager();
-		~GUIManager();
-
-		/** Registers a newly created widget with the GUI manager. This should be called by every GUI widget on creation. */
-		void registerWidget(GUIWidget* widget);
-
-		/**
-		 * Unregisters a GUI widget from the GUI manager. This should be called by every GUI widget before getting deleted.
-		 */
-		void unregisterWidget(GUIWidget* widget);
-
-		/**	Called once per frame. */
-		void update();
-
-		/** Queues the GUI element for destruction. Element will be destroyed during the next call to update(). */
-		void queueForDestroy(GUIElement* element);
-
-		/**	Change the GUI element focus state. */
-		void setFocus(GUIElement* element, bool focus);
-
-		/**	Changes the color of the input caret used in input boxes and similar controls. */
-		void setCaretColor(const Color& color) { mCaretColor = color; updateCaretTexture(); }
-
-		/**	Changes the text selection highlight color used in input boxes and similar controls. */
-		void setTextSelectionColor(const Color& color) { mTextSelectionColor = color; updateTextSelectionTexture(); }
-
-		/**	Returns the default caret texture used for rendering the input caret sprite. */
-		const HSpriteTexture& getCaretTexture() const { return mCaretTexture; }
-
-		/**	Returns the default selection highlight texture used for rendering the selection highlight sprites. */
-		const HSpriteTexture& getTextSelectionTexture() const { return mTextSelectionTexture; }
-
-		/**	Checks is the input caret visible this frame. */
-		bool getCaretBlinkState() const { return mIsCaretOn; }
-
-		/**
-		 * Returns input caret helper tool that allows you to easily position and show an input caret in your GUI controls.
-		 */
-		GUIInputCaret* getInputCaretTool() const { return mInputCaret; }
-
-		/**
-		 * Returns input selection helper tool that allows you to easily position and show an input selection highlight in
-		 * your GUI controls.
-		 */
-		GUIInputSelection* getInputSelectionTool() const { return mInputSelection; }
-
-		/**
-		 * Allows you to bridge GUI input from a GUI element into another render target.
-		 *
-		 * @param[in]	renderTex 	The render target to which to bridge the input.
-		 * @param[in]	element		The element from which to bridge input. Input will be transformed according to this 
-		 *							elements position and size. Provide nullptr if you want to remove a bridge for the
-		 *							specified widget.
-		 * 					
-		 * @note	
-		 * This is useful if you use render textures, where your GUI is rendered off-screen. In such case you need to 
-		 * display the render texture within another GUIElement in a GUIWidget, but have no way of sending input to the
-		 * render texture (normally input is only sent to render windows). This allows you to change that - any GUIWidget
-		 * using the bridged render texture as a render target will then receive input when mouse is over the specified
-		 * element.
-		 * @note			
-		 * Bridged element needs to remove itself as the bridge when it is destroyed.
-		 */
-		void setInputBridge(const RenderTexture* renderTex, const GUIElement* element);
-
-		/** Gets the core thread portion of the GUI manager, responsible for rendering of GUI elements. */
-		GUIManagerCore* getCore() const { return mCore.load(std::memory_order_relaxed); }
-
-	private:
-		friend class GUIManagerCore;
-
-		/**	Recreates all dirty GUI meshes and makes them ready for rendering. */
-		void updateMeshes();
-
-		/**	Recreates the input caret texture. */
-		void updateCaretTexture();
-
-		/**	Recreates the input text selection highlight texture. */
-		void updateTextSelectionTexture();
-
-		/**
-		 * Destroys the core thread counterpart of the GUI manager.
-		 *
-		 * @param[in]	core	Previously constructed core thread GUI manager instance.
-		 */
-		void destroyCore(GUIManagerCore* core);
-
-		/**
-		 * Destroys any elements or widgets queued for destruction.
-		 *
-		 * @note	
-		 * Returns true if more elements have been added for destruction (will happen when destruction of one element
-		 * queues up destruction of another).
-		 */
-		bool processDestroyQueue();
-
-		/**
-		 * Finds a GUI element under the pointer at the specified screen position. This method will also trigger pointer
-		 * move/hover/leave events.
-		 *
-		 * @param[in]	screenMousePos	Position of the pointer in screen coordinates.
-		 * @param[in]	buttonStates	States of the three mouse buttons (left, right, middle).
-		 * @param[in]	shift			Is shift key held.
-		 * @param[in]	control			Is control key held.
-		 * @param[in]	alt				Is alt key held.
-		 */
-		bool findElementUnderPointer(const Vector2I& screenMousePos, bool buttonStates[3], bool shift, bool control, bool alt);
-
-		/**	Called whenever a pointer (e.g. mouse cursor) is moved. */
-		void onPointerMoved(const PointerEvent& event);
-
-		/**	Called whenever a pointer button (e.g. mouse button) is released. */
-		void onPointerReleased(const PointerEvent& event);
-
-		/**	Called whenever a pointer button (e.g. mouse button) is pressed. */
-		void onPointerPressed(const PointerEvent& event);
-
-		/**	Called whenever a pointer button (e.g. mouse button) is double clicked. */
-		void onPointerDoubleClick(const PointerEvent& event);
-
-		/**	Called whenever a text is input. */
-		void onTextInput(const TextInputEvent& event);
-
-		/**	Called whenever an input command is input. */
-		void onInputCommandEntered(InputCommandType commandType);
-
-		/**	Called whenever a virtual button is pressed. */
-		void onVirtualButtonDown(const VirtualButton& button, UINT32 deviceIdx);
-
-		/**	Called by the drag and drop managed to notify us the drag ended. */
-		void onMouseDragEnded(const PointerEvent& event, DragCallbackInfo& dragInfo);
-
-		/**	Called when the specified window gains focus. */
-		void onWindowFocusGained(RenderWindow& win);
-
-		/**	Called when the specified window loses focus. */
-		void onWindowFocusLost(RenderWindow& win);
-
-		/**	Called when the mouse leaves the specified window. */
-		void onMouseLeftWindow(RenderWindow& win);
-
-		/**	Converts pointer buttons to mouse buttons. */
-		GUIMouseButton buttonToGUIButton(PointerEventButton pointerButton) const;
-
-		/**	Converts screen coordinates to coordinates relative to the specified widget. */
-		Vector2I getWidgetRelativePos(const GUIWidget* widget, const Vector2I& screenPos) const;
-
-		/**
-		 * Converts window coordinates to coordinates relative to the specified bridged widget. Returned coordinates will
-		 * be relative to the bridge element.
-		 *
-		 * @param[in]	If provided widget has no bridge, coordinates are returned as is.
-		 */
-		Vector2I windowToBridgedCoords(const GUIWidget& widget, const Vector2I& windowPos) const;
-
-		/**	Returns the parent render window of the specified widget. */
-		const RenderWindow* getWidgetWindow(const GUIWidget& widget) const;
-
-		/**	Hides the tooltip if any is shown. */
-		void hideTooltip();
-
-		/**
-		 * Sends a mouse event to the specified GUI element.
-		 *
-		 * @param[in]	element	Element to send the event to.
-		 * @param[in]	event	Event data.
-		 */
-		bool sendMouseEvent(GUIElement* element, const GUIMouseEvent& event);
-
-		/**
-		 * Sends a text input event to the specified GUI element.
-		 *
-		 * @param[in]	element	Element to send the event to.
-		 * @param[in]	event	Event data.
-		 */
-		bool sendTextInputEvent(GUIElement* element, const GUITextInputEvent& event);
-
-		/**
-		 * Sends a command event to the specified GUI element.
-		 *
-		 * @param[in]	element	Element to send the event to.
-		 * @param[in]	event	Event data.
-		 */
-		bool sendCommandEvent(GUIElement* element, const GUICommandEvent& event);
-
-		/**
-		 * Sends a virtual button event to the specified GUI element.
-		 *
-		 * @param[in]	element	Element to send the event to.
-		 * @param[in]	event	Event data.
-		 */
-		bool sendVirtualButtonEvent(GUIElement* element, const GUIVirtualButtonEvent& event);
-
-		static const UINT32 DRAG_DISTANCE;
-		static const float TOOLTIP_HOVER_TIME;
-
-		static const UINT32 MESH_HEAP_INITIAL_NUM_VERTS;
-		static const UINT32 MESH_HEAP_INITIAL_NUM_INDICES;
-
-		Vector<WidgetInfo> mWidgets;
-		UnorderedMap<const Viewport*, GUIRenderData> mCachedGUIData;
-		MeshHeapPtr mMeshHeap;
-
-		std::atomic<GUIManagerCore*> mCore;
-		bool mCoreDirty;
-
-		VertexDataDescPtr mVertexDesc;
-
-		Stack<GUIElement*> mScheduledForDestruction;
-
-		// Element and widget pointer is currently over
-		Vector<ElementInfoUnderPointer> mElementsUnderPointer;
-		Vector<ElementInfoUnderPointer> mNewElementsUnderPointer;
-
-		// Element and widget that's being clicked on
-		GUIMouseButton mActiveMouseButton;
-		Vector<ElementInfo> mActiveElements;
-		Vector<ElementInfo> mNewActiveElements;
-
-		// Element and widget that currently have the keyboard focus
-		Vector<ElementInfo> mElementsInFocus;
-		Vector<ElementInfo> mNewElementsInFocus;
-
-		Vector<ElementFocusInfo> mForcedFocusElements;
-
-		// Tooltip
-		bool mShowTooltip;
-		float mTooltipElementHoverStart;
-
-		GUIInputCaret* mInputCaret;
-		GUIInputSelection* mInputSelection;
-
-		bool mSeparateMeshesByWidget;
-		Vector2I mLastPointerScreenPos;
-
-		DragState mDragState;
-		Vector2I mLastPointerClickPos;
-		Vector2I mDragStartPos;
-
-		GUIMouseEvent mMouseEvent;
-		GUITextInputEvent mTextInputEvent;
-		GUICommandEvent mCommandEvent;
-		GUIVirtualButtonEvent mVirtualButtonEvent;
-
-		HSpriteTexture mCaretTexture;
-		Color mCaretColor;
-		float mCaretBlinkInterval;
-		float mCaretLastBlinkTime;
-		bool mIsCaretOn;
-		CursorType mActiveCursor;
-
-		HSpriteTexture mTextSelectionTexture;
-		Color mTextSelectionColor;
-
-		Map<const RenderTexture*, const GUIElement*> mInputBridge;
-
-		HEvent mOnPointerMovedConn;
-		HEvent mOnPointerPressedConn;
-		HEvent mOnPointerReleasedConn;
-		HEvent mOnPointerDoubleClick;
-		HEvent mOnTextInputConn;
-		HEvent mOnInputCommandConn;
-		HEvent mOnVirtualButtonDown;
-
-		HEvent mDragEndedConn;
-
-		HEvent mWindowGainedFocusConn;
-		HEvent mWindowLostFocusConn;
-
-		HEvent mMouseLeftWindowConn;
-	};
-
-	/**	Handles GUI rendering on the core thread. */
-	class BS_EXPORT GUIManagerCore
-	{
-		friend class GUIManager;
-
-		/** Material reference and parameter handles for a specific material type. */
-		struct MaterialInfo
-		{
-			MaterialInfo() { }
-			MaterialInfo(const SPtr<MaterialCore>& material);
-
-			SPtr<MaterialCore> material;
-
-			MaterialParamMat4Core worldTransformParam;
-			MaterialParamFloatCore invViewportWidthParam;
-			MaterialParamFloatCore invViewportHeightParam;
-			MaterialParamColorCore tintParam;
-			MaterialParamTextureCore textureParam;
-			MaterialParamSampStateCore samplerParam;
-		};
-
-	public:
-		~GUIManagerCore();
-
-		/**
-		 * Initializes the object. Must be called right after construction.
-		 *
-		 * @param[in]	textMat			Material used for drawing text sprites.
-		 * @param[in]	imageMat		Material used for drawing non-transparent image sprites.
-		 * @param[in]	imageAlphaMat	Material used for drawing transparent image sprites.
-		 */
-		void initialize(const SPtr<MaterialCore>& textMat, const SPtr<MaterialCore>& imageMat,
-			const SPtr<MaterialCore>& imageAlphaMat);
-
-	private:
-		/**
-		 * Updates the internal data that determines what will be rendered on the next render() call.
-		 *
-		 * @param[in]	data	GUI mesh/material per viewport.
-		 */
-		void updateData(const UnorderedMap<SPtr<CameraCore>, Vector<GUIManager::GUICoreRenderData>>& perCameraData);
-
-		/**	Triggered by the Renderer when the GUI should be rendered. */
-		void render(const SPtr<CameraCore>& camera);
-
-		UnorderedMap<SPtr<CameraCore>, Vector<GUIManager::GUICoreRenderData>> mPerCameraData;
-
-		// Immutable
-		MaterialInfo mTextMaterialInfo;
-		MaterialInfo mImageMaterialInfo;
-		MaterialInfo mImageAlphaMaterialInfo;
-
-		SPtr<SamplerStateCore> mSamplerState;
-	};
-
-	/** Provides easier access to GUIManager. */
-	BS_EXPORT GUIManager& gGUIManager();
-
-	/** @} */
-	/** @endcond */
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "BsGUIMouseEvent.h"
+#include "BsGUITextInputEvent.h"
+#include "BsGUICommandEvent.h"
+#include "BsGUIVirtualButtonEvent.h"
+#include "BsSprite.h"
+#include "BsModule.h"
+#include "BsColor.h"
+#include "BsMatrix4.h"
+#include "BsEvent.h"
+#include "BsMaterialParam.h"
+
+namespace BansheeEngine
+{
+	/** @cond INTERNAL */
+	/** @addtogroup GUI
+	 *  @{
+	 */
+
+	class GUIManagerCore;
+
+	/**
+	 * Manages the rendering and input of all GUI widgets in the scene. 
+	 * 			
+	 * @note	
+	 * If adding or modifying GUIManager functionality ensure that GUIManager data never gets modified outside of update()
+	 * method or Input callbacks. If you need such functionality add temporary variables that store you changes and then
+	 * execute them delayed in update().
+	 * @par
+	 * This ensures that GUIElements don't recursively modify GUIManager while GUIManager is still using that data.
+	 * @par
+	 * e.g. setFocus() usually gets called from within GUIElements, however we don't want elements in focus be modified 
+	 * immediately since that setFocus() call could have originated in sendCommandEvent and elements in focus array would
+	 * be modified while still being iterated upon.
+	 * @note
+	 * Internal class. Unless modifying internal engine systems you should have no need to access this class.
+	 */
+	class BS_EXPORT GUIManager : public Module<GUIManager>
+	{
+		/**	Valid states of a drag and drop operation. */
+		enum class DragState
+		{
+			NoDrag,
+			HeldWithoutDrag,
+			Dragging
+		};
+
+		/**	GUI render data for a single viewport. */
+		struct GUIRenderData
+		{
+			GUIRenderData()
+				:isDirty(true)
+			{ }
+
+			Vector<TransientMeshPtr> cachedMeshes;
+			Vector<SpriteMaterialInfo> cachedMaterials;
+			Vector<GUIWidget*> cachedWidgetsPerMesh;
+			Vector<GUIWidget*> widgets;
+			bool isDirty;
+		};
+
+		/**	Render data for a single GUI group used for notifying the core GUI renderer. */
+		struct GUICoreRenderData
+		{
+			SPtr<TransientMeshCore> mesh;
+			SPtr<TextureCore> texture;
+			SpriteMaterial materialType;
+			Color tint;
+			Matrix4 worldTransform;
+		};
+
+		/**	Container for a GUI widget. */
+		struct WidgetInfo
+		{
+			WidgetInfo(GUIWidget* _widget)
+				:widget(_widget)
+			{ }
+
+			GUIWidget* widget;
+		};
+
+		/**	Container for data about a single GUI element and its widget. */
+		struct ElementInfo
+		{
+			ElementInfo(GUIElement* element, GUIWidget* widget)
+				:element(element), widget(widget)
+			{ }
+
+			GUIElement* element;
+			GUIWidget* widget;
+		};
+
+		/**	Container for data about a single GUI element and its widget currently under the pointer. */
+		struct ElementInfoUnderPointer
+		{
+			ElementInfoUnderPointer(GUIElement* element, GUIWidget* widget)
+				:element(element), widget(widget), usesMouseOver(false), 
+				receivedMouseOver(false), isHovering(false)
+			{ }
+
+			GUIElement* element;
+			GUIWidget* widget;
+			bool usesMouseOver;
+			bool receivedMouseOver;
+			bool isHovering;
+		};
+
+		/**	Container for GUI element in focus. */
+		struct ElementFocusInfo
+		{
+			ElementFocusInfo(GUIElement* element, GUIWidget* widget, bool usesFocus)
+				:element(element), widget(widget), usesFocus(usesFocus)
+			{ }
+
+			GUIElement* element;
+			GUIWidget* widget;
+			bool usesFocus;
+		};
+
+		/**	Container for GUI elements that need to have their focus state forcefully changed. */
+		struct ElementForcedFocusInfo
+		{
+			GUIElement* element;
+			bool focus;
+			bool usesFocus;
+		};
+
+	public:
+		GUIManager();
+		~GUIManager();
+
+		/** Registers a newly created widget with the GUI manager. This should be called by every GUI widget on creation. */
+		void registerWidget(GUIWidget* widget);
+
+		/**
+		 * Unregisters a GUI widget from the GUI manager. This should be called by every GUI widget before getting deleted.
+		 */
+		void unregisterWidget(GUIWidget* widget);
+
+		/**	Called once per frame. */
+		void update();
+
+		/** Queues the GUI element for destruction. Element will be destroyed during the next call to update(). */
+		void queueForDestroy(GUIElement* element);
+
+		/**	Change the GUI element focus state. */
+		void setFocus(GUIElement* element, bool focus);
+
+		/**	Changes the color of the input caret used in input boxes and similar controls. */
+		void setCaretColor(const Color& color) { mCaretColor = color; updateCaretTexture(); }
+
+		/**	Changes the text selection highlight color used in input boxes and similar controls. */
+		void setTextSelectionColor(const Color& color) { mTextSelectionColor = color; updateTextSelectionTexture(); }
+
+		/**	Returns the default caret texture used for rendering the input caret sprite. */
+		const HSpriteTexture& getCaretTexture() const { return mCaretTexture; }
+
+		/**	Returns the default selection highlight texture used for rendering the selection highlight sprites. */
+		const HSpriteTexture& getTextSelectionTexture() const { return mTextSelectionTexture; }
+
+		/**	Checks is the input caret visible this frame. */
+		bool getCaretBlinkState() const { return mIsCaretOn; }
+
+		/**
+		 * Returns input caret helper tool that allows you to easily position and show an input caret in your GUI controls.
+		 */
+		GUIInputCaret* getInputCaretTool() const { return mInputCaret; }
+
+		/**
+		 * Returns input selection helper tool that allows you to easily position and show an input selection highlight in
+		 * your GUI controls.
+		 */
+		GUIInputSelection* getInputSelectionTool() const { return mInputSelection; }
+
+		/**
+		 * Allows you to bridge GUI input from a GUI element into another render target.
+		 *
+		 * @param[in]	renderTex 	The render target to which to bridge the input.
+		 * @param[in]	element		The element from which to bridge input. Input will be transformed according to this 
+		 *							elements position and size. Provide nullptr if you want to remove a bridge for the
+		 *							specified widget.
+		 * 					
+		 * @note	
+		 * This is useful if you use render textures, where your GUI is rendered off-screen. In such case you need to 
+		 * display the render texture within another GUIElement in a GUIWidget, but have no way of sending input to the
+		 * render texture (normally input is only sent to render windows). This allows you to change that - any GUIWidget
+		 * using the bridged render texture as a render target will then receive input when mouse is over the specified
+		 * element.
+		 * @note			
+		 * Bridged element needs to remove itself as the bridge when it is destroyed.
+		 */
+		void setInputBridge(const RenderTexture* renderTex, const GUIElement* element);
+
+		/** Gets the core thread portion of the GUI manager, responsible for rendering of GUI elements. */
+		GUIManagerCore* getCore() const { return mCore.load(std::memory_order_relaxed); }
+
+	private:
+		friend class GUIManagerCore;
+
+		/**	Recreates all dirty GUI meshes and makes them ready for rendering. */
+		void updateMeshes();
+
+		/**	Recreates the input caret texture. */
+		void updateCaretTexture();
+
+		/**	Recreates the input text selection highlight texture. */
+		void updateTextSelectionTexture();
+
+		/**
+		 * Destroys the core thread counterpart of the GUI manager.
+		 *
+		 * @param[in]	core	Previously constructed core thread GUI manager instance.
+		 */
+		void destroyCore(GUIManagerCore* core);
+
+		/**
+		 * Destroys any elements or widgets queued for destruction.
+		 *
+		 * @note	
+		 * Returns true if more elements have been added for destruction (will happen when destruction of one element
+		 * queues up destruction of another).
+		 */
+		bool processDestroyQueue();
+
+		/**
+		 * Finds a GUI element under the pointer at the specified screen position. This method will also trigger pointer
+		 * move/hover/leave events.
+		 *
+		 * @param[in]	screenMousePos	Position of the pointer in screen coordinates.
+		 * @param[in]	buttonStates	States of the three mouse buttons (left, right, middle).
+		 * @param[in]	shift			Is shift key held.
+		 * @param[in]	control			Is control key held.
+		 * @param[in]	alt				Is alt key held.
+		 */
+		bool findElementUnderPointer(const Vector2I& screenMousePos, bool buttonStates[3], bool shift, bool control, bool alt);
+
+		/**	Called whenever a pointer (e.g. mouse cursor) is moved. */
+		void onPointerMoved(const PointerEvent& event);
+
+		/**	Called whenever a pointer button (e.g. mouse button) is released. */
+		void onPointerReleased(const PointerEvent& event);
+
+		/**	Called whenever a pointer button (e.g. mouse button) is pressed. */
+		void onPointerPressed(const PointerEvent& event);
+
+		/**	Called whenever a pointer button (e.g. mouse button) is double clicked. */
+		void onPointerDoubleClick(const PointerEvent& event);
+
+		/**	Called whenever a text is input. */
+		void onTextInput(const TextInputEvent& event);
+
+		/**	Called whenever an input command is input. */
+		void onInputCommandEntered(InputCommandType commandType);
+
+		/**	Called whenever a virtual button is pressed. */
+		void onVirtualButtonDown(const VirtualButton& button, UINT32 deviceIdx);
+
+		/**	Called by the drag and drop managed to notify us the drag ended. */
+		void onMouseDragEnded(const PointerEvent& event, DragCallbackInfo& dragInfo);
+
+		/**	Called when the specified window gains focus. */
+		void onWindowFocusGained(RenderWindow& win);
+
+		/**	Called when the specified window loses focus. */
+		void onWindowFocusLost(RenderWindow& win);
+
+		/**	Called when the mouse leaves the specified window. */
+		void onMouseLeftWindow(RenderWindow& win);
+
+		/**	Converts pointer buttons to mouse buttons. */
+		GUIMouseButton buttonToGUIButton(PointerEventButton pointerButton) const;
+
+		/**	Converts screen coordinates to coordinates relative to the specified widget. */
+		Vector2I getWidgetRelativePos(const GUIWidget* widget, const Vector2I& screenPos) const;
+
+		/**
+		 * Converts window coordinates to coordinates relative to the specified bridged widget. Returned coordinates will
+		 * be relative to the bridge element.
+		 *
+		 * @param[in]	If provided widget has no bridge, coordinates are returned as is.
+		 */
+		Vector2I windowToBridgedCoords(const GUIWidget& widget, const Vector2I& windowPos) const;
+
+		/**	Returns the parent render window of the specified widget. */
+		const RenderWindow* getWidgetWindow(const GUIWidget& widget) const;
+
+		/**	Hides the tooltip if any is shown. */
+		void hideTooltip();
+
+		/**
+		 * Sends a mouse event to the specified GUI element.
+		 *
+		 * @param[in]	element	Element to send the event to.
+		 * @param[in]	event	Event data.
+		 */
+		bool sendMouseEvent(GUIElement* element, const GUIMouseEvent& event);
+
+		/**
+		 * Sends a text input event to the specified GUI element.
+		 *
+		 * @param[in]	element	Element to send the event to.
+		 * @param[in]	event	Event data.
+		 */
+		bool sendTextInputEvent(GUIElement* element, const GUITextInputEvent& event);
+
+		/**
+		 * Sends a command event to the specified GUI element.
+		 *
+		 * @param[in]	element	Element to send the event to.
+		 * @param[in]	event	Event data.
+		 */
+		bool sendCommandEvent(GUIElement* element, const GUICommandEvent& event);
+
+		/**
+		 * Sends a virtual button event to the specified GUI element.
+		 *
+		 * @param[in]	element	Element to send the event to.
+		 * @param[in]	event	Event data.
+		 */
+		bool sendVirtualButtonEvent(GUIElement* element, const GUIVirtualButtonEvent& event);
+
+		static const UINT32 DRAG_DISTANCE;
+		static const float TOOLTIP_HOVER_TIME;
+
+		static const UINT32 MESH_HEAP_INITIAL_NUM_VERTS;
+		static const UINT32 MESH_HEAP_INITIAL_NUM_INDICES;
+
+		Vector<WidgetInfo> mWidgets;
+		UnorderedMap<const Viewport*, GUIRenderData> mCachedGUIData;
+		MeshHeapPtr mMeshHeap;
+
+		std::atomic<GUIManagerCore*> mCore;
+		bool mCoreDirty;
+
+		VertexDataDescPtr mVertexDesc;
+
+		Stack<GUIElement*> mScheduledForDestruction;
+
+		// Element and widget pointer is currently over
+		Vector<ElementInfoUnderPointer> mElementsUnderPointer;
+		Vector<ElementInfoUnderPointer> mNewElementsUnderPointer;
+
+		// Element and widget that's being clicked on
+		GUIMouseButton mActiveMouseButton;
+		Vector<ElementInfo> mActiveElements;
+		Vector<ElementInfo> mNewActiveElements;
+
+		// Element and widget that currently have the keyboard focus
+		Vector<ElementFocusInfo> mElementsInFocus;
+		Vector<ElementFocusInfo> mNewElementsInFocus;
+
+		Vector<ElementForcedFocusInfo> mForcedFocusElements;
+
+		// Tooltip
+		bool mShowTooltip;
+		float mTooltipElementHoverStart;
+
+		GUIInputCaret* mInputCaret;
+		GUIInputSelection* mInputSelection;
+
+		bool mSeparateMeshesByWidget;
+		Vector2I mLastPointerScreenPos;
+
+		DragState mDragState;
+		Vector2I mLastPointerClickPos;
+		Vector2I mDragStartPos;
+
+		GUIMouseEvent mMouseEvent;
+		GUITextInputEvent mTextInputEvent;
+		GUICommandEvent mCommandEvent;
+		GUIVirtualButtonEvent mVirtualButtonEvent;
+
+		HSpriteTexture mCaretTexture;
+		Color mCaretColor;
+		float mCaretBlinkInterval;
+		float mCaretLastBlinkTime;
+		bool mIsCaretOn;
+		CursorType mActiveCursor;
+
+		HSpriteTexture mTextSelectionTexture;
+		Color mTextSelectionColor;
+
+		Map<const RenderTexture*, const GUIElement*> mInputBridge;
+
+		HEvent mOnPointerMovedConn;
+		HEvent mOnPointerPressedConn;
+		HEvent mOnPointerReleasedConn;
+		HEvent mOnPointerDoubleClick;
+		HEvent mOnTextInputConn;
+		HEvent mOnInputCommandConn;
+		HEvent mOnVirtualButtonDown;
+
+		HEvent mDragEndedConn;
+
+		HEvent mWindowGainedFocusConn;
+		HEvent mWindowLostFocusConn;
+
+		HEvent mMouseLeftWindowConn;
+	};
+
+	/**	Handles GUI rendering on the core thread. */
+	class BS_EXPORT GUIManagerCore
+	{
+		friend class GUIManager;
+
+		/** Material reference and parameter handles for a specific material type. */
+		struct MaterialInfo
+		{
+			MaterialInfo() { }
+			MaterialInfo(const SPtr<MaterialCore>& material);
+
+			SPtr<MaterialCore> material;
+
+			MaterialParamMat4Core worldTransformParam;
+			MaterialParamFloatCore invViewportWidthParam;
+			MaterialParamFloatCore invViewportHeightParam;
+			MaterialParamColorCore tintParam;
+			MaterialParamTextureCore textureParam;
+			MaterialParamSampStateCore samplerParam;
+		};
+
+	public:
+		~GUIManagerCore();
+
+		/**
+		 * Initializes the object. Must be called right after construction.
+		 *
+		 * @param[in]	textMat			Material used for drawing text sprites.
+		 * @param[in]	imageMat		Material used for drawing non-transparent image sprites.
+		 * @param[in]	imageAlphaMat	Material used for drawing transparent image sprites.
+		 */
+		void initialize(const SPtr<MaterialCore>& textMat, const SPtr<MaterialCore>& imageMat,
+			const SPtr<MaterialCore>& imageAlphaMat);
+
+	private:
+		/**
+		 * Updates the internal data that determines what will be rendered on the next render() call.
+		 *
+		 * @param[in]	data	GUI mesh/material per viewport.
+		 */
+		void updateData(const UnorderedMap<SPtr<CameraCore>, Vector<GUIManager::GUICoreRenderData>>& perCameraData);
+
+		/**	Triggered by the Renderer when the GUI should be rendered. */
+		void render(const SPtr<CameraCore>& camera);
+
+		UnorderedMap<SPtr<CameraCore>, Vector<GUIManager::GUICoreRenderData>> mPerCameraData;
+
+		// Immutable
+		MaterialInfo mTextMaterialInfo;
+		MaterialInfo mImageMaterialInfo;
+		MaterialInfo mImageAlphaMaterialInfo;
+
+		SPtr<SamplerStateCore> mSamplerState;
+	};
+
+	/** Provides easier access to GUIManager. */
+	BS_EXPORT GUIManager& gGUIManager();
+
+	/** @} */
+	/** @endcond */
 }

+ 434 - 432
Source/BansheeEngine/Source/BsGUIButtonBase.cpp

@@ -1,433 +1,435 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsGUIButtonBase.h"
-#include "BsImageSprite.h"
-#include "BsGUISkin.h"
-#include "BsSpriteTexture.h"
-#include "BsTextSprite.h"
-#include "BsGUIDimensions.h"
-#include "BsGUIMouseEvent.h"
-#include "BsGUIHelper.h"
-
-namespace BansheeEngine
-{
-	GUIButtonBase::GUIButtonBase(const String& styleName, const GUIContent& content, const GUIDimensions& dimensions)
-		:GUIElement(styleName, dimensions), mContent(content), mContentImageSprite(nullptr), mActiveState(GUIElementState::Normal)
-	{
-		mImageSprite = bs_new<ImageSprite>();
-		mTextSprite = bs_new<TextSprite>();
-
-		refreshContentSprite();
-	}
-
-	GUIButtonBase::~GUIButtonBase()
-	{
-		bs_delete(mTextSprite);
-		bs_delete(mImageSprite);
-
-		if(mContentImageSprite != nullptr)
-			bs_delete(mContentImageSprite);
-	}
-
-	void GUIButtonBase::setContent(const GUIContent& content)
-	{
-		Vector2I origSize = mDimensions.calculateSizeRange(_getOptimalSize()).optimal;
-		mContent = content;
-
-		refreshContentSprite();
-
-		Vector2I newSize = mDimensions.calculateSizeRange(_getOptimalSize()).optimal;
-
-		if (origSize != newSize)
-			_markLayoutAsDirty();
-		else
-			_markContentAsDirty();
-	}
-
-	void GUIButtonBase::_setOn(bool on) 
-	{ 
-		if(on)
-			_setState((GUIElementState)((INT32)mActiveState | 0x10)); 
-		else
-			_setState((GUIElementState)((INT32)mActiveState & (~0x10))); 
-	}
-
-	bool GUIButtonBase::_isOn() const
-	{
-		return ((INT32)mActiveState & 0x10) != 0;
-	}
-
-	UINT32 GUIButtonBase::_getNumRenderElements() const
-	{
-		UINT32 numElements = mImageSprite->getNumRenderElements();
-		numElements += mTextSprite->getNumRenderElements();
-
-		if(mContentImageSprite != nullptr)
-			numElements += mContentImageSprite->getNumRenderElements();
-
-		return numElements;
-	}
-
-	const SpriteMaterialInfo& GUIButtonBase::_getMaterial(UINT32 renderElementIdx) const
-	{
-		UINT32 textSpriteIdx = mImageSprite->getNumRenderElements();
-		UINT32 contentImgSpriteIdx = textSpriteIdx + mTextSprite->getNumRenderElements();
-
-		if(renderElementIdx >= contentImgSpriteIdx)
-			return mContentImageSprite->getMaterialInfo(contentImgSpriteIdx - renderElementIdx);
-		else if(renderElementIdx >= textSpriteIdx)
-			return mTextSprite->getMaterialInfo(textSpriteIdx - renderElementIdx);
-		else
-			return mImageSprite->getMaterialInfo(renderElementIdx);
-	}
-
-	UINT32 GUIButtonBase::_getNumQuads(UINT32 renderElementIdx) const
-	{
-		UINT32 textSpriteIdx = mImageSprite->getNumRenderElements();
-		UINT32 contentImgSpriteIdx = textSpriteIdx + mTextSprite->getNumRenderElements();
-
-		UINT32 numQuads = 0;
-		if(renderElementIdx >= contentImgSpriteIdx)
-			numQuads = mContentImageSprite->getNumQuads(contentImgSpriteIdx - renderElementIdx);
-		else if(renderElementIdx >= textSpriteIdx)
-			numQuads = mTextSprite->getNumQuads(textSpriteIdx - renderElementIdx);
-		else
-			numQuads = mImageSprite->getNumQuads(renderElementIdx);
-
-		return numQuads;
-	}
-
-	void GUIButtonBase::updateRenderElementsInternal()
-	{		
-		mImageDesc.width = mLayoutData.area.width;
-		mImageDesc.height = mLayoutData.area.height;
-
-		const HSpriteTexture& activeTex = getActiveTexture();
-		if (SpriteTexture::checkIsLoaded(activeTex))
-			mImageDesc.texture = activeTex.getInternalPtr();
-		else
-			mImageDesc.texture = nullptr;
-
-		mImageDesc.borderLeft = _getStyle()->border.left;
-		mImageDesc.borderRight = _getStyle()->border.right;
-		mImageDesc.borderTop = _getStyle()->border.top;
-		mImageDesc.borderBottom = _getStyle()->border.bottom;
-		mImageDesc.color = getTint();
-
-		mImageSprite->update(mImageDesc, (UINT64)_getParentWidget());
-
-		mTextSprite->update(getTextDesc(), (UINT64)_getParentWidget());
-
-		if(mContentImageSprite != nullptr)
-		{
-			Rect2I contentBounds = getCachedContentBounds();
-
-			HSpriteTexture image = mContent.getImage(mActiveState);
-			UINT32 contentWidth = image->getWidth();
-			UINT32 contentHeight = image->getHeight();
-
-			UINT32 contentMaxWidth = std::min((UINT32)contentBounds.width, contentWidth);
-			UINT32 contentMaxHeight = std::min((UINT32)contentBounds.height, contentHeight);
-
-			float horzRatio = contentMaxWidth / (float)contentWidth;
-			float vertRatio = contentMaxHeight / (float)contentHeight;
-
-			if (horzRatio < vertRatio)
-			{
-				contentWidth = Math::roundToInt(contentWidth * horzRatio);
-				contentHeight = Math::roundToInt(contentHeight * horzRatio);
-			}
-			else
-			{
-				contentWidth = Math::roundToInt(contentWidth * vertRatio);
-				contentHeight = Math::roundToInt(contentHeight * vertRatio);
-			}
-
-			IMAGE_SPRITE_DESC contentImgDesc;
-			contentImgDesc.texture = image.getInternalPtr();
-			contentImgDesc.width = contentWidth;
-			contentImgDesc.height = contentHeight;
-			contentImgDesc.color = getTint();
-
-			mContentImageSprite->update(contentImgDesc, (UINT64)_getParentWidget());
-		}
-
-		GUIElement::updateRenderElementsInternal();
-	}
-
-	Vector2I GUIButtonBase::_getOptimalSize() const
-	{
-		UINT32 imageWidth = 0;
-		UINT32 imageHeight = 0;
-
-		const HSpriteTexture& activeTex = getActiveTexture();
-		if(SpriteTexture::checkIsLoaded(activeTex))
-		{
-			imageWidth = activeTex->getWidth();
-			imageHeight = activeTex->getHeight();
-		}
-
-		Vector2I contentSize = GUIHelper::calcOptimalContentsSize(mContent, *_getStyle(), _getDimensions(), mActiveState);
-		UINT32 contentWidth = std::max(imageWidth, (UINT32)contentSize.x);
-		UINT32 contentHeight = std::max(imageHeight, (UINT32)contentSize.y);
-
-		return Vector2I(contentWidth, contentHeight);
-	}
-
-	UINT32 GUIButtonBase::_getRenderElementDepth(UINT32 renderElementIdx) const
-	{
-		UINT32 textSpriteIdx = mImageSprite->getNumRenderElements();
-		UINT32 contentImgSpriteIdx = textSpriteIdx + mTextSprite->getNumRenderElements();
-
-		if(renderElementIdx >= contentImgSpriteIdx)
-			return _getDepth();
-		else if(renderElementIdx >= textSpriteIdx)
-			return _getDepth();
-		else
-			return _getDepth() + 1;
-	}
-
-	UINT32 GUIButtonBase::_getRenderElementDepthRange() const
-	{
-		return 2;
-	}
-
-	void GUIButtonBase::_fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads, 
-		UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const
-	{
-		UINT32 textSpriteIdx = mImageSprite->getNumRenderElements();
-		UINT32 contentImgSpriteIdx = textSpriteIdx + mTextSprite->getNumRenderElements();
-
-		if(renderElementIdx < textSpriteIdx)
-		{
-			Vector2I offset(mLayoutData.area.x, mLayoutData.area.y);
-
-			mImageSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, 
-				vertexStride, indexStride, renderElementIdx, offset, mLayoutData.getLocalClipRect());
-
-			return;
-		}
-
-		Rect2I contentBounds = getCachedContentBounds();
-		Rect2I contentClipRect = getCachedContentClipRect();
-		Rect2I textBounds = mTextSprite->getBounds(Vector2I(), Rect2I());
-
-		Vector2I textOffset;
-		Rect2I textClipRect;
-
-		Vector2I imageOffset;
-		Rect2I imageClipRect;
-		if(mContentImageSprite != nullptr)
-		{
-			Rect2I imageBounds = mContentImageSprite->getBounds(Vector2I(), Rect2I());
-			INT32 imageXOffset = 0;
-			INT32 textImageSpacing = 0;
-			
-			if (textBounds.width == 0)
-			{
-				UINT32 freeWidth = (UINT32)std::max(0, contentBounds.width - textBounds.width - imageBounds.width);
-				imageXOffset = (INT32)(freeWidth / 2);
-			}
-			else
-				textImageSpacing = GUIContent::IMAGE_TEXT_SPACING;
-
-			if(_getStyle()->imagePosition == GUIImagePosition::Right)
-			{
-				INT32 imageReservedWidth = std::max(0, contentBounds.width - textBounds.width);
-
-				textOffset = Vector2I(contentBounds.x, contentBounds.y);
-				textClipRect = contentClipRect;
-				textClipRect.width = std::min(contentBounds.width - imageReservedWidth, textClipRect.width);
-
-				imageOffset = Vector2I(contentBounds.x + textBounds.width + imageXOffset + textImageSpacing, contentBounds.y);
-				imageClipRect = contentClipRect;
-				imageClipRect.x -= textBounds.width + imageXOffset;
-			}
-			else
-			{
-				INT32 imageReservedWidth = imageBounds.width + imageXOffset;
-
-				imageOffset = Vector2I(contentBounds.x + imageXOffset, contentBounds.y);
-				imageClipRect = contentClipRect;
-				imageClipRect.x -= imageXOffset;
-				imageClipRect.width = std::min(imageReservedWidth, imageClipRect.width);
-
-				textOffset = Vector2I(contentBounds.x + imageReservedWidth + textImageSpacing, contentBounds.y);
-				textClipRect = contentClipRect;
-				textClipRect.x -= imageReservedWidth;
-			}
-
-			INT32 imageYOffset = (contentBounds.height - imageBounds.height) / 2;
-			imageClipRect.y -= imageYOffset;
-			imageOffset.y += imageYOffset;
-		}
-		else
-		{
-			textOffset = Vector2I(contentBounds.x, contentBounds.y);
-			textClipRect = contentClipRect;
-		}
-
-		if(renderElementIdx >= contentImgSpriteIdx)
-		{
-			mContentImageSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, 
-				vertexStride, indexStride, contentImgSpriteIdx - renderElementIdx, imageOffset, imageClipRect);
-		}
-		else
-		{
-			mTextSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, 
-				vertexStride, indexStride, textSpriteIdx - renderElementIdx, textOffset, textClipRect);
-		}
-	}
-
-	bool GUIButtonBase::_mouseEvent(const GUIMouseEvent& ev)
-	{
-		if(ev.getType() == GUIMouseEventType::MouseOver)
-		{
-			if (!_isDisabled())
-			{
-				_setState(_isOn() ? GUIElementState::HoverOn : GUIElementState::Hover);
-				onHover();
-			}
-
-			return true;
-		}
-		else if(ev.getType() == GUIMouseEventType::MouseOut)
-		{
-			if (!_isDisabled())
-			{
-				_setState(_isOn() ? GUIElementState::NormalOn : GUIElementState::Normal);
-				onOut();
-			}
-
-			return true;
-		}
-		else if(ev.getType() == GUIMouseEventType::MouseDown)
-		{
-			if (!_isDisabled())
-				_setState(_isOn() ? GUIElementState::ActiveOn : GUIElementState::Active);
-
-			return true;
-		}
-		else if(ev.getType() == GUIMouseEventType::MouseUp)
-		{
-			if (!_isDisabled())
-			{
-				_setState(_isOn() ? GUIElementState::HoverOn : GUIElementState::Hover);
-				onClick();
-			}
-
-			return true;
-		}
-		else if (ev.getType() == GUIMouseEventType::MouseDoubleClick)
-		{
-			if (!_isDisabled())
-				onDoubleClick();
-		}
-
-		return false;
-	}
-
-	WString GUIButtonBase::_getTooltip() const
-	{
-		return mContent.getTooltip();
-	}
-
-	void GUIButtonBase::refreshContentSprite()
-	{
-		HSpriteTexture contentTex = mContent.getImage(mActiveState);
-		if (SpriteTexture::checkIsLoaded(contentTex))
-		{
-			if (mContentImageSprite == nullptr)
-				mContentImageSprite = bs_new<ImageSprite>();
-		}
-		else
-		{
-			if (mContentImageSprite != nullptr)
-			{
-				bs_delete(mContentImageSprite);
-				mContentImageSprite = nullptr;
-			}
-		}
-	}
-
-	TEXT_SPRITE_DESC GUIButtonBase::getTextDesc() const
-	{
-		TEXT_SPRITE_DESC textDesc;
-		textDesc.text = mContent.getText();
-		textDesc.font = _getStyle()->font;
-		textDesc.fontSize = _getStyle()->fontSize;
-		textDesc.color = getTint() * getActiveTextColor();
-
-		Rect2I textBounds = getCachedContentBounds();
-
-		textDesc.width = textBounds.width;
-		textDesc.height = textBounds.height;
-		textDesc.horzAlign = _getStyle()->textHorzAlign;
-		textDesc.vertAlign = _getStyle()->textVertAlign;
-
-		return textDesc;
-	}
-
-	void GUIButtonBase::_setState(GUIElementState state)
-	{
-		Vector2I origSize = mDimensions.calculateSizeRange(_getOptimalSize()).optimal;
-		mActiveState = state;
-		refreshContentSprite();
-		Vector2I newSize = mDimensions.calculateSizeRange(_getOptimalSize()).optimal;
-
-		if (origSize != newSize)
-			_markLayoutAsDirty();
-		else
-			_markContentAsDirty();
-	}
-
-	const HSpriteTexture& GUIButtonBase::getActiveTexture() const
-	{
-		switch(mActiveState)
-		{
-		case GUIElementState::Normal:
-			return _getStyle()->normal.texture;
-		case GUIElementState::Hover:
-			return _getStyle()->hover.texture;
-		case GUIElementState::Active:
-			return _getStyle()->active.texture;
-		case GUIElementState::Focused:
-			return _getStyle()->focused.texture;
-		case GUIElementState::NormalOn:
-			return _getStyle()->normalOn.texture;
-		case GUIElementState::HoverOn:
-			return _getStyle()->hoverOn.texture;
-		case GUIElementState::ActiveOn:
-			return _getStyle()->activeOn.texture;
-		case GUIElementState::FocusedOn:
-			return _getStyle()->focusedOn.texture;
-		}
-
-		return _getStyle()->normal.texture;
-	}
-
-	Color GUIButtonBase::getActiveTextColor() const
-	{
-		switch (mActiveState)
-		{
-		case GUIElementState::Normal:
-			return _getStyle()->normal.textColor;
-		case GUIElementState::Hover:
-			return _getStyle()->hover.textColor;
-		case GUIElementState::Active:
-			return _getStyle()->active.textColor;
-		case GUIElementState::Focused:
-			return _getStyle()->focused.textColor;
-		case GUIElementState::NormalOn:
-			return _getStyle()->normalOn.textColor;
-		case GUIElementState::HoverOn:
-			return _getStyle()->hoverOn.textColor;
-		case GUIElementState::ActiveOn:
-			return _getStyle()->activeOn.textColor;
-		case GUIElementState::FocusedOn:
-			return _getStyle()->focusedOn.textColor;
-		}
-
-		return _getStyle()->normal.textColor;
-	}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsGUIButtonBase.h"
+#include "BsImageSprite.h"
+#include "BsGUISkin.h"
+#include "BsSpriteTexture.h"
+#include "BsTextSprite.h"
+#include "BsGUIDimensions.h"
+#include "BsGUIMouseEvent.h"
+#include "BsGUIHelper.h"
+
+namespace BansheeEngine
+{
+	GUIButtonBase::GUIButtonBase(const String& styleName, const GUIContent& content, const GUIDimensions& dimensions)
+		:GUIElement(styleName, dimensions), mContent(content), mContentImageSprite(nullptr), mActiveState(GUIElementState::Normal)
+	{
+		mImageSprite = bs_new<ImageSprite>();
+		mTextSprite = bs_new<TextSprite>();
+
+		refreshContentSprite();
+	}
+
+	GUIButtonBase::~GUIButtonBase()
+	{
+		bs_delete(mTextSprite);
+		bs_delete(mImageSprite);
+
+		if(mContentImageSprite != nullptr)
+			bs_delete(mContentImageSprite);
+	}
+
+	void GUIButtonBase::setContent(const GUIContent& content)
+	{
+		Vector2I origSize = mDimensions.calculateSizeRange(_getOptimalSize()).optimal;
+		mContent = content;
+
+		refreshContentSprite();
+
+		Vector2I newSize = mDimensions.calculateSizeRange(_getOptimalSize()).optimal;
+
+		if (origSize != newSize)
+			_markLayoutAsDirty();
+		else
+			_markContentAsDirty();
+	}
+
+	void GUIButtonBase::_setOn(bool on) 
+	{ 
+		if(on)
+			_setState((GUIElementState)((INT32)mActiveState | 0x10)); 
+		else
+			_setState((GUIElementState)((INT32)mActiveState & (~0x10))); 
+	}
+
+	bool GUIButtonBase::_isOn() const
+	{
+		return ((INT32)mActiveState & 0x10) != 0;
+	}
+
+	UINT32 GUIButtonBase::_getNumRenderElements() const
+	{
+		UINT32 numElements = mImageSprite->getNumRenderElements();
+		numElements += mTextSprite->getNumRenderElements();
+
+		if(mContentImageSprite != nullptr)
+			numElements += mContentImageSprite->getNumRenderElements();
+
+		return numElements;
+	}
+
+	const SpriteMaterialInfo& GUIButtonBase::_getMaterial(UINT32 renderElementIdx) const
+	{
+		UINT32 textSpriteIdx = mImageSprite->getNumRenderElements();
+		UINT32 contentImgSpriteIdx = textSpriteIdx + mTextSprite->getNumRenderElements();
+
+		if(renderElementIdx >= contentImgSpriteIdx)
+			return mContentImageSprite->getMaterialInfo(contentImgSpriteIdx - renderElementIdx);
+		else if(renderElementIdx >= textSpriteIdx)
+			return mTextSprite->getMaterialInfo(textSpriteIdx - renderElementIdx);
+		else
+			return mImageSprite->getMaterialInfo(renderElementIdx);
+	}
+
+	UINT32 GUIButtonBase::_getNumQuads(UINT32 renderElementIdx) const
+	{
+		UINT32 textSpriteIdx = mImageSprite->getNumRenderElements();
+		UINT32 contentImgSpriteIdx = textSpriteIdx + mTextSprite->getNumRenderElements();
+
+		UINT32 numQuads = 0;
+		if(renderElementIdx >= contentImgSpriteIdx)
+			numQuads = mContentImageSprite->getNumQuads(contentImgSpriteIdx - renderElementIdx);
+		else if(renderElementIdx >= textSpriteIdx)
+			numQuads = mTextSprite->getNumQuads(textSpriteIdx - renderElementIdx);
+		else
+			numQuads = mImageSprite->getNumQuads(renderElementIdx);
+
+		return numQuads;
+	}
+
+	void GUIButtonBase::updateRenderElementsInternal()
+	{		
+		mImageDesc.width = mLayoutData.area.width;
+		mImageDesc.height = mLayoutData.area.height;
+
+		const HSpriteTexture& activeTex = getActiveTexture();
+		if (SpriteTexture::checkIsLoaded(activeTex))
+			mImageDesc.texture = activeTex.getInternalPtr();
+		else
+			mImageDesc.texture = nullptr;
+
+		mImageDesc.borderLeft = _getStyle()->border.left;
+		mImageDesc.borderRight = _getStyle()->border.right;
+		mImageDesc.borderTop = _getStyle()->border.top;
+		mImageDesc.borderBottom = _getStyle()->border.bottom;
+		mImageDesc.color = getTint();
+
+		mImageSprite->update(mImageDesc, (UINT64)_getParentWidget());
+
+		mTextSprite->update(getTextDesc(), (UINT64)_getParentWidget());
+
+		if(mContentImageSprite != nullptr)
+		{
+			Rect2I contentBounds = getCachedContentBounds();
+
+			HSpriteTexture image = mContent.getImage(mActiveState);
+			UINT32 contentWidth = image->getWidth();
+			UINT32 contentHeight = image->getHeight();
+
+			UINT32 contentMaxWidth = std::min((UINT32)contentBounds.width, contentWidth);
+			UINT32 contentMaxHeight = std::min((UINT32)contentBounds.height, contentHeight);
+
+			float horzRatio = contentMaxWidth / (float)contentWidth;
+			float vertRatio = contentMaxHeight / (float)contentHeight;
+
+			if (horzRatio < vertRatio)
+			{
+				contentWidth = Math::roundToInt(contentWidth * horzRatio);
+				contentHeight = Math::roundToInt(contentHeight * horzRatio);
+			}
+			else
+			{
+				contentWidth = Math::roundToInt(contentWidth * vertRatio);
+				contentHeight = Math::roundToInt(contentHeight * vertRatio);
+			}
+
+			IMAGE_SPRITE_DESC contentImgDesc;
+			contentImgDesc.texture = image.getInternalPtr();
+			contentImgDesc.width = contentWidth;
+			contentImgDesc.height = contentHeight;
+			contentImgDesc.color = getTint();
+
+			mContentImageSprite->update(contentImgDesc, (UINT64)_getParentWidget());
+		}
+
+		GUIElement::updateRenderElementsInternal();
+	}
+
+	Vector2I GUIButtonBase::_getOptimalSize() const
+	{
+		UINT32 imageWidth = 0;
+		UINT32 imageHeight = 0;
+
+		const HSpriteTexture& activeTex = getActiveTexture();
+		if(SpriteTexture::checkIsLoaded(activeTex))
+		{
+			imageWidth = activeTex->getWidth();
+			imageHeight = activeTex->getHeight();
+		}
+
+		Vector2I contentSize = GUIHelper::calcOptimalContentsSize(mContent, *_getStyle(), _getDimensions(), mActiveState);
+		UINT32 contentWidth = std::max(imageWidth, (UINT32)contentSize.x);
+		UINT32 contentHeight = std::max(imageHeight, (UINT32)contentSize.y);
+
+		return Vector2I(contentWidth, contentHeight);
+	}
+
+	UINT32 GUIButtonBase::_getRenderElementDepth(UINT32 renderElementIdx) const
+	{
+		UINT32 textSpriteIdx = mImageSprite->getNumRenderElements();
+		UINT32 contentImgSpriteIdx = textSpriteIdx + mTextSprite->getNumRenderElements();
+
+		if(renderElementIdx >= contentImgSpriteIdx)
+			return _getDepth();
+		else if(renderElementIdx >= textSpriteIdx)
+			return _getDepth();
+		else
+			return _getDepth() + 1;
+	}
+
+	UINT32 GUIButtonBase::_getRenderElementDepthRange() const
+	{
+		return 2;
+	}
+
+	void GUIButtonBase::_fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads, 
+		UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const
+	{
+		UINT32 textSpriteIdx = mImageSprite->getNumRenderElements();
+		UINT32 contentImgSpriteIdx = textSpriteIdx + mTextSprite->getNumRenderElements();
+
+		if(renderElementIdx < textSpriteIdx)
+		{
+			Vector2I offset(mLayoutData.area.x, mLayoutData.area.y);
+
+			mImageSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, 
+				vertexStride, indexStride, renderElementIdx, offset, mLayoutData.getLocalClipRect());
+
+			return;
+		}
+
+		Rect2I contentBounds = getCachedContentBounds();
+		Rect2I contentClipRect = getCachedContentClipRect();
+		Rect2I textBounds = mTextSprite->getBounds(Vector2I(), Rect2I());
+
+		Vector2I textOffset;
+		Rect2I textClipRect;
+
+		Vector2I imageOffset;
+		Rect2I imageClipRect;
+		if(mContentImageSprite != nullptr)
+		{
+			Rect2I imageBounds = mContentImageSprite->getBounds(Vector2I(), Rect2I());
+			INT32 imageXOffset = 0;
+			INT32 textImageSpacing = 0;
+			
+			if (textBounds.width == 0)
+			{
+				UINT32 freeWidth = (UINT32)std::max(0, contentBounds.width - textBounds.width - imageBounds.width);
+				imageXOffset = (INT32)(freeWidth / 2);
+			}
+			else
+				textImageSpacing = GUIContent::IMAGE_TEXT_SPACING;
+
+			if(_getStyle()->imagePosition == GUIImagePosition::Right)
+			{
+				INT32 imageReservedWidth = std::max(0, contentBounds.width - textBounds.width);
+
+				textOffset = Vector2I(contentBounds.x, contentBounds.y);
+				textClipRect = contentClipRect;
+				textClipRect.width = std::min(contentBounds.width - imageReservedWidth, textClipRect.width);
+
+				imageOffset = Vector2I(contentBounds.x + textBounds.width + imageXOffset + textImageSpacing, contentBounds.y);
+				imageClipRect = contentClipRect;
+				imageClipRect.x -= textBounds.width + imageXOffset;
+			}
+			else
+			{
+				INT32 imageReservedWidth = imageBounds.width + imageXOffset;
+
+				imageOffset = Vector2I(contentBounds.x + imageXOffset, contentBounds.y);
+				imageClipRect = contentClipRect;
+				imageClipRect.x -= imageXOffset;
+				imageClipRect.width = std::min(imageReservedWidth, imageClipRect.width);
+
+				textOffset = Vector2I(contentBounds.x + imageReservedWidth + textImageSpacing, contentBounds.y);
+				textClipRect = contentClipRect;
+				textClipRect.x -= imageReservedWidth;
+			}
+
+			INT32 imageYOffset = (contentBounds.height - imageBounds.height) / 2;
+			imageClipRect.y -= imageYOffset;
+			imageOffset.y += imageYOffset;
+		}
+		else
+		{
+			textOffset = Vector2I(contentBounds.x, contentBounds.y);
+			textClipRect = contentClipRect;
+		}
+
+		if(renderElementIdx >= contentImgSpriteIdx)
+		{
+			mContentImageSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, 
+				vertexStride, indexStride, contentImgSpriteIdx - renderElementIdx, imageOffset, imageClipRect);
+		}
+		else
+		{
+			mTextSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, 
+				vertexStride, indexStride, textSpriteIdx - renderElementIdx, textOffset, textClipRect);
+		}
+	}
+
+	bool GUIButtonBase::_mouseEvent(const GUIMouseEvent& ev)
+	{
+		if(ev.getType() == GUIMouseEventType::MouseOver)
+		{
+			if (!_isDisabled())
+			{
+				_setState(_isOn() ? GUIElementState::HoverOn : GUIElementState::Hover);
+				onHover();
+			}
+
+			return mBlockPointerEvents;
+		}
+		else if(ev.getType() == GUIMouseEventType::MouseOut)
+		{
+			if (!_isDisabled())
+			{
+				_setState(_isOn() ? GUIElementState::NormalOn : GUIElementState::Normal);
+				onOut();
+			}
+
+			return mBlockPointerEvents;
+		}
+		else if(ev.getType() == GUIMouseEventType::MouseDown)
+		{
+			if (!_isDisabled())
+				_setState(_isOn() ? GUIElementState::ActiveOn : GUIElementState::Active);
+
+			return mBlockPointerEvents;
+		}
+		else if(ev.getType() == GUIMouseEventType::MouseUp)
+		{
+			if (!_isDisabled())
+			{
+				_setState(_isOn() ? GUIElementState::HoverOn : GUIElementState::Hover);
+				onClick();
+			}
+
+			return mBlockPointerEvents;
+		}
+		else if (ev.getType() == GUIMouseEventType::MouseDoubleClick)
+		{
+			if (!_isDisabled())
+				onDoubleClick();
+
+			return mBlockPointerEvents;
+		}
+
+		return false;
+	}
+
+	WString GUIButtonBase::_getTooltip() const
+	{
+		return mContent.getTooltip();
+	}
+
+	void GUIButtonBase::refreshContentSprite()
+	{
+		HSpriteTexture contentTex = mContent.getImage(mActiveState);
+		if (SpriteTexture::checkIsLoaded(contentTex))
+		{
+			if (mContentImageSprite == nullptr)
+				mContentImageSprite = bs_new<ImageSprite>();
+		}
+		else
+		{
+			if (mContentImageSprite != nullptr)
+			{
+				bs_delete(mContentImageSprite);
+				mContentImageSprite = nullptr;
+			}
+		}
+	}
+
+	TEXT_SPRITE_DESC GUIButtonBase::getTextDesc() const
+	{
+		TEXT_SPRITE_DESC textDesc;
+		textDesc.text = mContent.getText();
+		textDesc.font = _getStyle()->font;
+		textDesc.fontSize = _getStyle()->fontSize;
+		textDesc.color = getTint() * getActiveTextColor();
+
+		Rect2I textBounds = getCachedContentBounds();
+
+		textDesc.width = textBounds.width;
+		textDesc.height = textBounds.height;
+		textDesc.horzAlign = _getStyle()->textHorzAlign;
+		textDesc.vertAlign = _getStyle()->textVertAlign;
+
+		return textDesc;
+	}
+
+	void GUIButtonBase::_setState(GUIElementState state)
+	{
+		Vector2I origSize = mDimensions.calculateSizeRange(_getOptimalSize()).optimal;
+		mActiveState = state;
+		refreshContentSprite();
+		Vector2I newSize = mDimensions.calculateSizeRange(_getOptimalSize()).optimal;
+
+		if (origSize != newSize)
+			_markLayoutAsDirty();
+		else
+			_markContentAsDirty();
+	}
+
+	const HSpriteTexture& GUIButtonBase::getActiveTexture() const
+	{
+		switch(mActiveState)
+		{
+		case GUIElementState::Normal:
+			return _getStyle()->normal.texture;
+		case GUIElementState::Hover:
+			return _getStyle()->hover.texture;
+		case GUIElementState::Active:
+			return _getStyle()->active.texture;
+		case GUIElementState::Focused:
+			return _getStyle()->focused.texture;
+		case GUIElementState::NormalOn:
+			return _getStyle()->normalOn.texture;
+		case GUIElementState::HoverOn:
+			return _getStyle()->hoverOn.texture;
+		case GUIElementState::ActiveOn:
+			return _getStyle()->activeOn.texture;
+		case GUIElementState::FocusedOn:
+			return _getStyle()->focusedOn.texture;
+		}
+
+		return _getStyle()->normal.texture;
+	}
+
+	Color GUIButtonBase::getActiveTextColor() const
+	{
+		switch (mActiveState)
+		{
+		case GUIElementState::Normal:
+			return _getStyle()->normal.textColor;
+		case GUIElementState::Hover:
+			return _getStyle()->hover.textColor;
+		case GUIElementState::Active:
+			return _getStyle()->active.textColor;
+		case GUIElementState::Focused:
+			return _getStyle()->focused.textColor;
+		case GUIElementState::NormalOn:
+			return _getStyle()->normalOn.textColor;
+		case GUIElementState::HoverOn:
+			return _getStyle()->hoverOn.textColor;
+		case GUIElementState::ActiveOn:
+			return _getStyle()->activeOn.textColor;
+		case GUIElementState::FocusedOn:
+			return _getStyle()->focusedOn.textColor;
+		}
+
+		return _getStyle()->normal.textColor;
+	}
 }

+ 133 - 133
Source/BansheeEngine/Source/BsGUIDropDownHitBox.cpp

@@ -1,134 +1,134 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsGUIDropDownHitBox.h"
-#include "BsGUICommandEvent.h"
-#include "BsGUIMouseEvent.h"
-
-namespace BansheeEngine
-{
-	const String& GUIDropDownHitBox::getGUITypeName()
-	{
-		static String name = "DropDownHitBox";
-		return name;
-	}
-
-	GUIDropDownHitBox* GUIDropDownHitBox::create(bool captureMouseOver, bool captureMousePresses)
-	{
-		return new (bs_alloc<GUIDropDownHitBox>()) 
-			GUIDropDownHitBox(captureMouseOver, captureMousePresses, GUIDimensions::create());
-	}
-
-	GUIDropDownHitBox* GUIDropDownHitBox::create(bool captureMouseOver, bool captureMousePresses, const GUIOptions& options)
-	{
-		return new (bs_alloc<GUIDropDownHitBox>()) 
-			GUIDropDownHitBox(captureMouseOver, captureMousePresses, GUIDimensions::create(options));
-	}
-
-	GUIDropDownHitBox::GUIDropDownHitBox(bool captureMouseOver, 
-		bool captureMousePresses, const GUIDimensions& dimensions)
-		:GUIElementContainer(dimensions), mCaptureMouseOver(captureMouseOver),
-		mCaptureMousePresses(captureMousePresses)
-	{
-
-	}
-
-	void GUIDropDownHitBox::setBounds(const Rect2I& bounds)
-	{
-		mBounds.clear();
-		mBounds.push_back(bounds);
-
-		updateClippedBounds();
-	}
-
-	void GUIDropDownHitBox::setBounds(const Vector<Rect2I>& bounds)
-	{
-		mBounds = bounds;
-
-		updateClippedBounds();
-	}
-
-	void GUIDropDownHitBox::updateClippedBounds()
-	{
-		mClippedBounds = Rect2I();
-
-		if (mBounds.size() > 0)
-		{
-			mClippedBounds = mBounds[0];
-
-			for (UINT32 i = 1; i < (UINT32)mBounds.size(); i++)
-				mClippedBounds.encapsulate(mBounds[i]);
-		}
-	}
-
-	bool GUIDropDownHitBox::_commandEvent(const GUICommandEvent& ev)
-	{
-		bool processed = GUIElementContainer::_commandEvent(ev);
-
-		if(ev.getType() == GUICommandEventType::FocusGained)
-		{
-			if(!onFocusGained.empty())
-				onFocusGained();
-
-			return true;
-		}
-		else if(ev.getType() == GUICommandEventType::FocusLost)
-		{
-			if(!onFocusLost.empty())
-				onFocusLost();
-
-			return true;
-		}
-
-		return processed;
-	}
-
-	bool GUIDropDownHitBox::_mouseEvent(const GUIMouseEvent& ev)
-	{
-		bool processed = GUIElementContainer::_mouseEvent(ev);
-
-		if(mCaptureMouseOver)
-		{
-			if (ev.getType() == GUIMouseEventType::MouseOver)
-			{
-				return true;
-			}
-			else if (ev.getType() == GUIMouseEventType::MouseOut)
-			{
-				return true;
-			}
-			else if (ev.getType() == GUIMouseEventType::MouseMove)
-			{
-				return true;
-			}
-		}
-
-		if (mCaptureMousePresses)
-		{
-			if (ev.getType() == GUIMouseEventType::MouseUp)
-			{
-				return true;
-			}
-			else if (ev.getType() == GUIMouseEventType::MouseDown)
-			{
-				return true;
-			}
-			else if (ev.getType() == GUIMouseEventType::MouseDoubleClick)
-			{
-				return true;
-			}
-		}
-
-		return processed;
-	}
-
-	bool GUIDropDownHitBox::_isInBounds(const Vector2I position) const
-	{
-		for(auto& bound : mBounds)
-		{
-			if(bound.contains(position))
-				return true;
-		}
-
-		return false;
-	}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsGUIDropDownHitBox.h"
+#include "BsGUICommandEvent.h"
+#include "BsGUIMouseEvent.h"
+
+namespace BansheeEngine
+{
+	const String& GUIDropDownHitBox::getGUITypeName()
+	{
+		static String name = "DropDownHitBox";
+		return name;
+	}
+
+	GUIDropDownHitBox* GUIDropDownHitBox::create(bool captureMouseOver, bool captureMousePresses)
+	{
+		return new (bs_alloc<GUIDropDownHitBox>()) 
+			GUIDropDownHitBox(captureMouseOver, captureMousePresses, GUIDimensions::create());
+	}
+
+	GUIDropDownHitBox* GUIDropDownHitBox::create(bool captureMouseOver, bool captureMousePresses, const GUIOptions& options)
+	{
+		return new (bs_alloc<GUIDropDownHitBox>()) 
+			GUIDropDownHitBox(captureMouseOver, captureMousePresses, GUIDimensions::create(options));
+	}
+
+	GUIDropDownHitBox::GUIDropDownHitBox(bool captureMouseOver, 
+		bool captureMousePresses, const GUIDimensions& dimensions)
+		:GUIElementContainer(dimensions), mCaptureMouseOver(captureMouseOver),
+		mCaptureMousePresses(captureMousePresses)
+	{
+
+	}
+
+	void GUIDropDownHitBox::setBounds(const Rect2I& bounds)
+	{
+		mBounds.clear();
+		mBounds.push_back(bounds);
+
+		updateClippedBounds();
+	}
+
+	void GUIDropDownHitBox::setBounds(const Vector<Rect2I>& bounds)
+	{
+		mBounds = bounds;
+
+		updateClippedBounds();
+	}
+
+	void GUIDropDownHitBox::updateClippedBounds()
+	{
+		mClippedBounds = Rect2I();
+
+		if (mBounds.size() > 0)
+		{
+			mClippedBounds = mBounds[0];
+
+			for (UINT32 i = 1; i < (UINT32)mBounds.size(); i++)
+				mClippedBounds.encapsulate(mBounds[i]);
+		}
+	}
+
+	bool GUIDropDownHitBox::_commandEvent(const GUICommandEvent& ev)
+	{
+		bool processed = GUIElementContainer::_commandEvent(ev);
+
+		if(ev.getType() == GUICommandEventType::FocusGained)
+		{
+			if(!onFocusGained.empty())
+				onFocusGained();
+
+			return false;
+		}
+		else if(ev.getType() == GUICommandEventType::FocusLost)
+		{
+			if(!onFocusLost.empty())
+				onFocusLost();
+
+			return false;
+		}
+
+		return processed;
+	}
+
+	bool GUIDropDownHitBox::_mouseEvent(const GUIMouseEvent& ev)
+	{
+		bool processed = GUIElementContainer::_mouseEvent(ev);
+
+		if(mCaptureMouseOver)
+		{
+			if (ev.getType() == GUIMouseEventType::MouseOver)
+			{
+				return true;
+			}
+			else if (ev.getType() == GUIMouseEventType::MouseOut)
+			{
+				return true;
+			}
+			else if (ev.getType() == GUIMouseEventType::MouseMove)
+			{
+				return true;
+			}
+		}
+
+		if (mCaptureMousePresses)
+		{
+			if (ev.getType() == GUIMouseEventType::MouseUp)
+			{
+				return true;
+			}
+			else if (ev.getType() == GUIMouseEventType::MouseDown)
+			{
+				return true;
+			}
+			else if (ev.getType() == GUIMouseEventType::MouseDoubleClick)
+			{
+				return true;
+			}
+		}
+
+		return processed;
+	}
+
+	bool GUIDropDownHitBox::_isInBounds(const Vector2I position) const
+	{
+		for(auto& bound : mBounds)
+		{
+			if(bound.contains(position))
+				return true;
+		}
+
+		return false;
+	}
 };

+ 263 - 257
Source/BansheeEngine/Source/BsGUIElement.cpp

@@ -1,258 +1,264 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsGUIElement.h"
-#include "BsGUIWidget.h"
-#include "BsGUISkin.h"
-#include "BsGUIManager.h"
-
-namespace BansheeEngine
-{
-	const Color GUIElement::DISABLED_COLOR = Color(0.5f, 0.5f, 0.5f, 1.0f);
-
-	GUIElement::GUIElement(const String& styleName, const GUIDimensions& dimensions)
-		:GUIElementBase(dimensions), mStyle(&GUISkin::DefaultStyle),
-		mIsDestroyed(false), mStyleName(styleName)
-	{
-		// Style is set to default here, and the proper one is assigned once GUI element
-		// is assigned to a parent (that's when the active GUI skin becomes known)
-	}
-
-	GUIElement::~GUIElement()
-	{
-
-	}
-
-	void GUIElement::_updateRenderElements()
-	{
-		updateRenderElementsInternal();
-	}
-
-	void GUIElement::updateRenderElementsInternal()
-	{
-		updateClippedBounds();
-	}
-
-	void GUIElement::updateClippedBounds()
-	{
-		mClippedBounds = mLayoutData.area;
-		mClippedBounds.clip(mLayoutData.clipRect);
-	}
-
-	void GUIElement::setStyle(const String& styleName)
-	{
-		mStyleName = styleName;
-		_refreshStyle();
-	}
-
-	bool GUIElement::_mouseEvent(const GUIMouseEvent& ev)
-	{
-		return false;
-	}
-
-	bool GUIElement::_textInputEvent(const GUITextInputEvent& ev)
-	{
-		return false;
-	}
-
-	bool GUIElement::_commandEvent(const GUICommandEvent& ev)
-	{
-		if (ev.getType() == GUICommandEventType::FocusGained)
-			onFocusChanged(true);
-		else if (ev.getType() == GUICommandEventType::FocusLost)
-			onFocusChanged(false);
-
-		return false;
-	}
-
-	bool GUIElement::_virtualButtonEvent(const GUIVirtualButtonEvent& ev)
-	{
-		return false;
-	}
-
-	void GUIElement::setTint(const Color& color)
-	{
-		mColor = color;
-
-		_markContentAsDirty();
-	}
-
-	void GUIElement::_setElementDepth(UINT8 depth)
-	{
-		mLayoutData.depth = depth | (mLayoutData.depth & 0xFFFFFF00);
-		_markMeshAsDirty();
-	}
-
-	UINT8 GUIElement::_getElementDepth() const
-	{
-		return mLayoutData.depth & 0xFF;
-	}
-
-	void GUIElement::_setLayoutData(const GUILayoutData& data)
-	{
-		// Preserve element depth as that is not controlled by layout but is stored
-		// there only for convenience
-		UINT8 elemDepth = _getElementDepth();
-		GUIElementBase::_setLayoutData(data);
-		_setElementDepth(elemDepth);
-
-		updateClippedBounds();
-	}
-
-	void GUIElement::_changeParentWidget(GUIWidget* widget)
-	{
-		if (_isDestroyed())
-			return;
-
-		bool doRefreshStyle = false;
-		if(mParentWidget != widget)
-			doRefreshStyle = true;
-
-		GUIElementBase::_changeParentWidget(widget);
-
-		if(doRefreshStyle)
-			_refreshStyle();
-	}
-
-	const RectOffset& GUIElement::_getPadding() const
-	{
-		if(mStyle != nullptr)
-			return mStyle->padding;
-		else
-		{
-			static RectOffset padding;
-
-			return padding;
-		}
-	}
-
-	void GUIElement::setFocus(bool enabled)
-	{
-		GUIManager::instance().setFocus(this, enabled);
-	}
-
-	void GUIElement::resetDimensions()
-	{
-		mDimensions = GUIDimensions::create();
-		mDimensions.updateWithStyle(mStyle);
-
-		_markLayoutAsDirty();
-	}
-
-	Rect2I GUIElement::getCachedVisibleBounds() const
-	{
-		Rect2I bounds = _getClippedBounds();
-		
-		bounds.x += mStyle->margins.left;
-		bounds.y += mStyle->margins.top;
-		bounds.width = (UINT32)std::max(0, (INT32)bounds.width - (INT32)(mStyle->margins.left + mStyle->margins.right));
-		bounds.height = (UINT32)std::max(0, (INT32)bounds.height - (INT32)(mStyle->margins.top + mStyle->margins.bottom));
-
-		return bounds;
-	}
-
-	Rect2I GUIElement::getCachedContentBounds() const
-	{
-		Rect2I bounds;
-
-		bounds.x = mLayoutData.area.x + mStyle->margins.left + mStyle->contentOffset.left;
-		bounds.y = mLayoutData.area.y + mStyle->margins.top + mStyle->contentOffset.top;
-		bounds.width = (UINT32)std::max(0, (INT32)mLayoutData.area.width -
-			(INT32)(mStyle->margins.left + mStyle->margins.right + mStyle->contentOffset.left + mStyle->contentOffset.right));
-		bounds.height = (UINT32)std::max(0, (INT32)mLayoutData.area.height -
-			(INT32)(mStyle->margins.top + mStyle->margins.bottom + mStyle->contentOffset.top + mStyle->contentOffset.bottom));
-
-		return bounds;
-	}
-
-	Rect2I GUIElement::getCachedContentClipRect() const
-	{
-		Rect2I contentBounds = getCachedContentBounds();
-		
-		// Transform into element space so we can clip it using the element clip rectangle
-		Vector2I offsetDiff = Vector2I(contentBounds.x - mLayoutData.area.x, contentBounds.y - mLayoutData.area.y);
-		Rect2I contentClipRect(offsetDiff.x, offsetDiff.y, contentBounds.width, contentBounds.height);
-		contentClipRect.clip(mLayoutData.getLocalClipRect());
-
-		// Transform into content sprite space
-		contentClipRect.x -= offsetDiff.x;
-		contentClipRect.y -= offsetDiff.y;
-
-		return contentClipRect;
-	}
-
-	Color GUIElement::getTint() const
-	{
-		if (!_isDisabled())
-			return mColor;
-
-		return mColor * DISABLED_COLOR;
-	}
-
-	bool GUIElement::_isInBounds(const Vector2I position) const
-	{
-		Rect2I contentBounds = getCachedVisibleBounds();
-
-		return contentBounds.contains(position);
-	}
-
-	GUIContextMenuPtr GUIElement::_getContextMenu() const
-	{
-		if (!_isDisabled())
-			return mContextMenu;
-
-		return nullptr;
-	}
-
-	void GUIElement::_refreshStyle()
-	{
-		const GUIElementStyle* newStyle = nullptr;
-		if(_getParentWidget() != nullptr && !mStyleName.empty())
-			newStyle = _getParentWidget()->getSkin().getStyle(mStyleName);
-		else
-			newStyle = &GUISkin::DefaultStyle;
-
-		if(newStyle != mStyle)
-		{
-			mStyle = newStyle;
-			mDimensions.updateWithStyle(mStyle);
-			styleUpdated();
-
-			_markLayoutAsDirty();
-		}
-	}
-
-	const String& GUIElement::getSubStyleName(const String& subStyleTypeName) const
-	{
-		auto iterFind = mStyle->subStyles.find(subStyleTypeName);
-
-		if (iterFind != mStyle->subStyles.end())
-			return iterFind->second;
-		else
-			return StringUtil::BLANK;
-	}
-
-	void GUIElement::destroy(GUIElement* element)
-	{
-		if(element->mIsDestroyed)
-			return;
-
-		if (element->mParentElement != nullptr)
-			element->mParentElement->_unregisterChildElement(element);
-
-		element->mIsDestroyed = true;
-
-		GUIManager::instance().queueForDestroy(element);
-	}
-
-	Rect2I GUIElement::getVisibleBounds()
-	{
-		Rect2I bounds = getBounds();
-
-		bounds.x += mStyle->margins.left;
-		bounds.y += mStyle->margins.top;
-		bounds.width = (UINT32)std::max(0, (INT32)bounds.width - (INT32)(mStyle->margins.left + mStyle->margins.right));
-		bounds.height = (UINT32)std::max(0, (INT32)bounds.height - (INT32)(mStyle->margins.top + mStyle->margins.bottom));
-
-		return bounds;
-	}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsGUIElement.h"
+#include "BsGUIWidget.h"
+#include "BsGUISkin.h"
+#include "BsGUIManager.h"
+
+namespace BansheeEngine
+{
+	const Color GUIElement::DISABLED_COLOR = Color(0.5f, 0.5f, 0.5f, 1.0f);
+
+	GUIElement::GUIElement(const String& styleName, const GUIDimensions& dimensions)
+		:GUIElementBase(dimensions), mStyle(&GUISkin::DefaultStyle),
+		mIsDestroyed(false), mStyleName(styleName), mBlockPointerEvents(true)
+	{
+		// Style is set to default here, and the proper one is assigned once GUI element
+		// is assigned to a parent (that's when the active GUI skin becomes known)
+	}
+
+	GUIElement::~GUIElement()
+	{
+
+	}
+
+	void GUIElement::_updateRenderElements()
+	{
+		updateRenderElementsInternal();
+	}
+
+	void GUIElement::updateRenderElementsInternal()
+	{
+		updateClippedBounds();
+	}
+
+	void GUIElement::updateClippedBounds()
+	{
+		mClippedBounds = mLayoutData.area;
+		mClippedBounds.clip(mLayoutData.clipRect);
+	}
+
+	void GUIElement::setStyle(const String& styleName)
+	{
+		mStyleName = styleName;
+		_refreshStyle();
+	}
+
+	bool GUIElement::_mouseEvent(const GUIMouseEvent& ev)
+	{
+		return false;
+	}
+
+	bool GUIElement::_textInputEvent(const GUITextInputEvent& ev)
+	{
+		return false;
+	}
+
+	bool GUIElement::_commandEvent(const GUICommandEvent& ev)
+	{
+		if (ev.getType() == GUICommandEventType::FocusGained)
+		{
+			onFocusChanged(true);
+			return mBlockPointerEvents;
+		}
+		else if (ev.getType() == GUICommandEventType::FocusLost)
+		{
+			onFocusChanged(false);
+			return mBlockPointerEvents;
+		}
+
+		return false;
+	}
+
+	bool GUIElement::_virtualButtonEvent(const GUIVirtualButtonEvent& ev)
+	{
+		return false;
+	}
+
+	void GUIElement::setTint(const Color& color)
+	{
+		mColor = color;
+
+		_markContentAsDirty();
+	}
+
+	void GUIElement::_setElementDepth(UINT8 depth)
+	{
+		mLayoutData.depth = depth | (mLayoutData.depth & 0xFFFFFF00);
+		_markMeshAsDirty();
+	}
+
+	UINT8 GUIElement::_getElementDepth() const
+	{
+		return mLayoutData.depth & 0xFF;
+	}
+
+	void GUIElement::_setLayoutData(const GUILayoutData& data)
+	{
+		// Preserve element depth as that is not controlled by layout but is stored
+		// there only for convenience
+		UINT8 elemDepth = _getElementDepth();
+		GUIElementBase::_setLayoutData(data);
+		_setElementDepth(elemDepth);
+
+		updateClippedBounds();
+	}
+
+	void GUIElement::_changeParentWidget(GUIWidget* widget)
+	{
+		if (_isDestroyed())
+			return;
+
+		bool doRefreshStyle = false;
+		if(mParentWidget != widget)
+			doRefreshStyle = true;
+
+		GUIElementBase::_changeParentWidget(widget);
+
+		if(doRefreshStyle)
+			_refreshStyle();
+	}
+
+	const RectOffset& GUIElement::_getPadding() const
+	{
+		if(mStyle != nullptr)
+			return mStyle->padding;
+		else
+		{
+			static RectOffset padding;
+
+			return padding;
+		}
+	}
+
+	void GUIElement::setFocus(bool enabled)
+	{
+		GUIManager::instance().setFocus(this, enabled);
+	}
+
+	void GUIElement::resetDimensions()
+	{
+		mDimensions = GUIDimensions::create();
+		mDimensions.updateWithStyle(mStyle);
+
+		_markLayoutAsDirty();
+	}
+
+	Rect2I GUIElement::getCachedVisibleBounds() const
+	{
+		Rect2I bounds = _getClippedBounds();
+		
+		bounds.x += mStyle->margins.left;
+		bounds.y += mStyle->margins.top;
+		bounds.width = (UINT32)std::max(0, (INT32)bounds.width - (INT32)(mStyle->margins.left + mStyle->margins.right));
+		bounds.height = (UINT32)std::max(0, (INT32)bounds.height - (INT32)(mStyle->margins.top + mStyle->margins.bottom));
+
+		return bounds;
+	}
+
+	Rect2I GUIElement::getCachedContentBounds() const
+	{
+		Rect2I bounds;
+
+		bounds.x = mLayoutData.area.x + mStyle->margins.left + mStyle->contentOffset.left;
+		bounds.y = mLayoutData.area.y + mStyle->margins.top + mStyle->contentOffset.top;
+		bounds.width = (UINT32)std::max(0, (INT32)mLayoutData.area.width -
+			(INT32)(mStyle->margins.left + mStyle->margins.right + mStyle->contentOffset.left + mStyle->contentOffset.right));
+		bounds.height = (UINT32)std::max(0, (INT32)mLayoutData.area.height -
+			(INT32)(mStyle->margins.top + mStyle->margins.bottom + mStyle->contentOffset.top + mStyle->contentOffset.bottom));
+
+		return bounds;
+	}
+
+	Rect2I GUIElement::getCachedContentClipRect() const
+	{
+		Rect2I contentBounds = getCachedContentBounds();
+		
+		// Transform into element space so we can clip it using the element clip rectangle
+		Vector2I offsetDiff = Vector2I(contentBounds.x - mLayoutData.area.x, contentBounds.y - mLayoutData.area.y);
+		Rect2I contentClipRect(offsetDiff.x, offsetDiff.y, contentBounds.width, contentBounds.height);
+		contentClipRect.clip(mLayoutData.getLocalClipRect());
+
+		// Transform into content sprite space
+		contentClipRect.x -= offsetDiff.x;
+		contentClipRect.y -= offsetDiff.y;
+
+		return contentClipRect;
+	}
+
+	Color GUIElement::getTint() const
+	{
+		if (!_isDisabled())
+			return mColor;
+
+		return mColor * DISABLED_COLOR;
+	}
+
+	bool GUIElement::_isInBounds(const Vector2I position) const
+	{
+		Rect2I contentBounds = getCachedVisibleBounds();
+
+		return contentBounds.contains(position);
+	}
+
+	GUIContextMenuPtr GUIElement::_getContextMenu() const
+	{
+		if (!_isDisabled())
+			return mContextMenu;
+
+		return nullptr;
+	}
+
+	void GUIElement::_refreshStyle()
+	{
+		const GUIElementStyle* newStyle = nullptr;
+		if(_getParentWidget() != nullptr && !mStyleName.empty())
+			newStyle = _getParentWidget()->getSkin().getStyle(mStyleName);
+		else
+			newStyle = &GUISkin::DefaultStyle;
+
+		if(newStyle != mStyle)
+		{
+			mStyle = newStyle;
+			mDimensions.updateWithStyle(mStyle);
+			styleUpdated();
+
+			_markLayoutAsDirty();
+		}
+	}
+
+	const String& GUIElement::getSubStyleName(const String& subStyleTypeName) const
+	{
+		auto iterFind = mStyle->subStyles.find(subStyleTypeName);
+
+		if (iterFind != mStyle->subStyles.end())
+			return iterFind->second;
+		else
+			return StringUtil::BLANK;
+	}
+
+	void GUIElement::destroy(GUIElement* element)
+	{
+		if(element->mIsDestroyed)
+			return;
+
+		if (element->mParentElement != nullptr)
+			element->mParentElement->_unregisterChildElement(element);
+
+		element->mIsDestroyed = true;
+
+		GUIManager::instance().queueForDestroy(element);
+	}
+
+	Rect2I GUIElement::getVisibleBounds()
+	{
+		Rect2I bounds = getBounds();
+
+		bounds.x += mStyle->margins.left;
+		bounds.y += mStyle->margins.top;
+		bounds.width = (UINT32)std::max(0, (INT32)bounds.width - (INT32)(mStyle->margins.left + mStyle->margins.right));
+		bounds.height = (UINT32)std::max(0, (INT32)bounds.height - (INT32)(mStyle->margins.top + mStyle->margins.bottom));
+
+		return bounds;
+	}
 }

+ 51 - 39
Source/BansheeEngine/Source/BsGUIElementContainer.cpp

@@ -1,40 +1,52 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsGUIElementContainer.h"
-#include "BsGUISkin.h"
-
-namespace BansheeEngine
-{
-	GUIElementContainer::GUIElementContainer(const GUIDimensions& dimensions, const String& style)
-		:GUIElement(style, dimensions)
-	{ }
-
-	GUIElementContainer::~GUIElementContainer()
-	{ }
-
-	UINT32 GUIElementContainer::_getNumRenderElements() const
-	{
-		return 0;
-	}
-
-	const SpriteMaterialInfo& GUIElementContainer::_getMaterial(UINT32 renderElementIdx) const
-	{
-		BS_EXCEPT(InvalidStateException, "Trying to retrieve a material from an element with no render elements.");
-		static SpriteMaterialInfo dummy;
-		return dummy;
-	}
-
-	UINT32 GUIElementContainer::_getNumQuads(UINT32 renderElementIdx) const
-	{
-		return 0;
-	}
-
-	void GUIElementContainer::_fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads, 
-		UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const
-	{ }
-
-	Vector2I GUIElementContainer::_getOptimalSize() const
-	{
-		return Vector2I();
-	}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsGUIElementContainer.h"
+#include "BsGUISkin.h"
+#include "BsGUIManager.h"
+
+namespace BansheeEngine
+{
+	GUIElementContainer::GUIElementContainer(const GUIDimensions& dimensions, const String& style)
+		:GUIElement(style, dimensions)
+	{ }
+
+	GUIElementContainer::~GUIElementContainer()
+	{ }
+
+	UINT32 GUIElementContainer::_getNumRenderElements() const
+	{
+		return 0;
+	}
+
+	const SpriteMaterialInfo& GUIElementContainer::_getMaterial(UINT32 renderElementIdx) const
+	{
+		BS_EXCEPT(InvalidStateException, "Trying to retrieve a material from an element with no render elements.");
+		static SpriteMaterialInfo dummy;
+		return dummy;
+	}
+
+	UINT32 GUIElementContainer::_getNumQuads(UINT32 renderElementIdx) const
+	{
+		return 0;
+	}
+
+	void GUIElementContainer::_fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads, 
+		UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const
+	{ }
+
+	Vector2I GUIElementContainer::_getOptimalSize() const
+	{
+		return Vector2I();
+	}
+
+	bool GUIElementContainer::_commandEvent(const GUICommandEvent& ev)
+	{
+		// Make sure to pass through focus events to elements below
+		if (ev.getType() == GUICommandEventType::FocusGained)
+			return false;
+		else if (ev.getType() == GUICommandEventType::FocusLost)
+			return false;
+
+		return GUIElement::_commandEvent(ev);
+	}
 }

+ 1797 - 1787
Source/BansheeEngine/Source/BsGUIManager.cpp

@@ -1,1788 +1,1798 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsGUIManager.h"
-#include "BsGUIWidget.h"
-#include "BsGUIElement.h"
-#include "BsSpriteTexture.h"
-#include "BsTime.h"
-#include "BsSceneObject.h"
-#include "BsMaterial.h"
-#include "BsMeshData.h"
-#include "BsVertexDataDesc.h"
-#include "BsMesh.h"
-#include "BsRenderWindowManager.h"
-#include "BsPlatform.h"
-#include "BsRect2I.h"
-#include "BsCoreApplication.h"
-#include "BsException.h"
-#include "BsInput.h"
-#include "BsGUIInputCaret.h"
-#include "BsGUIInputSelection.h"
-#include "BsGUIContextMenu.h"
-#include "BsDragAndDropManager.h"
-#include "BsGUIDropDownBoxManager.h"
-#include "BsProfilerCPU.h"
-#include "BsMeshHeap.h"
-#include "BsTransientMesh.h"
-#include "BsVirtualInput.h"
-#include "BsCursor.h"
-#include "BsCoreThread.h"
-#include "BsRendererManager.h"
-#include "BsRenderer.h"
-#include "BsCamera.h"
-#include "BsGUITooltipManager.h"
-#include "BsRendererUtility.h"
-#include "BsTexture.h"
-#include "BsRenderTexture.h"
-#include "BsSamplerState.h"
-#include "BsRenderStateManager.h"
-#include "BsBuiltinResources.h"
-
-using namespace std::placeholders;
-
-namespace BansheeEngine
-{
-	struct GUIGroupElement
-	{
-		GUIGroupElement()
-		{ }
-
-		GUIGroupElement(GUIElement* _element, UINT32 _renderElement)
-			:element(_element), renderElement(_renderElement)
-		{ }
-
-		GUIElement* element;
-		UINT32 renderElement;
-	};
-
-	struct GUIMaterialGroup
-	{
-		SpriteMaterialInfo matInfo;
-		UINT32 numQuads;
-		UINT32 depth;
-		UINT32 minDepth;
-		Rect2I bounds;
-		Vector<GUIGroupElement> elements;
-	};
-
-	const UINT32 GUIManager::DRAG_DISTANCE = 3;
-	const float GUIManager::TOOLTIP_HOVER_TIME = 1.0f;
-	const UINT32 GUIManager::MESH_HEAP_INITIAL_NUM_VERTS = 16384;
-	const UINT32 GUIManager::MESH_HEAP_INITIAL_NUM_INDICES = 49152;
-
-	GUIManager::GUIManager()
-		:mSeparateMeshesByWidget(true), mActiveMouseButton(GUIMouseButton::Left),
-		mCaretBlinkInterval(0.5f), mCaretLastBlinkTime(0.0f), mCaretColor(1.0f, 0.6588f, 0.0f), mIsCaretOn(false),
-		mTextSelectionColor(0.0f, 114/255.0f, 188/255.0f), mInputCaret(nullptr), mInputSelection(nullptr), 
-		mDragState(DragState::NoDrag), mActiveCursor(CursorType::Arrow), mCoreDirty(false), mShowTooltip(false), mTooltipElementHoverStart(0.0f)
-	{
-		mOnPointerMovedConn = gInput().onPointerMoved.connect(std::bind(&GUIManager::onPointerMoved, this, _1));
-		mOnPointerPressedConn = gInput().onPointerPressed.connect(std::bind(&GUIManager::onPointerPressed, this, _1));
-		mOnPointerReleasedConn = gInput().onPointerReleased.connect(std::bind(&GUIManager::onPointerReleased, this, _1));
-		mOnPointerDoubleClick = gInput().onPointerDoubleClick.connect(std::bind(&GUIManager::onPointerDoubleClick, this, _1));
-		mOnTextInputConn = gInput().onCharInput.connect(std::bind(&GUIManager::onTextInput, this, _1)); 
-		mOnInputCommandConn = gInput().onInputCommand.connect(std::bind(&GUIManager::onInputCommandEntered, this, _1)); 
-		mOnVirtualButtonDown = VirtualInput::instance().onButtonDown.connect(std::bind(&GUIManager::onVirtualButtonDown, this, _1, _2));
-
-		mWindowGainedFocusConn = RenderWindowManager::instance().onFocusGained.connect(std::bind(&GUIManager::onWindowFocusGained, this, _1));
-		mWindowLostFocusConn = RenderWindowManager::instance().onFocusLost.connect(std::bind(&GUIManager::onWindowFocusLost, this, _1));
-		mMouseLeftWindowConn = RenderWindowManager::instance().onMouseLeftWindow.connect(std::bind(&GUIManager::onMouseLeftWindow, this, _1));
-
-		mInputCaret = bs_new<GUIInputCaret>();
-		mInputSelection = bs_new<GUIInputSelection>();
-
-		DragAndDropManager::startUp();
-		mDragEndedConn = DragAndDropManager::instance().onDragEnded.connect(std::bind(&GUIManager::onMouseDragEnded, this, _1, _2));
-
-		GUIDropDownBoxManager::startUp();
-		GUITooltipManager::startUp();
-
-		mVertexDesc = bs_shared_ptr_new<VertexDataDesc>();
-		mVertexDesc->addVertElem(VET_FLOAT2, VES_POSITION);
-		mVertexDesc->addVertElem(VET_FLOAT2, VES_TEXCOORD);
-
-		mMeshHeap = MeshHeap::create(MESH_HEAP_INITIAL_NUM_VERTS, MESH_HEAP_INITIAL_NUM_INDICES, mVertexDesc);
-
-		// Need to defer this call because I want to make sure all managers are initialized first
-		deferredCall(std::bind(&GUIManager::updateCaretTexture, this));
-		deferredCall(std::bind(&GUIManager::updateTextSelectionTexture, this));
-
-		GUIManagerCore* core = bs_new<GUIManagerCore>();
-		mCore.store(core, std::memory_order_release);
-
-		HMaterial textMaterial = BuiltinResources::instance().createSpriteTextMaterial();
-		HMaterial imageMaterial = BuiltinResources::instance().createSpriteNonAlphaImageMaterial();
-		HMaterial imageAlphaMaterial = BuiltinResources::instance().createSpriteImageMaterial();
-
-		gCoreAccessor().queueCommand(std::bind(&GUIManagerCore::initialize, core,
-			textMaterial->getCore(), imageMaterial->getCore(), imageAlphaMaterial->getCore()));
-	}
-
-	GUIManager::~GUIManager()
-	{
-		GUITooltipManager::shutDown();
-		GUIDropDownBoxManager::shutDown();
-		DragAndDropManager::shutDown();
-
-		// Make a copy of widgets, since destroying them will remove them from mWidgets and
-		// we can't iterate over an array thats getting modified
-		Vector<WidgetInfo> widgetCopy = mWidgets;
-		for(auto& widget : widgetCopy)
-			widget.widget->_destroy();
-
-		// Ensure everything queued get destroyed, loop until queue empties
-		while (processDestroyQueue())
-		{ }
-
-		mOnPointerPressedConn.disconnect();
-		mOnPointerReleasedConn.disconnect();
-		mOnPointerMovedConn.disconnect();
-		mOnPointerDoubleClick.disconnect();
-		mOnTextInputConn.disconnect();
-		mOnInputCommandConn.disconnect();
-		mOnVirtualButtonDown.disconnect();
-
-		mDragEndedConn.disconnect();
-
-		mWindowGainedFocusConn.disconnect();
-		mWindowLostFocusConn.disconnect();
-
-		mMouseLeftWindowConn.disconnect();
-
-		bs_delete(mInputCaret);
-		bs_delete(mInputSelection);
-
-		gCoreAccessor().queueCommand(std::bind(&GUIManager::destroyCore, this, mCore.load(std::memory_order_relaxed)));
-
-		assert(mCachedGUIData.size() == 0);
-	}
-
-	void GUIManager::destroyCore(GUIManagerCore* core)
-	{
-		bs_delete(core);
-	}
-
-	void GUIManager::registerWidget(GUIWidget* widget)
-	{
-		const Viewport* renderTarget = widget->getTarget();
-		if (renderTarget == nullptr)
-			return;
-
-		mWidgets.push_back(WidgetInfo(widget));
-		auto findIter = mCachedGUIData.find(renderTarget);
-
-		if(findIter == end(mCachedGUIData))
-			mCachedGUIData[renderTarget] = GUIRenderData();
-
-		GUIRenderData& windowData = mCachedGUIData[renderTarget];
-		windowData.widgets.push_back(widget);
-		windowData.isDirty = true;
-	}
-
-	void GUIManager::unregisterWidget(GUIWidget* widget)
-	{
-		{
-			auto findIter = std::find_if(begin(mWidgets), end(mWidgets), [=] (const WidgetInfo& x) { return x.widget == widget; } );
-
-			if(findIter != mWidgets.end())
-				mWidgets.erase(findIter);
-		}
-
-		for(auto& entry : mElementsInFocus)
-		{
-			if (entry.widget == widget)
-				entry.widget = nullptr;
-		}
-
-		for (auto& entry : mElementsUnderPointer)
-		{
-			if (entry.widget == widget)
-				entry.widget = nullptr;
-		}
-
-		for (auto& entry : mActiveElements)
-		{
-			if (entry.widget == widget)
-				entry.widget = nullptr;
-		}
-
-		const Viewport* renderTarget = widget->getTarget();
-		GUIRenderData& renderData = mCachedGUIData[renderTarget];
-
-		{
-			auto findIter = std::find(begin(renderData.widgets), end(renderData.widgets), widget);
-			
-			if(findIter != end(renderData.widgets))
-				renderData.widgets.erase(findIter);
-		}
-
-		if(renderData.widgets.size() == 0)
-		{
-			for (auto& mesh : renderData.cachedMeshes)
-			{
-				if (mesh != nullptr)
-					mMeshHeap->dealloc(mesh);
-			}
-
-			mCachedGUIData.erase(renderTarget);
-			mCoreDirty = true;
-		}
-		else
-			renderData.isDirty = true;
-	}
-
-	void GUIManager::update()
-	{
-		DragAndDropManager::instance()._update();
-
-		// Show tooltip if needed
-		if (mShowTooltip)
-		{
-			float diff = gTime().getTime() - mTooltipElementHoverStart;
-			if (diff >= TOOLTIP_HOVER_TIME || gInput().isButtonHeld(BC_LCONTROL) || gInput().isButtonHeld(BC_RCONTROL))
-			{
-				for(auto& entry : mElementsUnderPointer)
-				{
-					const WString& tooltipText = entry.element->_getTooltip();
-					GUIWidget* parentWidget = entry.element->_getParentWidget();
-
-					if (!tooltipText.empty() && parentWidget != nullptr)
-					{
-						const RenderWindow* window = getWidgetWindow(*parentWidget);
-						if (window != nullptr)
-						{
-							Vector2I windowPos = window->screenToWindowPos(gInput().getPointerPosition());
-
-							GUITooltipManager::instance().show(*parentWidget, windowPos, tooltipText);
-							break;
-						}
-					}
-				}
-
-				mShowTooltip = false;
-			}
-		}
-
-		// Update layouts
-		gProfilerCPU().beginSample("UpdateLayout");
-		for(auto& widgetInfo : mWidgets)
-		{
-			widgetInfo.widget->_updateLayout();
-		}
-		gProfilerCPU().endSample("UpdateLayout");
-
-		// Destroy all queued elements (and loop in case any new ones get queued during destruction)
-		do
-		{
-			mNewElementsUnderPointer.clear();
-			for (auto& elementInfo : mElementsUnderPointer)
-			{
-				if (!elementInfo.element->_isDestroyed())
-					mNewElementsUnderPointer.push_back(elementInfo);
-			}
-
-			mElementsUnderPointer.swap(mNewElementsUnderPointer);
-
-			mNewActiveElements.clear();
-			for (auto& elementInfo : mActiveElements)
-			{
-				if (!elementInfo.element->_isDestroyed())
-					mNewActiveElements.push_back(elementInfo);
-			}
-
-			mActiveElements.swap(mNewActiveElements);
-
-			mNewElementsInFocus.clear();
-			for (auto& elementInfo : mElementsInFocus)
-			{
-				if (!elementInfo.element->_isDestroyed())
-					mNewElementsInFocus.push_back(elementInfo);
-			}
-
-			mElementsInFocus.swap(mNewElementsInFocus);
-
-			for (auto& focusElementInfo : mForcedFocusElements)
-			{
-				if (focusElementInfo.element->_isDestroyed())
-					continue;
-
-				if (focusElementInfo.focus)
-				{
-					auto iterFind = std::find_if(mElementsInFocus.begin(), mElementsInFocus.end(),
-						[&](const ElementInfo& x) { return x.element == focusElementInfo.element; });
-
-					if (iterFind == mElementsInFocus.end())
-					{
-						mElementsInFocus.push_back(ElementInfo(focusElementInfo.element, focusElementInfo.element->_getParentWidget()));
-
-						mCommandEvent = GUICommandEvent();
-						mCommandEvent.setType(GUICommandEventType::FocusGained);
-
-						sendCommandEvent(focusElementInfo.element, mCommandEvent);
-					}
-				}
-				else
-				{
-					mNewElementsInFocus.clear();
-					for (auto& elementInfo : mElementsInFocus)
-					{
-						if (elementInfo.element == focusElementInfo.element)
-						{
-							mCommandEvent = GUICommandEvent();
-							mCommandEvent.setType(GUICommandEventType::FocusLost);
-
-							sendCommandEvent(elementInfo.element, mCommandEvent);
-						}
-						else
-							mNewElementsInFocus.push_back(elementInfo);
-					}
-
-					mElementsInFocus.swap(mNewElementsInFocus);
-				}
-			}
-
-			mForcedFocusElements.clear();
-
-		} while (processDestroyQueue());
-
-		// Blink caret
-		float curTime = gTime().getTime();
-
-		if ((curTime - mCaretLastBlinkTime) >= mCaretBlinkInterval)
-		{
-			mCaretLastBlinkTime = curTime;
-			mIsCaretOn = !mIsCaretOn;
-
-			mCommandEvent = GUICommandEvent();
-			mCommandEvent.setType(GUICommandEventType::Redraw);
-
-			for (auto& elementInfo : mElementsInFocus)
-			{
-				sendCommandEvent(elementInfo.element, mCommandEvent);
-			}
-		}
-
-		PROFILE_CALL(updateMeshes(), "UpdateMeshes");
-
-		// Send potentially updated meshes to core for rendering
-		if (mCoreDirty)
-		{
-			UnorderedMap<SPtr<CameraCore>, Vector<GUICoreRenderData>> corePerCameraData;
-
-			for (auto& viewportData : mCachedGUIData)
-			{
-				const GUIRenderData& renderData = viewportData.second;
-
-				SPtr<Camera> camera;
-				for (auto& widget : viewportData.second.widgets)
-				{
-					camera = widget->getCamera();
-					if (camera != nullptr)
-						break;
-				}
-
-				if (camera == nullptr)
-					continue;
-
-				auto insertedData = corePerCameraData.insert(std::make_pair(camera->getCore(), Vector<GUICoreRenderData>()));
-				Vector<GUICoreRenderData>& cameraData = insertedData.first->second;
-
-				UINT32 meshIdx = 0;
-				for (auto& mesh : renderData.cachedMeshes)
-				{
-					SpriteMaterialInfo materialInfo = renderData.cachedMaterials[meshIdx];
-					GUIWidget* widget = renderData.cachedWidgetsPerMesh[meshIdx];
-
-					if (materialInfo.texture == nullptr || !materialInfo.texture.isLoaded())
-					{
-						meshIdx++;
-						continue;
-					}
-
-					if (mesh == nullptr)
-					{
-						meshIdx++;
-						continue;
-					}
-
-					cameraData.push_back(GUICoreRenderData());
-					GUICoreRenderData& newEntry = cameraData.back();
-
-					newEntry.materialType = materialInfo.type;
-					newEntry.texture = materialInfo.texture->getCore();
-					newEntry.tint = materialInfo.tint;
-					newEntry.mesh = mesh->getCore();
-					newEntry.worldTransform = widget->getWorldTfrm();
-
-					meshIdx++;
-				}
-			}
-
-			GUIManagerCore* core = mCore.load(std::memory_order_relaxed);
-			gCoreAccessor().queueCommand(std::bind(&GUIManagerCore::updateData, core, corePerCameraData));
-
-			mCoreDirty = false;
-		}
-	}
-
-	void GUIManager::updateMeshes()
-	{
-		for(auto& cachedMeshData : mCachedGUIData)
-		{
-			GUIRenderData& renderData = cachedMeshData.second;
-
-			// Check if anything is dirty. If nothing is we can skip the update
-			bool isDirty = renderData.isDirty;
-			renderData.isDirty = false;
-
-			for(auto& widget : renderData.widgets)
-			{
-				if (widget->isDirty(true))
-				{
-					isDirty = true;
-				}
-			}
-
-			if(!isDirty)
-				continue;
-
-			mCoreDirty = true;
-
-			bs_frame_mark();
-			{
-				// Make a list of all GUI elements, sorted from farthest to nearest (highest depth to lowest)
-				auto elemComp = [](const GUIGroupElement& a, const GUIGroupElement& b)
-				{
-					UINT32 aDepth = a.element->_getRenderElementDepth(a.renderElement);
-					UINT32 bDepth = b.element->_getRenderElementDepth(b.renderElement);
-
-					// Compare pointers just to differentiate between two elements with the same depth, their order doesn't really matter, but std::set
-					// requires all elements to be unique
-					return (aDepth > bDepth) || 
-						(aDepth == bDepth && a.element > b.element) || 
-						(aDepth == bDepth && a.element == b.element && a.renderElement > b.renderElement); 
-				};
-
-				FrameSet<GUIGroupElement, std::function<bool(const GUIGroupElement&, const GUIGroupElement&)>> allElements(elemComp);
-
-				for (auto& widget : renderData.widgets)
-				{
-					const Vector<GUIElement*>& elements = widget->getElements();
-
-					for (auto& element : elements)
-					{
-						if (!element->_isVisible())
-							continue;
-
-						UINT32 numRenderElems = element->_getNumRenderElements();
-						for (UINT32 i = 0; i < numRenderElems; i++)
-						{
-							allElements.insert(GUIGroupElement(element, i));
-						}
-					}
-				}
-
-				// Group the elements in such a way so that we end up with a smallest amount of
-				// meshes, without breaking back to front rendering order
-				FrameUnorderedMap<std::reference_wrapper<const SpriteMaterialInfo>, FrameVector<GUIMaterialGroup>> materialGroups;
-				for (auto& elem : allElements)
-				{
-					GUIElement* guiElem = elem.element;
-					UINT32 renderElemIdx = elem.renderElement;
-					UINT32 elemDepth = guiElem->_getRenderElementDepth(renderElemIdx);
-
-					Rect2I tfrmedBounds = guiElem->_getClippedBounds();
-					tfrmedBounds.transform(guiElem->_getParentWidget()->getWorldTfrm());
-
-					const SpriteMaterialInfo& matInfo = guiElem->_getMaterial(renderElemIdx);
-					FrameVector<GUIMaterialGroup>& groupsPerMaterial = materialGroups[std::cref(matInfo)];
-					
-					// Try to find a group this material will fit in:
-					//  - Group that has a depth value same or one below elements depth will always be a match
-					//  - Otherwise, we search higher depth values as well, but we only use them if no elements in between those depth values
-					//    overlap the current elements bounds.
-					GUIMaterialGroup* foundGroup = nullptr;
-
-					for (auto groupIter = groupsPerMaterial.rbegin(); groupIter != groupsPerMaterial.rend(); ++groupIter)
-					{
-						// If we separate meshes by widget, ignore any groups with widget parents other than mine
-						if (mSeparateMeshesByWidget)
-						{
-							if (groupIter->elements.size() > 0)
-							{
-								GUIElement* otherElem = groupIter->elements.begin()->element; // We only need to check the first element
-								if (otherElem->_getParentWidget() != guiElem->_getParentWidget())
-									continue;
-							}
-						}
-
-						GUIMaterialGroup& group = *groupIter;
-
-						if (group.depth == elemDepth)
-						{
-							foundGroup = &group;
-							break;
-						}
-						else
-						{
-							UINT32 startDepth = elemDepth;
-							UINT32 endDepth = group.depth;
-
-							Rect2I potentialGroupBounds = group.bounds;
-							potentialGroupBounds.encapsulate(tfrmedBounds);
-
-							bool foundOverlap = false;
-							for (auto& material : materialGroups)
-							{
-								for (auto& matGroup : material.second)
-								{
-									if (&matGroup == &group)
-										continue;
-
-									if ((matGroup.minDepth >= startDepth && matGroup.minDepth <= endDepth)
-										|| (matGroup.depth >= startDepth && matGroup.depth <= endDepth))
-									{
-										if (matGroup.bounds.overlaps(potentialGroupBounds))
-										{
-											foundOverlap = true;
-											break;
-										}
-									}
-								}
-							}
-
-							if (!foundOverlap)
-							{
-								foundGroup = &group;
-								break;
-							}
-						}
-					}
-
-					if (foundGroup == nullptr)
-					{
-						groupsPerMaterial.push_back(GUIMaterialGroup());
-						foundGroup = &groupsPerMaterial[groupsPerMaterial.size() - 1];
-
-						foundGroup->depth = elemDepth;
-						foundGroup->minDepth = elemDepth;
-						foundGroup->bounds = tfrmedBounds;
-						foundGroup->elements.push_back(GUIGroupElement(guiElem, renderElemIdx));
-						foundGroup->matInfo = matInfo;
-						foundGroup->numQuads = guiElem->_getNumQuads(renderElemIdx);
-					}
-					else
-					{
-						foundGroup->bounds.encapsulate(tfrmedBounds);
-						foundGroup->elements.push_back(GUIGroupElement(guiElem, renderElemIdx));
-						foundGroup->minDepth = std::min(foundGroup->minDepth, elemDepth);
-						foundGroup->numQuads += guiElem->_getNumQuads(renderElemIdx);
-					}
-				}
-
-				// Make a list of all GUI elements, sorted from farthest to nearest (highest depth to lowest)
-				auto groupComp = [](GUIMaterialGroup* a, GUIMaterialGroup* b)
-				{
-					return (a->depth > b->depth) || (a->depth == b->depth && a > b);
-					// Compare pointers just to differentiate between two elements with the same depth, their order doesn't really matter, but std::set
-					// requires all elements to be unique
-				};
-
-				FrameSet<GUIMaterialGroup*, std::function<bool(GUIMaterialGroup*, GUIMaterialGroup*)>> sortedGroups(groupComp);
-				for(auto& material : materialGroups)
-				{
-					for(auto& group : material.second)
-					{
-						sortedGroups.insert(&group);
-					}
-				}
-
-				UINT32 numMeshes = (UINT32)sortedGroups.size();
-				UINT32 oldNumMeshes = (UINT32)renderData.cachedMeshes.size();
-
-				if(numMeshes < oldNumMeshes)
-				{
-					for (UINT32 i = numMeshes; i < oldNumMeshes; i++)
-						mMeshHeap->dealloc(renderData.cachedMeshes[i]);
-
-					renderData.cachedMeshes.resize(numMeshes);
-				}
-
-				renderData.cachedMaterials.resize(numMeshes);
-
-				if(mSeparateMeshesByWidget)
-					renderData.cachedWidgetsPerMesh.resize(numMeshes);
-
-				// Fill buffers for each group and update their meshes
-				UINT32 groupIdx = 0;
-				for(auto& group : sortedGroups)
-				{
-					renderData.cachedMaterials[groupIdx] = group->matInfo;
-
-					if(mSeparateMeshesByWidget)
-					{
-						if(group->elements.size() == 0)
-							renderData.cachedWidgetsPerMesh[groupIdx] = nullptr;
-						else
-						{
-							GUIElement* elem = group->elements.begin()->element;
-							renderData.cachedWidgetsPerMesh[groupIdx] = elem->_getParentWidget();
-						}
-					}
-
-					MeshDataPtr meshData = bs_shared_ptr_new<MeshData>(group->numQuads * 4, group->numQuads * 6, mVertexDesc);
-
-					UINT8* vertices = meshData->getElementData(VES_POSITION);
-					UINT8* uvs = meshData->getElementData(VES_TEXCOORD);
-					UINT32* indices = meshData->getIndices32();
-					UINT32 vertexStride = meshData->getVertexDesc()->getVertexStride();
-					UINT32 indexStride = meshData->getIndexElementSize();
-
-					UINT32 quadOffset = 0;
-					for(auto& matElement : group->elements)
-					{
-						matElement.element->_fillBuffer(vertices, uvs, indices, quadOffset, group->numQuads, vertexStride, indexStride, matElement.renderElement);
-
-						UINT32 numQuads = matElement.element->_getNumQuads(matElement.renderElement);
-						UINT32 indexStart = quadOffset * 6;
-						UINT32 indexEnd = indexStart + numQuads * 6;
-						UINT32 vertOffset = quadOffset * 4;
-
-						for(UINT32 i = indexStart; i < indexEnd; i++)
-							indices[i] += vertOffset;
-
-						quadOffset += numQuads;
-					}
-
-					if(groupIdx < (UINT32)renderData.cachedMeshes.size())
-					{
-						mMeshHeap->dealloc(renderData.cachedMeshes[groupIdx]);
-						renderData.cachedMeshes[groupIdx] = mMeshHeap->alloc(meshData);
-					}
-					else
-					{
-						renderData.cachedMeshes.push_back(mMeshHeap->alloc(meshData));
-					}
-
-					groupIdx++;
-				}
-			}
-
-			bs_frame_clear();			
-		}
-	}
-
-	void GUIManager::updateCaretTexture()
-	{
-		if(mCaretTexture == nullptr)
-		{
-			HTexture newTex = Texture::create(TEX_TYPE_2D, 1, 1, 0, PF_R8G8B8A8);
-			mCaretTexture = SpriteTexture::create(newTex);
-		}
-
-		const HTexture& tex = mCaretTexture->getTexture();
-		UINT32 subresourceIdx = tex->getProperties().mapToSubresourceIdx(0, 0);
-		PixelDataPtr data = tex->getProperties().allocateSubresourceBuffer(subresourceIdx);
-
-		data->setColorAt(mCaretColor, 0, 0);
-		tex->writeSubresource(gCoreAccessor(), subresourceIdx, data, false);
-	}
-
-	void GUIManager::updateTextSelectionTexture()
-	{
-		if(mTextSelectionTexture == nullptr)
-		{
-			HTexture newTex = Texture::create(TEX_TYPE_2D, 1, 1, 0, PF_R8G8B8A8);
-			mTextSelectionTexture = SpriteTexture::create(newTex);
-		}
-
-		const HTexture& tex = mTextSelectionTexture->getTexture();
-		UINT32 subresourceIdx = tex->getProperties().mapToSubresourceIdx(0, 0);
-		PixelDataPtr data = tex->getProperties().allocateSubresourceBuffer(subresourceIdx);
-
-		data->setColorAt(mTextSelectionColor, 0, 0);
-
-		tex->writeSubresource(gCoreAccessor(), subresourceIdx, data, false);
-	}
-
-	void GUIManager::onMouseDragEnded(const PointerEvent& event, DragCallbackInfo& dragInfo)
-	{
-		GUIMouseButton guiButton = buttonToGUIButton(event.button);
-
-		if(DragAndDropManager::instance().isDragInProgress() && guiButton == GUIMouseButton::Left)
-		{
-			for(auto& elementInfo : mElementsUnderPointer)
-			{
-				Vector2I localPos;
-
-				if(elementInfo.widget != nullptr)
-					localPos = getWidgetRelativePos(elementInfo.widget, event.screenPos);
-
-				bool acceptDrop = true;
-				if(DragAndDropManager::instance().needsValidDropTarget())
-				{
-					acceptDrop = elementInfo.element->_acceptDragAndDrop(localPos, DragAndDropManager::instance().getDragTypeId());
-				}
-
-				if(acceptDrop)
-				{
-					mMouseEvent.setDragAndDropDroppedData(localPos, DragAndDropManager::instance().getDragTypeId(), DragAndDropManager::instance().getDragData());
-					dragInfo.processed = sendMouseEvent(elementInfo.element, mMouseEvent);
-
-					if(dragInfo.processed)
-						return;
-				}
-			}
-		}
-
-		dragInfo.processed = false;
-	}
-
-	void GUIManager::onPointerMoved(const PointerEvent& event)
-	{
-		if(event.isUsed())
-			return;
-
-		bool buttonStates[(int)GUIMouseButton::Count];
-		buttonStates[0] = event.buttonStates[0];
-		buttonStates[1] = event.buttonStates[1];
-		buttonStates[2] = event.buttonStates[2];
-
-		if(findElementUnderPointer(event.screenPos, buttonStates, event.shift, event.control, event.alt))
-			event.markAsUsed();
-
-		if(mDragState == DragState::HeldWithoutDrag)
-		{
-			UINT32 dist = mLastPointerClickPos.manhattanDist(event.screenPos);
-
-			if(dist > DRAG_DISTANCE)
-			{
-				for(auto& activeElement : mActiveElements)
-				{
-					Vector2I localPos = getWidgetRelativePos(activeElement.widget, event.screenPos);
-					Vector2I localDragStartPos = getWidgetRelativePos(activeElement.widget, mLastPointerClickPos);
-
-					mMouseEvent.setMouseDragStartData(localPos, localDragStartPos);
-					if(sendMouseEvent(activeElement.element, mMouseEvent))
-						event.markAsUsed();
-				}
-
-				mDragState = DragState::Dragging;
-				mDragStartPos = event.screenPos;
-			}
-		}
-
-		// If mouse is being held down send MouseDrag events
-		if(mDragState == DragState::Dragging)
-		{
-			for(auto& activeElement : mActiveElements)
-			{
-				if(mLastPointerScreenPos != event.screenPos)
-				{
-					Vector2I localPos = getWidgetRelativePos(activeElement.widget, event.screenPos);
-
-					mMouseEvent.setMouseDragData(localPos, event.screenPos - mDragStartPos);
-					if(sendMouseEvent(activeElement.element, mMouseEvent))
-						event.markAsUsed();
-				}
-			}
-
-			mLastPointerScreenPos = event.screenPos;
-
-			// Also if drag is in progress send DragAndDrop events
-			if(DragAndDropManager::instance().isDragInProgress())
-			{
-				bool acceptDrop = true;
-				for(auto& elementInfo : mElementsUnderPointer)
-				{
-					Vector2I localPos = getWidgetRelativePos(elementInfo.widget, event.screenPos);
-
-					acceptDrop = true;
-					if(DragAndDropManager::instance().needsValidDropTarget())
-					{
-						acceptDrop = elementInfo.element->_acceptDragAndDrop(localPos, DragAndDropManager::instance().getDragTypeId());
-					}
-
-					if(acceptDrop)
-					{
-						mMouseEvent.setDragAndDropDraggedData(localPos, DragAndDropManager::instance().getDragTypeId(), DragAndDropManager::instance().getDragData());
-						if(sendMouseEvent(elementInfo.element, mMouseEvent))
-						{
-							event.markAsUsed();
-							break;
-						}
-					}
-				}
-
-				if(acceptDrop)
-				{
-					if(mActiveCursor != CursorType::ArrowDrag)
-					{
-						Cursor::instance().setCursor(CursorType::ArrowDrag);
-						mActiveCursor = CursorType::ArrowDrag;
-					}
-				}
-				else
-				{
-					if(mActiveCursor != CursorType::Deny)
-					{
-						Cursor::instance().setCursor(CursorType::Deny);
-						mActiveCursor = CursorType::Deny;
-					}
-				}				
-			}
-		}
-		else // Otherwise, send MouseMove events if we are hovering over any element
-		{
-			if(mLastPointerScreenPos != event.screenPos)
-			{
-				bool moveProcessed = false;
-				bool hasCustomCursor = false;
-				for(auto& elementInfo : mElementsUnderPointer)
-				{
-					Vector2I localPos = getWidgetRelativePos(elementInfo.widget, event.screenPos);
-
-					if(!moveProcessed)
-					{
-						// Send MouseMove event
-						mMouseEvent.setMouseMoveData(localPos);
-						moveProcessed = sendMouseEvent(elementInfo.element, mMouseEvent);
-
-						if(moveProcessed)
-							event.markAsUsed();
-					}
-
-					if (mDragState == DragState::NoDrag)
-					{
-						CursorType newCursor = CursorType::Arrow;
-						if(elementInfo.element->_hasCustomCursor(localPos, newCursor))
-						{
-							if(newCursor != mActiveCursor)
-							{
-								Cursor::instance().setCursor(newCursor);
-								mActiveCursor = newCursor;
-							}
-
-							hasCustomCursor = true;
-						}
-					}
-
-					if(moveProcessed)
-						break;
-				}
-
-				// While dragging we don't want to modify the cursor
-				if (mDragState == DragState::NoDrag)
-				{
-					if (!hasCustomCursor)
-					{
-						if (mActiveCursor != CursorType::Arrow)
-						{
-							Cursor::instance().setCursor(CursorType::Arrow);
-							mActiveCursor = CursorType::Arrow;
-						}
-					}
-				}
-			}
-
-			mLastPointerScreenPos = event.screenPos;
-
-			if(Math::abs(event.mouseWheelScrollAmount) > 0.00001f)
-			{
-				for(auto& elementInfo : mElementsUnderPointer)
-				{
-					mMouseEvent.setMouseWheelScrollData(event.mouseWheelScrollAmount);
-					if(sendMouseEvent(elementInfo.element, mMouseEvent))
-					{
-						event.markAsUsed();
-						break;
-					}
-				}
-			}
-		}
-	}
-
-	void GUIManager::onPointerReleased(const PointerEvent& event)
-	{
-		if(event.isUsed())
-			return;
-
-		bool buttonStates[(int)GUIMouseButton::Count];
-		buttonStates[0] = event.buttonStates[0];
-		buttonStates[1] = event.buttonStates[1];
-		buttonStates[2] = event.buttonStates[2];
-
-		if(findElementUnderPointer(event.screenPos, buttonStates, event.shift, event.control, event.alt))
-			event.markAsUsed();
-
-		mMouseEvent = GUIMouseEvent(buttonStates, event.shift, event.control, event.alt);
-
-		GUIMouseButton guiButton = buttonToGUIButton(event.button);
-
-		// Send MouseUp event only if we are over the active element (we don't want to accidentally trigger other elements).
-		// And only activate when a button that originally caused the active state is released, otherwise ignore it.
-		if(mActiveMouseButton == guiButton)
-		{
-			for(auto& elementInfo : mElementsUnderPointer)
-			{
-				auto iterFind2 = std::find_if(mActiveElements.begin(), mActiveElements.end(), 
-					[&](const ElementInfo& x) { return x.element == elementInfo.element; });
-
-				if(iterFind2 != mActiveElements.end())
-				{
-					Vector2I localPos = getWidgetRelativePos(elementInfo.widget, event.screenPos);
-					mMouseEvent.setMouseUpData(localPos, guiButton);
-
-					if(sendMouseEvent(elementInfo.element, mMouseEvent))
-					{
-						event.markAsUsed();
-						break;
-					}
-				}
-			}
-		}
-
-		// Send DragEnd event to whichever element is active
-		bool acceptEndDrag = (mDragState == DragState::Dragging || mDragState == DragState::HeldWithoutDrag) && mActiveMouseButton == guiButton && 
-			(guiButton == GUIMouseButton::Left);
-
-		if(acceptEndDrag)
-		{
-			if(mDragState == DragState::Dragging)
-			{
-				for(auto& activeElement : mActiveElements)
-				{
-					Vector2I localPos = getWidgetRelativePos(activeElement.widget, event.screenPos);
-
-					mMouseEvent.setMouseDragEndData(localPos);
-					if(sendMouseEvent(activeElement.element, mMouseEvent))
-						event.markAsUsed();
-				}
-			}
-
-			mDragState = DragState::NoDrag;
-		}
-
-		if(mActiveMouseButton == guiButton)
-		{
-			mActiveElements.clear();
-			mActiveMouseButton = GUIMouseButton::Left;
-		}
-
-		if(mActiveCursor != CursorType::Arrow)
-		{
-			Cursor::instance().setCursor(CursorType::Arrow);
-			mActiveCursor = CursorType::Arrow;
-		}
-	}
-
-	void GUIManager::onPointerPressed(const PointerEvent& event)
-	{
-		if(event.isUsed())
-			return;
-
-		bool buttonStates[(int)GUIMouseButton::Count];
-		buttonStates[0] = event.buttonStates[0];
-		buttonStates[1] = event.buttonStates[1];
-		buttonStates[2] = event.buttonStates[2];
-
-		if(findElementUnderPointer(event.screenPos, buttonStates, event.shift, event.control, event.alt))
-			event.markAsUsed();
-
-		mMouseEvent = GUIMouseEvent(buttonStates, event.shift, event.control, event.alt);
-
-		GUIMouseButton guiButton = buttonToGUIButton(event.button);
-
-		// We only check for mouse down if mouse isn't already being held down, and we are hovering over an element
-		if(mActiveElements.size() == 0)
-		{
-			mNewActiveElements.clear();
-			for(auto& elementInfo : mElementsUnderPointer)
-			{
-				Vector2I localPos = getWidgetRelativePos(elementInfo.widget, event.screenPos);
-				mMouseEvent.setMouseDownData(localPos, guiButton);
-
-				bool processed = sendMouseEvent(elementInfo.element, mMouseEvent);
-
-				if(guiButton == GUIMouseButton::Left)
-				{
-					mDragState = DragState::HeldWithoutDrag;
-					mLastPointerClickPos = event.screenPos;
-				}
-
-				mNewActiveElements.push_back(ElementInfo(elementInfo.element, elementInfo.widget));
-				mActiveMouseButton = guiButton;
-
-				if(processed)
-				{
-					event.markAsUsed();
-					break;
-				}
-			}
-
-			mActiveElements.swap(mNewActiveElements);
-		}
-
-		mNewElementsInFocus.clear();
-		mCommandEvent = GUICommandEvent();
-		
-		// Determine elements that gained focus
-		mCommandEvent.setType(GUICommandEventType::FocusGained);
-
-		for(auto& elementInfo : mElementsUnderPointer)
-		{
-			mNewElementsInFocus.push_back(ElementInfo(elementInfo.element, elementInfo.widget));
-
-			auto iterFind = std::find_if(begin(mElementsInFocus), end(mElementsInFocus), 
-				[=] (const ElementInfo& x) { return x.element == elementInfo.element; });
-
-			if(iterFind == mElementsInFocus.end())
-			{
-				sendCommandEvent(elementInfo.element, mCommandEvent);
-			}
-		}
-
-		// Determine elements that lost focus
-		mCommandEvent.setType(GUICommandEventType::FocusLost);
-
-		for(auto& elementInfo : mElementsInFocus)
-		{
-			auto iterFind = std::find_if(begin(mNewElementsInFocus), end(mNewElementsInFocus), 
-				[=] (const ElementInfo& x) { return x.element == elementInfo.element; });
-
-			if(iterFind == mNewElementsInFocus.end())
-			{
-				sendCommandEvent(elementInfo.element, mCommandEvent);
-			}
-		}
-
-		if(mElementsUnderPointer.size() > 0)
-			event.markAsUsed();
-
-		mElementsInFocus.swap(mNewElementsInFocus);
-
-		// If right click try to open context menu
-		if(buttonStates[2] == true) 
-		{
-			for(auto& elementInfo : mElementsUnderPointer)
-			{
-				GUIContextMenuPtr menu = elementInfo.element->_getContextMenu();
-
-				if(menu != nullptr && elementInfo.widget != nullptr)
-				{
-					const RenderWindow* window = getWidgetWindow(*elementInfo.widget);
-					if (window != nullptr)
-					{
-						Vector2I windowPos = window->screenToWindowPos(event.screenPos);
-
-						menu->open(windowPos, *elementInfo.widget);
-						event.markAsUsed();
-						break;
-					}
-				}
-			}
-		}
-	}
-
-	void GUIManager::onPointerDoubleClick(const PointerEvent& event)
-	{
-		if(event.isUsed())
-			return;
-
-		bool buttonStates[(int)GUIMouseButton::Count];
-		buttonStates[0] = event.buttonStates[0];
-		buttonStates[1] = event.buttonStates[1];
-		buttonStates[2] = event.buttonStates[2];
-
-		if(findElementUnderPointer(event.screenPos, buttonStates, event.shift, event.control, event.alt))
-			event.markAsUsed();
-
-		mMouseEvent = GUIMouseEvent(buttonStates, event.shift, event.control, event.alt);
-
-		GUIMouseButton guiButton = buttonToGUIButton(event.button);
-
-		// We only check for mouse down if we are hovering over an element
-		for(auto& elementInfo : mElementsUnderPointer)
-		{
-			Vector2I localPos = getWidgetRelativePos(elementInfo.widget, event.screenPos);
-
-			mMouseEvent.setMouseDoubleClickData(localPos, guiButton);
-			if(sendMouseEvent(elementInfo.element, mMouseEvent))
-			{
-				event.markAsUsed();
-				break;
-			}
-		}
-	}
-
-	void GUIManager::onInputCommandEntered(InputCommandType commandType)
-	{
-		if(mElementsInFocus.size() == 0)
-			return;
-
-		hideTooltip();
-		mCommandEvent = GUICommandEvent();
-
-		switch(commandType)
-		{
-		case InputCommandType::Backspace:
-			mCommandEvent.setType(GUICommandEventType::Backspace);
-			break;
-		case InputCommandType::Delete:
-			mCommandEvent.setType(GUICommandEventType::Delete);
-			break;
-		case InputCommandType::Return:
-			mCommandEvent.setType(GUICommandEventType::Return);
-			break;
-		case InputCommandType::Confirm:
-			mCommandEvent.setType(GUICommandEventType::Confirm);
-			break;
-		case InputCommandType::Escape:
-			mCommandEvent.setType(GUICommandEventType::Escape);
-			break;
-		case InputCommandType::CursorMoveLeft:
-			mCommandEvent.setType(GUICommandEventType::MoveLeft);
-			break;
-		case InputCommandType::CursorMoveRight:
-			mCommandEvent.setType(GUICommandEventType::MoveRight);
-			break;
-		case InputCommandType::CursorMoveUp:
-			mCommandEvent.setType(GUICommandEventType::MoveUp);
-			break;
-		case InputCommandType::CursorMoveDown:
-			mCommandEvent.setType(GUICommandEventType::MoveDown);
-			break;
-		case InputCommandType::SelectLeft:
-			mCommandEvent.setType(GUICommandEventType::SelectLeft);
-			break;
-		case InputCommandType::SelectRight:
-			mCommandEvent.setType(GUICommandEventType::SelectRight);
-			break;
-		case InputCommandType::SelectUp:
-			mCommandEvent.setType(GUICommandEventType::SelectUp);
-			break;
-		case InputCommandType::SelectDown:
-			mCommandEvent.setType(GUICommandEventType::SelectDown);
-			break;
-		}
-
-		for(auto& elementInfo : mElementsInFocus)
-		{
-			sendCommandEvent(elementInfo.element, mCommandEvent);
-		}		
-	}
-
-	void GUIManager::onVirtualButtonDown(const VirtualButton& button, UINT32 deviceIdx)
-	{
-		hideTooltip();
-		mVirtualButtonEvent.setButton(button);
-		
-		for(auto& elementInFocus : mElementsInFocus)
-		{
-			bool processed = sendVirtualButtonEvent(elementInFocus.element, mVirtualButtonEvent);
-
-			if(processed)
-				break;
-		}
-	}
-
-	bool GUIManager::findElementUnderPointer(const Vector2I& pointerScreenPos, bool buttonStates[3], bool shift, bool control, bool alt)
-	{
-		Vector<const RenderWindow*> widgetWindows;
-		for(auto& widgetInfo : mWidgets)
-			widgetWindows.push_back(getWidgetWindow(*widgetInfo.widget));
-
-#if BS_DEBUG_MODE
-		// Checks if all referenced windows actually exist
-		Vector<RenderWindow*> activeWindows = RenderWindowManager::instance().getRenderWindows();
-		for(auto& window : widgetWindows)
-		{
-			if(window == nullptr)
-				continue;
-
-			auto iterFind = std::find(begin(activeWindows), end(activeWindows), window);
-
-			if(iterFind == activeWindows.end())
-			{
-				BS_EXCEPT(InternalErrorException, "GUI manager has a reference to a window that doesn't exist. \
-												  Please detach all GUIWidgets from windows before destroying a window.");
-			}
-		}
-#endif
-
-		mNewElementsUnderPointer.clear();
-
-		const RenderWindow* windowUnderPointer = nullptr;
-		UnorderedSet<const RenderWindow*> uniqueWindows;
-
-		for(auto& window : widgetWindows)
-		{
-			if(window == nullptr)
-				continue;
-
-			uniqueWindows.insert(window);
-		}
-
-		for(auto& window : uniqueWindows)
-		{
-			if(Platform::isPointOverWindow(*window, pointerScreenPos))
-			{
-				windowUnderPointer = window;
-				break;
-			}
-		}
-
-		if(windowUnderPointer != nullptr)
-		{
-			Vector2I windowPos = windowUnderPointer->screenToWindowPos(pointerScreenPos);
-			Vector4 vecWindowPos((float)windowPos.x, (float)windowPos.y, 0.0f, 1.0f);
-
-			UINT32 widgetIdx = 0;
-			for(auto& widgetInfo : mWidgets)
-			{
-				if(widgetWindows[widgetIdx] == nullptr)
-				{
-					widgetIdx++;
-					continue;
-				}
-
-				GUIWidget* widget = widgetInfo.widget;
-				if(widgetWindows[widgetIdx] == windowUnderPointer && widget->inBounds(windowToBridgedCoords(*widget, windowPos)))
-				{
-					const Vector<GUIElement*>& elements = widget->getElements();
-					Vector2I localPos = getWidgetRelativePos(widget, pointerScreenPos);
-
-					// Elements with lowest depth (most to the front) get handled first
-					for(auto iter = elements.begin(); iter != elements.end(); ++iter)
-					{
-						GUIElement* element = *iter;
-
-						if(element->_isVisible() && element->_isInBounds(localPos))
-						{
-							ElementInfoUnderPointer elementInfo(element, widget);
-
-							auto iterFind = std::find_if(mElementsUnderPointer.begin(), mElementsUnderPointer.end(),
-								[=](const ElementInfoUnderPointer& x) { return x.element == element; });
-
-							if (iterFind != mElementsUnderPointer.end())
-							{
-								elementInfo.usesMouseOver = iterFind->usesMouseOver;
-								elementInfo.receivedMouseOver = iterFind->receivedMouseOver;
-							}
-
-							mNewElementsUnderPointer.push_back(elementInfo);
-						}
-					}
-				}
-
-				widgetIdx++;
-			}
-		}
-
-		std::sort(mNewElementsUnderPointer.begin(), mNewElementsUnderPointer.end(), 
-			[](const ElementInfoUnderPointer& a, const ElementInfoUnderPointer& b)
-		{
-			return a.element->_getDepth() < b.element->_getDepth();
-		});
-
-		// Send MouseOut and MouseOver events
-		bool eventProcessed = false;
-
-		for (auto& elementInfo : mNewElementsUnderPointer)
-		{
-			GUIElement* element = elementInfo.element;
-			GUIWidget* widget = elementInfo.widget;
-
-			if (elementInfo.receivedMouseOver)
-			{
-				elementInfo.isHovering = true;
-				if (elementInfo.usesMouseOver)
-					break;
-
-				continue;
-			}
-
-			auto iterFind = std::find_if(mActiveElements.begin(), mActiveElements.end(),
-				[&](const ElementInfo& x) { return x.element == element; });
-
-			// Send MouseOver event
-			if (mActiveElements.size() == 0 || iterFind != mActiveElements.end())
-			{
-				Vector2I localPos = getWidgetRelativePos(widget, pointerScreenPos);
-
-				mMouseEvent = GUIMouseEvent(buttonStates, shift, control, alt);
-
-				mMouseEvent.setMouseOverData(localPos);
-				elementInfo.receivedMouseOver = true;
-				elementInfo.isHovering = true;
-				if (sendMouseEvent(element, mMouseEvent))
-				{
-					eventProcessed = true;
-					elementInfo.usesMouseOver = true;
-					break;
-				}
-			}
-		}
-
-		// Send DragAndDropLeft event - It is similar to MouseOut events but we send it to all
-		// elements a user might hover over, while we send mouse over/out events only to active elements while dragging
-		if (DragAndDropManager::instance().isDragInProgress())
-		{
-			for (auto& elementInfo : mElementsUnderPointer)
-			{
-				auto iterFind = std::find_if(mNewElementsUnderPointer.begin(), mNewElementsUnderPointer.end(),
-					[=](const ElementInfoUnderPointer& x) { return x.element == elementInfo.element; });
-
-				if (iterFind == mNewElementsUnderPointer.end())
-				{
-					Vector2I localPos = getWidgetRelativePos(elementInfo.widget, pointerScreenPos);
-
-					mMouseEvent.setDragAndDropLeftData(localPos, DragAndDropManager::instance().getDragTypeId(), DragAndDropManager::instance().getDragData());
-					if (sendMouseEvent(elementInfo.element, mMouseEvent))
-					{
-						eventProcessed = true;
-						break;
-					}
-				}
-			}
-		}
-
-		for(auto& elementInfo : mElementsUnderPointer)
-		{
-			GUIElement* element = elementInfo.element;
-			GUIWidget* widget = elementInfo.widget;
-
-			auto iterFind = std::find_if(mNewElementsUnderPointer.begin(), mNewElementsUnderPointer.end(),
-				[=](const ElementInfoUnderPointer& x) { return x.element == element; });
-
-			if (!elementInfo.receivedMouseOver)
-				continue;
-
-			if (iterFind == mNewElementsUnderPointer.end() || !iterFind->isHovering)
-			{
-				auto iterFind2 = std::find_if(mActiveElements.begin(), mActiveElements.end(), 
-					[=](const ElementInfo& x) { return x.element == element; });
-
-				// Send MouseOut event
-				if(mActiveElements.size() == 0 || iterFind2 != mActiveElements.end())
-				{
-					Vector2I localPos = getWidgetRelativePos(widget, pointerScreenPos);
-
-					mMouseEvent.setMouseOutData(localPos);
-					if (sendMouseEvent(element, mMouseEvent))
-					{
-						eventProcessed = true;
-						break;
-					}
-				}
-			}
-		}
-
-		mElementsUnderPointer.swap(mNewElementsUnderPointer);
-
-		// Tooltip
-		hideTooltip();
-		if (mElementsUnderPointer.size() > 0)
-			mShowTooltip = true;
-
-		mTooltipElementHoverStart = gTime().getTime();
-
-		return eventProcessed;
-	}
-
-	void GUIManager::onTextInput(const TextInputEvent& event)
-	{
-		mTextInputEvent = GUITextInputEvent();
-		mTextInputEvent.setData(event.textChar);
-
-		for(auto& elementInFocus : mElementsInFocus)
-		{
-			if(sendTextInputEvent(elementInFocus.element, mTextInputEvent))
-				event.markAsUsed();
-		}
-	}
-
-	void GUIManager::onWindowFocusGained(RenderWindow& win)
-	{
-		for(auto& widgetInfo : mWidgets)
-		{
-			GUIWidget* widget = widgetInfo.widget;
-			if(getWidgetWindow(*widget) == &win)
-				widget->ownerWindowFocusChanged();
-		}
-	}
-
-	void GUIManager::onWindowFocusLost(RenderWindow& win)
-	{
-		for(auto& widgetInfo : mWidgets)
-		{
-			GUIWidget* widget = widgetInfo.widget;
-			if(getWidgetWindow(*widget) == &win)
-				widget->ownerWindowFocusChanged();
-		}
-
-		mNewElementsInFocus.clear();
-		for(auto& focusedElement : mElementsInFocus)
-		{
-			if (focusedElement.element->_isDestroyed())
-				continue;
-
-			if (focusedElement.widget != nullptr && getWidgetWindow(*focusedElement.widget) == &win)
-			{
-				mCommandEvent = GUICommandEvent();
-				mCommandEvent.setType(GUICommandEventType::FocusLost);
-
-				sendCommandEvent(focusedElement.element, mCommandEvent);
-			}
-			else
-				mNewElementsInFocus.push_back(focusedElement);
-		}
-
-		mElementsInFocus.swap(mNewElementsInFocus);
-	}
-
-	// We stop getting mouse move events once it leaves the window, so make sure
-	// nothing stays in hover state
-	void GUIManager::onMouseLeftWindow(RenderWindow& win)
-	{
-		bool buttonStates[3];
-		buttonStates[0] = false;
-		buttonStates[1] = false;
-		buttonStates[2] = false;
-
-		mNewElementsUnderPointer.clear();
-
-		for(auto& elementInfo : mElementsUnderPointer)
-		{
-			GUIElement* element = elementInfo.element;
-			GUIWidget* widget = elementInfo.widget;
-
-			if (widget != nullptr && widget->getTarget()->getTarget().get() != &win)
-			{
-				mNewElementsUnderPointer.push_back(elementInfo);
-				continue;
-			}
-
-			auto iterFind = std::find_if(mActiveElements.begin(), mActiveElements.end(), 
-				[&](const ElementInfo& x) { return x.element == element; });
-
-			// Send MouseOut event
-			if(mActiveElements.size() == 0 || iterFind != mActiveElements.end())
-			{
-				Vector2I localPos = getWidgetRelativePos(widget, Vector2I());
-
-				mMouseEvent.setMouseOutData(localPos);
-				sendMouseEvent(element, mMouseEvent);
-			}
-		}
-
-		mElementsUnderPointer.swap(mNewElementsUnderPointer);
-
-		hideTooltip();
-		if(mDragState != DragState::Dragging)
-		{
-			if(mActiveCursor != CursorType::Arrow)
-			{
-				Cursor::instance().setCursor(CursorType::Arrow);
-				mActiveCursor = CursorType::Arrow;
-			}
-		}
-	}
-	
-	void GUIManager::hideTooltip()
-	{
-		GUITooltipManager::instance().hide();
-		mShowTooltip = false;
-	}
-
-	void GUIManager::queueForDestroy(GUIElement* element)
-	{
-		mScheduledForDestruction.push(element);
-	}
-
-	void GUIManager::setFocus(GUIElement* element, bool focus)
-	{
-		ElementFocusInfo efi;
-		efi.element = element;
-		efi.focus = focus;
-
-		mForcedFocusElements.push_back(efi);
-	}
-
-	bool GUIManager::processDestroyQueue()
-	{
-		Stack<GUIElement*> toDestroy = mScheduledForDestruction;
-		mScheduledForDestruction = Stack<GUIElement*>();
-
-		while (!toDestroy.empty())
-		{
-			bs_delete(toDestroy.top());
-			toDestroy.pop();
-		}
-
-		return !mScheduledForDestruction.empty();
-	}
-
-	void GUIManager::setInputBridge(const RenderTexture* renderTex, const GUIElement* element)
-	{
-		if(element == nullptr)
-			mInputBridge.erase(renderTex);
-		else
-			mInputBridge[renderTex] = element;
-	}
-
-	GUIMouseButton GUIManager::buttonToGUIButton(PointerEventButton pointerButton) const
-	{
-		if(pointerButton == PointerEventButton::Left)
-			return GUIMouseButton::Left;
-		else if(pointerButton == PointerEventButton::Middle)
-			return GUIMouseButton::Middle;
-		else if(pointerButton == PointerEventButton::Right)
-			return GUIMouseButton::Right;
-
-		BS_EXCEPT(InvalidParametersException, "Provided button is not a GUI supported mouse button.");
-		return GUIMouseButton::Left;
-	}
-
-	Vector2I GUIManager::getWidgetRelativePos(const GUIWidget* widget, const Vector2I& screenPos) const
-	{
-		if (widget == nullptr)
-			return screenPos;
-
-		const RenderWindow* window = getWidgetWindow(*widget);
-		if(window == nullptr)
-			return Vector2I();
-
-		Vector2I windowPos = window->screenToWindowPos(screenPos);
-		windowPos = windowToBridgedCoords(*widget, windowPos);
-
-		const Matrix4& worldTfrm = widget->getWorldTfrm();
-
-		Vector4 vecLocalPos = worldTfrm.inverse().multiplyAffine(Vector4((float)windowPos.x, (float)windowPos.y, 0.0f, 1.0f));
-		Vector2I curLocalPos(Math::roundToInt(vecLocalPos.x), Math::roundToInt(vecLocalPos.y));
-
-		return curLocalPos;
-	}
-
-	Vector2I GUIManager::windowToBridgedCoords(const GUIWidget& widget, const Vector2I& windowPos) const
-	{
-		// This cast might not be valid (the render target could be a window), but we only really need to cast
-		// so that mInputBridge map allows us to search through it - we don't access anything unless the target is bridged
-		// (in which case we know it is a RenderTexture)
-		const RenderTexture* renderTexture = static_cast<const RenderTexture*>(widget.getTarget()->getTarget().get());
-		const RenderTargetProperties& rtProps = renderTexture->getProperties();
-
-		auto iterFind = mInputBridge.find(renderTexture);
-		if(iterFind != mInputBridge.end()) // Widget input is bridged, which means we need to transform the coordinates
-		{
-			const GUIElement* bridgeElement = iterFind->second;
-			const GUIWidget* parentWidget = bridgeElement->_getParentWidget();
-			if (parentWidget == nullptr)
-				return windowPos;
-
-			const Matrix4& worldTfrm = parentWidget->getWorldTfrm();
-
-			Vector4 vecLocalPos = worldTfrm.inverse().multiplyAffine(Vector4((float)windowPos.x, (float)windowPos.y, 0.0f, 1.0f));
-			Rect2I bridgeBounds = bridgeElement->_getLayoutData().area;
-
-			// Find coordinates relative to the bridge element
-			float x = vecLocalPos.x - (float)bridgeBounds.x;
-			float y = vecLocalPos.y - (float)bridgeBounds.y;
-
-			float scaleX = rtProps.getWidth() / (float)bridgeBounds.width;
-			float scaleY = rtProps.getHeight() / (float)bridgeBounds.height;
-
-			return Vector2I(Math::roundToInt(x * scaleX), Math::roundToInt(y * scaleY));
-		}
-
-		return windowPos;
-	}
-
-	const RenderWindow* GUIManager::getWidgetWindow(const GUIWidget& widget) const
-	{
-		// This cast might not be valid (the render target could be a window), but we only really need to cast
-		// so that mInputBridge map allows us to search through it - we don't access anything unless the target is bridged
-		// (in which case we know it is a RenderTexture)
-
-		const Viewport* viewport = widget.getTarget();
-		if (viewport == nullptr)
-			return nullptr;
-
-		RenderTargetPtr target = viewport->getTarget();
-		if (target == nullptr)
-			return nullptr;
-
-		const RenderTexture* renderTexture = static_cast<const RenderTexture*>(target.get());
-
-		auto iterFind = mInputBridge.find(renderTexture);
-		if(iterFind != mInputBridge.end())
-		{
-			GUIWidget* parentWidget = iterFind->second->_getParentWidget();
-			if (parentWidget == nullptr)
-				return nullptr;
-
-			if(parentWidget != &widget)
-				return getWidgetWindow(*parentWidget);
-		}
-
-		Vector<RenderWindow*> renderWindows = RenderWindowManager::instance().getRenderWindows();
-
-		auto iterFindWin = std::find(renderWindows.begin(), renderWindows.end(), target.get());
-		if(iterFindWin != renderWindows.end())
-			return static_cast<RenderWindow*>(target.get());
-
-		return nullptr;
-	}
-
-	bool GUIManager::sendMouseEvent(GUIElement* element, const GUIMouseEvent& event)
-	{
-		if (element->_isDestroyed())
-			return false;
-
-		return element->_mouseEvent(event);
-	}
-
-	bool GUIManager::sendTextInputEvent(GUIElement* element, const GUITextInputEvent& event)
-	{
-		if (element->_isDestroyed())
-			return false;
-
-		return element->_textInputEvent(event);
-	}
-
-	bool GUIManager::sendCommandEvent(GUIElement* element, const GUICommandEvent& event)
-	{
-		if (element->_isDestroyed())
-			return false;
-
-		return element->_commandEvent(event);
-	}
-
-	bool GUIManager::sendVirtualButtonEvent(GUIElement* element, const GUIVirtualButtonEvent& event)
-	{
-		if (element->_isDestroyed())
-			return false;
-
-		return element->_virtualButtonEvent(event);
-	}
-
-	GUIManager& gGUIManager()
-	{
-		return GUIManager::instance();
-	}
-
-	GUIManagerCore::~GUIManagerCore()
-	{
-		CoreRendererPtr activeRenderer = RendererManager::instance().getActive();
-		for (auto& cameraData : mPerCameraData)
-			activeRenderer->_unregisterRenderCallback(cameraData.first.get(), 30);
-	}
-
-	void GUIManagerCore::initialize(const SPtr<MaterialCore>& textMat, const SPtr<MaterialCore>& imageMat,
-		const SPtr<MaterialCore>& imageAlphaMat)
-	{
-		mTextMaterialInfo = MaterialInfo(textMat);
-		mImageMaterialInfo = MaterialInfo(imageMat);
-		mImageAlphaMaterialInfo = MaterialInfo(imageAlphaMat);
-
-		SAMPLER_STATE_DESC ssDesc;
-		ssDesc.magFilter = FO_POINT;
-		ssDesc.minFilter = FO_POINT;
-		ssDesc.mipFilter = FO_POINT;
-
-		mSamplerState = RenderStateCoreManager::instance().createSamplerState(ssDesc);
-	}
-
-	void GUIManagerCore::updateData(const UnorderedMap<SPtr<CameraCore>, Vector<GUIManager::GUICoreRenderData>>& newPerCameraData)
-	{
-		bs_frame_mark();
-
-		{
-			FrameSet<SPtr<CameraCore>> validCameras;
-
-			CoreRendererPtr activeRenderer = RendererManager::instance().getActive();
-			for (auto& newCameraData : newPerCameraData)
-			{
-				UINT32 idx = 0;
-				Vector<GUIManager::GUICoreRenderData>* renderData = nullptr;
-				for (auto& oldCameraData : mPerCameraData)
-				{
-					if (newCameraData.first == oldCameraData.first)
-					{
-						renderData = &oldCameraData.second;
-						validCameras.insert(oldCameraData.first);
-						break;
-					}
-
-					idx++;
-				}
-
-				if (renderData == nullptr)
-				{
-					SPtr<CameraCore> camera = newCameraData.first;
-
-					auto insertedData = mPerCameraData.insert(std::make_pair(newCameraData.first, Vector<GUIManager::GUICoreRenderData>()));
-					renderData = &insertedData.first->second;
-
-					activeRenderer->_registerRenderCallback(camera.get(), 30, std::bind(&GUIManagerCore::render, this, camera), true);
-					validCameras.insert(camera);
-				}
-
-				*renderData = newCameraData.second;
-			}
-
-			FrameVector<SPtr<CameraCore>> cameraToRemove;
-			for (auto& cameraData : mPerCameraData)
-			{
-				auto iterFind = validCameras.find(cameraData.first);
-				if (iterFind == validCameras.end())
-					cameraToRemove.push_back(cameraData.first);
-			}
-
-			for (auto& camera : cameraToRemove)
-			{
-				activeRenderer->_unregisterRenderCallback(camera.get(), 30);
-				mPerCameraData.erase(camera);
-			}
-		}
-
-		bs_frame_clear();
-	}
-
-	void GUIManagerCore::render(const SPtr<CameraCore>& camera)
-	{
-		Vector<GUIManager::GUICoreRenderData>& renderData = mPerCameraData[camera];
-
-		float invViewportWidth = 1.0f / (camera->getViewport()->getWidth() * 0.5f);
-		float invViewportHeight = 1.0f / (camera->getViewport()->getHeight() * 0.5f);
-		for (auto& entry : renderData)
-		{
-			MaterialInfo& matInfo = entry.materialType == SpriteMaterial::Text ? mTextMaterialInfo :
-				(entry.materialType == SpriteMaterial::Image ? mImageMaterialInfo : mImageAlphaMaterialInfo);
-
-			matInfo.textureParam.set(entry.texture);
-			matInfo.samplerParam.set(mSamplerState);
-			matInfo.tintParam.set(entry.tint);
-			matInfo.invViewportWidthParam.set(invViewportWidth);
-			matInfo.invViewportHeightParam.set(invViewportHeight);
-			matInfo.worldTransformParam.set(entry.worldTransform);
-
-			// TODO - I shouldn't be re-applying the entire material for each entry, instead just check which programs
-			// changed, and apply only those + the modified constant buffers and/or texture.
-
-			gRendererUtility().setPass(matInfo.material, 0);
-			gRendererUtility().draw(entry.mesh, entry.mesh->getProperties().getSubMesh(0));
-		}
-	}
-
-	GUIManagerCore::MaterialInfo::MaterialInfo(const SPtr<MaterialCore>& material)
-		:material(material)
-	{
-		textureParam = material->getParamTexture("mainTexture");
-		samplerParam = material->getParamSamplerState("mainTexSamp");
-		tintParam = material->getParamColor("tint");
-		invViewportWidthParam = material->getParamFloat("invViewportWidth");
-		invViewportHeightParam = material->getParamFloat("invViewportHeight");
-		worldTransformParam = material->getParamMat4("worldTransform");
-	}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsGUIManager.h"
+#include "BsGUIWidget.h"
+#include "BsGUIElement.h"
+#include "BsSpriteTexture.h"
+#include "BsTime.h"
+#include "BsSceneObject.h"
+#include "BsMaterial.h"
+#include "BsMeshData.h"
+#include "BsVertexDataDesc.h"
+#include "BsMesh.h"
+#include "BsRenderWindowManager.h"
+#include "BsPlatform.h"
+#include "BsRect2I.h"
+#include "BsCoreApplication.h"
+#include "BsException.h"
+#include "BsInput.h"
+#include "BsGUIInputCaret.h"
+#include "BsGUIInputSelection.h"
+#include "BsGUIContextMenu.h"
+#include "BsDragAndDropManager.h"
+#include "BsGUIDropDownBoxManager.h"
+#include "BsProfilerCPU.h"
+#include "BsMeshHeap.h"
+#include "BsTransientMesh.h"
+#include "BsVirtualInput.h"
+#include "BsCursor.h"
+#include "BsCoreThread.h"
+#include "BsRendererManager.h"
+#include "BsRenderer.h"
+#include "BsCamera.h"
+#include "BsGUITooltipManager.h"
+#include "BsRendererUtility.h"
+#include "BsTexture.h"
+#include "BsRenderTexture.h"
+#include "BsSamplerState.h"
+#include "BsRenderStateManager.h"
+#include "BsBuiltinResources.h"
+
+using namespace std::placeholders;
+
+namespace BansheeEngine
+{
+	struct GUIGroupElement
+	{
+		GUIGroupElement()
+		{ }
+
+		GUIGroupElement(GUIElement* _element, UINT32 _renderElement)
+			:element(_element), renderElement(_renderElement)
+		{ }
+
+		GUIElement* element;
+		UINT32 renderElement;
+	};
+
+	struct GUIMaterialGroup
+	{
+		SpriteMaterialInfo matInfo;
+		UINT32 numQuads;
+		UINT32 depth;
+		UINT32 minDepth;
+		Rect2I bounds;
+		Vector<GUIGroupElement> elements;
+	};
+
+	const UINT32 GUIManager::DRAG_DISTANCE = 3;
+	const float GUIManager::TOOLTIP_HOVER_TIME = 1.0f;
+	const UINT32 GUIManager::MESH_HEAP_INITIAL_NUM_VERTS = 16384;
+	const UINT32 GUIManager::MESH_HEAP_INITIAL_NUM_INDICES = 49152;
+
+	GUIManager::GUIManager()
+		:mSeparateMeshesByWidget(true), mActiveMouseButton(GUIMouseButton::Left),
+		mCaretBlinkInterval(0.5f), mCaretLastBlinkTime(0.0f), mCaretColor(1.0f, 0.6588f, 0.0f), mIsCaretOn(false),
+		mTextSelectionColor(0.0f, 114/255.0f, 188/255.0f), mInputCaret(nullptr), mInputSelection(nullptr), 
+		mDragState(DragState::NoDrag), mActiveCursor(CursorType::Arrow), mCoreDirty(false), mShowTooltip(false), mTooltipElementHoverStart(0.0f)
+	{
+		mOnPointerMovedConn = gInput().onPointerMoved.connect(std::bind(&GUIManager::onPointerMoved, this, _1));
+		mOnPointerPressedConn = gInput().onPointerPressed.connect(std::bind(&GUIManager::onPointerPressed, this, _1));
+		mOnPointerReleasedConn = gInput().onPointerReleased.connect(std::bind(&GUIManager::onPointerReleased, this, _1));
+		mOnPointerDoubleClick = gInput().onPointerDoubleClick.connect(std::bind(&GUIManager::onPointerDoubleClick, this, _1));
+		mOnTextInputConn = gInput().onCharInput.connect(std::bind(&GUIManager::onTextInput, this, _1)); 
+		mOnInputCommandConn = gInput().onInputCommand.connect(std::bind(&GUIManager::onInputCommandEntered, this, _1)); 
+		mOnVirtualButtonDown = VirtualInput::instance().onButtonDown.connect(std::bind(&GUIManager::onVirtualButtonDown, this, _1, _2));
+
+		mWindowGainedFocusConn = RenderWindowManager::instance().onFocusGained.connect(std::bind(&GUIManager::onWindowFocusGained, this, _1));
+		mWindowLostFocusConn = RenderWindowManager::instance().onFocusLost.connect(std::bind(&GUIManager::onWindowFocusLost, this, _1));
+		mMouseLeftWindowConn = RenderWindowManager::instance().onMouseLeftWindow.connect(std::bind(&GUIManager::onMouseLeftWindow, this, _1));
+
+		mInputCaret = bs_new<GUIInputCaret>();
+		mInputSelection = bs_new<GUIInputSelection>();
+
+		DragAndDropManager::startUp();
+		mDragEndedConn = DragAndDropManager::instance().onDragEnded.connect(std::bind(&GUIManager::onMouseDragEnded, this, _1, _2));
+
+		GUIDropDownBoxManager::startUp();
+		GUITooltipManager::startUp();
+
+		mVertexDesc = bs_shared_ptr_new<VertexDataDesc>();
+		mVertexDesc->addVertElem(VET_FLOAT2, VES_POSITION);
+		mVertexDesc->addVertElem(VET_FLOAT2, VES_TEXCOORD);
+
+		mMeshHeap = MeshHeap::create(MESH_HEAP_INITIAL_NUM_VERTS, MESH_HEAP_INITIAL_NUM_INDICES, mVertexDesc);
+
+		// Need to defer this call because I want to make sure all managers are initialized first
+		deferredCall(std::bind(&GUIManager::updateCaretTexture, this));
+		deferredCall(std::bind(&GUIManager::updateTextSelectionTexture, this));
+
+		GUIManagerCore* core = bs_new<GUIManagerCore>();
+		mCore.store(core, std::memory_order_release);
+
+		HMaterial textMaterial = BuiltinResources::instance().createSpriteTextMaterial();
+		HMaterial imageMaterial = BuiltinResources::instance().createSpriteNonAlphaImageMaterial();
+		HMaterial imageAlphaMaterial = BuiltinResources::instance().createSpriteImageMaterial();
+
+		gCoreAccessor().queueCommand(std::bind(&GUIManagerCore::initialize, core,
+			textMaterial->getCore(), imageMaterial->getCore(), imageAlphaMaterial->getCore()));
+	}
+
+	GUIManager::~GUIManager()
+	{
+		GUITooltipManager::shutDown();
+		GUIDropDownBoxManager::shutDown();
+		DragAndDropManager::shutDown();
+
+		// Make a copy of widgets, since destroying them will remove them from mWidgets and
+		// we can't iterate over an array thats getting modified
+		Vector<WidgetInfo> widgetCopy = mWidgets;
+		for(auto& widget : widgetCopy)
+			widget.widget->_destroy();
+
+		// Ensure everything queued get destroyed, loop until queue empties
+		while (processDestroyQueue())
+		{ }
+
+		mOnPointerPressedConn.disconnect();
+		mOnPointerReleasedConn.disconnect();
+		mOnPointerMovedConn.disconnect();
+		mOnPointerDoubleClick.disconnect();
+		mOnTextInputConn.disconnect();
+		mOnInputCommandConn.disconnect();
+		mOnVirtualButtonDown.disconnect();
+
+		mDragEndedConn.disconnect();
+
+		mWindowGainedFocusConn.disconnect();
+		mWindowLostFocusConn.disconnect();
+
+		mMouseLeftWindowConn.disconnect();
+
+		bs_delete(mInputCaret);
+		bs_delete(mInputSelection);
+
+		gCoreAccessor().queueCommand(std::bind(&GUIManager::destroyCore, this, mCore.load(std::memory_order_relaxed)));
+
+		assert(mCachedGUIData.size() == 0);
+	}
+
+	void GUIManager::destroyCore(GUIManagerCore* core)
+	{
+		bs_delete(core);
+	}
+
+	void GUIManager::registerWidget(GUIWidget* widget)
+	{
+		const Viewport* renderTarget = widget->getTarget();
+		if (renderTarget == nullptr)
+			return;
+
+		mWidgets.push_back(WidgetInfo(widget));
+		auto findIter = mCachedGUIData.find(renderTarget);
+
+		if(findIter == end(mCachedGUIData))
+			mCachedGUIData[renderTarget] = GUIRenderData();
+
+		GUIRenderData& windowData = mCachedGUIData[renderTarget];
+		windowData.widgets.push_back(widget);
+		windowData.isDirty = true;
+	}
+
+	void GUIManager::unregisterWidget(GUIWidget* widget)
+	{
+		{
+			auto findIter = std::find_if(begin(mWidgets), end(mWidgets), [=] (const WidgetInfo& x) { return x.widget == widget; } );
+
+			if(findIter != mWidgets.end())
+				mWidgets.erase(findIter);
+		}
+
+		for(auto& entry : mElementsInFocus)
+		{
+			if (entry.widget == widget)
+				entry.widget = nullptr;
+		}
+
+		for (auto& entry : mElementsUnderPointer)
+		{
+			if (entry.widget == widget)
+				entry.widget = nullptr;
+		}
+
+		for (auto& entry : mActiveElements)
+		{
+			if (entry.widget == widget)
+				entry.widget = nullptr;
+		}
+
+		const Viewport* renderTarget = widget->getTarget();
+		GUIRenderData& renderData = mCachedGUIData[renderTarget];
+
+		{
+			auto findIter = std::find(begin(renderData.widgets), end(renderData.widgets), widget);
+			
+			if(findIter != end(renderData.widgets))
+				renderData.widgets.erase(findIter);
+		}
+
+		if(renderData.widgets.size() == 0)
+		{
+			for (auto& mesh : renderData.cachedMeshes)
+			{
+				if (mesh != nullptr)
+					mMeshHeap->dealloc(mesh);
+			}
+
+			mCachedGUIData.erase(renderTarget);
+			mCoreDirty = true;
+		}
+		else
+			renderData.isDirty = true;
+	}
+
+	void GUIManager::update()
+	{
+		DragAndDropManager::instance()._update();
+
+		// Show tooltip if needed
+		if (mShowTooltip)
+		{
+			float diff = gTime().getTime() - mTooltipElementHoverStart;
+			if (diff >= TOOLTIP_HOVER_TIME || gInput().isButtonHeld(BC_LCONTROL) || gInput().isButtonHeld(BC_RCONTROL))
+			{
+				for(auto& entry : mElementsUnderPointer)
+				{
+					const WString& tooltipText = entry.element->_getTooltip();
+					GUIWidget* parentWidget = entry.element->_getParentWidget();
+
+					if (!tooltipText.empty() && parentWidget != nullptr)
+					{
+						const RenderWindow* window = getWidgetWindow(*parentWidget);
+						if (window != nullptr)
+						{
+							Vector2I windowPos = window->screenToWindowPos(gInput().getPointerPosition());
+
+							GUITooltipManager::instance().show(*parentWidget, windowPos, tooltipText);
+							break;
+						}
+					}
+				}
+
+				mShowTooltip = false;
+			}
+		}
+
+		// Update layouts
+		gProfilerCPU().beginSample("UpdateLayout");
+		for(auto& widgetInfo : mWidgets)
+		{
+			widgetInfo.widget->_updateLayout();
+		}
+		gProfilerCPU().endSample("UpdateLayout");
+
+		// Destroy all queued elements (and loop in case any new ones get queued during destruction)
+		do
+		{
+			mNewElementsUnderPointer.clear();
+			for (auto& elementInfo : mElementsUnderPointer)
+			{
+				if (!elementInfo.element->_isDestroyed())
+					mNewElementsUnderPointer.push_back(elementInfo);
+			}
+
+			mElementsUnderPointer.swap(mNewElementsUnderPointer);
+
+			mNewActiveElements.clear();
+			for (auto& elementInfo : mActiveElements)
+			{
+				if (!elementInfo.element->_isDestroyed())
+					mNewActiveElements.push_back(elementInfo);
+			}
+
+			mActiveElements.swap(mNewActiveElements);
+
+			mNewElementsInFocus.clear();
+			for (auto& elementInfo : mElementsInFocus)
+			{
+				if (!elementInfo.element->_isDestroyed())
+					mNewElementsInFocus.push_back(elementInfo);
+			}
+
+			mElementsInFocus.swap(mNewElementsInFocus);
+
+			for (auto& focusElementInfo : mForcedFocusElements)
+			{
+				if (focusElementInfo.element->_isDestroyed())
+					continue;
+
+				if (focusElementInfo.focus)
+				{
+					auto iterFind = std::find_if(mElementsInFocus.begin(), mElementsInFocus.end(),
+						[&](const ElementFocusInfo& x) { return x.element == focusElementInfo.element; });
+
+					if (iterFind == mElementsInFocus.end())
+					{
+						mElementsInFocus.push_back(ElementFocusInfo(focusElementInfo.element, 
+							focusElementInfo.element->_getParentWidget(), false));
+
+						mCommandEvent = GUICommandEvent();
+						mCommandEvent.setType(GUICommandEventType::FocusGained);
+
+						sendCommandEvent(focusElementInfo.element, mCommandEvent);
+					}
+				}
+				else
+				{
+					mNewElementsInFocus.clear();
+					for (auto& elementInfo : mElementsInFocus)
+					{
+						if (elementInfo.element == focusElementInfo.element)
+						{
+							mCommandEvent = GUICommandEvent();
+							mCommandEvent.setType(GUICommandEventType::FocusLost);
+
+							sendCommandEvent(elementInfo.element, mCommandEvent);
+						}
+						else
+							mNewElementsInFocus.push_back(elementInfo);
+					}
+
+					mElementsInFocus.swap(mNewElementsInFocus);
+				}
+			}
+
+			mForcedFocusElements.clear();
+
+		} while (processDestroyQueue());
+
+		// Blink caret
+		float curTime = gTime().getTime();
+
+		if ((curTime - mCaretLastBlinkTime) >= mCaretBlinkInterval)
+		{
+			mCaretLastBlinkTime = curTime;
+			mIsCaretOn = !mIsCaretOn;
+
+			mCommandEvent = GUICommandEvent();
+			mCommandEvent.setType(GUICommandEventType::Redraw);
+
+			for (auto& elementInfo : mElementsInFocus)
+			{
+				sendCommandEvent(elementInfo.element, mCommandEvent);
+			}
+		}
+
+		PROFILE_CALL(updateMeshes(), "UpdateMeshes");
+
+		// Send potentially updated meshes to core for rendering
+		if (mCoreDirty)
+		{
+			UnorderedMap<SPtr<CameraCore>, Vector<GUICoreRenderData>> corePerCameraData;
+
+			for (auto& viewportData : mCachedGUIData)
+			{
+				const GUIRenderData& renderData = viewportData.second;
+
+				SPtr<Camera> camera;
+				for (auto& widget : viewportData.second.widgets)
+				{
+					camera = widget->getCamera();
+					if (camera != nullptr)
+						break;
+				}
+
+				if (camera == nullptr)
+					continue;
+
+				auto insertedData = corePerCameraData.insert(std::make_pair(camera->getCore(), Vector<GUICoreRenderData>()));
+				Vector<GUICoreRenderData>& cameraData = insertedData.first->second;
+
+				UINT32 meshIdx = 0;
+				for (auto& mesh : renderData.cachedMeshes)
+				{
+					SpriteMaterialInfo materialInfo = renderData.cachedMaterials[meshIdx];
+					GUIWidget* widget = renderData.cachedWidgetsPerMesh[meshIdx];
+
+					if (materialInfo.texture == nullptr || !materialInfo.texture.isLoaded())
+					{
+						meshIdx++;
+						continue;
+					}
+
+					if (mesh == nullptr)
+					{
+						meshIdx++;
+						continue;
+					}
+
+					cameraData.push_back(GUICoreRenderData());
+					GUICoreRenderData& newEntry = cameraData.back();
+
+					newEntry.materialType = materialInfo.type;
+					newEntry.texture = materialInfo.texture->getCore();
+					newEntry.tint = materialInfo.tint;
+					newEntry.mesh = mesh->getCore();
+					newEntry.worldTransform = widget->getWorldTfrm();
+
+					meshIdx++;
+				}
+			}
+
+			GUIManagerCore* core = mCore.load(std::memory_order_relaxed);
+			gCoreAccessor().queueCommand(std::bind(&GUIManagerCore::updateData, core, corePerCameraData));
+
+			mCoreDirty = false;
+		}
+	}
+
+	void GUIManager::updateMeshes()
+	{
+		for(auto& cachedMeshData : mCachedGUIData)
+		{
+			GUIRenderData& renderData = cachedMeshData.second;
+
+			// Check if anything is dirty. If nothing is we can skip the update
+			bool isDirty = renderData.isDirty;
+			renderData.isDirty = false;
+
+			for(auto& widget : renderData.widgets)
+			{
+				if (widget->isDirty(true))
+				{
+					isDirty = true;
+				}
+			}
+
+			if(!isDirty)
+				continue;
+
+			mCoreDirty = true;
+
+			bs_frame_mark();
+			{
+				// Make a list of all GUI elements, sorted from farthest to nearest (highest depth to lowest)
+				auto elemComp = [](const GUIGroupElement& a, const GUIGroupElement& b)
+				{
+					UINT32 aDepth = a.element->_getRenderElementDepth(a.renderElement);
+					UINT32 bDepth = b.element->_getRenderElementDepth(b.renderElement);
+
+					// Compare pointers just to differentiate between two elements with the same depth, their order doesn't really matter, but std::set
+					// requires all elements to be unique
+					return (aDepth > bDepth) || 
+						(aDepth == bDepth && a.element > b.element) || 
+						(aDepth == bDepth && a.element == b.element && a.renderElement > b.renderElement); 
+				};
+
+				FrameSet<GUIGroupElement, std::function<bool(const GUIGroupElement&, const GUIGroupElement&)>> allElements(elemComp);
+
+				for (auto& widget : renderData.widgets)
+				{
+					const Vector<GUIElement*>& elements = widget->getElements();
+
+					for (auto& element : elements)
+					{
+						if (!element->_isVisible())
+							continue;
+
+						UINT32 numRenderElems = element->_getNumRenderElements();
+						for (UINT32 i = 0; i < numRenderElems; i++)
+						{
+							allElements.insert(GUIGroupElement(element, i));
+						}
+					}
+				}
+
+				// Group the elements in such a way so that we end up with a smallest amount of
+				// meshes, without breaking back to front rendering order
+				FrameUnorderedMap<std::reference_wrapper<const SpriteMaterialInfo>, FrameVector<GUIMaterialGroup>> materialGroups;
+				for (auto& elem : allElements)
+				{
+					GUIElement* guiElem = elem.element;
+					UINT32 renderElemIdx = elem.renderElement;
+					UINT32 elemDepth = guiElem->_getRenderElementDepth(renderElemIdx);
+
+					Rect2I tfrmedBounds = guiElem->_getClippedBounds();
+					tfrmedBounds.transform(guiElem->_getParentWidget()->getWorldTfrm());
+
+					const SpriteMaterialInfo& matInfo = guiElem->_getMaterial(renderElemIdx);
+					FrameVector<GUIMaterialGroup>& groupsPerMaterial = materialGroups[std::cref(matInfo)];
+					
+					// Try to find a group this material will fit in:
+					//  - Group that has a depth value same or one below elements depth will always be a match
+					//  - Otherwise, we search higher depth values as well, but we only use them if no elements in between those depth values
+					//    overlap the current elements bounds.
+					GUIMaterialGroup* foundGroup = nullptr;
+
+					for (auto groupIter = groupsPerMaterial.rbegin(); groupIter != groupsPerMaterial.rend(); ++groupIter)
+					{
+						// If we separate meshes by widget, ignore any groups with widget parents other than mine
+						if (mSeparateMeshesByWidget)
+						{
+							if (groupIter->elements.size() > 0)
+							{
+								GUIElement* otherElem = groupIter->elements.begin()->element; // We only need to check the first element
+								if (otherElem->_getParentWidget() != guiElem->_getParentWidget())
+									continue;
+							}
+						}
+
+						GUIMaterialGroup& group = *groupIter;
+
+						if (group.depth == elemDepth)
+						{
+							foundGroup = &group;
+							break;
+						}
+						else
+						{
+							UINT32 startDepth = elemDepth;
+							UINT32 endDepth = group.depth;
+
+							Rect2I potentialGroupBounds = group.bounds;
+							potentialGroupBounds.encapsulate(tfrmedBounds);
+
+							bool foundOverlap = false;
+							for (auto& material : materialGroups)
+							{
+								for (auto& matGroup : material.second)
+								{
+									if (&matGroup == &group)
+										continue;
+
+									if ((matGroup.minDepth >= startDepth && matGroup.minDepth <= endDepth)
+										|| (matGroup.depth >= startDepth && matGroup.depth <= endDepth))
+									{
+										if (matGroup.bounds.overlaps(potentialGroupBounds))
+										{
+											foundOverlap = true;
+											break;
+										}
+									}
+								}
+							}
+
+							if (!foundOverlap)
+							{
+								foundGroup = &group;
+								break;
+							}
+						}
+					}
+
+					if (foundGroup == nullptr)
+					{
+						groupsPerMaterial.push_back(GUIMaterialGroup());
+						foundGroup = &groupsPerMaterial[groupsPerMaterial.size() - 1];
+
+						foundGroup->depth = elemDepth;
+						foundGroup->minDepth = elemDepth;
+						foundGroup->bounds = tfrmedBounds;
+						foundGroup->elements.push_back(GUIGroupElement(guiElem, renderElemIdx));
+						foundGroup->matInfo = matInfo;
+						foundGroup->numQuads = guiElem->_getNumQuads(renderElemIdx);
+					}
+					else
+					{
+						foundGroup->bounds.encapsulate(tfrmedBounds);
+						foundGroup->elements.push_back(GUIGroupElement(guiElem, renderElemIdx));
+						foundGroup->minDepth = std::min(foundGroup->minDepth, elemDepth);
+						foundGroup->numQuads += guiElem->_getNumQuads(renderElemIdx);
+					}
+				}
+
+				// Make a list of all GUI elements, sorted from farthest to nearest (highest depth to lowest)
+				auto groupComp = [](GUIMaterialGroup* a, GUIMaterialGroup* b)
+				{
+					return (a->depth > b->depth) || (a->depth == b->depth && a > b);
+					// Compare pointers just to differentiate between two elements with the same depth, their order doesn't really matter, but std::set
+					// requires all elements to be unique
+				};
+
+				FrameSet<GUIMaterialGroup*, std::function<bool(GUIMaterialGroup*, GUIMaterialGroup*)>> sortedGroups(groupComp);
+				for(auto& material : materialGroups)
+				{
+					for(auto& group : material.second)
+					{
+						sortedGroups.insert(&group);
+					}
+				}
+
+				UINT32 numMeshes = (UINT32)sortedGroups.size();
+				UINT32 oldNumMeshes = (UINT32)renderData.cachedMeshes.size();
+
+				if(numMeshes < oldNumMeshes)
+				{
+					for (UINT32 i = numMeshes; i < oldNumMeshes; i++)
+						mMeshHeap->dealloc(renderData.cachedMeshes[i]);
+
+					renderData.cachedMeshes.resize(numMeshes);
+				}
+
+				renderData.cachedMaterials.resize(numMeshes);
+
+				if(mSeparateMeshesByWidget)
+					renderData.cachedWidgetsPerMesh.resize(numMeshes);
+
+				// Fill buffers for each group and update their meshes
+				UINT32 groupIdx = 0;
+				for(auto& group : sortedGroups)
+				{
+					renderData.cachedMaterials[groupIdx] = group->matInfo;
+
+					if(mSeparateMeshesByWidget)
+					{
+						if(group->elements.size() == 0)
+							renderData.cachedWidgetsPerMesh[groupIdx] = nullptr;
+						else
+						{
+							GUIElement* elem = group->elements.begin()->element;
+							renderData.cachedWidgetsPerMesh[groupIdx] = elem->_getParentWidget();
+						}
+					}
+
+					MeshDataPtr meshData = bs_shared_ptr_new<MeshData>(group->numQuads * 4, group->numQuads * 6, mVertexDesc);
+
+					UINT8* vertices = meshData->getElementData(VES_POSITION);
+					UINT8* uvs = meshData->getElementData(VES_TEXCOORD);
+					UINT32* indices = meshData->getIndices32();
+					UINT32 vertexStride = meshData->getVertexDesc()->getVertexStride();
+					UINT32 indexStride = meshData->getIndexElementSize();
+
+					UINT32 quadOffset = 0;
+					for(auto& matElement : group->elements)
+					{
+						matElement.element->_fillBuffer(vertices, uvs, indices, quadOffset, group->numQuads, vertexStride, indexStride, matElement.renderElement);
+
+						UINT32 numQuads = matElement.element->_getNumQuads(matElement.renderElement);
+						UINT32 indexStart = quadOffset * 6;
+						UINT32 indexEnd = indexStart + numQuads * 6;
+						UINT32 vertOffset = quadOffset * 4;
+
+						for(UINT32 i = indexStart; i < indexEnd; i++)
+							indices[i] += vertOffset;
+
+						quadOffset += numQuads;
+					}
+
+					if(groupIdx < (UINT32)renderData.cachedMeshes.size())
+					{
+						mMeshHeap->dealloc(renderData.cachedMeshes[groupIdx]);
+						renderData.cachedMeshes[groupIdx] = mMeshHeap->alloc(meshData);
+					}
+					else
+					{
+						renderData.cachedMeshes.push_back(mMeshHeap->alloc(meshData));
+					}
+
+					groupIdx++;
+				}
+			}
+
+			bs_frame_clear();			
+		}
+	}
+
+	void GUIManager::updateCaretTexture()
+	{
+		if(mCaretTexture == nullptr)
+		{
+			HTexture newTex = Texture::create(TEX_TYPE_2D, 1, 1, 0, PF_R8G8B8A8);
+			mCaretTexture = SpriteTexture::create(newTex);
+		}
+
+		const HTexture& tex = mCaretTexture->getTexture();
+		UINT32 subresourceIdx = tex->getProperties().mapToSubresourceIdx(0, 0);
+		PixelDataPtr data = tex->getProperties().allocateSubresourceBuffer(subresourceIdx);
+
+		data->setColorAt(mCaretColor, 0, 0);
+		tex->writeSubresource(gCoreAccessor(), subresourceIdx, data, false);
+	}
+
+	void GUIManager::updateTextSelectionTexture()
+	{
+		if(mTextSelectionTexture == nullptr)
+		{
+			HTexture newTex = Texture::create(TEX_TYPE_2D, 1, 1, 0, PF_R8G8B8A8);
+			mTextSelectionTexture = SpriteTexture::create(newTex);
+		}
+
+		const HTexture& tex = mTextSelectionTexture->getTexture();
+		UINT32 subresourceIdx = tex->getProperties().mapToSubresourceIdx(0, 0);
+		PixelDataPtr data = tex->getProperties().allocateSubresourceBuffer(subresourceIdx);
+
+		data->setColorAt(mTextSelectionColor, 0, 0);
+
+		tex->writeSubresource(gCoreAccessor(), subresourceIdx, data, false);
+	}
+
+	void GUIManager::onMouseDragEnded(const PointerEvent& event, DragCallbackInfo& dragInfo)
+	{
+		GUIMouseButton guiButton = buttonToGUIButton(event.button);
+
+		if(DragAndDropManager::instance().isDragInProgress() && guiButton == GUIMouseButton::Left)
+		{
+			for(auto& elementInfo : mElementsUnderPointer)
+			{
+				Vector2I localPos;
+
+				if(elementInfo.widget != nullptr)
+					localPos = getWidgetRelativePos(elementInfo.widget, event.screenPos);
+
+				bool acceptDrop = true;
+				if(DragAndDropManager::instance().needsValidDropTarget())
+				{
+					acceptDrop = elementInfo.element->_acceptDragAndDrop(localPos, DragAndDropManager::instance().getDragTypeId());
+				}
+
+				if(acceptDrop)
+				{
+					mMouseEvent.setDragAndDropDroppedData(localPos, DragAndDropManager::instance().getDragTypeId(), DragAndDropManager::instance().getDragData());
+					dragInfo.processed = sendMouseEvent(elementInfo.element, mMouseEvent);
+
+					if(dragInfo.processed)
+						return;
+				}
+			}
+		}
+
+		dragInfo.processed = false;
+	}
+
+	void GUIManager::onPointerMoved(const PointerEvent& event)
+	{
+		if(event.isUsed())
+			return;
+
+		bool buttonStates[(int)GUIMouseButton::Count];
+		buttonStates[0] = event.buttonStates[0];
+		buttonStates[1] = event.buttonStates[1];
+		buttonStates[2] = event.buttonStates[2];
+
+		if(findElementUnderPointer(event.screenPos, buttonStates, event.shift, event.control, event.alt))
+			event.markAsUsed();
+
+		if(mDragState == DragState::HeldWithoutDrag)
+		{
+			UINT32 dist = mLastPointerClickPos.manhattanDist(event.screenPos);
+
+			if(dist > DRAG_DISTANCE)
+			{
+				for(auto& activeElement : mActiveElements)
+				{
+					Vector2I localPos = getWidgetRelativePos(activeElement.widget, event.screenPos);
+					Vector2I localDragStartPos = getWidgetRelativePos(activeElement.widget, mLastPointerClickPos);
+
+					mMouseEvent.setMouseDragStartData(localPos, localDragStartPos);
+					if(sendMouseEvent(activeElement.element, mMouseEvent))
+						event.markAsUsed();
+				}
+
+				mDragState = DragState::Dragging;
+				mDragStartPos = event.screenPos;
+			}
+		}
+
+		// If mouse is being held down send MouseDrag events
+		if(mDragState == DragState::Dragging)
+		{
+			for(auto& activeElement : mActiveElements)
+			{
+				if(mLastPointerScreenPos != event.screenPos)
+				{
+					Vector2I localPos = getWidgetRelativePos(activeElement.widget, event.screenPos);
+
+					mMouseEvent.setMouseDragData(localPos, event.screenPos - mDragStartPos);
+					if(sendMouseEvent(activeElement.element, mMouseEvent))
+						event.markAsUsed();
+				}
+			}
+
+			mLastPointerScreenPos = event.screenPos;
+
+			// Also if drag is in progress send DragAndDrop events
+			if(DragAndDropManager::instance().isDragInProgress())
+			{
+				bool acceptDrop = true;
+				for(auto& elementInfo : mElementsUnderPointer)
+				{
+					Vector2I localPos = getWidgetRelativePos(elementInfo.widget, event.screenPos);
+
+					acceptDrop = true;
+					if(DragAndDropManager::instance().needsValidDropTarget())
+					{
+						acceptDrop = elementInfo.element->_acceptDragAndDrop(localPos, DragAndDropManager::instance().getDragTypeId());
+					}
+
+					if(acceptDrop)
+					{
+						mMouseEvent.setDragAndDropDraggedData(localPos, DragAndDropManager::instance().getDragTypeId(), DragAndDropManager::instance().getDragData());
+						if(sendMouseEvent(elementInfo.element, mMouseEvent))
+						{
+							event.markAsUsed();
+							break;
+						}
+					}
+				}
+
+				if(acceptDrop)
+				{
+					if(mActiveCursor != CursorType::ArrowDrag)
+					{
+						Cursor::instance().setCursor(CursorType::ArrowDrag);
+						mActiveCursor = CursorType::ArrowDrag;
+					}
+				}
+				else
+				{
+					if(mActiveCursor != CursorType::Deny)
+					{
+						Cursor::instance().setCursor(CursorType::Deny);
+						mActiveCursor = CursorType::Deny;
+					}
+				}				
+			}
+		}
+		else // Otherwise, send MouseMove events if we are hovering over any element
+		{
+			if(mLastPointerScreenPos != event.screenPos)
+			{
+				bool moveProcessed = false;
+				bool hasCustomCursor = false;
+				for(auto& elementInfo : mElementsUnderPointer)
+				{
+					Vector2I localPos = getWidgetRelativePos(elementInfo.widget, event.screenPos);
+
+					if(!moveProcessed)
+					{
+						// Send MouseMove event
+						mMouseEvent.setMouseMoveData(localPos);
+						moveProcessed = sendMouseEvent(elementInfo.element, mMouseEvent);
+
+						if(moveProcessed)
+							event.markAsUsed();
+					}
+
+					if (mDragState == DragState::NoDrag)
+					{
+						CursorType newCursor = CursorType::Arrow;
+						if(elementInfo.element->_hasCustomCursor(localPos, newCursor))
+						{
+							if(newCursor != mActiveCursor)
+							{
+								Cursor::instance().setCursor(newCursor);
+								mActiveCursor = newCursor;
+							}
+
+							hasCustomCursor = true;
+						}
+					}
+
+					if(moveProcessed)
+						break;
+				}
+
+				// While dragging we don't want to modify the cursor
+				if (mDragState == DragState::NoDrag)
+				{
+					if (!hasCustomCursor)
+					{
+						if (mActiveCursor != CursorType::Arrow)
+						{
+							Cursor::instance().setCursor(CursorType::Arrow);
+							mActiveCursor = CursorType::Arrow;
+						}
+					}
+				}
+			}
+
+			mLastPointerScreenPos = event.screenPos;
+
+			if(Math::abs(event.mouseWheelScrollAmount) > 0.00001f)
+			{
+				for(auto& elementInfo : mElementsUnderPointer)
+				{
+					mMouseEvent.setMouseWheelScrollData(event.mouseWheelScrollAmount);
+					if(sendMouseEvent(elementInfo.element, mMouseEvent))
+					{
+						event.markAsUsed();
+						break;
+					}
+				}
+			}
+		}
+	}
+
+	void GUIManager::onPointerReleased(const PointerEvent& event)
+	{
+		if(event.isUsed())
+			return;
+
+		bool buttonStates[(int)GUIMouseButton::Count];
+		buttonStates[0] = event.buttonStates[0];
+		buttonStates[1] = event.buttonStates[1];
+		buttonStates[2] = event.buttonStates[2];
+
+		if(findElementUnderPointer(event.screenPos, buttonStates, event.shift, event.control, event.alt))
+			event.markAsUsed();
+
+		mMouseEvent = GUIMouseEvent(buttonStates, event.shift, event.control, event.alt);
+
+		GUIMouseButton guiButton = buttonToGUIButton(event.button);
+
+		// Send MouseUp event only if we are over the active element (we don't want to accidentally trigger other elements).
+		// And only activate when a button that originally caused the active state is released, otherwise ignore it.
+		if(mActiveMouseButton == guiButton)
+		{
+			for(auto& elementInfo : mElementsUnderPointer)
+			{
+				auto iterFind2 = std::find_if(mActiveElements.begin(), mActiveElements.end(), 
+					[&](const ElementInfo& x) { return x.element == elementInfo.element; });
+
+				if(iterFind2 != mActiveElements.end())
+				{
+					Vector2I localPos = getWidgetRelativePos(elementInfo.widget, event.screenPos);
+					mMouseEvent.setMouseUpData(localPos, guiButton);
+
+					if(sendMouseEvent(elementInfo.element, mMouseEvent))
+					{
+						event.markAsUsed();
+						break;
+					}
+				}
+			}
+		}
+
+		// Send DragEnd event to whichever element is active
+		bool acceptEndDrag = (mDragState == DragState::Dragging || mDragState == DragState::HeldWithoutDrag) && mActiveMouseButton == guiButton && 
+			(guiButton == GUIMouseButton::Left);
+
+		if(acceptEndDrag)
+		{
+			if(mDragState == DragState::Dragging)
+			{
+				for(auto& activeElement : mActiveElements)
+				{
+					Vector2I localPos = getWidgetRelativePos(activeElement.widget, event.screenPos);
+
+					mMouseEvent.setMouseDragEndData(localPos);
+					if(sendMouseEvent(activeElement.element, mMouseEvent))
+						event.markAsUsed();
+				}
+			}
+
+			mDragState = DragState::NoDrag;
+		}
+
+		if(mActiveMouseButton == guiButton)
+		{
+			mActiveElements.clear();
+			mActiveMouseButton = GUIMouseButton::Left;
+		}
+
+		if(mActiveCursor != CursorType::Arrow)
+		{
+			Cursor::instance().setCursor(CursorType::Arrow);
+			mActiveCursor = CursorType::Arrow;
+		}
+	}
+
+	void GUIManager::onPointerPressed(const PointerEvent& event)
+	{
+		if(event.isUsed())
+			return;
+
+		bool buttonStates[(int)GUIMouseButton::Count];
+		buttonStates[0] = event.buttonStates[0];
+		buttonStates[1] = event.buttonStates[1];
+		buttonStates[2] = event.buttonStates[2];
+
+		if(findElementUnderPointer(event.screenPos, buttonStates, event.shift, event.control, event.alt))
+			event.markAsUsed();
+
+		mMouseEvent = GUIMouseEvent(buttonStates, event.shift, event.control, event.alt);
+
+		GUIMouseButton guiButton = buttonToGUIButton(event.button);
+
+		// We only check for mouse down if mouse isn't already being held down, and we are hovering over an element
+		if(mActiveElements.size() == 0)
+		{
+			mNewActiveElements.clear();
+			for(auto& elementInfo : mElementsUnderPointer)
+			{
+				Vector2I localPos = getWidgetRelativePos(elementInfo.widget, event.screenPos);
+				mMouseEvent.setMouseDownData(localPos, guiButton);
+
+				bool processed = sendMouseEvent(elementInfo.element, mMouseEvent);
+
+				if(guiButton == GUIMouseButton::Left)
+				{
+					mDragState = DragState::HeldWithoutDrag;
+					mLastPointerClickPos = event.screenPos;
+				}
+
+				mNewActiveElements.push_back(ElementInfo(elementInfo.element, elementInfo.widget));
+				mActiveMouseButton = guiButton;
+
+				if(processed)
+				{
+					event.markAsUsed();
+					break;
+				}
+			}
+
+			mActiveElements.swap(mNewActiveElements);
+		}
+
+		mNewElementsInFocus.clear();
+		mCommandEvent = GUICommandEvent();
+		
+		// Determine elements that gained focus
+		mCommandEvent.setType(GUICommandEventType::FocusGained);
+
+		for(auto& elementInfo : mElementsUnderPointer)
+		{
+			auto iterFind = std::find_if(begin(mElementsInFocus), end(mElementsInFocus), 
+				[=] (const ElementFocusInfo& x) { return x.element == elementInfo.element; });
+
+			if(iterFind == mElementsInFocus.end())
+			{
+				bool processed = sendCommandEvent(elementInfo.element, mCommandEvent);
+				mNewElementsInFocus.push_back(ElementFocusInfo(elementInfo.element, elementInfo.widget, processed));
+
+				if (processed)
+					break;
+			}
+			else
+			{
+				mNewElementsInFocus.push_back(*iterFind);
+
+				if (iterFind->usesFocus)
+					break;
+			}
+		}
+
+		// Determine elements that lost focus
+		mCommandEvent.setType(GUICommandEventType::FocusLost);
+
+		for(auto& elementInfo : mElementsInFocus)
+		{
+			auto iterFind = std::find_if(begin(mNewElementsInFocus), end(mNewElementsInFocus), 
+				[=] (const ElementFocusInfo& x) { return x.element == elementInfo.element; });
+
+			if(iterFind == mNewElementsInFocus.end())
+			{
+				sendCommandEvent(elementInfo.element, mCommandEvent);
+			}
+		}
+
+		if(mElementsUnderPointer.size() > 0)
+			event.markAsUsed();
+
+		mElementsInFocus.swap(mNewElementsInFocus);
+
+		// If right click try to open context menu
+		if(buttonStates[2] == true) 
+		{
+			for(auto& elementInfo : mElementsUnderPointer)
+			{
+				GUIContextMenuPtr menu = elementInfo.element->_getContextMenu();
+
+				if(menu != nullptr && elementInfo.widget != nullptr)
+				{
+					const RenderWindow* window = getWidgetWindow(*elementInfo.widget);
+					if (window != nullptr)
+					{
+						Vector2I windowPos = window->screenToWindowPos(event.screenPos);
+
+						menu->open(windowPos, *elementInfo.widget);
+						event.markAsUsed();
+						break;
+					}
+				}
+			}
+		}
+	}
+
+	void GUIManager::onPointerDoubleClick(const PointerEvent& event)
+	{
+		if(event.isUsed())
+			return;
+
+		bool buttonStates[(int)GUIMouseButton::Count];
+		buttonStates[0] = event.buttonStates[0];
+		buttonStates[1] = event.buttonStates[1];
+		buttonStates[2] = event.buttonStates[2];
+
+		if(findElementUnderPointer(event.screenPos, buttonStates, event.shift, event.control, event.alt))
+			event.markAsUsed();
+
+		mMouseEvent = GUIMouseEvent(buttonStates, event.shift, event.control, event.alt);
+
+		GUIMouseButton guiButton = buttonToGUIButton(event.button);
+
+		// We only check for mouse down if we are hovering over an element
+		for(auto& elementInfo : mElementsUnderPointer)
+		{
+			Vector2I localPos = getWidgetRelativePos(elementInfo.widget, event.screenPos);
+
+			mMouseEvent.setMouseDoubleClickData(localPos, guiButton);
+			if(sendMouseEvent(elementInfo.element, mMouseEvent))
+			{
+				event.markAsUsed();
+				break;
+			}
+		}
+	}
+
+	void GUIManager::onInputCommandEntered(InputCommandType commandType)
+	{
+		if(mElementsInFocus.size() == 0)
+			return;
+
+		hideTooltip();
+		mCommandEvent = GUICommandEvent();
+
+		switch(commandType)
+		{
+		case InputCommandType::Backspace:
+			mCommandEvent.setType(GUICommandEventType::Backspace);
+			break;
+		case InputCommandType::Delete:
+			mCommandEvent.setType(GUICommandEventType::Delete);
+			break;
+		case InputCommandType::Return:
+			mCommandEvent.setType(GUICommandEventType::Return);
+			break;
+		case InputCommandType::Confirm:
+			mCommandEvent.setType(GUICommandEventType::Confirm);
+			break;
+		case InputCommandType::Escape:
+			mCommandEvent.setType(GUICommandEventType::Escape);
+			break;
+		case InputCommandType::CursorMoveLeft:
+			mCommandEvent.setType(GUICommandEventType::MoveLeft);
+			break;
+		case InputCommandType::CursorMoveRight:
+			mCommandEvent.setType(GUICommandEventType::MoveRight);
+			break;
+		case InputCommandType::CursorMoveUp:
+			mCommandEvent.setType(GUICommandEventType::MoveUp);
+			break;
+		case InputCommandType::CursorMoveDown:
+			mCommandEvent.setType(GUICommandEventType::MoveDown);
+			break;
+		case InputCommandType::SelectLeft:
+			mCommandEvent.setType(GUICommandEventType::SelectLeft);
+			break;
+		case InputCommandType::SelectRight:
+			mCommandEvent.setType(GUICommandEventType::SelectRight);
+			break;
+		case InputCommandType::SelectUp:
+			mCommandEvent.setType(GUICommandEventType::SelectUp);
+			break;
+		case InputCommandType::SelectDown:
+			mCommandEvent.setType(GUICommandEventType::SelectDown);
+			break;
+		}
+
+		for(auto& elementInfo : mElementsInFocus)
+		{
+			sendCommandEvent(elementInfo.element, mCommandEvent);
+		}		
+	}
+
+	void GUIManager::onVirtualButtonDown(const VirtualButton& button, UINT32 deviceIdx)
+	{
+		hideTooltip();
+		mVirtualButtonEvent.setButton(button);
+		
+		for(auto& elementInFocus : mElementsInFocus)
+		{
+			bool processed = sendVirtualButtonEvent(elementInFocus.element, mVirtualButtonEvent);
+
+			if(processed)
+				break;
+		}
+	}
+
+	bool GUIManager::findElementUnderPointer(const Vector2I& pointerScreenPos, bool buttonStates[3], bool shift, bool control, bool alt)
+	{
+		Vector<const RenderWindow*> widgetWindows;
+		for(auto& widgetInfo : mWidgets)
+			widgetWindows.push_back(getWidgetWindow(*widgetInfo.widget));
+
+#if BS_DEBUG_MODE
+		// Checks if all referenced windows actually exist
+		Vector<RenderWindow*> activeWindows = RenderWindowManager::instance().getRenderWindows();
+		for(auto& window : widgetWindows)
+		{
+			if(window == nullptr)
+				continue;
+
+			auto iterFind = std::find(begin(activeWindows), end(activeWindows), window);
+
+			if(iterFind == activeWindows.end())
+			{
+				BS_EXCEPT(InternalErrorException, "GUI manager has a reference to a window that doesn't exist. \
+												  Please detach all GUIWidgets from windows before destroying a window.");
+			}
+		}
+#endif
+
+		mNewElementsUnderPointer.clear();
+
+		const RenderWindow* windowUnderPointer = nullptr;
+		UnorderedSet<const RenderWindow*> uniqueWindows;
+
+		for(auto& window : widgetWindows)
+		{
+			if(window == nullptr)
+				continue;
+
+			uniqueWindows.insert(window);
+		}
+
+		for(auto& window : uniqueWindows)
+		{
+			if(Platform::isPointOverWindow(*window, pointerScreenPos))
+			{
+				windowUnderPointer = window;
+				break;
+			}
+		}
+
+		if(windowUnderPointer != nullptr)
+		{
+			Vector2I windowPos = windowUnderPointer->screenToWindowPos(pointerScreenPos);
+			Vector4 vecWindowPos((float)windowPos.x, (float)windowPos.y, 0.0f, 1.0f);
+
+			UINT32 widgetIdx = 0;
+			for(auto& widgetInfo : mWidgets)
+			{
+				if(widgetWindows[widgetIdx] == nullptr)
+				{
+					widgetIdx++;
+					continue;
+				}
+
+				GUIWidget* widget = widgetInfo.widget;
+				if(widgetWindows[widgetIdx] == windowUnderPointer && widget->inBounds(windowToBridgedCoords(*widget, windowPos)))
+				{
+					const Vector<GUIElement*>& elements = widget->getElements();
+					Vector2I localPos = getWidgetRelativePos(widget, pointerScreenPos);
+
+					// Elements with lowest depth (most to the front) get handled first
+					for(auto iter = elements.begin(); iter != elements.end(); ++iter)
+					{
+						GUIElement* element = *iter;
+
+						if(element->_isVisible() && element->_isInBounds(localPos))
+						{
+							ElementInfoUnderPointer elementInfo(element, widget);
+
+							auto iterFind = std::find_if(mElementsUnderPointer.begin(), mElementsUnderPointer.end(),
+								[=](const ElementInfoUnderPointer& x) { return x.element == element; });
+
+							if (iterFind != mElementsUnderPointer.end())
+							{
+								elementInfo.usesMouseOver = iterFind->usesMouseOver;
+								elementInfo.receivedMouseOver = iterFind->receivedMouseOver;
+							}
+
+							mNewElementsUnderPointer.push_back(elementInfo);
+						}
+					}
+				}
+
+				widgetIdx++;
+			}
+		}
+
+		std::sort(mNewElementsUnderPointer.begin(), mNewElementsUnderPointer.end(), 
+			[](const ElementInfoUnderPointer& a, const ElementInfoUnderPointer& b)
+		{
+			return a.element->_getDepth() < b.element->_getDepth();
+		});
+
+		// Send MouseOut and MouseOver events
+		bool eventProcessed = false;
+
+		for (auto& elementInfo : mNewElementsUnderPointer)
+		{
+			GUIElement* element = elementInfo.element;
+			GUIWidget* widget = elementInfo.widget;
+
+			if (elementInfo.receivedMouseOver)
+			{
+				elementInfo.isHovering = true;
+				if (elementInfo.usesMouseOver)
+					break;
+
+				continue;
+			}
+
+			auto iterFind = std::find_if(mActiveElements.begin(), mActiveElements.end(),
+				[&](const ElementInfo& x) { return x.element == element; });
+
+			// Send MouseOver event
+			if (mActiveElements.size() == 0 || iterFind != mActiveElements.end())
+			{
+				Vector2I localPos = getWidgetRelativePos(widget, pointerScreenPos);
+
+				mMouseEvent = GUIMouseEvent(buttonStates, shift, control, alt);
+
+				mMouseEvent.setMouseOverData(localPos);
+				elementInfo.receivedMouseOver = true;
+				elementInfo.isHovering = true;
+				if (sendMouseEvent(element, mMouseEvent))
+				{
+					eventProcessed = true;
+					elementInfo.usesMouseOver = true;
+					break;
+				}
+			}
+		}
+
+		// Send DragAndDropLeft event - It is similar to MouseOut events but we send it to all
+		// elements a user might hover over, while we send mouse over/out events only to active elements while dragging
+		if (DragAndDropManager::instance().isDragInProgress())
+		{
+			for (auto& elementInfo : mElementsUnderPointer)
+			{
+				auto iterFind = std::find_if(mNewElementsUnderPointer.begin(), mNewElementsUnderPointer.end(),
+					[=](const ElementInfoUnderPointer& x) { return x.element == elementInfo.element; });
+
+				if (iterFind == mNewElementsUnderPointer.end())
+				{
+					Vector2I localPos = getWidgetRelativePos(elementInfo.widget, pointerScreenPos);
+
+					mMouseEvent.setDragAndDropLeftData(localPos, DragAndDropManager::instance().getDragTypeId(), DragAndDropManager::instance().getDragData());
+					if (sendMouseEvent(elementInfo.element, mMouseEvent))
+					{
+						eventProcessed = true;
+						break;
+					}
+				}
+			}
+		}
+
+		for(auto& elementInfo : mElementsUnderPointer)
+		{
+			GUIElement* element = elementInfo.element;
+			GUIWidget* widget = elementInfo.widget;
+
+			auto iterFind = std::find_if(mNewElementsUnderPointer.begin(), mNewElementsUnderPointer.end(),
+				[=](const ElementInfoUnderPointer& x) { return x.element == element; });
+
+			if (!elementInfo.receivedMouseOver)
+				continue;
+
+			if (iterFind == mNewElementsUnderPointer.end() || !iterFind->isHovering)
+			{
+				auto iterFind2 = std::find_if(mActiveElements.begin(), mActiveElements.end(), 
+					[=](const ElementInfo& x) { return x.element == element; });
+
+				// Send MouseOut event
+				if(mActiveElements.size() == 0 || iterFind2 != mActiveElements.end())
+				{
+					Vector2I localPos = getWidgetRelativePos(widget, pointerScreenPos);
+
+					mMouseEvent.setMouseOutData(localPos);
+					if (sendMouseEvent(element, mMouseEvent))
+					{
+						eventProcessed = true;
+						break;
+					}
+				}
+			}
+		}
+
+		mElementsUnderPointer.swap(mNewElementsUnderPointer);
+
+		// Tooltip
+		hideTooltip();
+		if (mElementsUnderPointer.size() > 0)
+			mShowTooltip = true;
+
+		mTooltipElementHoverStart = gTime().getTime();
+
+		return eventProcessed;
+	}
+
+	void GUIManager::onTextInput(const TextInputEvent& event)
+	{
+		mTextInputEvent = GUITextInputEvent();
+		mTextInputEvent.setData(event.textChar);
+
+		for(auto& elementInFocus : mElementsInFocus)
+		{
+			if(sendTextInputEvent(elementInFocus.element, mTextInputEvent))
+				event.markAsUsed();
+		}
+	}
+
+	void GUIManager::onWindowFocusGained(RenderWindow& win)
+	{
+		for(auto& widgetInfo : mWidgets)
+		{
+			GUIWidget* widget = widgetInfo.widget;
+			if(getWidgetWindow(*widget) == &win)
+				widget->ownerWindowFocusChanged();
+		}
+	}
+
+	void GUIManager::onWindowFocusLost(RenderWindow& win)
+	{
+		for(auto& widgetInfo : mWidgets)
+		{
+			GUIWidget* widget = widgetInfo.widget;
+			if(getWidgetWindow(*widget) == &win)
+				widget->ownerWindowFocusChanged();
+		}
+
+		mNewElementsInFocus.clear();
+		for(auto& focusedElement : mElementsInFocus)
+		{
+			if (focusedElement.element->_isDestroyed())
+				continue;
+
+			if (focusedElement.widget != nullptr && getWidgetWindow(*focusedElement.widget) == &win)
+			{
+				mCommandEvent = GUICommandEvent();
+				mCommandEvent.setType(GUICommandEventType::FocusLost);
+
+				sendCommandEvent(focusedElement.element, mCommandEvent);
+			}
+			else
+				mNewElementsInFocus.push_back(focusedElement);
+		}
+
+		mElementsInFocus.swap(mNewElementsInFocus);
+	}
+
+	// We stop getting mouse move events once it leaves the window, so make sure
+	// nothing stays in hover state
+	void GUIManager::onMouseLeftWindow(RenderWindow& win)
+	{
+		bool buttonStates[3];
+		buttonStates[0] = false;
+		buttonStates[1] = false;
+		buttonStates[2] = false;
+
+		mNewElementsUnderPointer.clear();
+
+		for(auto& elementInfo : mElementsUnderPointer)
+		{
+			GUIElement* element = elementInfo.element;
+			GUIWidget* widget = elementInfo.widget;
+
+			if (widget != nullptr && widget->getTarget()->getTarget().get() != &win)
+			{
+				mNewElementsUnderPointer.push_back(elementInfo);
+				continue;
+			}
+
+			auto iterFind = std::find_if(mActiveElements.begin(), mActiveElements.end(), 
+				[&](const ElementInfo& x) { return x.element == element; });
+
+			// Send MouseOut event
+			if(mActiveElements.size() == 0 || iterFind != mActiveElements.end())
+			{
+				Vector2I localPos = getWidgetRelativePos(widget, Vector2I());
+
+				mMouseEvent.setMouseOutData(localPos);
+				sendMouseEvent(element, mMouseEvent);
+			}
+		}
+
+		mElementsUnderPointer.swap(mNewElementsUnderPointer);
+
+		hideTooltip();
+		if(mDragState != DragState::Dragging)
+		{
+			if(mActiveCursor != CursorType::Arrow)
+			{
+				Cursor::instance().setCursor(CursorType::Arrow);
+				mActiveCursor = CursorType::Arrow;
+			}
+		}
+	}
+	
+	void GUIManager::hideTooltip()
+	{
+		GUITooltipManager::instance().hide();
+		mShowTooltip = false;
+	}
+
+	void GUIManager::queueForDestroy(GUIElement* element)
+	{
+		mScheduledForDestruction.push(element);
+	}
+
+	void GUIManager::setFocus(GUIElement* element, bool focus)
+	{
+		ElementForcedFocusInfo efi;
+		efi.element = element;
+		efi.focus = focus;
+
+		mForcedFocusElements.push_back(efi);
+	}
+
+	bool GUIManager::processDestroyQueue()
+	{
+		Stack<GUIElement*> toDestroy = mScheduledForDestruction;
+		mScheduledForDestruction = Stack<GUIElement*>();
+
+		while (!toDestroy.empty())
+		{
+			bs_delete(toDestroy.top());
+			toDestroy.pop();
+		}
+
+		return !mScheduledForDestruction.empty();
+	}
+
+	void GUIManager::setInputBridge(const RenderTexture* renderTex, const GUIElement* element)
+	{
+		if(element == nullptr)
+			mInputBridge.erase(renderTex);
+		else
+			mInputBridge[renderTex] = element;
+	}
+
+	GUIMouseButton GUIManager::buttonToGUIButton(PointerEventButton pointerButton) const
+	{
+		if(pointerButton == PointerEventButton::Left)
+			return GUIMouseButton::Left;
+		else if(pointerButton == PointerEventButton::Middle)
+			return GUIMouseButton::Middle;
+		else if(pointerButton == PointerEventButton::Right)
+			return GUIMouseButton::Right;
+
+		BS_EXCEPT(InvalidParametersException, "Provided button is not a GUI supported mouse button.");
+		return GUIMouseButton::Left;
+	}
+
+	Vector2I GUIManager::getWidgetRelativePos(const GUIWidget* widget, const Vector2I& screenPos) const
+	{
+		if (widget == nullptr)
+			return screenPos;
+
+		const RenderWindow* window = getWidgetWindow(*widget);
+		if(window == nullptr)
+			return Vector2I();
+
+		Vector2I windowPos = window->screenToWindowPos(screenPos);
+		windowPos = windowToBridgedCoords(*widget, windowPos);
+
+		const Matrix4& worldTfrm = widget->getWorldTfrm();
+
+		Vector4 vecLocalPos = worldTfrm.inverse().multiplyAffine(Vector4((float)windowPos.x, (float)windowPos.y, 0.0f, 1.0f));
+		Vector2I curLocalPos(Math::roundToInt(vecLocalPos.x), Math::roundToInt(vecLocalPos.y));
+
+		return curLocalPos;
+	}
+
+	Vector2I GUIManager::windowToBridgedCoords(const GUIWidget& widget, const Vector2I& windowPos) const
+	{
+		// This cast might not be valid (the render target could be a window), but we only really need to cast
+		// so that mInputBridge map allows us to search through it - we don't access anything unless the target is bridged
+		// (in which case we know it is a RenderTexture)
+		const RenderTexture* renderTexture = static_cast<const RenderTexture*>(widget.getTarget()->getTarget().get());
+		const RenderTargetProperties& rtProps = renderTexture->getProperties();
+
+		auto iterFind = mInputBridge.find(renderTexture);
+		if(iterFind != mInputBridge.end()) // Widget input is bridged, which means we need to transform the coordinates
+		{
+			const GUIElement* bridgeElement = iterFind->second;
+			const GUIWidget* parentWidget = bridgeElement->_getParentWidget();
+			if (parentWidget == nullptr)
+				return windowPos;
+
+			const Matrix4& worldTfrm = parentWidget->getWorldTfrm();
+
+			Vector4 vecLocalPos = worldTfrm.inverse().multiplyAffine(Vector4((float)windowPos.x, (float)windowPos.y, 0.0f, 1.0f));
+			Rect2I bridgeBounds = bridgeElement->_getLayoutData().area;
+
+			// Find coordinates relative to the bridge element
+			float x = vecLocalPos.x - (float)bridgeBounds.x;
+			float y = vecLocalPos.y - (float)bridgeBounds.y;
+
+			float scaleX = rtProps.getWidth() / (float)bridgeBounds.width;
+			float scaleY = rtProps.getHeight() / (float)bridgeBounds.height;
+
+			return Vector2I(Math::roundToInt(x * scaleX), Math::roundToInt(y * scaleY));
+		}
+
+		return windowPos;
+	}
+
+	const RenderWindow* GUIManager::getWidgetWindow(const GUIWidget& widget) const
+	{
+		// This cast might not be valid (the render target could be a window), but we only really need to cast
+		// so that mInputBridge map allows us to search through it - we don't access anything unless the target is bridged
+		// (in which case we know it is a RenderTexture)
+
+		const Viewport* viewport = widget.getTarget();
+		if (viewport == nullptr)
+			return nullptr;
+
+		RenderTargetPtr target = viewport->getTarget();
+		if (target == nullptr)
+			return nullptr;
+
+		const RenderTexture* renderTexture = static_cast<const RenderTexture*>(target.get());
+
+		auto iterFind = mInputBridge.find(renderTexture);
+		if(iterFind != mInputBridge.end())
+		{
+			GUIWidget* parentWidget = iterFind->second->_getParentWidget();
+			if (parentWidget == nullptr)
+				return nullptr;
+
+			if(parentWidget != &widget)
+				return getWidgetWindow(*parentWidget);
+		}
+
+		Vector<RenderWindow*> renderWindows = RenderWindowManager::instance().getRenderWindows();
+
+		auto iterFindWin = std::find(renderWindows.begin(), renderWindows.end(), target.get());
+		if(iterFindWin != renderWindows.end())
+			return static_cast<RenderWindow*>(target.get());
+
+		return nullptr;
+	}
+
+	bool GUIManager::sendMouseEvent(GUIElement* element, const GUIMouseEvent& event)
+	{
+		if (element->_isDestroyed())
+			return false;
+
+		return element->_mouseEvent(event);
+	}
+
+	bool GUIManager::sendTextInputEvent(GUIElement* element, const GUITextInputEvent& event)
+	{
+		if (element->_isDestroyed())
+			return false;
+
+		return element->_textInputEvent(event);
+	}
+
+	bool GUIManager::sendCommandEvent(GUIElement* element, const GUICommandEvent& event)
+	{
+		if (element->_isDestroyed())
+			return false;
+
+		return element->_commandEvent(event);
+	}
+
+	bool GUIManager::sendVirtualButtonEvent(GUIElement* element, const GUIVirtualButtonEvent& event)
+	{
+		if (element->_isDestroyed())
+			return false;
+
+		return element->_virtualButtonEvent(event);
+	}
+
+	GUIManager& gGUIManager()
+	{
+		return GUIManager::instance();
+	}
+
+	GUIManagerCore::~GUIManagerCore()
+	{
+		CoreRendererPtr activeRenderer = RendererManager::instance().getActive();
+		for (auto& cameraData : mPerCameraData)
+			activeRenderer->_unregisterRenderCallback(cameraData.first.get(), 30);
+	}
+
+	void GUIManagerCore::initialize(const SPtr<MaterialCore>& textMat, const SPtr<MaterialCore>& imageMat,
+		const SPtr<MaterialCore>& imageAlphaMat)
+	{
+		mTextMaterialInfo = MaterialInfo(textMat);
+		mImageMaterialInfo = MaterialInfo(imageMat);
+		mImageAlphaMaterialInfo = MaterialInfo(imageAlphaMat);
+
+		SAMPLER_STATE_DESC ssDesc;
+		ssDesc.magFilter = FO_POINT;
+		ssDesc.minFilter = FO_POINT;
+		ssDesc.mipFilter = FO_POINT;
+
+		mSamplerState = RenderStateCoreManager::instance().createSamplerState(ssDesc);
+	}
+
+	void GUIManagerCore::updateData(const UnorderedMap<SPtr<CameraCore>, Vector<GUIManager::GUICoreRenderData>>& newPerCameraData)
+	{
+		bs_frame_mark();
+
+		{
+			FrameSet<SPtr<CameraCore>> validCameras;
+
+			CoreRendererPtr activeRenderer = RendererManager::instance().getActive();
+			for (auto& newCameraData : newPerCameraData)
+			{
+				UINT32 idx = 0;
+				Vector<GUIManager::GUICoreRenderData>* renderData = nullptr;
+				for (auto& oldCameraData : mPerCameraData)
+				{
+					if (newCameraData.first == oldCameraData.first)
+					{
+						renderData = &oldCameraData.second;
+						validCameras.insert(oldCameraData.first);
+						break;
+					}
+
+					idx++;
+				}
+
+				if (renderData == nullptr)
+				{
+					SPtr<CameraCore> camera = newCameraData.first;
+
+					auto insertedData = mPerCameraData.insert(std::make_pair(newCameraData.first, Vector<GUIManager::GUICoreRenderData>()));
+					renderData = &insertedData.first->second;
+
+					activeRenderer->_registerRenderCallback(camera.get(), 30, std::bind(&GUIManagerCore::render, this, camera), true);
+					validCameras.insert(camera);
+				}
+
+				*renderData = newCameraData.second;
+			}
+
+			FrameVector<SPtr<CameraCore>> cameraToRemove;
+			for (auto& cameraData : mPerCameraData)
+			{
+				auto iterFind = validCameras.find(cameraData.first);
+				if (iterFind == validCameras.end())
+					cameraToRemove.push_back(cameraData.first);
+			}
+
+			for (auto& camera : cameraToRemove)
+			{
+				activeRenderer->_unregisterRenderCallback(camera.get(), 30);
+				mPerCameraData.erase(camera);
+			}
+		}
+
+		bs_frame_clear();
+	}
+
+	void GUIManagerCore::render(const SPtr<CameraCore>& camera)
+	{
+		Vector<GUIManager::GUICoreRenderData>& renderData = mPerCameraData[camera];
+
+		float invViewportWidth = 1.0f / (camera->getViewport()->getWidth() * 0.5f);
+		float invViewportHeight = 1.0f / (camera->getViewport()->getHeight() * 0.5f);
+		for (auto& entry : renderData)
+		{
+			MaterialInfo& matInfo = entry.materialType == SpriteMaterial::Text ? mTextMaterialInfo :
+				(entry.materialType == SpriteMaterial::Image ? mImageMaterialInfo : mImageAlphaMaterialInfo);
+
+			matInfo.textureParam.set(entry.texture);
+			matInfo.samplerParam.set(mSamplerState);
+			matInfo.tintParam.set(entry.tint);
+			matInfo.invViewportWidthParam.set(invViewportWidth);
+			matInfo.invViewportHeightParam.set(invViewportHeight);
+			matInfo.worldTransformParam.set(entry.worldTransform);
+
+			// TODO - I shouldn't be re-applying the entire material for each entry, instead just check which programs
+			// changed, and apply only those + the modified constant buffers and/or texture.
+
+			gRendererUtility().setPass(matInfo.material, 0);
+			gRendererUtility().draw(entry.mesh, entry.mesh->getProperties().getSubMesh(0));
+		}
+	}
+
+	GUIManagerCore::MaterialInfo::MaterialInfo(const SPtr<MaterialCore>& material)
+		:material(material)
+	{
+		textureParam = material->getParamTexture("mainTexture");
+		samplerParam = material->getParamSamplerState("mainTexSamp");
+		tintParam = material->getParamColor("tint");
+		invViewportWidthParam = material->getParamFloat("invViewportWidth");
+		invViewportHeightParam = material->getParamFloat("invViewportHeight");
+		worldTransformParam = material->getParamMat4("worldTransform");
+	}
 }

+ 313 - 310
Source/BansheePhysX/Include/BsPhysX.h

@@ -1,311 +1,314 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#pragma once
-
-#include "BsPhysXPrerequisites.h"
-#include "BsPhysics.h"
-#include "BsPhysicsCommon.h"
-#include "PxPhysics.h"
-#include "foundation/Px.h"
-#include "characterkinematic\PxControllerManager.h"
-#include "cooking/PxCooking.h"
-
-namespace BansheeEngine
-{
-	/** @addtogroup PhysX
-	 *  @{
-	 */
-
-	/** NVIDIA PhysX implementation of Physics. */
-	class PhysX : public Physics
-	{
-		/** Type of contacts reported by PhysX simulation. */
-		enum class ContactEventType
-		{
-			ContactBegin,
-			ContactStay,
-			ContactEnd
-		};
-
-		/** Event reported when a physics object interacts with a collider. */
-		struct TriggerEvent
-		{
-			Collider* trigger; /** Trigger that was interacted with. */
-			Collider* other; /** Collider that was interacted with. */
-			ContactEventType type; /** Exact type of the event. */
-		};
-
-		/** Event reported when two colliders interact. */
-		struct ContactEvent
-		{
-			Collider* colliderA; /** First collider. */
-			Collider* colliderB; /** Second collider. */
-			ContactEventType type; /** Exact type of the event. */
-			// Note: Not too happy this is heap allocated, use static allocator?
-			Vector<ContactPoint> points; /** Information about all contact points between the colliders. */
-		};
-
-		/** Event reported when a joint breaks. */
-		struct JointBreakEvent
-		{
-			Joint* joint; /** Broken joint. */
-		};
-
-	public:
-		PhysX(const PHYSICS_INIT_DESC& input);
-		~PhysX();
-
-		/** @copydoc Physics::update */
-		void update() override;
-
-		/** @copydoc Physics::createMaterial */
-		SPtr<PhysicsMaterial> createMaterial(float staticFriction, float dynamicFriction, float restitution) override;
-
-		/** @copydoc Physics::createMesh */
-		SPtr<PhysicsMesh> createMesh(const MeshDataPtr& meshData, PhysicsMeshType type) override;
-
-		/** @copydoc Physics::createRigidbody */
-		SPtr<Rigidbody> createRigidbody(const HSceneObject& linkedSO) override;
-
-		/** @copydoc Physics::createBoxCollider */
-		SPtr<BoxCollider> createBoxCollider(const Vector3& extents, const Vector3& position,
-			const Quaternion& rotation) override;
-
-		/** @copydoc Physics::createSphereCollider */
-		SPtr<SphereCollider> createSphereCollider(float radius, const Vector3& position, const Quaternion& rotation) override;
-
-		/** @copydoc Physics::createPlaneCollider */
-		SPtr<PlaneCollider> createPlaneCollider(const Vector3& position, const Quaternion& rotation) override;
-
-		/** @copydoc Physics::createCapsuleCollider */
-		SPtr<CapsuleCollider> createCapsuleCollider(float radius, float halfHeight, const Vector3& position, 
-			const Quaternion& rotation) override;
-
-		/** @copydoc Physics::createMeshCollider */
-		SPtr<MeshCollider> createMeshCollider(const Vector3& position, const Quaternion& rotation) override;
-
-		/** @copydoc Physics::createFixedJoint */
-		SPtr<FixedJoint> createFixedJoint() override;
-
-		/** @copydoc Physics::createDistanceJoint */
-		SPtr<DistanceJoint> createDistanceJoint() override;
-
-		/** @copydoc Physics::createHingeJoint */
-		SPtr<HingeJoint> createHingeJoint() override;
-
-		/** @copydoc Physics::createSphericalJoint */
-		SPtr<SphericalJoint> createSphericalJoint() override;
-
-		/** @copydoc Physics::createSliderJoint */
-		SPtr<SliderJoint> createSliderJoint() override;
-
-		/** @copydoc Physics::createD6Joint */
-		SPtr<D6Joint> createD6Joint() override;
-
-		/** @copydoc Physics::createCharacterController*/
-		SPtr<CharacterController> createCharacterController(const CHAR_CONTROLLER_DESC& desc) override;
-
-		/** @copydoc Physics::rayCast(const Vector3&, const Vector3&, UINT64, float) */
-		bool rayCast(const Vector3& origin, const Vector3& unitDir, PhysicsQueryHit& hit,
-			UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
-
-		/** @copydoc Physics::boxCast */
-		bool boxCast(const AABox& box, const Quaternion& rotation, const Vector3& unitDir, PhysicsQueryHit& hit,
-			UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
-
-		/** @copydoc Physics::sphereCast */
-		bool sphereCast(const Sphere& sphere, const Vector3& unitDir, PhysicsQueryHit& hit,
-			UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
-
-		/** @copydoc Physics::capsuleCast */
-		bool capsuleCast(const Capsule& capsule, const Quaternion& rotation, const Vector3& unitDir,
-			PhysicsQueryHit& hit, UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
-
-		/** @copydoc Physics::convexCast */
-		bool convexCast(const HPhysicsMesh& mesh, const Vector3& position, const Quaternion& rotation,
-			const Vector3& unitDir, PhysicsQueryHit& hit, UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
-
-		/** @copydoc Physics::rayCastAll(const Vector3&, const Vector3&, UINT64, float) */
-		Vector<PhysicsQueryHit> rayCastAll(const Vector3& origin, const Vector3& unitDir,
-			UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
-
-		/** @copydoc Physics::boxCastAll */
-		Vector<PhysicsQueryHit> boxCastAll(const AABox& box, const Quaternion& rotation,
-			const Vector3& unitDir, UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
-
-		/** @copydoc Physics::sphereCastAll */
-		Vector<PhysicsQueryHit> sphereCastAll(const Sphere& sphere, const Vector3& unitDir,
-			UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
-
-		/** @copydoc Physics::capsuleCastAll */
-		Vector<PhysicsQueryHit> capsuleCastAll(const Capsule& capsule, const Quaternion& rotation,
-			const Vector3& unitDir, UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
-
-		/** @copydoc Physics::convexCastAll */
-		Vector<PhysicsQueryHit> convexCastAll(const HPhysicsMesh& mesh, const Vector3& position,
-			const Quaternion& rotation, const Vector3& unitDir, UINT64 layer = BS_ALL_LAYERS, 
-			float max = FLT_MAX) const override;
-
-		/** @copydoc Physics::rayCastAny(const Vector3&, const Vector3&, UINT64, float) */
-		bool rayCastAny(const Vector3& origin, const Vector3& unitDir,
-			UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
-
-		/** @copydoc Physics::boxCastAny */
-		bool boxCastAny(const AABox& box, const Quaternion& rotation, const Vector3& unitDir,
-			UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
-
-		/** @copydoc Physics::sphereCastAny */
-		bool sphereCastAny(const Sphere& sphere, const Vector3& unitDir,
-			UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
-
-		/** @copydoc Physics::capsuleCastAny */
-		bool capsuleCastAny(const Capsule& capsule, const Quaternion& rotation, const Vector3& unitDir,
-			UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
-
-		/** @copydoc Physics::convexCastAny */
-		bool convexCastAny(const HPhysicsMesh& mesh, const Vector3& position, const Quaternion& rotation,
-			const Vector3& unitDir, UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
-
-		/** @copydoc Physics::boxOverlapAny */
-		bool boxOverlapAny(const AABox& box, const Quaternion& rotation, UINT64 layer = BS_ALL_LAYERS) const override;
-
-		/** @copydoc Physics::sphereOverlapAny */
-		bool sphereOverlapAny(const Sphere& sphere, UINT64 layer = BS_ALL_LAYERS) const override;
-
-		/** @copydoc Physics::capsuleOverlapAny */
-		bool capsuleOverlapAny(const Capsule& capsule, const Quaternion& rotation,
-			UINT64 layer = BS_ALL_LAYERS) const override;
-
-		/** @copydoc Physics::convexOverlapAny */
-		bool convexOverlapAny(const HPhysicsMesh& mesh, const Vector3& position, const Quaternion& rotation,
-			UINT64 layer = BS_ALL_LAYERS) const override;
-
-		/** @copydoc Physics::setFlag */
-		void setFlag(PhysicsFlags flags, bool enabled) override;
-
-		/** @copydoc Physics::getGravity */
-		Vector3 getGravity() const override;
-
-		/** @copydoc Physics::setGravity */
-		void setGravity(const Vector3& gravity) override;
-
-		/** @copydoc Physics::getMaxTesselationEdgeLength */
-		float getMaxTesselationEdgeLength() const override { return mTesselationLength; }
-
-		/** @copydoc Physics::setMaxTesselationEdgeLength */
-		void setMaxTesselationEdgeLength(float length) override;
-
-		/** @copydoc Physics::addBroadPhaseRegion */
-		UINT32 addBroadPhaseRegion(const AABox& region) override;
-
-		/** @copydoc Physics::removeBroadPhaseRegion */
-		void removeBroadPhaseRegion(UINT32 regionId) override;
-
-		/** @copydoc Physics::clearBroadPhaseRegions */
-		void clearBroadPhaseRegions() override;
-
-		/** @copydoc Physics::_boxOverlap */
-		Vector<Collider*> _boxOverlap(const AABox& box, const Quaternion& rotation,
-			UINT64 layer = BS_ALL_LAYERS) const override;
-
-		/** @copydoc Physics::_sphereOverlap */
-		Vector<Collider*> _sphereOverlap(const Sphere& sphere, UINT64 layer = BS_ALL_LAYERS) const override;
-
-		/** @copydoc Physics::_capsuleOverlap */
-		Vector<Collider*> _capsuleOverlap(const Capsule& capsule, const Quaternion& rotation,
-			UINT64 layer = BS_ALL_LAYERS) const override;
-
-		/** @copydoc Physics::_convexOverlap */
-		Vector<Collider*> _convexOverlap(const HPhysicsMesh& mesh, const Vector3& position,
-			const Quaternion& rotation, UINT64 layer = BS_ALL_LAYERS) const override;
-
-		/** @copydoc Physics::_rayCast */
-		bool _rayCast(const Vector3& origin, const Vector3& unitDir, const Collider& collider, PhysicsQueryHit& hit, 
-			float maxDist = FLT_MAX) const override;
-
-		/** Triggered by the PhysX simulation when an interaction between two colliders is found. */
-		void _reportContactEvent(const ContactEvent& event);
-
-		/** Triggered by the PhysX simulation when an interaction between two trigger and a collider is found. */
-		void _reportTriggerEvent(const TriggerEvent& event);
-
-		/** Triggered by the PhysX simulation when a joint breaks. */
-		void _reportJointBreakEvent(const JointBreakEvent& event);
-
-		/** Returns the default PhysX material. */
-		physx::PxMaterial* getDefaultMaterial() const { return mDefaultMaterial; }
-
-		/** Returns the main PhysX object. */
-		physx::PxPhysics* getPhysX() const { return mPhysics; }
-
-		/** Returns the main PhysX scene. */
-		physx::PxScene* getScene() const { return mScene; }
-
-		/** Returns the PhysX object used for mesh cooking. */
-		physx::PxCooking* getCooking() const { return mCooking; }
-
-		/** Returns default scale used in the PhysX scene. */
-		physx::PxTolerancesScale getScale() const { return mScale; }
-
-	private:
-		friend class PhysXEventCallback;
-
-		/** Sends out all events recorded during simulation to the necessary physics objects. */
-		void triggerEvents();
-
-		/**
-		 * Helper method that performs a sweep query by checking if the provided geometry hits any physics objects
-		 * when moved along the specified direction. Returns information about the first hit.
-		 */
-		inline bool sweep(const physx::PxGeometry& geometry, const physx::PxTransform& tfrm, const Vector3& unitDir,
-			PhysicsQueryHit& hit, UINT64 layer, float maxDist) const;
-
-		/**
-		 * Helper method that performs a sweep query by checking if the provided geometry hits any physics objects
-		 * when moved along the specified direction. Returns information about all hit.
-		 */
-		inline Vector<PhysicsQueryHit> sweepAll(const physx::PxGeometry& geometry, const physx::PxTransform& tfrm, 
-			const Vector3& unitDir, UINT64 layer, float maxDist) const;
-
-		/**
-		 * Helper method that performs a sweep query by checking if the provided geometry hits any physics objects
-		 * when moved along the specified direction. Returns no information about the hit, but rather just if it happened or
-		 * not.
-		 */
-		inline bool sweepAny(const physx::PxGeometry& geometry, const physx::PxTransform& tfrm, const Vector3& unitDir,
-			UINT64 layer, float maxDist) const;
-
-		/** Helper method that returns all colliders that are overlapping the provided geometry. */
-		inline Vector<Collider*> overlap(const physx::PxGeometry& geometry, const physx::PxTransform& tfrm, 
-			UINT64 layer) const;
-
-		/** Helper method that checks if the provided geometry overlaps any physics object. */
-		inline bool overlapAny(const physx::PxGeometry& geometry, const physx::PxTransform& tfrm, UINT64 layer) const;
-
-		float mSimulationStep = 1.0f/60.0f;
-		float mLastSimulationTime = 0.0f;
-		float mTesselationLength = 3.0f;
-		UINT32 mNextRegionIdx = 1;
-
-		Vector<TriggerEvent> mTriggerEvents;
-		Vector<ContactEvent> mContactEvents;
-		Vector<JointBreakEvent> mJointBreakEvents;
-		UnorderedMap<UINT32, UINT32> mBroadPhaseRegionHandles;
-
-		physx::PxFoundation* mFoundation = nullptr;
-		physx::PxPhysics* mPhysics = nullptr;
-		physx::PxCooking* mCooking = nullptr;
-		physx::PxScene* mScene = nullptr;
-		physx::PxControllerManager* mCharManager = nullptr;
-
-		physx::PxMaterial* mDefaultMaterial = nullptr;
-		physx::PxTolerancesScale mScale;
-
-		static const UINT32 SCRATCH_BUFFER_SIZE;
-	};
-
-	/** Provides easier access to PhysX. */
-	PhysX& gPhysX();
-
-	/** @} */
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsPhysXPrerequisites.h"
+#include "BsPhysics.h"
+#include "BsPhysicsCommon.h"
+#include "PxPhysics.h"
+#include "foundation/Px.h"
+#include "characterkinematic\PxControllerManager.h"
+#include "cooking/PxCooking.h"
+
+namespace BansheeEngine
+{
+	/** @addtogroup PhysX
+	 *  @{
+	 */
+
+	/** NVIDIA PhysX implementation of Physics. */
+	class PhysX : public Physics
+	{
+		/** Type of contacts reported by PhysX simulation. */
+		enum class ContactEventType
+		{
+			ContactBegin,
+			ContactStay,
+			ContactEnd
+		};
+
+		/** Event reported when a physics object interacts with a collider. */
+		struct TriggerEvent
+		{
+			Collider* trigger; /** Trigger that was interacted with. */
+			Collider* other; /** Collider that was interacted with. */
+			ContactEventType type; /** Exact type of the event. */
+		};
+
+		/** Event reported when two colliders interact. */
+		struct ContactEvent
+		{
+			Collider* colliderA; /** First collider. */
+			Collider* colliderB; /** Second collider. */
+			ContactEventType type; /** Exact type of the event. */
+			// Note: Not too happy this is heap allocated, use static allocator?
+			Vector<ContactPoint> points; /** Information about all contact points between the colliders. */
+		};
+
+		/** Event reported when a joint breaks. */
+		struct JointBreakEvent
+		{
+			Joint* joint; /** Broken joint. */
+		};
+
+	public:
+		PhysX(const PHYSICS_INIT_DESC& input);
+		~PhysX();
+
+		/** @copydoc Physics::update */
+		void update() override;
+
+		/** @copydoc Physics::createMaterial */
+		SPtr<PhysicsMaterial> createMaterial(float staticFriction, float dynamicFriction, float restitution) override;
+
+		/** @copydoc Physics::createMesh */
+		SPtr<PhysicsMesh> createMesh(const MeshDataPtr& meshData, PhysicsMeshType type) override;
+
+		/** @copydoc Physics::createRigidbody */
+		SPtr<Rigidbody> createRigidbody(const HSceneObject& linkedSO) override;
+
+		/** @copydoc Physics::createBoxCollider */
+		SPtr<BoxCollider> createBoxCollider(const Vector3& extents, const Vector3& position,
+			const Quaternion& rotation) override;
+
+		/** @copydoc Physics::createSphereCollider */
+		SPtr<SphereCollider> createSphereCollider(float radius, const Vector3& position, const Quaternion& rotation) override;
+
+		/** @copydoc Physics::createPlaneCollider */
+		SPtr<PlaneCollider> createPlaneCollider(const Vector3& position, const Quaternion& rotation) override;
+
+		/** @copydoc Physics::createCapsuleCollider */
+		SPtr<CapsuleCollider> createCapsuleCollider(float radius, float halfHeight, const Vector3& position, 
+			const Quaternion& rotation) override;
+
+		/** @copydoc Physics::createMeshCollider */
+		SPtr<MeshCollider> createMeshCollider(const Vector3& position, const Quaternion& rotation) override;
+
+		/** @copydoc Physics::createFixedJoint */
+		SPtr<FixedJoint> createFixedJoint() override;
+
+		/** @copydoc Physics::createDistanceJoint */
+		SPtr<DistanceJoint> createDistanceJoint() override;
+
+		/** @copydoc Physics::createHingeJoint */
+		SPtr<HingeJoint> createHingeJoint() override;
+
+		/** @copydoc Physics::createSphericalJoint */
+		SPtr<SphericalJoint> createSphericalJoint() override;
+
+		/** @copydoc Physics::createSliderJoint */
+		SPtr<SliderJoint> createSliderJoint() override;
+
+		/** @copydoc Physics::createD6Joint */
+		SPtr<D6Joint> createD6Joint() override;
+
+		/** @copydoc Physics::createCharacterController*/
+		SPtr<CharacterController> createCharacterController(const CHAR_CONTROLLER_DESC& desc) override;
+
+		/** @copydoc Physics::rayCast(const Vector3&, const Vector3&, UINT64, float) */
+		bool rayCast(const Vector3& origin, const Vector3& unitDir, PhysicsQueryHit& hit,
+			UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
+
+		/** @copydoc Physics::boxCast */
+		bool boxCast(const AABox& box, const Quaternion& rotation, const Vector3& unitDir, PhysicsQueryHit& hit,
+			UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
+
+		/** @copydoc Physics::sphereCast */
+		bool sphereCast(const Sphere& sphere, const Vector3& unitDir, PhysicsQueryHit& hit,
+			UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
+
+		/** @copydoc Physics::capsuleCast */
+		bool capsuleCast(const Capsule& capsule, const Quaternion& rotation, const Vector3& unitDir,
+			PhysicsQueryHit& hit, UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
+
+		/** @copydoc Physics::convexCast */
+		bool convexCast(const HPhysicsMesh& mesh, const Vector3& position, const Quaternion& rotation,
+			const Vector3& unitDir, PhysicsQueryHit& hit, UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
+
+		/** @copydoc Physics::rayCastAll(const Vector3&, const Vector3&, UINT64, float) */
+		Vector<PhysicsQueryHit> rayCastAll(const Vector3& origin, const Vector3& unitDir,
+			UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
+
+		/** @copydoc Physics::boxCastAll */
+		Vector<PhysicsQueryHit> boxCastAll(const AABox& box, const Quaternion& rotation,
+			const Vector3& unitDir, UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
+
+		/** @copydoc Physics::sphereCastAll */
+		Vector<PhysicsQueryHit> sphereCastAll(const Sphere& sphere, const Vector3& unitDir,
+			UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
+
+		/** @copydoc Physics::capsuleCastAll */
+		Vector<PhysicsQueryHit> capsuleCastAll(const Capsule& capsule, const Quaternion& rotation,
+			const Vector3& unitDir, UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
+
+		/** @copydoc Physics::convexCastAll */
+		Vector<PhysicsQueryHit> convexCastAll(const HPhysicsMesh& mesh, const Vector3& position,
+			const Quaternion& rotation, const Vector3& unitDir, UINT64 layer = BS_ALL_LAYERS, 
+			float max = FLT_MAX) const override;
+
+		/** @copydoc Physics::rayCastAny(const Vector3&, const Vector3&, UINT64, float) */
+		bool rayCastAny(const Vector3& origin, const Vector3& unitDir,
+			UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
+
+		/** @copydoc Physics::boxCastAny */
+		bool boxCastAny(const AABox& box, const Quaternion& rotation, const Vector3& unitDir,
+			UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
+
+		/** @copydoc Physics::sphereCastAny */
+		bool sphereCastAny(const Sphere& sphere, const Vector3& unitDir,
+			UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
+
+		/** @copydoc Physics::capsuleCastAny */
+		bool capsuleCastAny(const Capsule& capsule, const Quaternion& rotation, const Vector3& unitDir,
+			UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
+
+		/** @copydoc Physics::convexCastAny */
+		bool convexCastAny(const HPhysicsMesh& mesh, const Vector3& position, const Quaternion& rotation,
+			const Vector3& unitDir, UINT64 layer = BS_ALL_LAYERS, float max = FLT_MAX) const override;
+
+		/** @copydoc Physics::boxOverlapAny */
+		bool boxOverlapAny(const AABox& box, const Quaternion& rotation, UINT64 layer = BS_ALL_LAYERS) const override;
+
+		/** @copydoc Physics::sphereOverlapAny */
+		bool sphereOverlapAny(const Sphere& sphere, UINT64 layer = BS_ALL_LAYERS) const override;
+
+		/** @copydoc Physics::capsuleOverlapAny */
+		bool capsuleOverlapAny(const Capsule& capsule, const Quaternion& rotation,
+			UINT64 layer = BS_ALL_LAYERS) const override;
+
+		/** @copydoc Physics::convexOverlapAny */
+		bool convexOverlapAny(const HPhysicsMesh& mesh, const Vector3& position, const Quaternion& rotation,
+			UINT64 layer = BS_ALL_LAYERS) const override;
+
+		/** @copydoc Physics::setFlag */
+		void setFlag(PhysicsFlags flags, bool enabled) override;
+
+		/** @copydoc Physics::getGravity */
+		Vector3 getGravity() const override;
+
+		/** @copydoc Physics::setGravity */
+		void setGravity(const Vector3& gravity) override;
+
+		/** @copydoc Physics::getMaxTesselationEdgeLength */
+		float getMaxTesselationEdgeLength() const override { return mTesselationLength; }
+
+		/** @copydoc Physics::setMaxTesselationEdgeLength */
+		void setMaxTesselationEdgeLength(float length) override;
+
+		/** @copydoc Physics::addBroadPhaseRegion */
+		UINT32 addBroadPhaseRegion(const AABox& region) override;
+
+		/** @copydoc Physics::removeBroadPhaseRegion */
+		void removeBroadPhaseRegion(UINT32 regionId) override;
+
+		/** @copydoc Physics::clearBroadPhaseRegions */
+		void clearBroadPhaseRegions() override;
+
+		/** @copydoc Physics::_boxOverlap */
+		Vector<Collider*> _boxOverlap(const AABox& box, const Quaternion& rotation,
+			UINT64 layer = BS_ALL_LAYERS) const override;
+
+		/** @copydoc Physics::_sphereOverlap */
+		Vector<Collider*> _sphereOverlap(const Sphere& sphere, UINT64 layer = BS_ALL_LAYERS) const override;
+
+		/** @copydoc Physics::_capsuleOverlap */
+		Vector<Collider*> _capsuleOverlap(const Capsule& capsule, const Quaternion& rotation,
+			UINT64 layer = BS_ALL_LAYERS) const override;
+
+		/** @copydoc Physics::_convexOverlap */
+		Vector<Collider*> _convexOverlap(const HPhysicsMesh& mesh, const Vector3& position,
+			const Quaternion& rotation, UINT64 layer = BS_ALL_LAYERS) const override;
+
+		/** @copydoc Physics::_rayCast */
+		bool _rayCast(const Vector3& origin, const Vector3& unitDir, const Collider& collider, PhysicsQueryHit& hit, 
+			float maxDist = FLT_MAX) const override;
+
+		/** Triggered by the PhysX simulation when an interaction between two colliders is found. */
+		void _reportContactEvent(const ContactEvent& event);
+
+		/** Triggered by the PhysX simulation when an interaction between two trigger and a collider is found. */
+		void _reportTriggerEvent(const TriggerEvent& event);
+
+		/** Triggered by the PhysX simulation when a joint breaks. */
+		void _reportJointBreakEvent(const JointBreakEvent& event);
+
+		/** Returns the default PhysX material. */
+		physx::PxMaterial* getDefaultMaterial() const { return mDefaultMaterial; }
+
+		/** Returns the main PhysX object. */
+		physx::PxPhysics* getPhysX() const { return mPhysics; }
+
+		/** Returns the main PhysX scene. */
+		physx::PxScene* getScene() const { return mScene; }
+
+		/** Returns the PhysX object used for mesh cooking. */
+		physx::PxCooking* getCooking() const { return mCooking; }
+
+		/** Returns default scale used in the PhysX scene. */
+		physx::PxTolerancesScale getScale() const { return mScale; }
+
+	private:
+		friend class PhysXEventCallback;
+
+		/** Sends out all events recorded during simulation to the necessary physics objects. */
+		void triggerEvents();
+
+		/**
+		 * Helper method that performs a sweep query by checking if the provided geometry hits any physics objects
+		 * when moved along the specified direction. Returns information about the first hit.
+		 */
+		inline bool sweep(const physx::PxGeometry& geometry, const physx::PxTransform& tfrm, const Vector3& unitDir,
+			PhysicsQueryHit& hit, UINT64 layer, float maxDist) const;
+
+		/**
+		 * Helper method that performs a sweep query by checking if the provided geometry hits any physics objects
+		 * when moved along the specified direction. Returns information about all hit.
+		 */
+		inline Vector<PhysicsQueryHit> sweepAll(const physx::PxGeometry& geometry, const physx::PxTransform& tfrm, 
+			const Vector3& unitDir, UINT64 layer, float maxDist) const;
+
+		/**
+		 * Helper method that performs a sweep query by checking if the provided geometry hits any physics objects
+		 * when moved along the specified direction. Returns no information about the hit, but rather just if it happened or
+		 * not.
+		 */
+		inline bool sweepAny(const physx::PxGeometry& geometry, const physx::PxTransform& tfrm, const Vector3& unitDir,
+			UINT64 layer, float maxDist) const;
+
+		/** Helper method that returns all colliders that are overlapping the provided geometry. */
+		inline Vector<Collider*> overlap(const physx::PxGeometry& geometry, const physx::PxTransform& tfrm, 
+			UINT64 layer) const;
+
+		/** Helper method that checks if the provided geometry overlaps any physics object. */
+		inline bool overlapAny(const physx::PxGeometry& geometry, const physx::PxTransform& tfrm, UINT64 layer) const;
+
+		float mSimulationStep = 1.0f/60.0f;
+		float mLastSimulationTime = 0.0f;
+		float mTesselationLength = 3.0f;
+		UINT32 mNextRegionIdx = 1;
+		bool mFirstUpdate = true;
+
+		Vector<TriggerEvent> mTriggerEvents;
+		Vector<ContactEvent> mContactEvents;
+		Vector<JointBreakEvent> mJointBreakEvents;
+		UnorderedMap<UINT32, UINT32> mBroadPhaseRegionHandles;
+
+		physx::PxFoundation* mFoundation = nullptr;
+		physx::PxPhysics* mPhysics = nullptr;
+		physx::PxCooking* mCooking = nullptr;
+		physx::PxScene* mScene = nullptr;
+		physx::PxControllerManager* mCharManager = nullptr;
+
+		physx::PxMaterial* mDefaultMaterial = nullptr;
+		physx::PxTolerancesScale mScale;
+
+		static const UINT32 SCRATCH_BUFFER_SIZE;
+		/** Determines how many physics updates per frame are allowed. Only relevant when framerate is low. */
+		static const UINT32 MAX_ITERATIONS_PER_FRAME;
+	};
+
+	/** Provides easier access to PhysX. */
+	PhysX& gPhysX();
+
+	/** @} */
 }

+ 1 - 1
Source/BansheePhysX/Source/BsFPhysXCollider.cpp

@@ -131,7 +131,7 @@ namespace BansheeEngine
 		else
 			materials[0] = gPhysX().getDefaultMaterial();
 
-		mShape->setMaterials(materials, sizeof(materials));
+		mShape->setMaterials(materials, sizeof(materials) / sizeof(materials[0]));
 	}
 
 	UINT64 FPhysXCollider::getLayer() const

+ 1170 - 1150
Source/BansheePhysX/Source/BsPhysX.cpp

@@ -1,1151 +1,1171 @@
-#include "BsPhysX.h"
-#include "PxPhysicsAPI.h"
-#include "BsPhysXMaterial.h"
-#include "BsPhysXMesh.h"
-#include "BsPhysXRigidbody.h"
-#include "BsPhysXBoxCollider.h"
-#include "BsPhysXSphereCollider.h"
-#include "BsPhysXPlaneCollider.h"
-#include "BsPhysXCapsuleCollider.h"
-#include "BsPhysXMeshCollider.h"
-#include "BsPhysXFixedJoint.h"
-#include "BsPhysXDistanceJoint.h"
-#include "BsPhysXHingeJoint.h"
-#include "BsPhysXSphericalJoint.h"
-#include "BsPhysXSliderJoint.h"
-#include "BsPhysXD6Joint.h"
-#include "BsPhysXCharacterController.h"
-#include "BsTaskScheduler.h"
-#include "BsCCollider.h"
-#include "BsFPhysXCollider.h"
-#include "BsTime.h"
-#include "Bsvector3.h"
-#include "BsAABox.h"
-#include "BsCapsule.h"
-#include "foundation\PxTransform.h"
-
-using namespace physx;
-
-namespace BansheeEngine
-{
-	class PhysXAllocator : public PxAllocatorCallback
-	{
-	public:
-		void* allocate(size_t size, const char*, const char*, int) override
-		{
-			void* ptr = bs_alloc_aligned16((UINT32)size);
-			PX_ASSERT((reinterpret_cast<size_t>(ptr) & 15) == 0);
-			return ptr;
-		}
-
-		void deallocate(void* ptr) override
-		{
-			bs_free_aligned16(ptr);
-		}
-	};
-
-	class PhysXErrorCallback : public PxErrorCallback
-	{
-	public:
-		void reportError(PxErrorCode::Enum code, const char* message, const char* file, int line) override
-		{
-			{
-				const char* errorCode = nullptr;
-
-				UINT32 severity = 0;
-				switch (code)
-				{
-				case PxErrorCode::eNO_ERROR:
-					errorCode = "No error";
-					break;
-				case PxErrorCode::eINVALID_PARAMETER:
-					errorCode = "Invalid parameter";
-					severity = 2;
-					break;
-				case PxErrorCode::eINVALID_OPERATION:
-					errorCode = "Invalid operation";
-					severity = 2;
-					break;
-				case PxErrorCode::eOUT_OF_MEMORY:
-					errorCode = "Out of memory";
-					severity = 2;
-					break;
-				case PxErrorCode::eDEBUG_INFO:
-					errorCode = "Info";
-					break;
-				case PxErrorCode::eDEBUG_WARNING:
-					errorCode = "Warning";
-					severity = 1;
-					break;
-				case PxErrorCode::ePERF_WARNING:
-					errorCode = "Performance warning";
-					severity = 1;
-					break;
-				case PxErrorCode::eABORT:
-					errorCode = "Abort";
-					severity = 2;
-					break;
-				case PxErrorCode::eINTERNAL_ERROR:
-					errorCode = "Internal error";
-					severity = 2;
-					break;
-				case PxErrorCode::eMASK_ALL:
-				default:
-					errorCode = "Unknown error";
-					severity = 2;
-					break;
-				}
-
-				StringStream ss;
-
-				switch(severity)
-				{
-				case 0:
-					ss << "PhysX info (" << errorCode << "): " << message << " at " << file << ":" << line;
-					LOGDBG(ss.str());
-					break;
-				case 1:
-					ss << "PhysX warning (" << errorCode << "): " << message << " at " << file << ":" << line;
-					LOGWRN(ss.str());
-					break;
-				case 2:
-					ss << "PhysX error (" << errorCode << "): " << message << " at " << file << ":" << line;
-					LOGERR(ss.str());
-					BS_ASSERT(false); // Halt execution on debug builds when error occurrs
-					break;
-				}
-			}
-		}
-	};
-
-	class PhysXEventCallback : public PxSimulationEventCallback
-	{
-		void onWake(PxActor** actors, PxU32 count) override { /* Do nothing */ }
-		void onSleep(PxActor** actors, PxU32 count) override { /* Do nothing */ }
-
-		void onTrigger(PxTriggerPair* pairs, PxU32 count) override
-		{
-			for (PxU32 i = 0; i < count; i++)
-			{
-				const PxTriggerPair& pair = pairs[i];
-				
-				PhysX::ContactEventType type;
-				bool ignoreContact = false;
-				PhysXObjectFilterFlags flags = PhysXObjectFilterFlags(pair.triggerShape->getSimulationFilterData().word2);
-
-				if (flags.isSet(PhysXObjectFilterFlag::ReportAll))
-				{
-					switch ((UINT32)pair.status)
-					{
-					case PxPairFlag::eNOTIFY_TOUCH_FOUND:
-						type = PhysX::ContactEventType::ContactBegin;
-						break;
-					case PxPairFlag::eNOTIFY_TOUCH_PERSISTS:
-						type = PhysX::ContactEventType::ContactStay;
-						break;
-					case PxPairFlag::eNOTIFY_TOUCH_LOST:
-						type = PhysX::ContactEventType::ContactEnd;
-						break;
-					default:
-						ignoreContact = true;
-						break;
-					}
-				}
-				else if (flags.isSet(PhysXObjectFilterFlag::ReportBasic))
-				{
-					switch ((UINT32)pair.status)
-					{
-					case PxPairFlag::eNOTIFY_TOUCH_FOUND:
-						type = PhysX::ContactEventType::ContactBegin;
-						break;
-					case PxPairFlag::eNOTIFY_TOUCH_LOST:
-						type = PhysX::ContactEventType::ContactEnd;
-						break;
-					default:
-						ignoreContact = true;
-						break;
-					}
-				}
-				else
-					ignoreContact = true;
-
-				if (ignoreContact)
-					continue;
-
-				PhysX::TriggerEvent event;
-				event.trigger = (Collider*)pair.triggerShape->userData;
-				event.other = (Collider*)pair.otherShape->userData;
-				event.type = type;
-
-				gPhysX()._reportTriggerEvent(event);
-			}
-		}
-
-		void onContact(const PxContactPairHeader& pairHeader, const PxContactPair* pairs, PxU32 count) override
-		{
-			for (PxU32 i = 0; i < count; i++)
-			{
-				const PxContactPair& pair = pairs[i];
-
-				PhysX::ContactEventType type;
-				bool ignoreContact = false;
-				switch((UINT32)pair.events)
-				{
-				case PxPairFlag::eNOTIFY_TOUCH_FOUND:
-					type = PhysX::ContactEventType::ContactBegin;
-					break;
-				case PxPairFlag::eNOTIFY_TOUCH_PERSISTS:
-					type = PhysX::ContactEventType::ContactStay;
-					break;
-				case PxPairFlag::eNOTIFY_TOUCH_LOST:
-					type = PhysX::ContactEventType::ContactEnd;
-					break;
-				default:
-					ignoreContact = true;
-					break;
-				}
-
-				if (ignoreContact)
-					continue;
-
-				PhysX::ContactEvent event;
-				event.type = type;
-
-				PxU32 contactCount = pair.contactCount;
-				const PxU8* stream = pair.contactStream;
-				PxU16 streamSize = pair.contactStreamSize;
-
-				if (contactCount > 0 && streamSize > 0)
-				{
-					PxU32 contactIdx = 0;
-					PxContactStreamIterator iter((PxU8*)stream, streamSize);
-
-					stream += ((streamSize + 15) & ~15);
-
-					const PxReal* impulses = reinterpret_cast<const PxReal*>(stream);
-					PxU32 hasImpulses = (pair.flags & PxContactPairFlag::eINTERNAL_HAS_IMPULSES);
-
-					while (iter.hasNextPatch())
-					{
-						iter.nextPatch();
-						while (iter.hasNextContact())
-						{
-							iter.nextContact();
-
-							ContactPoint point;
-							point.position = fromPxVector(iter.getContactPoint());
-							point.separation = iter.getSeparation();
-							point.normal = fromPxVector(iter.getContactNormal());
-
-							if (hasImpulses)
-								point.impulse = impulses[contactIdx];
-							else
-								point.impulse = 0.0f;
-
-							event.points.push_back(point);
-
-							contactIdx++;
-						}
-					}
-				}
-
-				event.colliderA = (Collider*)pair.shapes[0]->userData;
-				event.colliderB = (Collider*)pair.shapes[1]->userData;
-
-				gPhysX()._reportContactEvent(event);
-			}
-		}
-
-		void onConstraintBreak(PxConstraintInfo* constraints, PxU32 count) override 
-		{ 
-			for (UINT32 i = 0; i < count; i++)
-			{
-				PxConstraintInfo& constraintInfo = constraints[i];
-
-				if (constraintInfo.type != PxConstraintExtIDs::eJOINT)
-					continue;
-
-				PxJoint* pxJoint = (PxJoint*)constraintInfo.externalReference;
-				Joint* joint = (Joint*)pxJoint->userData;
-
-				
-			}
-		}
-	};
-
-	class PhysXCPUDispatcher : public PxCpuDispatcher
-	{
-	public:
-		void submitTask(PxBaseTask& physxTask) override
-		{
-			// Note: Banshee's task scheduler is pretty low granularity. Consider a better task manager in case PhysX ends
-			// up submitting many tasks.
-			// - PhysX's task manager doesn't seem much lighter either. But perhaps I can at least create a task pool to 
-			//   avoid allocating them constantly.
-
-			auto runTask = [&]() { physxTask.run(); physxTask.release(); };
-			TaskPtr task = Task::create("PhysX", runTask);
-
-			TaskScheduler::instance().addTask(task);
-		}
-
-		PxU32 getWorkerCount() const override
-		{
-			return (PxU32)TaskScheduler::instance().getNumWorkers();
-		}
-	};
-
-	class PhysXBroadPhaseCallback : public PxBroadPhaseCallback
-	{
-		void onObjectOutOfBounds(PxShape& shape, PxActor& actor) override
-		{
-			Collider* collider = (Collider*)shape.userData;
-			if (collider != nullptr)
-				LOGWRN("Physics object out of bounds. Consider increasing broadphase region!");
-		}
-
-		void onObjectOutOfBounds(PxAggregate& aggregate) override { /* Do nothing */ }
-	};
-
-	PxFilterFlags PhysXFilterShader(PxFilterObjectAttributes attr0, PxFilterData data0, PxFilterObjectAttributes attr1, 
-		PxFilterData data1, PxPairFlags& pairFlags, const void* constantBlock, PxU32 constantBlockSize)
-	{
-		PhysXObjectFilterFlags flags0 = PhysXObjectFilterFlags(data0.word2);
-		PhysXObjectFilterFlags flags1 = PhysXObjectFilterFlags(data1.word2);
-
-		if (flags0.isSet(PhysXObjectFilterFlag::ReportAll) || flags1.isSet(PhysXObjectFilterFlag::ReportAll))
-			pairFlags |= PxPairFlag::eNOTIFY_TOUCH_FOUND | PxPairFlag::eNOTIFY_TOUCH_LOST | PxPairFlag::eNOTIFY_TOUCH_PERSISTS;
-		else if (flags0.isSet(PhysXObjectFilterFlag::ReportBasic) || flags1.isSet(PhysXObjectFilterFlag::ReportBasic))
-			pairFlags |= PxPairFlag::eNOTIFY_TOUCH_FOUND | PxPairFlag::eNOTIFY_TOUCH_LOST;
-
-		if (PxFilterObjectIsTrigger(attr0) || PxFilterObjectIsTrigger(attr1))
-		{
-			pairFlags |= PxPairFlag::eDETECT_DISCRETE_CONTACT;
-			return PxFilterFlags();
-		}
-
-		UINT64 groupA = *(UINT64*)&data0.word0;
-		UINT64 groupB = *(UINT64*)&data1.word0;
-
-		bool canCollide = gPhysics().isCollisionEnabled(groupA, groupB);
-		if (!canCollide)
-			return PxFilterFlag::eSUPPRESS;
-
-		if (flags0.isSet(PhysXObjectFilterFlag::CCD) || flags1.isSet(PhysXObjectFilterFlag::CCD))
-			pairFlags |= PxPairFlag::eDETECT_CCD_CONTACT;
-
-		pairFlags |= PxPairFlag::eSOLVE_CONTACT | PxPairFlag::eDETECT_DISCRETE_CONTACT;
-		return PxFilterFlags();
-	}
-
-	void parseHit(const PxRaycastHit& input, PhysicsQueryHit& output)
-	{
-		output.point = fromPxVector(input.position);
-		output.normal = fromPxVector(input.normal);
-		output.distance = input.distance;
-		output.triangleIdx = input.faceIndex;
-		output.uv = Vector2(input.u, input.v);
-		output.colliderRaw = (Collider*)input.shape->userData;
-
-		if (output.colliderRaw != nullptr)
-		{
-			CCollider* component = (CCollider*)output.colliderRaw->_getOwner(PhysicsOwnerType::Component);
-			if (component != nullptr)
-				output.collider = component->getHandle();
-		}
-	}
-
-	void parseHit(const PxSweepHit& input, PhysicsQueryHit& output)
-	{
-		output.point = fromPxVector(input.position);
-		output.normal = fromPxVector(input.normal);
-		output.distance = input.distance;
-		output.triangleIdx = input.faceIndex;
-		output.colliderRaw = (Collider*)input.shape->userData;
-
-		if (output.colliderRaw != nullptr)
-		{
-			CCollider* component = (CCollider*)output.colliderRaw->_getOwner(PhysicsOwnerType::Component);
-			if (component != nullptr)
-				output.collider = component->getHandle();
-		}
-	}
-
-	struct PhysXRaycastQueryCallback : PxRaycastCallback
-	{
-		Vector<PhysicsQueryHit> data;
-
-		PhysXRaycastQueryCallback()
-			:PxRaycastCallback(nullptr, 0)
-		{ }
-
-		PxAgain processTouches(const PxRaycastHit* buffer, PxU32 nbHits) override
-		{
-			for (PxU32 i = 0; i < nbHits; i++)
-			{
-				data.push_back(PhysicsQueryHit());
-				parseHit(buffer[i], data.back());
-			}
-
-			return true;
-		}
-	};
-
-	struct PhysXSweepQueryCallback : PxSweepCallback
-	{
-		Vector<PhysicsQueryHit> data;
-
-		PhysXSweepQueryCallback()
-			:PxSweepCallback(nullptr, 0)
-		{ }
-
-		PxAgain processTouches(const PxSweepHit* buffer, PxU32 nbHits) override
-		{
-			for (PxU32 i = 0; i < nbHits; i++)
-			{
-				data.push_back(PhysicsQueryHit());
-				parseHit(buffer[i], data.back());
-			}
-
-			return true;
-		}
-	};
-
-	struct PhysXOverlapQueryCallback : PxOverlapCallback
-	{
-		Vector<Collider*> data;
-
-		PhysXOverlapQueryCallback()
-			:PxOverlapCallback(nullptr, 0)
-		{ }
-
-		PxAgain processTouches(const PxOverlapHit* buffer, PxU32 nbHits) override
-		{
-			for (PxU32 i = 0; i < nbHits; i++)
-				data.push_back((Collider*)buffer[i].shape->userData);
-
-			return true;
-		}
-	};
-
-	static PhysXAllocator gPhysXAllocator;
-	static PhysXErrorCallback gPhysXErrorHandler;
-	static PhysXCPUDispatcher gPhysXCPUDispatcher;
-	static PhysXEventCallback gPhysXEventCallback;
-	static PhysXBroadPhaseCallback gPhysXBroadphaseCallback;
-
-	static const UINT32 SIZE_16K = 1 << 14;
-	const UINT32 PhysX::SCRATCH_BUFFER_SIZE = SIZE_16K * 64; // 1MB by default
-
-	PhysX::PhysX(const PHYSICS_INIT_DESC& input)
-		:Physics(input)
-	{
-		mScale.length = input.typicalLength;
-		mScale.speed = input.typicalSpeed;
-
-		mFoundation = PxCreateFoundation(PX_PHYSICS_VERSION, gPhysXAllocator, gPhysXErrorHandler);
-		mPhysics = PxCreateBasePhysics(PX_PHYSICS_VERSION, *mFoundation, mScale);
-
-		PxRegisterArticulations(*mPhysics);
-
-		if (input.initCooking)
-		{
-			// Note: PhysX supports cooking for specific platforms to make the generated results better. Consider
-			// allowing the meshes to be re-cooked when target platform is changed. Right now we just use the default value.
-
-			PxCookingParams cookingParams(mScale);
-			mCooking = PxCreateCooking(PX_PHYSICS_VERSION, *mFoundation, cookingParams);
-		}
-
-		PxSceneDesc sceneDesc(mScale); // TODO - Test out various other parameters provided by scene desc
-		sceneDesc.gravity = toPxVector(input.gravity);
-		sceneDesc.cpuDispatcher = &gPhysXCPUDispatcher;
-		sceneDesc.filterShader = PhysXFilterShader;
-		sceneDesc.simulationEventCallback = &gPhysXEventCallback;
-		sceneDesc.broadPhaseCallback = &gPhysXBroadphaseCallback;
-
-		// Optionally: eENABLE_KINEMATIC_STATIC_PAIRS, eENABLE_KINEMATIC_PAIRS, eENABLE_PCM
-		sceneDesc.flags = PxSceneFlag::eENABLE_ACTIVETRANSFORMS;
-
-		if (input.flags.isSet(PhysicsFlag::CCD_Enable))
-			sceneDesc.flags |= PxSceneFlag::eENABLE_CCD;
-
-		// Optionally: eMBP
-		sceneDesc.broadPhaseType = PxBroadPhaseType::eSAP;
-
-		mScene = mPhysics->createScene(sceneDesc);
-
-		// Character controller
-		mCharManager = PxCreateControllerManager(*mScene);
-
-		mSimulationStep = input.timeStep;
-		mDefaultMaterial = mPhysics->createMaterial(0.0f, 0.0f, 0.0f);
-	}
-
-	PhysX::~PhysX()
-	{
-		mCharManager->release();
-		mScene->release();
-
-		if (mCooking != nullptr)
-			mCooking->release();
-
-		mPhysics->release();
-		mFoundation->release();
-	}
-
-	void PhysX::update()
-	{
-		mUpdateInProgress = true;
-
-		float nextFrameTime = mLastSimulationTime + mSimulationStep;
-		float curFrameTime = gTime().getTime();
-		if(curFrameTime < nextFrameTime)
-		{
-			// TODO - Interpolate rigidbodies but perform no actual simulation
-
-			return;
-		}
-
-		float simulationAmount = curFrameTime - mLastSimulationTime;
-		while (simulationAmount >= mSimulationStep) // In case we're running really slow multiple updates might be needed
-		{
-			// Note: Consider delaying fetchResults one frame. This could improve performance because Physics update would be
-			//       able to run parallel to the simulation thread, but at a cost to input latency.
-
-			bs_frame_mark();
-			UINT8* scratchBuffer = bs_frame_alloc_aligned(SCRATCH_BUFFER_SIZE, 16);
-
-			mScene->simulate(mSimulationStep, nullptr, scratchBuffer, SCRATCH_BUFFER_SIZE);
-			simulationAmount -= mSimulationStep;
-
-			UINT32 errorState;
-			if(!mScene->fetchResults(true, &errorState))
-			{
-				LOGWRN("Physics simualtion failed. Error code: " + toString(errorState));
-
-				bs_frame_free_aligned(scratchBuffer);
-				bs_frame_clear();
-				continue;
-			}
-
-			bs_frame_free_aligned(scratchBuffer);
-			bs_frame_clear();
-
-			// Update rigidbodies with new transforms
-			PxU32 numActiveTransforms;
-			const PxActiveTransform* activeTransforms = mScene->getActiveTransforms(numActiveTransforms);
-
-			for (PxU32 i = 0; i < numActiveTransforms; i++)
-			{
-				Rigidbody* rigidbody = static_cast<Rigidbody*>(activeTransforms[i].userData);
-				const PxTransform& transform = activeTransforms[i].actor2World;
-
-				// Note: Make this faster, avoid dereferencing Rigidbody and attempt to access pos/rot destination directly,
-				//       use non-temporal writes
-				rigidbody->_setTransform(fromPxVector(transform.p), fromPxQuaternion(transform.q));
-			}
-		}
-
-		// TODO - Consider extrapolating for the remaining "simulationAmount" value
-
-		mLastSimulationTime = curFrameTime; 
-		mUpdateInProgress = false;
-
-		triggerEvents();
-	}
-
-	void PhysX::_reportContactEvent(const ContactEvent& event)
-	{
-		mContactEvents.push_back(event);
-	}
-
-	void PhysX::_reportTriggerEvent(const TriggerEvent& event)
-	{
-		mTriggerEvents.push_back(event);
-	}
-
-	void PhysX::_reportJointBreakEvent(const JointBreakEvent& event)
-	{
-		mJointBreakEvents.push_back(event);
-	}
-
-	void PhysX::triggerEvents()
-	{
-		CollisionData data;
-
-		for(auto& entry : mTriggerEvents)
-		{
-			data.collidersRaw[0] = entry.trigger;
-			data.collidersRaw[1] = entry.other;
-
-			switch (entry.type)
-			{
-			case ContactEventType::ContactBegin:
-				entry.trigger->onCollisionBegin(data);
-				break;
-			case ContactEventType::ContactStay:
-				entry.trigger->onCollisionStay(data);
-				break;
-			case ContactEventType::ContactEnd:
-				entry.trigger->onCollisionEnd(data);
-				break;
-			}
-		}
-
-		auto notifyContact = [&](Collider* obj, Collider* other, ContactEventType type, 
-			const Vector<ContactPoint>& points, bool flipNormals = false)
-		{
-			data.collidersRaw[0] = obj;
-			data.collidersRaw[1] = other;
-			data.contactPoints = points;
-
-			if(flipNormals)
-			{
-				for (auto& point : data.contactPoints)
-					point.normal = -point.normal;
-			}
-
-			Rigidbody* rigidbody = obj->getRigidbody();
-			if(rigidbody != nullptr)
-			{
-				switch (type)
-				{
-				case ContactEventType::ContactBegin:
-					rigidbody->onCollisionBegin(data);
-					break;
-				case ContactEventType::ContactStay:
-					rigidbody->onCollisionStay(data);
-					break;
-				case ContactEventType::ContactEnd:
-					rigidbody->onCollisionEnd(data);
-					break;
-				}
-			}
-			else
-			{
-				switch (type)
-				{
-				case ContactEventType::ContactBegin:
-					obj->onCollisionBegin(data);
-					break;
-				case ContactEventType::ContactStay:
-					obj->onCollisionStay(data);
-					break;
-				case ContactEventType::ContactEnd:
-					obj->onCollisionEnd(data);
-					break;
-				}
-			}
-		};
-
-		for (auto& entry : mContactEvents)
-		{
-			CollisionReportMode reportModeA = entry.colliderA->getCollisionReportMode();
-
-			if (reportModeA == CollisionReportMode::ReportPersistent)
-				notifyContact(entry.colliderA, entry.colliderB, entry.type, entry.points, true);
-			else if (reportModeA == CollisionReportMode::Report && entry.type != ContactEventType::ContactStay)
-				notifyContact(entry.colliderA, entry.colliderB, entry.type, entry.points, true);
-
-			CollisionReportMode reportModeB = entry.colliderB->getCollisionReportMode();
-
-			if (reportModeB == CollisionReportMode::ReportPersistent)
-				notifyContact(entry.colliderB, entry.colliderA, entry.type, entry.points, false);
-			else if (reportModeB == CollisionReportMode::Report && entry.type != ContactEventType::ContactStay)
-				notifyContact(entry.colliderB, entry.colliderA, entry.type, entry.points, false);
-		}
-
-		for(auto& entry : mJointBreakEvents)
-		{
-			entry.joint->onJointBreak();
-		}
-
-		mTriggerEvents.clear();
-		mContactEvents.clear();
-		mJointBreakEvents.clear();
-	}
-
-	SPtr<PhysicsMaterial> PhysX::createMaterial(float staticFriction, float dynamicFriction, float restitution)
-	{
-		return bs_shared_ptr_new<PhysXMaterial>(mPhysics, staticFriction, dynamicFriction, restitution);
-	}
-
-	SPtr<PhysicsMesh> PhysX::createMesh(const MeshDataPtr& meshData, PhysicsMeshType type)
-	{
-		return bs_shared_ptr_new<PhysXMesh>(meshData, type);
-	}
-
-	SPtr<Rigidbody> PhysX::createRigidbody(const HSceneObject& linkedSO)
-	{
-		return bs_shared_ptr_new<PhysXRigidbody>(mPhysics, mScene, linkedSO);
-	}
-
-	SPtr<BoxCollider> PhysX::createBoxCollider(const Vector3& extents, const Vector3& position,
-		const Quaternion& rotation)
-	{
-		return bs_shared_ptr_new<PhysXBoxCollider>(mPhysics, position, rotation, extents);
-	}
-
-	SPtr<SphereCollider> PhysX::createSphereCollider(float radius, const Vector3& position, const Quaternion& rotation)
-	{
-		return bs_shared_ptr_new<PhysXSphereCollider>(mPhysics, position, rotation, radius);
-	}
-
-	SPtr<PlaneCollider> PhysX::createPlaneCollider(const Vector3& position, const Quaternion& rotation)
-	{
-		return bs_shared_ptr_new<PhysXPlaneCollider>(mPhysics, position, rotation);
-	}
-
-	SPtr<CapsuleCollider> PhysX::createCapsuleCollider(float radius, float halfHeight, const Vector3& position, 
-		const Quaternion& rotation)
-	{
-		return bs_shared_ptr_new<PhysXCapsuleCollider>(mPhysics, position, rotation, radius, halfHeight);
-	}
-
-	SPtr<MeshCollider> PhysX::createMeshCollider(const Vector3& position, const Quaternion& rotation)
-	{
-		return bs_shared_ptr_new<PhysXMeshCollider>(mPhysics, position, rotation);
-	}
-
-	SPtr<FixedJoint> PhysX::createFixedJoint()
-	{
-		return bs_shared_ptr_new<PhysXFixedJoint>(mPhysics);
-	}
-
-	SPtr<DistanceJoint> PhysX::createDistanceJoint()
-	{
-		return bs_shared_ptr_new<PhysXDistanceJoint>(mPhysics);
-	}
-
-	SPtr<HingeJoint> PhysX::createHingeJoint()
-	{
-		return bs_shared_ptr_new<PhysXHingeJoint>(mPhysics);
-	}
-
-	SPtr<SphericalJoint> PhysX::createSphericalJoint()
-	{
-		return bs_shared_ptr_new<PhysXSphericalJoint>(mPhysics);
-	}
-
-	SPtr<SliderJoint> PhysX::createSliderJoint()
-	{
-		return bs_shared_ptr_new<PhysXSliderJoint>(mPhysics);
-	}
-
-	SPtr<D6Joint> PhysX::createD6Joint()
-	{
-		return bs_shared_ptr_new<PhysXD6Joint>(mPhysics);
-	}
-
-	SPtr<CharacterController> PhysX::createCharacterController(const CHAR_CONTROLLER_DESC& desc)
-	{
-		return bs_shared_ptr_new<PhysXCharacterController>(mCharManager, desc);
-	}
-
-	Vector<PhysicsQueryHit> PhysX::sweepAll(const PxGeometry& geometry, const PxTransform& tfrm, const Vector3& unitDir,
-		UINT64 layer, float maxDist) const
-	{
-		PhysXSweepQueryCallback output;
-
-		PxQueryFilterData filterData;
-		memcpy(&filterData.data.word0, &layer, sizeof(layer));
-
-		mScene->sweep(geometry, tfrm, toPxVector(unitDir), maxDist, output,
-			PxHitFlag::eDEFAULT | PxHitFlag::eUV, filterData);
-
-		return output.data;
-	}
-
-	bool PhysX::sweepAny(const PxGeometry& geometry, const PxTransform& tfrm, const Vector3& unitDir, UINT64 layer, 
-		float maxDist) const
-	{
-		PxSweepBuffer output;
-
-		PxQueryFilterData filterData;
-		filterData.flags |= PxQueryFlag::eANY_HIT;
-		memcpy(&filterData.data.word0, &layer, sizeof(layer));
-
-		return mScene->sweep(geometry, tfrm, toPxVector(unitDir), maxDist, output, 
-			PxHitFlag::eDEFAULT | PxHitFlag::eUV | PxHitFlag::eMESH_ANY, filterData);
-	}
-
-	bool PhysX::rayCast(const Vector3& origin, const Vector3& unitDir, PhysicsQueryHit& hit, UINT64 layer, float max) const
-	{
-		PxRaycastBuffer output;
-
-		PxQueryFilterData filterData;
-		memcpy(&filterData.data.word0, &layer, sizeof(layer));
-
-		bool wasHit = mScene->raycast(toPxVector(origin),
-			toPxVector(unitDir), max, output, PxHitFlag::eDEFAULT | PxHitFlag::eUV, filterData);
-
-		if (wasHit)
-			parseHit(output.block, hit);
-
-		return wasHit;
-	}
-
-	bool PhysX::boxCast(const AABox& box, const Quaternion& rotation, const Vector3& unitDir, PhysicsQueryHit& hit,
-		UINT64 layer, float max) const
-	{
-		PxBoxGeometry geometry(toPxVector(box.getHalfSize()));
-		PxTransform transform = toPxTransform(box.getCenter(), rotation);
-
-		return sweep(geometry, transform, unitDir, hit, layer, max);
-	}
-
-	bool PhysX::sphereCast(const Sphere& sphere, const Vector3& unitDir, PhysicsQueryHit& hit,
-		UINT64 layer, float max) const
-	{
-		PxSphereGeometry geometry(sphere.getRadius());
-		PxTransform transform = toPxTransform(sphere.getCenter(), Quaternion::IDENTITY);
-
-		return sweep(geometry, transform, unitDir, hit, layer, max);
-	}
-
-	bool PhysX::capsuleCast(const Capsule& capsule, const Quaternion& rotation, const Vector3& unitDir,
-		PhysicsQueryHit& hit, UINT64 layer, float max) const
-	{
-		PxCapsuleGeometry geometry(capsule.getRadius(), capsule.getHeight() * 0.5f);
-		PxTransform transform = toPxTransform(capsule.getCenter(), Quaternion::IDENTITY);
-
-		return sweep(geometry, transform, unitDir, hit, layer, max);
-	}
-
-	bool PhysX::convexCast(const HPhysicsMesh& mesh, const Vector3& position, const Quaternion& rotation,
-		const Vector3& unitDir, PhysicsQueryHit& hit, UINT64 layer, float max) const
-	{
-		if (mesh == nullptr)
-			return false;
-
-		PhysXMesh* physxMesh = static_cast<PhysXMesh*>(mesh.get());
-		if (physxMesh->getType() != PhysicsMeshType::Convex)
-			return false;
-
-		PxConvexMeshGeometry geometry(physxMesh->_getConvex());
-		PxTransform transform = toPxTransform(position, rotation);
-
-		return sweep(geometry, transform, unitDir, hit, layer, max);
-	}
-
-	Vector<PhysicsQueryHit> PhysX::rayCastAll(const Vector3& origin, const Vector3& unitDir,
-		UINT64 layer, float max) const
-	{
-		PhysXRaycastQueryCallback output;
-
-		PxQueryFilterData filterData;
-		memcpy(&filterData.data.word0, &layer, sizeof(layer));
-
-		mScene->raycast(toPxVector(origin), toPxVector(unitDir), max, output,
-			PxHitFlag::eDEFAULT | PxHitFlag::eUV | PxHitFlag::eMESH_MULTIPLE, filterData);
-
-		return output.data;
-	}
-
-	Vector<PhysicsQueryHit> PhysX::boxCastAll(const AABox& box, const Quaternion& rotation,
-		const Vector3& unitDir, UINT64 layer, float max) const
-	{
-		PxBoxGeometry geometry(toPxVector(box.getHalfSize()));
-		PxTransform transform = toPxTransform(box.getCenter(), rotation);
-
-		return sweepAll(geometry, transform, unitDir, layer, max);
-	}
-
-	Vector<PhysicsQueryHit> PhysX::sphereCastAll(const Sphere& sphere, const Vector3& unitDir,
-		UINT64 layer, float max) const
-	{
-		PxSphereGeometry geometry(sphere.getRadius());
-		PxTransform transform = toPxTransform(sphere.getCenter(), Quaternion::IDENTITY);
-
-		return sweepAll(geometry, transform, unitDir, layer, max);
-	}
-
-	Vector<PhysicsQueryHit> PhysX::capsuleCastAll(const Capsule& capsule, const Quaternion& rotation,
-		const Vector3& unitDir, UINT64 layer, float max) const
-	{
-		PxCapsuleGeometry geometry(capsule.getRadius(), capsule.getHeight() * 0.5f);
-		PxTransform transform = toPxTransform(capsule.getCenter(), Quaternion::IDENTITY);
-
-		return sweepAll(geometry, transform, unitDir, layer, max);
-	}
-
-	Vector<PhysicsQueryHit> PhysX::convexCastAll(const HPhysicsMesh& mesh, const Vector3& position,
-		const Quaternion& rotation, const Vector3& unitDir, UINT64 layer, float max) const
-	{
-		if (mesh == nullptr)
-			return Vector<PhysicsQueryHit>(0);
-
-		PhysXMesh* physxMesh = static_cast<PhysXMesh*>(mesh.get());
-		if (physxMesh->getType() != PhysicsMeshType::Convex)
-			return Vector<PhysicsQueryHit>(0);
-
-		PxConvexMeshGeometry geometry(physxMesh->_getConvex());
-		PxTransform transform = toPxTransform(position, rotation);
-
-		return sweepAll(geometry, transform, unitDir, layer, max);
-	}
-
-	bool PhysX::rayCastAny(const Vector3& origin, const Vector3& unitDir,
-		UINT64 layer, float max) const
-	{
-		PxRaycastBuffer output;
-
-		PxQueryFilterData filterData;
-		filterData.flags |= PxQueryFlag::eANY_HIT;
-		memcpy(&filterData.data.word0, &layer, sizeof(layer));
-
-		return mScene->raycast(toPxVector(origin),
-			toPxVector(unitDir), max, output, PxHitFlag::eDEFAULT | PxHitFlag::eUV | PxHitFlag::eMESH_ANY, filterData);
-	}
-
-	bool PhysX::boxCastAny(const AABox& box, const Quaternion& rotation, const Vector3& unitDir,
-		UINT64 layer, float max) const
-	{
-		PxBoxGeometry geometry(toPxVector(box.getHalfSize()));
-		PxTransform transform = toPxTransform(box.getCenter(), rotation);
-
-		return sweepAny(geometry, transform, unitDir, layer, max);
-	}
-
-	bool PhysX::sphereCastAny(const Sphere& sphere, const Vector3& unitDir,
-		UINT64 layer, float max) const
-	{
-		PxSphereGeometry geometry(sphere.getRadius());
-		PxTransform transform = toPxTransform(sphere.getCenter(), Quaternion::IDENTITY);
-
-		return sweepAny(geometry, transform, unitDir, layer, max);
-	}
-
-	bool PhysX::capsuleCastAny(const Capsule& capsule, const Quaternion& rotation, const Vector3& unitDir,
-		UINT64 layer, float max) const
-	{
-		PxCapsuleGeometry geometry(capsule.getRadius(), capsule.getHeight() * 0.5f);
-		PxTransform transform = toPxTransform(capsule.getCenter(), Quaternion::IDENTITY);
-
-		return sweepAny(geometry, transform, unitDir, layer, max);
-	}
-
-	bool PhysX::convexCastAny(const HPhysicsMesh& mesh, const Vector3& position, const Quaternion& rotation,
-		const Vector3& unitDir, UINT64 layer, float max) const
-	{
-		if (mesh == nullptr)
-			return false;
-
-		PhysXMesh* physxMesh = static_cast<PhysXMesh*>(mesh.get());
-		if (physxMesh->getType() != PhysicsMeshType::Convex)
-			return false;
-
-		PxConvexMeshGeometry geometry(physxMesh->_getConvex());
-		PxTransform transform = toPxTransform(position, rotation);
-
-		return sweepAny(geometry, transform, unitDir, layer, max);
-	}
-
-	Vector<Collider*> PhysX::_boxOverlap(const AABox& box, const Quaternion& rotation,
-		UINT64 layer) const
-	{
-		PxBoxGeometry geometry(toPxVector(box.getHalfSize()));
-		PxTransform transform = toPxTransform(box.getCenter(), rotation);
-
-		return overlap(geometry, transform, layer);
-	}
-
-	Vector<Collider*> PhysX::_sphereOverlap(const Sphere& sphere, UINT64 layer) const
-	{
-		PxSphereGeometry geometry(sphere.getRadius());
-		PxTransform transform = toPxTransform(sphere.getCenter(), Quaternion::IDENTITY);
-
-		return overlap(geometry, transform, layer);
-	}
-
-	Vector<Collider*> PhysX::_capsuleOverlap(const Capsule& capsule, const Quaternion& rotation,
-		UINT64 layer) const
-	{
-		PxCapsuleGeometry geometry(capsule.getRadius(), capsule.getHeight() * 0.5f);
-		PxTransform transform = toPxTransform(capsule.getCenter(), Quaternion::IDENTITY);
-
-		return overlap(geometry, transform, layer);
-	}
-
-	Vector<Collider*> PhysX::_convexOverlap(const HPhysicsMesh& mesh, const Vector3& position,
-		const Quaternion& rotation, UINT64 layer) const
-	{
-		if (mesh == nullptr)
-			return Vector<Collider*>(0);
-
-		PhysXMesh* physxMesh = static_cast<PhysXMesh*>(mesh.get());
-		if (physxMesh->getType() != PhysicsMeshType::Convex)
-			return Vector<Collider*>(0);
-
-		PxConvexMeshGeometry geometry(physxMesh->_getConvex());
-		PxTransform transform = toPxTransform(position, rotation);
-
-		return overlap(geometry, transform, layer);
-	}
-
-	bool PhysX::boxOverlapAny(const AABox& box, const Quaternion& rotation, UINT64 layer) const
-	{
-		PxBoxGeometry geometry(toPxVector(box.getHalfSize()));
-		PxTransform transform = toPxTransform(box.getCenter(), rotation);
-
-		return overlapAny(geometry, transform, layer);
-	}
-
-	bool PhysX::sphereOverlapAny(const Sphere& sphere, UINT64 layer) const
-	{
-		PxSphereGeometry geometry(sphere.getRadius());
-		PxTransform transform = toPxTransform(sphere.getCenter(), Quaternion::IDENTITY);
-
-		return overlapAny(geometry, transform, layer);
-	}
-
-	bool PhysX::capsuleOverlapAny(const Capsule& capsule, const Quaternion& rotation,
-		UINT64 layer) const
-	{
-		PxCapsuleGeometry geometry(capsule.getRadius(), capsule.getHeight() * 0.5f);
-		PxTransform transform = toPxTransform(capsule.getCenter(), Quaternion::IDENTITY);
-
-		return overlapAny(geometry, transform, layer);
-	}
-
-	bool PhysX::convexOverlapAny(const HPhysicsMesh& mesh, const Vector3& position, const Quaternion& rotation,
-		UINT64 layer) const
-	{
-		if (mesh == nullptr)
-			return false;
-
-		PhysXMesh* physxMesh = static_cast<PhysXMesh*>(mesh.get());
-		if (physxMesh->getType() != PhysicsMeshType::Convex)
-			return false;
-
-		PxConvexMeshGeometry geometry(physxMesh->_getConvex());
-		PxTransform transform = toPxTransform(position, rotation);
-
-		return overlapAny(geometry, transform, layer);
-	}
-
-	bool PhysX::_rayCast(const Vector3& origin, const Vector3& unitDir, const Collider& collider, PhysicsQueryHit& hit,
-		float maxDist) const
-	{
-		FPhysXCollider* physxCollider = static_cast<FPhysXCollider*>(collider._getInternal());
-		PxShape* shape = physxCollider->_getShape();
-
-		PxTransform transform = toPxTransform(collider.getPosition(), collider.getRotation());
-
-		PxRaycastHit hitInfo;
-		PxU32 maxHits = 1;
-		bool anyHit = false;
-		PxHitFlags hitFlags = PxHitFlag::eDEFAULT | PxHitFlag::eUV;
-		PxU32 hitCount = PxGeometryQuery::raycast(toPxVector(origin), toPxVector(unitDir),
-			shape->getGeometry().any(), transform,
-			maxDist, hitFlags, maxHits, &hitInfo, anyHit);
-
-		if(hitCount > 0)
-			parseHit(hitInfo, hit);
-
-		return hitCount > 0;
-
-	}
-
-	bool PhysX::sweep(const PxGeometry& geometry, const PxTransform& tfrm, const Vector3& unitDir,
-		PhysicsQueryHit& hit, UINT64 layer, float maxDist) const
-	{
-		PxSweepBuffer output;
-
-		PxQueryFilterData filterData;
-		memcpy(&filterData.data.word0, &layer, sizeof(layer));
-
-		bool wasHit = mScene->sweep(geometry, tfrm, toPxVector(unitDir), maxDist, output,
-			PxHitFlag::eDEFAULT | PxHitFlag::eUV, filterData);
-
-		if (wasHit)
-			parseHit(output.block, hit);
-
-		return wasHit;
-	}
-
-	bool PhysX::overlapAny(const PxGeometry& geometry, const PxTransform& tfrm, UINT64 layer) const
-	{
-		PxOverlapBuffer output;
-
-		PxQueryFilterData filterData;
-		memcpy(&filterData.data.word0, &layer, sizeof(layer));
-
-		return mScene->overlap(geometry, tfrm, output, filterData);
-	}
-
-	Vector<Collider*> PhysX::overlap(const PxGeometry& geometry, const PxTransform& tfrm, UINT64 layer) const
-	{
-		PhysXOverlapQueryCallback output;
-
-		PxQueryFilterData filterData;
-		memcpy(&filterData.data.word0, &layer, sizeof(layer));
-
-		mScene->overlap(geometry, tfrm, output, filterData);
-		return output.data;
-	}
-
-	void PhysX::setFlag(PhysicsFlags flag, bool enabled)
-	{
-		Physics::setFlag(flag, enabled);
-
-		mCharManager->setOverlapRecoveryModule(mFlags.isSet(PhysicsFlag::CCT_OverlapRecovery));
-		mCharManager->setPreciseSweeps(mFlags.isSet(PhysicsFlag::CCT_PreciseSweeps));
-		mCharManager->setTessellation(mFlags.isSet(PhysicsFlag::CCT_Tesselation), mTesselationLength);
-	}
-
-	Vector3 PhysX::getGravity() const
-	{
-		return fromPxVector(mScene->getGravity());
-	}
-
-	void PhysX::setGravity(const Vector3& gravity)
-	{
-		mScene->setGravity(toPxVector(gravity));
-	}
-
-	void PhysX::setMaxTesselationEdgeLength(float length)
-	{
-		mTesselationLength = length;
-
-		mCharManager->setTessellation(mFlags.isSet(PhysicsFlag::CCT_Tesselation), mTesselationLength);
-	}
-
-	UINT32 PhysX::addBroadPhaseRegion(const AABox& region)
-	{
-		UINT32 id = mNextRegionIdx++;
-
-		PxBroadPhaseRegion pxRegion;
-		pxRegion.bounds = PxBounds3(toPxVector(region.getMin()), toPxVector(region.getMax()));
-		pxRegion.userData = (void*)(UINT64)id;
-
-		UINT32 handle = mScene->addBroadPhaseRegion(pxRegion, true);
-		mBroadPhaseRegionHandles[id] = handle;
-
-		return handle;
-	}
-
-	void PhysX::removeBroadPhaseRegion(UINT32 regionId)
-	{
-		auto iterFind = mBroadPhaseRegionHandles.find(regionId);
-		if (iterFind == mBroadPhaseRegionHandles.end())
-			return;
-
-		mScene->removeBroadPhaseRegion(iterFind->second);
-		mBroadPhaseRegionHandles.erase(iterFind);
-	}
-
-	void PhysX::clearBroadPhaseRegions()
-	{
-		for(auto& entry : mBroadPhaseRegionHandles)
-			mScene->removeBroadPhaseRegion(entry.second);
-
-		mBroadPhaseRegionHandles.clear();
-	}
-
-	PhysX& gPhysX()
-	{
-		return static_cast<PhysX&>(PhysX::instance());
-	}
+#include "BsPhysX.h"
+#include "PxPhysicsAPI.h"
+#include "BsPhysXMaterial.h"
+#include "BsPhysXMesh.h"
+#include "BsPhysXRigidbody.h"
+#include "BsPhysXBoxCollider.h"
+#include "BsPhysXSphereCollider.h"
+#include "BsPhysXPlaneCollider.h"
+#include "BsPhysXCapsuleCollider.h"
+#include "BsPhysXMeshCollider.h"
+#include "BsPhysXFixedJoint.h"
+#include "BsPhysXDistanceJoint.h"
+#include "BsPhysXHingeJoint.h"
+#include "BsPhysXSphericalJoint.h"
+#include "BsPhysXSliderJoint.h"
+#include "BsPhysXD6Joint.h"
+#include "BsPhysXCharacterController.h"
+#include "BsTaskScheduler.h"
+#include "BsCCollider.h"
+#include "BsFPhysXCollider.h"
+#include "BsTime.h"
+#include "Bsvector3.h"
+#include "BsAABox.h"
+#include "BsCapsule.h"
+#include "foundation\PxTransform.h"
+
+using namespace physx;
+
+namespace BansheeEngine
+{
+	class PhysXAllocator : public PxAllocatorCallback
+	{
+	public:
+		void* allocate(size_t size, const char*, const char*, int) override
+		{
+			void* ptr = bs_alloc_aligned16((UINT32)size);
+			PX_ASSERT((reinterpret_cast<size_t>(ptr) & 15) == 0);
+			return ptr;
+		}
+
+		void deallocate(void* ptr) override
+		{
+			bs_free_aligned16(ptr);
+		}
+	};
+
+	class PhysXErrorCallback : public PxErrorCallback
+	{
+	public:
+		void reportError(PxErrorCode::Enum code, const char* message, const char* file, int line) override
+		{
+			{
+				const char* errorCode = nullptr;
+
+				UINT32 severity = 0;
+				switch (code)
+				{
+				case PxErrorCode::eNO_ERROR:
+					errorCode = "No error";
+					break;
+				case PxErrorCode::eINVALID_PARAMETER:
+					errorCode = "Invalid parameter";
+					severity = 2;
+					break;
+				case PxErrorCode::eINVALID_OPERATION:
+					errorCode = "Invalid operation";
+					severity = 2;
+					break;
+				case PxErrorCode::eOUT_OF_MEMORY:
+					errorCode = "Out of memory";
+					severity = 2;
+					break;
+				case PxErrorCode::eDEBUG_INFO:
+					errorCode = "Info";
+					break;
+				case PxErrorCode::eDEBUG_WARNING:
+					errorCode = "Warning";
+					severity = 1;
+					break;
+				case PxErrorCode::ePERF_WARNING:
+					errorCode = "Performance warning";
+					severity = 1;
+					break;
+				case PxErrorCode::eABORT:
+					errorCode = "Abort";
+					severity = 2;
+					break;
+				case PxErrorCode::eINTERNAL_ERROR:
+					errorCode = "Internal error";
+					severity = 2;
+					break;
+				case PxErrorCode::eMASK_ALL:
+				default:
+					errorCode = "Unknown error";
+					severity = 2;
+					break;
+				}
+
+				StringStream ss;
+
+				switch(severity)
+				{
+				case 0:
+					ss << "PhysX info (" << errorCode << "): " << message << " at " << file << ":" << line;
+					LOGDBG(ss.str());
+					break;
+				case 1:
+					ss << "PhysX warning (" << errorCode << "): " << message << " at " << file << ":" << line;
+					LOGWRN(ss.str());
+					break;
+				case 2:
+					ss << "PhysX error (" << errorCode << "): " << message << " at " << file << ":" << line;
+					LOGERR(ss.str());
+					BS_ASSERT(false); // Halt execution on debug builds when error occurrs
+					break;
+				}
+			}
+		}
+	};
+
+	class PhysXEventCallback : public PxSimulationEventCallback
+	{
+		void onWake(PxActor** actors, PxU32 count) override { /* Do nothing */ }
+		void onSleep(PxActor** actors, PxU32 count) override { /* Do nothing */ }
+
+		void onTrigger(PxTriggerPair* pairs, PxU32 count) override
+		{
+			for (PxU32 i = 0; i < count; i++)
+			{
+				const PxTriggerPair& pair = pairs[i];
+				
+				PhysX::ContactEventType type;
+				bool ignoreContact = false;
+				PhysXObjectFilterFlags flags = PhysXObjectFilterFlags(pair.triggerShape->getSimulationFilterData().word2);
+
+				if (flags.isSet(PhysXObjectFilterFlag::ReportAll))
+				{
+					switch ((UINT32)pair.status)
+					{
+					case PxPairFlag::eNOTIFY_TOUCH_FOUND:
+						type = PhysX::ContactEventType::ContactBegin;
+						break;
+					case PxPairFlag::eNOTIFY_TOUCH_PERSISTS:
+						type = PhysX::ContactEventType::ContactStay;
+						break;
+					case PxPairFlag::eNOTIFY_TOUCH_LOST:
+						type = PhysX::ContactEventType::ContactEnd;
+						break;
+					default:
+						ignoreContact = true;
+						break;
+					}
+				}
+				else if (flags.isSet(PhysXObjectFilterFlag::ReportBasic))
+				{
+					switch ((UINT32)pair.status)
+					{
+					case PxPairFlag::eNOTIFY_TOUCH_FOUND:
+						type = PhysX::ContactEventType::ContactBegin;
+						break;
+					case PxPairFlag::eNOTIFY_TOUCH_LOST:
+						type = PhysX::ContactEventType::ContactEnd;
+						break;
+					default:
+						ignoreContact = true;
+						break;
+					}
+				}
+				else
+					ignoreContact = true;
+
+				if (ignoreContact)
+					continue;
+
+				PhysX::TriggerEvent event;
+				event.trigger = (Collider*)pair.triggerShape->userData;
+				event.other = (Collider*)pair.otherShape->userData;
+				event.type = type;
+
+				gPhysX()._reportTriggerEvent(event);
+			}
+		}
+
+		void onContact(const PxContactPairHeader& pairHeader, const PxContactPair* pairs, PxU32 count) override
+		{
+			for (PxU32 i = 0; i < count; i++)
+			{
+				const PxContactPair& pair = pairs[i];
+
+				PhysX::ContactEventType type;
+				bool ignoreContact = false;
+				switch((UINT32)pair.events)
+				{
+				case PxPairFlag::eNOTIFY_TOUCH_FOUND:
+					type = PhysX::ContactEventType::ContactBegin;
+					break;
+				case PxPairFlag::eNOTIFY_TOUCH_PERSISTS:
+					type = PhysX::ContactEventType::ContactStay;
+					break;
+				case PxPairFlag::eNOTIFY_TOUCH_LOST:
+					type = PhysX::ContactEventType::ContactEnd;
+					break;
+				default:
+					ignoreContact = true;
+					break;
+				}
+
+				if (ignoreContact)
+					continue;
+
+				PhysX::ContactEvent event;
+				event.type = type;
+
+				PxU32 contactCount = pair.contactCount;
+				const PxU8* stream = pair.contactStream;
+				PxU16 streamSize = pair.contactStreamSize;
+
+				if (contactCount > 0 && streamSize > 0)
+				{
+					PxU32 contactIdx = 0;
+					PxContactStreamIterator iter((PxU8*)stream, streamSize);
+
+					stream += ((streamSize + 15) & ~15);
+
+					const PxReal* impulses = reinterpret_cast<const PxReal*>(stream);
+					PxU32 hasImpulses = (pair.flags & PxContactPairFlag::eINTERNAL_HAS_IMPULSES);
+
+					while (iter.hasNextPatch())
+					{
+						iter.nextPatch();
+						while (iter.hasNextContact())
+						{
+							iter.nextContact();
+
+							ContactPoint point;
+							point.position = fromPxVector(iter.getContactPoint());
+							point.separation = iter.getSeparation();
+							point.normal = fromPxVector(iter.getContactNormal());
+
+							if (hasImpulses)
+								point.impulse = impulses[contactIdx];
+							else
+								point.impulse = 0.0f;
+
+							event.points.push_back(point);
+
+							contactIdx++;
+						}
+					}
+				}
+
+				event.colliderA = (Collider*)pair.shapes[0]->userData;
+				event.colliderB = (Collider*)pair.shapes[1]->userData;
+
+				gPhysX()._reportContactEvent(event);
+			}
+		}
+
+		void onConstraintBreak(PxConstraintInfo* constraints, PxU32 count) override 
+		{ 
+			for (UINT32 i = 0; i < count; i++)
+			{
+				PxConstraintInfo& constraintInfo = constraints[i];
+
+				if (constraintInfo.type != PxConstraintExtIDs::eJOINT)
+					continue;
+
+				PxJoint* pxJoint = (PxJoint*)constraintInfo.externalReference;
+				Joint* joint = (Joint*)pxJoint->userData;
+
+				
+			}
+		}
+	};
+
+	class PhysXCPUDispatcher : public PxCpuDispatcher
+	{
+	public:
+		void submitTask(PxBaseTask& physxTask) override
+		{
+			// Note: Banshee's task scheduler is pretty low granularity. Consider a better task manager in case PhysX ends
+			// up submitting many tasks.
+			// - PhysX's task manager doesn't seem much lighter either. But perhaps I can at least create a task pool to 
+			//   avoid allocating them constantly.
+
+			auto runTask = [&]() { physxTask.run(); physxTask.release(); };
+			TaskPtr task = Task::create("PhysX", runTask);
+
+			TaskScheduler::instance().addTask(task);
+		}
+
+		PxU32 getWorkerCount() const override
+		{
+			return (PxU32)TaskScheduler::instance().getNumWorkers();
+		}
+	};
+
+	class PhysXBroadPhaseCallback : public PxBroadPhaseCallback
+	{
+		void onObjectOutOfBounds(PxShape& shape, PxActor& actor) override
+		{
+			Collider* collider = (Collider*)shape.userData;
+			if (collider != nullptr)
+				LOGWRN("Physics object out of bounds. Consider increasing broadphase region!");
+		}
+
+		void onObjectOutOfBounds(PxAggregate& aggregate) override { /* Do nothing */ }
+	};
+
+	PxFilterFlags PhysXFilterShader(PxFilterObjectAttributes attr0, PxFilterData data0, PxFilterObjectAttributes attr1, 
+		PxFilterData data1, PxPairFlags& pairFlags, const void* constantBlock, PxU32 constantBlockSize)
+	{
+		PhysXObjectFilterFlags flags0 = PhysXObjectFilterFlags(data0.word2);
+		PhysXObjectFilterFlags flags1 = PhysXObjectFilterFlags(data1.word2);
+
+		if (flags0.isSet(PhysXObjectFilterFlag::ReportAll) || flags1.isSet(PhysXObjectFilterFlag::ReportAll))
+			pairFlags |= PxPairFlag::eNOTIFY_TOUCH_FOUND | PxPairFlag::eNOTIFY_TOUCH_LOST | PxPairFlag::eNOTIFY_TOUCH_PERSISTS;
+		else if (flags0.isSet(PhysXObjectFilterFlag::ReportBasic) || flags1.isSet(PhysXObjectFilterFlag::ReportBasic))
+			pairFlags |= PxPairFlag::eNOTIFY_TOUCH_FOUND | PxPairFlag::eNOTIFY_TOUCH_LOST;
+
+		if (PxFilterObjectIsTrigger(attr0) || PxFilterObjectIsTrigger(attr1))
+		{
+			pairFlags |= PxPairFlag::eDETECT_DISCRETE_CONTACT;
+			return PxFilterFlags();
+		}
+
+		UINT64 groupA = *(UINT64*)&data0.word0;
+		UINT64 groupB = *(UINT64*)&data1.word0;
+
+		bool canCollide = gPhysics().isCollisionEnabled(groupA, groupB);
+		if (!canCollide)
+			return PxFilterFlag::eSUPPRESS;
+
+		if (flags0.isSet(PhysXObjectFilterFlag::CCD) || flags1.isSet(PhysXObjectFilterFlag::CCD))
+			pairFlags |= PxPairFlag::eDETECT_CCD_CONTACT;
+
+		pairFlags |= PxPairFlag::eSOLVE_CONTACT | PxPairFlag::eDETECT_DISCRETE_CONTACT;
+		return PxFilterFlags();
+	}
+
+	void parseHit(const PxRaycastHit& input, PhysicsQueryHit& output)
+	{
+		output.point = fromPxVector(input.position);
+		output.normal = fromPxVector(input.normal);
+		output.distance = input.distance;
+		output.triangleIdx = input.faceIndex;
+		output.uv = Vector2(input.u, input.v);
+		output.colliderRaw = (Collider*)input.shape->userData;
+
+		if (output.colliderRaw != nullptr)
+		{
+			CCollider* component = (CCollider*)output.colliderRaw->_getOwner(PhysicsOwnerType::Component);
+			if (component != nullptr)
+				output.collider = component->getHandle();
+		}
+	}
+
+	void parseHit(const PxSweepHit& input, PhysicsQueryHit& output)
+	{
+		output.point = fromPxVector(input.position);
+		output.normal = fromPxVector(input.normal);
+		output.distance = input.distance;
+		output.triangleIdx = input.faceIndex;
+		output.colliderRaw = (Collider*)input.shape->userData;
+
+		if (output.colliderRaw != nullptr)
+		{
+			CCollider* component = (CCollider*)output.colliderRaw->_getOwner(PhysicsOwnerType::Component);
+			if (component != nullptr)
+				output.collider = component->getHandle();
+		}
+	}
+
+	struct PhysXRaycastQueryCallback : PxRaycastCallback
+	{
+		Vector<PhysicsQueryHit> data;
+
+		PhysXRaycastQueryCallback()
+			:PxRaycastCallback(nullptr, 0)
+		{ }
+
+		PxAgain processTouches(const PxRaycastHit* buffer, PxU32 nbHits) override
+		{
+			for (PxU32 i = 0; i < nbHits; i++)
+			{
+				data.push_back(PhysicsQueryHit());
+				parseHit(buffer[i], data.back());
+			}
+
+			return true;
+		}
+	};
+
+	struct PhysXSweepQueryCallback : PxSweepCallback
+	{
+		Vector<PhysicsQueryHit> data;
+
+		PhysXSweepQueryCallback()
+			:PxSweepCallback(nullptr, 0)
+		{ }
+
+		PxAgain processTouches(const PxSweepHit* buffer, PxU32 nbHits) override
+		{
+			for (PxU32 i = 0; i < nbHits; i++)
+			{
+				data.push_back(PhysicsQueryHit());
+				parseHit(buffer[i], data.back());
+			}
+
+			return true;
+		}
+	};
+
+	struct PhysXOverlapQueryCallback : PxOverlapCallback
+	{
+		Vector<Collider*> data;
+
+		PhysXOverlapQueryCallback()
+			:PxOverlapCallback(nullptr, 0)
+		{ }
+
+		PxAgain processTouches(const PxOverlapHit* buffer, PxU32 nbHits) override
+		{
+			for (PxU32 i = 0; i < nbHits; i++)
+				data.push_back((Collider*)buffer[i].shape->userData);
+
+			return true;
+		}
+	};
+
+	static PhysXAllocator gPhysXAllocator;
+	static PhysXErrorCallback gPhysXErrorHandler;
+	static PhysXCPUDispatcher gPhysXCPUDispatcher;
+	static PhysXEventCallback gPhysXEventCallback;
+	static PhysXBroadPhaseCallback gPhysXBroadphaseCallback;
+
+	static const UINT32 SIZE_16K = 1 << 14;
+	const UINT32 PhysX::SCRATCH_BUFFER_SIZE = SIZE_16K * 64; // 1MB by default
+	const UINT32 PhysX::MAX_ITERATIONS_PER_FRAME = 4; // At 60 physics updates per second this would mean user is running at 15fps
+
+	PhysX::PhysX(const PHYSICS_INIT_DESC& input)
+		:Physics(input)
+	{
+		mScale.length = input.typicalLength;
+		mScale.speed = input.typicalSpeed;
+
+		mFoundation = PxCreateFoundation(PX_PHYSICS_VERSION, gPhysXAllocator, gPhysXErrorHandler);
+		mPhysics = PxCreateBasePhysics(PX_PHYSICS_VERSION, *mFoundation, mScale);
+
+		PxRegisterArticulations(*mPhysics);
+
+		if (input.initCooking)
+		{
+			// Note: PhysX supports cooking for specific platforms to make the generated results better. Consider
+			// allowing the meshes to be re-cooked when target platform is changed. Right now we just use the default value.
+
+			PxCookingParams cookingParams(mScale);
+			mCooking = PxCreateCooking(PX_PHYSICS_VERSION, *mFoundation, cookingParams);
+		}
+
+		PxSceneDesc sceneDesc(mScale); // TODO - Test out various other parameters provided by scene desc
+		sceneDesc.gravity = toPxVector(input.gravity);
+		sceneDesc.cpuDispatcher = &gPhysXCPUDispatcher;
+		sceneDesc.filterShader = PhysXFilterShader;
+		sceneDesc.simulationEventCallback = &gPhysXEventCallback;
+		sceneDesc.broadPhaseCallback = &gPhysXBroadphaseCallback;
+
+		// Optionally: eENABLE_KINEMATIC_STATIC_PAIRS, eENABLE_KINEMATIC_PAIRS, eENABLE_PCM
+		sceneDesc.flags = PxSceneFlag::eENABLE_ACTIVETRANSFORMS;
+
+		if (input.flags.isSet(PhysicsFlag::CCD_Enable))
+			sceneDesc.flags |= PxSceneFlag::eENABLE_CCD;
+
+		// Optionally: eMBP
+		sceneDesc.broadPhaseType = PxBroadPhaseType::eSAP;
+
+		mScene = mPhysics->createScene(sceneDesc);
+
+		// Character controller
+		mCharManager = PxCreateControllerManager(*mScene);
+
+		mSimulationStep = input.timeStep;
+		mDefaultMaterial = mPhysics->createMaterial(0.0f, 0.0f, 0.0f);
+	}
+
+	PhysX::~PhysX()
+	{
+		mCharManager->release();
+		mScene->release();
+
+		if (mCooking != nullptr)
+			mCooking->release();
+
+		mPhysics->release();
+		mFoundation->release();
+	}
+
+	void PhysX::update()
+	{
+		mUpdateInProgress = true;
+
+		if(mFirstUpdate)
+		{
+			mLastSimulationTime = gTime().getTime() - mSimulationStep * 1.01f;
+			mFirstUpdate = false;
+		}
+
+		float nextFrameTime = mLastSimulationTime + mSimulationStep;
+		float curFrameTime = gTime().getTime();
+		if(curFrameTime < nextFrameTime)
+		{
+			// TODO - Interpolate rigidbodies but perform no actual simulation
+
+			return;
+		}
+
+		float simulationAmount = curFrameTime - mLastSimulationTime;
+		INT32 numIterations = Math::floorToInt(simulationAmount / mSimulationStep);
+
+		// If too many iterations are required, increase time step. This should only happen in extreme situations (or when
+		// debugging).
+		float step = mSimulationStep;
+		if (numIterations > MAX_ITERATIONS_PER_FRAME) 
+			step = (simulationAmount / MAX_ITERATIONS_PER_FRAME) * 0.99f;
+
+		UINT32 iterationCount = 0;
+		while (simulationAmount >= step) // In case we're running really slow multiple updates might be needed
+		{
+			// Note: Consider delaying fetchResults one frame. This could improve performance because Physics update would be
+			//       able to run parallel to the simulation thread, but at a cost to input latency.
+
+			bs_frame_mark();
+			UINT8* scratchBuffer = bs_frame_alloc_aligned(SCRATCH_BUFFER_SIZE, 16);
+
+			mScene->simulate(step, nullptr, scratchBuffer, SCRATCH_BUFFER_SIZE);
+			simulationAmount -= step;
+
+			UINT32 errorState;
+			if(!mScene->fetchResults(true, &errorState))
+			{
+				LOGWRN("Physics simulation failed. Error code: " + toString(errorState));
+
+				bs_frame_free_aligned(scratchBuffer);
+				bs_frame_clear();
+
+				iterationCount++;
+				continue;
+			}
+
+			bs_frame_free_aligned(scratchBuffer);
+			bs_frame_clear();
+
+			iterationCount++;
+		}
+
+		// Update rigidbodies with new transforms
+		PxU32 numActiveTransforms;
+		const PxActiveTransform* activeTransforms = mScene->getActiveTransforms(numActiveTransforms);
+
+		for (PxU32 i = 0; i < numActiveTransforms; i++)
+		{
+			Rigidbody* rigidbody = static_cast<Rigidbody*>(activeTransforms[i].userData);
+			const PxTransform& transform = activeTransforms[i].actor2World;
+
+			// Note: Make this faster, avoid dereferencing Rigidbody and attempt to access pos/rot destination directly,
+			//       use non-temporal writes
+			rigidbody->_setTransform(fromPxVector(transform.p), fromPxQuaternion(transform.q));
+		}
+
+		// TODO - Consider extrapolating for the remaining "simulationAmount" value
+
+		mLastSimulationTime = curFrameTime; 
+		mUpdateInProgress = false;
+
+		triggerEvents();
+	}
+
+	void PhysX::_reportContactEvent(const ContactEvent& event)
+	{
+		mContactEvents.push_back(event);
+	}
+
+	void PhysX::_reportTriggerEvent(const TriggerEvent& event)
+	{
+		mTriggerEvents.push_back(event);
+	}
+
+	void PhysX::_reportJointBreakEvent(const JointBreakEvent& event)
+	{
+		mJointBreakEvents.push_back(event);
+	}
+
+	void PhysX::triggerEvents()
+	{
+		CollisionData data;
+
+		for(auto& entry : mTriggerEvents)
+		{
+			data.collidersRaw[0] = entry.trigger;
+			data.collidersRaw[1] = entry.other;
+
+			switch (entry.type)
+			{
+			case ContactEventType::ContactBegin:
+				entry.trigger->onCollisionBegin(data);
+				break;
+			case ContactEventType::ContactStay:
+				entry.trigger->onCollisionStay(data);
+				break;
+			case ContactEventType::ContactEnd:
+				entry.trigger->onCollisionEnd(data);
+				break;
+			}
+		}
+
+		auto notifyContact = [&](Collider* obj, Collider* other, ContactEventType type, 
+			const Vector<ContactPoint>& points, bool flipNormals = false)
+		{
+			data.collidersRaw[0] = obj;
+			data.collidersRaw[1] = other;
+			data.contactPoints = points;
+
+			if(flipNormals)
+			{
+				for (auto& point : data.contactPoints)
+					point.normal = -point.normal;
+			}
+
+			Rigidbody* rigidbody = obj->getRigidbody();
+			if(rigidbody != nullptr)
+			{
+				switch (type)
+				{
+				case ContactEventType::ContactBegin:
+					rigidbody->onCollisionBegin(data);
+					break;
+				case ContactEventType::ContactStay:
+					rigidbody->onCollisionStay(data);
+					break;
+				case ContactEventType::ContactEnd:
+					rigidbody->onCollisionEnd(data);
+					break;
+				}
+			}
+			else
+			{
+				switch (type)
+				{
+				case ContactEventType::ContactBegin:
+					obj->onCollisionBegin(data);
+					break;
+				case ContactEventType::ContactStay:
+					obj->onCollisionStay(data);
+					break;
+				case ContactEventType::ContactEnd:
+					obj->onCollisionEnd(data);
+					break;
+				}
+			}
+		};
+
+		for (auto& entry : mContactEvents)
+		{
+			CollisionReportMode reportModeA = entry.colliderA->getCollisionReportMode();
+
+			if (reportModeA == CollisionReportMode::ReportPersistent)
+				notifyContact(entry.colliderA, entry.colliderB, entry.type, entry.points, true);
+			else if (reportModeA == CollisionReportMode::Report && entry.type != ContactEventType::ContactStay)
+				notifyContact(entry.colliderA, entry.colliderB, entry.type, entry.points, true);
+
+			CollisionReportMode reportModeB = entry.colliderB->getCollisionReportMode();
+
+			if (reportModeB == CollisionReportMode::ReportPersistent)
+				notifyContact(entry.colliderB, entry.colliderA, entry.type, entry.points, false);
+			else if (reportModeB == CollisionReportMode::Report && entry.type != ContactEventType::ContactStay)
+				notifyContact(entry.colliderB, entry.colliderA, entry.type, entry.points, false);
+		}
+
+		for(auto& entry : mJointBreakEvents)
+		{
+			entry.joint->onJointBreak();
+		}
+
+		mTriggerEvents.clear();
+		mContactEvents.clear();
+		mJointBreakEvents.clear();
+	}
+
+	SPtr<PhysicsMaterial> PhysX::createMaterial(float staticFriction, float dynamicFriction, float restitution)
+	{
+		return bs_shared_ptr_new<PhysXMaterial>(mPhysics, staticFriction, dynamicFriction, restitution);
+	}
+
+	SPtr<PhysicsMesh> PhysX::createMesh(const MeshDataPtr& meshData, PhysicsMeshType type)
+	{
+		return bs_shared_ptr_new<PhysXMesh>(meshData, type);
+	}
+
+	SPtr<Rigidbody> PhysX::createRigidbody(const HSceneObject& linkedSO)
+	{
+		return bs_shared_ptr_new<PhysXRigidbody>(mPhysics, mScene, linkedSO);
+	}
+
+	SPtr<BoxCollider> PhysX::createBoxCollider(const Vector3& extents, const Vector3& position,
+		const Quaternion& rotation)
+	{
+		return bs_shared_ptr_new<PhysXBoxCollider>(mPhysics, position, rotation, extents);
+	}
+
+	SPtr<SphereCollider> PhysX::createSphereCollider(float radius, const Vector3& position, const Quaternion& rotation)
+	{
+		return bs_shared_ptr_new<PhysXSphereCollider>(mPhysics, position, rotation, radius);
+	}
+
+	SPtr<PlaneCollider> PhysX::createPlaneCollider(const Vector3& position, const Quaternion& rotation)
+	{
+		return bs_shared_ptr_new<PhysXPlaneCollider>(mPhysics, position, rotation);
+	}
+
+	SPtr<CapsuleCollider> PhysX::createCapsuleCollider(float radius, float halfHeight, const Vector3& position, 
+		const Quaternion& rotation)
+	{
+		return bs_shared_ptr_new<PhysXCapsuleCollider>(mPhysics, position, rotation, radius, halfHeight);
+	}
+
+	SPtr<MeshCollider> PhysX::createMeshCollider(const Vector3& position, const Quaternion& rotation)
+	{
+		return bs_shared_ptr_new<PhysXMeshCollider>(mPhysics, position, rotation);
+	}
+
+	SPtr<FixedJoint> PhysX::createFixedJoint()
+	{
+		return bs_shared_ptr_new<PhysXFixedJoint>(mPhysics);
+	}
+
+	SPtr<DistanceJoint> PhysX::createDistanceJoint()
+	{
+		return bs_shared_ptr_new<PhysXDistanceJoint>(mPhysics);
+	}
+
+	SPtr<HingeJoint> PhysX::createHingeJoint()
+	{
+		return bs_shared_ptr_new<PhysXHingeJoint>(mPhysics);
+	}
+
+	SPtr<SphericalJoint> PhysX::createSphericalJoint()
+	{
+		return bs_shared_ptr_new<PhysXSphericalJoint>(mPhysics);
+	}
+
+	SPtr<SliderJoint> PhysX::createSliderJoint()
+	{
+		return bs_shared_ptr_new<PhysXSliderJoint>(mPhysics);
+	}
+
+	SPtr<D6Joint> PhysX::createD6Joint()
+	{
+		return bs_shared_ptr_new<PhysXD6Joint>(mPhysics);
+	}
+
+	SPtr<CharacterController> PhysX::createCharacterController(const CHAR_CONTROLLER_DESC& desc)
+	{
+		return bs_shared_ptr_new<PhysXCharacterController>(mCharManager, desc);
+	}
+
+	Vector<PhysicsQueryHit> PhysX::sweepAll(const PxGeometry& geometry, const PxTransform& tfrm, const Vector3& unitDir,
+		UINT64 layer, float maxDist) const
+	{
+		PhysXSweepQueryCallback output;
+
+		PxQueryFilterData filterData;
+		memcpy(&filterData.data.word0, &layer, sizeof(layer));
+
+		mScene->sweep(geometry, tfrm, toPxVector(unitDir), maxDist, output,
+			PxHitFlag::eDEFAULT | PxHitFlag::eUV, filterData);
+
+		return output.data;
+	}
+
+	bool PhysX::sweepAny(const PxGeometry& geometry, const PxTransform& tfrm, const Vector3& unitDir, UINT64 layer, 
+		float maxDist) const
+	{
+		PxSweepBuffer output;
+
+		PxQueryFilterData filterData;
+		filterData.flags |= PxQueryFlag::eANY_HIT;
+		memcpy(&filterData.data.word0, &layer, sizeof(layer));
+
+		return mScene->sweep(geometry, tfrm, toPxVector(unitDir), maxDist, output, 
+			PxHitFlag::eDEFAULT | PxHitFlag::eUV | PxHitFlag::eMESH_ANY, filterData);
+	}
+
+	bool PhysX::rayCast(const Vector3& origin, const Vector3& unitDir, PhysicsQueryHit& hit, UINT64 layer, float max) const
+	{
+		PxRaycastBuffer output;
+
+		PxQueryFilterData filterData;
+		memcpy(&filterData.data.word0, &layer, sizeof(layer));
+
+		bool wasHit = mScene->raycast(toPxVector(origin),
+			toPxVector(unitDir), max, output, PxHitFlag::eDEFAULT | PxHitFlag::eUV, filterData);
+
+		if (wasHit)
+			parseHit(output.block, hit);
+
+		return wasHit;
+	}
+
+	bool PhysX::boxCast(const AABox& box, const Quaternion& rotation, const Vector3& unitDir, PhysicsQueryHit& hit,
+		UINT64 layer, float max) const
+	{
+		PxBoxGeometry geometry(toPxVector(box.getHalfSize()));
+		PxTransform transform = toPxTransform(box.getCenter(), rotation);
+
+		return sweep(geometry, transform, unitDir, hit, layer, max);
+	}
+
+	bool PhysX::sphereCast(const Sphere& sphere, const Vector3& unitDir, PhysicsQueryHit& hit,
+		UINT64 layer, float max) const
+	{
+		PxSphereGeometry geometry(sphere.getRadius());
+		PxTransform transform = toPxTransform(sphere.getCenter(), Quaternion::IDENTITY);
+
+		return sweep(geometry, transform, unitDir, hit, layer, max);
+	}
+
+	bool PhysX::capsuleCast(const Capsule& capsule, const Quaternion& rotation, const Vector3& unitDir,
+		PhysicsQueryHit& hit, UINT64 layer, float max) const
+	{
+		PxCapsuleGeometry geometry(capsule.getRadius(), capsule.getHeight() * 0.5f);
+		PxTransform transform = toPxTransform(capsule.getCenter(), Quaternion::IDENTITY);
+
+		return sweep(geometry, transform, unitDir, hit, layer, max);
+	}
+
+	bool PhysX::convexCast(const HPhysicsMesh& mesh, const Vector3& position, const Quaternion& rotation,
+		const Vector3& unitDir, PhysicsQueryHit& hit, UINT64 layer, float max) const
+	{
+		if (mesh == nullptr)
+			return false;
+
+		PhysXMesh* physxMesh = static_cast<PhysXMesh*>(mesh.get());
+		if (physxMesh->getType() != PhysicsMeshType::Convex)
+			return false;
+
+		PxConvexMeshGeometry geometry(physxMesh->_getConvex());
+		PxTransform transform = toPxTransform(position, rotation);
+
+		return sweep(geometry, transform, unitDir, hit, layer, max);
+	}
+
+	Vector<PhysicsQueryHit> PhysX::rayCastAll(const Vector3& origin, const Vector3& unitDir,
+		UINT64 layer, float max) const
+	{
+		PhysXRaycastQueryCallback output;
+
+		PxQueryFilterData filterData;
+		memcpy(&filterData.data.word0, &layer, sizeof(layer));
+
+		mScene->raycast(toPxVector(origin), toPxVector(unitDir), max, output,
+			PxHitFlag::eDEFAULT | PxHitFlag::eUV | PxHitFlag::eMESH_MULTIPLE, filterData);
+
+		return output.data;
+	}
+
+	Vector<PhysicsQueryHit> PhysX::boxCastAll(const AABox& box, const Quaternion& rotation,
+		const Vector3& unitDir, UINT64 layer, float max) const
+	{
+		PxBoxGeometry geometry(toPxVector(box.getHalfSize()));
+		PxTransform transform = toPxTransform(box.getCenter(), rotation);
+
+		return sweepAll(geometry, transform, unitDir, layer, max);
+	}
+
+	Vector<PhysicsQueryHit> PhysX::sphereCastAll(const Sphere& sphere, const Vector3& unitDir,
+		UINT64 layer, float max) const
+	{
+		PxSphereGeometry geometry(sphere.getRadius());
+		PxTransform transform = toPxTransform(sphere.getCenter(), Quaternion::IDENTITY);
+
+		return sweepAll(geometry, transform, unitDir, layer, max);
+	}
+
+	Vector<PhysicsQueryHit> PhysX::capsuleCastAll(const Capsule& capsule, const Quaternion& rotation,
+		const Vector3& unitDir, UINT64 layer, float max) const
+	{
+		PxCapsuleGeometry geometry(capsule.getRadius(), capsule.getHeight() * 0.5f);
+		PxTransform transform = toPxTransform(capsule.getCenter(), Quaternion::IDENTITY);
+
+		return sweepAll(geometry, transform, unitDir, layer, max);
+	}
+
+	Vector<PhysicsQueryHit> PhysX::convexCastAll(const HPhysicsMesh& mesh, const Vector3& position,
+		const Quaternion& rotation, const Vector3& unitDir, UINT64 layer, float max) const
+	{
+		if (mesh == nullptr)
+			return Vector<PhysicsQueryHit>(0);
+
+		PhysXMesh* physxMesh = static_cast<PhysXMesh*>(mesh.get());
+		if (physxMesh->getType() != PhysicsMeshType::Convex)
+			return Vector<PhysicsQueryHit>(0);
+
+		PxConvexMeshGeometry geometry(physxMesh->_getConvex());
+		PxTransform transform = toPxTransform(position, rotation);
+
+		return sweepAll(geometry, transform, unitDir, layer, max);
+	}
+
+	bool PhysX::rayCastAny(const Vector3& origin, const Vector3& unitDir,
+		UINT64 layer, float max) const
+	{
+		PxRaycastBuffer output;
+
+		PxQueryFilterData filterData;
+		filterData.flags |= PxQueryFlag::eANY_HIT;
+		memcpy(&filterData.data.word0, &layer, sizeof(layer));
+
+		return mScene->raycast(toPxVector(origin),
+			toPxVector(unitDir), max, output, PxHitFlag::eDEFAULT | PxHitFlag::eUV | PxHitFlag::eMESH_ANY, filterData);
+	}
+
+	bool PhysX::boxCastAny(const AABox& box, const Quaternion& rotation, const Vector3& unitDir,
+		UINT64 layer, float max) const
+	{
+		PxBoxGeometry geometry(toPxVector(box.getHalfSize()));
+		PxTransform transform = toPxTransform(box.getCenter(), rotation);
+
+		return sweepAny(geometry, transform, unitDir, layer, max);
+	}
+
+	bool PhysX::sphereCastAny(const Sphere& sphere, const Vector3& unitDir,
+		UINT64 layer, float max) const
+	{
+		PxSphereGeometry geometry(sphere.getRadius());
+		PxTransform transform = toPxTransform(sphere.getCenter(), Quaternion::IDENTITY);
+
+		return sweepAny(geometry, transform, unitDir, layer, max);
+	}
+
+	bool PhysX::capsuleCastAny(const Capsule& capsule, const Quaternion& rotation, const Vector3& unitDir,
+		UINT64 layer, float max) const
+	{
+		PxCapsuleGeometry geometry(capsule.getRadius(), capsule.getHeight() * 0.5f);
+		PxTransform transform = toPxTransform(capsule.getCenter(), Quaternion::IDENTITY);
+
+		return sweepAny(geometry, transform, unitDir, layer, max);
+	}
+
+	bool PhysX::convexCastAny(const HPhysicsMesh& mesh, const Vector3& position, const Quaternion& rotation,
+		const Vector3& unitDir, UINT64 layer, float max) const
+	{
+		if (mesh == nullptr)
+			return false;
+
+		PhysXMesh* physxMesh = static_cast<PhysXMesh*>(mesh.get());
+		if (physxMesh->getType() != PhysicsMeshType::Convex)
+			return false;
+
+		PxConvexMeshGeometry geometry(physxMesh->_getConvex());
+		PxTransform transform = toPxTransform(position, rotation);
+
+		return sweepAny(geometry, transform, unitDir, layer, max);
+	}
+
+	Vector<Collider*> PhysX::_boxOverlap(const AABox& box, const Quaternion& rotation,
+		UINT64 layer) const
+	{
+		PxBoxGeometry geometry(toPxVector(box.getHalfSize()));
+		PxTransform transform = toPxTransform(box.getCenter(), rotation);
+
+		return overlap(geometry, transform, layer);
+	}
+
+	Vector<Collider*> PhysX::_sphereOverlap(const Sphere& sphere, UINT64 layer) const
+	{
+		PxSphereGeometry geometry(sphere.getRadius());
+		PxTransform transform = toPxTransform(sphere.getCenter(), Quaternion::IDENTITY);
+
+		return overlap(geometry, transform, layer);
+	}
+
+	Vector<Collider*> PhysX::_capsuleOverlap(const Capsule& capsule, const Quaternion& rotation,
+		UINT64 layer) const
+	{
+		PxCapsuleGeometry geometry(capsule.getRadius(), capsule.getHeight() * 0.5f);
+		PxTransform transform = toPxTransform(capsule.getCenter(), Quaternion::IDENTITY);
+
+		return overlap(geometry, transform, layer);
+	}
+
+	Vector<Collider*> PhysX::_convexOverlap(const HPhysicsMesh& mesh, const Vector3& position,
+		const Quaternion& rotation, UINT64 layer) const
+	{
+		if (mesh == nullptr)
+			return Vector<Collider*>(0);
+
+		PhysXMesh* physxMesh = static_cast<PhysXMesh*>(mesh.get());
+		if (physxMesh->getType() != PhysicsMeshType::Convex)
+			return Vector<Collider*>(0);
+
+		PxConvexMeshGeometry geometry(physxMesh->_getConvex());
+		PxTransform transform = toPxTransform(position, rotation);
+
+		return overlap(geometry, transform, layer);
+	}
+
+	bool PhysX::boxOverlapAny(const AABox& box, const Quaternion& rotation, UINT64 layer) const
+	{
+		PxBoxGeometry geometry(toPxVector(box.getHalfSize()));
+		PxTransform transform = toPxTransform(box.getCenter(), rotation);
+
+		return overlapAny(geometry, transform, layer);
+	}
+
+	bool PhysX::sphereOverlapAny(const Sphere& sphere, UINT64 layer) const
+	{
+		PxSphereGeometry geometry(sphere.getRadius());
+		PxTransform transform = toPxTransform(sphere.getCenter(), Quaternion::IDENTITY);
+
+		return overlapAny(geometry, transform, layer);
+	}
+
+	bool PhysX::capsuleOverlapAny(const Capsule& capsule, const Quaternion& rotation,
+		UINT64 layer) const
+	{
+		PxCapsuleGeometry geometry(capsule.getRadius(), capsule.getHeight() * 0.5f);
+		PxTransform transform = toPxTransform(capsule.getCenter(), Quaternion::IDENTITY);
+
+		return overlapAny(geometry, transform, layer);
+	}
+
+	bool PhysX::convexOverlapAny(const HPhysicsMesh& mesh, const Vector3& position, const Quaternion& rotation,
+		UINT64 layer) const
+	{
+		if (mesh == nullptr)
+			return false;
+
+		PhysXMesh* physxMesh = static_cast<PhysXMesh*>(mesh.get());
+		if (physxMesh->getType() != PhysicsMeshType::Convex)
+			return false;
+
+		PxConvexMeshGeometry geometry(physxMesh->_getConvex());
+		PxTransform transform = toPxTransform(position, rotation);
+
+		return overlapAny(geometry, transform, layer);
+	}
+
+	bool PhysX::_rayCast(const Vector3& origin, const Vector3& unitDir, const Collider& collider, PhysicsQueryHit& hit,
+		float maxDist) const
+	{
+		FPhysXCollider* physxCollider = static_cast<FPhysXCollider*>(collider._getInternal());
+		PxShape* shape = physxCollider->_getShape();
+
+		PxTransform transform = toPxTransform(collider.getPosition(), collider.getRotation());
+
+		PxRaycastHit hitInfo;
+		PxU32 maxHits = 1;
+		bool anyHit = false;
+		PxHitFlags hitFlags = PxHitFlag::eDEFAULT | PxHitFlag::eUV;
+		PxU32 hitCount = PxGeometryQuery::raycast(toPxVector(origin), toPxVector(unitDir),
+			shape->getGeometry().any(), transform,
+			maxDist, hitFlags, maxHits, &hitInfo, anyHit);
+
+		if(hitCount > 0)
+			parseHit(hitInfo, hit);
+
+		return hitCount > 0;
+
+	}
+
+	bool PhysX::sweep(const PxGeometry& geometry, const PxTransform& tfrm, const Vector3& unitDir,
+		PhysicsQueryHit& hit, UINT64 layer, float maxDist) const
+	{
+		PxSweepBuffer output;
+
+		PxQueryFilterData filterData;
+		memcpy(&filterData.data.word0, &layer, sizeof(layer));
+
+		bool wasHit = mScene->sweep(geometry, tfrm, toPxVector(unitDir), maxDist, output,
+			PxHitFlag::eDEFAULT | PxHitFlag::eUV, filterData);
+
+		if (wasHit)
+			parseHit(output.block, hit);
+
+		return wasHit;
+	}
+
+	bool PhysX::overlapAny(const PxGeometry& geometry, const PxTransform& tfrm, UINT64 layer) const
+	{
+		PxOverlapBuffer output;
+
+		PxQueryFilterData filterData;
+		memcpy(&filterData.data.word0, &layer, sizeof(layer));
+
+		return mScene->overlap(geometry, tfrm, output, filterData);
+	}
+
+	Vector<Collider*> PhysX::overlap(const PxGeometry& geometry, const PxTransform& tfrm, UINT64 layer) const
+	{
+		PhysXOverlapQueryCallback output;
+
+		PxQueryFilterData filterData;
+		memcpy(&filterData.data.word0, &layer, sizeof(layer));
+
+		mScene->overlap(geometry, tfrm, output, filterData);
+		return output.data;
+	}
+
+	void PhysX::setFlag(PhysicsFlags flag, bool enabled)
+	{
+		Physics::setFlag(flag, enabled);
+
+		mCharManager->setOverlapRecoveryModule(mFlags.isSet(PhysicsFlag::CCT_OverlapRecovery));
+		mCharManager->setPreciseSweeps(mFlags.isSet(PhysicsFlag::CCT_PreciseSweeps));
+		mCharManager->setTessellation(mFlags.isSet(PhysicsFlag::CCT_Tesselation), mTesselationLength);
+	}
+
+	Vector3 PhysX::getGravity() const
+	{
+		return fromPxVector(mScene->getGravity());
+	}
+
+	void PhysX::setGravity(const Vector3& gravity)
+	{
+		mScene->setGravity(toPxVector(gravity));
+	}
+
+	void PhysX::setMaxTesselationEdgeLength(float length)
+	{
+		mTesselationLength = length;
+
+		mCharManager->setTessellation(mFlags.isSet(PhysicsFlag::CCT_Tesselation), mTesselationLength);
+	}
+
+	UINT32 PhysX::addBroadPhaseRegion(const AABox& region)
+	{
+		UINT32 id = mNextRegionIdx++;
+
+		PxBroadPhaseRegion pxRegion;
+		pxRegion.bounds = PxBounds3(toPxVector(region.getMin()), toPxVector(region.getMax()));
+		pxRegion.userData = (void*)(UINT64)id;
+
+		UINT32 handle = mScene->addBroadPhaseRegion(pxRegion, true);
+		mBroadPhaseRegionHandles[id] = handle;
+
+		return handle;
+	}
+
+	void PhysX::removeBroadPhaseRegion(UINT32 regionId)
+	{
+		auto iterFind = mBroadPhaseRegionHandles.find(regionId);
+		if (iterFind == mBroadPhaseRegionHandles.end())
+			return;
+
+		mScene->removeBroadPhaseRegion(iterFind->second);
+		mBroadPhaseRegionHandles.erase(iterFind);
+	}
+
+	void PhysX::clearBroadPhaseRegions()
+	{
+		for(auto& entry : mBroadPhaseRegionHandles)
+			mScene->removeBroadPhaseRegion(entry.second);
+
+		mBroadPhaseRegionHandles.clear();
+	}
+
+	PhysX& gPhysX()
+	{
+		return static_cast<PhysX&>(PhysX::instance());
+	}
 }

+ 139 - 141
Source/BansheeUtility/Include/BsTaskScheduler.h

@@ -1,142 +1,140 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#pragma once
-
-#include "BsPrerequisitesUtil.h"
-#include "BsModule.h"
-#include "BsThreadPool.h"
-
-namespace BansheeEngine
-{
-	/** @addtogroup Threading
-	 *  @{
-	 */
-	class TaskScheduler;
-
-	/** Task priority. Tasks with higher priority will get executed sooner. */
-	enum class TaskPriority
-	{
-		VeryLow = 98,
-		Low = 99,
-		Normal = 100,
-		High = 101,
-		VeryHigh = 102
-	};
-
-	/**
-	 * Represents a single task that may be queued in the TaskScheduler.
-	 * 			
-	 * @note	Thread safe.
-	 */
-	class BS_UTILITY_EXPORT Task
-	{
-		struct PrivatelyConstruct {};
-
-	public:
-		Task(const PrivatelyConstruct& dummy, const String& name, std::function<void()> taskWorker, 
-			TaskPriority priority, TaskPtr dependency);
-
-		/**
-		 * Creates a new task. Task should be provided to TaskScheduler in order for it to start.
-		 *
-		 * @param[in]	name		Name you can use to more easily identify the task.
-		 * @param[in]	taskWorker	Worker method that does all of the work in the task.
-		 * @param[in]	priority  	(optional) Higher priority means the tasks will be executed sooner.
-		 * @param[in]	dependency	(optional) Task dependency if one exists. If provided the task will
-		 * 							not be executed until its dependency is complete.
-		 */
-		static TaskPtr create(const String& name, std::function<void()> taskWorker, TaskPriority priority = TaskPriority::Normal, 
-			TaskPtr dependency = nullptr);
-
-		/** Returns true if the task has completed. */
-		bool isComplete() const;
-
-		/**	Returns true if the task has been canceled. */
-		bool isCanceled() const;
-
-		/**
-		 * Blocks the current thread until the task has completed. 
-		 * 
-		 * @note	While waiting adds a new worker thread, so that the blocking threads core can be utilized.
-		 */
-		void wait();
-
-		/** Cancels the task and removes it from the TaskSchedulers queue. */
-		void cancel();
-
-	private:
-		friend class TaskScheduler;
-
-		String mName;
-		TaskPriority mPriority;
-		UINT32 mTaskId;
-		std::function<void()> mTaskWorker;
-		TaskPtr mTaskDependency;
-		std::atomic<UINT32> mState; /**< 0 - Inactive, 1 - In progress, 2 - Completed, 3 - Canceled */
-
-		TaskScheduler* mParent;
-	};
-
-	/**
-	 * Represents a task scheduler running on multiple threads. You may queue tasks on it from any thread and they will be
-	 * executed in user specified order on any available thread.
-	 * 			
-	 * @note	
-	 * Thread safe.
-	 * @note
-	 * This type of task scheduler uses a global queue and is best used for coarse granularity of tasks. (Number of tasks 
-	 * in the order of hundreds. Higher number of tasks might require different queuing and locking mechanism, potentially 
-	 * at the cost of flexibility.)
-	 * @note
-	 * By default the task scheduler will create as many threads as there are physical CPU cores. You may add or remove
-	 * threads using addWorker()/removeWorker() methods.
-	 */
-	class BS_UTILITY_EXPORT TaskScheduler : public Module<TaskScheduler>
-	{
-	public:
-		TaskScheduler();
-		~TaskScheduler();
-
-		/** Queues a new task. */
-		void addTask(const TaskPtr& task);
-
-		/**	Adds a new worker thread which will be used for executing queued tasks. */
-		void addWorker();
-
-		/**	Removes a worker thread (as soon as its current task is finished). */
-		void removeWorker();
-
-		/** Returns the maximum available worker threads (maximum number of tasks that can be executed simultaneously). */
-		UINT32 getNumWorkers() const { return mMaxActiveTasks; }
-	protected:
-		friend class Task;
-
-		/**	Main task scheduler method that dispatches tasks to other threads. */
-		void runMain();
-
-		/**	Worker method that runs a single task. */
-		void runTask(TaskPtr task);
-
-		/**	Blocks the calling thread until the specified task has completed. */
-		void waitUntilComplete(const Task* task);
-
-		/**	Method used for sorting tasks. */
-		static bool taskCompare(const TaskPtr& lhs, const TaskPtr& rhs);
-
-		HThread mTaskSchedulerThread;
-		Set<TaskPtr, std::function<bool(const TaskPtr&, const TaskPtr&)>> mTaskQueue;
-		Vector<TaskPtr> mActiveTasks;
-		UINT32 mNumActiveTasks;
-		UINT32 mMaxActiveTasks;
-		UINT32 mNextTaskId;
-		bool mShutdown;
-
-		BS_MUTEX(mReadyMutex);
-		BS_MUTEX(mCompleteMutex);
-		BS_MUTEX(mActiveTaskMutex);
-		BS_THREAD_SYNCHRONISER(mTaskReadyCond);
-		BS_THREAD_SYNCHRONISER(mTaskCompleteCond);
-	};
-
-	/** @} */
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsPrerequisitesUtil.h"
+#include "BsModule.h"
+#include "BsThreadPool.h"
+
+namespace BansheeEngine
+{
+	/** @addtogroup Threading
+	 *  @{
+	 */
+	class TaskScheduler;
+
+	/** Task priority. Tasks with higher priority will get executed sooner. */
+	enum class TaskPriority
+	{
+		VeryLow = 98,
+		Low = 99,
+		Normal = 100,
+		High = 101,
+		VeryHigh = 102
+	};
+
+	/**
+	 * Represents a single task that may be queued in the TaskScheduler.
+	 * 			
+	 * @note	Thread safe.
+	 */
+	class BS_UTILITY_EXPORT Task
+	{
+		struct PrivatelyConstruct {};
+
+	public:
+		Task(const PrivatelyConstruct& dummy, const String& name, std::function<void()> taskWorker, 
+			TaskPriority priority, TaskPtr dependency);
+
+		/**
+		 * Creates a new task. Task should be provided to TaskScheduler in order for it to start.
+		 *
+		 * @param[in]	name		Name you can use to more easily identify the task.
+		 * @param[in]	taskWorker	Worker method that does all of the work in the task.
+		 * @param[in]	priority  	(optional) Higher priority means the tasks will be executed sooner.
+		 * @param[in]	dependency	(optional) Task dependency if one exists. If provided the task will
+		 * 							not be executed until its dependency is complete.
+		 */
+		static TaskPtr create(const String& name, std::function<void()> taskWorker, TaskPriority priority = TaskPriority::Normal, 
+			TaskPtr dependency = nullptr);
+
+		/** Returns true if the task has completed. */
+		bool isComplete() const;
+
+		/**	Returns true if the task has been canceled. */
+		bool isCanceled() const;
+
+		/**
+		 * Blocks the current thread until the task has completed. 
+		 * 
+		 * @note	While waiting adds a new worker thread, so that the blocking threads core can be utilized.
+		 */
+		void wait();
+
+		/** Cancels the task and removes it from the TaskSchedulers queue. */
+		void cancel();
+
+	private:
+		friend class TaskScheduler;
+
+		String mName;
+		TaskPriority mPriority;
+		UINT32 mTaskId;
+		std::function<void()> mTaskWorker;
+		TaskPtr mTaskDependency;
+		std::atomic<UINT32> mState; /**< 0 - Inactive, 1 - In progress, 2 - Completed, 3 - Canceled */
+
+		TaskScheduler* mParent;
+	};
+
+	/**
+	 * Represents a task scheduler running on multiple threads. You may queue tasks on it from any thread and they will be
+	 * executed in user specified order on any available thread.
+	 * 			
+	 * @note	
+	 * Thread safe.
+	 * @note
+	 * This type of task scheduler uses a global queue and is best used for coarse granularity of tasks. (Number of tasks 
+	 * in the order of hundreds. Higher number of tasks might require different queuing and locking mechanism, potentially 
+	 * at the cost of flexibility.)
+	 * @note
+	 * By default the task scheduler will create as many threads as there are physical CPU cores. You may add or remove
+	 * threads using addWorker()/removeWorker() methods.
+	 */
+	class BS_UTILITY_EXPORT TaskScheduler : public Module<TaskScheduler>
+	{
+	public:
+		TaskScheduler();
+		~TaskScheduler();
+
+		/** Queues a new task. */
+		void addTask(const TaskPtr& task);
+
+		/**	Adds a new worker thread which will be used for executing queued tasks. */
+		void addWorker();
+
+		/**	Removes a worker thread (as soon as its current task is finished). */
+		void removeWorker();
+
+		/** Returns the maximum available worker threads (maximum number of tasks that can be executed simultaneously). */
+		UINT32 getNumWorkers() const { return mMaxActiveTasks; }
+	protected:
+		friend class Task;
+
+		/**	Main task scheduler method that dispatches tasks to other threads. */
+		void runMain();
+
+		/**	Worker method that runs a single task. */
+		void runTask(TaskPtr task);
+
+		/**	Blocks the calling thread until the specified task has completed. */
+		void waitUntilComplete(const Task* task);
+
+		/**	Method used for sorting tasks. */
+		static bool taskCompare(const TaskPtr& lhs, const TaskPtr& rhs);
+
+		HThread mTaskSchedulerThread;
+		Set<TaskPtr, std::function<bool(const TaskPtr&, const TaskPtr&)>> mTaskQueue;
+		Vector<TaskPtr> mActiveTasks;
+		UINT32 mMaxActiveTasks;
+		UINT32 mNextTaskId;
+		bool mShutdown;
+
+		BS_MUTEX(mReadyMutex);
+		BS_MUTEX(mCompleteMutex);
+		BS_THREAD_SYNCHRONISER(mTaskReadyCond);
+		BS_THREAD_SYNCHRONISER(mTaskCompleteCond);
+	};
+
+	/** @} */
 }

+ 187 - 189
Source/BansheeUtility/Source/BsTaskScheduler.cpp

@@ -1,190 +1,188 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsTaskScheduler.h"
-#include "BsThreadPool.h"
-
-namespace BansheeEngine
-{
-	Task::Task(const PrivatelyConstruct& dummy, const String& name, std::function<void()> taskWorker, 
-		TaskPriority priority, TaskPtr dependency)
-		:mName(name), mPriority(priority), mTaskId(0), mTaskWorker(taskWorker), mTaskDependency(dependency),
-		mParent(nullptr)
-	{
-
-	}
-
-	TaskPtr Task::create(const String& name, std::function<void()> taskWorker, TaskPriority priority, TaskPtr dependency)
-	{
-		return bs_shared_ptr_new<Task>(PrivatelyConstruct(), name, taskWorker, priority, dependency);
-	}
-
-	bool Task::isComplete() const
-	{
-		return mState.load() == 2;
-	}
-
-	bool Task::isCanceled() const
-	{
-		return mState.load() == 3;
-	}
-
-	void Task::wait()
-	{
-		mParent->waitUntilComplete(this);
-	}
-
-	void Task::cancel()
-	{
-		mState.store(3);
-	}
-
-	TaskScheduler::TaskScheduler()
-		:mTaskQueue(&TaskScheduler::taskCompare), mNumActiveTasks(0), mMaxActiveTasks(0), mNextTaskId(0), mShutdown(false)
-	{
-		mMaxActiveTasks = BS_THREAD_HARDWARE_CONCURRENCY;
-
-		mTaskSchedulerThread = ThreadPool::instance().run("TaskScheduler", std::bind(&TaskScheduler::runMain, this));
-	}
-
-	TaskScheduler::~TaskScheduler()
-	{
-		// Wait until all tasks complete
-		BS_LOCK_MUTEX_NAMED(mActiveTaskMutex, activeTaskLock);
-
-		while (mActiveTasks.size() > 0)
-		{
-			TaskPtr task = mActiveTasks[0];
-			activeTaskLock.unlock();
-
-			task->wait();
-			activeTaskLock.lock();
-		}
-
-		// Start shutdown of the main queue worker and wait until it exits
-		{
-			BS_LOCK_MUTEX(mReadyMutex);
-
-			mShutdown = true;
-		}
-
-		BS_THREAD_NOTIFY_ONE(mTaskReadyCond);
-
-		mTaskSchedulerThread.blockUntilComplete();
-	}
-
-	void TaskScheduler::addTask(const TaskPtr& task)
-	{
-		BS_LOCK_MUTEX(mReadyMutex);
-
-		task->mParent = this;
-		task->mTaskId = mNextTaskId++;
-
-		mTaskQueue.insert(task);
-
-		// Wake main scheduler thread
-		BS_THREAD_NOTIFY_ONE(mTaskReadyCond);
-	}
-
-	void TaskScheduler::addWorker()
-	{
-		BS_LOCK_MUTEX(mReadyMutex);
-
-		mMaxActiveTasks++;
-
-		// A spot freed up, queue new tasks on main scheduler thread if they exist
-		BS_THREAD_NOTIFY_ONE(mTaskReadyCond);
-	}
-
-	void TaskScheduler::removeWorker()
-	{
-		BS_LOCK_MUTEX(mReadyMutex);
-
-		if(mMaxActiveTasks > 0)
-			mMaxActiveTasks--;
-	}
-
-	void TaskScheduler::runMain()
-	{
-		while(true)
-		{
-			BS_LOCK_MUTEX_NAMED(mReadyMutex, lock);
-
-			while((mTaskQueue.size() == 0 || mNumActiveTasks == mMaxActiveTasks) && !mShutdown)
-				BS_THREAD_WAIT(mTaskReadyCond, mReadyMutex, lock);
-
-			if(mShutdown)
-				break;
-
-			for(UINT32 i = 0; (i < mTaskQueue.size()) && (mNumActiveTasks < mMaxActiveTasks); i++)
-			{
-				TaskPtr curTask = *mTaskQueue.begin();
-				mTaskQueue.erase(mTaskQueue.begin());
-
-				if(curTask->isCanceled())
-					continue;
-
-				if(curTask->mTaskDependency != nullptr && !curTask->mTaskDependency->isComplete())
-					continue;
-
-				BS_LOCK_MUTEX(mActiveTaskMutex);
-				{
-					curTask->mState.store(1);
-					mActiveTasks.push_back(curTask);
-					mNumActiveTasks++;
-				}
-
-				ThreadPool::instance().run(curTask->mName, std::bind(&TaskScheduler::runTask, this, curTask));
-			}
-		}
-	}
-
-	void TaskScheduler::runTask(TaskPtr task)
-	{
-		task->mTaskWorker();
-
-		{
-			BS_LOCK_MUTEX(mActiveTaskMutex);
-
-			auto findIter = std::find(mActiveTasks.begin(), mActiveTasks.end(), task);
-			if (findIter != mActiveTasks.end())
-				mActiveTasks.erase(findIter);
-		}
-
-		{
-			BS_LOCK_MUTEX(mCompleteMutex);
-			task->mState.store(2);
-
-			BS_THREAD_NOTIFY_ALL(mTaskCompleteCond);
-		}
-
-		// Possibly this task was someones dependency, so wake the main scheduler thread
-		BS_THREAD_NOTIFY_ONE(mTaskReadyCond);
-	}
-
-	void TaskScheduler::waitUntilComplete(const Task* task)
-	{
-		if(task->isCanceled())
-			return;
-
-		{
-			BS_LOCK_MUTEX_NAMED(mCompleteMutex, lock);
-			
-			while(!task->isComplete())
-			{
-				addWorker();
-				BS_THREAD_WAIT(mTaskCompleteCond, mCompleteMutex, lock);
-				removeWorker();
-			}
-		}
-	}
-
-	bool TaskScheduler::taskCompare(const TaskPtr& lhs, const TaskPtr& rhs)
-	{
-		// If one tasks priority is higher, that one goes first
-		if(lhs->mPriority > rhs->mPriority)
-			return true;
-
-		// Otherwise we go by smaller id, as that task was queued earlier than the other
-		return lhs->mTaskId < rhs->mTaskId;
-	}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsTaskScheduler.h"
+#include "BsThreadPool.h"
+
+namespace BansheeEngine
+{
+	Task::Task(const PrivatelyConstruct& dummy, const String& name, std::function<void()> taskWorker, 
+		TaskPriority priority, TaskPtr dependency)
+		:mName(name), mPriority(priority), mTaskId(0), mTaskWorker(taskWorker), mTaskDependency(dependency),
+		mParent(nullptr)
+	{
+
+	}
+
+	TaskPtr Task::create(const String& name, std::function<void()> taskWorker, TaskPriority priority, TaskPtr dependency)
+	{
+		return bs_shared_ptr_new<Task>(PrivatelyConstruct(), name, taskWorker, priority, dependency);
+	}
+
+	bool Task::isComplete() const
+	{
+		return mState.load() == 2;
+	}
+
+	bool Task::isCanceled() const
+	{
+		return mState.load() == 3;
+	}
+
+	void Task::wait()
+	{
+		mParent->waitUntilComplete(this);
+	}
+
+	void Task::cancel()
+	{
+		mState.store(3);
+	}
+
+	TaskScheduler::TaskScheduler()
+		:mTaskQueue(&TaskScheduler::taskCompare), mMaxActiveTasks(0), mNextTaskId(0), mShutdown(false)
+	{
+		mMaxActiveTasks = BS_THREAD_HARDWARE_CONCURRENCY;
+
+		mTaskSchedulerThread = ThreadPool::instance().run("TaskScheduler", std::bind(&TaskScheduler::runMain, this));
+	}
+
+	TaskScheduler::~TaskScheduler()
+	{
+		// Wait until all tasks complete
+		{
+			BS_LOCK_MUTEX_NAMED(mReadyMutex, activeTaskLock);
+
+			while (mActiveTasks.size() > 0)
+			{
+				TaskPtr task = mActiveTasks[0];
+				activeTaskLock.unlock();
+
+				task->wait();
+				activeTaskLock.lock();
+			}
+		}
+
+		// Start shutdown of the main queue worker and wait until it exits
+		{
+			BS_LOCK_MUTEX(mReadyMutex);
+
+			mShutdown = true;
+		}
+
+		BS_THREAD_NOTIFY_ONE(mTaskReadyCond);
+
+		mTaskSchedulerThread.blockUntilComplete();
+	}
+
+	void TaskScheduler::addTask(const TaskPtr& task)
+	{
+		BS_LOCK_MUTEX(mReadyMutex);
+
+		task->mParent = this;
+		task->mTaskId = mNextTaskId++;
+
+		mTaskQueue.insert(task);
+
+		// Wake main scheduler thread
+		BS_THREAD_NOTIFY_ONE(mTaskReadyCond);
+	}
+
+	void TaskScheduler::addWorker()
+	{
+		BS_LOCK_MUTEX(mReadyMutex);
+
+		mMaxActiveTasks++;
+
+		// A spot freed up, queue new tasks on main scheduler thread if they exist
+		BS_THREAD_NOTIFY_ONE(mTaskReadyCond);
+	}
+
+	void TaskScheduler::removeWorker()
+	{
+		BS_LOCK_MUTEX(mReadyMutex);
+
+		if(mMaxActiveTasks > 0)
+			mMaxActiveTasks--;
+	}
+
+	void TaskScheduler::runMain()
+	{
+		while(true)
+		{
+			BS_LOCK_MUTEX_NAMED(mReadyMutex, lock);
+
+			while((mTaskQueue.size() == 0 || (UINT32)mActiveTasks.size() >= mMaxActiveTasks) && !mShutdown)
+				BS_THREAD_WAIT(mTaskReadyCond, mReadyMutex, lock);
+
+			if(mShutdown)
+				break;
+
+			for(UINT32 i = 0; (i < mTaskQueue.size()) && ((UINT32)mActiveTasks.size() < mMaxActiveTasks); i++)
+			{
+				TaskPtr curTask = *mTaskQueue.begin();
+				mTaskQueue.erase(mTaskQueue.begin());
+
+				if(curTask->isCanceled())
+					continue;
+
+				if(curTask->mTaskDependency != nullptr && !curTask->mTaskDependency->isComplete())
+					continue;
+
+				curTask->mState.store(1);
+				mActiveTasks.push_back(curTask);
+
+				ThreadPool::instance().run(curTask->mName, std::bind(&TaskScheduler::runTask, this, curTask));
+			}
+		}
+	}
+
+	void TaskScheduler::runTask(TaskPtr task)
+	{
+		task->mTaskWorker();
+
+		{
+			BS_LOCK_MUTEX(mReadyMutex);
+
+			auto findIter = std::find(mActiveTasks.begin(), mActiveTasks.end(), task);
+			if (findIter != mActiveTasks.end())
+				mActiveTasks.erase(findIter);
+		}
+
+		{
+			BS_LOCK_MUTEX(mCompleteMutex);
+			task->mState.store(2);
+
+			BS_THREAD_NOTIFY_ALL(mTaskCompleteCond);
+		}
+
+		// Possibly this task was someones dependency, so wake the main scheduler thread
+		BS_THREAD_NOTIFY_ONE(mTaskReadyCond);
+	}
+
+	void TaskScheduler::waitUntilComplete(const Task* task)
+	{
+		if(task->isCanceled())
+			return;
+
+		{
+			BS_LOCK_MUTEX_NAMED(mCompleteMutex, lock);
+			
+			while(!task->isComplete())
+			{
+				addWorker();
+				BS_THREAD_WAIT(mTaskCompleteCond, mCompleteMutex, lock);
+				removeWorker();
+			}
+		}
+	}
+
+	bool TaskScheduler::taskCompare(const TaskPtr& lhs, const TaskPtr& rhs)
+	{
+		// If one tasks priority is higher, that one goes first
+		if(lhs->mPriority > rhs->mPriority)
+			return true;
+
+		// Otherwise we go by smaller id, as that task was queued earlier than the other
+		return lhs->mTaskId < rhs->mTaskId;
+	}
 }

+ 173 - 165
Source/MBansheeEditor/EditorWindow.cs

@@ -1,165 +1,173 @@
-//********************************** 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 BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Base class for all editor windows. Editor window can be docked on the main editor window or float as a separate 
-    /// window. User is allowed to reposition and resize the window as he wishes. Editor window GUI is fully customizable.
-    /// </summary>
-    public class EditorWindow : ScriptObject
-    {
-        /// <summary>
-        /// Width of the window in pixels.
-        /// </summary>
-        public int Width
-        {
-            get { return Internal_GetWidth(mCachedPtr); }
-        }
-
-        /// <summary>
-        /// Height of the window in pixels.
-        /// </summary>
-        public int Height
-        {
-            get { return Internal_GetHeight(mCachedPtr); }
-        }
-
-        /// <summary>
-        /// Screen space bounds of the window.
-        /// </summary>
-        public Rect2I Bounds
-        {
-            get
-            {
-                Rect2I bounds;
-                Internal_GetBounds(mCachedPtr, out bounds);
-                return bounds;
-            }
-        }
-
-        /// <summary>
-        /// Determines whether the editor window currently has keyboard focus (has been clicked on most recently).
-        /// </summary>
-        public bool HasFocus { get { return Internal_HasFocus(mCachedPtr); } }
-
-        /// <summary>
-        /// Determines is the mouse pointer currently hovering over the editor window.
-        /// </summary>
-        public bool IsPointerHovering { get { return Internal_IsPointerHovering(mCachedPtr); } }
-
-        /// <summary>
-        /// Checks if the window's tab is currently active. If the window is floating or not sharing space with any other
-        /// windows (just a single tab), it is always considered active.
-        /// </summary>
-        public bool Active { get { return Internal_IsActive(mCachedPtr); } }
-
-        protected GUIPanel GUI;
-
-        /// <summary>
-        /// Opens an editor window. If window is already open it returns the existing instance.
-        /// </summary>
-        /// <typeparam name="T">Type of window to open.</typeparam>
-        /// <returns>Instance of the open window.</returns>
-        public static T OpenWindow<T>() where T : EditorWindow
-        {
-            return (T)Internal_CreateOrGetInstance(typeof(T).Namespace, typeof(T).Name);
-        }
-
-        /// <summary>
-        /// Retrieves instance of an open window.
-        /// </summary>
-        /// <typeparam name="T">Type of window to retrieve the instance of.</typeparam>
-        /// <returns>Instance of the winodow if it is open, null otherwise.</returns>
-        public static T GetWindow<T>() where T : EditorWindow
-        {
-            return (T)Internal_GetInstance(typeof(T).Namespace, typeof(T).Name);
-        }
-
-        /// <summary>
-        /// Converts coordinates in screen space to coordinates relative to the window.
-        /// </summary>
-        /// <param name="screenPos">Coordinates in screen space.</param>
-        /// <returns>Coordinates relative to the window.</returns>
-        public Vector2I ScreenToWindowPos(Vector2I screenPos)
-        {
-            Vector2I windowPos;
-            Internal_ScreenToWindowPos(mCachedPtr, ref screenPos, out windowPos);
-            return windowPos;
-        }
-
-        /// <summary>
-        /// Converts coordinates relative to the window to screen space to coordinates.
-        /// </summary>
-        /// <param name="windowPos">Coordinates relative to the window.</param>
-        /// <returns>Coordinates in screen space.</returns>
-        public Vector2I WindowToScreenPos(Vector2I windowPos)
-        {
-            Vector2I screenPos;
-            Internal_WindowToScreenPos(mCachedPtr, ref windowPos, out screenPos);
-            return screenPos;
-        }
-
-        /// <summary>
-        /// Triggered whenever the window size ranges.
-        /// </summary>
-        /// <param name="width">New width of the window in pixels.</param>
-        /// <param name="height">New height of the window in pixels.</param>
-        protected virtual void WindowResized(int width, int height)
-        {
-
-        }
-
-        /// <summary>
-        /// Triggered whenever the window gains or loses focus.
-        /// </summary>
-        /// <param name="inFocus">True if focus was gained, false otherwise.</param>
-        protected virtual void FocusChanged(bool inFocus)
-        {
-            
-        }
-
-        /// <summary>
-        /// Name of the window to display in the title bar.
-        /// </summary>
-        /// <returns>Name of the window to display in the title bar.</returns>
-        protected virtual LocString GetDisplayName()
-        {
-            return GetType().Name;
-        }
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern EditorWindow Internal_CreateOrGetInstance(string ns, string typeName);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern EditorWindow Internal_GetInstance(string ns, string typeName);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern int Internal_GetWidth(IntPtr nativeInstance);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern int Internal_GetHeight(IntPtr nativeInstance);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern bool Internal_HasFocus(IntPtr nativeInstance);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern bool Internal_IsActive(IntPtr nativeInstance);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern bool Internal_IsPointerHovering(IntPtr nativeInstance);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_GetBounds(IntPtr nativeInstance, out Rect2I bounds);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_ScreenToWindowPos(IntPtr nativeInstance, ref Vector2I position, out Vector2I windowPos);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_WindowToScreenPos(IntPtr nativeInstance, ref Vector2I position, out Vector2I screenPos);
-    }
-}
+//********************************** 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 BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Base class for all editor windows. Editor window can be docked on the main editor window or float as a separate 
+    /// window. User is allowed to reposition and resize the window as he wishes. Editor window GUI is fully customizable.
+    /// </summary>
+    public class EditorWindow : ScriptObject
+    {
+        /// <summary>
+        /// Width of the window in pixels.
+        /// </summary>
+        public int Width
+        {
+            get { return Internal_GetWidth(mCachedPtr); }
+        }
+
+        /// <summary>
+        /// Height of the window in pixels.
+        /// </summary>
+        public int Height
+        {
+            get { return Internal_GetHeight(mCachedPtr); }
+        }
+
+        /// <summary>
+        /// Screen space bounds of the window.
+        /// </summary>
+        public Rect2I Bounds
+        {
+            get
+            {
+                Rect2I bounds;
+                Internal_GetBounds(mCachedPtr, out bounds);
+                return bounds;
+            }
+        }
+
+        /// <summary>
+        /// Determines whether the editor window currently has keyboard focus (has been clicked on most recently). Window
+        /// that isn't active (i.e. is not the active tab) cannot be put into focus without activating it first.
+        /// </summary>
+        public bool HasFocus
+        {
+            get { return Internal_HasFocus(mCachedPtr); }
+            set { Internal_SetFocus(mCachedPtr, value); }
+        }
+
+        /// <summary>
+        /// Determines is the mouse pointer currently hovering over the editor window.
+        /// </summary>
+        public bool IsPointerHovering { get { return Internal_IsPointerHovering(mCachedPtr); } }
+
+        /// <summary>
+        /// Checks if the window's tab is currently active. If the window is floating or not sharing space with any other
+        /// windows (just a single tab), it is always considered active.
+        /// </summary>
+        public bool Active { get { return Internal_IsActive(mCachedPtr); } }
+
+        protected GUIPanel GUI;
+
+        /// <summary>
+        /// Opens an editor window. If window is already open it returns the existing instance.
+        /// </summary>
+        /// <typeparam name="T">Type of window to open.</typeparam>
+        /// <returns>Instance of the open window.</returns>
+        public static T OpenWindow<T>() where T : EditorWindow
+        {
+            return (T)Internal_CreateOrGetInstance(typeof(T).Namespace, typeof(T).Name);
+        }
+
+        /// <summary>
+        /// Retrieves instance of an open window.
+        /// </summary>
+        /// <typeparam name="T">Type of window to retrieve the instance of.</typeparam>
+        /// <returns>Instance of the winodow if it is open, null otherwise.</returns>
+        public static T GetWindow<T>() where T : EditorWindow
+        {
+            return (T)Internal_GetInstance(typeof(T).Namespace, typeof(T).Name);
+        }
+
+        /// <summary>
+        /// Converts coordinates in screen space to coordinates relative to the window.
+        /// </summary>
+        /// <param name="screenPos">Coordinates in screen space.</param>
+        /// <returns>Coordinates relative to the window.</returns>
+        public Vector2I ScreenToWindowPos(Vector2I screenPos)
+        {
+            Vector2I windowPos;
+            Internal_ScreenToWindowPos(mCachedPtr, ref screenPos, out windowPos);
+            return windowPos;
+        }
+
+        /// <summary>
+        /// Converts coordinates relative to the window to screen space to coordinates.
+        /// </summary>
+        /// <param name="windowPos">Coordinates relative to the window.</param>
+        /// <returns>Coordinates in screen space.</returns>
+        public Vector2I WindowToScreenPos(Vector2I windowPos)
+        {
+            Vector2I screenPos;
+            Internal_WindowToScreenPos(mCachedPtr, ref windowPos, out screenPos);
+            return screenPos;
+        }
+
+        /// <summary>
+        /// Triggered whenever the window size ranges.
+        /// </summary>
+        /// <param name="width">New width of the window in pixels.</param>
+        /// <param name="height">New height of the window in pixels.</param>
+        protected virtual void WindowResized(int width, int height)
+        {
+
+        }
+
+        /// <summary>
+        /// Triggered whenever the window gains or loses focus.
+        /// </summary>
+        /// <param name="inFocus">True if focus was gained, false otherwise.</param>
+        protected virtual void FocusChanged(bool inFocus)
+        {
+            
+        }
+
+        /// <summary>
+        /// Name of the window to display in the title bar.
+        /// </summary>
+        /// <returns>Name of the window to display in the title bar.</returns>
+        protected virtual LocString GetDisplayName()
+        {
+            return GetType().Name;
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern EditorWindow Internal_CreateOrGetInstance(string ns, string typeName);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern EditorWindow Internal_GetInstance(string ns, string typeName);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern int Internal_GetWidth(IntPtr nativeInstance);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern int Internal_GetHeight(IntPtr nativeInstance);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool Internal_HasFocus(IntPtr nativeInstance);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetFocus(IntPtr nativeInstance, bool focus);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool Internal_IsActive(IntPtr nativeInstance);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool Internal_IsPointerHovering(IntPtr nativeInstance);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_GetBounds(IntPtr nativeInstance, out Rect2I bounds);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_ScreenToWindowPos(IntPtr nativeInstance, ref Vector2I position, out Vector2I windowPos);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_WindowToScreenPos(IntPtr nativeInstance, ref Vector2I position, out Vector2I screenPos);
+    }
+}

+ 405 - 410
Source/MBansheeEditor/Library/LibraryGUIEntry.cs

@@ -1,410 +1,405 @@
-//********************************** 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.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Represents GUI for a single resource tile used in <see cref="LibraryGUIContent"/>.
-    /// </summary>
-    internal class LibraryGUIEntry
-    {
-        private const int MAX_LABEL_HEIGHT = 50;
-
-        private static readonly Color PING_COLOR = Color.BansheeOrange;
-        private static readonly Color SELECTION_COLOR = Color.DarkCyan;
-        private static readonly Color HOVER_COLOR = new Color(Color.DarkCyan.r, Color.DarkCyan.g, Color.DarkCyan.b, 0.5f);
-        private static readonly Color CUT_COLOR = new Color(1.0f, 1.0f, 1.0f, 0.5f);
-        private const int SELECTION_EXTRA_WIDTH = 3;
-
-        /// <summary>
-        /// Possible visual states for the resource tile.
-        /// </summary>
-        enum UnderlayState // Note: Order of these is relevant
-        {
-            None, Hovered, Selected, Pinged
-        }
-
-        public int index;
-        public string path;
-        public GUITexture icon;
-        public GUILabel label;
-        public Rect2I bounds;
-
-        private GUITexture underlay;
-        private LibraryGUIContent owner;
-        private UnderlayState underlayState;
-        private GUITextBox renameTextBox;
-
-        private bool delayedSelect;
-        private float delayedSelectTime;
-        private ulong delayedOpenCodeEditorFrame = ulong.MaxValue;
-
-        /// <summary>
-        /// Bounds of the entry relative to part content area.
-        /// </summary>
-        public Rect2I Bounds
-        {
-            get { return bounds; }
-        }
-
-        /// <summary>
-        /// Constructs a new resource tile entry.
-        /// </summary>
-        /// <param name="owner">Content area this entry is part of.</param>
-        /// <param name="parent">Parent layout to add this entry's GUI elements to.</param>
-        /// <param name="path">Path to the project library entry to display data for.</param>
-        /// <param name="index">Sequential index of the entry in the conent area.</param>
-        /// <param name="labelWidth">Width of the GUI labels that display the elements.</param>
-        public LibraryGUIEntry(LibraryGUIContent owner, GUILayout parent, string path, int index, int labelWidth)
-        {
-            GUILayout entryLayout;
-
-            if (owner.GridLayout)
-                entryLayout = parent.AddLayoutY();
-            else
-                entryLayout = parent.AddLayoutX();
-
-            SpriteTexture iconTexture = GetIcon(path, owner.TileSize);
-
-            icon = new GUITexture(iconTexture, GUIImageScaleMode.ScaleToFit,
-                true, GUIOption.FixedHeight(owner.TileSize), GUIOption.FixedWidth(owner.TileSize));
-
-            label = null;
-
-            string name = PathEx.GetTail(path);
-            if (owner.GridLayout)
-            {
-                label = new GUILabel(name, EditorStyles.MultiLineLabelCentered,
-                    GUIOption.FixedWidth(labelWidth), GUIOption.FlexibleHeight(0, MAX_LABEL_HEIGHT));
-            }
-            else
-            {
-                label = new GUILabel(name);
-            }
-
-            entryLayout.AddElement(icon);
-            entryLayout.AddElement(label);
-
-            this.owner = owner;
-            this.index = index;
-            this.path = path;
-            this.bounds = new Rect2I();
-            this.underlay = null;
-        }
-
-        /// <summary>
-        /// Positions the GUI elements. Must be called after construction, but only after all content area entries have
-        /// been constructed so that entry's final bounds are known.
-        /// </summary>
-        public void Initialize()
-        {
-            bounds = icon.Bounds;
-            Rect2I labelBounds = label.Bounds;
-
-            bounds.x = MathEx.Min(bounds.x, labelBounds.x - SELECTION_EXTRA_WIDTH);
-            bounds.y = MathEx.Min(bounds.y, labelBounds.y) - 5; // 5 - Just padding for better look
-            bounds.width = MathEx.Max(bounds.x + bounds.width,
-                labelBounds.x + labelBounds.width) - bounds.x + SELECTION_EXTRA_WIDTH;
-            bounds.height = MathEx.Max(bounds.y + bounds.height,
-                labelBounds.y + labelBounds.height) - bounds.y;
-
-            string hoistedPath = path;
-
-            GUIButton overlayBtn = new GUIButton("", EditorStyles.Blank);
-            overlayBtn.Bounds = bounds;
-            overlayBtn.OnClick += () => OnEntryClicked(hoistedPath);
-            overlayBtn.OnDoubleClick += () => OnEntryDoubleClicked(hoistedPath);
-            overlayBtn.SetContextMenu(owner.Window.ContextMenu);
-
-            owner.Overlay.AddElement(overlayBtn);
-        }
-
-        /// <summary>
-        /// Called every frame.
-        /// </summary>
-        public void Update()
-        {
-            if (delayedSelect && Time.RealElapsed > delayedSelectTime)
-            {
-                owner.Window.Select(path);
-                delayedSelect = false;
-            }
-
-            if (delayedOpenCodeEditorFrame == Time.FrameIdx)
-            {
-                LibraryEntry entry = ProjectLibrary.GetEntry(path);
-                if (entry != null && entry.Type == LibraryEntryType.File)
-                {
-                    FileEntry resEntry = (FileEntry) entry;
-                    CodeEditor.OpenFile(resEntry.Path, 0);
-                }
-
-                ProgressBar.Hide();
-            }
-        }
-
-        /// <summary>
-        /// Changes the visual representation of the element as being cut.
-        /// </summary>
-        /// <param name="enable">True if mark as cut, false to reset to normal.</param>
-        public void MarkAsCut(bool enable)
-        {
-            if (enable)
-                icon.SetTint(CUT_COLOR);
-            else
-                icon.SetTint(Color.White);
-        }
-
-        /// <summary>
-        /// Changes the visual representation of the element as being selected.
-        /// </summary>
-        /// <param name="enable">True if mark as selected, false to reset to normal.</param>
-        public void MarkAsSelected(bool enable)
-        {
-            if ((int)underlayState > (int)UnderlayState.Selected)
-                return;
-
-            if (enable)
-            {
-                CreateUnderlay();
-                underlay.SetTint(SELECTION_COLOR);
-                underlayState = UnderlayState.Selected;
-            }
-            else
-            {
-                ClearUnderlay();
-                underlayState = UnderlayState.None;
-            }
-        }
-
-        /// <summary>
-        /// Changes the visual representation of the element as being pinged.
-        /// </summary>
-        /// <param name="enable">True if mark as pinged, false to reset to normal.</param>
-        public void MarkAsPinged(bool enable)
-        {
-            if ((int)underlayState > (int)UnderlayState.Pinged)
-                return;
-
-            if (enable)
-            {
-                CreateUnderlay();
-                underlay.SetTint(PING_COLOR);
-                underlayState = UnderlayState.Pinged;
-            }
-            else
-            {
-                ClearUnderlay();
-                underlayState = UnderlayState.None;
-            }
-        }
-
-        /// <summary>
-        /// Changes the visual representation of the element as being hovered over.
-        /// </summary>
-        /// <param name="enable">True if mark as hovered, false to reset to normal.</param>
-        public void MarkAsHovered(bool enable)
-        {
-            if ((int)underlayState > (int)UnderlayState.Hovered)
-                return;
-
-            if (enable)
-            {
-                CreateUnderlay();
-                underlay.SetTint(HOVER_COLOR);
-                underlayState = UnderlayState.Hovered;
-            }
-            else
-            {
-                ClearUnderlay();
-                underlayState = UnderlayState.None;
-            }
-        }
-
-        /// <summary>
-        /// Starts a rename operation over the entry, displaying the rename input box.
-        /// </summary>
-        public void StartRename()
-        {
-            if (renameTextBox != null)
-                return;
-
-            renameTextBox = new GUITextBox(true);
-            Rect2I renameBounds = label.Bounds;
-
-            // Rename box allows for less space for text than label, so adjust it slightly so it's more likely to be able
-            // to display all visible text.
-            renameBounds.x -= 4;
-            renameBounds.width += 8;
-            renameBounds.height += 8;
-
-            renameTextBox.Bounds = renameBounds;
-            owner.RenameOverlay.AddElement(renameTextBox);
-
-            string name = Path.GetFileNameWithoutExtension(PathEx.GetTail(path));
-            renameTextBox.Text = name;
-            renameTextBox.Focus = true;
-        }
-
-        /// <summary>
-        /// Stops a rename operation over the entry, hiding the rename input box.
-        /// </summary>
-        public void StopRename()
-        {
-            if (renameTextBox != null)
-            {
-                renameTextBox.Destroy();
-                renameTextBox = null;
-            }
-        }
-
-        /// <summary>
-        /// Gets the new name of the entry. Only valid while a rename operation is in progress.
-        /// </summary>
-        /// <returns>New name of the entry currently entered in the rename input box.</returns>
-        public string GetRenamedName()
-        {
-            if (renameTextBox != null)
-                return renameTextBox.Text;
-
-            return "";
-        }
-
-        /// <summary>
-        /// Clears the underlay GUI element (e.g. ping, hover, select).
-        /// </summary>
-        private void ClearUnderlay()
-        {
-            if (underlay != null)
-            {
-                underlay.Destroy();
-                underlay = null;
-            }
-
-            underlayState = UnderlayState.None;
-        }
-
-        /// <summary>
-        /// Creates a GUI elements that may be used for underlay effects (e.g. ping, hover, select).
-        /// </summary>
-        private void CreateUnderlay()
-        {
-            if (underlay == null)
-            {
-                underlay = new GUITexture(Builtin.WhiteTexture);
-                underlay.Bounds = Bounds;
-
-                owner.Underlay.AddElement(underlay);
-            }
-        }
-
-        /// <summary>
-        /// Triggered when the user clicks on the entry.
-        /// </summary>
-        /// <param name="path">Project library path of the clicked entry.</param>
-        private void OnEntryClicked(string path)
-        {
-            LibraryEntry entry = ProjectLibrary.GetEntry(path);
-            if (entry != null && entry.Type == LibraryEntryType.Directory)
-            {
-                // If entry is a directory delay selection as it might be a double-click, in which case we want to keep
-                // whatever selection is active currently so that user can perform drag and drop with its inspector
-                // from the folder he is browsing to.
-
-                delayedSelect = true;
-                delayedSelectTime = Time.RealElapsed + 0.5f;
-            }
-            else
-                owner.Window.Select(path);
-        }
-
-        /// <summary>
-        /// Triggered when the user double-clicked on the entry.
-        /// </summary>
-        /// <param name="path">Project library path of the double-clicked entry.</param>
-        private void OnEntryDoubleClicked(string path)
-        {
-            delayedSelect = false;
-
-            LibraryEntry entry = ProjectLibrary.GetEntry(path);
-            if (entry != null)
-            {
-                if (entry.Type == LibraryEntryType.Directory)
-                    owner.Window.EnterDirectory(path);
-                else
-                {
-                    ResourceMeta meta = ProjectLibrary.GetMeta(path);
-
-                    FileEntry fileEntry = (FileEntry)entry;
-                    if (meta.ResType == ResourceType.Prefab)
-                    {
-                        EditorApplication.LoadScene(fileEntry.Path);
-                    }
-                    else if (meta.ResType == ResourceType.ScriptCode)
-                    {
-                        ProgressBar.Show("Opening external code editor...", 1.0f);
-
-                        delayedOpenCodeEditorFrame = Time.FrameIdx + 1;
-                    }
-                }
-            }
-        }
-
-        /// <summary>
-        /// Returns an icon that can be used for displaying a resource of the specified type.
-        /// </summary>
-        /// <param name="path">Path to the project library entry to display data for.</param>
-        /// <param name="size">Size of the icon to retrieve, in pixels.</param>
-        /// <returns>Icon to display for the specified entry.</returns>
-        private static SpriteTexture GetIcon(string path, int size)
-        {
-            LibraryEntry entry = ProjectLibrary.GetEntry(path);
-            if (entry.Type == LibraryEntryType.Directory)
-            {
-                return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.Folder, size);
-            }
-            else
-            {
-                ResourceMeta meta = ProjectLibrary.GetMeta(path);
-                switch (meta.ResType)
-                {
-                    case ResourceType.Font:
-                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.Font, size);
-                    case ResourceType.Mesh:
-                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.Mesh, size);
-                    case ResourceType.Texture:
-                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.Texture, size);
-                    case ResourceType.PlainText:
-                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.PlainText, size);
-                    case ResourceType.ScriptCode:
-                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.ScriptCode, size);
-                    case ResourceType.SpriteTexture:
-                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.SpriteTexture, size);
-                    case ResourceType.Shader:
-                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.Shader, size);
-                    case ResourceType.ShaderInclude:
-                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.Shader, size);
-                    case ResourceType.Material:
-                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.Material, size);
-                    case ResourceType.Prefab:
-                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.Prefab, size);
-                    case ResourceType.GUISkin:
-                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.GUISkin, size);
-                    case ResourceType.PhysicsMaterial:
-                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.PhysicsMaterial, size);
-                    case ResourceType.PhysicsMesh:
-                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.PhysicsMesh, size);
-                }
-            }
-
-            return null;
-        }
-    }
-}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System.IO;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Represents GUI for a single resource tile used in <see cref="LibraryGUIContent"/>.
+    /// </summary>
+    internal class LibraryGUIEntry
+    {
+        private const int MAX_LABEL_HEIGHT = 50;
+
+        private static readonly Color PING_COLOR = Color.BansheeOrange;
+        private static readonly Color SELECTION_COLOR = Color.DarkCyan;
+        private static readonly Color HOVER_COLOR = new Color(Color.DarkCyan.r, Color.DarkCyan.g, Color.DarkCyan.b, 0.5f);
+        private static readonly Color CUT_COLOR = new Color(1.0f, 1.0f, 1.0f, 0.5f);
+        private const int SELECTION_EXTRA_WIDTH = 3;
+
+        /// <summary>
+        /// Possible visual states for the resource tile.
+        /// </summary>
+        enum UnderlayState // Note: Order of these is relevant
+        {
+            None, Hovered, Selected, Pinged
+        }
+
+        public int index;
+        public string path;
+        public GUITexture icon;
+        public GUILabel label;
+        public Rect2I bounds;
+
+        private GUITexture underlay;
+        private LibraryGUIContent owner;
+        private UnderlayState underlayState;
+        private GUITextBox renameTextBox;
+
+        private bool delayedSelect;
+        private float delayedSelectTime;
+        private ulong delayedOpenCodeEditorFrame = ulong.MaxValue;
+
+        /// <summary>
+        /// Bounds of the entry relative to part content area.
+        /// </summary>
+        public Rect2I Bounds
+        {
+            get { return bounds; }
+        }
+
+        /// <summary>
+        /// Constructs a new resource tile entry.
+        /// </summary>
+        /// <param name="owner">Content area this entry is part of.</param>
+        /// <param name="parent">Parent layout to add this entry's GUI elements to.</param>
+        /// <param name="path">Path to the project library entry to display data for.</param>
+        /// <param name="index">Sequential index of the entry in the conent area.</param>
+        /// <param name="labelWidth">Width of the GUI labels that display the elements.</param>
+        public LibraryGUIEntry(LibraryGUIContent owner, GUILayout parent, string path, int index, int labelWidth)
+        {
+            GUILayout entryLayout;
+
+            if (owner.GridLayout)
+                entryLayout = parent.AddLayoutY();
+            else
+                entryLayout = parent.AddLayoutX();
+
+            SpriteTexture iconTexture = GetIcon(path, owner.TileSize);
+
+            icon = new GUITexture(iconTexture, GUIImageScaleMode.ScaleToFit,
+                true, GUIOption.FixedHeight(owner.TileSize), GUIOption.FixedWidth(owner.TileSize));
+
+            label = null;
+
+            string name = PathEx.GetTail(path);
+            if (owner.GridLayout)
+            {
+                label = new GUILabel(name, EditorStyles.MultiLineLabelCentered,
+                    GUIOption.FixedWidth(labelWidth), GUIOption.FlexibleHeight(0, MAX_LABEL_HEIGHT));
+            }
+            else
+            {
+                label = new GUILabel(name);
+            }
+
+            entryLayout.AddElement(icon);
+            entryLayout.AddElement(label);
+
+            this.owner = owner;
+            this.index = index;
+            this.path = path;
+            this.bounds = new Rect2I();
+            this.underlay = null;
+        }
+
+        /// <summary>
+        /// Positions the GUI elements. Must be called after construction, but only after all content area entries have
+        /// been constructed so that entry's final bounds are known.
+        /// </summary>
+        public void Initialize()
+        {
+            bounds = icon.Bounds;
+            Rect2I labelBounds = label.Bounds;
+
+            bounds.x = MathEx.Min(bounds.x, labelBounds.x - SELECTION_EXTRA_WIDTH);
+            bounds.y = MathEx.Min(bounds.y, labelBounds.y) - 5; // 5 - Just padding for better look
+            bounds.width = MathEx.Max(bounds.x + bounds.width,
+                labelBounds.x + labelBounds.width) - bounds.x + SELECTION_EXTRA_WIDTH;
+            bounds.height = MathEx.Max(bounds.y + bounds.height,
+                labelBounds.y + labelBounds.height) - bounds.y;
+
+            string hoistedPath = path;
+
+            GUIButton overlayBtn = new GUIButton("", EditorStyles.Blank);
+            overlayBtn.Bounds = bounds;
+            overlayBtn.OnClick += () => OnEntryClicked(hoistedPath);
+            overlayBtn.OnDoubleClick += () => OnEntryDoubleClicked(hoistedPath);
+            overlayBtn.SetContextMenu(owner.Window.ContextMenu);
+
+            owner.Overlay.AddElement(overlayBtn);
+        }
+
+        /// <summary>
+        /// Called every frame.
+        /// </summary>
+        public void Update()
+        {
+            if (delayedSelect && Time.RealElapsed > delayedSelectTime)
+            {
+                owner.Window.Select(path);
+                delayedSelect = false;
+            }
+
+            if (delayedOpenCodeEditorFrame == Time.FrameIdx)
+            {
+                LibraryEntry entry = ProjectLibrary.GetEntry(path);
+                if (entry != null && entry.Type == LibraryEntryType.File)
+                {
+                    FileEntry resEntry = (FileEntry) entry;
+                    CodeEditor.OpenFile(resEntry.Path, 0);
+                }
+
+                ProgressBar.Hide();
+            }
+        }
+
+        /// <summary>
+        /// Changes the visual representation of the element as being cut.
+        /// </summary>
+        /// <param name="enable">True if mark as cut, false to reset to normal.</param>
+        public void MarkAsCut(bool enable)
+        {
+            if (enable)
+                icon.SetTint(CUT_COLOR);
+            else
+                icon.SetTint(Color.White);
+        }
+
+        /// <summary>
+        /// Changes the visual representation of the element as being selected.
+        /// </summary>
+        /// <param name="enable">True if mark as selected, false to reset to normal.</param>
+        public void MarkAsSelected(bool enable)
+        {
+            if ((int)underlayState > (int)UnderlayState.Selected)
+                return;
+
+            if (enable)
+            {
+                CreateUnderlay();
+                underlay.SetTint(SELECTION_COLOR);
+                underlayState = UnderlayState.Selected;
+            }
+            else
+            {
+                ClearUnderlay();
+                underlayState = UnderlayState.None;
+            }
+        }
+
+        /// <summary>
+        /// Changes the visual representation of the element as being pinged.
+        /// </summary>
+        /// <param name="enable">True if mark as pinged, false to reset to normal.</param>
+        public void MarkAsPinged(bool enable)
+        {
+            if ((int)underlayState > (int)UnderlayState.Pinged)
+                return;
+
+            if (enable)
+            {
+                CreateUnderlay();
+                underlay.SetTint(PING_COLOR);
+                underlayState = UnderlayState.Pinged;
+            }
+            else
+            {
+                ClearUnderlay();
+                underlayState = UnderlayState.None;
+            }
+        }
+
+        /// <summary>
+        /// Changes the visual representation of the element as being hovered over.
+        /// </summary>
+        /// <param name="enable">True if mark as hovered, false to reset to normal.</param>
+        public void MarkAsHovered(bool enable)
+        {
+            if ((int)underlayState > (int)UnderlayState.Hovered)
+                return;
+
+            if (enable)
+            {
+                CreateUnderlay();
+                underlay.SetTint(HOVER_COLOR);
+                underlayState = UnderlayState.Hovered;
+            }
+            else
+            {
+                ClearUnderlay();
+                underlayState = UnderlayState.None;
+            }
+        }
+
+        /// <summary>
+        /// Starts a rename operation over the entry, displaying the rename input box.
+        /// </summary>
+        public void StartRename()
+        {
+            if (renameTextBox != null)
+                return;
+
+            renameTextBox = new GUITextBox(true);
+            Rect2I renameBounds = label.Bounds;
+
+            // Rename box allows for less space for text than label, so adjust it slightly so it's more likely to be able
+            // to display all visible text.
+            renameBounds.x -= 4;
+            renameBounds.width += 8;
+            renameBounds.height += 8;
+
+            renameTextBox.Bounds = renameBounds;
+            owner.RenameOverlay.AddElement(renameTextBox);
+
+            string name = Path.GetFileNameWithoutExtension(PathEx.GetTail(path));
+            renameTextBox.Text = name;
+            renameTextBox.Focus = true;
+        }
+
+        /// <summary>
+        /// Stops a rename operation over the entry, hiding the rename input box.
+        /// </summary>
+        public void StopRename()
+        {
+            if (renameTextBox != null)
+            {
+                renameTextBox.Destroy();
+                renameTextBox = null;
+            }
+        }
+
+        /// <summary>
+        /// Gets the new name of the entry. Only valid while a rename operation is in progress.
+        /// </summary>
+        /// <returns>New name of the entry currently entered in the rename input box.</returns>
+        public string GetRenamedName()
+        {
+            if (renameTextBox != null)
+                return renameTextBox.Text;
+
+            return "";
+        }
+
+        /// <summary>
+        /// Clears the underlay GUI element (e.g. ping, hover, select).
+        /// </summary>
+        private void ClearUnderlay()
+        {
+            if (underlay != null)
+            {
+                underlay.Destroy();
+                underlay = null;
+            }
+
+            underlayState = UnderlayState.None;
+        }
+
+        /// <summary>
+        /// Creates a GUI elements that may be used for underlay effects (e.g. ping, hover, select).
+        /// </summary>
+        private void CreateUnderlay()
+        {
+            if (underlay == null)
+            {
+                underlay = new GUITexture(Builtin.WhiteTexture);
+                underlay.Bounds = Bounds;
+
+                owner.Underlay.AddElement(underlay);
+            }
+        }
+
+        /// <summary>
+        /// Triggered when the user clicks on the entry.
+        /// </summary>
+        /// <param name="path">Project library path of the clicked entry.</param>
+        private void OnEntryClicked(string path)
+        {
+            LibraryEntry entry = ProjectLibrary.GetEntry(path);
+            if (entry != null && entry.Type == LibraryEntryType.Directory)
+            {
+                // If entry is a directory delay selection as it might be a double-click, in which case we want to keep
+                // whatever selection is active currently so that user can perform drag and drop with its inspector
+                // from the folder he is browsing to.
+
+                delayedSelect = true;
+                delayedSelectTime = Time.RealElapsed + 0.5f;
+            }
+            else
+                owner.Window.Select(path);
+        }
+
+        /// <summary>
+        /// Triggered when the user double-clicked on the entry.
+        /// </summary>
+        /// <param name="path">Project library path of the double-clicked entry.</param>
+        private void OnEntryDoubleClicked(string path)
+        {
+            delayedSelect = false;
+
+            LibraryEntry entry = ProjectLibrary.GetEntry(path);
+            if (entry != null)
+            {
+                if (entry.Type == LibraryEntryType.Directory)
+                    owner.Window.EnterDirectory(path);
+                else
+                {
+                    ResourceMeta meta = ProjectLibrary.GetMeta(path);
+
+                    FileEntry fileEntry = (FileEntry)entry;
+                    if (meta.ResType == ResourceType.Prefab)
+                    {
+                        EditorApplication.LoadScene(fileEntry.Path);
+                    }
+                    else if (meta.ResType == ResourceType.ScriptCode)
+                    {
+                        ProgressBar.Show("Opening external code editor...", 1.0f);
+
+                        delayedOpenCodeEditorFrame = Time.FrameIdx + 1;
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Returns an icon that can be used for displaying a resource of the specified type.
+        /// </summary>
+        /// <param name="path">Path to the project library entry to display data for.</param>
+        /// <param name="size">Size of the icon to retrieve, in pixels.</param>
+        /// <returns>Icon to display for the specified entry.</returns>
+        private static SpriteTexture GetIcon(string path, int size)
+        {
+            LibraryEntry entry = ProjectLibrary.GetEntry(path);
+            if (entry.Type == LibraryEntryType.Directory)
+            {
+                return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.Folder, size);
+            }
+            else
+            {
+                ResourceMeta meta = ProjectLibrary.GetMeta(path);
+                switch (meta.ResType)
+                {
+                    case ResourceType.Font:
+                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.Font, size);
+                    case ResourceType.Mesh:
+                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.Mesh, size);
+                    case ResourceType.Texture:
+                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.Texture, size);
+                    case ResourceType.PlainText:
+                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.PlainText, size);
+                    case ResourceType.ScriptCode:
+                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.ScriptCode, size);
+                    case ResourceType.SpriteTexture:
+                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.SpriteTexture, size);
+                    case ResourceType.Shader:
+                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.Shader, size);
+                    case ResourceType.ShaderInclude:
+                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.Shader, size);
+                    case ResourceType.Material:
+                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.Material, size);
+                    case ResourceType.Prefab:
+                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.Prefab, size);
+                    case ResourceType.GUISkin:
+                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.GUISkin, size);
+                    case ResourceType.PhysicsMaterial:
+                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.PhysicsMaterial, size);
+                    case ResourceType.PhysicsMesh:
+                        return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.PhysicsMesh, size);
+                }
+            }
+
+            return null;
+        }
+    }
+}

+ 1550 - 1549
Source/MBansheeEditor/Library/LibraryWindow.cs

@@ -1,1549 +1,1550 @@
-//********************************** 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 BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Types of resource tile display in the library window.
-    /// </summary>
-    internal enum ProjectViewType
-    {
-        Grid64, Grid48, Grid32, List16
-    }
-
-    /// <summary>
-    /// Editor window that displays all resources in the project. Resources can be displayed as a grid or list of icons,
-    /// 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
-    {
-        /// <summary>
-        /// Directions the selection cursor in library window can be moved in.
-        /// </summary>
-        internal enum MoveDirection
-        {
-            Up, Down, Left, Right
-        }
-
-        private const int DRAG_SCROLL_HEIGHT = 20;
-        private const int DRAG_SCROLL_AMOUNT_PER_SECOND = 300;
-        private const int FOLDER_BUTTON_WIDTH = 30;
-        private const int FOLDER_SEPARATOR_WIDTH = 10;
-        private const string CURRENT_LIBRARY_DIRECTORY_KEY = "__CurrentLibDir";
-
-        private bool hasContentFocus = false;
-        private bool HasContentFocus { get { return HasFocus && hasContentFocus; } }
-
-        private string searchQuery;
-        private bool IsSearchActive { get { return !string.IsNullOrEmpty(searchQuery); } }
-
-        private ProjectViewType viewType = ProjectViewType.Grid32;
-
-        private bool requiresRefresh;
-        private List<string> selectionPaths = new List<string>();
-        private int selectionAnchorStart = -1;
-        private int selectionAnchorEnd = -1;
-        private string pingPath = "";
-        private string hoverHighlightPath = "";
-
-        private LibraryGUIContent content;
-        private GUIScrollArea contentScrollArea;
-        private GUILayoutX searchBarLayout;
-        private GUIButton optionsButton;
-        private GUILayout folderBarLayout;
-        private GUILayout folderListLayout;
-        private GUITextField searchField;
-        private GUITexture dragSelection;
-
-        private ContextMenu entryContextMenu;
-        private LibraryDropTarget dropTarget;
-
-        private int autoScrollAmount;
-        private bool isDraggingSelection;
-        private Vector2I dragSelectionStart;
-        private Vector2I dragSelectionEnd;
-
-        private LibraryGUIEntry inProgressRenameElement;
-
-        // Cut/Copy/Paste
-        private List<string> copyPaths = new List<string>();
-        private List<string> cutPaths = new List<string>();
-
-        /// <summary>
-        /// Determines how to display resource tiles in the library window.
-        /// </summary>
-        internal ProjectViewType ViewType
-        {
-            get { return viewType; }
-            set { viewType = value; Refresh(); }
-        }
-
-        /// <summary>
-        /// Returns a file or folder currently selected in the library window. If nothing is selected, returns the active
-        /// folder. Returned path is relative to project library resources folder.
-        /// </summary>
-        public string SelectedEntry
-        {
-            get
-            {
-                if (selectionPaths.Count == 1)
-                {
-                    LibraryEntry entry = ProjectLibrary.GetEntry(selectionPaths[0]);
-                    if (entry != null)
-                        return entry.Path;
-                }
-
-                return CurrentFolder;
-            }
-        }
-
-        /// <summary>
-        /// Returns a folder currently selected in the library window. If no folder is selected, returns the active
-        /// folder. Returned path is relative to project library resources folder.
-        /// </summary>
-        public string SelectedFolder
-        {
-            get
-            {
-                DirectoryEntry selectedDirectory = null;
-                if (selectionPaths.Count == 1)
-                {
-                    LibraryEntry entry = ProjectLibrary.GetEntry(selectionPaths[0]);
-                    if (entry != null && entry.Type == LibraryEntryType.Directory)
-                        selectedDirectory = (DirectoryEntry) entry;
-                }
-
-                if (selectedDirectory != null)
-                    return selectedDirectory.Path;
-                
-                return CurrentFolder;
-            }
-        }
-
-        /// <summary>
-        /// Returns the path to the folder currently displayed in the library window. Returned path is relative to project 
-        /// library resources folder.
-        /// </summary>
-        public string CurrentFolder
-        {
-            get { return ProjectSettings.GetString(CURRENT_LIBRARY_DIRECTORY_KEY); }
-            set { ProjectSettings.SetString(CURRENT_LIBRARY_DIRECTORY_KEY, value); }
-        }
-
-        /// <summary>
-        /// Context menu that should open when user right clicks on the content area.
-        /// </summary>
-        internal ContextMenu ContextMenu
-        {
-            get { return entryContextMenu; }
-        }
-
-        /// <summary>
-        /// Opens the library window if not already open.
-        /// </summary>
-        [MenuItem("Windows/Library", ButtonModifier.CtrlAlt, ButtonCode.L, 6000)]
-        private static void OpenLibraryWindow()
-        {
-            OpenWindow<LibraryWindow>();
-        }
-
-        private void OnInitialize()
-        {
-            ProjectLibrary.OnEntryAdded += OnEntryChanged;
-            ProjectLibrary.OnEntryRemoved += OnEntryChanged;
-
-            GUILayoutY contentLayout = GUI.AddLayoutY();
-
-            searchBarLayout = contentLayout.AddLayoutX();
-            searchField = new GUITextField();
-            searchField.OnChanged += OnSearchChanged;
-
-            GUIContent clearIcon = new GUIContent(EditorBuiltin.GetLibraryWindowIcon(LibraryWindowIcon.Clear), 
-                new LocEdString("Clear"));
-            GUIButton clearSearchBtn = new GUIButton(clearIcon);
-            clearSearchBtn.OnClick += ClearSearch;
-            clearSearchBtn.SetWidth(40);
-
-            GUIContent optionsIcon = new GUIContent(EditorBuiltin.GetLibraryWindowIcon(LibraryWindowIcon.Options), 
-                new LocEdString("Options"));
-            optionsButton = new GUIButton(optionsIcon);
-            optionsButton.OnClick += OpenOptionsWindow;
-            optionsButton.SetWidth(40);
-            searchBarLayout.AddElement(searchField);
-            searchBarLayout.AddElement(clearSearchBtn);
-            searchBarLayout.AddElement(optionsButton);
-
-            folderBarLayout = contentLayout.AddLayoutX();
-
-            GUIContent homeIcon = new GUIContent(EditorBuiltin.GetLibraryWindowIcon(LibraryWindowIcon.Home), 
-                new LocEdString("Home"));
-            GUIButton homeButton = new GUIButton(homeIcon, GUIOption.FixedWidth(FOLDER_BUTTON_WIDTH));
-            homeButton.OnClick += OnHomeClicked;
-
-            GUIContent upIcon = new GUIContent(EditorBuiltin.GetLibraryWindowIcon(LibraryWindowIcon.Up), 
-                new LocEdString("Up"));
-            GUIButton upButton = new GUIButton(upIcon, GUIOption.FixedWidth(FOLDER_BUTTON_WIDTH));
-            upButton.OnClick += OnUpClicked;
-
-            folderBarLayout.AddElement(homeButton);
-            folderBarLayout.AddElement(upButton);
-            folderBarLayout.AddSpace(10);
-
-            contentScrollArea = new GUIScrollArea(GUIOption.FlexibleWidth(), GUIOption.FlexibleHeight());
-            contentLayout.AddElement(contentScrollArea);
-            contentLayout.AddFlexibleSpace();
-
-            entryContextMenu = LibraryMenu.CreateContextMenu(this);
-            content = new LibraryGUIContent(this, contentScrollArea);
-
-            Refresh();
-
-            dropTarget = new LibraryDropTarget(this);
-            dropTarget.Bounds = GetScrollAreaBounds();
-            dropTarget.OnStart += OnDragStart;
-            dropTarget.OnDrag += OnDragMove;
-            dropTarget.OnLeave += OnDragLeave;
-            dropTarget.OnDropResource += OnResourceDragDropped;
-            dropTarget.OnDropSceneObject += OnSceneObjectDragDropped;
-            dropTarget.OnEnd += OnDragEnd;
-
-            Selection.OnSelectionChanged += OnSelectionChanged;
-            Selection.OnResourcePing += OnPing;
-        }
-
-        private void OnDestroy()
-        {
-            Selection.OnSelectionChanged -= OnSelectionChanged;
-            Selection.OnResourcePing -= OnPing;
-
-            dropTarget.Destroy();
-        }
-
-        private void OnEditorUpdate()
-        {
-            bool isRenameInProgress = inProgressRenameElement != null;
-
-            if (HasContentFocus)
-            {
-                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)
-                        {
-                            LibraryEntry entry = ProjectLibrary.GetEntry(selectionPaths[0]);
-                            if (entry != null && entry.Type == LibraryEntryType.Directory)
-                            {
-                                EnterDirectory(entry.Path);
-                            }
-                        }
-                    }
-                    else if (Input.IsButtonDown(ButtonCode.Back))
-                    {
-                        LibraryEntry entry = ProjectLibrary.GetEntry(CurrentFolder);
-                        if (entry != null && entry.Parent != null)
-                        {
-                            EnterDirectory(entry.Parent.Path);
-                        }
-                    }
-                    else if (Input.IsButtonDown(ButtonCode.Up))
-                    {
-                        MoveSelection(MoveDirection.Up);
-                    }
-                    else if (Input.IsButtonDown(ButtonCode.Down))
-                    {
-                        MoveSelection(MoveDirection.Down);
-                    }
-                    else if (Input.IsButtonDown(ButtonCode.Left))
-                    {
-                        MoveSelection(MoveDirection.Left);
-                    }
-                    else if (Input.IsButtonDown(ButtonCode.Right))
-                    {
-                        MoveSelection(MoveDirection.Right);
-                    }
-                    else if (Input.IsButtonDown(ButtonCode.F2))
-                    {
-                        RenameSelection();
-                    }
-                    else if (Input.IsButtonDown(ButtonCode.Delete))
-                    {
-                        DeleteSelection();
-                    }
-                }
-                else
-                {
-                    if (Input.IsButtonDown(ButtonCode.Return))
-                    {
-                        string newName = inProgressRenameElement.GetRenamedName();
-
-                        string originalPath = inProgressRenameElement.path;
-                        originalPath = originalPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
-
-                        string newPath = Path.GetDirectoryName(originalPath);
-                        string newNameWithExtension = newName + Path.GetExtension(originalPath);
-                        newPath = Path.Combine(newPath, newNameWithExtension);
-
-                        bool renameOK = true;
-                        if (!PathEx.IsValidFileName(newName))
-                        {
-                            DialogBox.Open(new LocEdString("Error"), new LocEdString("The name you specified is not a valid file name. Try another."), DialogBox.Type.OK);
-                            renameOK = false;
-                        }
-
-                        if (renameOK)
-                        {
-                            // Windows sees paths with dot at the end as if they didn't have it
-                            // so remove the dot to ensure the project library does the same
-                            string trimmedNewPath = newPath.TrimEnd('.');
-
-                            if (originalPath != trimmedNewPath && ProjectLibrary.Exists(trimmedNewPath))
-                            {
-                                DialogBox.Open(new LocEdString("Error"), new LocEdString("File/folder with that name already exists in this folder."), DialogBox.Type.OK);
-                                renameOK = false;
-                            }
-                        }
-
-                        if (renameOK)
-                        {
-                            ProjectLibrary.Rename(originalPath, newNameWithExtension);
-                            StopRename();
-                        }
-                    }
-                    else if (Input.IsButtonDown(ButtonCode.Escape))
-                    {
-                        StopRename();
-                    }
-                }
-            }
-            else
-            {
-                if (isRenameInProgress)
-                    StopRename();
-            }
-
-            if (autoScrollAmount != 0)
-            {
-                Rect2I contentBounds = contentScrollArea.ContentBounds;
-                float scrollPct = autoScrollAmount / (float)contentBounds.height;
-
-                contentScrollArea.VerticalScroll += scrollPct * Time.FrameDelta;
-            }
-
-            if (requiresRefresh)
-                Refresh();
-
-            dropTarget.Update();
-            content.Update();
-        }
-
-        /// <inheritdoc/>
-        protected override LocString GetDisplayName()
-        {
-            return new LocEdString("Library");
-        }
-
-        /// <inheritdoc/>
-        protected override void WindowResized(int width, int height)
-        {
-            base.WindowResized(width, height);
-
-            Refresh();
-
-            dropTarget.Bounds = GetScrollAreaBounds();
-        }
-
-        /// <summary>
-        /// Attempts to find a resource tile element at the specified coordinates.
-        /// </summary>
-        /// <param name="windowPos">Coordinates relative to the window.</param>
-        /// <returns>True if found an entry, false otherwise.</returns>
-        private LibraryGUIEntry FindElementAt(Vector2I windowPos)
-        {
-            Vector2I scrollPos = WindowToScrollAreaCoords(windowPos);
-
-            return content.FindElementAt(scrollPos);
-        }
-
-        /// <summary>
-        /// Clears hover highlight from the currently hovered over element.
-        /// </summary>
-        private void ClearHoverHighlight()
-        {
-            content.MarkAsHovered(hoverHighlightPath, false);
-            hoverHighlightPath = "";
-        }
-
-        /// <summary>
-        /// Pings an element at the specified path, displaying and highlighting it in the window.
-        /// </summary>
-        /// <param name="path">Project library path to the element.</param>
-        public void Ping(string path)
-        {
-            content.MarkAsPinged(pingPath, false);
-            pingPath = path;
-            content.MarkAsPinged(pingPath, true);
-        }
-
-        /// <summary>
-        /// Resets the library window to initial state.
-        /// </summary>
-        public void Reset()
-        {
-            CurrentFolder = ProjectLibrary.Root.Path;
-            selectionAnchorStart = -1;
-            selectionAnchorEnd = -1;
-            selectionPaths.Clear();
-            pingPath = "";
-            hoverHighlightPath = "";
-
-            Refresh();
-        }
-
-        /// <summary>
-        /// Deselects all selected elements.
-        /// </summary>
-        /// <param name="onlyInternal">If true, do not update the global <see cref="Selection"/>, instead the operation
-        ///                            will be contained to the library window internally.</param>
-        internal void DeselectAll(bool onlyInternal = false)
-        {
-            SetSelection(new List<string>(), onlyInternal);
-            selectionAnchorStart = -1;
-            selectionAnchorEnd = -1;
-        }
-
-        /// <summary>
-        /// Select an element at the specified path. If control or shift keys are pressed during this operations multiple 
-        /// elements can be selected.
-        /// </summary>
-        /// <param name="path">Project library path to the element.</param>
-        internal void Select(string path)
-        {
-            LibraryGUIEntry entry;
-            if (!content.TryGetEntry(path, out entry))
-                return;
-
-            bool ctrlDown = Input.IsButtonHeld(ButtonCode.LeftControl) || Input.IsButtonHeld(ButtonCode.RightControl);
-            bool shiftDown = Input.IsButtonHeld(ButtonCode.LeftShift) || Input.IsButtonHeld(ButtonCode.RightShift);
-
-            if (shiftDown)
-            {
-                if (selectionAnchorStart != -1 && selectionAnchorStart < content.Entries.Count)
-                {
-                    int start = Math.Min(entry.index, selectionAnchorStart);
-                    int end = Math.Max(entry.index, selectionAnchorStart);
-
-                    List<string> newSelection = new List<string>();
-                    for(int i = start; i <= end; i++)
-                        newSelection.Add(content.Entries[i].path);
-
-                    SetSelection(newSelection);
-                    selectionAnchorEnd = entry.index;
-                }
-                else
-                {
-                    SetSelection(new List<string>() {path});
-                    selectionAnchorStart = entry.index;
-                    selectionAnchorEnd = entry.index;
-                }
-            }
-            else if (ctrlDown)
-            {
-                List<string> newSelection = new List<string>(selectionPaths);
-
-                if (selectionPaths.Contains(path))
-                {
-                    newSelection.Remove(path);
-                    if (newSelection.Count == 0)
-                        DeselectAll();
-                    else
-                    {
-                        if (selectionAnchorStart == entry.index)
-                        {
-                            LibraryGUIEntry newAnchorEntry;
-                            if (!content.TryGetEntry(newSelection[0], out newAnchorEntry))
-                                selectionAnchorStart = -1;
-                            else
-                                selectionAnchorStart = newAnchorEntry.index;
-                        }
-
-                        if (selectionAnchorEnd == entry.index)
-                        {
-                            LibraryGUIEntry newAnchorEntry;
-                            if (!content.TryGetEntry(newSelection[newSelection.Count - 1], out newAnchorEntry))
-                                selectionAnchorEnd = -1;
-                            else
-                                selectionAnchorEnd = newAnchorEntry.index;
-                        }
-
-                        SetSelection(newSelection);
-                    }
-                }
-                else
-                {
-                    newSelection.Add(path);
-                    SetSelection(newSelection);
-                    selectionAnchorEnd = entry.index;
-                }
-            }
-            else
-            {
-                SetSelection(new List<string>() {path});
-
-                selectionAnchorStart = entry.index;
-                selectionAnchorEnd = entry.index;
-            }
-        }
-
-        /// <summary>
-        /// Selects a new element in the specified direction from the currently selected element. If shift or control are
-        /// held during this operation, the selected object will be added to existing selection. If no element is selected
-        /// the first or last element will be selected depending on direction.
-        /// </summary>
-        /// <param name="dir">Direction to move from the currently selected element.</param>
-        internal void MoveSelection(MoveDirection dir)
-        {
-            string newPath = "";
-
-            if (selectionPaths.Count == 0 || selectionAnchorEnd == -1)
-            {
-                // Nothing is selected so we arbitrarily select first or last element
-                if (content.Entries.Count > 0)
-                {
-                    switch (dir)
-                    {
-                        case MoveDirection.Left:
-                        case MoveDirection.Up:
-                            newPath = content.Entries[content.Entries.Count - 1].path;
-                            break;
-                        case MoveDirection.Right:
-                        case MoveDirection.Down:
-                            newPath = content.Entries[0].path;
-                            break;
-                    }
-                }
-            }
-            else
-            {
-                switch (dir)
-                {
-                    case MoveDirection.Left:
-                        if (selectionAnchorEnd - 1 >= 0)
-                            newPath = content.Entries[selectionAnchorEnd - 1].path;
-                        break;
-                    case MoveDirection.Up:
-                        if (selectionAnchorEnd - content.ElementsPerRow >= 0)
-                            newPath = content.Entries[selectionAnchorEnd - content.ElementsPerRow].path;
-                        break;
-                    case MoveDirection.Right:
-                        if (selectionAnchorEnd + 1 < content.Entries.Count)
-                            newPath = content.Entries[selectionAnchorEnd + 1].path;
-                        break;
-                    case MoveDirection.Down:
-                        if (selectionAnchorEnd + content.ElementsPerRow < content.Entries.Count)
-                            newPath = content.Entries[selectionAnchorEnd + content.ElementsPerRow].path;
-                        break;
-                }
-            }
-
-            if (!string.IsNullOrEmpty(newPath))
-            {
-                Select(newPath);
-                ScrollToEntry(newPath);
-            }
-        }
-
-        /// <summary>
-        /// Selects a set of elements based on the provided paths.
-        /// </summary>
-        /// <param name="paths">Project library paths of the elements to select.</param>
-        /// <param name="onlyInternal">If true, do not update the global <see cref="Selection"/>, instead the operation
-        ///                            will be contained to the library window internally.</param>
-        internal void SetSelection(List<string> paths, bool onlyInternal = false)
-        {
-            if (selectionPaths != null)
-            {
-                foreach (var path in selectionPaths)
-                    content.MarkAsSelected(path, false);
-            }
-
-            selectionPaths = paths;
-            Ping("");
-
-            if (selectionPaths != null)
-            {
-                foreach (var path in selectionPaths)
-                    content.MarkAsSelected(path, true);
-            }
-
-            StopRename();
-
-            if (!onlyInternal)
-            {
-                if (selectionPaths != null)
-                    Selection.ResourcePaths = selectionPaths.ToArray();
-                else
-                    Selection.ResourcePaths = new string[0];
-            }
-        }
-
-        /// <summary>
-        /// Changes the active directory to the provided directory. Current contents of the window will be cleared and
-        /// instead contents of the new directory will be displayed.
-        /// </summary>
-        /// <param name="directory">Project library path to the directory.</param>
-        internal void EnterDirectory(string directory)
-        {
-            CurrentFolder = directory;
-            DeselectAll(true);
-
-            Refresh();
-        }
-
-        /// <summary>
-        /// Marks the provided set of elements for a cut operation. Cut elements can be moved to a new location by calling
-        /// <see cref="Paste"/>.
-        /// </summary>
-        /// <param name="sourcePaths">Project library paths of the elements to cut.</param>
-        internal void Cut(IEnumerable<string> sourcePaths)
-        {
-            foreach (var path in cutPaths)
-                content.MarkAsCut(path, false);
-
-            string[] filePaths = GetFiles(sourcePaths);
-
-            cutPaths.Clear();
-            cutPaths.AddRange(filePaths);
-
-            foreach (var path in cutPaths)
-                content.MarkAsCut(path, true);
-
-            copyPaths.Clear();
-        }
-
-        /// <summary>
-        /// Marks the provided set of elements for a copy operation. You can copy the elements by calling <see cref="Paste"/>.
-        /// </summary>
-        /// <param name="sourcePaths">Project library paths of the elements to copy.</param>
-        internal void Copy(IEnumerable<string> sourcePaths)
-        {
-            copyPaths.Clear();
-
-            string[] filePaths = GetFiles(sourcePaths);
-            copyPaths.AddRange(filePaths);
-
-            foreach (var path in cutPaths)
-                content.MarkAsCut(path, false);
-
-            cutPaths.Clear();
-        }
-       
-        /// <summary>
-        /// Duplicates the provided set of elements.
-        /// </summary>
-        /// <param name="sourcePaths">Project library paths of the elements to duplicate.</param>
-        internal void Duplicate(IEnumerable<string> sourcePaths)
-        {
-            string[] filePaths = GetFiles(sourcePaths);
-
-            foreach (var source in filePaths)
-            {
-                string path = source;
-                if (!Path.IsPathRooted(path))
-                    path = Path.Combine(ProjectLibrary.ResourceFolder, path);
-
-                if (Directory.Exists(path))
-                    DirectoryEx.Copy(path, LibraryUtility.GetUniquePath(path));
-                else if (File.Exists(path))
-                    FileEx.Copy(path, LibraryUtility.GetUniquePath(path));
-
-                ProjectLibrary.Refresh();
-            }
-        }
-
-        /// <summary>
-        /// Performs a cut or copy operations on the elements previously marked by calling <see cref="Cut"/> or
-        /// <see cref="Copy"/>.
-        /// </summary>
-        /// <param name="destinationFolder">Project library folder into which to move/copy the elements.</param>
-        internal void Paste(string destinationFolder)
-        {
-            string rootedDestinationFolder = destinationFolder;
-            if (!Path.IsPathRooted(rootedDestinationFolder))
-                rootedDestinationFolder = Path.Combine(ProjectLibrary.ResourceFolder, rootedDestinationFolder);
-
-            if (copyPaths.Count > 0)
-            {
-                for (int i = 0; i < copyPaths.Count; i++)
-                {
-                    string path = copyPaths[i];
-                    if (!Path.IsPathRooted(path))
-                        path = Path.Combine(ProjectLibrary.ResourceFolder, path);
-
-                    string destination = Path.Combine(rootedDestinationFolder, PathEx.GetTail(copyPaths[i]));
-
-                    if (Directory.Exists(path))
-                        DirectoryEx.Copy(path, LibraryUtility.GetUniquePath(destination));
-                    else if (File.Exists(path))
-                        FileEx.Copy(path, LibraryUtility.GetUniquePath(destination));
-                }
-
-                ProjectLibrary.Refresh();
-            }
-            else if (cutPaths.Count > 0)
-            {
-                for (int i = 0; i < cutPaths.Count; i++)
-                {
-                    string path = cutPaths[i];
-                    if (!Path.IsPathRooted(path))
-                        path = Path.Combine(ProjectLibrary.ResourceFolder, path);
-
-                    string destination = Path.Combine(rootedDestinationFolder, PathEx.GetTail(cutPaths[i]));
-
-                    if (Directory.Exists(path))
-                        DirectoryEx.Move(path, LibraryUtility.GetUniquePath(destination));
-                    else if (File.Exists(path))
-                        FileEx.Move(path, LibraryUtility.GetUniquePath(destination));
-                }
-
-                cutPaths.Clear();
-                ProjectLibrary.Refresh();
-            }
-        }
-
-        /// <summary>
-        /// Scrolls the contents GUI area so that the element at the specified path becomes visible.
-        /// </summary>
-        /// <param name="path">Project library path to the element.</param>
-        private void ScrollToEntry(string path)
-        {
-            LibraryGUIEntry entryGUI;
-            if (!content.TryGetEntry(path, out entryGUI))
-                return;
-
-            Rect2I entryBounds = entryGUI.Bounds;
-
-            Rect2I contentBounds = contentScrollArea.Layout.Bounds;
-            Rect2I windowEntryBounds = entryBounds;
-            windowEntryBounds.x += contentBounds.x;
-            windowEntryBounds.y += contentBounds.y;
-
-            Rect2I scrollAreaBounds = contentScrollArea.Bounds;
-            bool requiresScroll = windowEntryBounds.y < scrollAreaBounds.y ||
-                                  (windowEntryBounds.y + windowEntryBounds.height) > (scrollAreaBounds.y + scrollAreaBounds.height);
-
-            if (!requiresScroll)
-                return;
-
-            int scrollableSize = contentBounds.height - scrollAreaBounds.height;
-            float percent = (((entryBounds.y + entryBounds.height * 0.5f) - scrollAreaBounds.height * 0.5f) / (float)scrollableSize);
-
-            percent = MathEx.Clamp01(percent);
-            contentScrollArea.VerticalScroll = percent;
-        }
-
-        /// <summary>
-        /// Rebuilds the library window GUI. Should be called any time the active folder or contents change.
-        /// </summary>
-        private void Refresh()
-        {
-            requiresRefresh = false;
-
-            LibraryEntry[] entriesToDisplay = new LibraryEntry[0];
-            if (IsSearchActive)
-            {
-                entriesToDisplay = ProjectLibrary.Search("*" + searchQuery + "*");
-            }
-            else
-            {
-                DirectoryEntry entry = ProjectLibrary.GetEntry(CurrentFolder) as DirectoryEntry;
-                if (entry == null)
-                {
-                    CurrentFolder = ProjectLibrary.Root.Path;
-                    entry = ProjectLibrary.GetEntry(CurrentFolder) as DirectoryEntry;
-                }
-
-                if(entry != null)
-                    entriesToDisplay = entry.Children;
-            }
-
-            inProgressRenameElement = null;
-
-            RefreshDirectoryBar();
-
-            SortEntries(entriesToDisplay);
-            content.Refresh(viewType, entriesToDisplay);
-
-            foreach (var path in cutPaths)
-                content.MarkAsCut(path, true);
-
-            foreach (var path in selectionPaths)
-                content.MarkAsSelected(path, true);
-
-            content.MarkAsPinged(pingPath, true);
-
-            Rect2I contentBounds = content.Bounds;
-            Rect2I minimalBounds = GetScrollAreaBounds();
-            contentBounds.height = Math.Max(contentBounds.height, minimalBounds.height);
-
-            GUIButton catchAll = new GUIButton("", EditorStyles.Blank);
-            catchAll.Bounds = contentBounds;
-            catchAll.OnClick += OnCatchAllClicked;
-            catchAll.SetContextMenu(entryContextMenu);
-
-            content.Underlay.AddElement(catchAll);
-
-            Rect2I focusBounds = contentBounds; // Contents + Folder bar
-            Rect2I scrollBounds = contentScrollArea.Bounds;
-            focusBounds.x += scrollBounds.x;
-            focusBounds.y += scrollBounds.y;
-
-            Rect2I folderBarBounds = folderListLayout.Bounds;
-            focusBounds.y -= folderBarBounds.height;
-            focusBounds.height += folderBarBounds.height;
-
-            GUIButton focusCatcher = new GUIButton("", EditorStyles.Blank);
-            focusCatcher.OnFocusGained += () => hasContentFocus = true;
-            focusCatcher.OnFocusLost += () => hasContentFocus = false;
-            focusCatcher.Bounds = focusBounds;
-
-            GUIPanel focusPanel = GUI.AddPanel(3);
-            focusPanel.AddElement(focusCatcher);
-
-            UpdateDragSelection(dragSelectionEnd);
-        }
-
-        /// <summary>
-        /// Converts coordinates relative to the window into coordinates relative to the contents scroll area.
-        /// </summary>
-        /// <param name="windowPos">Coordinates relative to the window.</param>
-        /// <returns>Coordinates relative to the contents scroll area.</returns>
-        private Vector2I WindowToScrollAreaCoords(Vector2I windowPos)
-        {
-            Rect2I scrollBounds = contentScrollArea.Layout.Bounds;
-            Vector2I scrollPos = windowPos;
-            scrollPos.x -= scrollBounds.x;
-            scrollPos.y -= scrollBounds.y;
-
-            return scrollPos;
-        }
-
-        /// <summary>
-        /// Starts a drag operation that displays a selection outline allowing the user to select multiple entries at once.
-        /// </summary>
-        /// <param name="windowPos">Coordinates relative to the window where the drag originated.</param>
-        private void StartDragSelection(Vector2I windowPos)
-        {
-            isDraggingSelection = true;
-            dragSelectionStart = WindowToScrollAreaCoords(windowPos);
-            dragSelectionEnd = dragSelectionStart;
-        }
-
-        /// <summary>
-        /// Updates a selection outline drag operation by expanding the outline to the new location. Elements in the outline
-        /// are selected.
-        /// </summary>
-        /// <param name="windowPos">Coordinates of the pointer relative to the window.</param>
-        /// <returns>True if the selection outline drag is valid and was updated, false otherwise.</returns>
-        private bool UpdateDragSelection(Vector2I windowPos)
-        {
-            if (!isDraggingSelection)
-                return false;
-
-            if (dragSelection == null)
-            {
-                dragSelection = new GUITexture(null, true, EditorStyles.SelectionArea);
-                content.Overlay.AddElement(dragSelection);
-            }
-
-            dragSelectionEnd = WindowToScrollAreaCoords(windowPos);
-
-            Rect2I selectionArea = CalculateSelectionArea();
-            SelectInArea(selectionArea);
-            dragSelection.Bounds = selectionArea;
-
-            return true;
-        }
-
-        /// <summary>
-        /// Ends the selection outline drag operation. Elements in the outline are selected.
-        /// </summary>
-        /// <returns>True if the selection outline drag is valid and was ended, false otherwise.</returns>
-        private bool EndDragSelection()
-        {
-            if (!isDraggingSelection)
-                return false;
-
-            if (dragSelection != null)
-            {
-                dragSelection.Destroy();
-                dragSelection = null;
-            }
-
-            Rect2I selectionArea = CalculateSelectionArea();
-            SelectInArea(selectionArea);
-
-            isDraggingSelection = false;
-            return false;
-        }
-
-        /// <summary>
-        /// Calculates bounds of the selection area used for selection overlay drag operation, depending on drag starting
-        /// point coordinates and current drag coordinates.
-        /// </summary>
-        /// <returns>Bounds of the selection area, relative to the content scroll area.</returns>
-        private Rect2I CalculateSelectionArea()
-        {
-            Rect2I selectionArea = new Rect2I();
-            if (dragSelectionStart.x < dragSelectionEnd.x)
-            {
-                selectionArea.x = dragSelectionStart.x;
-                selectionArea.width = dragSelectionEnd.x - dragSelectionStart.x;
-            }
-            else
-            {
-                selectionArea.x = dragSelectionEnd.x;
-                selectionArea.width = dragSelectionStart.x - dragSelectionEnd.x;
-            }
-
-            if (dragSelectionStart.y < dragSelectionEnd.y)
-            {
-                selectionArea.y = dragSelectionStart.y;
-                selectionArea.height = dragSelectionEnd.y - dragSelectionStart.y;
-            }
-            else
-            {
-                selectionArea.y = dragSelectionEnd.y;
-                selectionArea.height = dragSelectionStart.y - dragSelectionEnd.y;
-            }
-
-            Rect2I maxBounds = contentScrollArea.Layout.Bounds;
-            maxBounds.x = 0;
-            maxBounds.y = 0;
-
-            selectionArea.Clip(maxBounds);
-
-            return selectionArea;
-        }
-
-        /// <summary>
-        /// Selects all elements overlapping the specified bounds.
-        /// </summary>
-        /// <param name="scrollBounds">Bounds relative to the content scroll area.</param>
-        private void SelectInArea(Rect2I scrollBounds)
-        {
-            LibraryGUIEntry[] foundElements = content.FindElementsOverlapping(scrollBounds);
-
-            if (foundElements.Length > 0)
-            {
-                selectionAnchorStart = foundElements[0].index;
-                selectionAnchorEnd = foundElements[foundElements.Length - 1].index;
-            }
-            else
-            {
-                selectionAnchorStart = -1;
-                selectionAnchorEnd = -1;
-            }
-
-            List<string> elementPaths = new List<string>();
-            foreach (var elem in foundElements)
-                elementPaths.Add(elem.path);
-
-            SetSelection(elementPaths);
-        }
-
-        /// <summary>
-        /// Updates GUI for the directory bar. Should be called whenever the active folder changes.
-        /// </summary>
-        private void RefreshDirectoryBar()
-        {
-            if (folderListLayout != null)
-            {
-                folderListLayout.Destroy();
-                folderListLayout = null;
-            }
-
-            folderListLayout = folderBarLayout.AddLayoutX();
-
-            string[] folders = null;
-            string[] fullPaths = null;
-
-            if (IsSearchActive)
-            {
-                folders = new[] {searchQuery};
-                fullPaths = new[] { searchQuery };
-            }
-            else
-            {
-                string currentDir = Path.Combine("Resources", CurrentFolder);
-
-                folders = currentDir.Split(new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar },
-                    StringSplitOptions.RemoveEmptyEntries);
-
-                fullPaths = new string[folders.Length];
-                for (int i = 0; i < folders.Length; i++)
-                {
-                    if (i == 0)
-                        fullPaths[i] = "";
-                    else
-                        fullPaths[i] = Path.Combine(fullPaths[i - 1], folders[i]);
-                }
-            }
-
-            int availableWidth = folderBarLayout.Bounds.width - FOLDER_BUTTON_WIDTH * 2;
-            int numFolders = 0;
-            for (int i = folders.Length - 1; i >= 0; i--)
-            {
-                GUIButton folderButton = new GUIButton(folders[i]);
-
-                if (!IsSearchActive)
-                {
-                    string fullPath = fullPaths[i];
-                    folderButton.OnClick += () => OnFolderButtonClicked(fullPath);
-                }
-
-                GUIButton separator = new GUIButton("/", GUIOption.FixedWidth(FOLDER_SEPARATOR_WIDTH));
-
-                folderListLayout.InsertElement(0, separator);
-                folderListLayout.InsertElement(0, folderButton);
-                numFolders++;
-
-                Rect2I folderListBounds = folderListLayout.Bounds;
-                if (folderListBounds.width > availableWidth)
-                {
-                    if (numFolders > 2)
-                    {
-                        separator.Destroy();
-                        folderButton.Destroy();
-                        break;
-                    }
-                }
-            }
-        }
-
-        /// <summary>
-        /// Performs <see cref="Cut"/> operation on the currently selected elements.
-        /// </summary>
-        internal void CutSelection()
-        {
-            if (selectionPaths.Count > 0)
-                Cut(selectionPaths);
-        }
-
-        /// <summary>
-        /// Performs <see cref="Copy"/> operation on the currently selected elements.
-        /// </summary>
-        internal void CopySelection()
-        {
-            if (selectionPaths.Count > 0)
-                Copy(selectionPaths);
-        }
-
-        /// <summary>
-        /// Performs <see cref="Duplicate"/> operation on the currently selected elements.
-        /// </summary>
-        internal void DuplicateSelection()
-        {
-            if (selectionPaths.Count > 0)
-                Duplicate(selectionPaths);
-        }
-
-        /// <summary>
-        /// Performs <see cref="Paste"/> operation. Elements will be pasted in the currently selected directory (if any), or
-        /// the active directory otherwise.
-        /// </summary>
-        internal void PasteToSelection()
-        {
-            Paste(SelectedFolder);
-        }
-
-        /// <summary>
-        /// Starts a rename operation on the currently selected elements. If more than one elements are selected only the
-        /// first one will be affected.
-        /// </summary>
-        internal void RenameSelection()
-        {
-            string[] filePaths = GetFiles(selectionPaths);
-
-            if (filePaths.Length == 0)
-                return;
-
-            if (filePaths.Length > 1)
-            {
-                DeselectAll();
-                Select(filePaths[0]);
-            }
-
-            LibraryGUIEntry entry;
-            if (content.TryGetEntry(filePaths[0], out entry))
-            {
-                entry.StartRename();
-                inProgressRenameElement = entry;
-            }
-        }
-
-        /// <summary>
-        /// Deletes currently selected elements. User will be asked to confirm deletion via a dialog box.
-        /// </summary>
-        internal void DeleteSelection()
-        {
-            string[] filePaths = GetFiles(selectionPaths);
-
-            if (filePaths.Length == 0)
-                return;
-
-            DialogBox.Open(new LocEdString("Confirm deletion"), new LocEdString("Are you sure you want to delete the selected object(s)?"),
-                DialogBox.Type.YesNo,
-                type =>
-                {
-                    if (type == DialogBox.ResultType.Yes)
-                    {
-                        foreach (var path in filePaths)
-                            ProjectLibrary.Delete(path);
-
-                        DeselectAll();
-                        Refresh();
-                    }
-                });
-        }
-
-        /// <summary>
-        /// Stops the rename operation, if one is in progress on any element.
-        /// </summary>
-        internal void StopRename()
-        {
-            if (inProgressRenameElement != null)
-            {
-                inProgressRenameElement.StopRename();
-                inProgressRenameElement = null;
-            }
-        }
-
-        /// <summary>
-        /// Clears the search bar and refreshes the content area to display contents of the current directory.
-        /// </summary>
-        private void ClearSearch()
-        {
-            searchField.Value = "";
-            searchQuery = "";
-            Refresh();
-        }
-
-        /// <summary>
-        /// Takes a list of resource paths and returns only those referencing files or folder and not sub-resources.
-        /// </summary>
-        /// <param name="resourcePaths">List of resource paths to find files for.</param>
-        /// <returns>File paths for all the provided resources.</returns>
-        private string[] GetFiles(IEnumerable<string> resourcePaths)
-        {
-            HashSet<string> filePaths = new HashSet<string>();
-
-            foreach (var resPath in resourcePaths)
-            {
-                if (resPath == null)
-                    continue;
-
-                LibraryEntry entry = ProjectLibrary.GetEntry(resPath);
-                if (entry == null)
-                    continue;
-
-                if (ProjectLibrary.IsSubresource(resPath))
-                    continue;
-
-                if (!filePaths.Contains(entry.Path))
-                    filePaths.Add(entry.Path);
-            }
-
-            string[] output = new string[filePaths.Count];
-
-            int i = 0;
-            foreach(var path in filePaths)
-                output[i++] = path;
-
-            return output;
-        }
-
-        /// <summary>
-        /// Opens the drop down options window that allows you to customize library window look and feel.
-        /// </summary>
-        private void OpenOptionsWindow()
-        {
-            Vector2I openPosition;
-            Rect2I buttonBounds = GUILayoutUtility.CalculateBounds(optionsButton, GUI);
-
-            openPosition.x = buttonBounds.x + buttonBounds.width / 2;
-            openPosition.y = buttonBounds.y + buttonBounds.height / 2;
-
-            LibraryDropDown dropDown = DropDownWindow.Open<LibraryDropDown>(this, openPosition);
-            dropDown.Initialize(this);
-        }
-
-        /// <summary>
-        /// Returns the content scroll area bounds.
-        /// </summary>
-        /// <returns>Bounds of the content scroll area, relative to the window.</returns>
-        private Rect2I GetScrollAreaBounds()
-        {
-            Rect2I bounds = GUI.Bounds;
-            Rect2I folderListBounds = folderListLayout.Bounds;
-            Rect2I searchBarBounds = searchBarLayout.Bounds;
-
-            bounds.y = folderListBounds.height + searchBarBounds.height;
-            bounds.height -= folderListBounds.height + searchBarBounds.height;
-
-            return bounds;
-        }
-
-        /// <summary>
-        /// Triggered when a project library entry was changed (added, modified, deleted).
-        /// </summary>
-        /// <param name="entry">Project library path of the changed entry.</param>
-        private void OnEntryChanged(string entry)
-        {
-            requiresRefresh = true;
-        }
-
-        /// <summary>
-        /// Triggered when the drag and drop operation is starting while over the content area. If drag operation is over
-        /// an element, element will be dragged.
-        /// </summary>
-        /// <param name="windowPos">Coordinates where the drag operation started, relative to the window.</param>
-        private void OnDragStart(Vector2I windowPos)
-        {
-            bool isRenameInProgress = inProgressRenameElement != null;
-            if (isRenameInProgress)
-                return;
-
-            LibraryGUIEntry underCursorElem = FindElementAt(windowPos);
-            if (underCursorElem == null)
-            {
-                StartDragSelection(windowPos);
-                return;
-            }
-
-            string resourceDir = ProjectLibrary.ResourceFolder;
-
-            string[] dragPaths = null;
-            if (selectionPaths.Count > 0)
-            {
-                foreach (var path in selectionPaths)
-                {
-                    if (path == underCursorElem.path)
-                    {
-                        dragPaths = new string[selectionPaths.Count];
-                        for (int i = 0; i < selectionPaths.Count; i++)
-                        {
-                            dragPaths[i] = Path.Combine(resourceDir, selectionPaths[i]);
-                        }
-
-                        break;
-                    }
-                }
-            }
-
-            if (dragPaths == null)
-                dragPaths = new[] { Path.Combine(resourceDir, underCursorElem.path) };
-
-            ResourceDragDropData dragDropData = new ResourceDragDropData(dragPaths);
-            DragDrop.StartDrag(dragDropData);
-        }
-
-        /// <summary>
-        /// Triggered when a pointer is moved while a drag operation is in progress.
-        /// </summary>
-        ///  <param name="windowPos">Coordinates of the pointer relative to the window.</param>
-        private void OnDragMove(Vector2I windowPos)
-        {
-            // Auto-scroll
-            Rect2I scrollAreaBounds = contentScrollArea.Bounds;
-            int scrollAreaTop = scrollAreaBounds.y;
-            int scrollAreaBottom = scrollAreaBounds.y + scrollAreaBounds.height;
-
-            if (windowPos.y > scrollAreaTop && windowPos.y <= (scrollAreaTop + DRAG_SCROLL_HEIGHT))
-                autoScrollAmount = -DRAG_SCROLL_AMOUNT_PER_SECOND;
-            else if (windowPos.y >= (scrollAreaBottom - DRAG_SCROLL_HEIGHT) && windowPos.y < scrollAreaBottom)
-                autoScrollAmount = DRAG_SCROLL_AMOUNT_PER_SECOND;
-            else
-                autoScrollAmount = 0;
-
-            // Selection box
-            if (UpdateDragSelection(windowPos))
-                return;
-
-            // Drag and drop (hover element under cursor)
-            LibraryGUIEntry underCursorElem = FindElementAt(windowPos);
-
-            if (underCursorElem == null)
-            {
-                ClearHoverHighlight();
-            }
-            else
-            {
-                if (underCursorElem.path != hoverHighlightPath)
-                {
-                    ClearHoverHighlight();
-
-                    hoverHighlightPath = underCursorElem.path;
-                    underCursorElem.MarkAsHovered(true);
-                }
-            }
-        }
-
-        /// <summary>
-        /// Triggered when a pointer leaves the drop targer while a drag operation is in progress.
-        /// </summary>
-        private void OnDragLeave()
-        {
-            ClearHoverHighlight();
-            autoScrollAmount = 0;
-        }
-
-        /// <summary>
-        /// Triggered when a resource drop operation finishes over the content area.
-        /// </summary>
-        /// <param name="windowPos">Coordinates of the pointer relative to the window where the drop operation finished
-        ///                         .</param>
-        /// <param name="paths">Paths of the dropped resources.</param>
-        private void OnResourceDragDropped(Vector2I windowPos, string[] paths)
-        {
-            ClearHoverHighlight();
-            autoScrollAmount = 0;
-
-            if (EndDragSelection())
-                return;
-
-            string resourceDir = ProjectLibrary.ResourceFolder;
-            string destinationFolder = Path.Combine(resourceDir, CurrentFolder);
-
-            LibraryGUIEntry underCursorElement = FindElementAt(windowPos);
-            if (underCursorElement != null)
-            {
-                LibraryEntry entry = ProjectLibrary.GetEntry(underCursorElement.path);
-                if (entry != null && entry.Type == LibraryEntryType.Directory)
-                    destinationFolder = Path.Combine(resourceDir, entry.Path);
-            }
-
-            if (paths != null)
-            {
-                string[] filePaths = GetFiles(paths);
-
-                List<string> addedResources = new List<string>();
-                foreach (var path in filePaths)
-                {
-                    string absolutePath = path;
-                    if (!Path.IsPathRooted(absolutePath))
-                        absolutePath = Path.Combine(resourceDir, path);
-
-                    if (string.IsNullOrEmpty(absolutePath))
-                        continue;
-
-                    if (PathEx.IsPartOf(destinationFolder, absolutePath) || PathEx.Compare(absolutePath, destinationFolder))
-                        continue;
-
-                    string pathTail = PathEx.GetTail(absolutePath);
-                    string destination = Path.Combine(destinationFolder, pathTail);
-
-                    if (PathEx.Compare(absolutePath, destination))
-                        continue;
-
-                    bool doCopy = !ProjectLibrary.Exists(absolutePath);
-
-                    string uniqueDestination = LibraryUtility.GetUniquePath(destination);
-                    if (Directory.Exists(path))
-                    {
-                        if (doCopy)
-                            DirectoryEx.Copy(absolutePath, uniqueDestination);
-                        else
-                            DirectoryEx.Move(absolutePath, uniqueDestination);
-                    }
-                    else if (File.Exists(path))
-                    {
-                        if (doCopy)
-                            FileEx.Copy(absolutePath, uniqueDestination);
-                        else
-                            ProjectLibrary.Move(absolutePath, uniqueDestination);
-                    }
-
-                    string relativeDestination = uniqueDestination.Substring(resourceDir.Length, uniqueDestination.Length - resourceDir.Length);
-                    addedResources.Add(relativeDestination);
-                    ProjectLibrary.Refresh();
-                }
-
-                SetSelection(addedResources);
-            }
-        }
-
-        /// <summary>
-        /// Triggered when a scene object drop operation finishes over the content area.
-        /// </summary>
-        /// <param name="windowPos">Coordinates of the pointer relative to the window where the drop operation finished
-        ///                         .</param>
-        /// <param name="objects">Dropped scene objects.</param>
-        private void OnSceneObjectDragDropped(Vector2I windowPos, SceneObject[] objects)
-        {
-            ClearHoverHighlight();
-            autoScrollAmount = 0;
-
-            if (EndDragSelection())
-                return;
-
-            string destinationFolder = CurrentFolder;
-
-            LibraryGUIEntry underCursorElement = FindElementAt(windowPos);
-            if (underCursorElement != null)
-            {
-                LibraryEntry entry = ProjectLibrary.GetEntry(underCursorElement.path);
-                if (entry != null && entry.Type == LibraryEntryType.Directory)
-                    destinationFolder = entry.Path;
-            }
-
-            if (objects != null)
-            {
-                List<string> addedResources = new List<string>();
-                foreach (var so in objects)
-                {
-                    if (so == null)
-                        continue;
-
-                    Prefab newPrefab = new Prefab(so);
-
-                    string destination = LibraryUtility.GetUniquePath(Path.Combine(destinationFolder, so.Name + ".prefab"));
-                    addedResources.Add(destination);
-
-                    ProjectLibrary.Create(newPrefab, destination);
-                    ProjectLibrary.Refresh();
-                }
-
-                SetSelection(addedResources);
-            }
-        }
-
-        /// <summary>
-        /// Triggered when a drag operation that originated from this window ends.
-        /// </summary>
-        /// <param name="windowPos">Coordinates of the pointer where the drag ended relative to the window </param>
-        private void OnDragEnd(Vector2I windowPos)
-        {
-            EndDragSelection();
-            autoScrollAmount = 0;
-        }
-
-        /// <summary>
-        /// Triggered when the global selection changes.
-        /// </summary>
-        /// <param name="sceneObjects">A set of newly selected scene objects.</param>
-        /// <param name="resourcePaths">A set of paths for newly selected resources.</param>
-        private void OnSelectionChanged(SceneObject[] sceneObjects, string[] resourcePaths)
-        {
-            if (sceneObjects.Length > 0)
-                DeselectAll(true);
-            else
-                SetSelection(new List<string>(resourcePaths), true);
-        }
-
-        /// <summary>
-        /// Triggered when a ping operation was triggered externally.
-        /// </summary>
-        /// <param name="path">Path to the resource to highlight.</param>
-        private void OnPing(string path)
-        {
-            Ping(path);
-        }
-
-        /// <summary>
-        /// Triggered when a folder on the directory bar was selected.
-        /// </summary>
-        /// <param name="path">Project library path to the folder to enter.</param>
-        private void OnFolderButtonClicked(string path)
-        {
-            EnterDirectory(path);
-        }
-
-        /// <summary>
-        /// Triggered when the user clicks on empty space between elements.
-        /// </summary>
-        private void OnCatchAllClicked()
-        {
-            DeselectAll();
-        }
-
-        /// <summary>
-        /// Triggered when the user clicks on the home button on the directory bar, changing the active directory to
-        /// project library root.
-        /// </summary>
-        private void OnHomeClicked()
-        {
-            CurrentFolder = ProjectLibrary.Root.Path;
-            Refresh();
-        }
-
-        /// <summary>
-        /// Triggered when the user clicks on the up button on the directory bar, changing the active directory to the
-        /// parent directory, unless already at project library root.
-        /// </summary>
-        private void OnUpClicked()
-        {
-            string currentDir = CurrentFolder;
-            currentDir = currentDir.Trim(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
-
-            if (!string.IsNullOrEmpty(currentDir))
-            {
-                string parent = Path.GetDirectoryName(currentDir);
-
-                CurrentFolder = parent;
-                Refresh();
-            }
-        }
-
-        /// <summary>
-        /// Triggered when the user inputs new values into the search input box. Refreshes the contents so they display
-        /// elements matching the search text.
-        /// </summary>
-        /// <param name="newValue">Search box text.</param>
-        private void OnSearchChanged(string newValue)
-        {
-            searchQuery = newValue;
-            Refresh();
-        }
-
-        /// <summary>
-        /// Sorts the specified set of project library entries by type (folder or resource), followed by name.
-        /// </summary>
-        /// <param name="input">Set of project library entries to sort.</param>
-        private static void SortEntries(LibraryEntry[] input)
-        {
-            Array.Sort(input, (x, y) =>
-            {
-                if (x.Type == y.Type)
-                    return x.Name.CompareTo(y.Name);
-                else
-                    return x.Type == LibraryEntryType.File ? 1 : -1;
-            });
-        }
-    }
-}
+//********************************** 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 BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Types of resource tile display in the library window.
+    /// </summary>
+    internal enum ProjectViewType
+    {
+        Grid64, Grid48, Grid32, List16
+    }
+
+    /// <summary>
+    /// Editor window that displays all resources in the project. Resources can be displayed as a grid or list of icons,
+    /// 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
+    {
+        /// <summary>
+        /// Directions the selection cursor in library window can be moved in.
+        /// </summary>
+        internal enum MoveDirection
+        {
+            Up, Down, Left, Right
+        }
+
+        private const int DRAG_SCROLL_HEIGHT = 20;
+        private const int DRAG_SCROLL_AMOUNT_PER_SECOND = 300;
+        private const int FOLDER_BUTTON_WIDTH = 30;
+        private const int FOLDER_SEPARATOR_WIDTH = 10;
+        private const string CURRENT_LIBRARY_DIRECTORY_KEY = "__CurrentLibDir";
+
+        private bool hasContentFocus = false;
+        private bool HasContentFocus { get { return HasFocus && hasContentFocus; } }
+
+        private string searchQuery;
+        private bool IsSearchActive { get { return !string.IsNullOrEmpty(searchQuery); } }
+
+        private ProjectViewType viewType = ProjectViewType.Grid32;
+
+        private bool requiresRefresh;
+        private List<string> selectionPaths = new List<string>();
+        private int selectionAnchorStart = -1;
+        private int selectionAnchorEnd = -1;
+        private string pingPath = "";
+        private string hoverHighlightPath = "";
+
+        private LibraryGUIContent content;
+        private GUIScrollArea contentScrollArea;
+        private GUILayoutX searchBarLayout;
+        private GUIButton optionsButton;
+        private GUILayout folderBarLayout;
+        private GUILayout folderListLayout;
+        private GUITextField searchField;
+        private GUITexture dragSelection;
+
+        private ContextMenu entryContextMenu;
+        private LibraryDropTarget dropTarget;
+
+        private int autoScrollAmount;
+        private bool isDraggingSelection;
+        private Vector2I dragSelectionStart;
+        private Vector2I dragSelectionEnd;
+
+        private LibraryGUIEntry inProgressRenameElement;
+
+        // Cut/Copy/Paste
+        private List<string> copyPaths = new List<string>();
+        private List<string> cutPaths = new List<string>();
+
+        /// <summary>
+        /// Determines how to display resource tiles in the library window.
+        /// </summary>
+        internal ProjectViewType ViewType
+        {
+            get { return viewType; }
+            set { viewType = value; Refresh(); }
+        }
+
+        /// <summary>
+        /// Returns a file or folder currently selected in the library window. If nothing is selected, returns the active
+        /// folder. Returned path is relative to project library resources folder.
+        /// </summary>
+        public string SelectedEntry
+        {
+            get
+            {
+                if (selectionPaths.Count == 1)
+                {
+                    LibraryEntry entry = ProjectLibrary.GetEntry(selectionPaths[0]);
+                    if (entry != null)
+                        return entry.Path;
+                }
+
+                return CurrentFolder;
+            }
+        }
+
+        /// <summary>
+        /// Returns a folder currently selected in the library window. If no folder is selected, returns the active
+        /// folder. Returned path is relative to project library resources folder.
+        /// </summary>
+        public string SelectedFolder
+        {
+            get
+            {
+                DirectoryEntry selectedDirectory = null;
+                if (selectionPaths.Count == 1)
+                {
+                    LibraryEntry entry = ProjectLibrary.GetEntry(selectionPaths[0]);
+                    if (entry != null && entry.Type == LibraryEntryType.Directory)
+                        selectedDirectory = (DirectoryEntry) entry;
+                }
+
+                if (selectedDirectory != null)
+                    return selectedDirectory.Path;
+                
+                return CurrentFolder;
+            }
+        }
+
+        /// <summary>
+        /// Returns the path to the folder currently displayed in the library window. Returned path is relative to project 
+        /// library resources folder.
+        /// </summary>
+        public string CurrentFolder
+        {
+            get { return ProjectSettings.GetString(CURRENT_LIBRARY_DIRECTORY_KEY); }
+            set { ProjectSettings.SetString(CURRENT_LIBRARY_DIRECTORY_KEY, value); }
+        }
+
+        /// <summary>
+        /// Context menu that should open when user right clicks on the content area.
+        /// </summary>
+        internal ContextMenu ContextMenu
+        {
+            get { return entryContextMenu; }
+        }
+
+        /// <summary>
+        /// Opens the library window if not already open.
+        /// </summary>
+        [MenuItem("Windows/Library", ButtonModifier.CtrlAlt, ButtonCode.L, 6000)]
+        private static void OpenLibraryWindow()
+        {
+            OpenWindow<LibraryWindow>();
+        }
+
+        private void OnInitialize()
+        {
+            ProjectLibrary.OnEntryAdded += OnEntryChanged;
+            ProjectLibrary.OnEntryRemoved += OnEntryChanged;
+
+            GUILayoutY contentLayout = GUI.AddLayoutY();
+
+            searchBarLayout = contentLayout.AddLayoutX();
+            searchField = new GUITextField();
+            searchField.OnChanged += OnSearchChanged;
+
+            GUIContent clearIcon = new GUIContent(EditorBuiltin.GetLibraryWindowIcon(LibraryWindowIcon.Clear), 
+                new LocEdString("Clear"));
+            GUIButton clearSearchBtn = new GUIButton(clearIcon);
+            clearSearchBtn.OnClick += ClearSearch;
+            clearSearchBtn.SetWidth(40);
+
+            GUIContent optionsIcon = new GUIContent(EditorBuiltin.GetLibraryWindowIcon(LibraryWindowIcon.Options), 
+                new LocEdString("Options"));
+            optionsButton = new GUIButton(optionsIcon);
+            optionsButton.OnClick += OpenOptionsWindow;
+            optionsButton.SetWidth(40);
+            searchBarLayout.AddElement(searchField);
+            searchBarLayout.AddElement(clearSearchBtn);
+            searchBarLayout.AddElement(optionsButton);
+
+            folderBarLayout = contentLayout.AddLayoutX();
+
+            GUIContent homeIcon = new GUIContent(EditorBuiltin.GetLibraryWindowIcon(LibraryWindowIcon.Home), 
+                new LocEdString("Home"));
+            GUIButton homeButton = new GUIButton(homeIcon, GUIOption.FixedWidth(FOLDER_BUTTON_WIDTH));
+            homeButton.OnClick += OnHomeClicked;
+
+            GUIContent upIcon = new GUIContent(EditorBuiltin.GetLibraryWindowIcon(LibraryWindowIcon.Up), 
+                new LocEdString("Up"));
+            GUIButton upButton = new GUIButton(upIcon, GUIOption.FixedWidth(FOLDER_BUTTON_WIDTH));
+            upButton.OnClick += OnUpClicked;
+
+            folderBarLayout.AddElement(homeButton);
+            folderBarLayout.AddElement(upButton);
+            folderBarLayout.AddSpace(10);
+
+            contentScrollArea = new GUIScrollArea(GUIOption.FlexibleWidth(), GUIOption.FlexibleHeight());
+            contentLayout.AddElement(contentScrollArea);
+            contentLayout.AddFlexibleSpace();
+
+            entryContextMenu = LibraryMenu.CreateContextMenu(this);
+            content = new LibraryGUIContent(this, contentScrollArea);
+
+            Refresh();
+
+            dropTarget = new LibraryDropTarget(this);
+            dropTarget.Bounds = GetScrollAreaBounds();
+            dropTarget.OnStart += OnDragStart;
+            dropTarget.OnDrag += OnDragMove;
+            dropTarget.OnLeave += OnDragLeave;
+            dropTarget.OnDropResource += OnResourceDragDropped;
+            dropTarget.OnDropSceneObject += OnSceneObjectDragDropped;
+            dropTarget.OnEnd += OnDragEnd;
+
+            Selection.OnSelectionChanged += OnSelectionChanged;
+            Selection.OnResourcePing += OnPing;
+        }
+
+        private void OnDestroy()
+        {
+            Selection.OnSelectionChanged -= OnSelectionChanged;
+            Selection.OnResourcePing -= OnPing;
+
+            dropTarget.Destroy();
+        }
+
+        private void OnEditorUpdate()
+        {
+            bool isRenameInProgress = inProgressRenameElement != null;
+
+            if (HasContentFocus)
+            {
+                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)
+                        {
+                            LibraryEntry entry = ProjectLibrary.GetEntry(selectionPaths[0]);
+                            if (entry != null && entry.Type == LibraryEntryType.Directory)
+                            {
+                                EnterDirectory(entry.Path);
+                            }
+                        }
+                    }
+                    else if (Input.IsButtonDown(ButtonCode.Back))
+                    {
+                        LibraryEntry entry = ProjectLibrary.GetEntry(CurrentFolder);
+                        if (entry != null && entry.Parent != null)
+                        {
+                            EnterDirectory(entry.Parent.Path);
+                        }
+                    }
+                    else if (Input.IsButtonDown(ButtonCode.Up))
+                    {
+                        MoveSelection(MoveDirection.Up);
+                    }
+                    else if (Input.IsButtonDown(ButtonCode.Down))
+                    {
+                        MoveSelection(MoveDirection.Down);
+                    }
+                    else if (Input.IsButtonDown(ButtonCode.Left))
+                    {
+                        MoveSelection(MoveDirection.Left);
+                    }
+                    else if (Input.IsButtonDown(ButtonCode.Right))
+                    {
+                        MoveSelection(MoveDirection.Right);
+                    }
+                    else if (Input.IsButtonDown(ButtonCode.F2))
+                    {
+                        RenameSelection();
+                    }
+                    else if (Input.IsButtonDown(ButtonCode.Delete))
+                    {
+                        DeleteSelection();
+                    }
+                }
+                else
+                {
+                    if (Input.IsButtonDown(ButtonCode.Return))
+                    {
+                        string newName = inProgressRenameElement.GetRenamedName();
+
+                        string originalPath = inProgressRenameElement.path;
+                        originalPath = originalPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
+
+                        string newPath = Path.GetDirectoryName(originalPath);
+                        string newNameWithExtension = newName + Path.GetExtension(originalPath);
+                        newPath = Path.Combine(newPath, newNameWithExtension);
+
+                        bool renameOK = true;
+                        if (!PathEx.IsValidFileName(newName))
+                        {
+                            DialogBox.Open(new LocEdString("Error"), new LocEdString("The name you specified is not a valid file name. Try another."), DialogBox.Type.OK);
+                            renameOK = false;
+                        }
+
+                        if (renameOK)
+                        {
+                            // Windows sees paths with dot at the end as if they didn't have it
+                            // so remove the dot to ensure the project library does the same
+                            string trimmedNewPath = newPath.TrimEnd('.');
+
+                            if (originalPath != trimmedNewPath && ProjectLibrary.Exists(trimmedNewPath))
+                            {
+                                DialogBox.Open(new LocEdString("Error"), new LocEdString("File/folder with that name already exists in this folder."), DialogBox.Type.OK);
+                                renameOK = false;
+                            }
+                        }
+
+                        if (renameOK)
+                        {
+                            ProjectLibrary.Rename(originalPath, newNameWithExtension);
+                            StopRename();
+                        }
+                    }
+                    else if (Input.IsButtonDown(ButtonCode.Escape))
+                    {
+                        StopRename();
+                    }
+                }
+            }
+            else
+            {
+                if (isRenameInProgress)
+                    StopRename();
+            }
+
+            if (autoScrollAmount != 0)
+            {
+                Rect2I contentBounds = contentScrollArea.ContentBounds;
+                float scrollPct = autoScrollAmount / (float)contentBounds.height;
+
+                contentScrollArea.VerticalScroll += scrollPct * Time.FrameDelta;
+            }
+
+            if (requiresRefresh)
+                Refresh();
+
+            dropTarget.Update();
+            content.Update();
+        }
+
+        /// <inheritdoc/>
+        protected override LocString GetDisplayName()
+        {
+            return new LocEdString("Library");
+        }
+
+        /// <inheritdoc/>
+        protected override void WindowResized(int width, int height)
+        {
+            base.WindowResized(width, height);
+
+            Refresh();
+
+            dropTarget.Bounds = GetScrollAreaBounds();
+        }
+
+        /// <summary>
+        /// Attempts to find a resource tile element at the specified coordinates.
+        /// </summary>
+        /// <param name="windowPos">Coordinates relative to the window.</param>
+        /// <returns>True if found an entry, false otherwise.</returns>
+        private LibraryGUIEntry FindElementAt(Vector2I windowPos)
+        {
+            Vector2I scrollPos = WindowToScrollAreaCoords(windowPos);
+
+            return content.FindElementAt(scrollPos);
+        }
+
+        /// <summary>
+        /// Clears hover highlight from the currently hovered over element.
+        /// </summary>
+        private void ClearHoverHighlight()
+        {
+            content.MarkAsHovered(hoverHighlightPath, false);
+            hoverHighlightPath = "";
+        }
+
+        /// <summary>
+        /// Pings an element at the specified path, displaying and highlighting it in the window.
+        /// </summary>
+        /// <param name="path">Project library path to the element.</param>
+        public void Ping(string path)
+        {
+            content.MarkAsPinged(pingPath, false);
+            pingPath = path;
+            content.MarkAsPinged(pingPath, true);
+        }
+
+        /// <summary>
+        /// Resets the library window to initial state.
+        /// </summary>
+        public void Reset()
+        {
+            CurrentFolder = ProjectLibrary.Root.Path;
+            selectionAnchorStart = -1;
+            selectionAnchorEnd = -1;
+            selectionPaths.Clear();
+            pingPath = "";
+            hoverHighlightPath = "";
+
+            Refresh();
+        }
+
+        /// <summary>
+        /// Deselects all selected elements.
+        /// </summary>
+        /// <param name="onlyInternal">If true, do not update the global <see cref="Selection"/>, instead the operation
+        ///                            will be contained to the library window internally.</param>
+        internal void DeselectAll(bool onlyInternal = false)
+        {
+            SetSelection(new List<string>(), onlyInternal);
+            selectionAnchorStart = -1;
+            selectionAnchorEnd = -1;
+        }
+
+        /// <summary>
+        /// Select an element at the specified path. If control or shift keys are pressed during this operations multiple 
+        /// elements can be selected.
+        /// </summary>
+        /// <param name="path">Project library path to the element.</param>
+        internal void Select(string path)
+        {
+            LibraryGUIEntry entry;
+            if (!content.TryGetEntry(path, out entry))
+                return;
+
+            bool ctrlDown = Input.IsButtonHeld(ButtonCode.LeftControl) || Input.IsButtonHeld(ButtonCode.RightControl);
+            bool shiftDown = Input.IsButtonHeld(ButtonCode.LeftShift) || Input.IsButtonHeld(ButtonCode.RightShift);
+
+            if (shiftDown)
+            {
+                if (selectionAnchorStart != -1 && selectionAnchorStart < content.Entries.Count)
+                {
+                    int start = Math.Min(entry.index, selectionAnchorStart);
+                    int end = Math.Max(entry.index, selectionAnchorStart);
+
+                    List<string> newSelection = new List<string>();
+                    for(int i = start; i <= end; i++)
+                        newSelection.Add(content.Entries[i].path);
+
+                    SetSelection(newSelection);
+                    selectionAnchorEnd = entry.index;
+                }
+                else
+                {
+                    SetSelection(new List<string>() {path});
+                    selectionAnchorStart = entry.index;
+                    selectionAnchorEnd = entry.index;
+                }
+            }
+            else if (ctrlDown)
+            {
+                List<string> newSelection = new List<string>(selectionPaths);
+
+                if (selectionPaths.Contains(path))
+                {
+                    newSelection.Remove(path);
+                    if (newSelection.Count == 0)
+                        DeselectAll();
+                    else
+                    {
+                        if (selectionAnchorStart == entry.index)
+                        {
+                            LibraryGUIEntry newAnchorEntry;
+                            if (!content.TryGetEntry(newSelection[0], out newAnchorEntry))
+                                selectionAnchorStart = -1;
+                            else
+                                selectionAnchorStart = newAnchorEntry.index;
+                        }
+
+                        if (selectionAnchorEnd == entry.index)
+                        {
+                            LibraryGUIEntry newAnchorEntry;
+                            if (!content.TryGetEntry(newSelection[newSelection.Count - 1], out newAnchorEntry))
+                                selectionAnchorEnd = -1;
+                            else
+                                selectionAnchorEnd = newAnchorEntry.index;
+                        }
+
+                        SetSelection(newSelection);
+                    }
+                }
+                else
+                {
+                    newSelection.Add(path);
+                    SetSelection(newSelection);
+                    selectionAnchorEnd = entry.index;
+                }
+            }
+            else
+            {
+                SetSelection(new List<string>() {path});
+
+                selectionAnchorStart = entry.index;
+                selectionAnchorEnd = entry.index;
+            }
+        }
+
+        /// <summary>
+        /// Selects a new element in the specified direction from the currently selected element. If shift or control are
+        /// held during this operation, the selected object will be added to existing selection. If no element is selected
+        /// the first or last element will be selected depending on direction.
+        /// </summary>
+        /// <param name="dir">Direction to move from the currently selected element.</param>
+        internal void MoveSelection(MoveDirection dir)
+        {
+            string newPath = "";
+
+            if (selectionPaths.Count == 0 || selectionAnchorEnd == -1)
+            {
+                // Nothing is selected so we arbitrarily select first or last element
+                if (content.Entries.Count > 0)
+                {
+                    switch (dir)
+                    {
+                        case MoveDirection.Left:
+                        case MoveDirection.Up:
+                            newPath = content.Entries[content.Entries.Count - 1].path;
+                            break;
+                        case MoveDirection.Right:
+                        case MoveDirection.Down:
+                            newPath = content.Entries[0].path;
+                            break;
+                    }
+                }
+            }
+            else
+            {
+                switch (dir)
+                {
+                    case MoveDirection.Left:
+                        if (selectionAnchorEnd - 1 >= 0)
+                            newPath = content.Entries[selectionAnchorEnd - 1].path;
+                        break;
+                    case MoveDirection.Up:
+                        if (selectionAnchorEnd - content.ElementsPerRow >= 0)
+                            newPath = content.Entries[selectionAnchorEnd - content.ElementsPerRow].path;
+                        break;
+                    case MoveDirection.Right:
+                        if (selectionAnchorEnd + 1 < content.Entries.Count)
+                            newPath = content.Entries[selectionAnchorEnd + 1].path;
+                        break;
+                    case MoveDirection.Down:
+                        if (selectionAnchorEnd + content.ElementsPerRow < content.Entries.Count)
+                            newPath = content.Entries[selectionAnchorEnd + content.ElementsPerRow].path;
+                        break;
+                }
+            }
+
+            if (!string.IsNullOrEmpty(newPath))
+            {
+                Select(newPath);
+                ScrollToEntry(newPath);
+            }
+        }
+
+        /// <summary>
+        /// Selects a set of elements based on the provided paths.
+        /// </summary>
+        /// <param name="paths">Project library paths of the elements to select.</param>
+        /// <param name="onlyInternal">If true, do not update the global <see cref="Selection"/>, instead the operation
+        ///                            will be contained to the library window internally.</param>
+        internal void SetSelection(List<string> paths, bool onlyInternal = false)
+        {
+            if (selectionPaths != null)
+            {
+                foreach (var path in selectionPaths)
+                    content.MarkAsSelected(path, false);
+            }
+
+            selectionPaths = paths;
+            Ping("");
+
+            if (selectionPaths != null)
+            {
+                foreach (var path in selectionPaths)
+                    content.MarkAsSelected(path, true);
+            }
+
+            StopRename();
+
+            if (!onlyInternal)
+            {
+                if (selectionPaths != null)
+                    Selection.ResourcePaths = selectionPaths.ToArray();
+                else
+                    Selection.ResourcePaths = new string[0];
+            }
+        }
+
+        /// <summary>
+        /// Changes the active directory to the provided directory. Current contents of the window will be cleared and
+        /// instead contents of the new directory will be displayed.
+        /// </summary>
+        /// <param name="directory">Project library path to the directory.</param>
+        internal void EnterDirectory(string directory)
+        {
+            CurrentFolder = directory;
+            DeselectAll(true);
+
+            Refresh();
+        }
+
+        /// <summary>
+        /// Marks the provided set of elements for a cut operation. Cut elements can be moved to a new location by calling
+        /// <see cref="Paste"/>.
+        /// </summary>
+        /// <param name="sourcePaths">Project library paths of the elements to cut.</param>
+        internal void Cut(IEnumerable<string> sourcePaths)
+        {
+            foreach (var path in cutPaths)
+                content.MarkAsCut(path, false);
+
+            string[] filePaths = GetFiles(sourcePaths);
+
+            cutPaths.Clear();
+            cutPaths.AddRange(filePaths);
+
+            foreach (var path in cutPaths)
+                content.MarkAsCut(path, true);
+
+            copyPaths.Clear();
+        }
+
+        /// <summary>
+        /// Marks the provided set of elements for a copy operation. You can copy the elements by calling <see cref="Paste"/>.
+        /// </summary>
+        /// <param name="sourcePaths">Project library paths of the elements to copy.</param>
+        internal void Copy(IEnumerable<string> sourcePaths)
+        {
+            copyPaths.Clear();
+
+            string[] filePaths = GetFiles(sourcePaths);
+            copyPaths.AddRange(filePaths);
+
+            foreach (var path in cutPaths)
+                content.MarkAsCut(path, false);
+
+            cutPaths.Clear();
+        }
+       
+        /// <summary>
+        /// Duplicates the provided set of elements.
+        /// </summary>
+        /// <param name="sourcePaths">Project library paths of the elements to duplicate.</param>
+        internal void Duplicate(IEnumerable<string> sourcePaths)
+        {
+            string[] filePaths = GetFiles(sourcePaths);
+
+            foreach (var source in filePaths)
+            {
+                string path = source;
+                if (!Path.IsPathRooted(path))
+                    path = Path.Combine(ProjectLibrary.ResourceFolder, path);
+
+                if (Directory.Exists(path))
+                    DirectoryEx.Copy(path, LibraryUtility.GetUniquePath(path));
+                else if (File.Exists(path))
+                    FileEx.Copy(path, LibraryUtility.GetUniquePath(path));
+
+                ProjectLibrary.Refresh();
+            }
+        }
+
+        /// <summary>
+        /// Performs a cut or copy operations on the elements previously marked by calling <see cref="Cut"/> or
+        /// <see cref="Copy"/>.
+        /// </summary>
+        /// <param name="destinationFolder">Project library folder into which to move/copy the elements.</param>
+        internal void Paste(string destinationFolder)
+        {
+            string rootedDestinationFolder = destinationFolder;
+            if (!Path.IsPathRooted(rootedDestinationFolder))
+                rootedDestinationFolder = Path.Combine(ProjectLibrary.ResourceFolder, rootedDestinationFolder);
+
+            if (copyPaths.Count > 0)
+            {
+                for (int i = 0; i < copyPaths.Count; i++)
+                {
+                    string path = copyPaths[i];
+                    if (!Path.IsPathRooted(path))
+                        path = Path.Combine(ProjectLibrary.ResourceFolder, path);
+
+                    string destination = Path.Combine(rootedDestinationFolder, PathEx.GetTail(copyPaths[i]));
+
+                    if (Directory.Exists(path))
+                        DirectoryEx.Copy(path, LibraryUtility.GetUniquePath(destination));
+                    else if (File.Exists(path))
+                        FileEx.Copy(path, LibraryUtility.GetUniquePath(destination));
+                }
+
+                ProjectLibrary.Refresh();
+            }
+            else if (cutPaths.Count > 0)
+            {
+                for (int i = 0; i < cutPaths.Count; i++)
+                {
+                    string path = cutPaths[i];
+                    if (!Path.IsPathRooted(path))
+                        path = Path.Combine(ProjectLibrary.ResourceFolder, path);
+
+                    string destination = Path.Combine(rootedDestinationFolder, PathEx.GetTail(cutPaths[i]));
+
+                    if (Directory.Exists(path))
+                        DirectoryEx.Move(path, LibraryUtility.GetUniquePath(destination));
+                    else if (File.Exists(path))
+                        FileEx.Move(path, LibraryUtility.GetUniquePath(destination));
+                }
+
+                cutPaths.Clear();
+                ProjectLibrary.Refresh();
+            }
+        }
+
+        /// <summary>
+        /// Scrolls the contents GUI area so that the element at the specified path becomes visible.
+        /// </summary>
+        /// <param name="path">Project library path to the element.</param>
+        private void ScrollToEntry(string path)
+        {
+            LibraryGUIEntry entryGUI;
+            if (!content.TryGetEntry(path, out entryGUI))
+                return;
+
+            Rect2I entryBounds = entryGUI.Bounds;
+
+            Rect2I contentBounds = contentScrollArea.Layout.Bounds;
+            Rect2I windowEntryBounds = entryBounds;
+            windowEntryBounds.x += contentBounds.x;
+            windowEntryBounds.y += contentBounds.y;
+
+            Rect2I scrollAreaBounds = contentScrollArea.Bounds;
+            bool requiresScroll = windowEntryBounds.y < scrollAreaBounds.y ||
+                                  (windowEntryBounds.y + windowEntryBounds.height) > (scrollAreaBounds.y + scrollAreaBounds.height);
+
+            if (!requiresScroll)
+                return;
+
+            int scrollableSize = contentBounds.height - scrollAreaBounds.height;
+            float percent = (((entryBounds.y + entryBounds.height * 0.5f) - scrollAreaBounds.height * 0.5f) / (float)scrollableSize);
+
+            percent = MathEx.Clamp01(percent);
+            contentScrollArea.VerticalScroll = percent;
+        }
+
+        /// <summary>
+        /// Rebuilds the library window GUI. Should be called any time the active folder or contents change.
+        /// </summary>
+        private void Refresh()
+        {
+            requiresRefresh = false;
+
+            LibraryEntry[] entriesToDisplay = new LibraryEntry[0];
+            if (IsSearchActive)
+            {
+                entriesToDisplay = ProjectLibrary.Search("*" + searchQuery + "*");
+            }
+            else
+            {
+                DirectoryEntry entry = ProjectLibrary.GetEntry(CurrentFolder) as DirectoryEntry;
+                if (entry == null)
+                {
+                    CurrentFolder = ProjectLibrary.Root.Path;
+                    entry = ProjectLibrary.GetEntry(CurrentFolder) as DirectoryEntry;
+                }
+
+                if(entry != null)
+                    entriesToDisplay = entry.Children;
+            }
+
+            inProgressRenameElement = null;
+
+            RefreshDirectoryBar();
+
+            SortEntries(entriesToDisplay);
+            content.Refresh(viewType, entriesToDisplay);
+
+            foreach (var path in cutPaths)
+                content.MarkAsCut(path, true);
+
+            foreach (var path in selectionPaths)
+                content.MarkAsSelected(path, true);
+
+            content.MarkAsPinged(pingPath, true);
+
+            Rect2I contentBounds = content.Bounds;
+            Rect2I minimalBounds = GetScrollAreaBounds();
+            contentBounds.height = Math.Max(contentBounds.height, minimalBounds.height);
+
+            GUIButton catchAll = new GUIButton("", EditorStyles.Blank);
+            catchAll.Bounds = contentBounds;
+            catchAll.OnClick += OnCatchAllClicked;
+            catchAll.SetContextMenu(entryContextMenu);
+
+            content.Underlay.AddElement(catchAll);
+
+            Rect2I focusBounds = contentBounds; // Contents + Folder bar
+            Rect2I scrollBounds = contentScrollArea.Bounds;
+            focusBounds.x += scrollBounds.x;
+            focusBounds.y += scrollBounds.y;
+
+            Rect2I folderBarBounds = folderListLayout.Bounds;
+            focusBounds.y -= folderBarBounds.height;
+            focusBounds.height += folderBarBounds.height;
+
+            GUIButton focusCatcher = new GUIButton("", EditorStyles.Blank);
+            focusCatcher.Blocking = false;
+            focusCatcher.OnFocusGained += () => hasContentFocus = true;
+            focusCatcher.OnFocusLost += () => hasContentFocus = false;
+            focusCatcher.Bounds = focusBounds;
+
+            GUIPanel focusPanel = GUI.AddPanel(-3);
+            focusPanel.AddElement(focusCatcher);
+
+            UpdateDragSelection(dragSelectionEnd);
+        }
+
+        /// <summary>
+        /// Converts coordinates relative to the window into coordinates relative to the contents scroll area.
+        /// </summary>
+        /// <param name="windowPos">Coordinates relative to the window.</param>
+        /// <returns>Coordinates relative to the contents scroll area.</returns>
+        private Vector2I WindowToScrollAreaCoords(Vector2I windowPos)
+        {
+            Rect2I scrollBounds = contentScrollArea.Layout.Bounds;
+            Vector2I scrollPos = windowPos;
+            scrollPos.x -= scrollBounds.x;
+            scrollPos.y -= scrollBounds.y;
+
+            return scrollPos;
+        }
+
+        /// <summary>
+        /// Starts a drag operation that displays a selection outline allowing the user to select multiple entries at once.
+        /// </summary>
+        /// <param name="windowPos">Coordinates relative to the window where the drag originated.</param>
+        private void StartDragSelection(Vector2I windowPos)
+        {
+            isDraggingSelection = true;
+            dragSelectionStart = WindowToScrollAreaCoords(windowPos);
+            dragSelectionEnd = dragSelectionStart;
+        }
+
+        /// <summary>
+        /// Updates a selection outline drag operation by expanding the outline to the new location. Elements in the outline
+        /// are selected.
+        /// </summary>
+        /// <param name="windowPos">Coordinates of the pointer relative to the window.</param>
+        /// <returns>True if the selection outline drag is valid and was updated, false otherwise.</returns>
+        private bool UpdateDragSelection(Vector2I windowPos)
+        {
+            if (!isDraggingSelection)
+                return false;
+
+            if (dragSelection == null)
+            {
+                dragSelection = new GUITexture(null, true, EditorStyles.SelectionArea);
+                content.Overlay.AddElement(dragSelection);
+            }
+
+            dragSelectionEnd = WindowToScrollAreaCoords(windowPos);
+
+            Rect2I selectionArea = CalculateSelectionArea();
+            SelectInArea(selectionArea);
+            dragSelection.Bounds = selectionArea;
+
+            return true;
+        }
+
+        /// <summary>
+        /// Ends the selection outline drag operation. Elements in the outline are selected.
+        /// </summary>
+        /// <returns>True if the selection outline drag is valid and was ended, false otherwise.</returns>
+        private bool EndDragSelection()
+        {
+            if (!isDraggingSelection)
+                return false;
+
+            if (dragSelection != null)
+            {
+                dragSelection.Destroy();
+                dragSelection = null;
+            }
+
+            Rect2I selectionArea = CalculateSelectionArea();
+            SelectInArea(selectionArea);
+
+            isDraggingSelection = false;
+            return false;
+        }
+
+        /// <summary>
+        /// Calculates bounds of the selection area used for selection overlay drag operation, depending on drag starting
+        /// point coordinates and current drag coordinates.
+        /// </summary>
+        /// <returns>Bounds of the selection area, relative to the content scroll area.</returns>
+        private Rect2I CalculateSelectionArea()
+        {
+            Rect2I selectionArea = new Rect2I();
+            if (dragSelectionStart.x < dragSelectionEnd.x)
+            {
+                selectionArea.x = dragSelectionStart.x;
+                selectionArea.width = dragSelectionEnd.x - dragSelectionStart.x;
+            }
+            else
+            {
+                selectionArea.x = dragSelectionEnd.x;
+                selectionArea.width = dragSelectionStart.x - dragSelectionEnd.x;
+            }
+
+            if (dragSelectionStart.y < dragSelectionEnd.y)
+            {
+                selectionArea.y = dragSelectionStart.y;
+                selectionArea.height = dragSelectionEnd.y - dragSelectionStart.y;
+            }
+            else
+            {
+                selectionArea.y = dragSelectionEnd.y;
+                selectionArea.height = dragSelectionStart.y - dragSelectionEnd.y;
+            }
+
+            Rect2I maxBounds = contentScrollArea.Layout.Bounds;
+            maxBounds.x = 0;
+            maxBounds.y = 0;
+
+            selectionArea.Clip(maxBounds);
+
+            return selectionArea;
+        }
+
+        /// <summary>
+        /// Selects all elements overlapping the specified bounds.
+        /// </summary>
+        /// <param name="scrollBounds">Bounds relative to the content scroll area.</param>
+        private void SelectInArea(Rect2I scrollBounds)
+        {
+            LibraryGUIEntry[] foundElements = content.FindElementsOverlapping(scrollBounds);
+
+            if (foundElements.Length > 0)
+            {
+                selectionAnchorStart = foundElements[0].index;
+                selectionAnchorEnd = foundElements[foundElements.Length - 1].index;
+            }
+            else
+            {
+                selectionAnchorStart = -1;
+                selectionAnchorEnd = -1;
+            }
+
+            List<string> elementPaths = new List<string>();
+            foreach (var elem in foundElements)
+                elementPaths.Add(elem.path);
+
+            SetSelection(elementPaths);
+        }
+
+        /// <summary>
+        /// Updates GUI for the directory bar. Should be called whenever the active folder changes.
+        /// </summary>
+        private void RefreshDirectoryBar()
+        {
+            if (folderListLayout != null)
+            {
+                folderListLayout.Destroy();
+                folderListLayout = null;
+            }
+
+            folderListLayout = folderBarLayout.AddLayoutX();
+
+            string[] folders = null;
+            string[] fullPaths = null;
+
+            if (IsSearchActive)
+            {
+                folders = new[] {searchQuery};
+                fullPaths = new[] { searchQuery };
+            }
+            else
+            {
+                string currentDir = Path.Combine("Resources", CurrentFolder);
+
+                folders = currentDir.Split(new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar },
+                    StringSplitOptions.RemoveEmptyEntries);
+
+                fullPaths = new string[folders.Length];
+                for (int i = 0; i < folders.Length; i++)
+                {
+                    if (i == 0)
+                        fullPaths[i] = "";
+                    else
+                        fullPaths[i] = Path.Combine(fullPaths[i - 1], folders[i]);
+                }
+            }
+
+            int availableWidth = folderBarLayout.Bounds.width - FOLDER_BUTTON_WIDTH * 2;
+            int numFolders = 0;
+            for (int i = folders.Length - 1; i >= 0; i--)
+            {
+                GUIButton folderButton = new GUIButton(folders[i]);
+
+                if (!IsSearchActive)
+                {
+                    string fullPath = fullPaths[i];
+                    folderButton.OnClick += () => OnFolderButtonClicked(fullPath);
+                }
+
+                GUIButton separator = new GUIButton("/", GUIOption.FixedWidth(FOLDER_SEPARATOR_WIDTH));
+
+                folderListLayout.InsertElement(0, separator);
+                folderListLayout.InsertElement(0, folderButton);
+                numFolders++;
+
+                Rect2I folderListBounds = folderListLayout.Bounds;
+                if (folderListBounds.width > availableWidth)
+                {
+                    if (numFolders > 2)
+                    {
+                        separator.Destroy();
+                        folderButton.Destroy();
+                        break;
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Performs <see cref="Cut"/> operation on the currently selected elements.
+        /// </summary>
+        internal void CutSelection()
+        {
+            if (selectionPaths.Count > 0)
+                Cut(selectionPaths);
+        }
+
+        /// <summary>
+        /// Performs <see cref="Copy"/> operation on the currently selected elements.
+        /// </summary>
+        internal void CopySelection()
+        {
+            if (selectionPaths.Count > 0)
+                Copy(selectionPaths);
+        }
+
+        /// <summary>
+        /// Performs <see cref="Duplicate"/> operation on the currently selected elements.
+        /// </summary>
+        internal void DuplicateSelection()
+        {
+            if (selectionPaths.Count > 0)
+                Duplicate(selectionPaths);
+        }
+
+        /// <summary>
+        /// Performs <see cref="Paste"/> operation. Elements will be pasted in the currently selected directory (if any), or
+        /// the active directory otherwise.
+        /// </summary>
+        internal void PasteToSelection()
+        {
+            Paste(SelectedFolder);
+        }
+
+        /// <summary>
+        /// Starts a rename operation on the currently selected elements. If more than one elements are selected only the
+        /// first one will be affected.
+        /// </summary>
+        internal void RenameSelection()
+        {
+            string[] filePaths = GetFiles(selectionPaths);
+
+            if (filePaths.Length == 0)
+                return;
+
+            if (filePaths.Length > 1)
+            {
+                DeselectAll();
+                Select(filePaths[0]);
+            }
+
+            LibraryGUIEntry entry;
+            if (content.TryGetEntry(filePaths[0], out entry))
+            {
+                entry.StartRename();
+                inProgressRenameElement = entry;
+            }
+        }
+
+        /// <summary>
+        /// Deletes currently selected elements. User will be asked to confirm deletion via a dialog box.
+        /// </summary>
+        internal void DeleteSelection()
+        {
+            string[] filePaths = GetFiles(selectionPaths);
+
+            if (filePaths.Length == 0)
+                return;
+
+            DialogBox.Open(new LocEdString("Confirm deletion"), new LocEdString("Are you sure you want to delete the selected object(s)?"),
+                DialogBox.Type.YesNo,
+                type =>
+                {
+                    if (type == DialogBox.ResultType.Yes)
+                    {
+                        foreach (var path in filePaths)
+                            ProjectLibrary.Delete(path);
+
+                        DeselectAll();
+                        Refresh();
+                    }
+                });
+        }
+
+        /// <summary>
+        /// Stops the rename operation, if one is in progress on any element.
+        /// </summary>
+        internal void StopRename()
+        {
+            if (inProgressRenameElement != null)
+            {
+                inProgressRenameElement.StopRename();
+                inProgressRenameElement = null;
+            }
+        }
+
+        /// <summary>
+        /// Clears the search bar and refreshes the content area to display contents of the current directory.
+        /// </summary>
+        private void ClearSearch()
+        {
+            searchField.Value = "";
+            searchQuery = "";
+            Refresh();
+        }
+
+        /// <summary>
+        /// Takes a list of resource paths and returns only those referencing files or folder and not sub-resources.
+        /// </summary>
+        /// <param name="resourcePaths">List of resource paths to find files for.</param>
+        /// <returns>File paths for all the provided resources.</returns>
+        private string[] GetFiles(IEnumerable<string> resourcePaths)
+        {
+            HashSet<string> filePaths = new HashSet<string>();
+
+            foreach (var resPath in resourcePaths)
+            {
+                if (resPath == null)
+                    continue;
+
+                LibraryEntry entry = ProjectLibrary.GetEntry(resPath);
+                if (entry == null)
+                    continue;
+
+                if (ProjectLibrary.IsSubresource(resPath))
+                    continue;
+
+                if (!filePaths.Contains(entry.Path))
+                    filePaths.Add(entry.Path);
+            }
+
+            string[] output = new string[filePaths.Count];
+
+            int i = 0;
+            foreach(var path in filePaths)
+                output[i++] = path;
+
+            return output;
+        }
+
+        /// <summary>
+        /// Opens the drop down options window that allows you to customize library window look and feel.
+        /// </summary>
+        private void OpenOptionsWindow()
+        {
+            Vector2I openPosition;
+            Rect2I buttonBounds = GUILayoutUtility.CalculateBounds(optionsButton, GUI);
+
+            openPosition.x = buttonBounds.x + buttonBounds.width / 2;
+            openPosition.y = buttonBounds.y + buttonBounds.height / 2;
+
+            LibraryDropDown dropDown = DropDownWindow.Open<LibraryDropDown>(this, openPosition);
+            dropDown.Initialize(this);
+        }
+
+        /// <summary>
+        /// Returns the content scroll area bounds.
+        /// </summary>
+        /// <returns>Bounds of the content scroll area, relative to the window.</returns>
+        private Rect2I GetScrollAreaBounds()
+        {
+            Rect2I bounds = GUI.Bounds;
+            Rect2I folderListBounds = folderListLayout.Bounds;
+            Rect2I searchBarBounds = searchBarLayout.Bounds;
+
+            bounds.y = folderListBounds.height + searchBarBounds.height;
+            bounds.height -= folderListBounds.height + searchBarBounds.height;
+
+            return bounds;
+        }
+
+        /// <summary>
+        /// Triggered when a project library entry was changed (added, modified, deleted).
+        /// </summary>
+        /// <param name="entry">Project library path of the changed entry.</param>
+        private void OnEntryChanged(string entry)
+        {
+            requiresRefresh = true;
+        }
+
+        /// <summary>
+        /// Triggered when the drag and drop operation is starting while over the content area. If drag operation is over
+        /// an element, element will be dragged.
+        /// </summary>
+        /// <param name="windowPos">Coordinates where the drag operation started, relative to the window.</param>
+        private void OnDragStart(Vector2I windowPos)
+        {
+            bool isRenameInProgress = inProgressRenameElement != null;
+            if (isRenameInProgress)
+                return;
+
+            LibraryGUIEntry underCursorElem = FindElementAt(windowPos);
+            if (underCursorElem == null)
+            {
+                StartDragSelection(windowPos);
+                return;
+            }
+
+            string resourceDir = ProjectLibrary.ResourceFolder;
+
+            string[] dragPaths = null;
+            if (selectionPaths.Count > 0)
+            {
+                foreach (var path in selectionPaths)
+                {
+                    if (path == underCursorElem.path)
+                    {
+                        dragPaths = new string[selectionPaths.Count];
+                        for (int i = 0; i < selectionPaths.Count; i++)
+                        {
+                            dragPaths[i] = Path.Combine(resourceDir, selectionPaths[i]);
+                        }
+
+                        break;
+                    }
+                }
+            }
+
+            if (dragPaths == null)
+                dragPaths = new[] { Path.Combine(resourceDir, underCursorElem.path) };
+
+            ResourceDragDropData dragDropData = new ResourceDragDropData(dragPaths);
+            DragDrop.StartDrag(dragDropData);
+        }
+
+        /// <summary>
+        /// Triggered when a pointer is moved while a drag operation is in progress.
+        /// </summary>
+        ///  <param name="windowPos">Coordinates of the pointer relative to the window.</param>
+        private void OnDragMove(Vector2I windowPos)
+        {
+            // Auto-scroll
+            Rect2I scrollAreaBounds = contentScrollArea.Bounds;
+            int scrollAreaTop = scrollAreaBounds.y;
+            int scrollAreaBottom = scrollAreaBounds.y + scrollAreaBounds.height;
+
+            if (windowPos.y > scrollAreaTop && windowPos.y <= (scrollAreaTop + DRAG_SCROLL_HEIGHT))
+                autoScrollAmount = -DRAG_SCROLL_AMOUNT_PER_SECOND;
+            else if (windowPos.y >= (scrollAreaBottom - DRAG_SCROLL_HEIGHT) && windowPos.y < scrollAreaBottom)
+                autoScrollAmount = DRAG_SCROLL_AMOUNT_PER_SECOND;
+            else
+                autoScrollAmount = 0;
+
+            // Selection box
+            if (UpdateDragSelection(windowPos))
+                return;
+
+            // Drag and drop (hover element under cursor)
+            LibraryGUIEntry underCursorElem = FindElementAt(windowPos);
+
+            if (underCursorElem == null)
+            {
+                ClearHoverHighlight();
+            }
+            else
+            {
+                if (underCursorElem.path != hoverHighlightPath)
+                {
+                    ClearHoverHighlight();
+
+                    hoverHighlightPath = underCursorElem.path;
+                    underCursorElem.MarkAsHovered(true);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Triggered when a pointer leaves the drop targer while a drag operation is in progress.
+        /// </summary>
+        private void OnDragLeave()
+        {
+            ClearHoverHighlight();
+            autoScrollAmount = 0;
+        }
+
+        /// <summary>
+        /// Triggered when a resource drop operation finishes over the content area.
+        /// </summary>
+        /// <param name="windowPos">Coordinates of the pointer relative to the window where the drop operation finished
+        ///                         .</param>
+        /// <param name="paths">Paths of the dropped resources.</param>
+        private void OnResourceDragDropped(Vector2I windowPos, string[] paths)
+        {
+            ClearHoverHighlight();
+            autoScrollAmount = 0;
+
+            if (EndDragSelection())
+                return;
+
+            string resourceDir = ProjectLibrary.ResourceFolder;
+            string destinationFolder = Path.Combine(resourceDir, CurrentFolder);
+
+            LibraryGUIEntry underCursorElement = FindElementAt(windowPos);
+            if (underCursorElement != null)
+            {
+                LibraryEntry entry = ProjectLibrary.GetEntry(underCursorElement.path);
+                if (entry != null && entry.Type == LibraryEntryType.Directory)
+                    destinationFolder = Path.Combine(resourceDir, entry.Path);
+            }
+
+            if (paths != null)
+            {
+                string[] filePaths = GetFiles(paths);
+
+                List<string> addedResources = new List<string>();
+                foreach (var path in filePaths)
+                {
+                    string absolutePath = path;
+                    if (!Path.IsPathRooted(absolutePath))
+                        absolutePath = Path.Combine(resourceDir, path);
+
+                    if (string.IsNullOrEmpty(absolutePath))
+                        continue;
+
+                    if (PathEx.IsPartOf(destinationFolder, absolutePath) || PathEx.Compare(absolutePath, destinationFolder))
+                        continue;
+
+                    string pathTail = PathEx.GetTail(absolutePath);
+                    string destination = Path.Combine(destinationFolder, pathTail);
+
+                    if (PathEx.Compare(absolutePath, destination))
+                        continue;
+
+                    bool doCopy = !ProjectLibrary.Exists(absolutePath);
+
+                    string uniqueDestination = LibraryUtility.GetUniquePath(destination);
+                    if (Directory.Exists(path))
+                    {
+                        if (doCopy)
+                            DirectoryEx.Copy(absolutePath, uniqueDestination);
+                        else
+                            DirectoryEx.Move(absolutePath, uniqueDestination);
+                    }
+                    else if (File.Exists(path))
+                    {
+                        if (doCopy)
+                            FileEx.Copy(absolutePath, uniqueDestination);
+                        else
+                            ProjectLibrary.Move(absolutePath, uniqueDestination);
+                    }
+
+                    string relativeDestination = uniqueDestination.Substring(resourceDir.Length, uniqueDestination.Length - resourceDir.Length);
+                    addedResources.Add(relativeDestination);
+                    ProjectLibrary.Refresh();
+                }
+
+                SetSelection(addedResources);
+            }
+        }
+
+        /// <summary>
+        /// Triggered when a scene object drop operation finishes over the content area.
+        /// </summary>
+        /// <param name="windowPos">Coordinates of the pointer relative to the window where the drop operation finished
+        ///                         .</param>
+        /// <param name="objects">Dropped scene objects.</param>
+        private void OnSceneObjectDragDropped(Vector2I windowPos, SceneObject[] objects)
+        {
+            ClearHoverHighlight();
+            autoScrollAmount = 0;
+
+            if (EndDragSelection())
+                return;
+
+            string destinationFolder = CurrentFolder;
+
+            LibraryGUIEntry underCursorElement = FindElementAt(windowPos);
+            if (underCursorElement != null)
+            {
+                LibraryEntry entry = ProjectLibrary.GetEntry(underCursorElement.path);
+                if (entry != null && entry.Type == LibraryEntryType.Directory)
+                    destinationFolder = entry.Path;
+            }
+
+            if (objects != null)
+            {
+                List<string> addedResources = new List<string>();
+                foreach (var so in objects)
+                {
+                    if (so == null)
+                        continue;
+
+                    Prefab newPrefab = new Prefab(so);
+
+                    string destination = LibraryUtility.GetUniquePath(Path.Combine(destinationFolder, so.Name + ".prefab"));
+                    addedResources.Add(destination);
+
+                    ProjectLibrary.Create(newPrefab, destination);
+                    ProjectLibrary.Refresh();
+                }
+
+                SetSelection(addedResources);
+            }
+        }
+
+        /// <summary>
+        /// Triggered when a drag operation that originated from this window ends.
+        /// </summary>
+        /// <param name="windowPos">Coordinates of the pointer where the drag ended relative to the window </param>
+        private void OnDragEnd(Vector2I windowPos)
+        {
+            EndDragSelection();
+            autoScrollAmount = 0;
+        }
+
+        /// <summary>
+        /// Triggered when the global selection changes.
+        /// </summary>
+        /// <param name="sceneObjects">A set of newly selected scene objects.</param>
+        /// <param name="resourcePaths">A set of paths for newly selected resources.</param>
+        private void OnSelectionChanged(SceneObject[] sceneObjects, string[] resourcePaths)
+        {
+            if (sceneObjects.Length > 0)
+                DeselectAll(true);
+            else
+                SetSelection(new List<string>(resourcePaths), true);
+        }
+
+        /// <summary>
+        /// Triggered when a ping operation was triggered externally.
+        /// </summary>
+        /// <param name="path">Path to the resource to highlight.</param>
+        private void OnPing(string path)
+        {
+            Ping(path);
+        }
+
+        /// <summary>
+        /// Triggered when a folder on the directory bar was selected.
+        /// </summary>
+        /// <param name="path">Project library path to the folder to enter.</param>
+        private void OnFolderButtonClicked(string path)
+        {
+            EnterDirectory(path);
+        }
+
+        /// <summary>
+        /// Triggered when the user clicks on empty space between elements.
+        /// </summary>
+        private void OnCatchAllClicked()
+        {
+            DeselectAll();
+        }
+
+        /// <summary>
+        /// Triggered when the user clicks on the home button on the directory bar, changing the active directory to
+        /// project library root.
+        /// </summary>
+        private void OnHomeClicked()
+        {
+            CurrentFolder = ProjectLibrary.Root.Path;
+            Refresh();
+        }
+
+        /// <summary>
+        /// Triggered when the user clicks on the up button on the directory bar, changing the active directory to the
+        /// parent directory, unless already at project library root.
+        /// </summary>
+        private void OnUpClicked()
+        {
+            string currentDir = CurrentFolder;
+            currentDir = currentDir.Trim(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
+
+            if (!string.IsNullOrEmpty(currentDir))
+            {
+                string parent = Path.GetDirectoryName(currentDir);
+
+                CurrentFolder = parent;
+                Refresh();
+            }
+        }
+
+        /// <summary>
+        /// Triggered when the user inputs new values into the search input box. Refreshes the contents so they display
+        /// elements matching the search text.
+        /// </summary>
+        /// <param name="newValue">Search box text.</param>
+        private void OnSearchChanged(string newValue)
+        {
+            searchQuery = newValue;
+            Refresh();
+        }
+
+        /// <summary>
+        /// Sorts the specified set of project library entries by type (folder or resource), followed by name.
+        /// </summary>
+        /// <param name="input">Set of project library entries to sort.</param>
+        private static void SortEntries(LibraryEntry[] input)
+        {
+            Array.Sort(input, (x, y) =>
+            {
+                if (x.Type == y.Type)
+                    return x.Name.CompareTo(y.Name);
+                else
+                    return x.Type == LibraryEntryType.File ? 1 : -1;
+            });
+        }
+    }
+}

+ 859 - 847
Source/MBansheeEditor/Scene/SceneWindow.cs

@@ -1,847 +1,859 @@
-//********************************** 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 BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Displays the scene view camera and various scene controls.
-    /// </summary>
-    internal sealed class SceneWindow : EditorWindow
-    {
-        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;
-        private const float DefaultPlacementDepth = 5.0f;
-        private static readonly Color ClearColor = new Color(83.0f/255.0f, 83.0f/255.0f, 83.0f/255.0f);
-        private const string ProfilerOverlayActiveKey = "_Internal_ProfilerOverlayActive";
-        private const int HandleAxesGUISize = 50;
-        private const int HandleAxesGUIPaddingX = 10;
-        private const int HandleAxesGUIPaddingY = 5;
-
-        private Camera camera;
-        private SceneCamera cameraController;
-        private RenderTexture2D renderTexture;
-        private GUILayoutY mainLayout;
-        private GUIPanel rtPanel;
-
-        private GUIRenderTexture renderTextureGUI;
-        private SceneGrid sceneGrid;
-        private SceneSelection sceneSelection;
-        private SceneGizmos sceneGizmos;
-        private SceneHandles sceneHandles;
-
-        private GUIToggle viewButton;
-        private GUIToggle moveButton;
-        private GUIToggle rotateButton;
-        private GUIToggle scaleButton;
-
-        private GUIToggle localCoordButton;
-        private GUIToggle worldCoordButton;
-
-        private GUIToggle pivotButton;
-        private GUIToggle centerButton;
-
-        private GUIToggle moveSnapButton;
-        private GUIFloatField moveSnapInput;
-
-        private GUIToggle rotateSnapButton;
-        private GUIFloatField rotateSnapInput;
-
-        private SceneAxesGUI sceneAxesGUI;
-
-        private int editorSettingsHash = int.MaxValue;
-
-        private VirtualButton duplicateKey;
-        private VirtualButton deleteKey;
-        private VirtualButton frameKey;
-
-        // Tool shortcuts
-        private VirtualButton viewToolKey;
-        private VirtualButton moveToolKey;
-        private VirtualButton rotateToolKey;
-        private VirtualButton scaleToolKey;
-
-        // Profiler overlay
-        private ProfilerOverlay activeProfilerOverlay;
-        private Camera profilerCamera;
-        private VirtualButton toggleProfilerOverlayKey;
-
-        // Drag & drop
-        private bool dragActive;
-        private SceneObject draggedSO;
-
-        /// <summary>
-        /// Returns the scene camera.
-        /// </summary>
-        public Camera Camera
-        {
-            get { return camera; }
-        }
-
-        /// <summary>
-        /// Determines scene camera's projection type.
-        /// </summary>
-        internal ProjectionType ProjectionType
-        {
-            get { return cameraController.ProjectionType; }
-            set { cameraController.ProjectionType = value; sceneAxesGUI.ProjectionType = value; }
-        }
-
-        /// <summary>
-        /// Constructs a new scene window.
-        /// </summary>
-        internal SceneWindow()
-        { }
-
-        /// <summary>
-        /// Opens a scene window if its not open already.
-        /// </summary>
-        [MenuItem("Windows/Scene", ButtonModifier.CtrlAlt, ButtonCode.S, 6000)]
-        private static void OpenSceneWindow()
-        {
-            OpenWindow<SceneWindow>();
-        }
-
-        /// <summary>
-        /// Focuses on the currently selected object.
-        /// </summary>
-        [MenuItem("Tools/Frame Selected", ButtonModifier.None, ButtonCode.F, 9275, true)]
-        private static void OpenSettingsWindow()
-        {
-            SceneWindow window = GetWindow<SceneWindow>();
-            if (window != null)
-                window.cameraController.FrameSelected();
-        }
-
-        /// <summary>
-        /// Switches the active tool to the view tool.
-        /// </summary>
-        [MenuItem("Tools/View", ButtonModifier.Ctrl, ButtonCode.Q, 9274, true)]
-        private static void SetViewTool()
-        {
-            SceneWindow window = GetWindow<SceneWindow>();
-            if (window != null)
-                window.OnSceneToolButtonClicked(SceneViewTool.View);
-        }
-
-        /// <summary>
-        /// Switches the active tool to the move tool.
-        /// </summary>
-        [MenuItem("Tools/Move", ButtonModifier.Ctrl, ButtonCode.W, 9273)]
-        private static void SetMoveTool()
-        {
-            SceneWindow window = GetWindow<SceneWindow>();
-            if (window != null)
-                window.OnSceneToolButtonClicked(SceneViewTool.Move);
-        }
-
-        /// <summary>
-        /// Switches the active tool to the rotate tool.
-        /// </summary>
-        [MenuItem("Tools/Rotate", ButtonModifier.Ctrl, ButtonCode.E, 9272)]
-        private static void SetRotateTool()
-        {
-            SceneWindow window = GetWindow<SceneWindow>();
-            if (window != null)
-                window.OnSceneToolButtonClicked(SceneViewTool.Rotate);
-        }
-
-        /// <summary>
-        /// Switches the active tool to the scale tool.
-        /// </summary>
-        [MenuItem("Tools/Scale", ButtonModifier.Ctrl, ButtonCode.R, 9271)]
-        private static void SetScaleTool()
-        {
-            SceneWindow window = GetWindow<SceneWindow>();
-            if (window != null)
-                window.OnSceneToolButtonClicked(SceneViewTool.Scale);
-        }
-
-        /// <inheritdoc/>
-        protected override LocString GetDisplayName()
-        {
-            return new LocEdString("Scene");
-        }
-
-        private void OnInitialize()
-        {
-            mainLayout = GUI.AddLayoutY();
-
-            GUIContent viewIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.View), 
-                new LocEdString("View"));
-            GUIContent moveIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Move), 
-                new LocEdString("Move"));
-            GUIContent rotateIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Rotate), 
-                new LocEdString("Rotate"));
-            GUIContent scaleIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Scale), 
-                new LocEdString("Scale"));
-
-            GUIContent localIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Local), 
-                new LocEdString("Local"));
-            GUIContent worldIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.World), 
-                new LocEdString("World"));
-
-            GUIContent pivotIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Pivot), 
-                new LocEdString("Pivot"));
-            GUIContent centerIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Center), 
-                new LocEdString("Center"));
-
-            GUIContent moveSnapIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.MoveSnap), 
-                new LocEdString("Move snap"));
-            GUIContent rotateSnapIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.RotateSnap), 
-                new LocEdString("Rotate snap"));
-
-            GUIToggleGroup handlesTG = new GUIToggleGroup();
-            viewButton = new GUIToggle(viewIcon, handlesTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
-            moveButton = new GUIToggle(moveIcon, handlesTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
-            rotateButton = new GUIToggle(rotateIcon, handlesTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
-            scaleButton = new GUIToggle(scaleIcon, handlesTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
-
-            GUIToggleGroup coordModeTG = new GUIToggleGroup();
-            localCoordButton = new GUIToggle(localIcon, coordModeTG, EditorStyles.Button, GUIOption.FlexibleWidth(75));
-            worldCoordButton = new GUIToggle(worldIcon, coordModeTG, EditorStyles.Button, GUIOption.FlexibleWidth(75));
-
-            GUIToggleGroup pivotModeTG = new GUIToggleGroup();
-            pivotButton = new GUIToggle(pivotIcon, pivotModeTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
-            centerButton = new GUIToggle(centerIcon, pivotModeTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
-
-            moveSnapButton = new GUIToggle(moveSnapIcon, EditorStyles.Button, GUIOption.FlexibleWidth(35));
-            moveSnapInput = new GUIFloatField("", GUIOption.FlexibleWidth(35));
-
-            rotateSnapButton = new GUIToggle(rotateSnapIcon, EditorStyles.Button, GUIOption.FlexibleWidth(35));
-            rotateSnapInput = new GUIFloatField("", GUIOption.FlexibleWidth(35));
-
-            viewButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.View);
-            moveButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.Move);
-            rotateButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.Rotate);
-            scaleButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.Scale);
-
-            localCoordButton.OnClick += () => OnCoordinateModeButtonClicked(HandleCoordinateMode.Local);
-            worldCoordButton.OnClick += () => OnCoordinateModeButtonClicked(HandleCoordinateMode.World);
-
-            pivotButton.OnClick += () => OnPivotModeButtonClicked(HandlePivotMode.Pivot);
-            centerButton.OnClick += () => OnPivotModeButtonClicked(HandlePivotMode.Center);
-
-            moveSnapButton.OnToggled += (bool active) => OnMoveSnapToggled(active);
-            moveSnapInput.OnChanged += (float value) => OnMoveSnapValueChanged(value);
-
-            rotateSnapButton.OnToggled += (bool active) => OnRotateSnapToggled(active);
-            rotateSnapInput.OnChanged += (float value) => OnRotateSnapValueChanged(value);
-
-            GUILayout handlesLayout = mainLayout.AddLayoutX();
-            handlesLayout.AddElement(viewButton);
-            handlesLayout.AddElement(moveButton);
-            handlesLayout.AddElement(rotateButton);
-            handlesLayout.AddElement(scaleButton);
-            handlesLayout.AddSpace(10);
-            handlesLayout.AddElement(localCoordButton);
-            handlesLayout.AddElement(worldCoordButton);
-            handlesLayout.AddSpace(10);
-            handlesLayout.AddElement(pivotButton);
-            handlesLayout.AddElement(centerButton);
-            handlesLayout.AddFlexibleSpace();
-            handlesLayout.AddElement(moveSnapButton);
-            handlesLayout.AddElement(moveSnapInput);
-            handlesLayout.AddSpace(10);
-            handlesLayout.AddElement(rotateSnapButton);
-            handlesLayout.AddElement(rotateSnapInput);
-
-            GUIPanel mainPanel = mainLayout.AddPanel();
-            rtPanel = mainPanel.AddPanel();
-
-            GUIPanel sceneAxesPanel = mainPanel.AddPanel(-1);
-            sceneAxesGUI = new SceneAxesGUI(this, sceneAxesPanel, HandleAxesGUISize, HandleAxesGUISize, ProjectionType.Perspective);
-
-            toggleProfilerOverlayKey = new VirtualButton(ToggleProfilerOverlayBinding);
-            viewToolKey = new VirtualButton(ViewToolBinding);
-            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);
-            UpdateProfilerOverlay();
-        }
-
-        private void OnDestroy()
-        {
-            if (camera != null)
-            {
-                camera.SceneObject.Destroy();
-                camera = null;
-            }
-
-            sceneAxesGUI.Destroy();
-            sceneAxesGUI = null;
-        }
-
-        /// <summary>
-        /// Orients the camera so it looks along the provided axis.
-        /// </summary>
-        /// <param name="axis">Axis to look along.</param>
-        internal void LookAlong(Vector3 axis)
-        {
-            axis.Normalize();
-
-            cameraController.LookAlong(axis);
-            UpdateGridMode();
-        }
-
-        private void UpdateGridMode()
-        {
-            Vector3 forward = camera.SceneObject.Forward;
-
-            if (camera.ProjectionType == ProjectionType.Perspective)
-                sceneGrid.SetMode(GridMode.Perspective);
-            else
-            {
-                float dotX = Vector3.Dot(forward, Vector3.XAxis);
-                if (dotX >= 0.95f)
-                    sceneGrid.SetMode(GridMode.OrthoX);
-                else if (dotX <= -0.95f)
-                    sceneGrid.SetMode(GridMode.OrthoNegX);
-                else
-                {
-                    float dotY = Vector3.Dot(forward, Vector3.YAxis);
-                    if (dotY >= 0.95f)
-                        sceneGrid.SetMode(GridMode.OrthoY);
-                    else if (dotY <= -0.95f)
-                        sceneGrid.SetMode(GridMode.OrthoNegY);
-                    else
-                    {
-                        float dotZ = Vector3.Dot(forward, Vector3.ZAxis);
-                        if (dotZ >= 0.95f)
-                            sceneGrid.SetMode(GridMode.OrthoZ);
-                        else if (dotZ <= -0.95f)
-                            sceneGrid.SetMode(GridMode.OrthoNegZ);
-                        else
-                            sceneGrid.SetMode(GridMode.Perspective);
-                    }
-                }
-            }
-        }
-        
-        /// <summary>
-        /// Converts screen coordinates into coordinates relative to the scene view render texture.
-        /// </summary>
-        /// <param name="screenPos">Coordinates relative to the screen.</param>
-        /// <param name="scenePos">Output coordinates relative to the scene view texture.</param>
-        /// <returns>True if the coordinates are within the scene view texture, false otherwise.</returns>
-        private bool ScreenToScenePos(Vector2I screenPos, out Vector2I scenePos)
-        {
-            scenePos = screenPos;
-            Vector2I windowPos = ScreenToWindowPos(screenPos);
-
-            Rect2I bounds = GUILayoutUtility.CalculateBounds(renderTextureGUI, GUI);
-            if (bounds.Contains(windowPos))
-            {
-                scenePos.x = windowPos.x - bounds.x;
-                scenePos.y = windowPos.y - bounds.y;
-
-                return true;
-            }
-
-            return false;
-        }
-
-        private void OnEditorUpdate()
-        {
-            if (HasFocus)
-            {
-                if (!Input.IsPointerButtonHeld(PointerButton.Right))
-                {
-                    if (VirtualInput.IsButtonDown(toggleProfilerOverlayKey))
-                        EditorSettings.SetBool(ProfilerOverlayActiveKey, !EditorSettings.GetBool(ProfilerOverlayActiveKey));
-
-                    if (VirtualInput.IsButtonDown(viewToolKey))
-                        EditorApplication.ActiveSceneTool = SceneViewTool.View;
-
-                    if (VirtualInput.IsButtonDown(moveToolKey))
-                        EditorApplication.ActiveSceneTool = SceneViewTool.Move;
-
-                    if (VirtualInput.IsButtonDown(rotateToolKey))
-                        EditorApplication.ActiveSceneTool = SceneViewTool.Rotate;
-
-                    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();
-                        }
-                    }
-                }
-            }
-
-            // Refresh GUI buttons if needed (in case someones changes the values from script)
-            if (editorSettingsHash != EditorSettings.Hash)
-            {
-                UpdateButtonStates();
-                UpdateProfilerOverlay();
-                editorSettingsHash = EditorSettings.Hash;
-            }
-
-            // Update scene view handles and selection
-            sceneGizmos.Draw();
-            sceneGrid.Draw();
-
-            bool handleActive = false;
-            if (Input.IsPointerButtonUp(PointerButton.Left))
-            {
-                if (sceneHandles.IsActive())
-                {
-                    sceneHandles.ClearSelection();
-                    handleActive = true;
-                }
-
-                if (sceneAxesGUI.IsActive())
-                {
-                    sceneAxesGUI.ClearSelection();
-                    handleActive = true;
-                }
-            }
-
-            Vector2I scenePos;
-            bool inBounds = ScreenToScenePos(Input.PointerPosition, out scenePos);
-
-            bool draggedOver = DragDrop.DragInProgress || DragDrop.DropInProgress;
-            draggedOver &= IsPointerHovering && inBounds && DragDrop.Type == DragDropType.Resource;
-
-            if (draggedOver)
-            {
-                if (DragDrop.DropInProgress)
-                {
-                    dragActive = false;
-
-                    if (draggedSO != null)
-                    {
-                        Selection.SceneObject = draggedSO;
-                        EditorApplication.SetSceneDirty();
-                    }
-
-                    draggedSO = null;
-                }
-                else
-                {
-                    if (!dragActive)
-                    {
-                        dragActive = true;
-
-                        ResourceDragDropData dragData = (ResourceDragDropData)DragDrop.Data;
-
-                        string[] draggedPaths = dragData.Paths;
-
-                        for (int i = 0; i < draggedPaths.Length; i++)
-                        {
-                            ResourceMeta meta = ProjectLibrary.GetMeta(draggedPaths[i]);
-                            if (meta != null)
-                            {
-                                if (meta.ResType == ResourceType.Mesh)
-                                {
-                                    if (!string.IsNullOrEmpty(draggedPaths[i]))
-                                    {
-                                        string meshName = Path.GetFileNameWithoutExtension(draggedPaths[i]);
-                                        draggedSO = UndoRedo.CreateSO(meshName, "Created a new Renderable \"" + meshName + "\"");
-                                        Mesh mesh = ProjectLibrary.Load<Mesh>(draggedPaths[i]);
-
-                                        Renderable renderable = draggedSO.AddComponent<Renderable>();
-                                        renderable.Mesh = mesh;
-                                    }
-
-                                    break;
-                                }
-                                else if (meta.ResType == ResourceType.Prefab)
-                                {
-                                    if (!string.IsNullOrEmpty(draggedPaths[i]))
-                                    {
-                                        Prefab prefab = ProjectLibrary.Load<Prefab>(draggedPaths[i]);
-                                        draggedSO = UndoRedo.Instantiate(prefab, "Instantiating " + prefab.Name);
-                                    }
-
-                                    break;
-                                }
-                            }
-                        }
-                    }
-
-                    if (draggedSO != null)
-                    {
-                        Ray worldRay = camera.ScreenToWorldRay(scenePos);
-                        draggedSO.Position = worldRay*DefaultPlacementDepth;
-                    }
-                }
-
-                return;
-            }
-            else
-            {
-                if (dragActive)
-                {
-                    dragActive = false;
-
-                    if (draggedSO != null)
-                    {
-                        draggedSO.Destroy();
-                        draggedSO = null;
-                    }
-                }
-            }
-
-            if (HasFocus)
-            {
-                cameraController.EnableInput(true);
-
-                if (inBounds)
-                {
-                    if (Input.IsPointerButtonDown(PointerButton.Left))
-                    {
-                        Rect2I sceneAxesGUIBounds = new Rect2I(Width - HandleAxesGUISize - HandleAxesGUIPaddingX, 
-                            HandleAxesGUIPaddingY, HandleAxesGUISize, HandleAxesGUISize);
-
-                        if (sceneAxesGUIBounds.Contains(scenePos))
-                            sceneAxesGUI.TrySelect(scenePos);
-                        else
-                            sceneHandles.TrySelect(scenePos);
-                    }
-                    else if (Input.IsPointerButtonUp(PointerButton.Left))
-                    {
-                        if (!handleActive)
-                        {
-                            bool ctrlHeld = Input.IsButtonHeld(ButtonCode.LeftControl) ||
-                                            Input.IsButtonHeld(ButtonCode.RightControl);
-
-                            sceneSelection.PickObject(scenePos, ctrlHeld);
-                        }
-                    }
-                }
-            }
-            else
-                cameraController.EnableInput(false);
-
-            SceneHandles.BeginInput();
-            sceneHandles.UpdateInput(scenePos, Input.PointerDelta);
-            sceneHandles.Draw();
-
-            sceneAxesGUI.UpdateInput(scenePos);
-            sceneAxesGUI.Draw();
-            SceneHandles.EndInput();
-
-            sceneSelection.Draw();
-
-            UpdateGridMode();
-
-            if (VirtualInput.IsButtonDown(frameKey))
-                cameraController.FrameSelected();
-        }
-
-        /// <inheritdoc/>
-        protected override void WindowResized(int width, int height)
-        {
-            UpdateRenderTexture(width, height - HeaderHeight);
-
-            base.WindowResized(width, height);
-        }
-
-        /// <inheritdoc/>
-        protected override void FocusChanged(bool inFocus)
-        {
-            if (!inFocus)
-            {
-                sceneHandles.ClearSelection();
-            }
-        }
-
-        /// <summary>
-        /// Triggered when one of the scene tool buttons is clicked, changing the active scene handle.
-        /// </summary>
-        /// <param name="tool">Clicked scene tool to activate.</param>
-        private void OnSceneToolButtonClicked(SceneViewTool tool)
-        {
-            EditorApplication.ActiveSceneTool = tool;
-            editorSettingsHash = EditorSettings.Hash;
-        }
-
-        /// <summary>
-        /// Triggered when one of the coordinate mode buttons is clicked, changing the active coordinate mode.
-        /// </summary>
-        /// <param name="mode">Clicked coordinate mode to activate.</param>
-        private void OnCoordinateModeButtonClicked(HandleCoordinateMode mode)
-        {
-            EditorApplication.ActiveCoordinateMode = mode;
-            editorSettingsHash = EditorSettings.Hash;
-        }
-
-        /// <summary>
-        /// Triggered when one of the pivot buttons is clicked, changing the active pivot mode.
-        /// </summary>
-        /// <param name="mode">Clicked pivot mode to activate.</param>
-        private void OnPivotModeButtonClicked(HandlePivotMode mode)
-        {
-            EditorApplication.ActivePivotMode = mode;
-            editorSettingsHash = EditorSettings.Hash;
-        }
-
-        /// <summary>
-        /// Triggered when the move snap button is toggled.
-        /// </summary>
-        /// <param name="active">Determins should be move snap be activated or deactivated.</param>
-        private void OnMoveSnapToggled(bool active)
-        {
-            Handles.MoveHandleSnapActive = active;
-            editorSettingsHash = EditorSettings.Hash;
-        }
-
-        /// <summary>
-        /// Triggered when the move snap increment value changes.
-        /// </summary>
-        /// <param name="value">Value that determines in what increments to perform move snapping.</param>
-        private void OnMoveSnapValueChanged(float value)
-        {
-            Handles.MoveSnapAmount = MathEx.Clamp(value, 0.01f, 1000.0f);
-            editorSettingsHash = EditorSettings.Hash;
-        }
-
-        /// <summary>
-        /// Triggered when the rotate snap button is toggled.
-        /// </summary>
-        /// <param name="active">Determins should be rotate snap be activated or deactivated.</param>
-        private void OnRotateSnapToggled(bool active)
-        {
-            Handles.RotateHandleSnapActive = active;
-            editorSettingsHash = EditorSettings.Hash;
-        }
-
-        /// <summary>
-        /// Triggered when the rotate snap increment value changes.
-        /// </summary>
-        /// <param name="value">Value that determines in what increments to perform rotate snapping.</param>
-        private void OnRotateSnapValueChanged(float value)
-        {
-            Handles.RotateSnapAmount = (Degree)MathEx.Clamp(value, 0.01f, 360.0f);
-            editorSettingsHash = EditorSettings.Hash;
-        }
-
-        /// <summary>
-        /// Updates toggle button states according to current editor options. This is useful if tools, coordinate mode,
-        /// pivot or other scene view options have been modified externally.
-        /// </summary>
-        private void UpdateButtonStates()
-        {
-            switch (EditorApplication.ActiveSceneTool)
-            {
-                case SceneViewTool.View:
-                    viewButton.Value = true;
-                    break;
-                case SceneViewTool.Move:
-                    moveButton.Value = true;
-                    break;
-                case SceneViewTool.Rotate:
-                    rotateButton.Value = true;
-                    break;
-                case SceneViewTool.Scale:
-                    scaleButton.Value = true;
-                    break;
-            }
-
-            switch (EditorApplication.ActiveCoordinateMode)
-            {
-                case HandleCoordinateMode.Local:
-                    localCoordButton.Value = true;
-                    break;
-                case HandleCoordinateMode.World:
-                    worldCoordButton.Value = true;
-                    break;
-            }
-
-            switch (EditorApplication.ActivePivotMode)
-            {
-                case HandlePivotMode.Center:
-                    centerButton.Value = true;
-                    break;
-                case HandlePivotMode.Pivot:
-                    pivotButton.Value = true;
-                    break;
-            }
-
-            if (Handles.MoveHandleSnapActive)
-                moveSnapButton.Value = true;
-            else
-                moveSnapButton.Value = false;
-
-            moveSnapInput.Value = Handles.MoveSnapAmount;
-
-            if (Handles.RotateHandleSnapActive)
-                rotateSnapButton.Value = true;
-            else
-                rotateSnapButton.Value = false;
-
-            moveSnapInput.Value = Handles.RotateSnapAmount.Degrees;
-        }
-
-        /// <summary>
-        /// Activates or deactivates the profiler overlay according to current editor settings.
-        /// </summary>
-        private void UpdateProfilerOverlay()
-        {
-            if (EditorSettings.GetBool(ProfilerOverlayActiveKey))
-            {
-                if (activeProfilerOverlay == null)
-                {
-                    SceneObject profilerSO = new SceneObject("EditorProfilerOverlay");
-                    profilerCamera = profilerSO.AddComponent<Camera>();
-                    profilerCamera.Target = renderTexture;
-                    profilerCamera.ClearFlags = ClearFlags.None;
-                    profilerCamera.Priority = 1;
-                    profilerCamera.Layers = 0;
-
-                    activeProfilerOverlay = profilerSO.AddComponent<ProfilerOverlay>();
-                }
-            }
-            else
-            {
-                if (activeProfilerOverlay != null)
-                {
-                    activeProfilerOverlay.SceneObject.Destroy();
-                    activeProfilerOverlay = null;
-                    profilerCamera = null;
-                }
-            }
-        }
-
-        /// <summary>
-        /// Creates the scene camera and updates the render texture. Should be called at least once before using the
-        /// scene view. Should be called whenever the window is resized.
-        /// </summary>
-        /// <param name="width">Width of the scene render target, in pixels.</param>
-        /// <param name="height">Height of the scene render target, in pixels.</param>
-        private void UpdateRenderTexture(int width, int height)
-	    {
-            width = MathEx.Max(20, width);
-            height = MathEx.Max(20, height);
-
-            renderTexture = new RenderTexture2D(PixelFormat.R8G8B8A8, width, height);
-            renderTexture.Priority = 1;
-
-		    if (camera == null)
-		    {
-                SceneObject sceneCameraSO = new SceneObject("SceneCamera", true);
-                camera = sceneCameraSO.AddComponent<Camera>();
-                camera.Target = renderTexture;
-                camera.ViewportRect = new Rect2(0.0f, 0.0f, 1.0f, 1.0f);
-
-                sceneCameraSO.Position = new Vector3(0, 0.5f, 1);
-                sceneCameraSO.LookAt(new Vector3(0, 0.5f, 0));
-
-                camera.Priority = 2;
-                camera.NearClipPlane = 0.05f;
-                camera.FarClipPlane = 2500.0f;
-                camera.ClearColor = ClearColor;
-                camera.Layers = UInt64.MaxValue & ~SceneAxesHandle.LAYER; // Don't draw scene axes in this camera
-
-                cameraController = sceneCameraSO.AddComponent<SceneCamera>();
-
-                renderTextureGUI = new GUIRenderTexture(renderTexture);
-                rtPanel.AddElement(renderTextureGUI);
-
-		        sceneGrid = new SceneGrid(camera);
-		        sceneSelection = new SceneSelection(camera);
-		        sceneGizmos = new SceneGizmos(camera);
-		        sceneHandles = new SceneHandles(this, camera);
-		    }
-		    else
-		    {
-		        camera.Target = renderTexture;
-		        renderTextureGUI.RenderTexture = renderTexture;
-		    }
-
-            Rect2I rtBounds = new Rect2I(0, 0, width, height);
-            renderTextureGUI.Bounds = rtBounds;
-
-            sceneAxesGUI.SetPosition(width - HandleAxesGUISize - HandleAxesGUIPaddingX, HandleAxesGUIPaddingY);
-
-            // TODO - Consider only doing the resize once user stops resizing the widget in order to reduce constant
-            // render target destroy/create cycle for every single pixel.
-
-            camera.AspectRatio = width / (float)height;
-
-            if (profilerCamera != null)
-                profilerCamera.Target = renderTexture;
-	    }
-
-        /// <summary>
-        /// Parses an array of scene objects and removes elements that are children of elements that are also in the array.
-        /// </summary>
-        /// <param name="objects">Array containing duplicate objects as input, and array without duplicate objects as
-        ///                       output.</param>
-        private void CleanDuplicates(ref SceneObject[] objects)
-	    {
-		    List<SceneObject> cleanList = new List<SceneObject>();
-		    for (int i = 0; i < objects.Length; i++)
-		    {
-			    bool foundParent = false;
-                for (int j = 0; j < objects.Length; j++)
-                {
-                    SceneObject elem = objects[i];
-
-                    while (elem != null && elem != objects[j])
-                        elem = elem.Parent;
-
-                    bool isChildOf = elem == objects[j];
-
-				    if (i != j && isChildOf)
-				    {
-					    foundParent = true;
-					    break;
-				    }
-			    }
-
-			    if (!foundParent)
-				    cleanList.Add(objects[i]);
-		    }
-
-		    objects = cleanList.ToArray();
-	    }
-    }
-}
+//********************************** 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 BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Displays the scene view camera and various scene controls.
+    /// </summary>
+    internal sealed class SceneWindow : EditorWindow
+    {
+        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;
+        private const float DefaultPlacementDepth = 5.0f;
+        private static readonly Color ClearColor = new Color(83.0f/255.0f, 83.0f/255.0f, 83.0f/255.0f);
+        private const string ProfilerOverlayActiveKey = "_Internal_ProfilerOverlayActive";
+        private const int HandleAxesGUISize = 50;
+        private const int HandleAxesGUIPaddingX = 10;
+        private const int HandleAxesGUIPaddingY = 5;
+
+        private Camera camera;
+        private SceneCamera cameraController;
+        private RenderTexture2D renderTexture;
+        private GUILayoutY mainLayout;
+        private GUIPanel rtPanel;
+        private GUIButton focusCatcher;
+
+        private GUIRenderTexture renderTextureGUI;
+        private SceneGrid sceneGrid;
+        private SceneSelection sceneSelection;
+        private SceneGizmos sceneGizmos;
+        private SceneHandles sceneHandles;
+
+        private GUIToggle viewButton;
+        private GUIToggle moveButton;
+        private GUIToggle rotateButton;
+        private GUIToggle scaleButton;
+
+        private GUIToggle localCoordButton;
+        private GUIToggle worldCoordButton;
+
+        private GUIToggle pivotButton;
+        private GUIToggle centerButton;
+
+        private GUIToggle moveSnapButton;
+        private GUIFloatField moveSnapInput;
+
+        private GUIToggle rotateSnapButton;
+        private GUIFloatField rotateSnapInput;
+
+        private SceneAxesGUI sceneAxesGUI;
+
+        private bool hasContentFocus = false;
+        private bool HasContentFocus { get { return HasFocus && hasContentFocus; } }
+
+        private int editorSettingsHash = int.MaxValue;
+
+        private VirtualButton duplicateKey;
+        private VirtualButton deleteKey;
+        private VirtualButton frameKey;
+
+        // Tool shortcuts
+        private VirtualButton viewToolKey;
+        private VirtualButton moveToolKey;
+        private VirtualButton rotateToolKey;
+        private VirtualButton scaleToolKey;
+
+        // Profiler overlay
+        private ProfilerOverlay activeProfilerOverlay;
+        private Camera profilerCamera;
+        private VirtualButton toggleProfilerOverlayKey;
+
+        // Drag & drop
+        private bool dragActive;
+        private SceneObject draggedSO;
+
+        /// <summary>
+        /// Returns the scene camera.
+        /// </summary>
+        public Camera Camera
+        {
+            get { return camera; }
+        }
+
+        /// <summary>
+        /// Determines scene camera's projection type.
+        /// </summary>
+        internal ProjectionType ProjectionType
+        {
+            get { return cameraController.ProjectionType; }
+            set { cameraController.ProjectionType = value; sceneAxesGUI.ProjectionType = value; }
+        }
+
+        /// <summary>
+        /// Constructs a new scene window.
+        /// </summary>
+        internal SceneWindow()
+        { }
+
+        /// <summary>
+        /// Opens a scene window if its not open already.
+        /// </summary>
+        [MenuItem("Windows/Scene", ButtonModifier.CtrlAlt, ButtonCode.S, 6000)]
+        private static void OpenSceneWindow()
+        {
+            OpenWindow<SceneWindow>();
+        }
+
+        /// <summary>
+        /// Focuses on the currently selected object.
+        /// </summary>
+        [MenuItem("Tools/Frame Selected", ButtonModifier.None, ButtonCode.F, 9275, true)]
+        private static void OpenSettingsWindow()
+        {
+            SceneWindow window = GetWindow<SceneWindow>();
+            if (window != null)
+                window.cameraController.FrameSelected();
+        }
+
+        /// <summary>
+        /// Switches the active tool to the view tool.
+        /// </summary>
+        [MenuItem("Tools/View", ButtonModifier.Ctrl, ButtonCode.Q, 9274, true)]
+        private static void SetViewTool()
+        {
+            SceneWindow window = GetWindow<SceneWindow>();
+            if (window != null)
+                window.OnSceneToolButtonClicked(SceneViewTool.View);
+        }
+
+        /// <summary>
+        /// Switches the active tool to the move tool.
+        /// </summary>
+        [MenuItem("Tools/Move", ButtonModifier.Ctrl, ButtonCode.W, 9273)]
+        private static void SetMoveTool()
+        {
+            SceneWindow window = GetWindow<SceneWindow>();
+            if (window != null)
+                window.OnSceneToolButtonClicked(SceneViewTool.Move);
+        }
+
+        /// <summary>
+        /// Switches the active tool to the rotate tool.
+        /// </summary>
+        [MenuItem("Tools/Rotate", ButtonModifier.Ctrl, ButtonCode.E, 9272)]
+        private static void SetRotateTool()
+        {
+            SceneWindow window = GetWindow<SceneWindow>();
+            if (window != null)
+                window.OnSceneToolButtonClicked(SceneViewTool.Rotate);
+        }
+
+        /// <summary>
+        /// Switches the active tool to the scale tool.
+        /// </summary>
+        [MenuItem("Tools/Scale", ButtonModifier.Ctrl, ButtonCode.R, 9271)]
+        private static void SetScaleTool()
+        {
+            SceneWindow window = GetWindow<SceneWindow>();
+            if (window != null)
+                window.OnSceneToolButtonClicked(SceneViewTool.Scale);
+        }
+
+        /// <inheritdoc/>
+        protected override LocString GetDisplayName()
+        {
+            return new LocEdString("Scene");
+        }
+
+        private void OnInitialize()
+        {
+            mainLayout = GUI.AddLayoutY();
+
+            GUIContent viewIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.View), 
+                new LocEdString("View"));
+            GUIContent moveIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Move), 
+                new LocEdString("Move"));
+            GUIContent rotateIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Rotate), 
+                new LocEdString("Rotate"));
+            GUIContent scaleIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Scale), 
+                new LocEdString("Scale"));
+
+            GUIContent localIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Local), 
+                new LocEdString("Local"));
+            GUIContent worldIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.World), 
+                new LocEdString("World"));
+
+            GUIContent pivotIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Pivot), 
+                new LocEdString("Pivot"));
+            GUIContent centerIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Center), 
+                new LocEdString("Center"));
+
+            GUIContent moveSnapIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.MoveSnap), 
+                new LocEdString("Move snap"));
+            GUIContent rotateSnapIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.RotateSnap), 
+                new LocEdString("Rotate snap"));
+
+            GUIToggleGroup handlesTG = new GUIToggleGroup();
+            viewButton = new GUIToggle(viewIcon, handlesTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
+            moveButton = new GUIToggle(moveIcon, handlesTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
+            rotateButton = new GUIToggle(rotateIcon, handlesTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
+            scaleButton = new GUIToggle(scaleIcon, handlesTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
+
+            GUIToggleGroup coordModeTG = new GUIToggleGroup();
+            localCoordButton = new GUIToggle(localIcon, coordModeTG, EditorStyles.Button, GUIOption.FlexibleWidth(75));
+            worldCoordButton = new GUIToggle(worldIcon, coordModeTG, EditorStyles.Button, GUIOption.FlexibleWidth(75));
+
+            GUIToggleGroup pivotModeTG = new GUIToggleGroup();
+            pivotButton = new GUIToggle(pivotIcon, pivotModeTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
+            centerButton = new GUIToggle(centerIcon, pivotModeTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
+
+            moveSnapButton = new GUIToggle(moveSnapIcon, EditorStyles.Button, GUIOption.FlexibleWidth(35));
+            moveSnapInput = new GUIFloatField("", GUIOption.FlexibleWidth(35));
+
+            rotateSnapButton = new GUIToggle(rotateSnapIcon, EditorStyles.Button, GUIOption.FlexibleWidth(35));
+            rotateSnapInput = new GUIFloatField("", GUIOption.FlexibleWidth(35));
+
+            viewButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.View);
+            moveButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.Move);
+            rotateButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.Rotate);
+            scaleButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.Scale);
+
+            localCoordButton.OnClick += () => OnCoordinateModeButtonClicked(HandleCoordinateMode.Local);
+            worldCoordButton.OnClick += () => OnCoordinateModeButtonClicked(HandleCoordinateMode.World);
+
+            pivotButton.OnClick += () => OnPivotModeButtonClicked(HandlePivotMode.Pivot);
+            centerButton.OnClick += () => OnPivotModeButtonClicked(HandlePivotMode.Center);
+
+            moveSnapButton.OnToggled += (bool active) => OnMoveSnapToggled(active);
+            moveSnapInput.OnChanged += (float value) => OnMoveSnapValueChanged(value);
+
+            rotateSnapButton.OnToggled += (bool active) => OnRotateSnapToggled(active);
+            rotateSnapInput.OnChanged += (float value) => OnRotateSnapValueChanged(value);
+
+            GUILayout handlesLayout = mainLayout.AddLayoutX();
+            handlesLayout.AddElement(viewButton);
+            handlesLayout.AddElement(moveButton);
+            handlesLayout.AddElement(rotateButton);
+            handlesLayout.AddElement(scaleButton);
+            handlesLayout.AddSpace(10);
+            handlesLayout.AddElement(localCoordButton);
+            handlesLayout.AddElement(worldCoordButton);
+            handlesLayout.AddSpace(10);
+            handlesLayout.AddElement(pivotButton);
+            handlesLayout.AddElement(centerButton);
+            handlesLayout.AddFlexibleSpace();
+            handlesLayout.AddElement(moveSnapButton);
+            handlesLayout.AddElement(moveSnapInput);
+            handlesLayout.AddSpace(10);
+            handlesLayout.AddElement(rotateSnapButton);
+            handlesLayout.AddElement(rotateSnapInput);
+
+            GUIPanel mainPanel = mainLayout.AddPanel();
+            rtPanel = mainPanel.AddPanel();
+
+            GUIPanel sceneAxesPanel = mainPanel.AddPanel(-1);
+            sceneAxesGUI = new SceneAxesGUI(this, sceneAxesPanel, HandleAxesGUISize, HandleAxesGUISize, ProjectionType.Perspective);
+
+            focusCatcher = new GUIButton("", EditorStyles.Blank);
+            focusCatcher.OnFocusGained += () => hasContentFocus = true;
+            focusCatcher.OnFocusLost += () => hasContentFocus = false;
+
+            GUIPanel focusPanel = GUI.AddPanel(-2);
+            focusPanel.AddElement(focusCatcher);
+
+            toggleProfilerOverlayKey = new VirtualButton(ToggleProfilerOverlayBinding);
+            viewToolKey = new VirtualButton(ViewToolBinding);
+            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);
+            UpdateProfilerOverlay();
+        }
+
+        private void OnDestroy()
+        {
+            if (camera != null)
+            {
+                camera.SceneObject.Destroy();
+                camera = null;
+            }
+
+            sceneAxesGUI.Destroy();
+            sceneAxesGUI = null;
+        }
+
+        /// <summary>
+        /// Orients the camera so it looks along the provided axis.
+        /// </summary>
+        /// <param name="axis">Axis to look along.</param>
+        internal void LookAlong(Vector3 axis)
+        {
+            axis.Normalize();
+
+            cameraController.LookAlong(axis);
+            UpdateGridMode();
+        }
+
+        private void UpdateGridMode()
+        {
+            Vector3 forward = camera.SceneObject.Forward;
+
+            if (camera.ProjectionType == ProjectionType.Perspective)
+                sceneGrid.SetMode(GridMode.Perspective);
+            else
+            {
+                float dotX = Vector3.Dot(forward, Vector3.XAxis);
+                if (dotX >= 0.95f)
+                    sceneGrid.SetMode(GridMode.OrthoX);
+                else if (dotX <= -0.95f)
+                    sceneGrid.SetMode(GridMode.OrthoNegX);
+                else
+                {
+                    float dotY = Vector3.Dot(forward, Vector3.YAxis);
+                    if (dotY >= 0.95f)
+                        sceneGrid.SetMode(GridMode.OrthoY);
+                    else if (dotY <= -0.95f)
+                        sceneGrid.SetMode(GridMode.OrthoNegY);
+                    else
+                    {
+                        float dotZ = Vector3.Dot(forward, Vector3.ZAxis);
+                        if (dotZ >= 0.95f)
+                            sceneGrid.SetMode(GridMode.OrthoZ);
+                        else if (dotZ <= -0.95f)
+                            sceneGrid.SetMode(GridMode.OrthoNegZ);
+                        else
+                            sceneGrid.SetMode(GridMode.Perspective);
+                    }
+                }
+            }
+        }
+        
+        /// <summary>
+        /// Converts screen coordinates into coordinates relative to the scene view render texture.
+        /// </summary>
+        /// <param name="screenPos">Coordinates relative to the screen.</param>
+        /// <param name="scenePos">Output coordinates relative to the scene view texture.</param>
+        /// <returns>True if the coordinates are within the scene view texture, false otherwise.</returns>
+        private bool ScreenToScenePos(Vector2I screenPos, out Vector2I scenePos)
+        {
+            scenePos = screenPos;
+            Vector2I windowPos = ScreenToWindowPos(screenPos);
+
+            Rect2I bounds = GUILayoutUtility.CalculateBounds(renderTextureGUI, GUI);
+            if (bounds.Contains(windowPos))
+            {
+                scenePos.x = windowPos.x - bounds.x;
+                scenePos.y = windowPos.y - bounds.y;
+
+                return true;
+            }
+
+            return false;
+        }
+
+        private void OnEditorUpdate()
+        {
+            if (HasFocus)
+            {
+                if (!Input.IsPointerButtonHeld(PointerButton.Right))
+                {
+                    if (VirtualInput.IsButtonDown(toggleProfilerOverlayKey))
+                        EditorSettings.SetBool(ProfilerOverlayActiveKey, !EditorSettings.GetBool(ProfilerOverlayActiveKey));
+
+                    if (VirtualInput.IsButtonDown(viewToolKey))
+                        EditorApplication.ActiveSceneTool = SceneViewTool.View;
+
+                    if (VirtualInput.IsButtonDown(moveToolKey))
+                        EditorApplication.ActiveSceneTool = SceneViewTool.Move;
+
+                    if (VirtualInput.IsButtonDown(rotateToolKey))
+                        EditorApplication.ActiveSceneTool = SceneViewTool.Rotate;
+
+                    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();
+                        }
+                    }
+                }
+            }
+
+            // Refresh GUI buttons if needed (in case someones changes the values from script)
+            if (editorSettingsHash != EditorSettings.Hash)
+            {
+                UpdateButtonStates();
+                UpdateProfilerOverlay();
+                editorSettingsHash = EditorSettings.Hash;
+            }
+
+            // Update scene view handles and selection
+            sceneGizmos.Draw();
+            sceneGrid.Draw();
+
+            bool handleActive = false;
+            if (Input.IsPointerButtonUp(PointerButton.Left))
+            {
+                if (sceneHandles.IsActive())
+                {
+                    sceneHandles.ClearSelection();
+                    handleActive = true;
+                }
+
+                if (sceneAxesGUI.IsActive())
+                {
+                    sceneAxesGUI.ClearSelection();
+                    handleActive = true;
+                }
+            }
+
+            Vector2I scenePos;
+            bool inBounds = ScreenToScenePos(Input.PointerPosition, out scenePos);
+
+            bool draggedOver = DragDrop.DragInProgress || DragDrop.DropInProgress;
+            draggedOver &= IsPointerHovering && inBounds && DragDrop.Type == DragDropType.Resource;
+
+            if (draggedOver)
+            {
+                if (DragDrop.DropInProgress)
+                {
+                    dragActive = false;
+
+                    if (draggedSO != null)
+                    {
+                        Selection.SceneObject = draggedSO;
+                        EditorApplication.SetSceneDirty();
+                    }
+
+                    draggedSO = null;
+                }
+                else
+                {
+                    if (!dragActive)
+                    {
+                        dragActive = true;
+
+                        ResourceDragDropData dragData = (ResourceDragDropData)DragDrop.Data;
+
+                        string[] draggedPaths = dragData.Paths;
+
+                        for (int i = 0; i < draggedPaths.Length; i++)
+                        {
+                            ResourceMeta meta = ProjectLibrary.GetMeta(draggedPaths[i]);
+                            if (meta != null)
+                            {
+                                if (meta.ResType == ResourceType.Mesh)
+                                {
+                                    if (!string.IsNullOrEmpty(draggedPaths[i]))
+                                    {
+                                        string meshName = Path.GetFileNameWithoutExtension(draggedPaths[i]);
+                                        draggedSO = UndoRedo.CreateSO(meshName, "Created a new Renderable \"" + meshName + "\"");
+                                        Mesh mesh = ProjectLibrary.Load<Mesh>(draggedPaths[i]);
+
+                                        Renderable renderable = draggedSO.AddComponent<Renderable>();
+                                        renderable.Mesh = mesh;
+                                    }
+
+                                    break;
+                                }
+                                else if (meta.ResType == ResourceType.Prefab)
+                                {
+                                    if (!string.IsNullOrEmpty(draggedPaths[i]))
+                                    {
+                                        Prefab prefab = ProjectLibrary.Load<Prefab>(draggedPaths[i]);
+                                        draggedSO = UndoRedo.Instantiate(prefab, "Instantiating " + prefab.Name);
+                                    }
+
+                                    break;
+                                }
+                            }
+                        }
+                    }
+
+                    if (draggedSO != null)
+                    {
+                        Ray worldRay = camera.ScreenToWorldRay(scenePos);
+                        draggedSO.Position = worldRay*DefaultPlacementDepth;
+                    }
+                }
+
+                return;
+            }
+            else
+            {
+                if (dragActive)
+                {
+                    dragActive = false;
+
+                    if (draggedSO != null)
+                    {
+                        draggedSO.Destroy();
+                        draggedSO = null;
+                    }
+                }
+            }
+
+            if (HasContentFocus)
+            {
+                cameraController.EnableInput(true);
+
+                if (inBounds)
+                {
+                    if (Input.IsPointerButtonDown(PointerButton.Left))
+                    {
+                        Rect2I sceneAxesGUIBounds = new Rect2I(Width - HandleAxesGUISize - HandleAxesGUIPaddingX, 
+                            HandleAxesGUIPaddingY, HandleAxesGUISize, HandleAxesGUISize);
+
+                        if (sceneAxesGUIBounds.Contains(scenePos))
+                            sceneAxesGUI.TrySelect(scenePos);
+                        else
+                            sceneHandles.TrySelect(scenePos);
+                    }
+                    else if (Input.IsPointerButtonUp(PointerButton.Left))
+                    {
+                        if (!handleActive)
+                        {
+                            bool ctrlHeld = Input.IsButtonHeld(ButtonCode.LeftControl) ||
+                                            Input.IsButtonHeld(ButtonCode.RightControl);
+
+                            sceneSelection.PickObject(scenePos, ctrlHeld);
+                        }
+                    }
+                }
+            }
+            else
+                cameraController.EnableInput(false);
+
+            SceneHandles.BeginInput();
+            sceneHandles.UpdateInput(scenePos, Input.PointerDelta);
+            sceneHandles.Draw();
+
+            sceneAxesGUI.UpdateInput(scenePos);
+            sceneAxesGUI.Draw();
+            SceneHandles.EndInput();
+
+            sceneSelection.Draw();
+
+            UpdateGridMode();
+
+            if (VirtualInput.IsButtonDown(frameKey))
+                cameraController.FrameSelected();
+        }
+
+        /// <inheritdoc/>
+        protected override void WindowResized(int width, int height)
+        {
+            UpdateRenderTexture(width, height - HeaderHeight);
+
+            base.WindowResized(width, height);
+        }
+
+        /// <inheritdoc/>
+        protected override void FocusChanged(bool inFocus)
+        {
+            if (!inFocus)
+            {
+                sceneHandles.ClearSelection();
+            }
+        }
+
+        /// <summary>
+        /// Triggered when one of the scene tool buttons is clicked, changing the active scene handle.
+        /// </summary>
+        /// <param name="tool">Clicked scene tool to activate.</param>
+        private void OnSceneToolButtonClicked(SceneViewTool tool)
+        {
+            EditorApplication.ActiveSceneTool = tool;
+            editorSettingsHash = EditorSettings.Hash;
+        }
+
+        /// <summary>
+        /// Triggered when one of the coordinate mode buttons is clicked, changing the active coordinate mode.
+        /// </summary>
+        /// <param name="mode">Clicked coordinate mode to activate.</param>
+        private void OnCoordinateModeButtonClicked(HandleCoordinateMode mode)
+        {
+            EditorApplication.ActiveCoordinateMode = mode;
+            editorSettingsHash = EditorSettings.Hash;
+        }
+
+        /// <summary>
+        /// Triggered when one of the pivot buttons is clicked, changing the active pivot mode.
+        /// </summary>
+        /// <param name="mode">Clicked pivot mode to activate.</param>
+        private void OnPivotModeButtonClicked(HandlePivotMode mode)
+        {
+            EditorApplication.ActivePivotMode = mode;
+            editorSettingsHash = EditorSettings.Hash;
+        }
+
+        /// <summary>
+        /// Triggered when the move snap button is toggled.
+        /// </summary>
+        /// <param name="active">Determins should be move snap be activated or deactivated.</param>
+        private void OnMoveSnapToggled(bool active)
+        {
+            Handles.MoveHandleSnapActive = active;
+            editorSettingsHash = EditorSettings.Hash;
+        }
+
+        /// <summary>
+        /// Triggered when the move snap increment value changes.
+        /// </summary>
+        /// <param name="value">Value that determines in what increments to perform move snapping.</param>
+        private void OnMoveSnapValueChanged(float value)
+        {
+            Handles.MoveSnapAmount = MathEx.Clamp(value, 0.01f, 1000.0f);
+            editorSettingsHash = EditorSettings.Hash;
+        }
+
+        /// <summary>
+        /// Triggered when the rotate snap button is toggled.
+        /// </summary>
+        /// <param name="active">Determins should be rotate snap be activated or deactivated.</param>
+        private void OnRotateSnapToggled(bool active)
+        {
+            Handles.RotateHandleSnapActive = active;
+            editorSettingsHash = EditorSettings.Hash;
+        }
+
+        /// <summary>
+        /// Triggered when the rotate snap increment value changes.
+        /// </summary>
+        /// <param name="value">Value that determines in what increments to perform rotate snapping.</param>
+        private void OnRotateSnapValueChanged(float value)
+        {
+            Handles.RotateSnapAmount = (Degree)MathEx.Clamp(value, 0.01f, 360.0f);
+            editorSettingsHash = EditorSettings.Hash;
+        }
+
+        /// <summary>
+        /// Updates toggle button states according to current editor options. This is useful if tools, coordinate mode,
+        /// pivot or other scene view options have been modified externally.
+        /// </summary>
+        private void UpdateButtonStates()
+        {
+            switch (EditorApplication.ActiveSceneTool)
+            {
+                case SceneViewTool.View:
+                    viewButton.Value = true;
+                    break;
+                case SceneViewTool.Move:
+                    moveButton.Value = true;
+                    break;
+                case SceneViewTool.Rotate:
+                    rotateButton.Value = true;
+                    break;
+                case SceneViewTool.Scale:
+                    scaleButton.Value = true;
+                    break;
+            }
+
+            switch (EditorApplication.ActiveCoordinateMode)
+            {
+                case HandleCoordinateMode.Local:
+                    localCoordButton.Value = true;
+                    break;
+                case HandleCoordinateMode.World:
+                    worldCoordButton.Value = true;
+                    break;
+            }
+
+            switch (EditorApplication.ActivePivotMode)
+            {
+                case HandlePivotMode.Center:
+                    centerButton.Value = true;
+                    break;
+                case HandlePivotMode.Pivot:
+                    pivotButton.Value = true;
+                    break;
+            }
+
+            if (Handles.MoveHandleSnapActive)
+                moveSnapButton.Value = true;
+            else
+                moveSnapButton.Value = false;
+
+            moveSnapInput.Value = Handles.MoveSnapAmount;
+
+            if (Handles.RotateHandleSnapActive)
+                rotateSnapButton.Value = true;
+            else
+                rotateSnapButton.Value = false;
+
+            moveSnapInput.Value = Handles.RotateSnapAmount.Degrees;
+        }
+
+        /// <summary>
+        /// Activates or deactivates the profiler overlay according to current editor settings.
+        /// </summary>
+        private void UpdateProfilerOverlay()
+        {
+            if (EditorSettings.GetBool(ProfilerOverlayActiveKey))
+            {
+                if (activeProfilerOverlay == null)
+                {
+                    SceneObject profilerSO = new SceneObject("EditorProfilerOverlay");
+                    profilerCamera = profilerSO.AddComponent<Camera>();
+                    profilerCamera.Target = renderTexture;
+                    profilerCamera.ClearFlags = ClearFlags.None;
+                    profilerCamera.Priority = 1;
+                    profilerCamera.Layers = 0;
+
+                    activeProfilerOverlay = profilerSO.AddComponent<ProfilerOverlay>();
+                }
+            }
+            else
+            {
+                if (activeProfilerOverlay != null)
+                {
+                    activeProfilerOverlay.SceneObject.Destroy();
+                    activeProfilerOverlay = null;
+                    profilerCamera = null;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Creates the scene camera and updates the render texture. Should be called at least once before using the
+        /// scene view. Should be called whenever the window is resized.
+        /// </summary>
+        /// <param name="width">Width of the scene render target, in pixels.</param>
+        /// <param name="height">Height of the scene render target, in pixels.</param>
+        private void UpdateRenderTexture(int width, int height)
+	    {
+            width = MathEx.Max(20, width);
+            height = MathEx.Max(20, height);
+
+            renderTexture = new RenderTexture2D(PixelFormat.R8G8B8A8, width, height);
+            renderTexture.Priority = 1;
+
+		    if (camera == null)
+		    {
+                SceneObject sceneCameraSO = new SceneObject("SceneCamera", true);
+                camera = sceneCameraSO.AddComponent<Camera>();
+                camera.Target = renderTexture;
+                camera.ViewportRect = new Rect2(0.0f, 0.0f, 1.0f, 1.0f);
+
+                sceneCameraSO.Position = new Vector3(0, 0.5f, 1);
+                sceneCameraSO.LookAt(new Vector3(0, 0.5f, 0));
+
+                camera.Priority = 2;
+                camera.NearClipPlane = 0.05f;
+                camera.FarClipPlane = 2500.0f;
+                camera.ClearColor = ClearColor;
+                camera.Layers = UInt64.MaxValue & ~SceneAxesHandle.LAYER; // Don't draw scene axes in this camera
+
+                cameraController = sceneCameraSO.AddComponent<SceneCamera>();
+
+                renderTextureGUI = new GUIRenderTexture(renderTexture);
+                rtPanel.AddElement(renderTextureGUI);
+
+		        sceneGrid = new SceneGrid(camera);
+		        sceneSelection = new SceneSelection(camera);
+		        sceneGizmos = new SceneGizmos(camera);
+		        sceneHandles = new SceneHandles(this, camera);
+		    }
+		    else
+		    {
+		        camera.Target = renderTexture;
+		        renderTextureGUI.RenderTexture = renderTexture;
+		    }
+
+            Rect2I rtBounds = new Rect2I(0, 0, width, height);
+            renderTextureGUI.Bounds = rtBounds;
+            focusCatcher.Bounds = GUILayoutUtility.CalculateBounds(rtPanel, GUI);
+
+            sceneAxesGUI.SetPosition(width - HandleAxesGUISize - HandleAxesGUIPaddingX, HandleAxesGUIPaddingY);
+
+            // TODO - Consider only doing the resize once user stops resizing the widget in order to reduce constant
+            // render target destroy/create cycle for every single pixel.
+
+            camera.AspectRatio = width / (float)height;
+
+            if (profilerCamera != null)
+                profilerCamera.Target = renderTexture;
+	    }
+
+        /// <summary>
+        /// Parses an array of scene objects and removes elements that are children of elements that are also in the array.
+        /// </summary>
+        /// <param name="objects">Array containing duplicate objects as input, and array without duplicate objects as
+        ///                       output.</param>
+        private void CleanDuplicates(ref SceneObject[] objects)
+	    {
+		    List<SceneObject> cleanList = new List<SceneObject>();
+		    for (int i = 0; i < objects.Length; i++)
+		    {
+			    bool foundParent = false;
+                for (int j = 0; j < objects.Length; j++)
+                {
+                    SceneObject elem = objects[i];
+
+                    while (elem != null && elem != objects[j])
+                        elem = elem.Parent;
+
+                    bool isChildOf = elem == objects[j];
+
+				    if (i != j && isChildOf)
+				    {
+					    foundParent = true;
+					    break;
+				    }
+			    }
+
+			    if (!foundParent)
+				    cleanList.Add(objects[i]);
+		    }
+
+		    objects = cleanList.ToArray();
+	    }
+    }
+}

+ 292 - 276
Source/MBansheeEngine/GUI/GUIElement.cs

@@ -1,276 +1,292 @@
-//********************************** 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;
-
-namespace BansheeEngine
-{
-    /// <summary>
-    /// Base class for all GUI elements. Every GUI element can at least be positioned in it's parent layout/panel and be 
-    /// hidden/visible, focused/unfocused and assigned a context menu.
-    /// </summary>
-    public abstract class GUIElement : ScriptObject
-    {
-        /// <summary>
-        /// Triggered when a GUI element receives keyboard focus.
-        /// </summary>
-        public Action OnFocusGained;
-
-        /// <summary>
-        /// Triggered when a GUI element loses keyboard focus.
-        /// </summary>
-        public Action OnFocusLost;
-
-        /// <summary>
-        /// Returns the layout this element belongs to, if any.
-        /// </summary>
-        public GUILayout Parent
-        {
-            get { return Internal_GetParent(mCachedPtr); }
-        }
-
-        /// <summary>
-        /// Name of the style that determines the appearance of this GUI element.
-        /// </summary>
-        public string Style
-        {
-            get { return Internal_GetStyle(mCachedPtr); }
-            set { Internal_SetStyle(mCachedPtr, value); }
-        }
-
-        /// <summary>
-        /// Gets or sets non-clipped bounds of the GUI element. Relative to a parent GUI panel.
-        /// </summary>
-        public Rect2I Bounds
-        {
-            get
-            {
-                Rect2I bounds;
-                Internal_GetBounds(mCachedPtr, out bounds);
-                return bounds;
-            }
-            set { Internal_SetBounds(mCachedPtr, ref value); }
-        }
-
-        /// <summary>
-        /// Gets or sets non-clipped bounds of the GUI element including the margins. Relative to a parent GUI panel.
-        /// </summary>
-        public Rect2I VisualBounds
-        {
-            get
-            {
-                Rect2I bounds;
-                Internal_GetVisualBounds(mCachedPtr, out bounds);
-                return bounds;
-            }
-        }
-
-        /// <summary>
-        /// Makes the element hidden or visible. This will not affect the layout as the room for the element will still
-        /// be reserved in the parent layout, use <see cref="Active"/> if you need to affect the layout as well.
-        /// </summary>
-        public bool Visible
-        {
-            set { Internal_SetVisible(mCachedPtr, value); }
-            get { return Internal_GetVisible(mCachedPtr); }
-        }
-
-        /// <summary>
-        /// Activates or deactivates the element, making it hidden or visible. When disabled it is essentially removed from 
-        /// the parent achieving the similar effect as if the element was destroyed.
-        /// </summary>
-        public bool Active
-        {
-            set { Internal_SetActive(mCachedPtr, value); }
-            get { return Internal_GetActive(mCachedPtr); }
-        }
-
-        /// <summary>
-        /// Disables or enables the element. Disabled elements cannot be interacted with and have a faded out appearance.
-        /// </summary>
-        public bool Disabled
-        {
-            set { Internal_SetDisabled(mCachedPtr, value); }
-            get { return Internal_GetDisabled(mCachedPtr); }
-        }
-
-        /// <summary>
-        /// Assigns or removes keyboard focus on this element.
-        /// </summary>
-        public bool Focus
-        {
-            set { Internal_SetFocus(mCachedPtr, value); }
-        }
-
-        /// <summary>
-        /// Destroys this element and all its children. Removes the element from parent layout/panel.
-        /// </summary>
-        /// <remarks>
-        /// Calling methods on a destroyed element is a no-operation. Destroyed elements aren't allowed to be assigned as 
-        /// children of other elements, or be parents of other elements.
-        /// </remarks>
-        public virtual void Destroy()
-        {
-            Internal_Destroy(mCachedPtr);
-        }
-
-        /// <summary>
-        /// Sets element position relative to parent GUI panel.
-        /// </summary>
-        /// <param name="x">X position of the element in pixels, relative to parent GUI panel.</param>
-        /// <param name="y">Y position of the element in pixels, relative to parent GUI panel.</param>
-        /// <remarks>
-        /// Be aware that this value will be ignored if GUI element is part of a layout because the layout controls placement 
-        /// of child elements.
-        /// </remarks>
-        public void SetPosition(int x, int y)
-        {
-            Internal_SetPosition(mCachedPtr, x, y);
-        }
-
-        /// <summary>
-        /// Sets a fixed element width.
-        /// </summary>
-        /// <param name="width">Width in pixels.</param>
-        public void SetWidth(int width)
-        {
-            Internal_SetWidth(mCachedPtr, width);
-        }
-
-        /// <summary>
-        /// Sets a flexible element width. Element will be resized according to its contents and parent layout but will 
-        /// always stay within the provided range.
-        /// </summary>
-        /// <param name="minWidth">Minimum width in pixels. Element will never be smaller than this width.</param>
-        /// <param name="maxWidth">Maximum width in pixels. Element will never be larger than this width. Specify zero for 
-        ///                        unlimited width.</param>
-        public void SetFlexibleWidth(int minWidth, int maxWidth)
-        {
-            Internal_SetFlexibleWidth(mCachedPtr, minWidth, maxWidth);
-        }
-
-        /// <summary>
-        /// Sets a fixed element height.
-        /// </summary>
-        /// <param name="height">Height in pixels.</param>
-        public void SetHeight(int height)
-        {
-            Internal_SetHeight(mCachedPtr, height);
-        }
-
-        /// <summary>
-        /// Sets a flexible element height. Element will be resized according to its contents and parent layout but will 
-        /// always stay within the provided range.
-        /// </summary>
-        /// <param name="minHeight">Minimum height in pixels. Element will never be smaller than this height.</param>
-        /// <param name="maxHeight">Maximum height in pixels. Element will never be larger than this height. Specify zero 
-        ///                         for unlimited height.</param>
-        public void SetFlexibleHeight(int minHeight, int maxHeight)
-        {
-            Internal_SetFlexibleHeight(mCachedPtr, minHeight, maxHeight);
-        }
-
-        /// <summary>
-        /// Resets element bounds to their initial values dictated by the element's style.
-        /// </summary>
-        public void ResetDimensions()
-        {
-            Internal_ResetDimensions(mCachedPtr);
-        }
-
-        /// <summary>
-        /// Assigns a new context menu that will be opened when the element is right clicked.
-        /// </summary>
-        /// <param name="menu">Object containing context menu contents. Can be null if no menu is wanted.</param>
-        public void SetContextMenu(ContextMenu menu)
-        {
-            IntPtr menuPtr = IntPtr.Zero;
-            if (menu != null)
-                menuPtr = menu.GetCachedPtr();
-
-            Internal_SetContextMenu(mCachedPtr, menuPtr);
-        }
-
-        /// <summary>
-        /// Triggered by the native interop object when the element gains keyboard focus.
-        /// </summary>
-        private void Internal_OnFocusGained()
-        {
-            if (OnFocusGained != null)
-                OnFocusGained();
-        }
-
-        /// <summary>
-        /// Triggered by the native interop object when the element loses keyboard focus.
-        /// </summary>
-        private void Internal_OnFocusLost()
-        {
-            if (OnFocusLost != null)
-                OnFocusLost();
-        }
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern bool Internal_GetVisible(IntPtr nativeInstance);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetVisible(IntPtr nativeInstance, bool visible);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern bool Internal_GetActive(IntPtr nativeInstance);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetActive(IntPtr nativeInstance, bool enabled);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern bool Internal_GetDisabled(IntPtr nativeInstance);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetDisabled(IntPtr nativeInstance, bool disabled);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetFocus(IntPtr nativeInstance, bool focus);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern GUILayout Internal_GetParent(IntPtr nativeInstance);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetPosition(IntPtr nativeInstance, int x, int y);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetWidth(IntPtr nativeInstance, int width);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetFlexibleWidth(IntPtr nativeInstance, int minWidth, int maxWidth);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetHeight(IntPtr nativeInstance, int height);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetFlexibleHeight(IntPtr nativeInstance, int minHeight, int maxHeight);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_ResetDimensions(IntPtr nativeInstance);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_GetBounds(IntPtr nativeInstance, out Rect2I value);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetBounds(IntPtr nativeInstance, ref Rect2I value);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_GetVisualBounds(IntPtr nativeInstance, out Rect2I value);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetContextMenu(IntPtr nativeInstance, IntPtr contextMenu);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern string Internal_GetStyle(IntPtr nativeInstance);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetStyle(IntPtr nativeInstance, string style);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_Destroy(IntPtr nativeInstance);
-    }
-}
+//********************************** 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;
+
+namespace BansheeEngine
+{
+    /// <summary>
+    /// Base class for all GUI elements. Every GUI element can at least be positioned in it's parent layout/panel and be 
+    /// hidden/visible, focused/unfocused and assigned a context menu.
+    /// </summary>
+    public abstract class GUIElement : ScriptObject
+    {
+        /// <summary>
+        /// Triggered when a GUI element receives keyboard focus.
+        /// </summary>
+        public Action OnFocusGained;
+
+        /// <summary>
+        /// Triggered when a GUI element loses keyboard focus.
+        /// </summary>
+        public Action OnFocusLost;
+
+        /// <summary>
+        /// Returns the layout this element belongs to, if any.
+        /// </summary>
+        public GUILayout Parent
+        {
+            get { return Internal_GetParent(mCachedPtr); }
+        }
+
+        /// <summary>
+        /// Name of the style that determines the appearance of this GUI element.
+        /// </summary>
+        public string Style
+        {
+            get { return Internal_GetStyle(mCachedPtr); }
+            set { Internal_SetStyle(mCachedPtr, value); }
+        }
+
+        /// <summary>
+        /// Gets or sets non-clipped bounds of the GUI element. Relative to a parent GUI panel.
+        /// </summary>
+        public Rect2I Bounds
+        {
+            get
+            {
+                Rect2I bounds;
+                Internal_GetBounds(mCachedPtr, out bounds);
+                return bounds;
+            }
+            set { Internal_SetBounds(mCachedPtr, ref value); }
+        }
+
+        /// <summary>
+        /// Gets or sets non-clipped bounds of the GUI element including the margins. Relative to a parent GUI panel.
+        /// </summary>
+        public Rect2I VisualBounds
+        {
+            get
+            {
+                Rect2I bounds;
+                Internal_GetVisualBounds(mCachedPtr, out bounds);
+                return bounds;
+            }
+        }
+
+        /// <summary>
+        /// Makes the element hidden or visible. This will not affect the layout as the room for the element will still
+        /// be reserved in the parent layout, use <see cref="Active"/> if you need to affect the layout as well.
+        /// </summary>
+        public bool Visible
+        {
+            set { Internal_SetVisible(mCachedPtr, value); }
+            get { return Internal_GetVisible(mCachedPtr); }
+        }
+
+        /// <summary>
+        /// Activates or deactivates the element, making it hidden or visible. When disabled it is essentially removed from 
+        /// the parent achieving the similar effect as if the element was destroyed.
+        /// </summary>
+        public bool Active
+        {
+            set { Internal_SetActive(mCachedPtr, value); }
+            get { return Internal_GetActive(mCachedPtr); }
+        }
+
+        /// <summary>
+        /// Disables or enables the element. Disabled elements cannot be interacted with and have a faded out appearance.
+        /// </summary>
+        public bool Disabled
+        {
+            set { Internal_SetDisabled(mCachedPtr, value); }
+            get { return Internal_GetDisabled(mCachedPtr); }
+        }
+
+        /// <summary>
+        /// Assigns or removes keyboard focus on this element.
+        /// </summary>
+        public bool Focus
+        {
+            set { Internal_SetFocus(mCachedPtr, value); }
+        }
+
+        /// <summary>
+        /// Determines will this element block elements underneath it from receiving events like pointer click, hover
+        /// on/off or be able to gain focus. True by default.
+        /// </summary>
+        public bool Blocking
+        {
+            get { return Internal_GetBlocking(mCachedPtr); }
+            set { Internal_SetBlocking(mCachedPtr, value); }
+        }
+
+        /// <summary>
+        /// Destroys this element and all its children. Removes the element from parent layout/panel.
+        /// </summary>
+        /// <remarks>
+        /// Calling methods on a destroyed element is a no-operation. Destroyed elements aren't allowed to be assigned as 
+        /// children of other elements, or be parents of other elements.
+        /// </remarks>
+        public virtual void Destroy()
+        {
+            Internal_Destroy(mCachedPtr);
+        }
+
+        /// <summary>
+        /// Sets element position relative to parent GUI panel.
+        /// </summary>
+        /// <param name="x">X position of the element in pixels, relative to parent GUI panel.</param>
+        /// <param name="y">Y position of the element in pixels, relative to parent GUI panel.</param>
+        /// <remarks>
+        /// Be aware that this value will be ignored if GUI element is part of a layout because the layout controls placement 
+        /// of child elements.
+        /// </remarks>
+        public void SetPosition(int x, int y)
+        {
+            Internal_SetPosition(mCachedPtr, x, y);
+        }
+
+        /// <summary>
+        /// Sets a fixed element width.
+        /// </summary>
+        /// <param name="width">Width in pixels.</param>
+        public void SetWidth(int width)
+        {
+            Internal_SetWidth(mCachedPtr, width);
+        }
+
+        /// <summary>
+        /// Sets a flexible element width. Element will be resized according to its contents and parent layout but will 
+        /// always stay within the provided range.
+        /// </summary>
+        /// <param name="minWidth">Minimum width in pixels. Element will never be smaller than this width.</param>
+        /// <param name="maxWidth">Maximum width in pixels. Element will never be larger than this width. Specify zero for 
+        ///                        unlimited width.</param>
+        public void SetFlexibleWidth(int minWidth, int maxWidth)
+        {
+            Internal_SetFlexibleWidth(mCachedPtr, minWidth, maxWidth);
+        }
+
+        /// <summary>
+        /// Sets a fixed element height.
+        /// </summary>
+        /// <param name="height">Height in pixels.</param>
+        public void SetHeight(int height)
+        {
+            Internal_SetHeight(mCachedPtr, height);
+        }
+
+        /// <summary>
+        /// Sets a flexible element height. Element will be resized according to its contents and parent layout but will 
+        /// always stay within the provided range.
+        /// </summary>
+        /// <param name="minHeight">Minimum height in pixels. Element will never be smaller than this height.</param>
+        /// <param name="maxHeight">Maximum height in pixels. Element will never be larger than this height. Specify zero 
+        ///                         for unlimited height.</param>
+        public void SetFlexibleHeight(int minHeight, int maxHeight)
+        {
+            Internal_SetFlexibleHeight(mCachedPtr, minHeight, maxHeight);
+        }
+
+        /// <summary>
+        /// Resets element bounds to their initial values dictated by the element's style.
+        /// </summary>
+        public void ResetDimensions()
+        {
+            Internal_ResetDimensions(mCachedPtr);
+        }
+
+        /// <summary>
+        /// Assigns a new context menu that will be opened when the element is right clicked.
+        /// </summary>
+        /// <param name="menu">Object containing context menu contents. Can be null if no menu is wanted.</param>
+        public void SetContextMenu(ContextMenu menu)
+        {
+            IntPtr menuPtr = IntPtr.Zero;
+            if (menu != null)
+                menuPtr = menu.GetCachedPtr();
+
+            Internal_SetContextMenu(mCachedPtr, menuPtr);
+        }
+
+        /// <summary>
+        /// Triggered by the native interop object when the element gains keyboard focus.
+        /// </summary>
+        private void Internal_OnFocusGained()
+        {
+            if (OnFocusGained != null)
+                OnFocusGained();
+        }
+
+        /// <summary>
+        /// Triggered by the native interop object when the element loses keyboard focus.
+        /// </summary>
+        private void Internal_OnFocusLost()
+        {
+            if (OnFocusLost != null)
+                OnFocusLost();
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool Internal_GetVisible(IntPtr nativeInstance);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetVisible(IntPtr nativeInstance, bool visible);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool Internal_GetActive(IntPtr nativeInstance);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetActive(IntPtr nativeInstance, bool enabled);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool Internal_GetDisabled(IntPtr nativeInstance);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetDisabled(IntPtr nativeInstance, bool disabled);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetFocus(IntPtr nativeInstance, bool focus);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool Internal_GetBlocking(IntPtr nativeInstance);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetBlocking(IntPtr nativeInstance, bool blocking);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern GUILayout Internal_GetParent(IntPtr nativeInstance);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetPosition(IntPtr nativeInstance, int x, int y);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetWidth(IntPtr nativeInstance, int width);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetFlexibleWidth(IntPtr nativeInstance, int minWidth, int maxWidth);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetHeight(IntPtr nativeInstance, int height);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetFlexibleHeight(IntPtr nativeInstance, int minHeight, int maxHeight);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_ResetDimensions(IntPtr nativeInstance);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_GetBounds(IntPtr nativeInstance, out Rect2I value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetBounds(IntPtr nativeInstance, ref Rect2I value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_GetVisualBounds(IntPtr nativeInstance, out Rect2I value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetContextMenu(IntPtr nativeInstance, IntPtr contextMenu);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string Internal_GetStyle(IntPtr nativeInstance);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetStyle(IntPtr nativeInstance, string style);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_Destroy(IntPtr nativeInstance);
+    }
+}

+ 234 - 233
Source/SBansheeEditor/Include/BsScriptEditorWindow.h

@@ -1,234 +1,235 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#pragma once
-
-#include "BsScriptEditorPrerequisites.h"
-#include "BsScriptObject.h"
-#include "BsEditorWidget.h"
-#include "BsVector2I.h"
-
-namespace BansheeEngine
-{
-	class ScriptEditorWidget;
-
-	/**
-	 * @brief	Interop class between C++ & CLR for ScriptEditorWidget.
-	 */
-	class BS_SCR_BED_EXPORT ScriptEditorWindow : public ScriptObject<ScriptEditorWindow, PersistentScriptObjectBase>
-	{
-		/**
-		 * @brief	Contains data about the managed handle to an editor window.
-		 */
-		struct EditorWindowHandle
-		{
-			uint32_t gcHandle;
-			ScriptEditorWindow* nativeObj;
-		};
-	public:
-		SCRIPT_OBJ(EDITOR_ASSEMBLY, "BansheeEditor", "EditorWindow")
-
-		~ScriptEditorWindow();
-
-		/**
-		 * @brief	Returns the internal wrapped editor widget.
-		 */
-		EditorWidgetBase* getEditorWidget() const;
-
-		/**
-		 * @brief	Checks has the native widget been destroyed.
-		 */
-		bool isDestroyed() const { return mIsDestroyed; }
-
-		/**
-		 * @brief	Finds all editor window implementations in managed assemblies and registers
-		 *			them with the editor widget system.
-		 */
-		static void registerManagedEditorWindows();
-
-		/**
-		 * @brief	Removes all editor widgets registered previously with ::registerManagedEditorWindows.
-		 *			Useful during assembly refresh when editor window implementations might be added/removed.
-		 */
-		static void clearRegisteredEditorWindow();
-
-	private:
-		friend class ScriptEditorWidget;
-
-		ScriptEditorWindow(ScriptEditorWidget* editorWidget);
-
-		/**
-		 * @brief	Triggered when the native editor widget is resized.
-		 */
-		void onWidgetResized(UINT32 width, UINT32 height);
-
-		/**
-		 * @brief	Triggered when the native editor widget gains or loses focus.
-		 */
-		void onFocusChanged(bool inFocus);
-
-		/**
-		 * @brief	Triggered when assembly refresh has started.
-		 */
-		void onAssemblyRefreshStarted();
-
-		/**
-		 * @copydoc	ScriptObjectBase::_onManagedInstanceDeleted
-		 */
-		void _onManagedInstanceDeleted() override;
-
-		/**
-		 * @copydoc	ScriptObjectBase::beginRefresh
-		 */
-		ScriptObjectBackup beginRefresh() override;
-
-		/**
-		 * @copydoc	ScriptObjectBase::endRefresh
-		 */
-		void endRefresh(const ScriptObjectBackup& backupData) override;
-
-		/**
-		 * @copydoc	ScriptObjectBase::_createManagedInstance
-		 */
-		MonoObject* _createManagedInstance(bool construct) override;
-
-		String mName;
-		ScriptEditorWidget* mEditorWidget;
-		HEvent mOnWidgetResizedConn;
-		HEvent mOnFocusChangedConn;
-		HEvent mOnAssemblyRefreshStartedConn;
-		bool mRefreshInProgress;
-		bool mIsDestroyed;
-
-		static MonoMethod* onResizedMethod;
-		static MonoMethod* onFocusChangedMethod;
-		static MonoField* guiPanelField;
-
-		// Global editor window management methods
-
-		/**
-		 * @brief	Registers a newly created editor window interop object and adds it to
-		 *			a list of currently active editor windows.
-		 */
-		static void registerScriptEditorWindow(ScriptEditorWindow* editorWindow);
-
-		/**
-		 * @brief	Removes a window from the active editor window list.
-		 *
-		 * @param	windowTypeName	Name of the window type. Provided by EditorWidget::getName.
-		 */
-		static void unregisterScriptEditorWindow(const String& windowTypeName);
-
-		/**
-		* @brief	Callback that is triggered when user requests a widget to be opened.
-		*/
-		static EditorWidgetBase* openEditorWidgetCallback(String ns, String type, UINT32 width, UINT32 height, 
-			EditorWidgetContainer& parentContainer);
-
-		static UnorderedMap<String, EditorWindowHandle> OpenScriptEditorWindows;
-		static Vector<String> AvailableWindowTypes;
-
-		/************************************************************************/
-		/* 								CLR HOOKS						   		*/
-		/************************************************************************/
-		static MonoObject* internal_createOrGetInstance(MonoString* ns, MonoString* typeName);
-		static MonoObject* internal_getInstance(MonoString* ns, MonoString* typeName);
-
-		static bool internal_hasFocus(ScriptEditorWindow* thisPtr);
-		static bool internal_isActive(ScriptEditorWindow* thisPtr);
-		static bool internal_isPointerHovering(ScriptEditorWindow* thisPtr);
-		static void internal_screenToWindowPos(ScriptEditorWindow* thisPtr, Vector2I* screenPos, Vector2I* windowPos);
-		static void internal_windowToScreenPos(ScriptEditorWindow* thisPtr, Vector2I* windowPos, Vector2I* screenPos);
-		static UINT32 internal_getWidth(ScriptEditorWindow* thisPtr);
-		static UINT32 internal_getHeight(ScriptEditorWindow* thisPtr);
-		static void internal_getBounds(ScriptEditorWindow* thisPtr, Rect2I* bounds);
-	};
-
-	/**
-	 * @brief	Editor widget implementation that handles managed editor window implementations.
-	 *			Each implementation is wrapped in this object and then managed by its parent interop
-	 *			object of ScriptEditorWindow type.
-	 */
-	class BS_SCR_BED_EXPORT ScriptEditorWidget : public EditorWidgetBase
-	{
-	public:
-		/**
-		 * @brief	Constructs a new managed widget.
-		 *
-		 * @param	ns				Namespace of the widget type.
-		 * @param	type			Name of the widget type.
-		 * @param	defaultWidth	Default width of the widget when initially created.
-		 * @param	defaultHeight	Default height of the widget when initially created.
-		 * @param	parentContainer	Container to initially dock the widget in.
-		 */
-		ScriptEditorWidget(const String& ns, const String& type, UINT32 defaultWidth, 
-			UINT32 defaultHeight, EditorWidgetContainer& parentContainer);
-		~ScriptEditorWidget();
-
-		/**
-		 * @brief	Attempts to create a managed instance for the editor window described by the
-		 *			type provided upon construction.
-		 *
-		 * @return	True if the managed instance was created.
-		 */
-		bool createManagedInstance();
-
-		/**
-		 * @brief	Checks has the OnInitialize method been called yet.
-		 */
-		bool isInitialized() const { return mIsInitialized; }
-
-		/**
-		 * @copydoc	EditorWidgetBase::update 
-		 */
-		void update() override;
-
-		/**
-		 * @brief	Loads all required mono methods, fields and types required
-		 *			for operation of this object. Must be called after construction
-		 *			and after assembly refresh.
-		 *
-		 * @param	windowClass	Mono class to load the types from.
-		 */
-		void reloadMonoTypes(MonoClass* windowClass);
-
-		/**
-		 * @brief	Triggers OnInitialize callbacks on the managed instance.
-		 */
-		void triggerOnInitialize();
-
-		/**
-		 * @brief	Triggers OnDestroy callbacks on the managed instance.
-		 */
-		void triggerOnDestroy();
-
-		/**
-		 * @brief	Sets the parent interop object that handles part of the communication
-		 *			between this object and the managed instance.
-		 */
-		void setScriptOwner(ScriptEditorWindow* owner) { mScriptOwner = owner; }
-
-		/**
-		 * @brief	Returns the managed instance for the editor window
-		 *			represented by this object.
-		 */
-		MonoObject* getManagedInstance() const { return mManagedInstance; }
-
-	private:
-		typedef void(__stdcall *OnInitializeThunkDef) (MonoObject*, MonoException**);
-		typedef void(__stdcall *OnDestroyThunkDef) (MonoObject*, MonoException**);
-		typedef void(__stdcall *UpdateThunkDef) (MonoObject*, MonoException**);
-
-		String mNamespace;
-		String mTypename;
-
-		OnInitializeThunkDef mOnInitializeThunk;
-		OnDestroyThunkDef mOnDestroyThunk;
-		UpdateThunkDef mUpdateThunk;
-		MonoObject* mManagedInstance;
-		MonoMethod* mGetDisplayName;
-
-		ScriptEditorWindow* mScriptOwner;
-		ScriptGUILayout* mContentsPanel;
-		bool mIsInitialized;
-	};
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsScriptEditorPrerequisites.h"
+#include "BsScriptObject.h"
+#include "BsEditorWidget.h"
+#include "BsVector2I.h"
+
+namespace BansheeEngine
+{
+	class ScriptEditorWidget;
+
+	/**
+	 * @brief	Interop class between C++ & CLR for ScriptEditorWidget.
+	 */
+	class BS_SCR_BED_EXPORT ScriptEditorWindow : public ScriptObject<ScriptEditorWindow, PersistentScriptObjectBase>
+	{
+		/**
+		 * @brief	Contains data about the managed handle to an editor window.
+		 */
+		struct EditorWindowHandle
+		{
+			uint32_t gcHandle;
+			ScriptEditorWindow* nativeObj;
+		};
+	public:
+		SCRIPT_OBJ(EDITOR_ASSEMBLY, "BansheeEditor", "EditorWindow")
+
+		~ScriptEditorWindow();
+
+		/**
+		 * @brief	Returns the internal wrapped editor widget.
+		 */
+		EditorWidgetBase* getEditorWidget() const;
+
+		/**
+		 * @brief	Checks has the native widget been destroyed.
+		 */
+		bool isDestroyed() const { return mIsDestroyed; }
+
+		/**
+		 * @brief	Finds all editor window implementations in managed assemblies and registers
+		 *			them with the editor widget system.
+		 */
+		static void registerManagedEditorWindows();
+
+		/**
+		 * @brief	Removes all editor widgets registered previously with ::registerManagedEditorWindows.
+		 *			Useful during assembly refresh when editor window implementations might be added/removed.
+		 */
+		static void clearRegisteredEditorWindow();
+
+	private:
+		friend class ScriptEditorWidget;
+
+		ScriptEditorWindow(ScriptEditorWidget* editorWidget);
+
+		/**
+		 * @brief	Triggered when the native editor widget is resized.
+		 */
+		void onWidgetResized(UINT32 width, UINT32 height);
+
+		/**
+		 * @brief	Triggered when the native editor widget gains or loses focus.
+		 */
+		void onFocusChanged(bool inFocus);
+
+		/**
+		 * @brief	Triggered when assembly refresh has started.
+		 */
+		void onAssemblyRefreshStarted();
+
+		/**
+		 * @copydoc	ScriptObjectBase::_onManagedInstanceDeleted
+		 */
+		void _onManagedInstanceDeleted() override;
+
+		/**
+		 * @copydoc	ScriptObjectBase::beginRefresh
+		 */
+		ScriptObjectBackup beginRefresh() override;
+
+		/**
+		 * @copydoc	ScriptObjectBase::endRefresh
+		 */
+		void endRefresh(const ScriptObjectBackup& backupData) override;
+
+		/**
+		 * @copydoc	ScriptObjectBase::_createManagedInstance
+		 */
+		MonoObject* _createManagedInstance(bool construct) override;
+
+		String mName;
+		ScriptEditorWidget* mEditorWidget;
+		HEvent mOnWidgetResizedConn;
+		HEvent mOnFocusChangedConn;
+		HEvent mOnAssemblyRefreshStartedConn;
+		bool mRefreshInProgress;
+		bool mIsDestroyed;
+
+		static MonoMethod* onResizedMethod;
+		static MonoMethod* onFocusChangedMethod;
+		static MonoField* guiPanelField;
+
+		// Global editor window management methods
+
+		/**
+		 * @brief	Registers a newly created editor window interop object and adds it to
+		 *			a list of currently active editor windows.
+		 */
+		static void registerScriptEditorWindow(ScriptEditorWindow* editorWindow);
+
+		/**
+		 * @brief	Removes a window from the active editor window list.
+		 *
+		 * @param	windowTypeName	Name of the window type. Provided by EditorWidget::getName.
+		 */
+		static void unregisterScriptEditorWindow(const String& windowTypeName);
+
+		/**
+		* @brief	Callback that is triggered when user requests a widget to be opened.
+		*/
+		static EditorWidgetBase* openEditorWidgetCallback(String ns, String type, UINT32 width, UINT32 height, 
+			EditorWidgetContainer& parentContainer);
+
+		static UnorderedMap<String, EditorWindowHandle> OpenScriptEditorWindows;
+		static Vector<String> AvailableWindowTypes;
+
+		/************************************************************************/
+		/* 								CLR HOOKS						   		*/
+		/************************************************************************/
+		static MonoObject* internal_createOrGetInstance(MonoString* ns, MonoString* typeName);
+		static MonoObject* internal_getInstance(MonoString* ns, MonoString* typeName);
+
+		static bool internal_hasFocus(ScriptEditorWindow* thisPtr);
+		static void internal_setFocus(ScriptEditorWindow* thisPtr, bool focus);
+		static bool internal_isActive(ScriptEditorWindow* thisPtr);
+		static bool internal_isPointerHovering(ScriptEditorWindow* thisPtr);
+		static void internal_screenToWindowPos(ScriptEditorWindow* thisPtr, Vector2I* screenPos, Vector2I* windowPos);
+		static void internal_windowToScreenPos(ScriptEditorWindow* thisPtr, Vector2I* windowPos, Vector2I* screenPos);
+		static UINT32 internal_getWidth(ScriptEditorWindow* thisPtr);
+		static UINT32 internal_getHeight(ScriptEditorWindow* thisPtr);
+		static void internal_getBounds(ScriptEditorWindow* thisPtr, Rect2I* bounds);
+	};
+
+	/**
+	 * @brief	Editor widget implementation that handles managed editor window implementations.
+	 *			Each implementation is wrapped in this object and then managed by its parent interop
+	 *			object of ScriptEditorWindow type.
+	 */
+	class BS_SCR_BED_EXPORT ScriptEditorWidget : public EditorWidgetBase
+	{
+	public:
+		/**
+		 * @brief	Constructs a new managed widget.
+		 *
+		 * @param	ns				Namespace of the widget type.
+		 * @param	type			Name of the widget type.
+		 * @param	defaultWidth	Default width of the widget when initially created.
+		 * @param	defaultHeight	Default height of the widget when initially created.
+		 * @param	parentContainer	Container to initially dock the widget in.
+		 */
+		ScriptEditorWidget(const String& ns, const String& type, UINT32 defaultWidth, 
+			UINT32 defaultHeight, EditorWidgetContainer& parentContainer);
+		~ScriptEditorWidget();
+
+		/**
+		 * @brief	Attempts to create a managed instance for the editor window described by the
+		 *			type provided upon construction.
+		 *
+		 * @return	True if the managed instance was created.
+		 */
+		bool createManagedInstance();
+
+		/**
+		 * @brief	Checks has the OnInitialize method been called yet.
+		 */
+		bool isInitialized() const { return mIsInitialized; }
+
+		/**
+		 * @copydoc	EditorWidgetBase::update 
+		 */
+		void update() override;
+
+		/**
+		 * @brief	Loads all required mono methods, fields and types required
+		 *			for operation of this object. Must be called after construction
+		 *			and after assembly refresh.
+		 *
+		 * @param	windowClass	Mono class to load the types from.
+		 */
+		void reloadMonoTypes(MonoClass* windowClass);
+
+		/**
+		 * @brief	Triggers OnInitialize callbacks on the managed instance.
+		 */
+		void triggerOnInitialize();
+
+		/**
+		 * @brief	Triggers OnDestroy callbacks on the managed instance.
+		 */
+		void triggerOnDestroy();
+
+		/**
+		 * @brief	Sets the parent interop object that handles part of the communication
+		 *			between this object and the managed instance.
+		 */
+		void setScriptOwner(ScriptEditorWindow* owner) { mScriptOwner = owner; }
+
+		/**
+		 * @brief	Returns the managed instance for the editor window
+		 *			represented by this object.
+		 */
+		MonoObject* getManagedInstance() const { return mManagedInstance; }
+
+	private:
+		typedef void(__stdcall *OnInitializeThunkDef) (MonoObject*, MonoException**);
+		typedef void(__stdcall *OnDestroyThunkDef) (MonoObject*, MonoException**);
+		typedef void(__stdcall *UpdateThunkDef) (MonoObject*, MonoException**);
+
+		String mNamespace;
+		String mTypename;
+
+		OnInitializeThunkDef mOnInitializeThunk;
+		OnDestroyThunkDef mOnDestroyThunk;
+		UpdateThunkDef mUpdateThunk;
+		MonoObject* mManagedInstance;
+		MonoMethod* mGetDisplayName;
+
+		ScriptEditorWindow* mScriptOwner;
+		ScriptGUILayout* mContentsPanel;
+		bool mIsInitialized;
+	};
 }

+ 502 - 495
Source/SBansheeEditor/Source/BsScriptEditorWindow.cpp

@@ -1,496 +1,503 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsScriptEditorWindow.h"
-#include "BsScriptMeta.h"
-#include "BsMonoField.h"
-#include "BsMonoClass.h"
-#include "BsMonoMethod.h"
-#include "BsMonoManager.h"
-#include "BsMonoUtil.h"
-#include "BsEditorWindow.h"
-#include "BsEditorWidget.h"
-#include "BsEditorWidgetManager.h"
-#include "BsEditorWidgetContainer.h"
-#include "BsMonoAssembly.h"
-#include "BsScriptObjectManager.h"
-#include "BsScriptGUILayout.h"
-#include "BsScriptHString.h"
-#include "BsPlatform.h"
-#include "BsInput.h"
-
-using namespace std::placeholders;
-
-namespace BansheeEngine
-{
-	UnorderedMap<String, ScriptEditorWindow::EditorWindowHandle> ScriptEditorWindow::OpenScriptEditorWindows;
-	Vector<String> ScriptEditorWindow::AvailableWindowTypes;
-	MonoMethod* ScriptEditorWindow::onResizedMethod = nullptr;
-	MonoMethod* ScriptEditorWindow::onFocusChangedMethod = nullptr;
-	MonoField* ScriptEditorWindow::guiPanelField = nullptr;
-
-	ScriptEditorWindow::ScriptEditorWindow(ScriptEditorWidget* editorWidget)
-		:ScriptObject(editorWidget->getManagedInstance()), mName(editorWidget->getName()), mEditorWidget(editorWidget), 
-		mRefreshInProgress(false), mIsDestroyed(false)
-	{
-		mOnWidgetResizedConn = editorWidget->onResized.connect(std::bind(&ScriptEditorWindow::onWidgetResized, this, _1, _2));
-		mOnFocusChangedConn = editorWidget->onFocusChanged.connect(std::bind(&ScriptEditorWindow::onFocusChanged, this, _1));
-
-		mOnAssemblyRefreshStartedConn = ScriptObjectManager::instance().onRefreshStarted.connect(std::bind(&ScriptEditorWindow::onAssemblyRefreshStarted, this));
-	}
-
-	ScriptEditorWindow::~ScriptEditorWindow()
-	{
-		// TODO - This probably need to be called whenever we close a window, and not when script class is destructed
-		mOnWidgetResizedConn.disconnect();
-		mOnFocusChangedConn.disconnect();
-
-		mOnAssemblyRefreshStartedConn.disconnect();
-	}
-
-	void ScriptEditorWindow::initRuntimeData()
-	{
-		metaData.scriptClass->addInternalCall("Internal_CreateOrGetInstance", &ScriptEditorWindow::internal_createOrGetInstance);
-		metaData.scriptClass->addInternalCall("Internal_GetInstance", &ScriptEditorWindow::internal_getInstance);
-		metaData.scriptClass->addInternalCall("Internal_GetWidth", &ScriptEditorWindow::internal_getWidth);
-		metaData.scriptClass->addInternalCall("Internal_GetHeight", &ScriptEditorWindow::internal_getHeight);
-		metaData.scriptClass->addInternalCall("Internal_GetBounds", &ScriptEditorWindow::internal_getBounds);
-		metaData.scriptClass->addInternalCall("Internal_HasFocus", &ScriptEditorWindow::internal_hasFocus);
-		metaData.scriptClass->addInternalCall("Internal_IsActive", &ScriptEditorWindow::internal_isActive);
-		metaData.scriptClass->addInternalCall("Internal_IsPointerHovering", &ScriptEditorWindow::internal_isPointerHovering);
-		metaData.scriptClass->addInternalCall("Internal_ScreenToWindowPos", &ScriptEditorWindow::internal_screenToWindowPos);
-		metaData.scriptClass->addInternalCall("Internal_WindowToScreenPos", &ScriptEditorWindow::internal_windowToScreenPos);
-
-		onResizedMethod = metaData.scriptClass->getMethod("WindowResized", 2);
-		onFocusChangedMethod = metaData.scriptClass->getMethod("FocusChanged", 1);
-
-		guiPanelField = metaData.scriptClass->getField("GUI");
-	}
-
-	MonoObject* ScriptEditorWindow::internal_createOrGetInstance(MonoString* ns, MonoString* typeName)
-	{
-		String strTypeName = toString(MonoUtil::monoToWString(typeName));
-		String strNamespace = toString(MonoUtil::monoToWString(ns));
-		String fullName = strNamespace + "." + strTypeName;
-
-		auto findIter = OpenScriptEditorWindows.find(fullName);
-		if(findIter != OpenScriptEditorWindows.end())
-			return findIter->second.nativeObj->mManagedInstance;
-
-		EditorWidgetManager::instance().open(fullName); // This will trigger openEditorWidgetCallback and update OpenScriptEditorWindows
-
-		auto findIter2 = OpenScriptEditorWindows.find(fullName);
-		if(findIter2 != OpenScriptEditorWindows.end())
-			return findIter2->second.nativeObj->mManagedInstance;
-
-		return nullptr;
-	}
-
-	MonoObject* ScriptEditorWindow::internal_getInstance(MonoString* ns, MonoString* typeName)
-	{
-		String strTypeName = toString(MonoUtil::monoToWString(typeName));
-		String strNamespace = toString(MonoUtil::monoToWString(ns));
-		String fullName = strNamespace + "." + strTypeName;
-
-		auto findIter = OpenScriptEditorWindows.find(fullName);
-		if (findIter != OpenScriptEditorWindows.end())
-			return findIter->second.nativeObj->mManagedInstance;
-
-		return nullptr;
-	}
-
-	EditorWidgetBase* ScriptEditorWindow::getEditorWidget() const 
-	{ 
-		return mEditorWidget; 
-	}
-
-	void ScriptEditorWindow::_onManagedInstanceDeleted()
-	{
-		if (!mRefreshInProgress)
-			ScriptObject::_onManagedInstanceDeleted();
-		else
-			mManagedInstance = nullptr;
-	}
-
-	ScriptObjectBackup ScriptEditorWindow::beginRefresh()
-	{
-		mRefreshInProgress = true;
-
-		auto iterFind = OpenScriptEditorWindows.find(mName);
-		if (iterFind != OpenScriptEditorWindows.end())
-		{
-			EditorWindowHandle handle = iterFind->second;
-
-			mono_gchandle_free(handle.gcHandle);
-			iterFind->second.gcHandle = 0;
-		}
-
-		return PersistentScriptObjectBase::beginRefresh();
-	}
-
-	void ScriptEditorWindow::endRefresh(const ScriptObjectBackup& backupData)
-	{
-		mRefreshInProgress = false;
-
-		if (!isDestroyed())
-			mManagedInstance = mEditorWidget->getManagedInstance();
-		else
-			mManagedInstance = nullptr;
-
-		if (mManagedInstance != nullptr)
-		{
-			auto iterFind = OpenScriptEditorWindows.find(mName);
-			if (iterFind != OpenScriptEditorWindows.end())
-				iterFind->second.gcHandle = mono_gchandle_new(mManagedInstance, false);
-		}
-		else
-		{
-			// We couldn't restore managed instance because window class cannot be found
-			_onManagedInstanceDeleted();
-		}
-
-		PersistentScriptObjectBase::endRefresh(backupData);
-	}
-
-	void ScriptEditorWindow::onAssemblyRefreshStarted()
-	{
-		if (!isDestroyed())
-			mEditorWidget->triggerOnDestroy();
-	}
-
-	MonoObject* ScriptEditorWindow::_createManagedInstance(bool construct)
-	{
-		if (!isDestroyed())
-		{
-			mEditorWidget->createManagedInstance();
-
-			return mEditorWidget->getManagedInstance();
-		}
-		else
-			return nullptr;
-	}
-
-	bool ScriptEditorWindow::internal_hasFocus(ScriptEditorWindow* thisPtr)
-	{
-		if (!thisPtr->isDestroyed())
-			return thisPtr->getEditorWidget()->hasFocus();
-		else
-			return false;
-	}
-
-	bool ScriptEditorWindow::internal_isActive(ScriptEditorWindow* thisPtr)
-	{
-		if (!thisPtr->isDestroyed())
-			return thisPtr->getEditorWidget()->isActive();
-		else
-			return false;
-	}
-
-	bool ScriptEditorWindow::internal_isPointerHovering(ScriptEditorWindow* thisPtr)
-	{
-		if (!thisPtr->isDestroyed())
-		{
-			EditorWidgetBase* widget = thisPtr->getEditorWidget();
-			EditorWindowBase* window = widget->getParentWindow();
-			if (window == nullptr)
-				return false;
-
-			RenderWindowPtr renderWindow = window->getRenderWindow();
-
-			Vector2I pointerPos = gInput().getPointerPosition();
-			if(Platform::isPointOverWindow(*renderWindow, pointerPos))
-			{
-				Rect2I bounds = thisPtr->getEditorWidget()->getBounds();
-
-				Vector2I widgetPos(bounds.x, bounds.y);
-				Vector2I screenPos = widget->widgetToScreenPos(widgetPos);
-
-				bounds.x = screenPos.x;
-				bounds.y = screenPos.y;
-
-				return bounds.contains(pointerPos);
-			}
-		}
-
-		return false;
-	}
-
-	void ScriptEditorWindow::internal_screenToWindowPos(ScriptEditorWindow* thisPtr, Vector2I* screenPos, Vector2I* windowPos)
-	{
-		if (!thisPtr->isDestroyed())
-			*windowPos = thisPtr->getEditorWidget()->screenToWidgetPos(*screenPos);
-		else
-			*windowPos = *screenPos;
-	}
-
-	void ScriptEditorWindow::internal_windowToScreenPos(ScriptEditorWindow* thisPtr, Vector2I* windowPos, Vector2I* screenPos)
-	{
-		if (!thisPtr->isDestroyed())
-			*screenPos = thisPtr->getEditorWidget()->widgetToScreenPos(*windowPos);
-		else
-			*screenPos = *windowPos;
-	}
-
-	UINT32 ScriptEditorWindow::internal_getWidth(ScriptEditorWindow* thisPtr)
-	{
-		if (!thisPtr->isDestroyed())
-			return thisPtr->mEditorWidget->getWidth();
-		else
-			return 0;
-	}
-
-	UINT32 ScriptEditorWindow::internal_getHeight(ScriptEditorWindow* thisPtr)
-	{
-		if (!thisPtr->isDestroyed())
-			return thisPtr->mEditorWidget->getHeight();
-		else
-			return 0;
-	}
-
-	void ScriptEditorWindow::internal_getBounds(ScriptEditorWindow* thisPtr, Rect2I* bounds)
-	{
-		if (!thisPtr->isDestroyed())
-		{
-			EditorWidgetBase* widget = thisPtr->getEditorWidget();
-			*bounds = thisPtr->getEditorWidget()->getBounds();
-			
-			Vector2I widgetPos(bounds->x, bounds->y);
-			Vector2I screenPos = widget->widgetToScreenPos(widgetPos);
-
-			bounds->x = screenPos.x;
-			bounds->y = screenPos.y;
-		}
-		else
-			*bounds = Rect2I();
-	}
-
-	void ScriptEditorWindow::onWidgetResized(UINT32 width, UINT32 height)
-	{
-		if (isDestroyed() || !mEditorWidget->isInitialized() || mManagedInstance == nullptr)
-			return;
-
-		void* params[2] = { &width, &height };
-		onResizedMethod->invokeVirtual(mManagedInstance, params);
-	}
-
-	void ScriptEditorWindow::onFocusChanged(bool inFocus)
-	{
-		if (isDestroyed() || mManagedInstance == nullptr)
-			return;
-
-		void* params[1] = { &inFocus};
-		onFocusChangedMethod->invokeVirtual(mManagedInstance, params);
-	}
-
-	void ScriptEditorWindow::registerManagedEditorWindows()
-	{
-		MonoAssembly* assembly = MonoManager::instance().getAssembly(EDITOR_ASSEMBLY);
-
-		if(assembly != nullptr)
-		{
-			MonoClass* defaultSizeAttrib = assembly->getClass("BansheeEditor", "DefaultSize");
-			if (defaultSizeAttrib == nullptr)
-				BS_EXCEPT(InternalErrorException, "Cannot find DefaultSize managed class.");
-
-			MonoField* defaultWidthField = defaultSizeAttrib->getField("width");
-			MonoField* defaultHeightField = defaultSizeAttrib->getField("height");
-
-			MonoClass* editorWindowClass = assembly->getClass("BansheeEditor", "EditorWindow");
-
-			const Vector<MonoClass*>& allClasses = assembly->getAllClasses();
-			for(auto& curClass : allClasses)
-			{
-				if(curClass->isSubClassOf(editorWindowClass) && curClass != editorWindowClass)
-				{
-					UINT32 width = 400;
-					UINT32 height = 400;
-
-					MonoObject* defaultSize = curClass->getAttribute(defaultSizeAttrib);
-					if (defaultSize != nullptr)
-					{
-						defaultWidthField->getValue(defaultSize, &width);
-						defaultHeightField->getValue(defaultSize, &height);
-					}
-
-					const String& className = curClass->getFullName();
-					EditorWidgetManager::instance().registerWidget(className, 
-						std::bind(&ScriptEditorWindow::openEditorWidgetCallback, curClass->getNamespace(), 
-						curClass->getTypeName(), width, height, _1));
-					AvailableWindowTypes.push_back(className);
-				}
-			}
-		}
-	}
-
-	void ScriptEditorWindow::clearRegisteredEditorWindow()
-	{
-		for (auto& windowType : AvailableWindowTypes)
-		{
-			EditorWidgetManager::instance().unregisterWidget(windowType);
-		}
-
-		AvailableWindowTypes.clear();
-	}
-
-	EditorWidgetBase* ScriptEditorWindow::openEditorWidgetCallback(String ns, String type, UINT32 width, 
-		UINT32 height, EditorWidgetContainer& parentContainer)
-	{
-		ScriptEditorWidget* editorWidget = bs_new<ScriptEditorWidget>(ns, type, width, height, parentContainer);
-		ScriptEditorWindow* nativeInstance = new (bs_alloc<ScriptEditorWindow>()) ScriptEditorWindow(editorWidget);
-
-		ScriptEditorWindow::registerScriptEditorWindow(nativeInstance);
-
-		mono_runtime_object_init(editorWidget->getManagedInstance()); // Construct it
-		editorWidget->setScriptOwner(nativeInstance);
-
-		return editorWidget;
-	}
-
-	void ScriptEditorWindow::registerScriptEditorWindow(ScriptEditorWindow* editorWindow)
-	{
-		if(editorWindow == nullptr)
-			return;
-
-		auto findIter = OpenScriptEditorWindows.find(editorWindow->mName);
-		if(findIter == OpenScriptEditorWindows.end())
-		{
-			EditorWindowHandle newHandle;
-			newHandle.nativeObj = editorWindow;
-			newHandle.gcHandle = mono_gchandle_new(editorWindow->mManagedInstance, false);
-
-			OpenScriptEditorWindows[editorWindow->mName] = newHandle;
-		}
-	}
-
-	void ScriptEditorWindow::unregisterScriptEditorWindow(const String& windowTypeName)
-	{
-		auto findIter = OpenScriptEditorWindows.find(windowTypeName);
-		if(findIter != OpenScriptEditorWindows.end())
-		{
-			EditorWindowHandle& foundHandle = findIter->second;
-			mono_gchandle_free(foundHandle.gcHandle);
-
-			OpenScriptEditorWindows.erase(findIter);
-		}
-	}
-
-	ScriptEditorWidget::ScriptEditorWidget(const String& ns, const String& type, UINT32 defaultWidth, 
-		UINT32 defaultHeight, EditorWidgetContainer& parentContainer)
-		:EditorWidgetBase(HString(toWString(type)), ns + "." + type, defaultWidth, defaultHeight, parentContainer), 
-		mNamespace(ns), mTypename(type), mUpdateThunk(nullptr), mManagedInstance(nullptr), mOnInitializeThunk(nullptr), 
-		mOnDestroyThunk(nullptr), mContentsPanel(nullptr), mScriptOwner(nullptr), mGetDisplayName(nullptr), mIsInitialized(false)
-	{
-		if(createManagedInstance())
-		{
-			if (mGetDisplayName != nullptr)
-			{
-				MonoObject* displayNameMono = mGetDisplayName->invokeVirtual(mManagedInstance, nullptr);
-				ScriptHString* scriptHString = ScriptHString::toNative(displayNameMono);
-
-				if (scriptHString != nullptr)
-				{
-					mDisplayName = HString(scriptHString->getInternalValue());
-					parentContainer.refreshWidgetNames();
-				}
-			}
-		}
-	}
-
-	ScriptEditorWidget::~ScriptEditorWidget()
-	{
-		mScriptOwner->mIsDestroyed = true;
-		mScriptOwner->mEditorWidget = nullptr;
-
-		mContentsPanel->destroy();
-		mContentsPanel = nullptr;
-
-		triggerOnDestroy();
-		ScriptEditorWindow::unregisterScriptEditorWindow(getName());
-	}
-
-	bool ScriptEditorWidget::createManagedInstance()
-	{
-		const char* assemblies[2] = { EDITOR_ASSEMBLY, SCRIPT_EDITOR_ASSEMBLY };
-		UINT32 numAssemblies = sizeof(assemblies) / sizeof(assemblies[0]);
-
-		for (UINT32 i = 0; i < numAssemblies; i++)
-		{
-			MonoAssembly* assembly = MonoManager::instance().getAssembly(assemblies[i]);
-
-			if (assembly != nullptr)
-			{
-				MonoClass* editorWindowClass = assembly->getClass(mNamespace, mTypename);
-
-				if (editorWindowClass != nullptr)
-				{
-					mManagedInstance = editorWindowClass->createInstance();
-
-					MonoObject* guiPanel = ScriptGUIPanel::createFromExisting(mContent);
-					mContentsPanel = ScriptGUILayout::toNative(guiPanel);
-					ScriptEditorWindow::guiPanelField->setValue(mManagedInstance, guiPanel);
-
-					reloadMonoTypes(editorWindowClass);
-					return true;
-				}
-			}
-		}
-
-		return false;
-	}
-
-	void ScriptEditorWidget::triggerOnInitialize()
-	{
-		if (mOnInitializeThunk != nullptr && mManagedInstance != nullptr)
-		{
-			// Note: Not calling virtual methods. Can be easily done if needed but for now doing this
-			// for some extra speed.
-			MonoUtil::invokeThunk(mOnInitializeThunk, mManagedInstance);
-		}
-
-		mIsInitialized = true;
-	}
-
-	void ScriptEditorWidget::triggerOnDestroy()
-	{
-		if (mOnDestroyThunk != nullptr && mManagedInstance != nullptr)
-		{
-			// Note: Not calling virtual methods. Can be easily done if needed but for now doing this
-			// for some extra speed.
-			MonoUtil::invokeThunk(mOnDestroyThunk, mManagedInstance);
-		}
-
-		mIsInitialized = false;
-	}
-
-	void ScriptEditorWidget::update()
-	{
-		if (!mIsInitialized)
-			triggerOnInitialize();
-
-		if (mUpdateThunk != nullptr && mManagedInstance != nullptr)
-		{
-			// Note: Not calling virtual methods. Can be easily done if needed but for now doing this
-			// for some extra speed.
-			MonoUtil::invokeThunk(mUpdateThunk, mManagedInstance);
-		}
-	}
-
-	void ScriptEditorWidget::reloadMonoTypes(MonoClass* windowClass)
-	{
-		MonoMethod* updateMethod = windowClass->getMethod("OnEditorUpdate", 0);
-
-		if (updateMethod != nullptr)
-			mUpdateThunk = (UpdateThunkDef)updateMethod->getThunk();
-
-		MonoMethod* onInitializeMethod = windowClass->getMethod("OnInitialize", 0);
-
-		if (onInitializeMethod != nullptr)
-			mOnInitializeThunk = (OnInitializeThunkDef)onInitializeMethod->getThunk();
-
-		MonoMethod* onDestroyMethod = windowClass->getMethod("OnDestroy", 0);
-
-		if (onDestroyMethod != nullptr)
-			mOnDestroyThunk = (OnDestroyThunkDef)onDestroyMethod->getThunk();
-
-		mGetDisplayName = windowClass->getMethod("GetDisplayName", 0);
-	}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsScriptEditorWindow.h"
+#include "BsScriptMeta.h"
+#include "BsMonoField.h"
+#include "BsMonoClass.h"
+#include "BsMonoMethod.h"
+#include "BsMonoManager.h"
+#include "BsMonoUtil.h"
+#include "BsEditorWindow.h"
+#include "BsEditorWidget.h"
+#include "BsEditorWidgetManager.h"
+#include "BsEditorWidgetContainer.h"
+#include "BsMonoAssembly.h"
+#include "BsScriptObjectManager.h"
+#include "BsScriptGUILayout.h"
+#include "BsScriptHString.h"
+#include "BsPlatform.h"
+#include "BsInput.h"
+
+using namespace std::placeholders;
+
+namespace BansheeEngine
+{
+	UnorderedMap<String, ScriptEditorWindow::EditorWindowHandle> ScriptEditorWindow::OpenScriptEditorWindows;
+	Vector<String> ScriptEditorWindow::AvailableWindowTypes;
+	MonoMethod* ScriptEditorWindow::onResizedMethod = nullptr;
+	MonoMethod* ScriptEditorWindow::onFocusChangedMethod = nullptr;
+	MonoField* ScriptEditorWindow::guiPanelField = nullptr;
+
+	ScriptEditorWindow::ScriptEditorWindow(ScriptEditorWidget* editorWidget)
+		:ScriptObject(editorWidget->getManagedInstance()), mName(editorWidget->getName()), mEditorWidget(editorWidget), 
+		mRefreshInProgress(false), mIsDestroyed(false)
+	{
+		mOnWidgetResizedConn = editorWidget->onResized.connect(std::bind(&ScriptEditorWindow::onWidgetResized, this, _1, _2));
+		mOnFocusChangedConn = editorWidget->onFocusChanged.connect(std::bind(&ScriptEditorWindow::onFocusChanged, this, _1));
+
+		mOnAssemblyRefreshStartedConn = ScriptObjectManager::instance().onRefreshStarted.connect(std::bind(&ScriptEditorWindow::onAssemblyRefreshStarted, this));
+	}
+
+	ScriptEditorWindow::~ScriptEditorWindow()
+	{
+		// TODO - This probably need to be called whenever we close a window, and not when script class is destructed
+		mOnWidgetResizedConn.disconnect();
+		mOnFocusChangedConn.disconnect();
+
+		mOnAssemblyRefreshStartedConn.disconnect();
+	}
+
+	void ScriptEditorWindow::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_CreateOrGetInstance", &ScriptEditorWindow::internal_createOrGetInstance);
+		metaData.scriptClass->addInternalCall("Internal_GetInstance", &ScriptEditorWindow::internal_getInstance);
+		metaData.scriptClass->addInternalCall("Internal_GetWidth", &ScriptEditorWindow::internal_getWidth);
+		metaData.scriptClass->addInternalCall("Internal_GetHeight", &ScriptEditorWindow::internal_getHeight);
+		metaData.scriptClass->addInternalCall("Internal_GetBounds", &ScriptEditorWindow::internal_getBounds);
+		metaData.scriptClass->addInternalCall("Internal_SetFocus", &ScriptEditorWindow::internal_setFocus);
+		metaData.scriptClass->addInternalCall("Internal_HasFocus", &ScriptEditorWindow::internal_hasFocus);
+		metaData.scriptClass->addInternalCall("Internal_IsActive", &ScriptEditorWindow::internal_isActive);
+		metaData.scriptClass->addInternalCall("Internal_IsPointerHovering", &ScriptEditorWindow::internal_isPointerHovering);
+		metaData.scriptClass->addInternalCall("Internal_ScreenToWindowPos", &ScriptEditorWindow::internal_screenToWindowPos);
+		metaData.scriptClass->addInternalCall("Internal_WindowToScreenPos", &ScriptEditorWindow::internal_windowToScreenPos);
+
+		onResizedMethod = metaData.scriptClass->getMethod("WindowResized", 2);
+		onFocusChangedMethod = metaData.scriptClass->getMethod("FocusChanged", 1);
+
+		guiPanelField = metaData.scriptClass->getField("GUI");
+	}
+
+	MonoObject* ScriptEditorWindow::internal_createOrGetInstance(MonoString* ns, MonoString* typeName)
+	{
+		String strTypeName = toString(MonoUtil::monoToWString(typeName));
+		String strNamespace = toString(MonoUtil::monoToWString(ns));
+		String fullName = strNamespace + "." + strTypeName;
+
+		auto findIter = OpenScriptEditorWindows.find(fullName);
+		if(findIter != OpenScriptEditorWindows.end())
+			return findIter->second.nativeObj->mManagedInstance;
+
+		EditorWidgetManager::instance().open(fullName); // This will trigger openEditorWidgetCallback and update OpenScriptEditorWindows
+
+		auto findIter2 = OpenScriptEditorWindows.find(fullName);
+		if(findIter2 != OpenScriptEditorWindows.end())
+			return findIter2->second.nativeObj->mManagedInstance;
+
+		return nullptr;
+	}
+
+	MonoObject* ScriptEditorWindow::internal_getInstance(MonoString* ns, MonoString* typeName)
+	{
+		String strTypeName = toString(MonoUtil::monoToWString(typeName));
+		String strNamespace = toString(MonoUtil::monoToWString(ns));
+		String fullName = strNamespace + "." + strTypeName;
+
+		auto findIter = OpenScriptEditorWindows.find(fullName);
+		if (findIter != OpenScriptEditorWindows.end())
+			return findIter->second.nativeObj->mManagedInstance;
+
+		return nullptr;
+	}
+
+	EditorWidgetBase* ScriptEditorWindow::getEditorWidget() const 
+	{ 
+		return mEditorWidget; 
+	}
+
+	void ScriptEditorWindow::_onManagedInstanceDeleted()
+	{
+		if (!mRefreshInProgress)
+			ScriptObject::_onManagedInstanceDeleted();
+		else
+			mManagedInstance = nullptr;
+	}
+
+	ScriptObjectBackup ScriptEditorWindow::beginRefresh()
+	{
+		mRefreshInProgress = true;
+
+		auto iterFind = OpenScriptEditorWindows.find(mName);
+		if (iterFind != OpenScriptEditorWindows.end())
+		{
+			EditorWindowHandle handle = iterFind->second;
+
+			mono_gchandle_free(handle.gcHandle);
+			iterFind->second.gcHandle = 0;
+		}
+
+		return PersistentScriptObjectBase::beginRefresh();
+	}
+
+	void ScriptEditorWindow::endRefresh(const ScriptObjectBackup& backupData)
+	{
+		mRefreshInProgress = false;
+
+		if (!isDestroyed())
+			mManagedInstance = mEditorWidget->getManagedInstance();
+		else
+			mManagedInstance = nullptr;
+
+		if (mManagedInstance != nullptr)
+		{
+			auto iterFind = OpenScriptEditorWindows.find(mName);
+			if (iterFind != OpenScriptEditorWindows.end())
+				iterFind->second.gcHandle = mono_gchandle_new(mManagedInstance, false);
+		}
+		else
+		{
+			// We couldn't restore managed instance because window class cannot be found
+			_onManagedInstanceDeleted();
+		}
+
+		PersistentScriptObjectBase::endRefresh(backupData);
+	}
+
+	void ScriptEditorWindow::onAssemblyRefreshStarted()
+	{
+		if (!isDestroyed())
+			mEditorWidget->triggerOnDestroy();
+	}
+
+	MonoObject* ScriptEditorWindow::_createManagedInstance(bool construct)
+	{
+		if (!isDestroyed())
+		{
+			mEditorWidget->createManagedInstance();
+
+			return mEditorWidget->getManagedInstance();
+		}
+		else
+			return nullptr;
+	}
+
+	void ScriptEditorWindow::internal_setFocus(ScriptEditorWindow* thisPtr, bool focus)
+	{
+		if (!thisPtr->isDestroyed())
+			return thisPtr->getEditorWidget()->setHasFocus(focus);
+	}
+
+	bool ScriptEditorWindow::internal_hasFocus(ScriptEditorWindow* thisPtr)
+	{
+		if (!thisPtr->isDestroyed())
+			return thisPtr->getEditorWidget()->hasFocus();
+		else
+			return false;
+	}
+
+	bool ScriptEditorWindow::internal_isActive(ScriptEditorWindow* thisPtr)
+	{
+		if (!thisPtr->isDestroyed())
+			return thisPtr->getEditorWidget()->isActive();
+		else
+			return false;
+	}
+
+	bool ScriptEditorWindow::internal_isPointerHovering(ScriptEditorWindow* thisPtr)
+	{
+		if (!thisPtr->isDestroyed())
+		{
+			EditorWidgetBase* widget = thisPtr->getEditorWidget();
+			EditorWindowBase* window = widget->getParentWindow();
+			if (window == nullptr)
+				return false;
+
+			RenderWindowPtr renderWindow = window->getRenderWindow();
+
+			Vector2I pointerPos = gInput().getPointerPosition();
+			if(Platform::isPointOverWindow(*renderWindow, pointerPos))
+			{
+				Rect2I bounds = thisPtr->getEditorWidget()->getBounds();
+
+				Vector2I widgetPos(bounds.x, bounds.y);
+				Vector2I screenPos = widget->widgetToScreenPos(widgetPos);
+
+				bounds.x = screenPos.x;
+				bounds.y = screenPos.y;
+
+				return bounds.contains(pointerPos);
+			}
+		}
+
+		return false;
+	}
+
+	void ScriptEditorWindow::internal_screenToWindowPos(ScriptEditorWindow* thisPtr, Vector2I* screenPos, Vector2I* windowPos)
+	{
+		if (!thisPtr->isDestroyed())
+			*windowPos = thisPtr->getEditorWidget()->screenToWidgetPos(*screenPos);
+		else
+			*windowPos = *screenPos;
+	}
+
+	void ScriptEditorWindow::internal_windowToScreenPos(ScriptEditorWindow* thisPtr, Vector2I* windowPos, Vector2I* screenPos)
+	{
+		if (!thisPtr->isDestroyed())
+			*screenPos = thisPtr->getEditorWidget()->widgetToScreenPos(*windowPos);
+		else
+			*screenPos = *windowPos;
+	}
+
+	UINT32 ScriptEditorWindow::internal_getWidth(ScriptEditorWindow* thisPtr)
+	{
+		if (!thisPtr->isDestroyed())
+			return thisPtr->mEditorWidget->getWidth();
+		else
+			return 0;
+	}
+
+	UINT32 ScriptEditorWindow::internal_getHeight(ScriptEditorWindow* thisPtr)
+	{
+		if (!thisPtr->isDestroyed())
+			return thisPtr->mEditorWidget->getHeight();
+		else
+			return 0;
+	}
+
+	void ScriptEditorWindow::internal_getBounds(ScriptEditorWindow* thisPtr, Rect2I* bounds)
+	{
+		if (!thisPtr->isDestroyed())
+		{
+			EditorWidgetBase* widget = thisPtr->getEditorWidget();
+			*bounds = thisPtr->getEditorWidget()->getBounds();
+			
+			Vector2I widgetPos(bounds->x, bounds->y);
+			Vector2I screenPos = widget->widgetToScreenPos(widgetPos);
+
+			bounds->x = screenPos.x;
+			bounds->y = screenPos.y;
+		}
+		else
+			*bounds = Rect2I();
+	}
+
+	void ScriptEditorWindow::onWidgetResized(UINT32 width, UINT32 height)
+	{
+		if (isDestroyed() || !mEditorWidget->isInitialized() || mManagedInstance == nullptr)
+			return;
+
+		void* params[2] = { &width, &height };
+		onResizedMethod->invokeVirtual(mManagedInstance, params);
+	}
+
+	void ScriptEditorWindow::onFocusChanged(bool inFocus)
+	{
+		if (isDestroyed() || mManagedInstance == nullptr)
+			return;
+
+		void* params[1] = { &inFocus};
+		onFocusChangedMethod->invokeVirtual(mManagedInstance, params);
+	}
+
+	void ScriptEditorWindow::registerManagedEditorWindows()
+	{
+		MonoAssembly* assembly = MonoManager::instance().getAssembly(EDITOR_ASSEMBLY);
+
+		if(assembly != nullptr)
+		{
+			MonoClass* defaultSizeAttrib = assembly->getClass("BansheeEditor", "DefaultSize");
+			if (defaultSizeAttrib == nullptr)
+				BS_EXCEPT(InternalErrorException, "Cannot find DefaultSize managed class.");
+
+			MonoField* defaultWidthField = defaultSizeAttrib->getField("width");
+			MonoField* defaultHeightField = defaultSizeAttrib->getField("height");
+
+			MonoClass* editorWindowClass = assembly->getClass("BansheeEditor", "EditorWindow");
+
+			const Vector<MonoClass*>& allClasses = assembly->getAllClasses();
+			for(auto& curClass : allClasses)
+			{
+				if(curClass->isSubClassOf(editorWindowClass) && curClass != editorWindowClass)
+				{
+					UINT32 width = 400;
+					UINT32 height = 400;
+
+					MonoObject* defaultSize = curClass->getAttribute(defaultSizeAttrib);
+					if (defaultSize != nullptr)
+					{
+						defaultWidthField->getValue(defaultSize, &width);
+						defaultHeightField->getValue(defaultSize, &height);
+					}
+
+					const String& className = curClass->getFullName();
+					EditorWidgetManager::instance().registerWidget(className, 
+						std::bind(&ScriptEditorWindow::openEditorWidgetCallback, curClass->getNamespace(), 
+						curClass->getTypeName(), width, height, _1));
+					AvailableWindowTypes.push_back(className);
+				}
+			}
+		}
+	}
+
+	void ScriptEditorWindow::clearRegisteredEditorWindow()
+	{
+		for (auto& windowType : AvailableWindowTypes)
+		{
+			EditorWidgetManager::instance().unregisterWidget(windowType);
+		}
+
+		AvailableWindowTypes.clear();
+	}
+
+	EditorWidgetBase* ScriptEditorWindow::openEditorWidgetCallback(String ns, String type, UINT32 width, 
+		UINT32 height, EditorWidgetContainer& parentContainer)
+	{
+		ScriptEditorWidget* editorWidget = bs_new<ScriptEditorWidget>(ns, type, width, height, parentContainer);
+		ScriptEditorWindow* nativeInstance = new (bs_alloc<ScriptEditorWindow>()) ScriptEditorWindow(editorWidget);
+
+		ScriptEditorWindow::registerScriptEditorWindow(nativeInstance);
+
+		mono_runtime_object_init(editorWidget->getManagedInstance()); // Construct it
+		editorWidget->setScriptOwner(nativeInstance);
+
+		return editorWidget;
+	}
+
+	void ScriptEditorWindow::registerScriptEditorWindow(ScriptEditorWindow* editorWindow)
+	{
+		if(editorWindow == nullptr)
+			return;
+
+		auto findIter = OpenScriptEditorWindows.find(editorWindow->mName);
+		if(findIter == OpenScriptEditorWindows.end())
+		{
+			EditorWindowHandle newHandle;
+			newHandle.nativeObj = editorWindow;
+			newHandle.gcHandle = mono_gchandle_new(editorWindow->mManagedInstance, false);
+
+			OpenScriptEditorWindows[editorWindow->mName] = newHandle;
+		}
+	}
+
+	void ScriptEditorWindow::unregisterScriptEditorWindow(const String& windowTypeName)
+	{
+		auto findIter = OpenScriptEditorWindows.find(windowTypeName);
+		if(findIter != OpenScriptEditorWindows.end())
+		{
+			EditorWindowHandle& foundHandle = findIter->second;
+			mono_gchandle_free(foundHandle.gcHandle);
+
+			OpenScriptEditorWindows.erase(findIter);
+		}
+	}
+
+	ScriptEditorWidget::ScriptEditorWidget(const String& ns, const String& type, UINT32 defaultWidth, 
+		UINT32 defaultHeight, EditorWidgetContainer& parentContainer)
+		:EditorWidgetBase(HString(toWString(type)), ns + "." + type, defaultWidth, defaultHeight, parentContainer), 
+		mNamespace(ns), mTypename(type), mUpdateThunk(nullptr), mManagedInstance(nullptr), mOnInitializeThunk(nullptr), 
+		mOnDestroyThunk(nullptr), mContentsPanel(nullptr), mScriptOwner(nullptr), mGetDisplayName(nullptr), mIsInitialized(false)
+	{
+		if(createManagedInstance())
+		{
+			if (mGetDisplayName != nullptr)
+			{
+				MonoObject* displayNameMono = mGetDisplayName->invokeVirtual(mManagedInstance, nullptr);
+				ScriptHString* scriptHString = ScriptHString::toNative(displayNameMono);
+
+				if (scriptHString != nullptr)
+				{
+					mDisplayName = HString(scriptHString->getInternalValue());
+					parentContainer.refreshWidgetNames();
+				}
+			}
+		}
+	}
+
+	ScriptEditorWidget::~ScriptEditorWidget()
+	{
+		mScriptOwner->mIsDestroyed = true;
+		mScriptOwner->mEditorWidget = nullptr;
+
+		mContentsPanel->destroy();
+		mContentsPanel = nullptr;
+
+		triggerOnDestroy();
+		ScriptEditorWindow::unregisterScriptEditorWindow(getName());
+	}
+
+	bool ScriptEditorWidget::createManagedInstance()
+	{
+		const char* assemblies[2] = { EDITOR_ASSEMBLY, SCRIPT_EDITOR_ASSEMBLY };
+		UINT32 numAssemblies = sizeof(assemblies) / sizeof(assemblies[0]);
+
+		for (UINT32 i = 0; i < numAssemblies; i++)
+		{
+			MonoAssembly* assembly = MonoManager::instance().getAssembly(assemblies[i]);
+
+			if (assembly != nullptr)
+			{
+				MonoClass* editorWindowClass = assembly->getClass(mNamespace, mTypename);
+
+				if (editorWindowClass != nullptr)
+				{
+					mManagedInstance = editorWindowClass->createInstance();
+
+					MonoObject* guiPanel = ScriptGUIPanel::createFromExisting(mContent);
+					mContentsPanel = ScriptGUILayout::toNative(guiPanel);
+					ScriptEditorWindow::guiPanelField->setValue(mManagedInstance, guiPanel);
+
+					reloadMonoTypes(editorWindowClass);
+					return true;
+				}
+			}
+		}
+
+		return false;
+	}
+
+	void ScriptEditorWidget::triggerOnInitialize()
+	{
+		if (mOnInitializeThunk != nullptr && mManagedInstance != nullptr)
+		{
+			// Note: Not calling virtual methods. Can be easily done if needed but for now doing this
+			// for some extra speed.
+			MonoUtil::invokeThunk(mOnInitializeThunk, mManagedInstance);
+		}
+
+		mIsInitialized = true;
+	}
+
+	void ScriptEditorWidget::triggerOnDestroy()
+	{
+		if (mOnDestroyThunk != nullptr && mManagedInstance != nullptr)
+		{
+			// Note: Not calling virtual methods. Can be easily done if needed but for now doing this
+			// for some extra speed.
+			MonoUtil::invokeThunk(mOnDestroyThunk, mManagedInstance);
+		}
+
+		mIsInitialized = false;
+	}
+
+	void ScriptEditorWidget::update()
+	{
+		if (!mIsInitialized)
+			triggerOnInitialize();
+
+		if (mUpdateThunk != nullptr && mManagedInstance != nullptr)
+		{
+			// Note: Not calling virtual methods. Can be easily done if needed but for now doing this
+			// for some extra speed.
+			MonoUtil::invokeThunk(mUpdateThunk, mManagedInstance);
+		}
+	}
+
+	void ScriptEditorWidget::reloadMonoTypes(MonoClass* windowClass)
+	{
+		MonoMethod* updateMethod = windowClass->getMethod("OnEditorUpdate", 0);
+
+		if (updateMethod != nullptr)
+			mUpdateThunk = (UpdateThunkDef)updateMethod->getThunk();
+
+		MonoMethod* onInitializeMethod = windowClass->getMethod("OnInitialize", 0);
+
+		if (onInitializeMethod != nullptr)
+			mOnInitializeThunk = (OnInitializeThunkDef)onInitializeMethod->getThunk();
+
+		MonoMethod* onDestroyMethod = windowClass->getMethod("OnDestroy", 0);
+
+		if (onDestroyMethod != nullptr)
+			mOnDestroyThunk = (OnDestroyThunkDef)onDestroyMethod->getThunk();
+
+		mGetDisplayName = windowClass->getMethod("GetDisplayName", 0);
+	}
 }

+ 2 - 0
Source/SBansheeEngine/Include/BsScriptGUIElement.h

@@ -128,9 +128,11 @@ namespace BansheeEngine
 		static void internal_setActive(ScriptGUIElementBaseTBase* nativeInstance, bool active);
 		static void internal_setDisabled(ScriptGUIElementBaseTBase* nativeInstance, bool disabled);
 		static void internal_setFocus(ScriptGUIElementBaseTBase* nativeInstance, bool focus);
+		static void internal_setBlocking(ScriptGUIElementBaseTBase* nativeInstance, bool blocking);
 		static bool internal_getVisible(ScriptGUIElementBaseTBase* nativeInstance);
 		static bool internal_getActive(ScriptGUIElementBaseTBase* nativeInstance);
 		static bool internal_getDisabled(ScriptGUIElementBaseTBase* nativeInstance);
+		static bool internal_getBlocking(ScriptGUIElementBaseTBase* nativeInstance);
 		static MonoObject* internal_getParent(ScriptGUIElementBaseTBase* nativeInstance);
 		static void internal_getBounds(ScriptGUIElementBaseTBase* nativeInstance, Rect2I* bounds);
 		static void internal_setBounds(ScriptGUIElementBaseTBase* nativeInstance, Rect2I* bounds);

+ 343 - 313
Source/SBansheeEngine/Source/BsScriptGUIElement.cpp

@@ -1,314 +1,344 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsScriptGUIElement.h"
-#include "BsScriptMeta.h"
-#include "BsMonoField.h"
-#include "BsMonoClass.h"
-#include "BsMonoManager.h"
-#include "BsMonoMethod.h"
-#include "BsMonoUtil.h"
-#include "BsGUIElement.h"
-#include "BsScriptGUILayout.h"
-#include "BsScriptContextMenu.h"
-#include "BsGUIElement.h"
-
-using namespace std::placeholders;
-
-namespace BansheeEngine
-{
-	ScriptGUIElementBaseTBase::ScriptGUIElementBaseTBase(MonoObject* instance)
-		:ScriptObjectBase(instance), mIsDestroyed(false), mElement(nullptr), mParent(nullptr)
-	{ }
-
-	void ScriptGUIElementBaseTBase::initialize(GUIElementBase* element)
-	{
-		mElement = element;
-
-		if (mElement != nullptr && mElement->_getType() == GUIElementBase::Type::Element)
-		{
-			GUIElement* guiElem = static_cast<GUIElement*>(element);
-			guiElem->onFocusChanged.connect(std::bind(&ScriptGUIElementBaseTBase::onFocusChanged, mManagedInstance, _1));
-		}
-	}
-
-	void ScriptGUIElementBaseTBase::onFocusChanged(MonoObject* instance, bool focus)
-	{
-		if (focus)
-			MonoUtil::invokeThunk(ScriptGUIElement::onFocusGainedThunk, instance);
-		else
-			MonoUtil::invokeThunk(ScriptGUIElement::onFocusLostThunk, instance);
-	}
-
-	void ScriptGUIElementBaseTBase::_onManagedInstanceDeleted()
-	{
-		destroy();
-
-		ScriptObjectBase::_onManagedInstanceDeleted();
-	}
-
-	ScriptGUIElementTBase::ScriptGUIElementTBase(MonoObject* instance)
-		:ScriptGUIElementBaseTBase(instance)
-	{
-
-	}
-
-	void ScriptGUIElementTBase::destroy()
-	{
-		if(!mIsDestroyed)
-		{
-			if (mParent != nullptr)
-				mParent->removeChild(this);
-
-			if (mElement->_getType() == GUIElementBase::Type::Element)
-			{
-				GUIElement::destroy((GUIElement*)mElement);
-				mElement = nullptr;
-
-				mIsDestroyed = true;
-			}
-		}
-	}
-
-	ScriptGUIElement::OnFocusChangedThunkDef ScriptGUIElement::onFocusGainedThunk;
-	ScriptGUIElement::OnFocusChangedThunkDef ScriptGUIElement::onFocusLostThunk;
-
-	ScriptGUIElement::ScriptGUIElement(MonoObject* instance)
-		:ScriptObject(instance)
-	{
-
-	}
-
-	void ScriptGUIElement::initRuntimeData()
-	{
-		metaData.scriptClass->addInternalCall("Internal_Destroy", &ScriptGUIElement::internal_destroy);
-		metaData.scriptClass->addInternalCall("Internal_SetVisible", &ScriptGUIElement::internal_setVisible);
-		metaData.scriptClass->addInternalCall("Internal_SetActive", &ScriptGUIElement::internal_setActive);
-		metaData.scriptClass->addInternalCall("Internal_SetDisabled", &ScriptGUIElement::internal_setDisabled);
-		metaData.scriptClass->addInternalCall("Internal_GetVisible", &ScriptGUIElement::internal_getVisible);
-		metaData.scriptClass->addInternalCall("Internal_GetActive", &ScriptGUIElement::internal_getActive);
-		metaData.scriptClass->addInternalCall("Internal_GetDisabled", &ScriptGUIElement::internal_getDisabled);
-		metaData.scriptClass->addInternalCall("Internal_SetFocus", &ScriptGUIElement::internal_setFocus);
-		metaData.scriptClass->addInternalCall("Internal_GetParent", &ScriptGUIElement::internal_getParent);
-		metaData.scriptClass->addInternalCall("Internal_GetBounds", &ScriptGUIElement::internal_getBounds);
-		metaData.scriptClass->addInternalCall("Internal_SetBounds", &ScriptGUIElement::internal_setBounds);
-		metaData.scriptClass->addInternalCall("Internal_GetVisibleBounds", &ScriptGUIElement::internal_getVisibleBounds);
-		metaData.scriptClass->addInternalCall("Internal_SetPosition", &ScriptGUIElement::internal_SetPosition);
-		metaData.scriptClass->addInternalCall("Internal_SetWidth", &ScriptGUIElement::internal_SetWidth);
-		metaData.scriptClass->addInternalCall("Internal_SetFlexibleWidth", &ScriptGUIElement::internal_SetFlexibleWidth);
-		metaData.scriptClass->addInternalCall("Internal_SetHeight", &ScriptGUIElement::internal_SetHeight);
-		metaData.scriptClass->addInternalCall("Internal_SetFlexibleHeight", &ScriptGUIElement::internal_SetFlexibleHeight);
-		metaData.scriptClass->addInternalCall("Internal_ResetDimensions", &ScriptGUIElement::internal_ResetDimensions);
-		metaData.scriptClass->addInternalCall("Internal_SetContextMenu", &ScriptGUIElement::internal_SetContextMenu);
-		metaData.scriptClass->addInternalCall("Internal_GetStyle", &ScriptGUIElement::internal_GetStyle);
-		metaData.scriptClass->addInternalCall("Internal_SetStyle", &ScriptGUIElement::internal_SetStyle);
-
-		onFocusGainedThunk = (OnFocusChangedThunkDef)metaData.scriptClass->getMethod("Internal_OnFocusGained", 0)->getThunk();
-		onFocusLostThunk = (OnFocusChangedThunkDef)metaData.scriptClass->getMethod("Internal_OnFocusLost", 0)->getThunk();
-	}
-
-	void ScriptGUIElement::internal_destroy(ScriptGUIElementBaseTBase* nativeInstance)
-	{
-		nativeInstance->destroy();
-	}
-
-	void ScriptGUIElement::internal_setVisible(ScriptGUIElementBaseTBase* nativeInstance, bool visible)
-	{
-		if (nativeInstance->isDestroyed())
-			return;
-
-		nativeInstance->getGUIElement()->setVisible(visible);
-	}
-
-	void ScriptGUIElement::internal_setActive(ScriptGUIElementBaseTBase* nativeInstance, bool enabled)
-	{
-		if (nativeInstance->isDestroyed())
-			return;
-
-		nativeInstance->getGUIElement()->setActive(enabled);
-	}
-
-	void ScriptGUIElement::internal_setDisabled(ScriptGUIElementBaseTBase* nativeInstance, bool disabled)
-	{
-		if (nativeInstance->isDestroyed())
-			return;
-
-		nativeInstance->getGUIElement()->setDisabled(disabled);
-	}
-
-	void ScriptGUIElement::internal_setFocus(ScriptGUIElementBaseTBase* nativeInstance, bool focus)
-	{
-		if (nativeInstance->isDestroyed())
-			return;
-
-		GUIElementBase* guiElemBase = nativeInstance->getGUIElement();
-		if (guiElemBase->_getType() == GUIElementBase::Type::Element)
-		{
-			GUIElement* guiElem = static_cast<GUIElement*>(guiElemBase);
-			guiElem->setFocus(focus);
-		}		
-	}
-
-	bool ScriptGUIElement::internal_getVisible(ScriptGUIElementBaseTBase* nativeInstance)
-	{
-		if (nativeInstance->isDestroyed())
-			return false;
-
-		GUIElementBase* guiElemBase = nativeInstance->getGUIElement();
-		return guiElemBase->_isVisible();
-	}
-
-	bool ScriptGUIElement::internal_getActive(ScriptGUIElementBaseTBase* nativeInstance)
-	{
-		if (nativeInstance->isDestroyed())
-			return false;
-
-		GUIElementBase* guiElemBase = nativeInstance->getGUIElement();
-		return guiElemBase->_isActive();
-	}
-
-	bool ScriptGUIElement::internal_getDisabled(ScriptGUIElementBaseTBase* nativeInstance)
-	{
-		if (nativeInstance->isDestroyed())
-			return false;
-
-		GUIElementBase* guiElemBase = nativeInstance->getGUIElement();
-		return guiElemBase->_isDisabled();
-	}
-
-	MonoObject* ScriptGUIElement::internal_getParent(ScriptGUIElementBaseTBase* nativeInstance)
-	{
-		if (nativeInstance->isDestroyed())
-			return nullptr;
-
-		if (nativeInstance->getParent() != nullptr)
-			return nativeInstance->getParent()->getManagedInstance();
-
-		return nullptr;
-	}
-
-	void ScriptGUIElement::internal_getBounds(ScriptGUIElementBaseTBase* nativeInstance, Rect2I* bounds)
-	{
-		if (nativeInstance->isDestroyed())
-		{
-			*bounds = Rect2I();
-			return;
-		}
-
-		*bounds = nativeInstance->getGUIElement()->getBounds();
-	}
-
-	void ScriptGUIElement::internal_setBounds(ScriptGUIElementBaseTBase* nativeInstance, Rect2I* bounds)
-	{
-		if (nativeInstance->isDestroyed())
-			return;
-
-		nativeInstance->getGUIElement()->setPosition(bounds->x, bounds->y);
-		nativeInstance->getGUIElement()->setWidth(bounds->width);
-		nativeInstance->getGUIElement()->setHeight(bounds->height);
-	}
-
-	void ScriptGUIElement::internal_getVisibleBounds(ScriptGUIElementBaseTBase* nativeInstance, Rect2I* bounds)
-	{
-		if (nativeInstance->isDestroyed())
-		{
-			*bounds = Rect2I();
-			return;
-		}
-
-		*bounds = nativeInstance->getGUIElement()->getVisibleBounds();
-	}
-
-	void ScriptGUIElement::internal_SetPosition(ScriptGUIElementBaseTBase* nativeInstance, INT32 x, INT32 y)
-	{
-		if (nativeInstance->isDestroyed())
-			return;
-
-		nativeInstance->getGUIElement()->setPosition(x, y);
-	}
-
-	void ScriptGUIElement::internal_SetWidth(ScriptGUIElementBaseTBase* nativeInstance, UINT32 width)
-	{
-		if (nativeInstance->isDestroyed())
-			return;
-
-		nativeInstance->getGUIElement()->setWidth(width);
-	}
-
-	void ScriptGUIElement::internal_SetFlexibleWidth(ScriptGUIElementBaseTBase* nativeInstance, UINT32 minWidth, UINT32 maxWidth)
-	{
-		if (nativeInstance->isDestroyed())
-			return;
-
-		nativeInstance->getGUIElement()->setFlexibleWidth(minWidth, maxWidth);
-	}
-
-	void ScriptGUIElement::internal_SetHeight(ScriptGUIElementBaseTBase* nativeInstance, UINT32 height)
-	{
-		if (nativeInstance->isDestroyed())
-			return;
-
-		nativeInstance->getGUIElement()->setHeight(height);
-	}
-
-	void ScriptGUIElement::internal_SetFlexibleHeight(ScriptGUIElementBaseTBase* nativeInstance, UINT32 minHeight, UINT32 maxHeight)
-	{
-		if (nativeInstance->isDestroyed())
-			return;
-
-		nativeInstance->getGUIElement()->setFlexibleHeight(minHeight, maxHeight);
-	}
-
-	void ScriptGUIElement::internal_ResetDimensions(ScriptGUIElementBaseTBase* nativeInstance)
-	{
-		if (nativeInstance->isDestroyed())
-			return;
-
-		nativeInstance->getGUIElement()->resetDimensions();
-	}
-
-	void ScriptGUIElement::internal_SetContextMenu(ScriptGUIElementBaseTBase* nativeInstance, ScriptContextMenu* contextMenu)
-	{
-		if (nativeInstance->isDestroyed())
-			return;
-
-		GUIElementBase* guiElemBase = nativeInstance->getGUIElement();
-		if (guiElemBase->_getType() == GUIElementBase::Type::Element)
-		{
-			GUIElement* guiElem = static_cast<GUIElement*>(guiElemBase);
-
-			GUIContextMenuPtr nativeContextMenu;
-			if (contextMenu != nullptr)
-				nativeContextMenu = contextMenu->getInternal();
-
-			guiElem->setContextMenu(nativeContextMenu);
-		}
-	}
-
-	MonoString* ScriptGUIElement::internal_GetStyle(ScriptGUIElementBaseTBase* nativeInstance)
-	{
-		if (!nativeInstance->isDestroyed())
-		{
-			GUIElementBase* guiElemBase = nativeInstance->getGUIElement();
-			if (guiElemBase->_getType() == GUIElementBase::Type::Element)
-			{
-				GUIElement* guiElem = static_cast<GUIElement*>(guiElemBase);
-				return MonoUtil::stringToMono(guiElem->getStyleName());
-			}
-		}
-
-		return MonoUtil::stringToMono(StringUtil::BLANK);
-	}
-
-	void ScriptGUIElement::internal_SetStyle(ScriptGUIElementBaseTBase* nativeInstance, MonoString* style)
-	{
-		if (!nativeInstance->isDestroyed())
-		{
-			GUIElementBase* guiElemBase = nativeInstance->getGUIElement();
-			if (guiElemBase->_getType() == GUIElementBase::Type::Element)
-			{
-				GUIElement* guiElem = static_cast<GUIElement*>(guiElemBase);
-				guiElem->setStyle(MonoUtil::monoToString(style));
-			}
-		}
-	}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsScriptGUIElement.h"
+#include "BsScriptMeta.h"
+#include "BsMonoField.h"
+#include "BsMonoClass.h"
+#include "BsMonoManager.h"
+#include "BsMonoMethod.h"
+#include "BsMonoUtil.h"
+#include "BsGUIElement.h"
+#include "BsScriptGUILayout.h"
+#include "BsScriptContextMenu.h"
+#include "BsGUIElement.h"
+
+using namespace std::placeholders;
+
+namespace BansheeEngine
+{
+	ScriptGUIElementBaseTBase::ScriptGUIElementBaseTBase(MonoObject* instance)
+		:ScriptObjectBase(instance), mIsDestroyed(false), mElement(nullptr), mParent(nullptr)
+	{ }
+
+	void ScriptGUIElementBaseTBase::initialize(GUIElementBase* element)
+	{
+		mElement = element;
+
+		if (mElement != nullptr && mElement->_getType() == GUIElementBase::Type::Element)
+		{
+			GUIElement* guiElem = static_cast<GUIElement*>(element);
+			guiElem->onFocusChanged.connect(std::bind(&ScriptGUIElementBaseTBase::onFocusChanged, mManagedInstance, _1));
+		}
+	}
+
+	void ScriptGUIElementBaseTBase::onFocusChanged(MonoObject* instance, bool focus)
+	{
+		if (focus)
+			MonoUtil::invokeThunk(ScriptGUIElement::onFocusGainedThunk, instance);
+		else
+			MonoUtil::invokeThunk(ScriptGUIElement::onFocusLostThunk, instance);
+	}
+
+	void ScriptGUIElementBaseTBase::_onManagedInstanceDeleted()
+	{
+		destroy();
+
+		ScriptObjectBase::_onManagedInstanceDeleted();
+	}
+
+	ScriptGUIElementTBase::ScriptGUIElementTBase(MonoObject* instance)
+		:ScriptGUIElementBaseTBase(instance)
+	{
+
+	}
+
+	void ScriptGUIElementTBase::destroy()
+	{
+		if(!mIsDestroyed)
+		{
+			if (mParent != nullptr)
+				mParent->removeChild(this);
+
+			if (mElement->_getType() == GUIElementBase::Type::Element)
+			{
+				GUIElement::destroy((GUIElement*)mElement);
+				mElement = nullptr;
+
+				mIsDestroyed = true;
+			}
+		}
+	}
+
+	ScriptGUIElement::OnFocusChangedThunkDef ScriptGUIElement::onFocusGainedThunk;
+	ScriptGUIElement::OnFocusChangedThunkDef ScriptGUIElement::onFocusLostThunk;
+
+	ScriptGUIElement::ScriptGUIElement(MonoObject* instance)
+		:ScriptObject(instance)
+	{
+
+	}
+
+	void ScriptGUIElement::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_Destroy", &ScriptGUIElement::internal_destroy);
+		metaData.scriptClass->addInternalCall("Internal_SetVisible", &ScriptGUIElement::internal_setVisible);
+		metaData.scriptClass->addInternalCall("Internal_SetActive", &ScriptGUIElement::internal_setActive);
+		metaData.scriptClass->addInternalCall("Internal_SetDisabled", &ScriptGUIElement::internal_setDisabled);
+		metaData.scriptClass->addInternalCall("Internal_GetVisible", &ScriptGUIElement::internal_getVisible);
+		metaData.scriptClass->addInternalCall("Internal_GetActive", &ScriptGUIElement::internal_getActive);
+		metaData.scriptClass->addInternalCall("Internal_GetDisabled", &ScriptGUIElement::internal_getDisabled);
+		metaData.scriptClass->addInternalCall("Internal_SetFocus", &ScriptGUIElement::internal_setFocus);
+		metaData.scriptClass->addInternalCall("Internal_SetFocus", &ScriptGUIElement::internal_setFocus);
+		metaData.scriptClass->addInternalCall("Internal_GetBlocking", &ScriptGUIElement::internal_getBlocking);
+		metaData.scriptClass->addInternalCall("Internal_SetBlocking", &ScriptGUIElement::internal_setBlocking);
+		metaData.scriptClass->addInternalCall("Internal_GetBounds", &ScriptGUIElement::internal_getBounds);
+		metaData.scriptClass->addInternalCall("Internal_SetBounds", &ScriptGUIElement::internal_setBounds);
+		metaData.scriptClass->addInternalCall("Internal_GetVisibleBounds", &ScriptGUIElement::internal_getVisibleBounds);
+		metaData.scriptClass->addInternalCall("Internal_SetPosition", &ScriptGUIElement::internal_SetPosition);
+		metaData.scriptClass->addInternalCall("Internal_SetWidth", &ScriptGUIElement::internal_SetWidth);
+		metaData.scriptClass->addInternalCall("Internal_SetFlexibleWidth", &ScriptGUIElement::internal_SetFlexibleWidth);
+		metaData.scriptClass->addInternalCall("Internal_SetHeight", &ScriptGUIElement::internal_SetHeight);
+		metaData.scriptClass->addInternalCall("Internal_SetFlexibleHeight", &ScriptGUIElement::internal_SetFlexibleHeight);
+		metaData.scriptClass->addInternalCall("Internal_ResetDimensions", &ScriptGUIElement::internal_ResetDimensions);
+		metaData.scriptClass->addInternalCall("Internal_SetContextMenu", &ScriptGUIElement::internal_SetContextMenu);
+		metaData.scriptClass->addInternalCall("Internal_GetStyle", &ScriptGUIElement::internal_GetStyle);
+		metaData.scriptClass->addInternalCall("Internal_SetStyle", &ScriptGUIElement::internal_SetStyle);
+
+		onFocusGainedThunk = (OnFocusChangedThunkDef)metaData.scriptClass->getMethod("Internal_OnFocusGained", 0)->getThunk();
+		onFocusLostThunk = (OnFocusChangedThunkDef)metaData.scriptClass->getMethod("Internal_OnFocusLost", 0)->getThunk();
+	}
+
+	void ScriptGUIElement::internal_destroy(ScriptGUIElementBaseTBase* nativeInstance)
+	{
+		nativeInstance->destroy();
+	}
+
+	void ScriptGUIElement::internal_setVisible(ScriptGUIElementBaseTBase* nativeInstance, bool visible)
+	{
+		if (nativeInstance->isDestroyed())
+			return;
+
+		nativeInstance->getGUIElement()->setVisible(visible);
+	}
+
+	void ScriptGUIElement::internal_setActive(ScriptGUIElementBaseTBase* nativeInstance, bool enabled)
+	{
+		if (nativeInstance->isDestroyed())
+			return;
+
+		nativeInstance->getGUIElement()->setActive(enabled);
+	}
+
+	void ScriptGUIElement::internal_setDisabled(ScriptGUIElementBaseTBase* nativeInstance, bool disabled)
+	{
+		if (nativeInstance->isDestroyed())
+			return;
+
+		nativeInstance->getGUIElement()->setDisabled(disabled);
+	}
+
+	void ScriptGUIElement::internal_setFocus(ScriptGUIElementBaseTBase* nativeInstance, bool focus)
+	{
+		if (nativeInstance->isDestroyed())
+			return;
+
+		GUIElementBase* guiElemBase = nativeInstance->getGUIElement();
+		if (guiElemBase->_getType() == GUIElementBase::Type::Element)
+		{
+			GUIElement* guiElem = static_cast<GUIElement*>(guiElemBase);
+			guiElem->setFocus(focus);
+		}		
+	}
+
+	bool ScriptGUIElement::internal_getVisible(ScriptGUIElementBaseTBase* nativeInstance)
+	{
+		if (nativeInstance->isDestroyed())
+			return false;
+
+		GUIElementBase* guiElemBase = nativeInstance->getGUIElement();
+		return guiElemBase->_isVisible();
+	}
+
+	bool ScriptGUIElement::internal_getActive(ScriptGUIElementBaseTBase* nativeInstance)
+	{
+		if (nativeInstance->isDestroyed())
+			return false;
+
+		GUIElementBase* guiElemBase = nativeInstance->getGUIElement();
+		return guiElemBase->_isActive();
+	}
+
+	bool ScriptGUIElement::internal_getDisabled(ScriptGUIElementBaseTBase* nativeInstance)
+	{
+		if (nativeInstance->isDestroyed())
+			return false;
+
+		GUIElementBase* guiElemBase = nativeInstance->getGUIElement();
+		return guiElemBase->_isDisabled();
+	}
+
+	bool ScriptGUIElement::internal_getBlocking(ScriptGUIElementBaseTBase* nativeInstance)
+	{
+		if (nativeInstance->isDestroyed())
+			return false;
+
+		GUIElementBase* guiElemBase = nativeInstance->getGUIElement();
+		if (guiElemBase->_getType() == GUIElementBase::Type::Element)
+		{
+			GUIElement* guiElem = static_cast<GUIElement*>(guiElemBase);
+			return guiElem->getBlockPointerEvents();
+		}
+
+		return false;
+	}
+
+	void ScriptGUIElement::internal_setBlocking(ScriptGUIElementBaseTBase* nativeInstance, bool blocking)
+	{
+		if (nativeInstance->isDestroyed())
+			return;
+
+		GUIElementBase* guiElemBase = nativeInstance->getGUIElement();
+		if (guiElemBase->_getType() == GUIElementBase::Type::Element)
+		{
+			GUIElement* guiElem = static_cast<GUIElement*>(guiElemBase);
+			guiElem->setBlockPointerEvents(blocking);
+		}
+	}
+
+	MonoObject* ScriptGUIElement::internal_getParent(ScriptGUIElementBaseTBase* nativeInstance)
+	{
+		if (nativeInstance->isDestroyed())
+			return nullptr;
+
+		if (nativeInstance->getParent() != nullptr)
+			return nativeInstance->getParent()->getManagedInstance();
+
+		return nullptr;
+	}
+
+	void ScriptGUIElement::internal_getBounds(ScriptGUIElementBaseTBase* nativeInstance, Rect2I* bounds)
+	{
+		if (nativeInstance->isDestroyed())
+		{
+			*bounds = Rect2I();
+			return;
+		}
+
+		*bounds = nativeInstance->getGUIElement()->getBounds();
+	}
+
+	void ScriptGUIElement::internal_setBounds(ScriptGUIElementBaseTBase* nativeInstance, Rect2I* bounds)
+	{
+		if (nativeInstance->isDestroyed())
+			return;
+
+		nativeInstance->getGUIElement()->setPosition(bounds->x, bounds->y);
+		nativeInstance->getGUIElement()->setWidth(bounds->width);
+		nativeInstance->getGUIElement()->setHeight(bounds->height);
+	}
+
+	void ScriptGUIElement::internal_getVisibleBounds(ScriptGUIElementBaseTBase* nativeInstance, Rect2I* bounds)
+	{
+		if (nativeInstance->isDestroyed())
+		{
+			*bounds = Rect2I();
+			return;
+		}
+
+		*bounds = nativeInstance->getGUIElement()->getVisibleBounds();
+	}
+
+	void ScriptGUIElement::internal_SetPosition(ScriptGUIElementBaseTBase* nativeInstance, INT32 x, INT32 y)
+	{
+		if (nativeInstance->isDestroyed())
+			return;
+
+		nativeInstance->getGUIElement()->setPosition(x, y);
+	}
+
+	void ScriptGUIElement::internal_SetWidth(ScriptGUIElementBaseTBase* nativeInstance, UINT32 width)
+	{
+		if (nativeInstance->isDestroyed())
+			return;
+
+		nativeInstance->getGUIElement()->setWidth(width);
+	}
+
+	void ScriptGUIElement::internal_SetFlexibleWidth(ScriptGUIElementBaseTBase* nativeInstance, UINT32 minWidth, UINT32 maxWidth)
+	{
+		if (nativeInstance->isDestroyed())
+			return;
+
+		nativeInstance->getGUIElement()->setFlexibleWidth(minWidth, maxWidth);
+	}
+
+	void ScriptGUIElement::internal_SetHeight(ScriptGUIElementBaseTBase* nativeInstance, UINT32 height)
+	{
+		if (nativeInstance->isDestroyed())
+			return;
+
+		nativeInstance->getGUIElement()->setHeight(height);
+	}
+
+	void ScriptGUIElement::internal_SetFlexibleHeight(ScriptGUIElementBaseTBase* nativeInstance, UINT32 minHeight, UINT32 maxHeight)
+	{
+		if (nativeInstance->isDestroyed())
+			return;
+
+		nativeInstance->getGUIElement()->setFlexibleHeight(minHeight, maxHeight);
+	}
+
+	void ScriptGUIElement::internal_ResetDimensions(ScriptGUIElementBaseTBase* nativeInstance)
+	{
+		if (nativeInstance->isDestroyed())
+			return;
+
+		nativeInstance->getGUIElement()->resetDimensions();
+	}
+
+	void ScriptGUIElement::internal_SetContextMenu(ScriptGUIElementBaseTBase* nativeInstance, ScriptContextMenu* contextMenu)
+	{
+		if (nativeInstance->isDestroyed())
+			return;
+
+		GUIElementBase* guiElemBase = nativeInstance->getGUIElement();
+		if (guiElemBase->_getType() == GUIElementBase::Type::Element)
+		{
+			GUIElement* guiElem = static_cast<GUIElement*>(guiElemBase);
+
+			GUIContextMenuPtr nativeContextMenu;
+			if (contextMenu != nullptr)
+				nativeContextMenu = contextMenu->getInternal();
+
+			guiElem->setContextMenu(nativeContextMenu);
+		}
+	}
+
+	MonoString* ScriptGUIElement::internal_GetStyle(ScriptGUIElementBaseTBase* nativeInstance)
+	{
+		if (!nativeInstance->isDestroyed())
+		{
+			GUIElementBase* guiElemBase = nativeInstance->getGUIElement();
+			if (guiElemBase->_getType() == GUIElementBase::Type::Element)
+			{
+				GUIElement* guiElem = static_cast<GUIElement*>(guiElemBase);
+				return MonoUtil::stringToMono(guiElem->getStyleName());
+			}
+		}
+
+		return MonoUtil::stringToMono(StringUtil::BLANK);
+	}
+
+	void ScriptGUIElement::internal_SetStyle(ScriptGUIElementBaseTBase* nativeInstance, MonoString* style)
+	{
+		if (!nativeInstance->isDestroyed())
+		{
+			GUIElementBase* guiElemBase = nativeInstance->getGUIElement();
+			if (guiElemBase->_getType() == GUIElementBase::Type::Element)
+			{
+				GUIElement* guiElem = static_cast<GUIElement*>(guiElemBase);
+				guiElem->setStyle(MonoUtil::monoToString(style));
+			}
+		}
+	}
 }