1use crate::{
2 AnyElement, Bounds, DispatchPhase, Element, IdentifiedElement, Interactive, MouseDownEvent,
3 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> Element for Clickable<E>
55where
56 E: IdentifiedElement,
57{
58 type ViewState = E::ViewState;
59 type ElementState = ClickableState<E::ElementState>;
60
61 fn element_id(&self) -> Option<crate::ElementId> {
62 Some(IdentifiedElement::element_id(&self.child))
63 }
64
65 fn layout(
66 &mut self,
67 state: &mut Self::ViewState,
68 element_state: Option<Self::ElementState>,
69 cx: &mut ViewContext<Self::ViewState>,
70 ) -> (crate::LayoutId, Self::ElementState) {
71 if let Some(element_state) = element_state {
72 let (layout_id, child_state) =
73 self.child
74 .layout(state, Some(element_state.child_state), cx);
75
76 let element_state = ClickableState {
77 last_mouse_down: element_state.last_mouse_down,
78 child_state,
79 };
80 (layout_id, element_state)
81 } else {
82 let (layout_id, child_state) = self.child.layout(state, None, cx);
83 let element_state = ClickableState {
84 last_mouse_down: Default::default(),
85 child_state,
86 };
87 (layout_id, element_state)
88 }
89 }
90
91 fn paint(
92 &mut self,
93 bounds: Bounds<Pixels>,
94 state: &mut Self::ViewState,
95 element_state: &mut Self::ElementState,
96 cx: &mut ViewContext<Self::ViewState>,
97 ) {
98 let last_mouse_down = element_state.last_mouse_down.clone();
99 let is_some = last_mouse_down.lock().is_some();
100
101 if is_some {
102 let listener = self.listener.clone();
103 cx.on_mouse_event(move |view, up_event: &MouseUpEvent, phase, cx| {
104 if phase == DispatchPhase::Capture && !bounds.contains_point(up_event.position) {
105 *last_mouse_down.lock() = None;
106 } else if phase == DispatchPhase::Bubble && bounds.contains_point(up_event.position)
107 {
108 if let Some(down_event) = last_mouse_down.lock().take() {
109 listener(view, (&down_event, up_event), cx);
110 } else {
111 log::error!("No mouse down event found for click event");
112 }
113 }
114 })
115 } else {
116 cx.on_mouse_event(move |_, event: &MouseDownEvent, phase, _| {
117 if phase == DispatchPhase::Bubble {
118 if bounds.contains_point(event.position) {
119 *last_mouse_down.lock() = Some(event.clone());
120 }
121 }
122 })
123 }
124
125 self.child
126 .paint(bounds, state, &mut element_state.child_state, cx);
127 }
128}
129
130impl<E: IdentifiedElement + ParentElement> ParentElement for Clickable<E> {
131 type State = E::State;
132
133 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::State>; 2]> {
134 self.child.children_mut()
135 }
136}
137
138impl<E> IdentifiedElement for Clickable<E> where E: IdentifiedElement + Styled {}