Skip to main content

sequoia_elements/
droppable.rs

1use std::marker::PhantomData;
2use std::rc::Rc;
3
4use sequoia_core::{Edges, Length};
5use sequoia_layout::LayoutStyle;
6use sequoia_reactive::Signal;
7use sequoia_view::{AnyView, DragPayload, DropPosition, EventCtx, PaintCtx, View};
8
9pub struct Droppable<V, T> {
10    child: AnyView,
11    on_drop: Rc<dyn Fn(T, DropPosition)>,
12    on_over: Option<Rc<dyn Fn(DropPosition)>>,
13    highlight: Option<Signal<bool>>,
14    style: LayoutStyle,
15    _view: PhantomData<(V, T)>,
16}
17
18impl<V: View + 'static, T: Clone + 'static> Droppable<V, T> {
19    pub fn new(child: V, on_drop: impl Fn(T, DropPosition) + 'static) -> Self {
20        Self {
21            child: AnyView::new(child),
22            on_drop: Rc::new(on_drop),
23            on_over: None,
24            highlight: None,
25            style: LayoutStyle::default(),
26            _view: PhantomData,
27        }
28    }
29
30    pub fn on_over(mut self, f: impl Fn(DropPosition) + 'static) -> Self {
31        self.on_over = Some(Rc::new(f));
32        self
33    }
34
35    pub fn highlight(mut self, signal: Signal<bool>) -> Self {
36        self.highlight = Some(signal);
37        self
38    }
39
40    pub fn width(mut self, width: impl Into<Length>) -> Self {
41        self.style = self.style.width(width.into());
42        self
43    }
44
45    pub fn height(mut self, height: impl Into<Length>) -> Self {
46        self.style = self.style.height(height.into());
47        self
48    }
49
50    pub fn min_width(mut self, v: f32) -> Self {
51        self.style = self.style.min_width(v);
52        self
53    }
54
55    pub fn max_width(mut self, v: f32) -> Self {
56        self.style = self.style.max_width(v);
57        self
58    }
59
60    pub fn min_height(mut self, v: f32) -> Self {
61        self.style = self.style.min_height(v);
62        self
63    }
64
65    pub fn max_height(mut self, v: f32) -> Self {
66        self.style = self.style.max_height(v);
67        self
68    }
69
70    pub fn flex(mut self, weight: f32) -> Self {
71        self.style = self.style.flex(weight);
72        self
73    }
74
75    pub fn expand(self) -> Self {
76        self.flex(1.0)
77    }
78
79    pub fn fill_width(mut self) -> Self {
80        self.style = self.style.width(Length::Fill(1.0));
81        self
82    }
83
84    pub fn fill_height(mut self) -> Self {
85        self.style = self.style.height(Length::Fill(1.0));
86        self
87    }
88
89    pub fn fill_size(self) -> Self {
90        self.fill_width().fill_height()
91    }
92
93    pub fn margin(mut self, margin: impl Into<Edges>) -> Self {
94        self.style = self.style.margin(margin);
95        self
96    }
97
98    pub fn margin_xy(mut self, x: f32, y: f32) -> Self {
99        self.style = self.style.margin_xy(x, y);
100        self
101    }
102
103    pub fn margin_x(mut self, x: f32) -> Self {
104        self.style = self.style.margin_x(x);
105        self
106    }
107
108    pub fn margin_y(mut self, y: f32) -> Self {
109        self.style = self.style.margin_y(y);
110        self
111    }
112
113    pub fn margin_left(mut self, v: f32) -> Self {
114        self.style = self.style.margin_left(v);
115        self
116    }
117
118    pub fn margin_right(mut self, v: f32) -> Self {
119        self.style = self.style.margin_right(v);
120        self
121    }
122
123    pub fn margin_top(mut self, v: f32) -> Self {
124        self.style = self.style.margin_top(v);
125        self
126    }
127
128    pub fn margin_bottom(mut self, v: f32) -> Self {
129        self.style = self.style.margin_bottom(v);
130        self
131    }
132}
133
134impl<V: View + 'static, T: Clone + 'static> View for Droppable<V, T> {
135    fn body(&self) -> impl View + 'static {
136        sequoia_view::Unit
137    }
138
139    fn children_override(&self) -> Option<Vec<AnyView>> {
140        Some(vec![self.child.clone()])
141    }
142
143    fn layout_style(&self) -> LayoutStyle {
144        self.style
145    }
146
147    fn has_visual_state(&self) -> bool {
148        true
149    }
150
151    fn accepts_drop(&self, payload: &DragPayload) -> bool {
152        payload.is::<T>()
153    }
154
155    fn on_drag_over(&self, pos: DropPosition, _ctx: &mut EventCtx) {
156        if let Some(f) = &self.on_over {
157            f(pos);
158        }
159    }
160
161    fn on_drop(&self, pos: DropPosition, ctx: &mut EventCtx) {
162        let Some(value) = ctx
163            .drag_session()
164            .and_then(|s| s.payload.get::<T>().cloned())
165        else {
166            return;
167        };
168        (self.on_drop)(value, pos);
169        ctx.handled();
170    }
171
172    fn paint_self(&self, ctx: &mut PaintCtx) {
173        if let Some(sig) = &self.highlight {
174            let over = ctx.is_drag_target();
175            if sig.get_untracked() != over {
176                sig.set(over);
177            }
178        }
179    }
180}