ソースを参照

Refactored editor widget container so that it managed widget closing more neatly
Dock/Undock working in DockManager

Marko Pintera 12 年 前
コミット
f32647320b

+ 10 - 2
BansheeEngine/Source/BsGUIManager.cpp

@@ -1112,10 +1112,18 @@ namespace BansheeEngine
 
 	void GUIManager::processDestroyQueue()
 	{
+		// Need two loops and a temporary since element destructors may themselves
+		// queue other elements for destruction
 		while(!mScheduledForDestruction.empty())
 		{
-			cm_delete<PoolAlloc>(mScheduledForDestruction.top());
-			mScheduledForDestruction.pop();
+			CM::Stack<GUIElement*>::type toDestroy = mScheduledForDestruction;
+			mScheduledForDestruction = CM::Stack<GUIElement*>::type();
+
+			while(!toDestroy.empty())
+			{
+				cm_delete<PoolAlloc>(toDestroy.top());
+				toDestroy.pop();
+			}
 		}
 	}
 

+ 5 - 3
CamelotClient/Include/BsEditorWidgetContainer.h

@@ -15,8 +15,6 @@ namespace BansheeEditor
 		void remove(EditorWidget& widget);
 		void insert(CM::UINT32 idx, EditorWidget& widget);
 
-		void hide(EditorWidget& widget);
-
 		void setSize(CM::UINT32 width, CM::UINT32 height);
 		void setPosition(CM::INT32 x, CM::INT32 y);
 
@@ -24,9 +22,10 @@ namespace BansheeEditor
 		BS::GUIWidget& getParentWidget() const { return *mParent; }
 
 		void _notifyWidgetDestroyed(EditorWidget* widget);
+		bool _isHandlingWidgetDragAndDrop() const { return mIsHandlingWidgetDragAndDrop; }
+		void _addCallbackOnDraggedWidgetDropped(std::function<void()> callback);
 
 		boost::signal<void()> onWidgetClosed;
-		boost::signal<void()> onWidgetHidden;
 	private:
 		GUITabbedTitleBar* mTitleBar;
 		BS::GUIWidget* mParent;
@@ -35,6 +34,9 @@ namespace BansheeEditor
 		CM::Vector<EditorWidget*>::type mWidgets;
 		CM::INT32 mActiveWidget;
 
+		std::function<void()> mWidgetDroppedCallback;
+		bool mIsHandlingWidgetDragAndDrop;
+
 		static const CM::UINT32 TitleBarHeight;
 
 		void setActiveWidget(CM::UINT32 idx);

+ 1 - 1
CamelotClient/Include/BsEditorWindow.h

@@ -24,6 +24,6 @@ namespace BansheeEditor
 
 		void updateSize();
 		void widgetRemoved();
-		void widgetHidden();
+		void closeWindowDelayed();
 	};
 }

+ 4 - 6
CamelotClient/Source/BsDockManager.cpp

@@ -152,15 +152,14 @@ namespace BansheeEditor
 		mChildren[idxA] = cm_new<DockContainer>(this);
 		mChildren[idxB] = cm_new<DockContainer>(this);
 
+		mWidgets->onWidgetClosed.disconnect_all_slots();
+
 		mChildren[idxA]->makeLeaf(widgetParent, parentWindow, widget);
+		mChildren[idxB]->makeLeaf(mWidgets);
 
 		mIsLeaf = false;
 		mIsHorizontal = horizontal;
 		mSplitPosition = 0.5f;
-
-		if(mWidgets != nullptr)
-			cm_delete(mWidgets);
-
 		mWidgets = nullptr;
 
 		// TODO - Add slider
@@ -193,10 +192,9 @@ namespace BansheeEditor
 					sibling = mParent->mChildren[0];
 
 				sibling->mWidgets->onWidgetClosed.disconnect_all_slots();
