nested.rs

  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}