Explorar o código

More work on modal windows & progress bar window

Marko Pintera %!s(int64=11) %!d(string=hai) anos
pai
achega
3f3075d532

+ 1 - 0
BansheeEditor/Include/BsEditorWindowBase.h

@@ -52,6 +52,7 @@ namespace BansheeEngine
 		HCamera mCamera;
 		GameObjectHandle<WindowFrameWidget> mWindowFrame;
 		bool mOwnsRenderWindow;
+		bool mIsModal;
 
 		void construct(const RenderWindowPtr& renderWindow);
 		virtual void initialize();

+ 0 - 1
BansheeEditor/Include/BsEditorWindowManager.h

@@ -14,7 +14,6 @@ namespace BansheeEngine
 
 		MainEditorWindow* createMain(const RenderWindowPtr& parentRenderWindow);
 		EditorWindow* create();
-		ModalWindow* createModal();
 		void destroy(EditorWindowBase* window);
 
 		MainEditorWindow* getMainWindow() const { return mMainWindow; }

+ 2 - 1
BansheeEditor/Include/BsGUIWindowFrameWidget.h

@@ -9,12 +9,13 @@ namespace BansheeEngine
 	class WindowFrameWidget : public GUIWidget
 	{
 	public:
-		WindowFrameWidget(const HSceneObject& parent, Viewport* target, RenderWindow* ownerWindow, const GUISkin& skin);
+		WindowFrameWidget(const HSceneObject& parent, bool allowResize, Viewport* target, RenderWindow* ownerWindow, const GUISkin& skin);
 		virtual ~WindowFrameWidget();
 
 	protected:
 		static const UINT32 RESIZE_BORDER_WIDTH;
 
+		bool mAllowResize;
 		GUIArea* mWindowFrameArea;
 		RenderWindow* mParentWindow;
 		GUIWindowFrame* mWindowFrame;

+ 12 - 3
BansheeEditor/Include/BsModalWindow.h

@@ -11,16 +11,25 @@ namespace BansheeEngine
 		virtual ~ModalWindow();
 
 		virtual void update();
-
-		static ModalWindow* create();
+		virtual void close();
+		void setTitle(const HString& title);
 
 	protected:
 		friend class EditorWindowManager;
 
-		ModalWindow();
+		ModalWindow(const HString& title, bool hasCloseButton = false);
+		Rect2I getContentArea() const;
 
 		virtual void resized();
 	private:
 		void updateSize();
+		UINT32 getTitleBarHeight() const;
+
+		GUIArea* mTitleBarArea;
+		GUIArea* mTitleBarBgArea;
+
+		GUILabel* mTitle;
+		GUIButton* mCloseButton;
+		GUITexture* mTitleBarBg;
 	};
 }

+ 2 - 2
BansheeEditor/Source/BsEditorWindowBase.cpp

