cranpose_ui/modifier/
rotary_input.rs1use std::{
2 cell::Cell,
3 fmt,
4 hash::{Hash, Hasher},
5 rc::Rc,
6};
7
8use cranpose_foundation::{
9 DelegatableNode, ModifierNode, ModifierNodeElement, NodeCapabilities, NodeState, PointerEvent,
10 PointerEventKind, PointerInputNode, RotaryScrollEvent, impl_pointer_input_node,
11};
12
13use super::{Modifier, inspector_metadata};
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
16enum RotaryPass {
17 Pre,
18 Bubble,
19}
20
21impl RotaryPass {
22 fn matches(self, kind: PointerEventKind) -> bool {
23 matches!(
24 (self, kind),
25 (RotaryPass::Pre, PointerEventKind::RotaryScrollPre)
26 | (RotaryPass::Bubble, PointerEventKind::RotaryScroll)
27 )
28 }
29}
30
31type RotaryHandler = Rc<dyn Fn(RotaryScrollEvent) -> bool>;
32
33impl Modifier {
34 pub fn on_rotary_scroll_event<F>(self, handler: F) -> Self
50 where
51 F: Fn(RotaryScrollEvent) -> bool + 'static,
52 {
53 self.rotary_element(RotaryPass::Bubble, Rc::new(handler), "onRotaryScrollEvent")
54 }
55
56 pub fn on_pre_rotary_scroll_event<F>(self, handler: F) -> Self
63 where
64 F: Fn(RotaryScrollEvent) -> bool + 'static,
65 {
66 self.rotary_element(RotaryPass::Pre, Rc::new(handler), "onPreRotaryScrollEvent")
67 }
68
69 fn rotary_element(
70 self,
71 pass: RotaryPass,
72 handler: RotaryHandler,
73 inspector_name: &'static str,
74 ) -> Self {
75 let element = RotaryInputElement::new(pass, handler);
76 let handler_id = element.handler_id;
77 self.then(
78 Self::with_element(element).with_inspector_metadata(inspector_metadata(
79 inspector_name,
80 move |info| {
81 info.add_property("handlerId", handler_id.to_string());
82 },
83 )),
84 )
85 }
86}
87
88#[derive(Clone)]
89struct RotaryInputElement {
90 pass: RotaryPass,
91 handler: RotaryHandler,
92 handler_id: u64,
93}
94
95impl RotaryInputElement {
96 fn new(pass: RotaryPass, handler: RotaryHandler) -> Self {
97 let handler_id = Rc::as_ptr(&handler) as *const () as usize as u64;
98 Self {
99 pass,
100 handler,
101 handler_id,
102 }
103 }
104}
105
106impl fmt::Debug for RotaryInputElement {
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 f.debug_struct("RotaryInputElement")
109 .field("pass", &self.pass)
110 .field("handler_id", &self.handler_id)
111 .finish()
112 }
113}
114
115impl PartialEq for RotaryInputElement {
116 fn eq(&self, other: &Self) -> bool {
117 self.pass == other.pass
118 }
119}
120
121impl Eq for RotaryInputElement {}
122
123impl Hash for RotaryInputElement {
124 fn hash<H: Hasher>(&self, state: &mut H) {
125 self.pass.hash(state);
126 }
127}
128
129impl ModifierNodeElement for RotaryInputElement {
130 type Node = RotaryInputModifierNode;
131
132 fn create(&self) -> Self::Node {
133 RotaryInputModifierNode::new(self.pass, self.handler.clone())
134 }
135
136 fn update(&self, node: &mut Self::Node) {
137 node.handler.set(Some(self.handler.clone()));
138 }
139
140 fn always_update(&self) -> bool {
141 true
142 }
143
144 fn capabilities(&self) -> NodeCapabilities {
145 NodeCapabilities::POINTER_INPUT
146 }
147}
148
149pub struct RotaryInputModifierNode {
156 handler: Rc<Cell<Option<RotaryHandler>>>,
157 dispatch: Rc<dyn Fn(PointerEvent)>,
158 state: NodeState,
159}
160
161impl RotaryInputModifierNode {
162 fn new(pass: RotaryPass, handler: RotaryHandler) -> Self {
163 let handler_cell: Rc<Cell<Option<RotaryHandler>>> = Rc::new(Cell::new(Some(handler)));
164 let handler_for_dispatch = Rc::clone(&handler_cell);
165 let dispatch = Rc::new(move |event: PointerEvent| {
166 if !pass.matches(event.kind) || event.is_consumed() {
167 return;
168 }
169 let Some(rotary) = event.rotary_scroll_event() else {
170 return;
171 };
172 let Some(handler) = handler_for_dispatch.take() else {
173 return;
174 };
175 let consumed = handler(rotary);
176 if handler_for_dispatch.take().is_none() {
177 handler_for_dispatch.set(Some(handler));
178 }
179 if consumed {
180 event.consume();
181 }
182 });
183
184 Self {
185 handler: handler_cell,
186 dispatch,
187 state: NodeState::new(),
188 }
189 }
190}
191
192impl ModifierNode for RotaryInputModifierNode {
193 impl_pointer_input_node!();
194}
195
196impl DelegatableNode for RotaryInputModifierNode {
197 fn node_state(&self) -> &NodeState {
198 &self.state
199 }
200}
201
202impl PointerInputNode for RotaryInputModifierNode {
203 fn pointer_input_handler(&self) -> Option<Rc<dyn Fn(PointerEvent)>> {
204 Some(Rc::clone(&self.dispatch))
205 }
206}
207
208#[cfg(test)]
209#[path = "tests/rotary_input_tests.rs"]
210mod tests;