1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "GUI/BsDragAndDropManager.h"
4#include "Platform/BsPlatform.h"
5#include "BsCoreApplication.h"
6#include "Utility/BsTime.h"
7
8using namespace std::placeholders;
9
10namespace bs
11{
12 DragAndDropManager::DragAndDropManager()
13 {
14 mMouseCaptureChangedConn = Platform::onMouseCaptureChanged.connect(std::bind(&DragAndDropManager::mouseCaptureChanged, this));
15 Input::instance().onPointerReleased.connect(std::bind(&DragAndDropManager::cursorReleased, this, _1));
16 }
17
18 DragAndDropManager::~DragAndDropManager()
19 {
20 mMouseCaptureChangedConn.disconnect();
21 }
22
23 void DragAndDropManager::addDropCallback(std::function<void(bool)> dropCallback)
24 {
25 mDropCallbacks.push_back(dropCallback);
26 }
27
28 void DragAndDropManager::startDrag(UINT32 typeId, void* data, std::function<void(bool)> dropCallback, bool needsValidDropTarget)
29 {
30 if (mIsDragInProgress)
31 endDrag(false);
32
33 mDragTypeId = typeId;
34 mData = data;
35 mNeedsValidDropTarget = needsValidDropTarget;
36 addDropCallback(dropCallback);
37 mIsDragInProgress = true;
38
39 mCaptureActive.store(false);
40 mCaptureChanged.store(false);
41
42 Platform::captureMouse(*gCoreApplication().getPrimaryWindow());
43 }
44
45 void DragAndDropManager::_update()
46 {
47 if(!mIsDragInProgress)
48 return;
49
50 // This generally happens when window loses focus and capture is lost (for example alt+tab)
51 int captureActive = mCaptureActive.load();
52 if (!captureActive && mCaptureChanged.load() &&
53 (gTime().getFrameIdx() > mCaptureChangeFrame.load())) // Wait one frame to ensure input (like mouse up) gets a chance to be processed
54 {
55 endDrag(false);
56 mCaptureChanged.store(false);
57 }
58 }
59
60 void DragAndDropManager::endDrag(bool processed)
61 {
62 for(auto& callback : mDropCallbacks)
63 callback(processed);
64
65 mDragTypeId = 0;
66 mData = nullptr;
67 mDropCallbacks.clear();
68 mIsDragInProgress = false;
69 }
70
71 void DragAndDropManager::mouseCaptureChanged()
72 {
73 mCaptureActive.fetch_xor(1); // mCaptureActive = !mCaptureActive;
74 mCaptureChanged.store(true);
75 mCaptureChangeFrame.store(gTime().getFrameIdx());
76 }
77
78 void DragAndDropManager::cursorReleased(const PointerEvent& event)
79 {
80 if(!mIsDragInProgress)
81 return;
82
83 if(!onDragEnded.empty())
84 {
85 DragCallbackInfo info;
86 onDragEnded(event, info);
87
88 endDrag(info.processed);
89 }
90 else
91 endDrag(false);
92
93 Platform::releaseMouseCapture();
94 }
95}