-				sibling->mWidgets->onWidgetHidden.disconnect_all_slots();
-				sibling->mWidgets = nullptr;
 
 				mParent->makeLeaf(sibling->mWidgets);
+				sibling->mWidgets = nullptr;
 
 				cm_delete(sibling);
 				cm_delete(this);

+ 19 - 47
CamelotClient/Source/BsEditorWidgetContainer.cpp

@@ -14,7 +14,8 @@ namespace BansheeEditor
 	const CM::UINT32 EditorWidgetContainer::TitleBarHeight = 13;
 
 	EditorWidgetContainer::EditorWidgetContainer(BS::GUIWidget* parent, RenderWindow* renderWindow)
-		:mParent(parent), mX(0), mY(0), mWidth(0), mHeight(0), mTitleBar(nullptr), mActiveWidget(-1)
+		:mParent(parent), mX(0), mY(0), mWidth(0), mHeight(0), mTitleBar(nullptr), mActiveWidget(-1),
+		mIsHandlingWidgetDragAndDrop(false)
 	{
 		mTitleBar = cm_new<GUITabbedTitleBar>(parent, renderWindow);
 		mTitleBar->onTabActivated.connect(boost::bind(&EditorWidgetContainer::tabActivated, this, _1));
@@ -89,37 +90,6 @@ namespace BansheeEditor
 			widget._disable();
 	}
 
-	// Note: Hiding is used for a special purpose right now and there is no way to unhide a widget
-	// (But such functionally may be easily added)
-	void EditorWidgetContainer::hide(EditorWidget& widget)
-	{
-		INT32 widgetIdx = -1;
-		UINT32 curIdx = 0;
-		for(auto& curWidget : mWidgets)
-		{
-			if(curWidget == &widget)
-			{
-				widgetIdx = curIdx;
-				break;
-			}
-
-			curIdx++;
-		}
-
-		if(widgetIdx == -1)
-			return;
-
-		mTitleBar->removeTab(widgetIdx);
-
-		if(widgetIdx == mActiveWidget)
-		{
-			if(mWidgets.size() > 0)
-			{
-				setActiveWidget(0);
-			}
-		}
-	}
-
 	void EditorWidgetContainer::setSize(UINT32 width, UINT32 height)
 	{
 		// TODO - Title bar is always TitleBarHeight size, so what happens when the container area is smaller than that?
@@ -192,18 +162,15 @@ namespace BansheeEditor
 	void EditorWidgetContainer::tabDraggedOff(CM::UINT32 idx)
 	{
 		EditorWidget* widget = mWidgets[idx];
-		hide(*widget);
+		remove(*widget);
+
+		mIsHandlingWidgetDragAndDrop = true;
 
 		// TODO - Hook up drag and drop texture
 		DragAndDropManager::instance().startDrag(HTexture(), (UINT32)DragAndDropType::EditorWidget, (void*)widget, boost::bind(&EditorWidgetContainer::tabDroppedCallback, this, _1));
 
-		// We don't want to remove the widget just yet. For now mark it as hidden in case
-		// user just drops it somewhere else.
-		// Note: This is primarily implemented because Windows doesn't like me destroying a window
-		// while I'm capturing mouse input (required for drag and drop outside of window borders). 
-		// So instead I just hide it and do the destroying after drag and drop is done.
-		if(!onWidgetHidden.empty())
-			onWidgetHidden();
+		if(!onWidgetClosed.empty())
+			onWidgetClosed();
 	}
 
 	void EditorWidgetContainer::tabDraggedOn(CM::UINT32 idx)
@@ -226,15 +193,9 @@ namespace BansheeEditor
 		if(DragAndDropManager::instance().getDragTypeId() != (UINT32)DragAndDropType::EditorWidget)
 			return;
 
-		EditorWidget* draggedWidget = static_cast<EditorWidget*>(DragAndDropManager::instance().getDragData());
-		draggedWidget->_changeParent(nullptr); // To ensure it doesn't get destroyed with the parent window
-		remove(*draggedWidget);
-
-		if(!onWidgetClosed.empty())
-			onWidgetClosed();
-
 		if(!wasDragProcessed)
 		{
+			EditorWidget* draggedWidget = static_cast<EditorWidget*>(DragAndDropManager::instance().getDragData());
 			EditorWindow* newWindow = EditorWindow::create();
 
 			newWindow->widgets().add(*draggedWidget);
@@ -242,6 +203,11 @@ namespace BansheeEditor
 			Vector2I mousePos = Input::instance().getCursorPosition();
 			newWindow->setPosition(mousePos.x, mousePos.y);
 		}
+
+		mIsHandlingWidgetDragAndDrop = false;
+
+		if(mWidgetDroppedCallback != nullptr)
+			mWidgetDroppedCallback();
 	}
 
 	void EditorWidgetContainer::_notifyWidgetDestroyed(EditorWidget* widget)
