Selaa lähdekoodia

Removed owner window from GUIWidget

Marko Pintera 12 vuotta sitten
vanhempi
sitoutus
fc3d0fb7ce

+ 1 - 1
BansheeEngine/Include/BsGUIDropDownBox.h

@@ -107,7 +107,7 @@ namespace BansheeEngine
 	class BS_EXPORT GUIDropDownBox : public GUIWidget
 	{
 	public:
-		GUIDropDownBox(const CM::HSceneObject& parent, CM::Viewport* target, CM::RenderWindow* window, const GUIDropDownAreaPlacement& placement,
+		GUIDropDownBox(const CM::HSceneObject& parent, CM::Viewport* target, const GUIDropDownAreaPlacement& placement,
 			const GUIDropDownData& dropDownData, const GUISkin& skin, GUIDropDownType type);
 		~GUIDropDownBox();
 

+ 20 - 0
BansheeEngine/Include/BsGUIManager.h

@@ -84,6 +84,22 @@ namespace BansheeEngine
 		void addSelectiveInputWidget(const GUIWidget* widget);
 		void addSelectiveInputElement(const GUIElement* element);
 
+		/**
+		 * @brief	Allows you to bridge GUI input from a GUI element into a widget.
+		 *
+		 * @param	widget 	The widget for which to bridge input.
+		 * @param	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.
+		 * 			
+		 *			Bridged element needs to remove itself as the bridge when it is destroyed.
+		 */
+		void setInputBridge(const GUIWidget* widget, const GUIElement* element);
+
 		boost::signal<void(GUIWidget*, GUIElement*, const GUIMouseEvent&)> mouseEventFilter;
 		boost::signal<void(GUIWidget*, GUIElement*, const GUITextInputEvent&)> textInputEventFilter;
 	private:
@@ -144,6 +160,8 @@ namespace BansheeEngine
 		CM::Map<const GUIWidget*, SelectiveInputData>::type mSelectiveInputData;
 		std::function<void()> mOnOutsideClickCallback;
 
+		CM::Map<const GUIWidget*, const GUIElement*>::type mInputBridge;
+
 		boost::signals::connection mOnCursorMovedConn;
 		boost::signals::connection mOnCursorPressedConn;
 		boost::signals::connection mOnCursorReleasedConn;
@@ -183,6 +201,8 @@ namespace BansheeEngine
 
 		GUIMouseButton buttonToGUIButton(CM::PositionalInputEventButton cursorButton) const;
 		CM::Int2 getWidgetRelativePos(const GUIWidget& widget, const CM::Int2& screenPos) const;
+		CM::Int2 windowToBridgedCoords(const GUIWidget& widget, const CM::Int2& windowPos) const;
+		const CM::RenderWindow* getWidgetWindow(const GUIWidget& widget) const;
 
 		bool sendMouseEvent(GUIWidget* widget, GUIElement* element, const GUIMouseEvent& event);
 		bool sendTextInputEvent(GUIWidget* widget, GUIElement* element, const GUITextInputEvent& event);

+ 2 - 3
BansheeEngine/Include/BsGUIWidget.h

@@ -21,6 +21,7 @@ namespace BansheeEngine
 		void setDepth(CM::UINT8 depth) { mDepth = depth; mWidgetIsDirty = true; }
 
 		bool inBounds(const CM::Int2& position) const;
+		const CM::Rect& getBounds() const { return mBounds; }
 
 		/**
 		 * @brief	Return true if widget or any of its elements are dirty.
@@ -31,7 +32,6 @@ namespace BansheeEngine
 		 */
 		bool isDirty(bool cleanIfDirty);
 
-		CM::RenderWindow* getOwnerWindow() const { return mOwnerWindow; }
 		CM::Viewport* getTarget() const { return mTarget; }
 		const CM::Vector<GUIElement*>::type& getElements() const { return mElements; }
 
@@ -62,7 +62,7 @@ namespace BansheeEngine
 		friend class GUIArea;
 		friend class GUIManager;
 
-		GUIWidget(const CM::HSceneObject& parent, CM::Viewport* target, CM::RenderWindow* ownerWindow);
+		GUIWidget(const CM::HSceneObject& parent, CM::Viewport* target);
 
 		void registerElement(GUIElement* elem);
 		void unregisterElement(GUIElement* elem);
@@ -79,7 +79,6 @@ namespace BansheeEngine
 
 		void updateBounds() const;
 
-		CM::RenderWindow* mOwnerWindow;
 		CM::Viewport* mTarget;
 		CM::Vector<GUIElement*>::type mElements;
 		CM::Vector<GUIArea*>::type mAreas;

+ 2 - 3
BansheeEngine/Include/BsProfilerOverlay.h

@@ -48,10 +48,10 @@ namespace BansheeEngine
 		};
 
 	public:
-		ProfilerOverlay(const CM::ViewportPtr& target, const CM::RenderWindowPtr& ownerWindow);
+		ProfilerOverlay(const CM::ViewportPtr& target);
 		~ProfilerOverlay();
 
-		void setTarget(const CM::ViewportPtr& target, const CM::RenderWindowPtr& ownerWindow);
+		void setTarget(const CM::ViewportPtr& target);
 
 		void show();
 		void hide();
@@ -64,7 +64,6 @@ namespace BansheeEngine
 		static const CM::UINT32 MAX_DEPTH;
 
 		CM::ViewportPtr mTarget;
-		CM::RenderWindowPtr mOwnerWindow;
 
 		CM::HSceneObject mWidgetSO;
 		CM::GameObjectHandle<GUIWidget> mWidget;

+ 2 - 2
BansheeEngine/Source/BsGUIDropDownBox.cpp

@@ -72,9 +72,9 @@ namespace BansheeEngine
 		return instance;
 	}
 
