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);
}
}
}
_ => {}
})
}