sequoia_elements/
draggable.rs1use std::rc::Rc;
2
3use sequoia_core::{Edges, Length};
4use sequoia_input::{CursorIcon, Event, EventPhase, PointerButton, PointerEvent};
5use sequoia_layout::LayoutStyle;
6use sequoia_style::VisualState;
7use sequoia_view::{AnyView, DragEnd, DragOptions, DragPayload, EventCtx, View};
8
9pub struct Draggable<V, T> {
10 child: AnyView,
11 payload: T,
12 threshold: f32,
13 button: PointerButton,
14 preview: Option<AnyView>,
15 on_start: Option<Rc<dyn Fn()>>,
16 on_end: Option<Rc<dyn Fn(DragEnd)>>,
17 style: LayoutStyle,
18 _view: std::marker::PhantomData<V>,
19}
20
21impl<V: View + 'static, T: Clone + 'static> Draggable<V, T> {
22 pub fn new(child: V, payload: T) -> Self {
23 Self {
24 child: AnyView::new(child),
25 payload,
26 threshold: sequoia_view::DRAG_THRESHOLD,
27 button: PointerButton::Left,
28 preview: None,
29 on_start: None,
30 on_end: None,
31 style: LayoutStyle::default(),
32 _view: std::marker::PhantomData,
33 }
34 }
35
36 pub fn threshold(mut self, px: f32) -> Self {
37 self.threshold = px.max(0.0);
38 self
39 }
40
41 pub fn button(mut self, button: PointerButton) -> Self {
42 self.button = button;
43 self
44 }
45
46 pub fn preview<W: View + 'static>(mut self, preview: W) -> Self {
47 self.preview = Some(AnyView::new(preview));
48 self
49 }
50
51 pub fn on_drag_start(mut self, f: impl Fn() + 'static) -> Self {
52 self.on_start = Some(Rc::new(f));
53 self
54 }
55
56 pub fn on_drag_end(mut self, f: impl Fn(DragEnd) + 'static) -> Self {
57 self.on_end = Some(Rc::new(f));
58 self
59 }
60
61 pub fn width(mut self, width: impl Into<Length>) -> Self {
62 self.style = self.style.width(width.into());
63 self
64 }
65
66 pub fn height(mut self, height: impl Into<Length>) -> Self {
67 self.style = self.style.height(height.into());
68 self
69 }
70
71 pub fn min_width(mut self, v: f32) -> Self {
72 self.style = self.style.min_width(v);
73 self
74 }
75
76 pub fn max_width(mut self, v: f32) -> Self {
77 self.style = self.style.max_width(v);
78 self
79 }
80
81 pub fn min_height(mut self, v: f32) -> Self {
82 self.style = self.style.min_height(v);
83 self
84 }
85
86 pub fn max_height(mut self, v: f32) -> Self {
87 self.style = self.style.max_height(v);
88 self
89 }
90
91 pub fn flex(mut self, weight: f32) -> Self {
92 self.style = self.style.flex(weight);
93 self
94 }
95
96 pub fn expand(self) -> Self {
97 self.flex(1.0)
98 }
99
100 pub fn fill_width(mut self) -> Self {
101 self.style = self.style.width(Length::Fill(1.0));
102 self
103 }
104
105 pub fn fill_height(mut self) -> Self {
106 self.style = self.style.height(Length::Fill(1.0));
107 self
108 }
109
110 pub fn fill_size(self) -> Self {
111 self.fill_width().fill_height()
112 }
113
114 pub fn margin(mut self, margin: impl Into<Edges>) -> Self {
115 self.style = self.style.margin(margin);
116 self
117 }
118
119 pub fn margin_xy(mut self, x: f32, y: f32) -> Self {
120 self.style = self.style.margin_xy(x, y);
121 self
122 }
123
124 pub fn margin_x(mut self, x: f32) -> Self {
125 self.style = self.style.margin_x(x);
126 self
127 }
128
129 pub fn margin_y(mut self, y: f32) -> Self {
130 self.style = self.style.margin_y(y);
131 self
132 }
133
134 pub fn margin_left(mut self, v: f32) -> Self {
135 self.style = self.style.margin_left(v);
136 self
137 }
138
139 pub fn margin_right(mut self, v: f32) -> Self {
140 self.style = self.style.margin_right(v);
141 self
142 }
143
144 pub fn margin_top(mut self, v: f32) -> Self {
145 self.style = self.style.margin_top(v);
146 self
147 }
148
149 pub fn margin_bottom(mut self, v: f32) -> Self {
150 self.style = self.style.margin_bottom(v);
151 self
152 }
153
154 pub fn drag_payload(&self) -> DragPayload {
155 DragPayload::new(self.payload.clone())
156 }
157}
158
159impl<V: View + 'static, T: Clone + 'static> View for Draggable<V, T> {
160 fn body(&self) -> impl View + 'static {
161 sequoia_view::Unit
162 }
163
164 fn children_override(&self) -> Option<Vec<AnyView>> {
165 Some(vec![self.child.clone()])
166 }
167
168 fn layout_style(&self) -> LayoutStyle {
169 self.style
170 }
171
172 fn has_visual_state(&self) -> bool {
173 true
174 }
175
176 fn captures_pointer(&self) -> bool {
177 true
178 }
179
180 fn cursor_icon(&self, state: VisualState) -> CursorIcon {
181 if state.pressed {
182 CursorIcon::Grabbing
183 } else {
184 CursorIcon::Grab
185 }
186 }
187
188 fn on_event(&self, event: &Event, ctx: &mut EventCtx) {
189 if ctx.phase == EventPhase::Bubble {
190 return;
191 }
192 match event {
193 Event::Pointer(PointerEvent::Down { button, pos, .. })
194 if *button == self.button && ctx.rect.contains(*pos) =>
195 {
196 let mut opts = DragOptions::new().threshold(self.threshold);
197 if let Some(pv) = self.preview.clone() {
198 opts = opts.preview(pv);
199 }
200 ctx.begin_drag(self.payload.clone(), opts);
201 }
202 _ => {}
203 }
204 }
205
206 fn on_drag_start(&self, _ctx: &mut EventCtx) {
207 if let Some(f) = &self.on_start {
208 f();
209 }
210 }
211
212 fn on_drag_end(&self, end: DragEnd, _ctx: &mut EventCtx) {
213 if let Some(f) = &self.on_end {
214 f(end);
215 }
216 }
217}