1use crate::{
2 group_bounds, AnyElement, BorrowWindow, DispatchPhase, Element, ElementId, IdentifiedElement,
3 IntoAnyElement, MouseDownEvent, MouseMoveEvent, MouseUpEvent, SharedString, Style,
4 StyleCascade, StyleRefinement, ViewContext,
5};
6use parking_lot::Mutex;
7use refineable::{CascadeSlot, Refineable};
8use smallvec::SmallVec;
9use std::sync::{
10 atomic::{AtomicBool, Ordering::SeqCst},
11 Arc,
12};
13
14trait LayoutNode<V: 'static + Send + Sync, K: ElementKind> {
15 fn state(&mut self) -> &mut LayoutNodeElement<V, K>;
16
17 fn child(mut self, child: impl IntoAnyElement<V>) -> Self
18 where
19 Self: Sized,
20 {
21 self.state().children.push(child.into_any());
22 self
23 }
24
25 fn children<C, E>(mut self, children: C) -> Self
26 where
27 C: IntoIterator<Item = E>,
28 E: IntoAnyElement<V>,
29 Self: Sized,
30 {
31 for child in children {
32 self.state().children.push(child.into_any());
33 }
34 self
35 }
36}
37
38pub trait ElementKind: 'static + Send + Sync {
39 fn id(&self) -> Option<ElementId>;
40}
41
42pub struct Identified(ElementId);
43pub struct Anonymous;
44
45impl ElementKind for Identified {
46 fn id(&self) -> Option<ElementId> {
47 Some(self.0.clone())
48 }
49}
50
51impl ElementKind for Anonymous {
52 fn id(&self) -> Option<ElementId> {
53 None
54 }
55}
56
57struct LayoutNodeElement<V: 'static + Send + Sync, K: ElementKind> {
58 style_cascade: StyleCascade,
59 computed_style: Option<Style>,
60 children: SmallVec<[AnyElement<V>; 2]>,
61 kind: K,
62}
63
64impl<V: 'static + Send + Sync> LayoutNodeElement<V, Anonymous> {
65 pub fn identify(self, id: impl Into<ElementId>) -> LayoutNodeElement<V, Identified> {
66 LayoutNodeElement {
67 style_cascade: self.style_cascade,
68 computed_style: self.computed_style,
69 children: self.children,
70 kind: Identified(id.into()),
71 }
72 }
73}
74
75impl<V: 'static + Send + Sync, E: ElementKind> LayoutNodeElement<V, E> {
76 fn with_element_id<R>(
77 &mut self,
78 cx: &mut ViewContext<V>,
79 f: impl FnOnce(&mut Self, &mut ViewContext<V>) -> R,
80 ) -> R {
81 if let Some(id) = self.id() {
82 cx.with_element_id(id, |cx| f(self, cx))
83 } else {
84 f(self, cx)
85 }
86 }
87}
88
89impl<V: 'static + Send + Sync, K: ElementKind> Styled for LayoutNodeElement<V, K> {
90 fn style_cascade(&mut self) -> &mut StyleCascade {
91 &mut self.style_cascade
92 }
93
94 fn computed_style(&mut self) -> &Style {
95 self.computed_style
96 .get_or_insert_with(|| Style::default().refined(self.style_cascade.merged()))
97 }
98}
99
100impl<V: 'static + Send + Sync> IdentifiedElement for LayoutNodeElement<V, Identified> {
101 fn element_id(&self) -> ElementId {
102 self.kind.0.clone()
103 }
104}
105
106impl<V, K> IntoAnyElement<V> for LayoutNodeElement<V, K>
107where
108 V: 'static + Send + Sync,
109 K: ElementKind,
110{
111 fn into_any(self) -> AnyElement<V> {
112 AnyElement::new(self)
113 }
114}
115
116impl<V: 'static + Send + Sync, K: ElementKind> Element for LayoutNodeElement<V, K> {
117 type ViewState = V;
118 type ElementState = ();
119
120 fn id(&self) -> Option<ElementId> {
121 self.kind.id()
122 }
123
124 fn layout(
125 &mut self,
126 state: &mut Self::ViewState,
127 _: Option<Self::ElementState>,
128 cx: &mut crate::ViewContext<Self::ViewState>,
129 ) -> (crate::LayoutId, Self::ElementState) {
130 self.with_element_id(cx, |this, cx| {
131 let layout_ids = this
132 .children
133 .iter_mut()
134 .map(|child| child.layout(state, cx))
135 .collect::<Vec<_>>();
136
137 let style = this.computed_style();
138 let layout_id = cx.request_layout(style, layout_ids);
139 (layout_id, ())
140 })
141 }
142
143 fn paint(
144 &mut self,
145 _: crate::Bounds<crate::Pixels>,
146 state: &mut Self::ViewState,
147 _: &mut Self::ElementState,
148 cx: &mut crate::ViewContext<Self::ViewState>,
149 ) {
150 self.with_element_id(cx, |this, cx| {
151 for child in &mut this.children {
152 child.paint(state, None, cx);
153 }
154 })
155 }
156}
157
158pub trait Styled {
159 fn style_cascade(&mut self) -> &mut StyleCascade;
160 fn computed_style(&mut self) -> &Style;
161}
162
163pub trait Hoverable {
164 fn hover_style(&mut self) -> &mut StyleRefinement;
165
166 fn hover(mut self, f: impl FnOnce(&mut StyleRefinement) -> &mut StyleRefinement) -> Self
167 where
168 Self: Sized,
169 {
170 f(self.hover_style());
171 self
172 }
173}
174
175struct HoverableElement<E> {
176 hover_style: StyleRefinement,
177 group: Option<SharedString>,
178 cascade_slot: CascadeSlot,
179 hovered: Arc<AtomicBool>,
180 child: E,
181}
182
183impl<E: Styled + Element> HoverableElement<E> {
184 pub fn replace_child<E2: Element<ViewState = E::ViewState>>(
185 self,
186 replace: impl FnOnce(E) -> E2,
187 ) -> HoverableElement<E2> {
188 HoverableElement {
189 hover_style: self.hover_style,
190 group: self.group,
191 cascade_slot: self.cascade_slot,
192 hovered: self.hovered,
193 child: replace(self.child),
194 }
195 }
196
197 fn hover_style(&mut self) -> &mut StyleRefinement {
198 &mut self.hover_style
199 }
200}
201
202impl<E> IntoAnyElement<E::ViewState> for HoverableElement<E>
203where
204 E: Styled + Element,
205{
206 fn into_any(self) -> AnyElement<E::ViewState> {
207 AnyElement::new(self)
208 }
209}
210
211impl<E> Element for HoverableElement<E>
212where
213 E: Styled + Element,
214{
215 type ViewState = E::ViewState;
216 type ElementState = E::ElementState;
217
218 fn id(&self) -> Option<ElementId> {
219 self.child.id()
220 }
221
222 fn layout(
223 &mut self,
224 state: &mut Self::ViewState,
225 element_state: Option<Self::ElementState>,
226 cx: &mut crate::ViewContext<Self::ViewState>,
227 ) -> (crate::LayoutId, Self::ElementState) {
228 self.child.layout(state, element_state, cx)
229 }
230
231 fn paint(
232 &mut self,
233 bounds: crate::Bounds<crate::Pixels>,
234 state: &mut Self::ViewState,
235 element_state: &mut Self::ElementState,
236 cx: &mut crate::ViewContext<Self::ViewState>,
237 ) {
238 let target_bounds = self
239 .group
240 .as_ref()
241 .and_then(|group| group_bounds(group, cx))
242 .unwrap_or(bounds);
243
244 let hovered = target_bounds.contains_point(cx.mouse_position());
245
246 let slot = self.cascade_slot;
247 let style = hovered.then_some(self.hover_style.clone());
248 self.child.style_cascade().set(slot, style);
249 self.hovered.store(hovered, SeqCst);
250
251 let hovered = self.hovered.clone();
252 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
253 if phase == DispatchPhase::Capture {
254 if target_bounds.contains_point(event.position) != hovered.load(SeqCst) {
255 cx.notify();
256 }
257 }
258 });
259
260 self.child.paint(bounds, state, element_state, cx);
261 }
262}
263
264impl<E: Styled + Element> Styled for HoverableElement<E> {
265 fn style_cascade(&mut self) -> &mut StyleCascade {
266 self.child.style_cascade()
267 }
268
269 fn computed_style(&mut self) -> &Style {
270 self.child.computed_style()
271 }
272}
273
274impl<E: Styled + IdentifiedElement> IdentifiedElement for HoverableElement<E> {}
275
276pub trait Clickable: Element + Sized {
277 fn active_style(&mut self) -> &mut StyleRefinement;
278 fn listeners(&mut self) -> &mut ClickListeners<Self::ViewState>;
279
280 fn on_click(
281 &mut self,
282 f: impl Fn(&mut Self::ViewState, &MouseClickEvent, &mut ViewContext<Self::ViewState>)
283 + 'static
284 + Send
285 + Sync,
286 ) where
287 Self: Sized,
288 {
289 self.listeners().push(Arc::new(f));
290 }
291
292 fn active(mut self, f: impl FnOnce(&mut StyleRefinement) -> &mut StyleRefinement) -> Self
293 where
294 Self: Sized,
295 {
296 f(self.active_style());
297 self
298 }
299}
300
301type ClickListeners<V> =
302 SmallVec<[Arc<dyn Fn(&mut V, &MouseClickEvent, &mut ViewContext<V>) + Send + Sync>; 1]>;
303
304pub struct ClickableElementState<E: 'static + Send + Sync> {
305 mouse_down: Arc<Mutex<Option<MouseDownEvent>>>,
306 child_state: E,
307}
308
309pub struct MouseClickEvent {
310 pub down: MouseDownEvent,
311 pub up: MouseUpEvent,
312}
313
314pub struct ClickableElement<E: Element> {
315 child: E,
316 listeners: ClickListeners<E::ViewState>,
317 active_style: StyleRefinement,
318 cascade_slot: CascadeSlot,
319}
320
321impl<E: Element> ClickableElement<E> {
322 pub fn replace_child<E2: Element<ViewState = E::ViewState>>(
323 self,
324 replace: impl FnOnce(E) -> E2,
325 ) -> ClickableElement<E2> {
326 ClickableElement {
327 child: replace(self.child),
328 listeners: self.listeners,
329 active_style: self.active_style,
330 cascade_slot: self.cascade_slot,
331 }
332 }
333}
334
335impl<E> IntoAnyElement<E::ViewState> for ClickableElement<E>
336where
337 E: Styled + Element,
338{
339 fn into_any(self) -> AnyElement<E::ViewState> {
340 AnyElement::new(self)
341 }
342}
343
344impl<E> Element for ClickableElement<E>
345where
346 E: Styled + Element,
347{
348 type ViewState = E::ViewState;
349 type ElementState = ClickableElementState<E::ElementState>;
350
351 fn id(&self) -> Option<ElementId> {
352 self.child.id()
353 }
354
355 fn layout(
356 &mut self,
357 state: &mut Self::ViewState,
358 element_state: Option<Self::ElementState>,
359 cx: &mut crate::ViewContext<Self::ViewState>,
360 ) -> (crate::LayoutId, Self::ElementState) {
361 if let Some(element_state) = element_state {
362 if element_state.mouse_down.lock().is_some() {
363 self.child
364 .style_cascade()
365 .set(self.cascade_slot, Some(self.active_style.clone()));
366 }
367
368 let (layout_id, child_state) =
369 self.child
370 .layout(state, Some(element_state.child_state), cx);
371 (
372 layout_id,
373 ClickableElementState {
374 mouse_down: element_state.mouse_down,
375 child_state,
376 },
377 )
378 } else {
379 let (layout_id, child_state) = self.child.layout(state, None, cx);
380 (
381 layout_id,
382 ClickableElementState {
383 mouse_down: Default::default(),
384 child_state,
385 },
386 )
387 }
388 }
389
390 fn paint(
391 &mut self,
392 bounds: crate::Bounds<crate::Pixels>,
393 state: &mut Self::ViewState,
394 element_state: &mut Self::ElementState,
395 cx: &mut crate::ViewContext<Self::ViewState>,
396 ) {
397 if !self.listeners.is_empty() || self.active_style.is_some() {
398 if let Some(mouse_down) = element_state.mouse_down.lock().clone() {
399 self.child
400 .style_cascade()
401 .set(self.cascade_slot, Some(self.active_style.clone()));
402 let listeners = self.listeners.clone();
403 let mouse_down_mutex = element_state.mouse_down.clone();
404 cx.on_mouse_event(move |view, up: &MouseUpEvent, phase, cx| {
405 if phase == DispatchPhase::Bubble && bounds.contains_point(up.position) {
406 for listener in &*listeners {
407 listener(
408 view,
409 &MouseClickEvent {
410 down: mouse_down.clone(),
411 up: up.clone(),
412 },
413 cx,
414 );
415 }
416 }
417
418 mouse_down_mutex.lock().take();
419 cx.notify();
420 });
421 } else {
422 let mouse_down_mutex = element_state.mouse_down.clone();
423 cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| {
424 if phase == DispatchPhase::Bubble && bounds.contains_point(down.position) {
425 *mouse_down_mutex.lock() = Some(down.clone());
426 cx.notify();
427 }
428 });
429 }
430 }
431
432 self.child
433 .paint(bounds, state, &mut element_state.child_state, cx);
434 }
435}
436
437impl<E: Styled + IdentifiedElement> IdentifiedElement for ClickableElement<E> {}
438
439impl<E> Clickable for ClickableElement<E>
440where
441 E: Styled + IdentifiedElement,
442{
443 fn active_style(&mut self) -> &mut StyleRefinement {
444 &mut self.active_style
445 }
446
447 fn listeners(&mut self) -> &mut ClickListeners<Self::ViewState> {
448 &mut self.listeners
449 }
450}
451
452pub struct Div<V: 'static + Send + Sync, K: ElementKind>(
453 ClickableElement<HoverableElement<LayoutNodeElement<V, K>>>,
454);
455
456impl<V: 'static + Send + Sync> Div<V, Anonymous> {
457 pub fn id(self, id: impl Into<ElementId>) -> Div<V, Identified> {
458 Div(self.0.replace_child(|hoverable| {
459 hoverable.replace_child(|layout_node| layout_node.identify(id))
460 }))
461 }
462}
463
464impl<V: 'static + Send + Sync, K: ElementKind> LayoutNode<V, K> for Div<V, K> {
465 fn state(&mut self) -> &mut LayoutNodeElement<V, K> {
466 &mut self.0.child.child
467 }
468}
469
470impl<V: 'static + Send + Sync, K: ElementKind> Styled for Div<V, K> {
471 fn style_cascade(&mut self) -> &mut StyleCascade {
472 self.0.child.child.style_cascade()
473 }
474
475 fn computed_style(&mut self) -> &Style {
476 self.0.child.child.computed_style()
477 }
478}
479
480impl<V: 'static + Send + Sync, K: ElementKind> Hoverable for Div<V, K> {
481 fn hover_style(&mut self) -> &mut StyleRefinement {
482 self.0.child.hover_style()
483 }
484}
485
486impl<V: 'static + Send + Sync> Clickable for Div<V, Identified> {
487 fn active_style(&mut self) -> &mut StyleRefinement {
488 self.0.active_style()
489 }
490
491 fn listeners(&mut self) -> &mut ClickListeners<V> {
492 self.0.listeners()
493 }
494}
495
496impl<V, K> IntoAnyElement<V> for Div<V, K>
497where
498 V: 'static + Send + Sync,
499 K: ElementKind,
500{
501 fn into_any(self) -> AnyElement<V> {
502 AnyElement::new(self)
503 }
504}
505
506impl<V, K> Element for Div<V, K>
507where
508 V: 'static + Send + Sync,
509 K: ElementKind,
510{
511 type ViewState = V;
512 type ElementState = ClickableElementState<()>;
513
514 fn id(&self) -> Option<ElementId> {
515 self.0.id()
516 }
517
518 fn layout(
519 &mut self,
520 state: &mut Self::ViewState,
521 element_state: Option<Self::ElementState>,
522 cx: &mut crate::ViewContext<Self::ViewState>,
523 ) -> (crate::LayoutId, Self::ElementState) {
524 self.0.layout(state, element_state, cx)
525 }
526
527 fn paint(
528 &mut self,
529 bounds: crate::Bounds<crate::Pixels>,
530 state: &mut Self::ViewState,
531 element_state: &mut Self::ElementState,
532 cx: &mut crate::ViewContext<Self::ViewState>,
533 ) {
534 self.0.paint(bounds, state, element_state, cx);
535 }
536}