Skip to main content

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::{node::bind_state_init, 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 IntoUiNode, input: impl IntoVar<bool>) -> 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 } if input.get() => {
26            info.draggable();
27        }
28        _ => {}
29    })
30}
31
32event_property! {
33    /// Draggable widget started dragging.
34    ///
35    /// To receive this event in a widget set [`draggable`] to `true`.
36    ///
37    /// [`draggable`]: fn@draggable
38    #[property(EVENT)]
39    pub fn on_drag_start<on_pre_drag_start>(child: impl IntoUiNode, handler: Handler<DragStartArgs>) -> UiNode {
40        const PRE: bool;
41        EventNodeBuilder::new(DRAG_START_EVENT).build::<PRE>(child, handler)
42    }
43
44    /// Draggable widget stopped dragging.
45    ///
46    /// This event is always paired with [`on_drag_start`] first.
47    ///
48    /// [`on_drag_start`]: fn@on_drag_start
49    #[property(EVENT)]
50    pub fn on_drag_end<on_pre_drag_end>(child: impl IntoUiNode, handler: Handler<DragEndArgs>) -> UiNode {
51        const PRE: bool;
52        EventNodeBuilder::new(DRAG_END_EVENT).build::<PRE>(child, handler)
53    }
54
55    /// Dragging cursor entered or exited the widget area and the widget is enabled.
56    #[property(EVENT)]
57    pub fn on_drag_hovered<on_pre_drag_hovered>(child: impl IntoUiNode, handler: Handler<DragHoveredArgs>) -> UiNode {
58        const PRE: bool;
59        EventNodeBuilder::new(DRAG_HOVERED_EVENT)
60            .filter(|| {
61                let id = WIDGET.id();
62                move |args| args.is_drag_enter_enabled(id) || args.is_drag_leave_enabled(id)
63            })
64            .build::<PRE>(child, handler)
65    }
66    /// Dragging cursor entered the widget area and the widget is enabled.
67    #[property(EVENT)]
68    pub fn on_drag_enter<on_pre_drag_enter>(child: impl IntoUiNode, handler: Handler<DragHoveredArgs>) -> UiNode {
69        const PRE: bool;
70        EventNodeBuilder::new(DRAG_HOVERED_EVENT)
71            .filter(|| {
72                let id = WIDGET.id();
73                move |args| args.is_drag_enter_enabled(id)
74            })
75            .build::<PRE>(child, handler)
76    }
77    /// Dragging cursor exited the widget area and the widget is enabled.
78    #[property(EVENT)]
79    pub fn on_drag_leave<on_pre_drag_leave>(child: impl IntoUiNode, handler: Handler<DragHoveredArgs>) -> UiNode {
80        const PRE: bool;
81        EventNodeBuilder::new(DRAG_HOVERED_EVENT)
82            .filter(|| {
83                let id = WIDGET.id();
84                move |args| args.is_drag_leave_enabled(id)
85            })
86            .build::<PRE>(child, handler)
87    }
88
89    /// Dragging cursor dropped data in the widget area and the widget is enabled.
90    #[property(EVENT)]
91    pub fn on_drop<on_pre_drop>(child: impl IntoUiNode, handler: Handler<DropArgs>) -> UiNode {
92        const PRE: bool;
93        EventNodeBuilder::new(DROP_EVENT)
94            .filter(|| {
95                let id = WIDGET.id();
96                move |args| args.target.contains_enabled(id)
97            })
98            .build::<PRE>(child, handler)
99    }
100}
101
102/// If the dragging cursor is over the widget or a descendant and the widget is enabled.
103///
104/// The value is always `false` when the widget is not [`ENABLED`], use [`is_drag_hovered_disabled`] to implement *disabled hovered* visuals.
105///
106/// [`ENABLED`]: Interactivity::ENABLED
107/// [`is_drag_hovered_disabled`]: fn@is_drag_hovered_disabled
108#[property(EVENT)]
109pub fn is_drag_hovered(child: impl IntoUiNode, state: impl IntoVar<bool>) -> UiNode {
110    bind_state_init(child, state, |s| {
111        let id = WIDGET.id();
112        DRAG_HOVERED_EVENT.var_bind(s, move |args| {
113            if args.is_drag_enter_enabled(id) {
114                Some(true)
115            } else if args.is_drag_leave_enabled(id) {
116                Some(false)
117            } else {
118                None
119            }
120        })
121    })
122}
123
124/// If the dragging cursor is over the widget or a descendant and the widget is disabled.
125#[property(EVENT)]
126pub fn is_drag_hovered_disabled(child: impl IntoUiNode, state: impl IntoVar<bool>) -> UiNode {
127    bind_state_init(child, state, |s| {
128        let id = WIDGET.id();
129        DRAG_HOVERED_EVENT.var_bind(s, move |args| {
130            if args.is_drag_enter_disabled(id) {
131                Some(true)
132            } else if args.is_drag_leave_disabled(id) {
133                Some(false)
134            } else {
135                None
136            }
137        })
138    })
139}
140
141/// If the draggable widget is dragging.
142#[property(EVENT)]
143pub fn is_dragging(child: impl IntoUiNode, state: impl IntoVar<bool>) -> UiNode {
144    bind_state_init(child, state, |s| {
145        let id = WIDGET.id();
146        let handle = DRAG_START_EVENT.var_bind(s, move |args| if args.target.contains(id) { Some(true) } else { None });
147        DRAG_END_EVENT.var_bind(s, move |args| {
148            let _hold = &handle;
149            if args.target.contains(id) { Some(false) } else { None }
150        })
151    })
152}