-	GUIDropDownBox::GUIDropDownBox(const HSceneObject& parent, CM::Viewport* target, CM::RenderWindow* window, const GUIDropDownAreaPlacement& placement,
+	GUIDropDownBox::GUIDropDownBox(const HSceneObject& parent, CM::Viewport* target, const GUIDropDownAreaPlacement& placement,
 		const GUIDropDownData& dropDownData, const GUISkin& skin, GUIDropDownType type)
-		:GUIWidget(parent, target, window), mScrollUpStyle(nullptr),
+		:GUIWidget(parent, target), mScrollUpStyle(nullptr),
 		mScrollDownStyle(nullptr), mEntryBtnStyle(nullptr), mEntryExpBtnStyle(nullptr), 
 		mSeparatorStyle(nullptr), mBackgroundStyle(nullptr)
 	{

+ 1 - 1
BansheeEngine/Source/BsGUIDropDownBoxManager.cpp

@@ -16,7 +16,7 @@ namespace BansheeEngine
 		closeDropDownBox();
 
 		mDropDownSO = SceneObject::create("DropDownBox");
-		mDropDownBox = mDropDownSO->addComponent<GUIDropDownBox>(target, nullptr, placement, dropDownData, skin, type);
+		mDropDownBox = mDropDownSO->addComponent<GUIDropDownBox>(target, placement, dropDownData, skin, type);
 		mOnClosedCallback = onClosedCallback;
 
 		return mDropDownBox;

+ 88 - 13
BansheeEngine/Source/BsGUIManager.cpp

@@ -761,7 +761,7 @@ namespace BansheeEngine
 
 			if(menu != nullptr)
 			{
-				const RenderWindow* window = mWidgetUnderCursor->getOwnerWindow();
+				const RenderWindow* window = getWidgetWindow(*mWidgetUnderCursor);
 				Int2 windowPos = window->screenToWindowPos(event.screenPos);
 
 				menu->open(windowPos, *mWidgetUnderCursor);
@@ -872,12 +872,19 @@ namespace BansheeEngine
 
 	bool GUIManager::findElementUnderCursor(const CM::Int2& cursorScreenPos, bool buttonStates[3], bool shift, bool control, bool alt)
 	{
+		Vector<const RenderWindow*>::type widgetWindows;
+		for(auto& widgetInfo : mWidgets)
+			widgetWindows.push_back(getWidgetWindow(*widgetInfo.widget));
+
 #if CM_DEBUG_MODE
 		// Checks if all referenced windows actually exist
 		Vector<RenderWindow*>::type activeWindows = RenderWindowManager::instance().getRenderWindows();
-		for(auto& widgetInfo : mWidgets)
+		for(auto& window : widgetWindows)
 		{
-			auto iterFind = std::find(begin(activeWindows), end(activeWindows), widgetInfo.widget->getOwnerWindow());
+			if(window == nullptr)
+				continue;
+
+			auto iterFind = std::find(begin(activeWindows), end(activeWindows), window);
 
 			if(iterFind == activeWindows.end())
 			{
@@ -893,9 +900,11 @@ namespace BansheeEngine
 		const RenderWindow* windowUnderCursor = nullptr;
 		UnorderedSet<const RenderWindow*>::type uniqueWindows;
 
-		for(auto& widgetInfo : mWidgets)
+		for(auto& window : widgetWindows)
 		{
-			const RenderWindow* window = widgetInfo.widget->getOwnerWindow();
+			if(window == nullptr)
+				continue;
+
 			uniqueWindows.insert(window);
 		}
 
@@ -914,10 +923,17 @@ namespace BansheeEngine
 			Vector4 vecWindowPos((float)windowPos.x, (float)windowPos.y, 0.0f, 1.0f);
 
 			UINT32 topMostDepth = std::numeric_limits<UINT32>::max();
+			UINT32 widgetIdx = 0;
 			for(auto& widgetInfo : mWidgets)
 			{
+				if(widgetWindows[widgetIdx] == nullptr)
+				{
+					widgetIdx++;
+					continue;
+				}
+
 				GUIWidget* widget = widgetInfo.widget;
-				if(widget->getOwnerWindow() == windowUnderCursor && widget->inBounds(windowPos))
+				if(widgetWindows[widgetIdx] == windowUnderCursor && widget->inBounds(windowToBridgedCoords(*widget, windowPos)))
 				{
 					SelectiveInputData* selectiveInputData = nullptr;
 					if(mSelectiveInputActive)
@@ -925,15 +941,15 @@ namespace BansheeEngine
 						auto selectionIterFind = mSelectiveInputData.find(widget);
 
 						if(selectionIterFind == mSelectiveInputData.end())
+						{
+							widgetIdx++;
 							continue;
+						}
 						else
 							selectiveInputData = &selectionIterFind->second;
 					}
 
-					const Matrix4& worldTfrm = widget->SO()->getWorldTfrm();
-
-					Vector4 vecLocalPos = worldTfrm.inverse() * vecWindowPos;
-					Int2 localPos = Int2(Math::RoundToInt(vecLocalPos.x), Math::RoundToInt(vecLocalPos.y));
+					Int2 localPos = getWidgetRelativePos(*widget, cursorScreenPos);
 
 					Vector<GUIElement*>::type sortedElements = widget->getElements();
 					std::sort(sortedElements.begin(), sortedElements.end(), 
@@ -963,6 +979,8 @@ namespace BansheeEngine
 						}
 					}
 				}
+
+				widgetIdx++;
 			}
 		}
 
@@ -1032,7 +1050,7 @@ namespace BansheeEngine
 		for(auto& widgetInfo : mWidgets)
 		{
 			GUIWidget* widget = widgetInfo.widget;
-			if(widget->getOwnerWindow() == &win)
+			if(getWidgetWindow(*widget) == &win)
 				widget->ownerWindowFocusChanged();
 		}
 	}
@@ -1042,7 +1060,7 @@ namespace BansheeEngine
 		for(auto& widgetInfo : mWidgets)
 		{
 			GUIWidget* widget = widgetInfo.widget;
-			if(widget->getOwnerWindow() == &win)
+			if(getWidgetWindow(*widget) == &win)
 				widget->ownerWindowFocusChanged();
 		}
 	}
@@ -1108,6 +1126,14 @@ namespace BansheeEngine
 		mSelectiveInputData[&element->_getParentWidget()].elements.insert(element);
 	}
 
