embedded_ui/event.rs
1use core::fmt::Debug;
2use core::{marker::PhantomData, ops::ControlFlow};
3
4use alloc::vec::Vec;
5
6use crate::el::ElId;
7
8#[derive(Clone, Debug)]
9pub enum Capture {
10 /// Event is captured by element and should not be accepted by its parents
11 Captured,
12}
13
14impl<E: Event> Into<EventResponse<E>> for Capture {
15 #[inline]
16 fn into(self) -> EventResponse<E> {
17 EventResponse::Break(self)
18 }
19}
20
21#[derive(Clone, Debug)]
22pub enum Propagate<E: Event> {
23 /// Event is ignored by element and can be accepted by parents
24 Ignored,
25 /// Event is accepted by element and does not belongs to it logic but its parent.
26 /// For example FocusMove on focused button is captured by button but bubbles up to its container which already moves the focus to next children. Check source of Linear container as an example of how to handle bubble up and why it doesn't need to store any state or identifier of element started the bubble up.
27 BubbleUp(ElId, E),
28}
29
30impl<E: Event> Into<EventResponse<E>> for Propagate<E> {
31 #[inline]
32 fn into(self) -> EventResponse<E> {
33 EventResponse::Continue(self)
34 }
35}
36
37pub type EventResponse<E> = ControlFlow<Capture, Propagate<E>>;
38
39#[derive(Clone, Copy, Debug)]
40pub enum CommonEvent {
41 /// Moves focus to current ±offset
42 FocusMove(i32),
43 /// Moves focus starting from back (internal usage only)
44 // FocusMoveRev(i32),
45 /// Focus click button (e.g. enter key) is down
46 FocusClickDown,
47 // Focus click button is up
48 FocusClickUp,
49}
50
51// Unused
52// impl Event for CommonEvent {
53// fn as_common(&self) -> Option<CommonEvent> {
54// Some(*self)
55// }
56// }
57
58pub trait Event: Clone + From<CommonEvent> + Debug {
59 // fn is_focus_move(&self) -> Option<i32>;
60
61 // fn is_focus_click(&self) -> bool;
62
63 fn as_common(&self) -> Option<CommonEvent>;
64
65 // TODO: This might better be split and moved to separate traits such as `AsSelectShift`, etc. so if user don't want to use Slider for example, these methods don't need to be implemented.
66 // Or the easier way is to make these methods return `None` or use `FocusMove` by default.
67 fn as_select_shift(&self) -> Option<i32>;
68 fn as_slider_shift(&self) -> Option<i32>;
69 fn as_knob_rotation(&self) -> Option<i32>;
70 fn as_input_letter_scroll(&self) -> Option<i32>;
71}
72
73#[derive(Clone, Debug)]
74pub struct EventStub;
75
76impl Event for EventStub {
77 fn as_common(&self) -> Option<CommonEvent> {
78 None
79 }
80
81 fn as_select_shift(&self) -> Option<i32> {
82 None
83 }
84
85 fn as_slider_shift(&self) -> Option<i32> {
86 None
87 }
88
89 fn as_knob_rotation(&self) -> Option<i32> {
90 None
91 }
92
93 fn as_input_letter_scroll(&self) -> Option<i32> {
94 None
95 }
96}
97
98impl From<CommonEvent> for EventStub {
99 fn from(_: CommonEvent) -> Self {
100 Self
101 }
102}
103
104// pub struct EventHandler<E: Event, H: FnOnce(E) -> EventResponse> {
105// handler: H,
106// // TODO: Can I get rid of this marker?
107// marker: PhantomData<E>,
108// }
109
110// impl<E: Event, H> From<H> for EventHandler<E, H>
111// where
112// H: FnOnce(E) -> EventResponse,
113// {
114// fn from(value: H) -> Self {
115// EventHandler {
116// handler: value,
117// marker: PhantomData,
118// }
119// }
120// }
121
122pub trait Controls<E: Event> {
123 // TODO: Pass state to event collector of platform. Is should include:
124 // - Focus target (widget id). For example, encoder click in common case is FocusClick, but on other page its logic differs
125 fn events(&mut self) -> Vec<E>;
126}
127
128impl<F, E: Event> Controls<E> for F
129where
130 F: FnMut() -> Vec<E>,
131{
132 fn events(&mut self) -> Vec<E> {
133 self()
134 }
135}
136
137pub struct NullControls<E: Event> {
138 marker: PhantomData<E>,
139}
140
141impl<E: Event> Controls<E> for NullControls<E> {
142 fn events(&mut self) -> Vec<E> {
143 vec![]
144 }
145}
146
147impl<E: Event> Default for NullControls<E> {
148 fn default() -> Self {
149 Self { marker: PhantomData }
150 }
151}