zng_wgt_input/
drag_drop.rs

1#![cfg(feature = "drag_drop")]
2
3//! Drag&drop properties, event properties.
4
5use zng_ext_input::drag_drop::{
6    DRAG_END_EVENT, DRAG_HOVERED_EVENT, DRAG_START_EVENT, DROP_EVENT, DragEndArgs, DragHoveredArgs, DragStartArgs, DropArgs,
7    WidgetInfoBuilderDragDropExt as _,
8};
9use zng_wgt::prelude::*;
10
11/// If this widget can be dragged in a drag&drop operation.
12///
13/// When this is `true` the widget can be dragged and dropped within the same app or it can handle [`on_drag_start`] and
14/// use the [`DRAG_DROP.drag`] service to set a system wide drag data.
15///
16/// [`on_drag_start`]: fn@on_drag_start
17/// [`DRAG_DROP.drag`]: zng_ext_input::drag_drop::DRAG_DROP::drag
18#[property(CONTEXT, default(false))]
19pub fn draggable(child: impl UiNode, input: impl IntoVar<bool>) -> impl UiNode {
20    let input = input.into_var();
21    match_node(child, move |_c, op| match op {
22        UiNodeOp::Init => {
23            WIDGET.sub_var_info(&input);
24        }
25        UiNodeOp::Info { info } => {
26            if input.get() {
27                info.draggable();
28            }
29        }
30        _ => {}
31    })
32}
33
34event_property! {
35    /// Draggable widget started dragging.
36    ///
37    /// To receive this event in a widget set [`draggable`] to `true`.
38    ///
39    /// [`draggable`]: fn@draggable
40    pub fn drag_start {
41        event: DRAG_START_EVENT,
42        args: DragStartArgs,
43    }
44
45    /// Draggable widget stopped dragging.
46    ///
47    /// This event is always paired with [`on_drag_start`] first.
48    ///
49    /// [`on_drag_start`]: fn@on_drag_start
50    pub fn drag_end {
51        event: DRAG_END_EVENT,
52        args: DragEndArgs,
53    }
54
55    /// Dragging cursor entered or exited the widget area and the widget is enabled.
56    pub fn drag_hovered {
57        event: DRAG_HOVERED_EVENT,
58        args: DragHoveredArgs,
59        filter: |args| args.is_drag_enter_enabled()
60    }
61    /// Dragging cursor entered the widget area and the widget is enabled.
62    pub fn drag_enter {
63        event: DRAG_HOVERED_EVENT,
64        args: DragHoveredArgs,
65        filter: |args| args.is_drag_enter_enabled(),
66    }
67    /// Dragging cursor exited the widget area and the widget is enabled.
68    pub fn drag_leave {
69        event: DRAG_HOVERED_EVENT,
70        args: DragHoveredArgs,
71        filter: |args| args.is_drag_leave_enabled(),
72    }
73
74    /// Dragging cursor dropped data in the widget area and the widget is enabled.
75    pub fn drop {
76        event: DROP_EVENT,
77        args: DropArgs,
78        filter: |args| args.is_enabled(WIDGET.id()),
79    }
80}
81
82/// If the dragging cursor is over the widget or a descendant and the widget is enabled.
83///
84/// The value is always `false` when the widget is not [`ENABLED`], use [`is_drag_hovered_disabled`] to implement *disabled hovered* visuals.
85///
86/// [`ENABLED`]: Interactivity::ENABLED
87/// [`is_drag_hovered_disabled`]: fn@is_drag_hovered_disabled
88#[property(EVENT)]
89pub fn is_drag_hovered(child: impl UiNode, state: impl IntoVar<bool>) -> impl UiNode {
90    event_state(child, state, false, DRAG_HOVERED_EVENT, |args| {
91        if args.is_drag_enter_enabled() {
92            Some(true)
93        } else if args.is_drag_leave_enabled() {
94            Some(false)
95        } else {
96            None
97        }
98    })
99}
100
101/// If the dragging cursor is over the widget or a descendant and the widget is disabled.
102#[property(EVENT)]
103pub fn is_drag_hovered_disabled(child: impl UiNode, state: impl IntoVar<bool>) -> impl UiNode {
104    event_state(child, state, false, DRAG_HOVERED_EVENT, |args| {
105        if args.is_drag_enter_disabled() {
106            Some(true)
107        } else if args.is_drag_leave_disabled() {
108            Some(false)
109        } else {
110            None
111        }
112    })
113}
114
115/// If the draggable widget is dragging.
116#[property(EVENT)]
117pub fn is_dragging(child: impl UiNode, state: impl IntoVar<bool>) -> impl UiNode {
118    let state = state.into_var();
119    match_node(child, move |_, op| match op {
120        UiNodeOp::Init => {
121            WIDGET.sub_event(&DRAG_START_EVENT).sub_event(&DRAG_END_EVENT);
122        }
123        UiNodeOp::Deinit => {
124            let _ = state.set(false);
125        }
126        UiNodeOp::Event { update } => {
127            if let Some(args) = DRAG_START_EVENT.on(update) {
128                if args.target.contains(WIDGET.id()) {
129                    let _ = state.set(true);
130                }
131            } else if let Some(args) = DRAG_END_EVENT.on(update) {
132                if args.target.contains(WIDGET.id()) {
133                    let _ = state.set(false);
134                }
135            }
136        }
137        _ => {}
138    })
139}