Browse Source

Finalized and tested OS drop target support

Marko Pintera 12 years ago
parent
commit
f1832c9c66

+ 1 - 0
CamelotClient/Include/BsMainEditorWindow.h

@@ -21,6 +21,7 @@ namespace BansheeEditor
 		GUIMenuBar* mMenuBar;
 		DockManager* mDockManager;
 
+		void itemDropped(CM::OSDropTarget& dropTarget, CM::INT32 x, CM::INT32 y);
 		virtual void resized();
 
 		void updateAreas();

+ 11 - 0
CamelotClient/Source/BsMainEditorWindow.cpp

@@ -15,6 +15,7 @@
 #include "BsDrawHelper3D.h"
 #include "CmFRect.h"
 #include "BsProfilerOverlay.h"
+#include "CmPlatform.h"
 
 using namespace CamelotFramework;
 using namespace BansheeEngine;
@@ -73,6 +74,11 @@ namespace BansheeEditor
 		AABox dbgBox(Vector3(-300, -200, 1000), Vector3(300, 300, 1500));
 		DrawHelper3D::instance().drawAABox(sceneCamera, dbgBox, Color::Green, 250.0f);
 
+		OSDropTarget& dropTarget = Platform::createDropTarget(mRenderWindow.get(), 100, 100, 400, 400);
+		dropTarget.onDrop.connect(boost::bind(&MainEditorWindow::itemDropped, this, boost::ref(dropTarget), _1, _2));
+
+		Platform::destroyDropTarget(dropTarget);
+
 		ProfilerOverlay::startUp(cm_new<ProfilerOverlay>(sceneCamera->getViewport()));
 		ProfilerOverlay::instance().show();
 	}
@@ -85,6 +91,11 @@ namespace BansheeEditor
 		cm_delete(mMenuBar);
 	}
 
