Skip to main content

sequoia_elements/
draggable.rs

1use 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}