old school music tracker
at dev 464 lines 16 kB view raw
1use winit::keyboard::{Key, NamedKey}; 2 3use crate::{ 4 EventQueue, GlobalEvent, PlaybackType, 5 coordinates::{CharPosition, CharRect, FONT_SIZE16, PixelRect}, 6 draw_buffer::DrawBuffer, 7 pages::PagesEnum, 8}; 9 10use super::{Dialog, DialogResponse}; 11 12enum Action { 13 Menu(Menu), 14 Page(PagesEnum), 15 Playback(PlaybackType), 16 // const data. Allows Recursing this inside the GlobalEvent 17 Event(fn() -> GlobalEvent), 18 // TODO: should be removed when it's all implemented 19 NotYetImplemented, 20} 21 22// Main missing, because it cant be opened from a menu 23#[derive(Clone, Copy)] 24enum Menu { 25 File, 26 Playback, 27 Sample, 28 Instrument, 29 Settings, 30} 31 32pub struct PageMenu { 33 name: &'static str, 34 rect: CharRect, 35 selected: u8, 36 pressed: bool, 37 buttons: &'static [(&'static str, Action)], 38 #[cfg(feature = "accesskit")] 39 node_id: accesskit::NodeId, 40} 41 42impl PageMenu { 43 const BACKGROUND_COLOR: u8 = 2; 44 const TOPLEFT_COLOR: u8 = 3; 45 const BOTRIGHT_COLOR: u8 = 1; 46 const fn new( 47 name: &'static str, 48 pos: CharPosition, 49 width: u8, 50 buttons: &'static [(&'static str, Action)], 51 #[cfg(feature = "accesskit")] node_id: accesskit::NodeId, 52 ) -> Self { 53 let rect = CharRect::new( 54 pos.y(), 55 // TODO: const trait 56 pos.y() + 5 + (3 * buttons.len() as u8), 57 pos.x(), 58 pos.x() + width + 3, 59 ); 60 61 Self { 62 name, 63 rect, 64 selected: 0, 65 pressed: false, 66 buttons, 67 #[cfg(feature = "accesskit")] 68 node_id, 69 } 70 } 71 fn draw_button_corners(rect: CharRect, draw_buffer: &mut DrawBuffer) { 72 // let framebuffer = &mut draw_buffer.framebuffer; 73 let pixel_rect = PixelRect::from(rect); 74 // draw top right corner 75 for y in 0..FONT_SIZE16 { 76 for x in y..FONT_SIZE16 { 77 draw_buffer.set( 78 pixel_rect.top() + y, 79 pixel_rect.right() - FONT_SIZE16 + x + 1, 80 Self::BOTRIGHT_COLOR, 81 ); 82 } 83 } 84 // draw botleft corner 85 for y in 0..FONT_SIZE16 { 86 for x in 0..(FONT_SIZE16 - y) { 87 draw_buffer.set( 88 pixel_rect.bot() - y, 89 pixel_rect.left() + x, 90 Self::BOTRIGHT_COLOR, 91 ); 92 } 93 } 94 } 95 96 fn draw(&self, has_child: bool, draw_buffer: &mut DrawBuffer) { 97 let top_left = self.rect.top_left(); 98 let top = top_left.y(); 99 let left = top_left.x(); 100 101 draw_buffer.draw_rect(Self::BACKGROUND_COLOR, self.rect); 102 draw_buffer.draw_string(self.name, top_left + CharPosition::new(7, 2), 3, 2); 103 // self.draw_outer_box(draw_buffer); 104 draw_buffer.draw_out_border(self.rect, Self::TOPLEFT_COLOR, Self::TOPLEFT_COLOR, 2); 105 draw_buffer.draw_out_border( 106 CharRect::new( 107 self.rect.top() + 1, 108 self.rect.bot() - 1, 109 self.rect.left() + 1, 110 self.rect.right() - 1, 111 ), 112 Self::BOTRIGHT_COLOR, 113 Self::BOTRIGHT_COLOR, 114 1, 115 ); 116 for (num, (name, _)) in self.buttons.iter().enumerate() { 117 let num = u8::try_from(num).unwrap(); 118 let text_color = match self.selected == num { 119 true => 11, 120 false => 0, 121 }; 122 123 draw_buffer.draw_string( 124 name, 125 top_left + CharPosition::new(4, (3 * num) + 5), 126 text_color, 127 Self::BACKGROUND_COLOR, 128 ); 129 let top = top + (3 * num) + 4; 130 let (top_left, bot_right) = match (self.pressed || has_child) && self.selected == num { 131 true => (Self::BOTRIGHT_COLOR, Self::TOPLEFT_COLOR), 132 false => (Self::TOPLEFT_COLOR, Self::BOTRIGHT_COLOR), 133 }; 134 let rect = CharRect::new(top, top + 2, left + 2, left + self.rect.width() - 2); 135 draw_buffer.draw_out_border(rect, top_left, bot_right, 1); 136 Self::draw_button_corners(rect, draw_buffer); 137 } 138 } 139 140 fn process_input( 141 &mut self, 142 key_event: &winit::event::KeyEvent, 143 events: &mut EventQueue<'_>, 144 ) -> (DialogResponse, Option<Menu>) { 145 if key_event.logical_key == Key::Named(NamedKey::Enter) { 146 if key_event.state.is_pressed() { 147 self.pressed = true; 148 return (DialogResponse::RequestRedraw, None); 149 } else if self.pressed { 150 self.pressed = false; 151 match &self.buttons[usize::from(self.selected)].1 { 152 Action::Menu(menu) => { 153 return (DialogResponse::RequestRedraw, Some(*menu)); 154 } 155 Action::Page(page) => { 156 events.push(GlobalEvent::GoToPage(*page)); 157 return (DialogResponse::Close, None); 158 } 159 Action::NotYetImplemented => { 160 eprintln!("Not yet implementes"); 161 return (DialogResponse::RequestRedraw, None); 162 } 163 Action::Event(global_event) => { 164 events.push(global_event()); 165 return (DialogResponse::Close, None); 166 } 167 Action::Playback(p) => { 168 events.push(GlobalEvent::Playback(*p)); 169 return (DialogResponse::Close, None); 170 } 171 } 172 } 173 } 174 175 if key_event.state.is_pressed() { 176 if key_event.logical_key == Key::Named(NamedKey::ArrowUp) && self.selected > 0 { 177 self.selected -= 1; 178 self.pressed = false; 179 return (DialogResponse::RequestRedraw, None); 180 } else if key_event.logical_key == Key::Named(NamedKey::ArrowDown) 181 && usize::from(self.selected) < self.buttons.len() - 1 182 { 183 self.selected += 1; 184 self.pressed = false; 185 return (DialogResponse::RequestRedraw, None); 186 } 187 } 188 189 (DialogResponse::None, None) 190 } 191 192 #[cfg(feature = "accesskit")] 193 fn build_tree( 194 &self, 195 tree: &mut Vec<(accesskit::NodeId, accesskit::Node)>, 196 ) -> crate::AccessResponse { 197 use accesskit::{Action, Node, NodeId, Rect, Role}; 198 199 use crate::AccessResponse; 200 let mut root = Node::new(Role::Dialog); 201 root.set_label(self.name); 202 root.set_bounds(dbg!(Rect::from(self.rect))); 203 let selected = NodeId(u64::from(self.selected) + self.node_id.0 + 1); 204 205 // root of the sub_menu. Will be set as the child of the selected button 206 // let sub_menu = self.sub_menu.as_ref().map(|m| { 207 // let resp = m.build_tree(tree); 208 209 // selected = resp.selected; 210 211 // resp.root 212 // }); 213 214 for (num, (name, _)) in self.buttons.iter().enumerate() { 215 let mut node = Node::new(Role::Button); 216 node.add_action(Action::Click); 217 node.set_keyboard_shortcut("Enter"); 218 node.set_label(*name); 219 if usize::from(self.selected) == num { 220 node.set_selected(true); 221 // if let Some(id) = sub_menu { 222 // node.push_child(id); 223 // } 224 } 225 let id = NodeId(u64::try_from(num).unwrap() + self.node_id.0 + 1); 226 tree.push((id, node)); 227 root.push_child(id); 228 } 229 230 tree.push((self.node_id, root)); 231 AccessResponse { 232 root: self.node_id, 233 selected, 234 } 235 } 236} 237 238pub struct MainMenu { 239 main: PageMenu, 240 child: Option<PageMenu>, 241} 242 243impl Dialog for MainMenu { 244 fn draw(&self, draw_buffer: &mut DrawBuffer) { 245 self.main.draw(self.child.is_some(), draw_buffer); 246 if let Some(child) = &self.child { 247 // the child never has a child 248 child.draw(false, draw_buffer); 249 } 250 } 251 252 fn process_input( 253 &mut self, 254 key_event: &winit::event::KeyEvent, 255 _modifiers: &winit::event::Modifiers, 256 events: &mut EventQueue<'_>, 257 ) -> DialogResponse { 258 // check for close dialog 259 if key_event.state.is_pressed() && key_event.logical_key == Key::Named(NamedKey::Escape) { 260 if self.child.is_some() { 261 self.child = None; 262 events.push(GlobalEvent::ConstRedraw); 263 return DialogResponse::RequestRedraw; 264 } else { 265 return DialogResponse::Close; 266 } 267 } 268 269 if let Some(child) = &mut self.child { 270 let (resp, open_child) = child.process_input(key_event, events); 271 assert!(open_child.is_none(), "the child can't open a child"); 272 resp 273 } else { 274 let (resp, open_child) = self.main.process_input(key_event, events); 275 if let Some(open_child) = open_child { 276 assert!( 277 resp == DialogResponse::RequestRedraw, 278 "child open is always in combination with redraw" 279 ); 280 let child = match open_child { 281 Menu::File => PageMenu::file(), 282 Menu::Playback => PageMenu::playback(), 283 Menu::Sample => PageMenu::sample(), 284 Menu::Instrument => PageMenu::instrument(), 285 Menu::Settings => PageMenu::settings(), 286 }; 287 self.child = Some(child); 288 } 289 resp 290 } 291 } 292 293 #[cfg(feature = "accesskit")] 294 fn build_tree( 295 &self, 296 _tree: &mut Vec<(accesskit::NodeId, accesskit::Node)>, 297 ) -> crate::AccessResponse { 298 todo!("look at PageMenu commented out stuff"); 299 } 300} 301 302impl MainMenu { 303 pub fn new() -> Self { 304 Self { 305 main: PageMenu::main(), 306 child: None, 307 } 308 } 309} 310 311impl PageMenu { 312 pub const fn main() -> Self { 313 Self::new( 314 "Main Menu", 315 CharPosition::new(6, 11), 316 29, 317 &[ 318 ("File Menu...", Action::Menu(Menu::File)), 319 ("Playback Menu...", Action::Menu(Menu::Playback)), 320 ( 321 "View Patterns (F2)", 322 Action::Page(PagesEnum::Pattern), 323 ), 324 ("Sample Menu...", Action::Menu(Menu::Sample)), 325 ("Instrument Menu...", Action::Menu(Menu::Instrument)), 326 ( 327 "View Orders/Panning (F11)", 328 Action::Page(PagesEnum::OrderList), 329 ), 330 ( 331 "View Variables (F12)", 332 Action::Page(PagesEnum::SongDirectoryConfig), 333 ), 334 ("Message Editor (Shift-F9)", Action::NotYetImplemented), 335 ("Settings Menu...", Action::Menu(Menu::Settings)), 336 ("Help! (F1)", Action::Page(PagesEnum::Help)), 337 ], 338 #[cfg(feature = "accesskit")] 339 accesskit::NodeId(1_000_000_000), 340 ) 341 } 342 343 pub const fn file() -> Self { 344 Self::new( 345 "File Menu", 346 CharPosition::new(25, 13), 347 26, 348 &[ 349 ("Load... (F9)", Action::NotYetImplemented), 350 ("New... (Ctrl-N)", Action::NotYetImplemented), 351 ("Save Current (Ctrl-S)", Action::NotYetImplemented), 352 ("Save As... (F10)", Action::NotYetImplemented), 353 ("Export... (Shift-F10)", Action::NotYetImplemented), 354 ("Message Log (Ctrl-F11)", Action::NotYetImplemented), 355 ( 356 "Quit (Ctrl-Q)", 357 Action::Event(|| GlobalEvent::CloseRequested), 358 ), 359 ], 360 #[cfg(feature = "accesskit")] 361 accesskit::NodeId(1_000_000_100), 362 ) 363 } 364 365 pub const fn playback() -> Self { 366 Self::new( 367 "Playback Menu", 368 CharPosition::new(25, 13), 369 31, 370 &[ 371 ("Show Infopage (F5)", Action::NotYetImplemented), 372 ( 373 "Play Song (Ctrl-F5)", 374 Action::Playback(PlaybackType::Song), 375 ), 376 ( 377 "Play Pattern (F6)", 378 Action::Playback(PlaybackType::Pattern), 379 ), 380 ( 381 "Play from Order (Shift-F6)", 382 Action::Playback(PlaybackType::FromOrder), 383 ), 384 ("Play from Mark/Cursor (F7)", Action::NotYetImplemented), 385 ( 386 "Stop (F8)", 387 Action::Playback(PlaybackType::Stop), 388 ), 389 ("Reinit Soundcard (Ctrl-I)", Action::NotYetImplemented), 390 ("Driver Screen (Shift-F5)", Action::NotYetImplemented), 391 ("Calculate Length (Ctrl-P)", Action::NotYetImplemented), 392 ], 393 #[cfg(feature = "accesskit")] 394 accesskit::NodeId(1_000_000_200), 395 ) 396 } 397 398 pub const fn sample() -> Self { 399 Self::new( 400 "Sample Menu", 401 CharPosition::new(25, 20), 402 29, 403 &[ 404 ( 405 "Sample List (F3)", 406 Action::Page(PagesEnum::SampleList), 407 ), 408 ("Sample Library (Ctrl-F3)", Action::NotYetImplemented), 409 ], 410 #[cfg(feature = "accesskit")] 411 accesskit::NodeId(1_000_000_300), 412 ) 413 } 414 415 pub const fn instrument() -> Self { 416 Self::new( 417 "Instrument Menu", 418 CharPosition::new(20, 23), 419 33, 420 &[ 421 ("Instrument List (F4)", Action::NotYetImplemented), 422 ("Instrument Library (Ctrl-F4)", Action::NotYetImplemented), 423 ], 424 #[cfg(feature = "accesskit")] 425 accesskit::NodeId(1_000_000_400), 426 ) 427 } 428 429 pub const fn settings() -> Self { 430 Self::new( 431 "Settings Menu", 432 CharPosition::new(22, 25), 433 38, 434 &[ 435 ( 436 "Preferences (Shift-F5)", 437 Action::NotYetImplemented, 438 ), 439 ( 440 "MIDI Configuration (Shift-F1)", 441 Action::NotYetImplemented, 442 ), 443 ( 444 "System Configuration (Ctrl-F1)", 445 Action::NotYetImplemented, 446 ), 447 ( 448 "Palette Editor (Ctrl-F12)", 449 Action::NotYetImplemented, 450 ), 451 ( 452 "Font Editor (Shift-F12)", 453 Action::NotYetImplemented, 454 ), 455 ( 456 "Toggle Fullscreen (Ctrl-Alt-Enter)", 457 Action::NotYetImplemented, 458 ), 459 ], 460 #[cfg(feature = "accesskit")] 461 accesskit::NodeId(1_000_000_500), 462 ) 463 } 464}