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