@@ -259,4 +225,10 @@ namespace BansheeEditor
 			}
 		}		
 	}
+
+	void EditorWidgetContainer::_addCallbackOnDraggedWidgetDropped(std::function<void()> callback)
+	{
+		if(mIsHandlingWidgetDragAndDrop)
+			mWidgetDroppedCallback = callback;
+	}
 }

+ 17 - 5
CamelotClient/Source/BsEditorWindow.cpp

@@ -13,7 +13,6 @@ namespace BansheeEditor
 		updateSize();
 		
 		mWidgets->onWidgetClosed.connect(boost::bind(&EditorWindow::widgetRemoved, this));
-		mWidgets->onWidgetHidden.connect(boost::bind(&EditorWindow::widgetHidden, this));
 	}
 
 	EditorWindow::~EditorWindow()
@@ -41,13 +40,26 @@ namespace BansheeEditor
 	void EditorWindow::widgetRemoved()
 	{
 		if(mWidgets->getNumWidgets() == 0)
-			close();
+		{
+			// HACK - If widget is being handled by drag and drop we don't want to
+			// destroy its parent window just yet because Windows doesn't approve of
+			// windows being destroyed while mouse is being held down (some events won't get
+			// fired). I should probably handle this at a lower level, in RenderWindowManager.
+			if(mWidgets->_isHandlingWidgetDragAndDrop())
+			{
+				hide();
+
+				// Get notified when drag and drop is done
+				mWidgets->_addCallbackOnDraggedWidgetDropped(std::bind(&EditorWindow::closeWindowDelayed, this));
+			}
+			else
+				close();
+		}
 	}
 
-	void EditorWindow::widgetHidden()
+	void EditorWindow::closeWindowDelayed()
 	{
-		if(mWidgets->getNumWidgets() == 1)
-			hide();
+		close();
 	}
 
 	EditorWindow* EditorWindow::create()

+ 6 - 7
EditorWindowDock.txt

@@ -1,10 +1,6 @@
-DockManager implementation steps:
-Initial:
- - Open up two editor windows on app start
- - Initially DockManager has empty background
- - Dragging one editor window anywhere on the empty background docks the window
- - Dragging another editor window over the window shows the drop overlay and you can dock that window onto any side
-   - Currently the overlay is always shown and will trigger on mouse move and not only mouse drag
+TODO:
+ - Dock manager doesn't work as intended when I drag two windows onto it
+ - Ensure that dragging items onto the title bar works
 
 Resize sliders
  - Add a button between docked windows
@@ -15,6 +11,9 @@ TitleBar dock/undock
  - Moving the dragged window over a title bar will temporarily dock the title bar and allow you to move it (as in first step)
    - If you release the mouse the window will then be permanently docked at that location
 
+CONSIDER getting rid of mouse event filters for DockManager
+ - I can probably convert DockManager into a GUIElement?
+
 Polish TOOD:
  - Change cursor icon when window is dragged
  - Prevent docking if available size is less than 20 pixels, otherwise there might be some weirdness