clickable.rs

  1use crate::{
  2    AnyElement, Bounds, DispatchPhase, Element, IdentifiedElement, Interactive, IntoAnyElement,
  3    MouseDownEvent, MouseEventListeners, MouseUpEvent, ParentElement, Pixels, Styled, ViewContext,
  4};
  5use parking_lot::Mutex;
  6use refineable::Cascade;
  7use smallvec::SmallVec;
  8use std::sync::Arc;
  9
 10pub type ClickListener<S> =
 11    dyn Fn(&mut S, (&MouseDownEvent, &MouseUpEvent), &mut ViewContext<S>) + Send + Sync + 'static;
 12
 13pub struct Clickable<E: Element> {
 14    child: E,
 15    listener: Arc<ClickListener<E::ViewState>>,
 16}
 17
 18pub struct ClickableState<S> {
 19    last_mouse_down: Arc<Mutex<Option<MouseDownEvent>>>,
 20    child_state: S,
 21}
 22
 23impl<E: Element> Clickable<E> {
 24    pub fn new(child: E, listener: Arc<ClickListener<E::ViewState>>) -> Self {
 25        Self { child, listener }
 26    }
 27}
 28
 29impl<E> Styled for Clickable<E>
 30where
 31    E: Styled + IdentifiedElement,
 32{
 33    type Style = E::Style;
 34
 35    fn style_cascade(&mut self) -> &mut Cascade<E::Style> {
 36        self.child.style_cascade()
 37    }
 38
 39    fn declared_style(&mut self) -> &mut <Self::Style as refineable::Refineable>::Refinement {
 40        self.child.declared_style()
 41    }
 42}
 43
 44impl<S, E> Interactive<S> for Clickable<E>
 45where
 46    S: 'static + Send + Sync,
 47    E: IdentifiedElement + Interactive<S>,
 48{
 49    fn listeners(&mut self) -> &mut MouseEventListeners<S> {
 50        self.child.listeners()
 51    }
 52}
 53
 54impl<E: IdentifiedElement> IntoAnyElement<E::ViewState> for Clickable<E> {
 55    fn into_any(self) -> AnyElement<E::ViewState> {
 56        AnyElement::new(self)
 57    }
 58}
 59
 60impl<E> Element for Clickable<E>
 61where
 62    E: IdentifiedElement,
 63{
 64    type ViewState = E::ViewState;
 65    type ElementState = ClickableState<E::ElementState>;
 66
 67    fn element_id(&self) -> Option<crate::ElementId> {
 68        Some(IdentifiedElement::element_id(&self.child))
 69    }
 70
 71    fn layout(
 72        &mut self,
 73        state: &mut Self::ViewState,
 74        element_state: Option<Self::ElementState>,
 75        cx: &mut ViewContext<Self::ViewState>,
 76    ) -> (crate::LayoutId, Self::ElementState) {
 77        if let Some(element_state) = element_state {
 78            let (layout_id, child_state) =
 79                self.child
 80                    .layout(state, Some(element_state.child_state), cx);
 81
 82            let element_state = ClickableState {
 83                last_mouse_down: element_state.last_mouse_down,
 84                child_state,
 85            };
 86            (layout_id, element_state)
 87        } else {
 88            let (layout_id, child_state) = self.child.layout(state, None, cx);
 89            let element_state = ClickableState {
 90                last_mouse_down: Default::default(),
 91                child_state,
 92            };
 93            (layout_id, element_state)
 94        }
 95    }
 96
 97    fn paint(
 98        &mut self,
 99        bounds: Bounds<Pixels>,
100        state: &mut Self::ViewState,
101        element_state: &mut Self::ElementState,
102        cx: &mut ViewContext<Self::ViewState>,
103    ) {
104        let last_mouse_down = element_state.last_mouse_down.clone();
105        let is_some = last_mouse_down.lock().is_some();
106
107        if is_some {
108            let listener = self.listener.clone();
109            cx.on_mouse_event(move |view, up_event: &MouseUpEvent, phase, cx| {
110                if phase == DispatchPhase::Capture && !bounds.contains_point(up_event.position) {
111                    *last_mouse_down.lock() = None;
112                } else if phase == DispatchPhase::Bubble && bounds.contains_point(up_event.position)
113                {
114                    if let Some(down_event) = last_mouse_down.lock().take() {
115                        listener(view, (&down_event, up_event), cx);
116                    } else {
117                        log::error!("No mouse down event found for click event");
118                    }
119                }
120            })
121        } else {
122            cx.on_mouse_event(move |_, event: &MouseDownEvent, phase, _| {
123                if phase == DispatchPhase::Bubble {
124                    if bounds.contains_point(event.position) {
125                        *last_mouse_down.lock() = Some(event.clone());
126                    }
127                }
128            })
129        }
130
131        self.child
132            .paint(bounds, state, &mut element_state.child_state, cx);
133    }
134}
135
136impl<E: IdentifiedElement + ParentElement> ParentElement for Clickable<E> {
137    type State = E::State;
138
139    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::State>; 2]> {
140        self.child.children_mut()
141    }
142}
143
144impl<E> IdentifiedElement for Clickable<E> where E: IdentifiedElement + Styled {}