neoGFX
Cross-platform C++ app/game engine
Loading...
Searching...
No Matches
drag_drop.hpp
Go to the documentation of this file.
1// drag_drop.hpp
2/*
3 neogfx C++ App/Game Engine
4 Copyright (c) 2020 Leigh Johnston. All Rights Reserved.
5
6 This program is free software: you can redistribute it and / or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#pragma once
21
22#include <neogfx/neogfx.hpp>
26
27namespace neogfx
28{
29 template <typename DragDropObjectInterface>
30 class drag_drop_object : public DragDropObjectInterface
31 {
33 public:
34 typedef DragDropObjectInterface object_interface;
35 public:
36 drag_drop_object(i_drag_drop_source& aSource, drag_drop_object_type_id aType = object_interface::otid()) :
37 iSource{ aSource },
38 iType{ aType }
39 {
40 }
41 public:
43 {
44 return iSource;
45 }
47 {
48 return iType;
49 }
50 public:
51 bool can_render() const override
52 {
53 return false;
54 }
55 size render_extents() const override
56 {
57 return {};
58 }
59 void render(i_graphics_context& aGc, point const& aPosition = {}) const override
60 {
61 }
62 private:
63 i_drag_drop_source& iSource;
65 };
66
67 class drag_drop_file_list : public drag_drop_object<i_drag_drop_file_list>
68 {
70 public:
71 template <typename... Files>
72 drag_drop_file_list(i_drag_drop_source& aSource, Files&&... aFiles) :
73 base_type{ aSource },
74 iFilePaths{ std::forward<Files>(aFiles)... }
75 {
76 }
77 public:
79 {
80 return iFilePaths;
81 }
82 private:
83 neolib::vector<string> iFilePaths;
84 };
85
86 class drag_drop_item : public drag_drop_object<i_drag_drop_item>
87 {
89 public:
90 drag_drop_item(i_drag_drop_source& aSource, i_item_presentation_model const& aPresentationModel, item_presentation_model_index const& aItem) :
91 base_type{ aSource },
92 iPresentationModel{ aPresentationModel },
93 iItem{ aItem }
94 {
95 }
96 public:
97 i_item_presentation_model const& presentation_model() const final
98 {
99 return iPresentationModel;
100 }
101 item_presentation_model_index const& index() const final
102 {
103 return iItem;
104 }
105 public:
106 bool can_render() const final
107 {
108 bool canRender = false;
109 size renderExtents;
110 presentation_model().dragging_item_render_info().trigger(*this, canRender, renderExtents);
111 return canRender;
112 }
113 size render_extents() const final
114 {
115 bool canRender = false;
116 size renderExtents;
117 presentation_model().dragging_item_render_info().trigger(*this, canRender, renderExtents);
118 return renderExtents;
119 }
120 void render(i_graphics_context& aGc, point const& aPosition = {}) const final
121 {
122 presentation_model().dragging_item_render().trigger(*this, aGc, aPosition);
123 }
124 private:
125 i_item_presentation_model const& iPresentationModel;
126 item_presentation_model_index iItem;
127 };
128
129 class drag_drop_entity : public drag_drop_object<i_drag_drop_entity>
130 {
132 public:
134 base_type{ aSource },
135 iEcs{ aEcs },
136 iEntity{ aEntity }
137 {
138 }
139 public:
140 game::i_ecs const& ecs() const final
141 {
142 return iEcs;
143 }
145 {
146 return iEntity;
147 }
148 private:
149 game::i_ecs const& iEcs;
150 game::entity_id iEntity;
151 };
152
154
155 template <typename Base = drag_drop_source_empty_base>
157 {
158 typedef Base base_type;
159 public:
160 define_declared_event(DraggingObject, dragging_object, i_drag_drop_object const&)
162 define_declared_event(ObjectDroppedOnTarget, object_dropped_on_target, i_drag_drop_object const&, i_drag_drop_target&)
163 public:
164 template <typename... Args>
165 drag_drop_source(Args&&... aArgs) :
166 base_type{ std::forward<Args>(aArgs)... }, iEnabled{ false }, iObject{ nullptr }, iMonitor{ nullptr }
167 {
168 }
173 public:
174 bool drag_drop_source_enabled() const final
175 {
176 return iEnabled;
177 }
178 void enable_drag_drop_source(bool aEnable = true) override
179 {
180 if (iEnabled != aEnable)
181 {
182 iEnabled = aEnable;
184 {
185 if constexpr (std::is_base_of_v<i_widget, base_type>)
187 service<i_drag_drop>().register_source(*this);
188 }
189 else
190 {
191 service<i_drag_drop>().unregister_source(*this);
193 }
194 }
195 }
196 bool drag_drop_active() const final
197 {
198 return iObject != nullptr;
199 }
201 {
202 if (!drag_drop_active())
203 throw drag_drop_not_active();
204 return *iObject;
205 }
206 void start_drag_drop(i_drag_drop_object const& aObject) final
207 {
210 if (drag_drop_active())
212 iObject = &aObject;
213 DraggingObject.trigger(*iObject);
214 }
215 void cancel_drag_drop() final
216 {
217 if (!drag_drop_active())
218 throw drag_drop_not_active();
219 auto object = iObject;
220 iObject = nullptr;
221 DraggingCancelled.trigger(*object);
222 iWidget = nullptr;
223 iWidgetOffset = std::nullopt;
224 }
226 {
227 if (!drag_drop_active())
228 throw drag_drop_not_active();
229 auto object = iObject;
230 iObject = nullptr;
231 ObjectDroppedOnTarget.trigger(*object, aTarget);
232 iWidget = nullptr;
233 iWidgetOffset = std::nullopt;
234 }
235 public:
237 {
238 if (drag_drop_active())
239 return *iTrackCurrent;
240 throw drag_drop_not_active();
241 }
243 {
244 return iWidget;
245 }
246 void set_drag_drop_widget(i_ref_ptr<i_widget> const& aWidget) final
247 {
248 iWidget = aWidget;
249 if (iWidget)
250 iWidgetOffset = iWidget->position() - drag_drop_tracking_position();
251 else
252 iWidgetOffset = std::nullopt;
253 }
254 public:
256 {
257 if (iMonitor != nullptr)
258 return *iMonitor;
260 }
262 {
263 iMonitor = &aWidget;
264 iSink = aWidget.mouse_event([this, &aWidget](const neogfx::mouse_event& aEvent)
265 {
266 handle_drag_drop_event(aWidget, aEvent);
267 });
268 iSink += aWidget.root().paint_overlay([&](i_graphics_context& aGc)
269 {
270 if (drag_drop_active() && object_being_dragged().can_render() && iTrackCurrent)
271 object_being_dragged().render(aGc, aWidget.to_window_coordinates(*iTrackCurrent));
272 });
273 }
275 {
276 iMonitor = nullptr;
277 iSink.clear();
278 }
279 protected:
281 {
282 return iTriggerDistance;
283 }
285 {
286 iTriggerDistance = aDistance;
287 }
288 protected:
289 virtual bool is_drag_drop_object(point const& aPosition) const = 0;
290 virtual i_drag_drop_object const* drag_drop_object(point const& aPosition) = 0;
291 private:
292 void handle_drag_drop_event(i_widget& aWidget, const neogfx::mouse_event& aEvent)
293 {
294 auto const eventPos = aEvent.position() - aWidget.origin();
295 switch (aEvent.type())
296 {
298 if (aEvent.is_left_button() && is_drag_drop_object(eventPos))
299 {
300 aWidget.set_capture();
301 iTrackStart = eventPos;
302 iTrackCurrent = eventPos;
303 }
304 break;
306 if (iTrackStart)
307 {
308 iTrackCurrent = eventPos;
309 bool needUpdate = false;
310 if ((*iTrackStart - *iTrackCurrent).to_vec2().magnitude() >= drag_drop_trigger_distance())
311 {
312 if (!drag_drop_active())
313 start_drag_drop(*drag_drop_object(*iTrackStart));
314 needUpdate = object_being_dragged().can_render();
315 if (iWidget)
316 {
317 iWidget->move(*iTrackCurrent + *iWidgetOffset);
318 auto const windowPosition = aWidget.to_window_coordinates(eventPos) + aWidget.root().window_position();
319 iWidget->set_opacity(service<i_drag_drop>().is_target_at(object_being_dragged(), windowPosition) ? 1.0 : 0.25);
320 }
321 }
322 else
323 {
324 if (drag_drop_active())
325 {
326 needUpdate = object_being_dragged().can_render();
328 }
329 }
330 if (needUpdate)
331 aWidget.root().as_widget().update();
332 }
333 break;
335 if (iTrackStart)
336 {
337 if (aEvent.is_left_button())
338 {
339 iTrackStart = std::nullopt;
340 iTrackCurrent = std::nullopt;
341 if (drag_drop_active())
342 {
343 if (object_being_dragged().can_render())
344 aWidget.root().as_widget().update();
345 auto const windowPosition = aWidget.to_window_coordinates(eventPos) + aWidget.root().window_position();
346 if (service<i_drag_drop>().is_target_at(object_being_dragged(), windowPosition))
347 {
348 if (iWidget)
349 iWidget->set_opacity(1.0);
350 auto& target = service<i_drag_drop>().target_at(object_being_dragged(), windowPosition);
351 target.accept(object_being_dragged(), target.as_widget().to_client_coordinates(windowPosition - aWidget.root().window_position()));
352 end_drag_drop(target);
353 }
354 else
356 }
357 }
358 }
359 break;
360 }
361 }
362 private:
363 bool iEnabled;
364 i_drag_drop_object const* iObject;
365 i_widget* iMonitor;
366 sink iSink;
367 optional_point iTrackStart;
368 optional_point iTrackCurrent;
369 scalar iTriggerDistance = 8.0;
370 ref_ptr<i_widget> iWidget;
371 optional_point iWidgetOffset;
372 };
373
374 template <typename Base>
376 {
378 public:
379 define_declared_event(ObjectAcceptable, object_acceptable, i_drag_drop_object const&, optional_point const&, drop_operation&)
380 define_declared_event(ObjectDropped, object_dropped, i_drag_drop_object const&, optional_point const&)
381 public:
382 template <typename... Args>
383 drag_drop_target(Args&&... aArgs) :
384 base_type{ std::forward<Args>(aArgs)... }, iEnabled{ false }
385 {
386 }
388 {
390 }
391 public:
392 bool drag_drop_target_enabled() const final
393 {
394 return iEnabled;
395 }
396 void enable_drag_drop_target(bool aEnable = true) final
397 {
398 if (iEnabled != aEnable)
399 {
400 iEnabled = aEnable;
402 service<i_drag_drop>().register_target(*this);
403 else
404 service<i_drag_drop>().unregister_target(*this);
405 }
406 }
407 public:
408 bool can_accept(i_drag_drop_object const& aObject, optional_point const& aDropPosition = {}) const final
409 {
410 return accepted_as(aObject, aDropPosition) != drop_operation::None;
411 }
412 drop_operation accepted_as(i_drag_drop_object const& aObject, optional_point const& aDropPosition = {}) const final
413 {
415 ObjectAcceptable.trigger(aObject, aDropPosition, acceptableAs);
416 return acceptableAs;
417 }
418 bool accept(i_drag_drop_object const& aObject, optional_point const& aDropPosition = {}) final
419 {
420 if (can_accept(aObject, aDropPosition))
421 {
422 ObjectDropped.trigger(aObject, aDropPosition);
423 return true;
424 }
425 return false;
426 }
427 public:
428 bool is_widget() const final
429 {
430 return std::is_base_of_v<i_widget, base_type>;
431 }
432 i_widget const& as_widget() const final
433 {
434 if constexpr (std::is_base_of_v<i_widget, base_type>)
435 return *this;
436 else
438 }
440 {
441 return const_cast<i_widget&>(to_const(*this).as_widget());
442 }
443 private:
444 bool iEnabled;
445 };
446
447 class drag_drop : public i_drag_drop
448 {
449 public:
450 define_declared_event(SourceRegistered, source_registered, i_drag_drop_source&)
452 define_declared_event(TargetRegistered, target_registered, i_drag_drop_target&)
454 private:
455 typedef std::vector<std::shared_ptr<i_drag_drop_source>> sources_t;
456 typedef std::vector<i_drag_drop_target*> targets_t;
457 public:
459 public:
460 void register_source(i_drag_drop_source& aSource) override;
461 void unregister_source(i_drag_drop_source& aSource) override;
462 void register_target(i_drag_drop_target& aTarget) override;
463 void unregister_target(i_drag_drop_target& aTarget) override;
464 public:
465 bool is_target_for(i_drag_drop_object const& aObject) const final;
466 bool is_target_at(i_drag_drop_object const& aObject, point const& aPosition) const final;
468 i_drag_drop_target& target_at(i_drag_drop_object const& aObject, point const& aPosition) const final;
469 protected:
470 void register_source(std::shared_ptr<i_drag_drop_source> const& aSource);
472 i_drag_drop_target* find_target(i_drag_drop_object const& aObject, point const& aPosition) const;
473 private:
474 sources_t iSources;
475 targets_t iTargets;
476 };
477}
mouse_event_type type() const
neogfx::point position() const
game::i_ecs const & ecs() const final
game::entity_id entity() const final
drag_drop_entity(i_drag_drop_source &aSource, game::i_ecs const &aEcs, game::entity_id aEntity)
drag_drop_file_list(i_drag_drop_source &aSource, Files &&... aFiles)
Definition drag_drop.hpp:72
neolib::vector< string > const & file_paths() const final
Definition drag_drop.hpp:78
i_item_presentation_model const & presentation_model() const final
Definition drag_drop.hpp:97
item_presentation_model_index const & index() const final
size render_extents() const final
void render(i_graphics_context &aGc, point const &aPosition={}) const final
drag_drop_item(i_drag_drop_source &aSource, i_item_presentation_model const &aPresentationModel, item_presentation_model_index const &aItem)
Definition drag_drop.hpp:90
bool can_render() const final
drag_drop_object_type_id ddo_type() const final
Definition drag_drop.hpp:46
bool can_render() const override
Definition drag_drop.hpp:51
i_drag_drop_source & source() const final
Definition drag_drop.hpp:42
DragDropObjectInterface object_interface
Definition drag_drop.hpp:34
void render(i_graphics_context &aGc, point const &aPosition={}) const override
Definition drag_drop.hpp:59
size render_extents() const override
Definition drag_drop.hpp:55
drag_drop_object(i_drag_drop_source &aSource, drag_drop_object_type_id aType=object_interface::otid())
Definition drag_drop.hpp:36
i_drag_drop_object const & object_being_dragged() const final
void set_drag_drop_widget(i_ref_ptr< i_widget > const &aWidget) final
scalar drag_drop_trigger_distance() const
void enable_drag_drop_source(bool aEnable=true) override
bool drag_drop_active() const final
bool drag_drop_source_enabled() const final
void monitor_drag_drop_events(i_widget &aWidget) final
void stop_monitoring_drag_drop_events() final
virtual bool is_drag_drop_object(point const &aPosition) const =0
void set_drag_drop_trigger_distance(scalar aDistance)
void start_drag_drop(i_drag_drop_object const &aObject) final
point const & drag_drop_tracking_position() const final
void cancel_drag_drop() final
void end_drag_drop(i_drag_drop_target &aTarget) final
virtual i_drag_drop_object const * drag_drop_object(point const &aPosition)=0
i_ref_ptr< i_widget > const & drag_drop_widget() const final
i_widget & drag_drop_event_monitor() const final
define_declared_event(DraggingObject, dragging_object, i_drag_drop_object const &) define_declared_event(DraggingCancelled
bool drag_drop_target_enabled() const final
bool is_widget() const final
bool accept(i_drag_drop_object const &aObject, optional_point const &aDropPosition={}) final
void enable_drag_drop_target(bool aEnable=true) final
i_widget const & as_widget() const final
drop_operation accepted_as(i_drag_drop_object const &aObject, optional_point const &aDropPosition={}) const final
bool can_accept(i_drag_drop_object const &aObject, optional_point const &aDropPosition={}) const final
i_widget & as_widget() final
i_drag_drop_target & target_at(i_drag_drop_object const &aObject, point const &aPosition) const final
void unregister_source(i_drag_drop_source &aSource) override
void register_source(i_drag_drop_source &aSource) override
bool is_target_at(i_drag_drop_object const &aObject, point const &aPosition) const final
i_drag_drop_source target_unregistered
define_declared_event(SourceRegistered, source_registered, i_drag_drop_source &) define_declared_event(SourceUnregistered
i_drag_drop_target & target_for(i_drag_drop_object const &aObject) const final
bool is_target_for(i_drag_drop_object const &aObject) const final
void register_target(i_drag_drop_target &aTarget) override
i_drag_drop_target * find_target(i_drag_drop_object const &aObject) const
void unregister_target(i_drag_drop_target &aTarget) override
virtual void render(i_graphics_context &aGc, point const &aPosition={}) const =0
virtual bool can_render() const =0
virtual const i_window & root() const =0
virtual void set_capture(capture_reason aReason=capture_reason::Other, const optional_point &aPosition={})=0
virtual bool update(bool aIncludeNonClient=false)=0
point to_window_coordinates(const point &aClientCoordinates) const
Definition i_widget.hpp:317
virtual const i_widget & as_widget() const =0
virtual point window_position() const =0
void clear()
Definition i_event.hpp:290
uuid drag_drop_object_type_id
id_t entity_id
Definition ecs_ids.hpp:51
double scalar
Definition numerical.hpp:63
Definition plf_hive.h:79
#define meta_object(...)
Definition i_object.hpp:28
#define define_declared_event(name, declName,...)
Definition event.hpp:195