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 {}