old school music tracker
1pub mod button;
2pub mod slider;
3pub mod text_in;
4pub 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::{app::EventQueue, draw_buffer::DrawBuffer};
14
15pub(crate) trait Widget {
16 type Response;
17 fn draw(&self, draw_buffer: &mut DrawBuffer, selected: bool);
18 /// returns a Some(usize) if the next widget gets selected
19 fn process_input(
20 &mut self,
21 modifiers: &Modifiers,
22 key_event: &KeyEvent,
23 events: &mut EventQueue<'_>,
24 ) -> WidgetResponse<Self::Response>;
25}
26
27#[derive(Debug)]
28pub struct WidgetResponse<R> {
29 pub standard: StandardResponse,
30 pub extra: Option<R>,
31}
32
33impl<R> Default for WidgetResponse<R> {
34 fn default() -> Self {
35 Self {
36 standard: StandardResponse::default(),
37 extra: None,
38 }
39 }
40}
41
42impl<R> WidgetResponse<R> {
43 pub fn request_redraw() -> Self {
44 Self {
45 standard: StandardResponse::RequestRedraw,
46 extra: None,
47 }
48 }
49
50 pub fn next_widget(value: usize) -> Self {
51 Self {
52 standard: StandardResponse::SwitchFocus(value),
53 extra: None,
54 }
55 }
56}
57
58// SwitchFocus also has to request a redraw
59#[derive(Debug, Default)]
60pub enum StandardResponse {
61 SwitchFocus(usize),
62 RequestRedraw,
63 // GlobalEvent(GlobalEvent),
64 #[default]
65 None,
66}
67
68impl<R> From<StandardResponse> for WidgetResponse<R> {
69 fn from(value: StandardResponse) -> Self {
70 Self {
71 standard: value,
72 extra: None,
73 }
74 }
75}
76
77#[derive(Debug, Default)]
78pub struct NextWidget {
79 pub left: Option<usize>,
80 pub right: Option<usize>,
81 pub up: Option<usize>,
82 pub down: Option<usize>,
83 pub tab: Option<usize>,
84 pub shift_tab: Option<usize>,
85}
86
87impl NextWidget {
88 /// supposed to be called from Widgets, that own a NextWidget after catching their custom KeyEvents to pick a return
89 pub fn process_key_event<R>(
90 &self,
91 key_event: &KeyEvent,
92 modifiers: &Modifiers,
93 ) -> WidgetResponse<R> {
94 if !key_event.state.is_pressed() {
95 return WidgetResponse::default();
96 }
97
98 #[expect(
99 non_local_definitions,
100 reason = "this is only valid with these specific Option<usize> not in general"
101 )]
102 impl<R> From<Option<usize>> for WidgetResponse<R> {
103 fn from(value: Option<usize>) -> Self {
104 Self {
105 standard: match value {
106 Some(num) => StandardResponse::SwitchFocus(num),
107 None => StandardResponse::None,
108 },
109 extra: None,
110 }
111 }
112 }
113
114 if key_event.logical_key == Key::Named(NamedKey::ArrowUp) && modifiers.state().is_empty() {
115 self.up.into()
116 } else if key_event.logical_key == Key::Named(NamedKey::ArrowDown)
117 && modifiers.state().is_empty()
118 {
119 self.down.into()
120 } else if key_event.logical_key == Key::Named(NamedKey::ArrowRight)
121 && modifiers.state().is_empty()
122 {
123 self.right.into()
124 } else if key_event.logical_key == Key::Named(NamedKey::ArrowLeft)
125 && modifiers.state().is_empty()
126 {
127 self.left.into()
128 } else if key_event.logical_key == Key::Named(NamedKey::Tab) {
129 if modifiers.state() == ModifiersState::SHIFT {
130 self.shift_tab.into()
131 } else {
132 self.tab.into()
133 }
134 } else {
135 WidgetResponse::default()
136 }
137 }
138}