@@ -12,7 +12,7 @@
 namespace BansheeEngine
 {
 	EditorWindowBase::EditorWindowBase(bool isModal)
-		:mOwnsRenderWindow(true)
+		:mOwnsRenderWindow(true), mIsModal(isModal)
 	{
 		RENDER_WINDOW_DESC renderWindowDesc;
 		renderWindowDesc.videoMode = VideoMode(200, 200);
@@ -74,7 +74,7 @@ namespace BansheeEngine
 
 		mGUI->setSkin(BuiltinEditorResources::instance().getSkin());
 
-		mWindowFrame = mSceneObject->addComponent<WindowFrameWidget>(mCamera->getViewport().get(), renderWindow.get(), BuiltinEditorResources::instance().getSkin());
+		mWindowFrame = mSceneObject->addComponent<WindowFrameWidget>(!mIsModal, mCamera->getViewport().get(), renderWindow.get(), BuiltinEditorResources::instance().getSkin());
 		mWindowFrame->setDepth(129);
 
 		mResizedConn = renderWindow->onResized.connect(std::bind(&EditorWindowBase::resized, this));

+ 0 - 10
BansheeEditor/Source/BsEditorWindowManager.cpp

@@ -1,6 +1,5 @@
 #include "BsEditorWindowManager.h"
 #include "BsEditorWindow.h"
-#include "BsModalWindow.h"
 #include "BsMainEditorWindow.h"
 
 namespace BansheeEngine
@@ -42,15 +41,6 @@ namespace BansheeEngine
 		return newWindow;
 	}
 
-	ModalWindow* EditorWindowManager::createModal()
-	{
-		ModalWindow* newWindow = new (bs_alloc<ModalWindow>()) ModalWindow();
-		mEditorWindows.push_back(newWindow);
-
-		newWindow->initialize();
-		return newWindow;
-	}
-
 	void EditorWindowManager::destroy(EditorWindowBase* window)
 	{
 		auto iterFind = std::find(begin(mEditorWindows), end(mEditorWindows), window);

+ 5 - 2
BansheeEditor/Source/BsGUIWindowFrameWidget.cpp

@@ -13,8 +13,8 @@ namespace BansheeEngine
 {
 	const UINT32 WindowFrameWidget::RESIZE_BORDER_WIDTH = 3;
 
-	WindowFrameWidget::WindowFrameWidget(const HSceneObject& parent, Viewport* target, RenderWindow* parentWindow, const GUISkin& skin)
-		:GUIWidget(parent, target), mWindowFrameArea(nullptr), mParentWindow(parentWindow)
+	WindowFrameWidget::WindowFrameWidget(const HSceneObject& parent, bool allowResize, Viewport* target, RenderWindow* parentWindow, const GUISkin& skin)
+		:GUIWidget(parent, target), mWindowFrameArea(nullptr), mParentWindow(parentWindow), mAllowResize(allowResize)
 	{
 		setSkin(skin);
 
@@ -61,6 +61,9 @@ namespace BansheeEngine
 
 	void WindowFrameWidget::refreshNonClientAreas() const
 	{
+		if (!mAllowResize)
+			return;
+
 		INT32 x = mWindowFrameArea->x();
 		INT32 y = mWindowFrameArea->y();
 

+ 55 - 5
BansheeEditor/Source/BsModalWindow.cpp

@@ -2,12 +2,47 @@
 #include "BsEditorWindowManager.h"
 #include "BsRenderWindow.h"
 #include "BsPlatform.h"
+#include "BsGUIArea.h"
+#include "BsGUILayoutX.h"
+#include "BsGUISpace.h"
+#include "BsGUIButton.h"
+#include "BsGUITexture.h"
+#include "BsGUILabel.h"
 
 namespace BansheeEngine
 {
-	ModalWindow::ModalWindow()
-		:EditorWindowBase(true)
+	ModalWindow::ModalWindow(const HString& title, bool hasCloseButton)
+		:EditorWindowBase(true), mTitleBarArea(nullptr), mTitleBarBgArea(nullptr), 
+		mCloseButton(nullptr), mTitleBarBg(nullptr), mTitle(nullptr)
 	{
+		mTitleBarBgArea = GUIArea::createStretchedXY(*mGUI, 1, 1, 1, 1, 1);
+		mTitleBarArea = GUIArea::createStretchedXY(*mGUI, 1, 1, 1, 1, 0);
+
+		mTitleBarBg = GUITexture::create(GUIOptions(GUIOption::flexibleWidth()), "TitleBarBackground");
+		mTitle = GUILabel::create(title);
+
+		GUILayout& bgLayout = mTitleBarBgArea->getLayout().addLayoutX();
+		bgLayout.addElement(mTitleBarBg);
+		bgLayout.addFlexibleSpace();
+
+		GUILayout& contentLayout = mTitleBarArea->getLayout().addLayoutX();
+		contentLayout.addFlexibleSpace();
+		contentLayout.addElement(mTitle);
+		contentLayout.addFlexibleSpace();
+
+		if (hasCloseButton)
+		{
+			mCloseButton = GUIButton::create(HString(L""), "WinCloseBtn");
+
+			GUILayout& contentLayout = mTitleBarArea->getLayout().addLayoutX();
+			contentLayout.addFlexibleSpace();
+			contentLayout.addElement(mCloseButton);
+			
+			mCloseButton->onClick.connect(std::bind(&ModalWindow::close, this));
+		}
+
+		mTitleBarArea->getLayout().addFlexibleSpace();
+		
 		updateSize();
 	}
 
@@ -21,6 +56,16 @@ namespace BansheeEngine
 
 	}
 
+	void ModalWindow::close()
+	{
+		bs_delete(this);
+	}
+
+	void ModalWindow::setTitle(const HString& title)
+	{
+		mTitle->setContent(GUIContent(title));
+	}
+
 	void ModalWindow::resized()
 	{
 		EditorWindowBase::resized();
@@ -31,13 +76,18 @@ namespace BansheeEngine
 	void ModalWindow::updateSize()
 	{
 		Vector<Rect2I> captionAreas;
-		captionAreas.push_back(Rect2I(0, 0, getWidth(), 20));
+		captionAreas.push_back(Rect2I(1, 1, getWidth() - 2, getTitleBarHeight()));
 
 		Platform::setCaptionNonClientAreas(*mRenderWindow->getCore().get(), captionAreas);
 	}
 
-	ModalWindow* ModalWindow::create()
+	Rect2I ModalWindow::getContentArea() const
+	{
+		return Rect2I(1, 1 + getTitleBarHeight(), getWidth() - 2, getHeight() - getTitleBarHeight() - 2);
+	}
+
+	UINT32 ModalWindow::getTitleBarHeight() const
 	{
-		return EditorWindowManager::instance().createModal();
+		return mTitleBarBg->getBounds().height;
 	}
 }

+ 5 - 0
BansheeEngine/Include/BsGUITexture.h

@@ -200,6 +200,11 @@ namespace BansheeEngine
 		 */
 		virtual void updateClippedBounds();
 
+		/**
+		 * @copydoc GUIElement::styleUpdated
+		 */
+		virtual void styleUpdated();
+
 		ImageSprite* mImageSprite;
 		HSpriteTexture mActiveTexture;
 		IMAGE_SPRITE_DESC mDesc;

+ 9 - 3
BansheeEngine/Source/BsGUITexture.cpp

@@ -27,7 +27,10 @@ namespace BansheeEngine
 			mUsingStyleTexture = false;
 		}
 		else
+		{
+			mActiveTexture = _getStyle()->normal.texture;
 			mUsingStyleTexture = true;
+		}
 	}
 
 	GUITexture::~GUITexture()
@@ -134,9 +137,6 @@ namespace BansheeEngine
 		mDesc.borderBottom = _getStyle()->border.bottom;
 		mDesc.transparent = mTransparent;
 
-		if(mUsingStyleTexture)
-			mActiveTexture = _getStyle()->normal.texture;
-
 		float optimalWidth = 0.0f;
 		float optimalHeight = 0.0f;
 		if (SpriteTexture::checkIsLoaded(mActiveTexture))
@@ -201,6 +201,12 @@ namespace BansheeEngine
 		mClippedBounds = mImageSprite->getBounds(mOffset, mClipRect);
 	}
 
+	void GUITexture::styleUpdated()
+	{
+		if (mUsingStyleTexture)
+			mActiveTexture = _getStyle()->normal.texture;
+	}
+
 	Vector2I GUITexture::_getOptimalSize() const
 	{
 		Vector2I optimalSize;

+ 1 - 1
MBansheeEditor/EditorApplication.cs

@@ -95,7 +95,7 @@ namespace BansheeEditor
             SceneObject gizmoDbgObject = new SceneObject("GizmoDebug");
             gizmoDbgObject.AddComponent<DbgGizmoComponent>();
 
-            ModalWindow.Open<ProgressBar>();
+            ProgressBar.Show("Test", 0.5f);
 
             // DEBUG ONLY END
         }

+ 33 - 5
MBansheeEditor/ModalWindow.cs

@@ -7,14 +7,28 @@ namespace BansheeEditor
 {
     public class ModalWindow : ScriptObject
     {
-        public int Width { get { return Internal_GetWidth(mCachedPtr); } }
-        public int Height { get { return Internal_GetHeight(mCachedPtr); } }
+        public int Width
+        {
+            get { return Internal_GetWidth(mCachedPtr); }
+            set { Internal_SetWidth(mCachedPtr, value); }
+        }
+
+        public int Height
+        {
+            get { return Internal_GetHeight(mCachedPtr); }
+            set { Internal_SetHeight(mCachedPtr, value); }
+        }
 
         protected GUIPanel GUI;
 
-        public static T Open<T>() where T : ModalWindow
+        protected ModalWindow()
         {
-            return (T)Internal_CreateInstance(typeof(T).Namespace, typeof(T).Name);
+            Internal_CreateInstance(this, false);
+        }
+
+        protected ModalWindow(bool allowCloseButton)
+        {
+            Internal_CreateInstance(this, allowCloseButton);
         }
 
         private void OnInitializeInternal()
@@ -41,12 +55,20 @@ namespace BansheeEditor
             Internal_Close(mCachedPtr);
         }
 
+        public LocString Title
+        {
+            set { Internal_SetTitle(mCachedPtr, value); }
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern ModalWindow Internal_CreateInstance(string ns, string typeName);
+        private static extern void Internal_CreateInstance(ModalWindow instance, bool allowCloseButton);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_Close(IntPtr nativeInstance);
 
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetTitle(IntPtr nativeInstance, LocString title);
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_InitializeGUIPanel(IntPtr nativeInstance, GUIPanel panel);
 
@@ -56,7 +78,13 @@ namespace BansheeEditor
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern int Internal_GetWidth(IntPtr nativeInstance);
 
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern int Internal_SetWidth(IntPtr nativeInstance, int value);
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern int Internal_GetHeight(IntPtr nativeInstance);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern int Internal_SetHeight(IntPtr nativeInstance, int value);
     }
 }

+ 36 - 1
MBansheeEditor/ProgressBar.cs

@@ -5,12 +5,47 @@ namespace BansheeEditor
 {
     public class ProgressBar : ModalWindow
     {
+        private static ProgressBar instance;
+
         private GUIProgressBar progressBar;
 
+        public float Percent
+        {
+            set { progressBar.Percent = value; }
+            get { return progressBar.Percent; }
+        }
+
+        public static void Show(LocString title, float percent)
+        {
+            if (instance == null)
+                instance = new ProgressBar();
+
+            instance.Width = 200;
+            instance.Height = 100;
+
+            instance.Title = title;
+            instance.Percent = percent;
+        }
+
+        public static void Hide()
+        {
+            if (instance != null)
+                instance.Close();
+
+            instance = null;
+        }
+
+        protected ProgressBar()
+            :base(false)
+        { }
+
         private void OnInitialize()
         {
             progressBar = new GUIProgressBar();
-            progressBar.Percent = 0.5f; // DEBUG ONLY
+
+            GUI.layout.AddFlexibleSpace();
+            GUI.layout.AddElement(progressBar);
+            GUI.layout.AddFlexibleSpace();
         }
     }
 }

+ 7 - 2
SBansheeEditor/Include/BsScriptModalWindow.h

@@ -26,11 +26,14 @@ namespace BansheeEngine
 
 		ScriptModalWindow(ManagedModalWindow* editorWidget);
 
-		static MonoObject* internal_createInstance(MonoString* ns, MonoString* typeName);
+		static void internal_createInstance(MonoObject* instance, bool allowCloseButton);
 		static void internal_close(ScriptModalWindow* thisPtr);
 		static UINT32 internal_getWidth(ScriptModalWindow* thisPtr);
 		static UINT32 internal_getHeight(ScriptModalWindow* thisPtr);
+		static void internal_setWidth(ScriptModalWindow* thisPtr, UINT32 value);
+		static void internal_setHeight(ScriptModalWindow* thisPtr, UINT32 value);
 		static void internal_initializeGUIPanel(ScriptModalWindow* thisPtr, MonoObject* panel);
+		static void internal_setTitle(ScriptModalWindow* thisPtr, MonoObject* title);
 
 		void onAssemblyRefreshStarted();
 
@@ -38,6 +41,7 @@ namespace BansheeEngine
 		ScriptObjectBackup beginRefresh();
 		void endRefresh(const ScriptObjectBackup& backupData);
 		MonoObject* _createManagedInstance(bool construct);
+		void closeWindow();
 
 		ManagedModalWindow* mModalWindow;
 		ScriptGUIPanel* mGUIPanel;
@@ -51,7 +55,7 @@ namespace BansheeEngine
 	class BS_SCR_BED_EXPORT ManagedModalWindow : public ModalWindow
 	{
 	public:
-		ManagedModalWindow(const String& ns, const String& type);
+		ManagedModalWindow(bool allowCloseButton, MonoObject* managedInstance);
 		~ManagedModalWindow();
 
 		bool createManagedInstance();
@@ -66,6 +70,7 @@ namespace BansheeEngine
 		MonoObject* getManagedInstance() const { return mManagedInstance; }
 	protected:
 		virtual void resized();
+		virtual void close();
 
 	private:
 		friend class ScriptModalWindow;

+ 61 - 25
SBansheeEditor/Source/BsScriptModalWindow.cpp

@@ -8,6 +8,7 @@
 #include "BsMonoUtil.h"
 #include "BsMonoAssembly.h"
 #include "BsScriptObjectManager.h"
+#include "BsScriptHString.h"
 
 using namespace std::placeholders;
 
@@ -34,36 +35,48 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_InitializeGUIPanel", &ScriptModalWindow::internal_initializeGUIPanel);
 		metaData.scriptClass->addInternalCall("Internal_GetWidth", &ScriptModalWindow::internal_getWidth);
 		metaData.scriptClass->addInternalCall("Internal_GetHeight", &ScriptModalWindow::internal_getHeight);
+		metaData.scriptClass->addInternalCall("Internal_SetWidth", &ScriptModalWindow::internal_setWidth);
+		metaData.scriptClass->addInternalCall("Internal_SetHeight", &ScriptModalWindow::internal_setHeight);
+		metaData.scriptClass->addInternalCall("Internal_SetTitle", &ScriptModalWindow::internal_setTitle);
 
 		onInitializedInternalMethod = metaData.scriptClass->getMethod("OnInitializeInternal", 0);
 		onDestroyInternalMethod = metaData.scriptClass->getMethod("OnDestroyInternal", 0);
 	}
 
-	MonoObject* ScriptModalWindow::internal_createInstance(MonoString* ns, MonoString* typeName)
+	void ScriptModalWindow::internal_createInstance(MonoObject* instance, bool allowCloseButton)
 	{
-		String strTypeName = toString(MonoUtil::monoToWString(typeName));
-		String strNamespace = toString(MonoUtil::monoToWString(ns));
-		String fullName = strNamespace + "." + strTypeName;
-
-		ManagedModalWindow* modalWindow = bs_new<ManagedModalWindow>(strNamespace, strTypeName);
+		ManagedModalWindow* modalWindow = bs_new<ManagedModalWindow>(allowCloseButton, instance);
 		ScriptModalWindow* nativeInstance = new (bs_alloc<ScriptModalWindow>()) ScriptModalWindow(modalWindow);
 		modalWindow->initialize(nativeInstance);
-
-		mono_runtime_object_init(modalWindow->getManagedInstance()); // Construct it
 		modalWindow->triggerOnInitialize();
-
-		return modalWindow->getManagedInstance();
 	}
 
 	void ScriptModalWindow::internal_close(ScriptModalWindow* thisPtr)
 	{
-		if (thisPtr->mModalWindow == nullptr)
+		thisPtr->closeWindow();
+	}
+
+	void ScriptModalWindow::internal_setTitle(ScriptModalWindow* thisPtr, MonoObject* title)
+	{
+		HString titleStr = HString::dummy();
+		if (title != nullptr)
+		{
+			ScriptHString* textScript = ScriptHString::toNative(title);
+			titleStr = textScript->getInternalValue();
+		}
+
+		thisPtr->mModalWindow->setTitle(titleStr);
+	}
+
+	void ScriptModalWindow::closeWindow()
+	{
+		if (mModalWindow == nullptr)
 			return;
 
-		thisPtr->mModalWindow->triggerOnDestroy();
-		thisPtr->mModalWindow->releaseManagedInstance();
-		thisPtr->mModalWindow->close();
-		thisPtr->mModalWindow = nullptr;
+		mModalWindow->triggerOnDestroy();
+		mModalWindow->releaseManagedInstance();
+		mModalWindow->close();
+		mModalWindow = nullptr;
 	}
 
 	void ScriptModalWindow::_onManagedInstanceDeleted()
@@ -144,29 +157,47 @@ namespace BansheeEngine
 		return 0;
 	}
 
+	void ScriptModalWindow::internal_setWidth(ScriptModalWindow* thisPtr, UINT32 value)
+	{
+		if (thisPtr->mModalWindow != nullptr)
+			thisPtr->mModalWindow->setSize(value, thisPtr->mModalWindow->getHeight());
+	}
+
+	void ScriptModalWindow::internal_setHeight(ScriptModalWindow* thisPtr, UINT32 value)
+	{
+		if (thisPtr->mModalWindow != nullptr)
+			thisPtr->mModalWindow->setSize(thisPtr->mModalWindow->getWidth(), value);
+	}
+
 	void ScriptModalWindow::internal_initializeGUIPanel(ScriptModalWindow* thisPtr, MonoObject* panel)
 	{
 		ScriptGUIPanel* scriptGUIPanel = ScriptGUIPanel::toNative(panel);
 		thisPtr->mGUIPanel = scriptGUIPanel;
 
-		// TODO - Handle TitleBar when I add it
-		scriptGUIPanel->setParentArea(0, 0,
-			thisPtr->mModalWindow->getWidth(), thisPtr->mModalWindow->getHeight());
-
+		Rect2I contentArea = thisPtr->mModalWindow->getContentArea();
+		scriptGUIPanel->setParentArea(contentArea.x, contentArea.y, contentArea.width, contentArea.height);
 		scriptGUIPanel->setParentWidget(thisPtr->mModalWindow->getGUIWidget().get());
 	}
 
-	ManagedModalWindow::ManagedModalWindow(const String& ns, const String& type)
-		:ModalWindow(), mNamespace(ns), mTypename(type), mUpdateThunk(nullptr), mManagedInstance(nullptr), 
+	ManagedModalWindow::ManagedModalWindow(bool allowCloseButton, MonoObject* managedInstance)
+		:ModalWindow(HString::dummy(), allowCloseButton), mUpdateThunk(nullptr), mManagedInstance(managedInstance),
 		mOnInitializeThunk(nullptr), mOnDestroyThunk(nullptr), mOnWindowResizedMethod(nullptr), mGCHandle(0),
 		mScriptParent(nullptr)
 	{
-		createManagedInstance();
+		mGCHandle = mono_gchandle_new(mManagedInstance, false);
+
+		::MonoClass* rawMonoClass = mono_object_get_class(mManagedInstance);
+		MonoClass* monoClass = MonoManager::instance().findClass(rawMonoClass);
+
+		mNamespace = monoClass->getNamespace();
+		mTypename = monoClass->getTypeName();
+
+		reloadMonoTypes(monoClass);
 	}
 
 	ManagedModalWindow::~ManagedModalWindow()
 	{
-
+		assert(mGCHandle == 0); // We expect "close" to be called either from C++ or C# before destruction
 	}
 
 	bool ManagedModalWindow::createManagedInstance()
@@ -254,8 +285,8 @@ namespace BansheeEngine
 		UINT32 width = getWidth();
 		UINT32 height = getHeight();
 
-		// TODO - Handle TitleBar when I add it
-		mScriptParent->mGUIPanel->setParentArea(0, 0, width, height);
+		Rect2I contentArea = getContentArea();
+		mScriptParent->mGUIPanel->setParentArea(contentArea.x, contentArea.y, contentArea.width, contentArea.height);
 
 		if (mOnWindowResizedMethod != nullptr && mManagedInstance != nullptr)
 		{
@@ -264,6 +295,11 @@ namespace BansheeEngine
 		}
 	}
 
+	void ManagedModalWindow::close()
+	{
+		mScriptParent->closeWindow();
+	}
+
 	void ManagedModalWindow::reloadMonoTypes(MonoClass* windowClass)
 	{
 		MonoMethod* updateMethod = windowClass->getMethod("OnEditorUpdate", 0);

+ 2 - 3
TODO.txt

@@ -9,9 +9,8 @@ Possibly set up automatic refresh in debug mode after initialization? As an ad-h
 
 Test file/folder open/save dialog
 
-Add a simple way to create modal dialogs (ModalDialog in C#, similar to EditorWindow)
-Modal window needs a title bar probably (with an X button, which would be optional)
- - When I add it make sure to update GUIPanel area in ScriptModalWindow
+Check why focus highlight on windows doesn't work
+Elements in GUIScrollArea will not have valid bounds calculated using GUILayoutUtility::calcBounds
 
 C#:
 Dialog.Show(title, text, btn1 text, btn1 callback, btn2 text, btn2 callback, btn3 text, btn3 callback)