1use crate::{
2 AnonymousElementKind, AnyElement, AppContext, BorrowWindow, Bounds, DispatchPhase, Element,
3 ElementId, ElementKind, IntoAnyElement, LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
4 Pixels, ScrollWheelEvent, SharedString, Style, StyleRefinement, ViewContext,
5};
6use collections::HashMap;
7use parking_lot::Mutex;
8use refineable::Refineable;
9use smallvec::SmallVec;
10use std::sync::Arc;
11
12#[derive(Default)]
13pub struct DivState {
14 active_state: Arc<Mutex<ActiveState>>,
15}
16
17#[derive(Copy, Clone, Default, Eq, PartialEq)]
18struct ActiveState {
19 group: bool,
20 element: bool,
21}
22
23impl ActiveState {
24 pub fn is_none(&self) -> bool {
25 !self.group && !self.element
26 }
27}
28
29#[derive(Default)]
30struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
31
32pub fn group_bounds(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
33 cx.default_global::<GroupBounds>()
34 .0
35 .get(name)
36 .and_then(|bounds_stack| bounds_stack.last().cloned())
37}
38
39pub struct Div<V: 'static + Send + Sync, K: ElementKind = AnonymousElementKind> {
40 kind: K,
41 children: SmallVec<[AnyElement<V>; 2]>,
42 group: Option<SharedString>,
43 base_style: StyleRefinement,
44 hover_style: StyleRefinement,
45 group_hover: Option<GroupStyle>,
46 active_style: StyleRefinement,
47 group_active: Option<GroupStyle>,
48 listeners: MouseEventListeners<V>,
49}
50
51struct GroupStyle {
52 group: SharedString,
53 style: StyleRefinement,
54}
55
56impl<V, K> Div<V, K>
57where
58 V: 'static + Send + Sync,
59 K: ElementKind,
60{
61 fn with_element_id<R>(
62 &mut self,
63 cx: &mut ViewContext<V>,
64 f: impl FnOnce(&mut Self, &mut ViewContext<V>) -> R,
65 ) -> R {
66 if let Some(id) = self.id() {
67 cx.with_element_id(id, |cx| f(self, cx))
68 } else {
69 f(self, cx)
70 }
71 }
72
73 fn compute_style(
74 &self,
75 bounds: Bounds<Pixels>,
76 group_bounds: Option<Bounds<Pixels>>,
77 active_state: ActiveState,
78 cx: &mut ViewContext<V>,
79 ) -> Style {
80 let mut computed_style = Style::default();
81 computed_style.refine(&self.base_style);
82
83 let mouse_position = cx.mouse_position();
84 if let Some(group_bounds) = group_bounds {
85 if group_bounds.contains_point(mouse_position) {
86 if let Some(GroupStyle { style, .. }) = self.group_hover.as_ref() {
87 computed_style.refine(style);
88 }
89 }
90 }
91 if bounds.contains_point(mouse_position) {
92 computed_style.refine(&self.hover_style);
93 }
94
95 if active_state.group {
96 if let Some(GroupStyle { style, .. }) = self.group_active.as_ref() {
97 computed_style.refine(style);
98 }
99 }
100
101 if active_state.element {
102 computed_style.refine(&self.active_style);
103 }
104
105 computed_style
106 }
107
108 fn paint_hover_listeners(
109 &self,
110 bounds: Bounds<Pixels>,
111 group_bounds: Option<Bounds<Pixels>>,
112 cx: &mut ViewContext<V>,
113 ) {
114 if let Some(group_bounds) = group_bounds {
115 paint_hover_listener(group_bounds, cx);
116 }
117
118 if self.hover_style.is_some() {
119 paint_hover_listener(bounds, cx);
120 }
121 }
122
123 fn paint_active_listener(
124 &self,
125 bounds: Bounds<Pixels>,
126 group_bounds: Option<Bounds<Pixels>>,
127 active_state: Arc<Mutex<ActiveState>>,
128 cx: &mut ViewContext<V>,
129 ) {
130 if active_state.lock().is_none() {
131 cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| {
132 if phase == DispatchPhase::Bubble {
133 let group =
134 group_bounds.map_or(false, |bounds| bounds.contains_point(down.position));
135 let element = bounds.contains_point(down.position);
136 if group || element {
137 *active_state.lock() = ActiveState { group, element };
138 cx.notify();
139 }
140 }
141 });
142 } else {
143 cx.on_mouse_event(move |_, _: &MouseUpEvent, phase, cx| {
144 if phase == DispatchPhase::Capture {
145 *active_state.lock() = ActiveState::default();
146 cx.notify();
147 }
148 });
149 }
150
151 // for listener in self.listeners.mouse_down.iter().cloned() {
152 // cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
153 // listener(state, event, &bounds, phase, cx);
154 // })
155 // }
156
157 // for listener in self.listeners.mouse_up.iter().cloned() {
158 // cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
159 // listener(state, event, &bounds, phase, cx);
160 // })
161 // }
162
163 // for listener in self.listeners.mouse_move.iter().cloned() {
164 // cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
165 // listener(state, event, &bounds, phase, cx);
166 // })
167 // }
168
169 // for listener in self.listeners.scroll_wheel.iter().cloned() {
170 // cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
171 // listener(state, event, &bounds, phase, cx);
172 // })
173 // }
174 }
175}
176
177fn paint_hover_listener<V>(bounds: Bounds<Pixels>, cx: &mut ViewContext<V>)
178where
179 V: 'static + Send + Sync,
180{
181 let hovered = bounds.contains_point(cx.mouse_position());
182 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
183 if phase == DispatchPhase::Capture {
184 if bounds.contains_point(event.position) != hovered {
185 cx.notify();
186 }
187 }
188 });
189}
190
191impl<V, K> Element for Div<V, K>
192where
193 V: 'static + Send + Sync,
194 K: ElementKind,
195{
196 type ViewState = V;
197 type ElementState = DivState;
198
199 fn id(&self) -> Option<ElementId> {
200 self.kind.id()
201 }
202
203 fn layout(
204 &mut self,
205 view_state: &mut Self::ViewState,
206 element_state: Option<Self::ElementState>,
207 cx: &mut ViewContext<Self::ViewState>,
208 ) -> (LayoutId, Self::ElementState) {
209 self.with_element_id(cx, |this, cx| {
210 let layout_ids = this
211 .children
212 .iter_mut()
213 .map(|child| child.layout(view_state, cx))
214 .collect::<Vec<_>>();
215
216 let element_state = element_state.unwrap_or_default();
217 let style = this.compute_style(
218 Bounds::default(),
219 None,
220 *element_state.active_state.lock(),
221 cx,
222 );
223 let layout_id = cx.request_layout(&style, layout_ids);
224 (layout_id, element_state)
225 })
226 }
227
228 fn paint(
229 &mut self,
230 bounds: Bounds<Pixels>,
231 view_state: &mut Self::ViewState,
232 element_state: &mut Self::ElementState,
233 cx: &mut ViewContext<Self::ViewState>,
234 ) {
235 self.with_element_id(cx, |this, cx| {
236 if let Some(group) = this.group.clone() {
237 cx.default_global::<GroupBounds>()
238 .0
239 .entry(group)
240 .or_default()
241 .push(bounds);
242 }
243
244 let hover_group_bounds = this
245 .group_hover
246 .as_ref()
247 .and_then(|group_hover| group_bounds(&group_hover.group, cx));
248 let active_group_bounds = this
249 .group_active
250 .as_ref()
251 .and_then(|group_active| group_bounds(&group_active.group, cx));
252 let active_state = *element_state.active_state.lock();
253 let style = this.compute_style(bounds, hover_group_bounds, active_state, cx);
254 let z_index = style.z_index.unwrap_or(0);
255
256 // Paint background and event handlers.
257 cx.stack(z_index, |cx| {
258 cx.stack(0, |cx| {
259 style.paint(bounds, cx);
260 this.paint_hover_listeners(bounds, hover_group_bounds, cx);
261 this.paint_active_listener(
262 bounds,
263 active_group_bounds,
264 element_state.active_state.clone(),
265 cx,
266 );
267 });
268 });
269
270 style.apply_text_style(cx, |cx| {
271 style.apply_overflow(bounds, cx, |cx| {
272 cx.stack(z_index + 1, |cx| {
273 for child in &mut this.children {
274 child.paint(view_state, None, cx);
275 }
276 })
277 })
278 });
279
280 if let Some(group) = this.group.as_ref() {
281 cx.default_global::<GroupBounds>()
282 .0
283 .get_mut(group)
284 .unwrap()
285 .pop();
286 }
287 })
288 }
289}
290
291impl<V, K> IntoAnyElement<V> for Div<V, K>
292where
293 V: 'static + Send + Sync,
294 K: ElementKind,
295{
296 fn into_any(self) -> AnyElement<V> {
297 AnyElement::new(self)
298 }
299}
300
301pub struct MouseClickEvent {
302 pub down: MouseDownEvent,
303 pub up: MouseUpEvent,
304}
305
306type MouseDownHandler<V> = Arc<
307 dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
308 + Send
309 + Sync
310 + 'static,
311>;
312type MouseUpHandler<V> = Arc<
313 dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
314 + Send
315 + Sync
316 + 'static,
317>;
318type MouseClickHandler<V> = Arc<
319 dyn Fn(&mut V, &MouseClickEvent, &Bounds<Pixels>, &mut ViewContext<V>) + Send + Sync + 'static,
320>;
321
322type MouseMoveHandler<V> = Arc<
323 dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
324 + Send
325 + Sync
326 + 'static,
327>;
328type ScrollWheelHandler<V> = Arc<
329 dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
330 + Send
331 + Sync
332 + 'static,
333>;
334
335pub struct MouseEventListeners<V: 'static> {
336 mouse_down: SmallVec<[MouseDownHandler<V>; 2]>,
337 mouse_up: SmallVec<[MouseUpHandler<V>; 2]>,
338 mouse_click: SmallVec<[MouseClickHandler<V>; 2]>,
339 mouse_move: SmallVec<[MouseMoveHandler<V>; 2]>,
340 scroll_wheel: SmallVec<[ScrollWheelHandler<V>; 2]>,
341}
342
343impl<V> Default for MouseEventListeners<V> {
344 fn default() -> Self {
345 Self {
346 mouse_down: SmallVec::new(),
347 mouse_up: SmallVec::new(),
348 mouse_click: SmallVec::new(),
349 mouse_move: SmallVec::new(),
350 scroll_wheel: SmallVec::new(),
351 }
352 }
353}