sequoia_elements/
droppable.rs1use 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}