| 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 | |
| 8 | using namespace std::placeholders; |
| 9 | |
| 10 | namespace 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 | } |