clickable.rs

  1use crate::{
  2    AnyElement, Bounds, DispatchPhase, Element, ElementId, Hoverable, IdentifiedElement,
  3    IntoAnyElement, LayoutId, MouseDownEvent, MouseUpEvent, ParentElement, Pixels, SharedString,
  4    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: Styled + Element> ClickableElement<E> {
 57    pub fn new(mut child: E) -> Self {
 58        let cascade_slot = child.style_cascade().reserve();
 59        ClickableElement {
 60            child,
 61            listeners: Default::default(),
 62            active_style: Default::default(),
 63            cascade_slot,
 64        }
 65    }
 66
 67    pub fn replace_child<E2: Element<ViewState = E::ViewState>>(
 68        self,
 69        replace: impl FnOnce(E) -> E2,
 70    ) -> ClickableElement<E2> {
 71        ClickableElement {
 72            child: replace(self.child),
 73            listeners: self.listeners,
 74            active_style: self.active_style,
 75            cascade_slot: self.cascade_slot,
 76        }
 77    }
 78}
 79
 80impl<E> IntoAnyElement<E::ViewState> for ClickableElement<E>
 81where
 82    E: Styled + Element,
 83{
 84    fn into_any(self) -> AnyElement<E::ViewState> {
 85        AnyElement::new(self)
 86    }
 87}
 88
 89impl<E> Element for ClickableElement<E>
 90where
 91    E: Styled + Element,
 92{
 93    type ViewState = E::ViewState;
 94    type ElementState = ClickableElementState<E::ElementState>;
 95
 96    fn id(&self) -> Option<ElementId> {
 97        self.child.id()
 98    }
 99
100    fn layout(
101        &mut self,
102        state: &mut Self::ViewState,
103        element_state: Option<Self::ElementState>,
104        cx: &mut ViewContext<Self::ViewState>,
105    ) -> (LayoutId, Self::ElementState) {
106        if let Some(element_state) = element_state {
107            if element_state.mouse_down.lock().is_some() {
108                self.child
109                    .style_cascade()
110                    .set(self.cascade_slot, Some(self.active_style.clone()));
111            }
112
113            let (layout_id, child_state) =
114                self.child
115                    .layout(state, Some(element_state.child_state), cx);
116            (
117                layout_id,
118                ClickableElementState {
119                    mouse_down: element_state.mouse_down,
120                    child_state,
121                },
122            )
123        } else {
124            let (layout_id, child_state) = self.child.layout(state, None, cx);
125            (
126                layout_id,
127                ClickableElementState {
128                    mouse_down: Default::default(),
129                    child_state,
130                },
131            )
132        }
133    }
134
135    fn paint(
136        &mut self,
137        bounds: Bounds<Pixels>,
138        state: &mut Self::ViewState,
139        element_state: &mut Self::ElementState,
140        cx: &mut ViewContext<Self::ViewState>,
141    ) {
142        if !self.listeners.is_empty() || self.active_style.is_some() {
143            if let Some(mouse_down) = element_state.mouse_down.lock().clone() {
144                self.child
145                    .style_cascade()
146                    .set(self.cascade_slot, Some(self.active_style.clone()));
147                let listeners = self.listeners.clone();
148                let mouse_down_mutex = element_state.mouse_down.clone();
149                cx.on_mouse_event(move |view, up: &MouseUpEvent, phase, cx| {
150                    if phase == DispatchPhase::Bubble && bounds.contains_point(up.position) {
151                        for listener in &*listeners {
152                            listener(
153                                view,
154                                &MouseClickEvent {
155                                    down: mouse_down.clone(),
156                                    up: up.clone(),
157                                },
158                                cx,
159                            );
160                        }
161                    }
162
163                    mouse_down_mutex.lock().take();
164                    cx.notify();
165                });
166            } else {
167                let mouse_down_mutex = element_state.mouse_down.clone();
168                cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| {
169                    if phase == DispatchPhase::Bubble && bounds.contains_point(down.position) {
170                        *mouse_down_mutex.lock() = Some(down.clone());
171                        cx.notify();
172                    }
173                });
174            }
175        }
176
177        self.child
178            .paint(bounds, state, &mut element_state.child_state, cx);
179    }
180}
181
182impl<E: Styled + IdentifiedElement> IdentifiedElement for ClickableElement<E> {}
183
184impl<E> ParentElement for ClickableElement<E>
185where
186    E: Styled + ParentElement,
187{
188    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::ViewState>; 2]> {
189        self.child.children_mut()
190    }
191
192    fn group_mut(&mut self) -> &mut Option<SharedString> {
193        self.child.group_mut()
194    }
195}
196
197impl<E> Styled for ClickableElement<E>
198where
199    E: Styled + Element,
200{
201    fn style_cascade(&mut self) -> &mut crate::StyleCascade {
202        self.child.style_cascade()
203    }
204
205    fn computed_style(&mut self) -> &crate::Style {
206        self.child.computed_style()
207    }
208}
209
210impl<E> Hoverable for ClickableElement<E>
211where
212    E: Element + Hoverable,
213{
214    fn hover_style(&mut self) -> &mut StyleRefinement {
215        self.child.hover_style()
216    }
217}
218
219impl<E> Clickable for ClickableElement<E>
220where
221    E: Styled + IdentifiedElement,
222{
223    fn active_style(&mut self) -> &mut StyleRefinement {
224        &mut self.active_style
225    }
226
227    fn listeners(&mut self) -> &mut ClickListeners<Self::ViewState> {
228        &mut self.listeners
229    }
230}