old school music tracker
1pub mod button;
2pub mod slider;
3pub mod text_in;
4// pub mod text_in_scroll;
5pub mod toggle;
6pub mod toggle_button;
7
8use winit::{
9 event::{KeyEvent, Modifiers},
10 keyboard::{Key, ModifiersState, NamedKey},
11};
12
13use crate::{EventQueue, draw_buffer::DrawBuffer, pages::PageResponse};
14
15pub(crate) trait Widget {
16 fn draw(&self, draw_buffer: &mut DrawBuffer, selected: bool);
17 /// true if the widget data was changed by the input
18 fn process_input(
19 &mut self,
20 modifiers: &Modifiers,
21 key_event: &KeyEvent,
22 events: &mut EventQueue<'_>,
23 ) -> WidgetResponse;
24
25 #[cfg(feature = "accesskit")]
26 fn build_tree(&self, tree: &mut Vec<(accesskit::NodeId, accesskit::Node)>);
27}
28
29// #[derive(Debug)]
30// pub struct WidgetResponse<R> {
31// pub standard: StandardResponse,
32// pub extra: Option<R>,
33// }
34
35// impl<R> Default for WidgetResponse<R> {
36// fn default() -> Self {
37// Self {
38// standard: StandardResponse::default(),
39// extra: None,
40// }
41// }
42// }
43
44// impl<R> WidgetResponse<R> {
45// pub fn request_redraw() -> Self {
46// Self {
47// standard: StandardResponse::RequestRedraw,
48// extra: None,
49// }
50// }
51
52// pub fn next_widget(value: u8) -> Self {
53// Self {
54// standard: StandardResponse::SwitchFocus(value),
55// extra: None,
56// }
57// }
58// }
59
60// SwitchFocus also has to request a redraw
61#[derive(Debug, Default, PartialEq, Eq)]
62pub enum WidgetResponse {
63 SwitchFocus(u8),
64 /// true if the redraw is because the widget data was changed.
65 /// This could be true even if the data didn't change. This makes the implementation of some widgets easier.
66 /// It is still useful to keep other parts of the system in sync.
67 ///
68 /// A redraw request could otherwise be made because for example a internal cursor changed
69 RequestRedraw(bool),
70 // GlobalEvent(GlobalEvent),
71 #[default]
72 None,
73}
74
75// impl<R> From<StandardResponse> for WidgetResponse<R> {
76// fn from(value: StandardResponse) -> Self {
77// Self {
78// standard: value,
79// extra: None,
80// }
81// }
82// }
83
84impl WidgetResponse {
85 pub fn to_page_resp(self, selected: &mut u8) -> PageResponse {
86 match self {
87 WidgetResponse::SwitchFocus(s) => {
88 *selected = s;
89 PageResponse::RequestRedraw
90 }
91 WidgetResponse::RequestRedraw(_) => PageResponse::RequestRedraw,
92 WidgetResponse::None => PageResponse::None,
93 }
94 }
95
96 pub fn on_change(self, f: impl FnOnce()) -> Self {
97 if self == WidgetResponse::RequestRedraw(true) {
98 f();
99 }
100 self
101 }
102}
103
104#[derive(Debug, Default)]
105pub struct NextWidget {
106 pub left: Option<u8>,
107 pub right: Option<u8>,
108 pub up: Option<u8>,
109 pub down: Option<u8>,
110 pub tab: Option<u8>,
111 pub shift_tab: Option<u8>,
112}
113
114impl NextWidget {
115 /// supposed to be called from Widgets, that own a NextWidget after catching their custom KeyEvents to pick a return
116 pub fn process_key_event(&self, key_event: &KeyEvent, modifiers: &Modifiers) -> WidgetResponse {
117 if !key_event.state.is_pressed() {
118 return WidgetResponse::None;
119 }
120
121 #[expect(
122 non_local_definitions,
123 reason = "this is only valid with these specific Option<u8> not in general"
124 )]
125 impl From<Option<u8>> for WidgetResponse {
126 fn from(value: Option<u8>) -> Self {
127 match value {
128 Some(num) => WidgetResponse::SwitchFocus(num),
129 None => WidgetResponse::None,
130 }
131 }
132 }
133
134 if key_event.logical_key == Key::Named(NamedKey::ArrowUp) && modifiers.state().is_empty() {
135 self.up.into()
136 } else if key_event.logical_key == Key::Named(NamedKey::ArrowDown)
137 && modifiers.state().is_empty()
138 {
139 self.down.into()
140 } else if key_event.logical_key == Key::Named(NamedKey::ArrowRight)
141 && modifiers.state().is_empty()
142 {
143 self.right.into()
144 } else if key_event.logical_key == Key::Named(NamedKey::ArrowLeft)
145 && modifiers.state().is_empty()
146 {
147 self.left.into()
148 } else if key_event.logical_key == Key::Named(NamedKey::Tab) {
149 if modifiers.state() == ModifiersState::SHIFT {
150 self.shift_tab.into()
151 } else {
152 self.tab.into()
153 }
154 } else {
155 WidgetResponse::None
156 }
157 }
158}