nested.rs

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