+	void GUIManager::setInputBridge(const GUIWidget* widget, const GUIElement* element)
+	{
+		if(element == nullptr)
+			mInputBridge.erase(widget);
+		else
+			mInputBridge[widget] = element;
+	}
+
 	GUIMouseButton GUIManager::buttonToGUIButton(PositionalInputEventButton cursorButton) const
 	{
 		if(cursorButton == PositionalInputEventButton::Left)
@@ -1122,8 +1148,9 @@ namespace BansheeEngine
 
 	Int2 GUIManager::getWidgetRelativePos(const GUIWidget& widget, const Int2& screenPos) const
 	{
-		const RenderWindow* window = widget.getOwnerWindow();
+		const RenderWindow* window = getWidgetWindow(widget);
 		Int2 windowPos = window->screenToWindowPos(screenPos);
+		windowPos = windowToBridgedCoords(widget, windowPos);
 
 		const Matrix4& worldTfrm = widget.SO()->getWorldTfrm();
 
@@ -1133,6 +1160,54 @@ namespace BansheeEngine
 		return curLocalPos;
 	}
 
+	Int2 GUIManager::windowToBridgedCoords(const GUIWidget& widget, const Int2& windowPos) const
+	{
+		auto iterFind = mInputBridge.find(&widget);
+		if(iterFind != mInputBridge.end()) // Widget input is bridged, which means we need to transform the coordinates
+		{
+			const GUIElement* bridgeElement = iterFind->second;
+
+			const Matrix4& worldTfrm = bridgeElement->_getParentWidget().SO()->getWorldTfrm();
+
+			Vector4 vecLocalPos = worldTfrm.inverse() * Vector4((float)windowPos.x, (float)windowPos.y, 0.0f, 1.0f);
+			Rect bridgeBounds = bridgeElement->getBounds();
+			Rect actualBounds = widget.getBounds();
+
+			float x = vecLocalPos.x - (float)bridgeBounds.x;
+			float y = vecLocalPos.y - (float)bridgeBounds.y;
+
+			float scaleX = actualBounds.width / (float)bridgeBounds.width;
+			float scaleY = actualBounds.height / (float)bridgeBounds.height;
+
+			return Int2(Math::RoundToInt(x * scaleX), Math::RoundToInt(y * scaleY));
+		}
+
+		return windowPos;
+	}
+
+	const CM::RenderWindow* GUIManager::getWidgetWindow(const GUIWidget& widget) const
+	{
+		auto iterFind = mInputBridge.find(&widget);
+
+		if(iterFind != mInputBridge.end())
+		{
+			GUIWidget& parentWidget = iterFind->second->_getParentWidget();
+			if(&parentWidget != &widget)
+			{
+				return getWidgetWindow(parentWidget);
+			}
+		}
+
+		RenderTargetPtr renderTarget = widget.getTarget()->getTarget();
+		Vector<RenderWindow*>::type renderWindows = RenderWindowManager::instance().getRenderWindows();
+
+		auto iterFindWin = std::find(renderWindows.begin(), renderWindows.end(), renderTarget.get());
+		if(iterFindWin != renderWindows.end())
+			return static_cast<RenderWindow*>(renderTarget.get());
+
+		return nullptr;
+	}
+
 	bool GUIManager::sendMouseEvent(GUIWidget* widget, GUIElement* element, const GUIMouseEvent& event)
 	{
 		if(!mouseEventFilter.empty())

+ 2 - 7
BansheeEngine/Source/BsGUIWidget.cpp

@@ -22,21 +22,16 @@ namespace BansheeEngine
 {
 	GUISkin GUIWidget::DefaultSkin;
 
-	GUIWidget::GUIWidget(const HSceneObject& parent, CM::Viewport* target, CM::RenderWindow* ownerWindow)
-		:Component(parent), mSkin(nullptr), mOwnerWindow(nullptr), mWidgetIsDirty(false), mTarget(nullptr), mDepth(0)
+	GUIWidget::GUIWidget(const HSceneObject& parent, CM::Viewport* target)
+		:Component(parent), mSkin(nullptr), mWidgetIsDirty(false), mTarget(nullptr), mDepth(0)
 	{
 		mLastFramePosition = SO()->getWorldPosition();
 		mLastFrameRotation = SO()->getWorldRotation();
 		mLastFrameScale = SO()->getWorldScale();
 
 		assert(target != nullptr);
-		assert(ownerWindow != nullptr);
-
-		if(mOwnerWindow != nullptr)
-			CM_EXCEPT(InvalidStateException, "Widget has already been initialized.");
 
 		mTarget = target;
-		mOwnerWindow = ownerWindow;
 
 		mOwnerTargetResizedConn = mTarget->onResized.connect(boost::bind(&GUIWidget::ownerTargetResized, this));
 

+ 4 - 5
BansheeEngine/Source/BsProfilerOverlay.cpp

@@ -234,7 +234,7 @@ namespace BansheeEngine
 
 	const UINT32 ProfilerOverlay::MAX_DEPTH = 2;
 
-	ProfilerOverlay::ProfilerOverlay(const CM::ViewportPtr& target, const CM::RenderWindowPtr& ownerWindow)
+	ProfilerOverlay::ProfilerOverlay(const CM::ViewportPtr& target)
 		:mIsShown(false), mBasicAreaLabels(nullptr), mPreciseAreaLabels(nullptr), mBasicAreaContents(nullptr), mPreciseAreaContents(nullptr),
 		mBasicLayoutLabels(nullptr), mPreciseLayoutLabels(nullptr), mBasicLayoutContents(nullptr), mPreciseLayoutContents(nullptr),
 		mTitleBasicName(nullptr), mTitleBasicPctOfParent(nullptr), mTitleBasicNumCalls(nullptr), mTitleBasicAvgTime(nullptr), 
@@ -243,7 +243,7 @@ namespace BansheeEngine
 		mTitlePreciseNumCalls(nullptr), mTitlePreciseAvgCycles(nullptr), mTitlePreciseTotalCycles(nullptr), mTitlePreciseMaxCycles(nullptr), 
 		mTitlePreciseAvgCyclesSelf(nullptr), mTitlePreciseTotalCyclesSelf(nullptr), mTitlePreciseEstOverhead(nullptr), mTitlePreciseEstOverheadSelf(nullptr)
 	{
-		setTarget(target, ownerWindow);
+		setTarget(target);
 	}
 
 	ProfilerOverlay::~ProfilerOverlay()
@@ -258,13 +258,12 @@ namespace BansheeEngine
 			mWidgetSO->destroy();
 	}
 
-	void ProfilerOverlay::setTarget(const CM::ViewportPtr& target, const CM::RenderWindowPtr& ownerWindow)
+	void ProfilerOverlay::setTarget(const CM::ViewportPtr& target)
 	{
 		if(mTarget != nullptr)
 			mTargetResizedConn.disconnect();
 
 		mTarget = target;
-		mOwnerWindow = ownerWindow;
 
 		mTargetResizedConn = target->onResized.connect(boost::bind(&ProfilerOverlay::targetResized, this));
 
@@ -272,7 +271,7 @@ namespace BansheeEngine
 			mWidgetSO->destroy();
 
 		mWidgetSO = SceneObject::create("ProfilerOverlay");
-		mWidget = mWidgetSO->addComponent<GUIWidget>(mTarget.get(), mOwnerWindow.get());
+		mWidget = mWidgetSO->addComponent<GUIWidget>(mTarget.get());
 		mWidget->setDepth(127);
 		mWidget->setSkin(EngineGUI::instance().getSkin());
 

+ 1 - 1
CamelotClient/Include/CmTestTextSprite.h

@@ -8,7 +8,7 @@ namespace CamelotFramework
 	protected:
 		friend class CM::SceneObject;
 
-		TestTextSprite(const HSceneObject& parent, CM::Viewport* target, CM::RenderWindow* ownerWindow);
+		TestTextSprite(const HSceneObject& parent, CM::Viewport* target);
 	public:
 		~TestTextSprite();
 

+ 1 - 1
CamelotClient/Source/BsEditorWindowBase.cpp

@@ -72,7 +72,7 @@ namespace BansheeEditor
 		mCamera->setAspectRatio(1.0f);
 		mCamera->setIgnoreSceneRenderables(true);
 
-		mGUI = mSceneObject->addComponent<GUIWidget>(mCamera->getViewport().get(), renderWindow.get());
+		mGUI = mSceneObject->addComponent<GUIWidget>(mCamera->getViewport().get());
 		mGUI->setDepth(128);
 
 		mGUI->setSkin(EngineGUI::instance().getSkin());

+ 1 - 1
CamelotClient/Source/BsGUIWindowFrameWidget.cpp

@@ -17,7 +17,7 @@ namespace BansheeEditor
 	const UINT32 WindowFrameWidget::RESIZE_BORDER_WIDTH = 3;
 
 	WindowFrameWidget::WindowFrameWidget(const HSceneObject& parent, CM::Viewport* target, CM::RenderWindow* parentWindow, const GUISkin& skin)
-		:GUIWidget(parent, target, parentWindow), mWindowFrameArea(nullptr), mParentWindow(parentWindow)
+		:GUIWidget(parent, target), mWindowFrameArea(nullptr), mParentWindow(parentWindow)
 	{
 		setSkin(skin);
 

+ 2 - 2
CamelotClient/Source/BsMainEditorWindow.cpp

@@ -58,7 +58,7 @@ namespace BansheeEditor
 
 		GameObjectHandle<DebugCamera> debugCamera = sceneCameraGO->addComponent<DebugCamera>();
 
-		GameObjectHandle<TestTextSprite> textSprite = mSceneObject->addComponent<TestTextSprite>(mCamera->getViewport().get(), renderWindow.get());
+		GameObjectHandle<TestTextSprite> textSprite = mSceneObject->addComponent<TestTextSprite>(mCamera->getViewport().get());
 
 		textSprite->init(sceneCamera, "Testing in a new row, does this work?", sceneRenderTarget);
 
@@ -72,7 +72,7 @@ namespace BansheeEditor
 		AABox dbgBox(Vector3(-300, -200, 1000), Vector3(300, 300, 1500));
 		DrawHelper3D::instance().drawAABox(sceneCamera, dbgBox, Color::Green, 250.0f);
 
-		ProfilerOverlay::startUp(cm_new<ProfilerOverlay>(sceneCamera->getViewport(), renderWindow));
+		ProfilerOverlay::startUp(cm_new<ProfilerOverlay>(sceneCamera->getViewport()));
 		ProfilerOverlay::instance().show();
 	}
 

+ 2 - 2
CamelotClient/Source/CmTestTextSprite.cpp

@@ -28,8 +28,8 @@ using namespace BansheeEngine;
 
 namespace CamelotFramework
 {
-	TestTextSprite::TestTextSprite(const HSceneObject& parent, CM::Viewport* target, CM::RenderWindow* ownerWindow)
-		:GUIWidget(parent, target, ownerWindow)
+	TestTextSprite::TestTextSprite(const HSceneObject& parent, CM::Viewport* target)
+		:GUIWidget(parent, target)
 	{
 	}
 

+ 2 - 6
TODO.txt

@@ -15,14 +15,10 @@ PROFILER:
 When rendering GUI in front of scene view it seems to be rendering before the actual scene!?
 
 Update GUIWidget input handling:
- - Remove RenderWindow from GUIWidget constructor
- - Remove getOwnerWindow from GUIWidget
+ - Recheck the bridging code, especially windowToBridgedCoords method.
+ - Add GUIRenderTexture method and ensure that bridging actually works
  - GUIWidget ownerWindowFocusChanged will also likely need to be replaced
 
- - If GUIWidget target is a window, GUIManager can detect it and automatically use that as the input source
-   - GUIRenderTexture are special GUI elements that are registered with GUIManager (add a new type derived from GUITexture)
-   - If a GUIWidget target is a render texture GUIManager can detect that as well and figure out the real parent and offset the input
-
 Generating a report only generates it on the active thread. I need a way to generate profiler reports on the render thread as well. Maybe extend CoreThreadAccessor?