clickable.rs

  1use crate::{
  2    AnyElement, Bounds, DispatchPhase, Element, ElementId, ElementKind, Hoverable,
  3    IdentifiedElement, IntoAnyElement, LayoutId, LayoutNode, MouseDownEvent, MouseUpEvent, Pixels,
  4    SharedString, StyleRefinement, Styled, ViewContext,
  5};
  6use parking_lot::Mutex;
  7use refineable::CascadeSlot;
  8use smallvec::SmallVec;
  9use std::sync::Arc;
 10
 11pub trait Clickable: Element + Sized {
 12    fn active_style(&mut self) -> &mut StyleRefinement;
 13    fn listeners(&mut self) -> &mut ClickListeners<Self::ViewState>;
 14
 15    fn on_click(
 16        &mut self,
 17        f: impl Fn(&mut Self::ViewState, &MouseClickEvent, &mut ViewContext<Self::ViewState>)
 18            + 'static
 19            + Send
 20            + Sync,
 21    ) where
 22        Self: Sized,
 23    {
 24        self.listeners().push(Arc::new(f));
 25    }
 26
 27    fn active(mut self, f: impl FnOnce(&mut StyleRefinement) -> &mut StyleRefinement) -> Self
 28    where
 29        Self: Sized,
 30    {
 31        f(self.active_style());
 32        self
 33    }
 34}
 35
 36pub type ClickListeners<V> =
 37    SmallVec<[Arc<dyn Fn(&mut V, &MouseClickEvent, &mut ViewContext<V>) + Send + Sync>; 1]>;
 38
 39pub struct ClickableElementState<E: 'static + Send + Sync> {
 40    mouse_down: Arc<Mutex<Option<MouseDownEvent>>>,
 41    child_state: E,
 42}
 43
 44pub struct MouseClickEvent {
 45    pub down: MouseDownEvent,
 46    pub up: MouseUpEvent,
 47}
 48
 49pub struct ClickableElement<E: Element> {
 50    child: E,
 51    listeners: ClickListeners<E::ViewState>,
 52    active_style: StyleRefinement,
 53    cascade_slot: CascadeSlot,
 54}
 55
 56impl<E: Element> ClickableElement<E> {
 57    pub fn replace_child<E2: Element<ViewState = E::ViewState>>(
 58        self,
 59        replace: impl FnOnce(E) -> E2,
 60    ) -> ClickableElement<E2> {
 61        ClickableElement {
 62            child: replace(self.child),
 63            listeners: self.listeners,
 64            active_style: self.active_style,
 65            cascade_slot: self.cascade_slot,
 66        }
 67    }
 68}
 69
 70impl<E> IntoAnyElement<E::ViewState> for ClickableElement<E>
 71where
 72    E: Styled + Element,
 73{
 74    fn into_any(self) -> AnyElement<E::ViewState> {
 75        AnyElement::new(self)
 76    }
 77}
 78
 79impl<E> Element for ClickableElement<E>
 80where
 81    E: Styled + Element,
 82{
 83    type ViewState = E::ViewState;
 84    type ElementState = ClickableElementState<E::ElementState>;
 85
 86    fn id(&self) -> Option<ElementId> {
 87        self.child.id()
 88    }
 89
 90    fn layout(
 91        &mut self,
 92        state: &mut Self::ViewState,
 93        element_state: Option<Self::ElementState>,
 94        cx: &mut ViewContext<Self::ViewState>,
 95    ) -> (LayoutId, Self::ElementState) {
 96        if let Some(element_state) = element_state {
 97            if element_state.mouse_down.lock().is_some() {
 98                self.child
 99                    .style_cascade()
100                    .set(self.cascade_slot, Some(self.active_style.clone()));
101            }
102
103            let (layout_id, child_state) =
104                self.child
105                    .layout(state, Some(element_state.child_state), cx);
106            (
107                layout_id,
108                ClickableElementState {
109                    mouse_down: element_state.mouse_down,
110                    child_state,
111                },
112            )
113        } else {
114            let (layout_id, child_state) = self.child.layout(state, None, cx);
115            (
116                layout_id,
117                ClickableElementState {
118                    mouse_down: Default::default(),
119                    child_state,
120                },
121            )
122        }
123    }
124
125    fn paint(
126        &mut self,
127        bounds: Bounds<Pixels>,
128        state: &mut Self::ViewState,
129        element_state: &mut Self::ElementState,
130        cx: &mut ViewContext<Self::ViewState>,
131    ) {
132        if !self.listeners.is_empty() || self.active_style.is_some() {
133            if let Some(mouse_down) = element_state.mouse_down.lock().clone() {
134                self.child
135                    .style_cascade()
136                    .set(self.cascade_slot, Some(self.active_style.clone()));
137                let listeners = self.listeners.clone();
138                let mouse_down_mutex = element_state.mouse_down.clone();
139                cx.on_mouse_event(move |view, up: &MouseUpEvent, phase, cx| {
140                    if phase == DispatchPhase::Bubble && bounds.contains_point(up.position) {
141                        for listener in &*listeners {
142                            listener(
143                                view,
144                                &MouseClickEvent {
145                                    down: mouse_down.clone(),
146                                    up: up.clone(),
147                                },
148                                cx,
149                            );
150                        }
151                    }
152
153                    mouse_down_mutex.lock().take();
154                    cx.notify();
155                });
156            } else {
157                let mouse_down_mutex = element_state.mouse_down.clone();
158                cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| {
159                    if phase == DispatchPhase::Bubble && bounds.contains_point(down.position) {
160                        *mouse_down_mutex.lock() = Some(down.clone());
161                        cx.notify();
162                    }
163                });
164            }
165        }
166
167        self.child
168            .paint(bounds, state, &mut element_state.child_state, cx);
169    }
170}
171
172impl<E: Styled + IdentifiedElement> IdentifiedElement for ClickableElement<E> {}
173
174impl<E, K> LayoutNode<E::ViewState, K> for ClickableElement<E>
175where
176    E: Element + LayoutNode<E::ViewState, K>,
177    K: ElementKind,
178{
179    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<E::ViewState>; 2]> {
180        self.child.children_mut()
181    }
182
183    fn group_mut(&mut self) -> &mut Option<SharedString> {
184        self.child.group_mut()
185    }
186}
187
188impl<E> Styled for ClickableElement<E>
189where
190    E: Styled + Element,
191{
192    fn style_cascade(&mut self) -> &mut crate::StyleCascade {
193        self.child.style_cascade()
194    }
195
196    fn computed_style(&mut self) -> &crate::Style {
197        self.child.computed_style()
198    }
199}
200
201impl<E> Hoverable for ClickableElement<E>
202where
203    E: Element + Hoverable,
204{
205    fn hover_style(&mut self) -> &mut StyleRefinement {
206        self.child.hover_style()
207    }
208}
209
210impl<E> Clickable for ClickableElement<E>
211where
212    E: Styled + IdentifiedElement,
213{
214    fn active_style(&mut self) -> &mut StyleRefinement {
215        &mut self.active_style
216    }
217
218    fn listeners(&mut self) -> &mut ClickListeners<Self::ViewState> {
219        &mut self.listeners
220    }
221}