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#pragma once
4
5#include "BsPrerequisites.h"
6#include "Utility/BsModule.h"
7#include "Input/BsInput.h"
8#include "Utility/BsEvent.h"
9#include <atomic>
10
11namespace bs
12{
13 /** @addtogroup GUI-Internal
14 * @{
15 */
16
17 /** Holds data returned by DragAndDropManager callbacks. */
18 struct BS_EXPORT DragCallbackInfo
19 {
20 bool processed = false;
21 };
22
23 /**
24 * Handles GUI drag and drop operations. When active GUI elements will be notified of any drag events and will be able
25 * to retrieve dragged data.
26 *
27 * @note Sim thread only.
28 */
29 class BS_EXPORT DragAndDropManager : public Module<DragAndDropManager>
30 {
31 public:
32 DragAndDropManager();
33 ~DragAndDropManager();
34
35 /**
36 * Starts a drag operation of the specified type. This means GUI elements will start receiving drag and drop
37 * related events and they may choose to handle them.
38 *
39 * @param[in] typeId Type of the drag and drop operation that other objects may query and decide
40 * if they want to handle it. User defined.
41 * @param[in] data Some operation specific data that is just passed through to however needs it.
42 * @param[in] dropCallback The drop callback that gets triggered whenever mouse button is released and
43 * drag operation ends. You should perform any cleanup here.
44 * @param[in] needsValidDropTarget (optional) Determines whether the drop operation may happen anywhere or
45 * does the GUI element need to specifically accept the drag of this type.
46 * If false all GUI elements we mouse over will receive drag/drop events,
47 * otherwise only those that specifically subscribe to the specified drag
48 * operation of this typeId will.
49 * Additionally this will determine the cursor displayed (whether or not it
50 * can have a "denied" state).
51 */
52 void startDrag(UINT32 typeId, void* data, std::function<void(bool)> dropCallback, bool needsValidDropTarget = false);
53
54 /** Returns true if drag is currently in progress. */
55 bool isDragInProgress() const { return mIsDragInProgress; }
56
57 /** Get type ID of drag currently in progress. Only valid if drag is in progress. */
58 UINT32 getDragTypeId() const { return mDragTypeId; }
59
60 /** Gets drag specific data specified when the drag started. Only valid if drag is in progress. */
61 void* getDragData() const { return mData; }
62
63 /**
64 * Determines whether the drop operation may happen anywhere or does the GUI element need to specifically accept the
65 * drag of this type. If false all GUI elements we mouse over will receive drag/drop events, otherwise only those
66 * that specifically subscribe to the specified drag operation of this typeId will.
67 *
68 * Additionally this will determine the cursor displayed (whether or not it can have a "denied" state).
69 */
70 bool needsValidDropTarget() const { return mNeedsValidDropTarget; }
71
72 /**
73 * Registers a new callback that will be triggered when dragged item is dropped. Provided parameter specifies if
74 * the drop operation was handled by anyone or not.
75 */
76 void addDropCallback(std::function<void(bool)> dropCallback);
77
78 /** Called once per frame. Checks if drag ended or if window loses focus. */
79 void _update();
80
81 /**
82 * Triggers a callback when user releases the pointer and the drag operation ends. Provided parameters inform the
83 * subscriber where the pointer was released, and allows the subscriber to note whether the drag operation was
84 * processed or not.
85 *
86 * @note Internal event. You should use addDropCallback for normal use.
87 */
88 Event<void(const PointerEvent&, DragCallbackInfo&)> onDragEnded;
89 private:
90
91 /** Triggers any drop callbacks and clears callback data. */
92 void endDrag(bool processed);
93
94 /**
95 * Called by the core thread whenever mouse capture state changes. This can happen when window loses focus
96 * (for example alt+tab). In that case we want to end the drag even if the user is still holding the dragged item.
97 *
98 * @note Core thread.
99 */
100 void mouseCaptureChanged();
101
102 /** Called by the input system when pointer is released. */
103 void cursorReleased(const PointerEvent& event);
104
105 private:
106 UINT32 mDragTypeId = 0;
107 void* mData = nullptr;
108 Vector<std::function<void(bool)>> mDropCallbacks;
109 bool mIsDragInProgress = false;
110 bool mNeedsValidDropTarget = false;
111 HEvent mMouseCaptureChangedConn;
112
113 std::atomic<bool> mCaptureChanged { false };
114 std::atomic<int> mCaptureActive { 0 };
115 std::atomic<UINT64> mCaptureChangeFrame;
116 };
117
118 /** @} */
119}