zng_wgt_input/
drag_drop.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//! Drag& drop properties, event properties.

use zng_ext_input::drag_drop::{
    DragEndArgs, DragHoveredArgs, DragStartArgs, DropArgs, WidgetInfoBuilderDragDropExt as _, DRAG_END_EVENT, DRAG_HOVERED_EVENT,
    DRAG_START_EVENT, DROP_EVENT,
};
use zng_wgt::prelude::*;

/// If this widget can be dragged in a drag&drop operation.
///
/// When this is `true` the widget can be dragged and dropped within the same app or it can handle [`on_drag_start`] and
/// use the [`DRAG_DROP.drag`] service to set a system wide drag data.
///
/// [`on_drag_start`]: fn@on_drag_start
/// [`DRAG_DROP.drag`]: zng_ext_input::drag_drop::DRAG_DROP::drag
#[property(CONTEXT, default(false))]
pub fn draggable(child: impl UiNode, input: impl IntoVar<bool>) -> impl UiNode {
    let input = input.into_var();
    match_node(child, move |_c, op| match op {
        UiNodeOp::Init => {
            WIDGET.sub_var_info(&input);
        }
        UiNodeOp::Info { info } => {
            if input.get() {
                info.draggable();
            }
        }
        _ => {}
    })
}

event_property! {
    /// Draggable widget started dragging.
    ///
    /// To receive this event in a widget set [`draggable`] to `true`.
    ///
    /// [`draggable`]: fn@draggable
    pub fn drag_start {
        event: DRAG_START_EVENT,
        args: DragStartArgs,
    }

    /// Draggable widget stopped dragging.
    ///
    /// This event is always paired with [`on_drag_start`] first.
    ///
    /// [`on_drag_start`]: fn@on_drag_start
    pub fn drag_end {
        event: DRAG_END_EVENT,
        args: DragEndArgs,
    }

    /// Dragging cursor entered or exited the widget area and the widget is enabled.
    pub fn drag_hovered {
        event: DRAG_HOVERED_EVENT,
        args: DragHoveredArgs,
        filter: |args| args.is_drag_enter_enabled()
    }
    /// Dragging cursor entered the widget area and the widget is enabled.
    pub fn drag_enter {
        event: DRAG_HOVERED_EVENT,
        args: DragHoveredArgs,
        filter: |args| args.is_drag_enter_enabled(),
    }
    /// Dragging cursor exited the widget area and the widget is enabled.
    pub fn drag_leave {
        event: DRAG_HOVERED_EVENT,
        args: DragHoveredArgs,
        filter: |args| args.is_drag_leave_enabled(),
    }

    /// Dragging cursor dropped data in the widget area and the widget is enabled.
    pub fn drop {
        event: DROP_EVENT,
        args: DropArgs,
        filter: |args| args.is_enabled(WIDGET.id()),
    }
}

/// If the dragging cursor is over the widget or a descendant and the widget is enabled.
///
/// The value is always `false` when the widget is not [`ENABLED`], use [`is_drag_hovered_disabled`] to implement *disabled hovered* visuals.
///
/// [`is_cap_hovered`]: fn@is_cap_hovered
/// [`ENABLED`]: Interactivity::ENABLED
/// [`is_drag_hovered_disabled`]: fn@is_drag_hovered_disabled
#[property(EVENT)]
pub fn is_drag_hovered(child: impl UiNode, state: impl IntoVar<bool>) -> impl UiNode {
    event_state(child, state, false, DRAG_HOVERED_EVENT, |args| {
        if args.is_drag_enter_enabled() {
            Some(true)
        } else if args.is_drag_leave_enabled() {
            Some(false)
        } else {
            None
        }
    })
}

/// If the dragging cursor is over the widget or a descendant and the widget is disabled.
#[property(EVENT)]
pub fn is_drag_hovered_disabled(child: impl UiNode, state: impl IntoVar<bool>) -> impl UiNode {
    event_state(child, state, false, DRAG_HOVERED_EVENT, |args| {
        if args.is_drag_enter_disabled() {
            Some(true)
        } else if args.is_drag_leave_disabled() {
            Some(false)
        } else {
            None
        }
    })
}

/// If the draggable widget is dragging.
#[property(EVENT)]
pub fn is_dragging(child: impl UiNode, state: impl IntoVar<bool>) -> impl UiNode {
    let state = state.into_var();
    match_node(child, move |_, op| match op {
        UiNodeOp::Init => {
            WIDGET.sub_event(&DRAG_START_EVENT).sub_event(&DRAG_END_EVENT);
        }
        UiNodeOp::Deinit => {
            let _ = state.set(false);
        }
        UiNodeOp::Event { update } => {
            if let Some(args) = DRAG_START_EVENT.on(update) {
                if args.target.contains(WIDGET.id()) {
                    let _ = state.set(true);
                }
            } else if let Some(args) = DRAG_END_EVENT.on(update) {
                if args.target.contains(WIDGET.id()) {
                    let _ = state.set(false);
                }
            }
        }
        _ => {}
    })
}