element.rs

  1use crate::{
  2    AppContext, BorrowWindow, Bounds, DispatchPhase, ElementId, FocusHandle, FocusListeners,
  3    KeyDownEvent, KeyListener, KeyMatch, LayoutId, MouseClickEvent, MouseClickListener,
  4    MouseDownEvent, MouseDownListener, MouseMoveEvent, MouseMoveListener, MouseUpEvent,
  5    MouseUpListener, Pixels, Point, ScrollWheelEvent, ScrollWheelListener, SharedString, Style,
  6    StyleRefinement, ViewContext, WindowContext,
  7};
  8use collections::HashMap;
  9use derive_more::{Deref, DerefMut};
 10use parking_lot::Mutex;
 11use refineable::Refineable;
 12pub(crate) use smallvec::SmallVec;
 13use std::{any::TypeId, mem, sync::Arc};
 14
 15pub trait Element: 'static + Send + Sync + IntoAnyElement<Self::ViewState> {
 16    type ViewState: 'static + Send + Sync;
 17    type ElementState: 'static + Send + Sync;
 18
 19    fn id(&self) -> Option<ElementId>;
 20
 21    fn initialize(
 22        &mut self,
 23        view_state: &mut Self::ViewState,
 24        element_state: Option<Self::ElementState>,
 25        cx: &mut ViewContext<Self::ViewState>,
 26    ) -> Self::ElementState;
 27
 28    fn layout(
 29        &mut self,
 30        view_state: &mut Self::ViewState,
 31        element_state: &mut Self::ElementState,
 32        cx: &mut ViewContext<Self::ViewState>,
 33    ) -> LayoutId;
 34
 35    fn paint(
 36        &mut self,
 37        bounds: Bounds<Pixels>,
 38        view_state: &mut Self::ViewState,
 39        element_state: &mut Self::ElementState,
 40        cx: &mut ViewContext<Self::ViewState>,
 41    );
 42}
 43
 44#[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
 45pub struct GlobalElementId(SmallVec<[ElementId; 32]>);
 46
 47pub trait ElementInteractivity<V: 'static + Send + Sync>: 'static + Send + Sync {
 48    fn as_stateless(&self) -> &StatelessInteractivity<V>;
 49    fn as_stateless_mut(&mut self) -> &mut StatelessInteractivity<V>;
 50    fn as_stateful(&self) -> Option<&StatefulInteractivity<V>>;
 51    fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity<V>>;
 52
 53    fn initialize<R>(
 54        &mut self,
 55        cx: &mut ViewContext<V>,
 56        f: impl FnOnce(&mut ViewContext<V>) -> R,
 57    ) -> R {
 58        if let Some(stateful) = self.as_stateful_mut() {
 59            cx.with_element_id(stateful.id.clone(), |global_id, cx| {
 60                stateful.key_listeners.push((
 61                    TypeId::of::<KeyDownEvent>(),
 62                    Arc::new(move |_, key_down, context, phase, cx| {
 63                        if phase == DispatchPhase::Bubble {
 64                            let key_down = key_down.downcast_ref::<KeyDownEvent>().unwrap();
 65                            if let KeyMatch::Some(action) =
 66                                cx.match_keystroke(&global_id, &key_down.keystroke, context)
 67                            {
 68                                return Some(action);
 69                            }
 70                        }
 71
 72                        None
 73                    }),
 74                ));
 75                let result = stateful.stateless.initialize(cx, f);
 76                stateful.key_listeners.pop();
 77                result
 78            })
 79        } else {
 80            cx.with_key_listeners(&self.as_stateless().key_listeners, f)
 81        }
 82    }
 83
 84    fn refine_style(&self, style: &mut Style, bounds: Bounds<Pixels>, cx: &mut ViewContext<V>) {
 85        let mouse_position = cx.mouse_position();
 86        let stateless = self.as_stateless();
 87        if let Some(group_hover) = stateless.group_hover.as_ref() {
 88            if let Some(group_bounds) = group_bounds(&group_hover.group, cx) {
 89                if group_bounds.contains_point(&mouse_position) {
 90                    style.refine(&group_hover.style);
 91                }
 92            }
 93        }
 94        if bounds.contains_point(&mouse_position) {
 95            style.refine(&stateless.hover_style);
 96        }
 97    }
 98
 99    fn paint(
100        &mut self,
101        bounds: Bounds<Pixels>,
102        pending_click: Arc<Mutex<Option<MouseDownEvent>>>,
103        cx: &mut ViewContext<V>,
104    ) {
105        let stateless = self.as_stateless();
106        for listener in stateless.mouse_down_listeners.iter().cloned() {
107            cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
108                listener(state, event, &bounds, phase, cx);
109            })
110        }
111
112        for listener in stateless.mouse_up_listeners.iter().cloned() {
113            cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
114                listener(state, event, &bounds, phase, cx);
115            })
116        }
117
118        for listener in stateless.mouse_move_listeners.iter().cloned() {
119            cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
120                listener(state, event, &bounds, phase, cx);
121            })
122        }
123
124        for listener in stateless.scroll_wheel_listeners.iter().cloned() {
125            cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
126                listener(state, event, &bounds, phase, cx);
127            })
128        }
129
130        let hover_group_bounds = stateless
131            .group_hover
132            .as_ref()
133            .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx));
134
135        if let Some(group_bounds) = hover_group_bounds {
136            paint_hover_listener(group_bounds, cx);
137        }
138
139        if stateless.hover_style.is_some() {
140            paint_hover_listener(bounds, cx);
141        }
142
143        if let Some(stateful) = self.as_stateful() {
144            let click_listeners = stateful.mouse_click_listeners.clone();
145
146            let mouse_down = pending_click.lock().clone();
147            if let Some(mouse_down) = mouse_down {
148                cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
149                    if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
150                        let mouse_click = MouseClickEvent {
151                            down: mouse_down.clone(),
152                            up: event.clone(),
153                        };
154                        for listener in &click_listeners {
155                            listener(state, &mouse_click, cx);
156                        }
157                    }
158
159                    *pending_click.lock() = None;
160                });
161            } else {
162                cx.on_mouse_event(move |_state, event: &MouseDownEvent, phase, _cx| {
163                    if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
164                        *pending_click.lock() = Some(event.clone());
165                    }
166                });
167            };
168        }
169    }
170}
171
172fn paint_hover_listener<V>(bounds: Bounds<Pixels>, cx: &mut ViewContext<V>)
173where
174    V: 'static + Send + Sync,
175{
176    let hovered = bounds.contains_point(&cx.mouse_position());
177    cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
178        if phase == DispatchPhase::Capture {
179            if bounds.contains_point(&event.position) != hovered {
180                cx.notify();
181            }
182        }
183    });
184}
185
186#[derive(Deref, DerefMut)]
187pub struct StatefulInteractivity<V: 'static + Send + Sync> {
188    pub id: ElementId,
189    #[deref]
190    #[deref_mut]
191    stateless: StatelessInteractivity<V>,
192    pub mouse_click_listeners: SmallVec<[MouseClickListener<V>; 2]>,
193}
194
195impl<V> ElementInteractivity<V> for StatefulInteractivity<V>
196where
197    V: 'static + Send + Sync,
198{
199    fn as_stateful(&self) -> Option<&StatefulInteractivity<V>> {
200        Some(self)
201    }
202
203    fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity<V>> {
204        Some(self)
205    }
206
207    fn as_stateless(&self) -> &StatelessInteractivity<V> {
208        &self.stateless
209    }
210
211    fn as_stateless_mut(&mut self) -> &mut StatelessInteractivity<V> {
212        &mut self.stateless
213    }
214}
215
216impl<V> From<ElementId> for StatefulInteractivity<V>
217where
218    V: 'static + Send + Sync,
219{
220    fn from(id: ElementId) -> Self {
221        Self {
222            id,
223            stateless: StatelessInteractivity::default(),
224            mouse_click_listeners: SmallVec::new(),
225        }
226    }
227}
228
229pub struct StatelessInteractivity<V> {
230    pub mouse_down_listeners: SmallVec<[MouseDownListener<V>; 2]>,
231    pub mouse_up_listeners: SmallVec<[MouseUpListener<V>; 2]>,
232    pub mouse_move_listeners: SmallVec<[MouseMoveListener<V>; 2]>,
233    pub scroll_wheel_listeners: SmallVec<[ScrollWheelListener<V>; 2]>,
234    pub key_listeners: SmallVec<[(TypeId, KeyListener<V>); 32]>,
235    pub hover_style: StyleRefinement,
236    pub group_hover: Option<GroupStyle>,
237}
238
239pub struct GroupStyle {
240    pub group: SharedString,
241    pub style: StyleRefinement,
242}
243
244#[derive(Default)]
245pub struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
246
247impl GroupBounds {
248    pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
249        cx.default_global::<Self>()
250            .0
251            .get(name)
252            .and_then(|bounds_stack| bounds_stack.last())
253            .cloned()
254    }
255
256    pub fn push(name: SharedString, bounds: Bounds<Pixels>, cx: &mut AppContext) {
257        cx.default_global::<Self>()
258            .0
259            .entry(name)
260            .or_default()
261            .push(bounds);
262    }
263
264    pub fn pop(name: &SharedString, cx: &mut AppContext) {
265        cx.default_global::<GroupBounds>()
266            .0
267            .get_mut(name)
268            .unwrap()
269            .pop();
270    }
271}
272
273pub fn group_bounds(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
274    cx.default_global::<GroupBounds>()
275        .0
276        .get(name)
277        .and_then(|bounds_stack| bounds_stack.last().cloned())
278}
279
280impl<V> Default for StatelessInteractivity<V> {
281    fn default() -> Self {
282        Self {
283            mouse_down_listeners: SmallVec::new(),
284            mouse_up_listeners: SmallVec::new(),
285            mouse_move_listeners: SmallVec::new(),
286            scroll_wheel_listeners: SmallVec::new(),
287            key_listeners: SmallVec::new(),
288            hover_style: StyleRefinement::default(),
289            group_hover: None,
290        }
291    }
292}
293
294impl<V> ElementInteractivity<V> for StatelessInteractivity<V>
295where
296    V: 'static + Send + Sync,
297{
298    fn as_stateful(&self) -> Option<&StatefulInteractivity<V>> {
299        None
300    }
301
302    fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity<V>> {
303        None
304    }
305
306    fn as_stateless(&self) -> &StatelessInteractivity<V> {
307        self
308    }
309
310    fn as_stateless_mut(&mut self) -> &mut StatelessInteractivity<V> {
311        self
312    }
313}
314
315pub trait ElementFocusability<V: 'static + Send + Sync>: 'static + Send + Sync {
316    fn as_focusable(&self) -> Option<&Focusable<V>>;
317
318    fn initialize<R>(
319        &self,
320        cx: &mut ViewContext<V>,
321        f: impl FnOnce(&mut ViewContext<V>) -> R,
322    ) -> R {
323        if let Some(focusable) = self.as_focusable() {
324            for listener in focusable.focus_listeners.iter().cloned() {
325                cx.on_focus_changed(move |view, event, cx| listener(view, event, cx));
326            }
327            cx.with_focus(focusable.focus_handle.clone(), |cx| f(cx))
328        } else {
329            f(cx)
330        }
331    }
332
333    fn refine_style(&self, style: &mut Style, cx: &WindowContext) {
334        if let Some(focusable) = self.as_focusable() {
335            if focusable.focus_handle.contains_focused(cx) {
336                style.refine(&focusable.focus_in_style);
337            }
338
339            if focusable.focus_handle.within_focused(cx) {
340                style.refine(&focusable.in_focus_style);
341            }
342
343            if focusable.focus_handle.is_focused(cx) {
344                style.refine(&focusable.focus_style);
345            }
346        }
347    }
348
349    fn paint(&self, bounds: Bounds<Pixels>, cx: &mut WindowContext) {
350        if let Some(focusable) = self.as_focusable() {
351            let focus_handle = focusable.focus_handle.clone();
352            cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
353                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
354                    if !cx.default_prevented() {
355                        cx.focus(&focus_handle);
356                        cx.prevent_default();
357                    }
358                }
359            })
360        }
361    }
362}
363
364pub struct Focusable<V: 'static + Send + Sync> {
365    pub focus_handle: FocusHandle,
366    pub focus_listeners: FocusListeners<V>,
367    pub focus_style: StyleRefinement,
368    pub focus_in_style: StyleRefinement,
369    pub in_focus_style: StyleRefinement,
370}
371
372impl<V> ElementFocusability<V> for Focusable<V>
373where
374    V: 'static + Send + Sync,
375{
376    fn as_focusable(&self) -> Option<&Focusable<V>> {
377        Some(self)
378    }
379}
380
381impl<V> From<FocusHandle> for Focusable<V>
382where
383    V: 'static + Send + Sync,
384{
385    fn from(value: FocusHandle) -> Self {
386        Self {
387            focus_handle: value,
388            focus_listeners: FocusListeners::default(),
389            focus_style: StyleRefinement::default(),
390            focus_in_style: StyleRefinement::default(),
391            in_focus_style: StyleRefinement::default(),
392        }
393    }
394}
395
396pub struct NonFocusable;
397
398impl<V> ElementFocusability<V> for NonFocusable
399where
400    V: 'static + Send + Sync,
401{
402    fn as_focusable(&self) -> Option<&Focusable<V>> {
403        None
404    }
405}
406
407pub trait ParentElement: Element {
408    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::ViewState>; 2]>;
409
410    fn child(mut self, child: impl IntoAnyElement<Self::ViewState>) -> Self
411    where
412        Self: Sized,
413    {
414        self.children_mut().push(child.into_any());
415        self
416    }
417
418    fn children(
419        mut self,
420        iter: impl IntoIterator<Item = impl IntoAnyElement<Self::ViewState>>,
421    ) -> Self
422    where
423        Self: Sized,
424    {
425        self.children_mut()
426            .extend(iter.into_iter().map(|item| item.into_any()));
427        self
428    }
429}
430
431trait ElementObject<V>: 'static + Send + Sync {
432    fn initialize(&mut self, view_state: &mut V, cx: &mut ViewContext<V>);
433    fn layout(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) -> LayoutId;
434    fn paint(&mut self, view_state: &mut V, offset: Option<Point<Pixels>>, cx: &mut ViewContext<V>);
435}
436
437struct RenderedElement<E: Element> {
438    element: E,
439    phase: ElementRenderPhase<E::ElementState>,
440}
441
442#[derive(Default)]
443enum ElementRenderPhase<V> {
444    #[default]
445    Start,
446    Initialized {
447        frame_state: Option<V>,
448    },
449    LayoutRequested {
450        layout_id: LayoutId,
451        frame_state: Option<V>,
452    },
453    Painted,
454}
455
456/// Internal struct that wraps an element to store Layout and ElementState after the element is rendered.
457/// It's allocated as a trait object to erase the element type and wrapped in AnyElement<E::State> for
458/// improved usability.
459impl<E: Element> RenderedElement<E> {
460    fn new(element: E) -> Self {
461        RenderedElement {
462            element,
463            phase: ElementRenderPhase::Start,
464        }
465    }
466}
467
468impl<E> ElementObject<E::ViewState> for RenderedElement<E>
469where
470    E: Element,
471{
472    fn initialize(&mut self, view_state: &mut E::ViewState, cx: &mut ViewContext<E::ViewState>) {
473        let frame_state = if let Some(id) = self.element.id() {
474            cx.with_element_state(id, |element_state, cx| {
475                let element_state = self.element.initialize(view_state, element_state, cx);
476                ((), element_state)
477            });
478            None
479        } else {
480            let frame_state = self.element.initialize(view_state, None, cx);
481            Some(frame_state)
482        };
483
484        self.phase = ElementRenderPhase::Initialized { frame_state };
485    }
486
487    fn layout(&mut self, state: &mut E::ViewState, cx: &mut ViewContext<E::ViewState>) -> LayoutId {
488        let layout_id;
489        let mut frame_state;
490        match mem::take(&mut self.phase) {
491            ElementRenderPhase::Initialized {
492                frame_state: initial_frame_state,
493            } => {
494                frame_state = initial_frame_state;
495                if let Some(id) = self.element.id() {
496                    layout_id = cx.with_element_state(id, |element_state, cx| {
497                        let mut element_state = element_state.unwrap();
498                        let layout_id = self.element.layout(state, &mut element_state, cx);
499                        (layout_id, element_state)
500                    });
501                } else {
502                    layout_id = self
503                        .element
504                        .layout(state, frame_state.as_mut().unwrap(), cx);
505                }
506            }
507            _ => panic!("must call initialize before layout"),
508        };
509
510        self.phase = ElementRenderPhase::LayoutRequested {
511            layout_id,
512            frame_state,
513        };
514        layout_id
515    }
516
517    fn paint(
518        &mut self,
519        view_state: &mut E::ViewState,
520        offset: Option<Point<Pixels>>,
521        cx: &mut ViewContext<E::ViewState>,
522    ) {
523        self.phase = match mem::take(&mut self.phase) {
524            ElementRenderPhase::LayoutRequested {
525                layout_id,
526                mut frame_state,
527            } => {
528                let mut bounds = cx.layout_bounds(layout_id);
529                offset.map(|offset| bounds.origin += offset);
530                if let Some(id) = self.element.id() {
531                    cx.with_element_state(id, |element_state, cx| {
532                        let mut element_state = element_state.unwrap();
533                        self.element
534                            .paint(bounds, view_state, &mut element_state, cx);
535                        ((), element_state)
536                    });
537                } else {
538                    self.element
539                        .paint(bounds, view_state, frame_state.as_mut().unwrap(), cx);
540                }
541                ElementRenderPhase::Painted
542            }
543
544            _ => panic!("must call layout before paint"),
545        };
546    }
547}
548
549pub struct AnyElement<V>(Box<dyn ElementObject<V>>);
550
551impl<V: 'static + Send + Sync> AnyElement<V> {
552    pub fn new<E: Element<ViewState = V>>(element: E) -> Self {
553        AnyElement(Box::new(RenderedElement::new(element)))
554    }
555
556    pub fn initialize(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) {
557        self.0.initialize(view_state, cx);
558    }
559
560    pub fn layout(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) -> LayoutId {
561        self.0.layout(view_state, cx)
562    }
563
564    pub fn paint(
565        &mut self,
566        view_state: &mut V,
567        offset: Option<Point<Pixels>>,
568        cx: &mut ViewContext<V>,
569    ) {
570        self.0.paint(view_state, offset, cx)
571    }
572}
573
574pub trait IntoAnyElement<V> {
575    fn into_any(self) -> AnyElement<V>;
576}
577
578impl<V> IntoAnyElement<V> for AnyElement<V> {
579    fn into_any(self) -> AnyElement<V> {
580        self
581    }
582}