+	void MainEditorWindow::itemDropped(OSDropTarget& dropTarget, INT32 x, INT32 y)
+	{
+		int a = 5;
+	}
+
 	void MainEditorWindow::resized()
 	{
 		EditorWindowBase::resized();

+ 10 - 2
CamelotCore/Include/CmPlatform.h

@@ -62,23 +62,31 @@ namespace CamelotFramework
 	public:
 		boost::signal<void(INT32 x, INT32 y)> onDragOver;
 		boost::signal<void(INT32 x, INT32 y)> onDrop;
+		boost::signal<void(INT32 x, INT32 y)> onEnter;
+		boost::signal<void()> onLeave;
 
 		void setArea(INT32 x, INT32 y, UINT32 width, UINT32 height);
 		OSDropType getDropType() const { return mDropType; }
 
 		const Vector<WString>::type& getFileList() const { return *mFileList; }
+
+		void _clear();
+		void _setFileList(const Vector<WString>::type& fileList);
+		void _setActive(bool active) { mActive = active; }
+
+		bool _isInside(const Int2& pos) const;
+		bool _isActive() const { return mActive; }
 	private:
 		friend class Platform;
 
 		OSDropTarget(const RenderWindow* ownerWindow, INT32 x, INT32 y, UINT32 width, UINT32 height);
 		~OSDropTarget();
 
-		void clean();
-		void setFileList(const Vector<WString>::type& fileList);
 		const RenderWindow* getOwnerWindow() const { return mOwnerWindow; }
 	private:
 		INT32 mX, mY;
 		UINT32 mWidth, mHeight;
+		bool mActive;
 		const RenderWindow* mOwnerWindow;
 
 		OSDropType mDropType;

+ 1 - 0
CamelotCore/Include/CmPrerequisites.h

@@ -143,6 +143,7 @@ namespace CamelotFramework {
 	class Texture;
 	class Mesh;
 	class Font;
+	class OSDropTarget;
 	// Scene
 	class SceneObject;
 	class Component;

+ 152 - 16
CamelotCore/Include/Win32/CmWin32DropTarget.h

@@ -16,18 +16,57 @@ namespace CamelotFramework
 	*/
 	class Win32DropTarget : public IDropTarget
 	{
+		enum class DropOpType
+		{
+			DragOver,
+			Drop,
+			Leave
+		};
+
+		enum class DropOpDataType
+		{
+			FileList,
+			None
+		};
+
+		struct DropTargetOp
+		{
+			DropTargetOp(DropOpType _type, const Int2& _pos)
+				:type(_type), position(_pos), dataType(DropOpDataType::None)
+			{ }
+
+			DropOpType type;
+			Int2 position;
+
+			DropOpDataType dataType;
+			union 
+			{
+				Vector<WString>::type* mFileList;
+			};
+		};
+
 	public:
 		Win32DropTarget(HWND hWnd)
 			:mHWnd(hWnd), mRefCount(1), mAcceptDrag(false)
 		{ }
 
 		~Win32DropTarget()
-		{ }
+		{
+			CM_LOCK_MUTEX(mSync);
+
+			for(auto& fileList : mFileLists)
+			{
+				cm_delete(fileList);
+			}
+
+			mFileLists.clear();
+			mQueuedDropOps.clear();
+		}
 
 		void registerWithOS()
 		{
 			CoLockObjectExternal(this, TRUE, FALSE);
-			RegisterDragDrop(mHWnd, this);
+			HRESULT hr = RegisterDragDrop(mHWnd, this);
 		}
 
 		void unregisterWithOS()
@@ -73,46 +112,84 @@ namespace CamelotFramework
 
 		HRESULT __stdcall DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
 		{
-			*pdwEffect = DROPEFFECT_NONE;
+			*pdwEffect = DROPEFFECT_LINK;
 
 			mAcceptDrag = isDataValid(pDataObj);
 			if(!mAcceptDrag)
 				return S_OK;
-			
-			// TODO - Queue (or set) move command to use later in update()
 
+			{
+				CM_LOCK_MUTEX(mSync);
+
+				mFileLists.push_back(getFileListFromData(pDataObj));
+
+				ScreenToClient(mHWnd, (POINT *)&pt);
+				mQueuedDropOps.push_back(DropTargetOp(DropOpType::DragOver, Int2((int)pt.x, (int)pt.y)));
+
+				DropTargetOp& op = mQueuedDropOps.back();
+				op.dataType = DropOpDataType::FileList;
+				op.mFileList = mFileLists.back();
+			}
+			
 			return S_OK;
 		}
 
 		HRESULT __stdcall DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
 		{
-			*pdwEffect = DROPEFFECT_NONE;
+			*pdwEffect = DROPEFFECT_LINK;
 
 			if(!mAcceptDrag)
 				return S_OK;
 
-			// TODO - Queue (or set) move command to use later in update()
+			{
+				CM_LOCK_MUTEX(mSync);
+
+				ScreenToClient(mHWnd, (POINT *)&pt);
+				mQueuedDropOps.push_back(DropTargetOp(DropOpType::DragOver, Int2((int)pt.x, (int)pt.y)));
+
+				DropTargetOp& op = mQueuedDropOps.back();
+				op.dataType = DropOpDataType::FileList;
+				op.mFileList = mFileLists.back();
+			}
 			
 			return S_OK;
 		} 
 
 		HRESULT __stdcall DragLeave()
 		{
-			// Do nothing
+			{
+				CM_LOCK_MUTEX(mSync);
+
+				mQueuedDropOps.push_back(DropTargetOp(DropOpType::Leave, Int2()));
+
+				DropTargetOp& op = mQueuedDropOps.back();
+				op.dataType = DropOpDataType::FileList;
+				op.mFileList = mFileLists.back();
+			}
 
 			return S_OK;
 		}
 
 		HRESULT __stdcall Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
 		{
-			*pdwEffect = DROPEFFECT_NONE;
+			*pdwEffect = DROPEFFECT_LINK;
+			mAcceptDrag = false;
 
 			if(!isDataValid(pDataObj))
 				return S_OK;
 
-			Vector<WString>::type fileList = getFileListFromData(pDataObj);
+			{
+				CM_LOCK_MUTEX(mSync);
+
+				mFileLists.push_back(getFileListFromData(pDataObj));
 
-			// TODO - Add drop operation to a queue, and process it in update()
+				ScreenToClient(mHWnd, (POINT *)&pt);
+				mQueuedDropOps.push_back(DropTargetOp(DropOpType::Drop, Int2((int)pt.x, (int)pt.y)));
+
+				DropTargetOp& op = mQueuedDropOps.back();
+				op.dataType = DropOpDataType::FileList;
+				op.mFileList = mFileLists.back();
+			}
 
 			return S_OK;
 		}
@@ -140,7 +217,59 @@ namespace CamelotFramework
 		 */
 		void update()
 		{
-			// TODO - Process received data and notify child OSDropTargets
+			CM_LOCK_MUTEX(mSync);
+
+			for(auto& op: mQueuedDropOps)
+			{
+				for(auto& target : mDropTargets)
+				{
+					if(op.type != DropOpType::Leave)
+					{
+						if(target->_isInside(op.position))
+						{
+							if(!target->_isActive())
+							{
+								target->_setFileList(*op.mFileList);
+								target->onEnter(op.position.x, op.position.y);
+							}
+
+							if(op.type == DropOpType::DragOver)
+								target->onDragOver(op.position.x, op.position.y);
+							else if(op.type == DropOpType::Drop)
+							{
+								target->_setFileList(*op.mFileList);
+								target->onDrop(op.position.x, op.position.y);
+							}
+						}
+						else
+						{
+							if(target->_isActive())
+							{
+								target->onLeave();
+								target->_clear();
+								target->_setActive(false);
+							}
+						}
+					}
+					else
+					{
+						if(target->_isActive())
+						{
+							target->onLeave();
+							target->_clear();
+							target->_setActive(false);
+						}
+					}
+				}
+
+				if(op.type == DropOpType::Leave || op.type == DropOpType::Drop)
+				{
+					cm_delete(op.mFileList);
+					mFileLists.erase(mFileLists.begin());
+				}
+			}
+
+			mQueuedDropOps.clear();
 		}
 	private:
 		bool isDataValid(IDataObject* data)
@@ -151,12 +280,12 @@ namespace CamelotFramework
 			return data->QueryGetData(&fmtetc) == S_OK ? true : false;
 		}
 
-		Vector<WString>::type getFileListFromData(IDataObject* data)
+		Vector<WString>::type* getFileListFromData(IDataObject* data)
 		{
 			FORMATETC fmtetc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
 			STGMEDIUM stgmed;
 
-			Vector<WString>::type files;
+			Vector<WString>::type* files = cm_new<Vector<WString>::type>();
 			if(data->GetData(&fmtetc, &stgmed) == S_OK)
 			{
 				PVOID data = GlobalLock(stgmed.hGlobal);
@@ -164,7 +293,7 @@ namespace CamelotFramework
 				HDROP hDrop = (HDROP)data;
 				UINT numFiles = DragQueryFileW(hDrop, 0xFFFFFFFF, nullptr, 0);
 
-				files.resize(numFiles);
+				files->resize(numFiles);
 				for(UINT i = 0; i < numFiles; i++)
 				{
 					UINT numChars = DragQueryFileW(hDrop, i, nullptr, 0) + 1;
@@ -172,7 +301,9 @@ namespace CamelotFramework
 
 					DragQueryFileW(hDrop, i, buffer, numChars);
 
-					files[i] = WString(buffer);
+					(*files)[i] = WString(buffer);
+
+					cm_free(buffer);
 				}
 
 				GlobalUnlock(stgmed.hGlobal);
@@ -188,5 +319,10 @@ namespace CamelotFramework
 		LONG mRefCount;
 		HWND mHWnd;
 		bool mAcceptDrag;
+
+		Vector<DropTargetOp>::type mQueuedDropOps;
+		Vector<Vector<WString>::type*>::type mFileLists; 
+
+		CM_MUTEX(mSync);
 	};
 }

+ 3 - 0
CamelotCore/Source/CmApplication.cpp

@@ -48,6 +48,7 @@ namespace CamelotFramework
 
 	void Application::startUp(START_UP_DESC& desc)
 	{
+		Platform::startUp();
 		MemStack::setupHeap(HID_Main);
 
 		StringTable::startUp(cm_new<StringTable>());
@@ -182,6 +183,8 @@ namespace CamelotFramework
 		Time::shutDown();
 		DeferredCallManager::shutDown();
 		StringTable::shutDown();
+
+		Platform::shutDown();
 	}
 
 	void* Application::loadPlugin(const String& pluginName)

+ 17 - 5
CamelotCore/Source/CmPlatform.cpp

@@ -3,14 +3,15 @@
 namespace CamelotFramework
 {
 	OSDropTarget::OSDropTarget(const RenderWindow* ownerWindow, INT32 x, INT32 y, UINT32 width, UINT32 height)
-		:mOwnerWindow(ownerWindow), mX(x), mY(y), mWidth(width), mHeight(height), mDropType(OSDropType::None), mFileList(nullptr)
+		:mOwnerWindow(ownerWindow), mX(x), mY(y), mWidth(width), mHeight(height), mDropType(OSDropType::None), 
+		mFileList(nullptr), mActive(false)
 	{
 		
 	}
 
 	OSDropTarget::~OSDropTarget()
 	{
-		clean();
+		_clear();
 	}
 
 	void OSDropTarget::setArea(INT32 x, INT32 y, UINT32 width, UINT32 height)
@@ -21,15 +22,26 @@ namespace CamelotFramework
 		mHeight = height;
 	}
 
-	void OSDropTarget::clean()
+	void OSDropTarget::_clear()
 	{
 		if(mFileList != nullptr)
+		{
 			cm_delete(mFileList);
+			mFileList = nullptr;
+		}
 	}
 
-	void OSDropTarget::setFileList(const Vector<WString>::type& fileList)
+	bool OSDropTarget::_isInside(const Int2& pos) const
 	{
-		clean();
+		INT32 right = mX + mWidth;
+		INT32 bottom = mY + mHeight;
+
+		return (pos.x >= mX && pos.x < right && pos.y >= mY && pos.y < bottom);
+	}
+
+	void OSDropTarget::_setFileList(const Vector<WString>::type& fileList)
+	{
+		_clear();
 
 		mFileList = cm_new<Vector<WString>::type>();
 		*mFileList = fileList;

+ 6 - 2
CamelotCore/Source/Win32/CmPlatformImpl.cpp

@@ -416,8 +416,12 @@ namespace CamelotFramework
 
 			if(win32DropTarget->getNumDropTargets() == 0)
 			{
-				CM_LOCK_MUTEX(mSync);
-				mDropTargets.data->dropTargetsToDestroy.push_back(win32DropTarget);
+				mDropTargets.data->dropTargetsPerWindow.erase(iterFind);
+
+				{
+					CM_LOCK_MUTEX(mSync);
+					mDropTargets.data->dropTargetsToDestroy.push_back(win32DropTarget);
+				}
 			}
 		}