use winit::keyboard::{Key, NamedKey}; use crate::{ EventQueue, GlobalEvent, PlaybackType, coordinates::{CharPosition, CharRect, FONT_SIZE16, PixelRect}, draw_buffer::DrawBuffer, pages::PagesEnum, }; use super::{Dialog, DialogResponse}; enum Action { Menu(Menu), Page(PagesEnum), Playback(PlaybackType), // const data. Allows Recursing this inside the GlobalEvent Event(fn() -> GlobalEvent), // TODO: should be removed when it's all implemented NotYetImplemented, } // Main missing, because it cant be opened from a menu #[derive(Clone, Copy)] enum Menu { File, Playback, Sample, Instrument, Settings, } pub struct PageMenu { name: &'static str, rect: CharRect, selected: u8, pressed: bool, buttons: &'static [(&'static str, Action)], #[cfg(feature = "accesskit")] node_id: accesskit::NodeId, } impl PageMenu { const BACKGROUND_COLOR: u8 = 2; const TOPLEFT_COLOR: u8 = 3; const BOTRIGHT_COLOR: u8 = 1; const fn new( name: &'static str, pos: CharPosition, width: u8, buttons: &'static [(&'static str, Action)], #[cfg(feature = "accesskit")] node_id: accesskit::NodeId, ) -> Self { let rect = CharRect::new( pos.y(), // TODO: const trait pos.y() + 5 + (3 * buttons.len() as u8), pos.x(), pos.x() + width + 3, ); Self { name, rect, selected: 0, pressed: false, buttons, #[cfg(feature = "accesskit")] node_id, } } fn draw_button_corners(rect: CharRect, draw_buffer: &mut DrawBuffer) { // let framebuffer = &mut draw_buffer.framebuffer; let pixel_rect = PixelRect::from(rect); // draw top right corner for y in 0..FONT_SIZE16 { for x in y..FONT_SIZE16 { draw_buffer.set( pixel_rect.top() + y, pixel_rect.right() - FONT_SIZE16 + x + 1, Self::BOTRIGHT_COLOR, ); } } // draw botleft corner for y in 0..FONT_SIZE16 { for x in 0..(FONT_SIZE16 - y) { draw_buffer.set( pixel_rect.bot() - y, pixel_rect.left() + x, Self::BOTRIGHT_COLOR, ); } } } fn draw(&self, has_child: bool, draw_buffer: &mut DrawBuffer) { let top_left = self.rect.top_left(); let top = top_left.y(); let left = top_left.x(); draw_buffer.draw_rect(Self::BACKGROUND_COLOR, self.rect); draw_buffer.draw_string(self.name, top_left + CharPosition::new(7, 2), 3, 2); // self.draw_outer_box(draw_buffer); draw_buffer.draw_out_border(self.rect, Self::TOPLEFT_COLOR, Self::TOPLEFT_COLOR, 2); draw_buffer.draw_out_border( CharRect::new( self.rect.top() + 1, self.rect.bot() - 1, self.rect.left() + 1, self.rect.right() - 1, ), Self::BOTRIGHT_COLOR, Self::BOTRIGHT_COLOR, 1, ); for (num, (name, _)) in self.buttons.iter().enumerate() { let num = u8::try_from(num).unwrap(); let text_color = match self.selected == num { true => 11, false => 0, }; draw_buffer.draw_string( name, top_left + CharPosition::new(4, (3 * num) + 5), text_color, Self::BACKGROUND_COLOR, ); let top = top + (3 * num) + 4; let (top_left, bot_right) = match (self.pressed || has_child) && self.selected == num { true => (Self::BOTRIGHT_COLOR, Self::TOPLEFT_COLOR), false => (Self::TOPLEFT_COLOR, Self::BOTRIGHT_COLOR), }; let rect = CharRect::new(top, top + 2, left + 2, left + self.rect.width() - 2); draw_buffer.draw_out_border(rect, top_left, bot_right, 1); Self::draw_button_corners(rect, draw_buffer); } } fn process_input( &mut self, key_event: &winit::event::KeyEvent, events: &mut EventQueue<'_>, ) -> (DialogResponse, Option) { if key_event.logical_key == Key::Named(NamedKey::Enter) { if key_event.state.is_pressed() { self.pressed = true; return (DialogResponse::RequestRedraw, None); } else if self.pressed { self.pressed = false; match &self.buttons[usize::from(self.selected)].1 { Action::Menu(menu) => { return (DialogResponse::RequestRedraw, Some(*menu)); } Action::Page(page) => { events.push(GlobalEvent::GoToPage(*page)); return (DialogResponse::Close, None); } Action::NotYetImplemented => { eprintln!("Not yet implementes"); return (DialogResponse::RequestRedraw, None); } Action::Event(global_event) => { events.push(global_event()); return (DialogResponse::Close, None); } Action::Playback(p) => { events.push(GlobalEvent::Playback(*p)); return (DialogResponse::Close, None); } } } } if key_event.state.is_pressed() { if key_event.logical_key == Key::Named(NamedKey::ArrowUp) && self.selected > 0 { self.selected -= 1; self.pressed = false; return (DialogResponse::RequestRedraw, None); } else if key_event.logical_key == Key::Named(NamedKey::ArrowDown) && usize::from(self.selected) < self.buttons.len() - 1 { self.selected += 1; self.pressed = false; return (DialogResponse::RequestRedraw, None); } } (DialogResponse::None, None) } #[cfg(feature = "accesskit")] fn build_tree( &self, tree: &mut Vec<(accesskit::NodeId, accesskit::Node)>, ) -> crate::AccessResponse { use accesskit::{Action, Node, NodeId, Rect, Role}; use crate::AccessResponse; let mut root = Node::new(Role::Dialog); root.set_label(self.name); root.set_bounds(dbg!(Rect::from(self.rect))); let selected = NodeId(u64::from(self.selected) + self.node_id.0 + 1); // root of the sub_menu. Will be set as the child of the selected button // let sub_menu = self.sub_menu.as_ref().map(|m| { // let resp = m.build_tree(tree); // selected = resp.selected; // resp.root // }); for (num, (name, _)) in self.buttons.iter().enumerate() { let mut node = Node::new(Role::Button); node.add_action(Action::Click); node.set_keyboard_shortcut("Enter"); node.set_label(*name); if usize::from(self.selected) == num { node.set_selected(true); // if let Some(id) = sub_menu { // node.push_child(id); // } } let id = NodeId(u64::try_from(num).unwrap() + self.node_id.0 + 1); tree.push((id, node)); root.push_child(id); } tree.push((self.node_id, root)); AccessResponse { root: self.node_id, selected, } } } pub struct MainMenu { main: PageMenu, child: Option, } impl Dialog for MainMenu { fn draw(&self, draw_buffer: &mut DrawBuffer) { self.main.draw(self.child.is_some(), draw_buffer); if let Some(child) = &self.child { // the child never has a child child.draw(false, draw_buffer); } } fn process_input( &mut self, key_event: &winit::event::KeyEvent, _modifiers: &winit::event::Modifiers, events: &mut EventQueue<'_>, ) -> DialogResponse { // check for close dialog if key_event.state.is_pressed() && key_event.logical_key == Key::Named(NamedKey::Escape) { if self.child.is_some() { self.child = None; events.push(GlobalEvent::ConstRedraw); return DialogResponse::RequestRedraw; } else { return DialogResponse::Close; } } if let Some(child) = &mut self.child { let (resp, open_child) = child.process_input(key_event, events); assert!(open_child.is_none(), "the child can't open a child"); resp } else { let (resp, open_child) = self.main.process_input(key_event, events); if let Some(open_child) = open_child { assert!( resp == DialogResponse::RequestRedraw, "child open is always in combination with redraw" ); let child = match open_child { Menu::File => PageMenu::file(), Menu::Playback => PageMenu::playback(), Menu::Sample => PageMenu::sample(), Menu::Instrument => PageMenu::instrument(), Menu::Settings => PageMenu::settings(), }; self.child = Some(child); } resp } } #[cfg(feature = "accesskit")] fn build_tree( &self, _tree: &mut Vec<(accesskit::NodeId, accesskit::Node)>, ) -> crate::AccessResponse { todo!("look at PageMenu commented out stuff"); } } impl MainMenu { pub fn new() -> Self { Self { main: PageMenu::main(), child: None, } } } impl PageMenu { pub const fn main() -> Self { Self::new( "Main Menu", CharPosition::new(6, 11), 29, &[ ("File Menu...", Action::Menu(Menu::File)), ("Playback Menu...", Action::Menu(Menu::Playback)), ( "View Patterns (F2)", Action::Page(PagesEnum::Pattern), ), ("Sample Menu...", Action::Menu(Menu::Sample)), ("Instrument Menu...", Action::Menu(Menu::Instrument)), ( "View Orders/Panning (F11)", Action::Page(PagesEnum::OrderList), ), ( "View Variables (F12)", Action::Page(PagesEnum::SongDirectoryConfig), ), ("Message Editor (Shift-F9)", Action::NotYetImplemented), ("Settings Menu...", Action::Menu(Menu::Settings)), ("Help! (F1)", Action::Page(PagesEnum::Help)), ], #[cfg(feature = "accesskit")] accesskit::NodeId(1_000_000_000), ) } pub const fn file() -> Self { Self::new( "File Menu", CharPosition::new(25, 13), 26, &[ ("Load... (F9)", Action::NotYetImplemented), ("New... (Ctrl-N)", Action::NotYetImplemented), ("Save Current (Ctrl-S)", Action::NotYetImplemented), ("Save As... (F10)", Action::NotYetImplemented), ("Export... (Shift-F10)", Action::NotYetImplemented), ("Message Log (Ctrl-F11)", Action::NotYetImplemented), ( "Quit (Ctrl-Q)", Action::Event(|| GlobalEvent::CloseRequested), ), ], #[cfg(feature = "accesskit")] accesskit::NodeId(1_000_000_100), ) } pub const fn playback() -> Self { Self::new( "Playback Menu", CharPosition::new(25, 13), 31, &[ ("Show Infopage (F5)", Action::NotYetImplemented), ( "Play Song (Ctrl-F5)", Action::Playback(PlaybackType::Song), ), ( "Play Pattern (F6)", Action::Playback(PlaybackType::Pattern), ), ( "Play from Order (Shift-F6)", Action::Playback(PlaybackType::FromOrder), ), ("Play from Mark/Cursor (F7)", Action::NotYetImplemented), ( "Stop (F8)", Action::Playback(PlaybackType::Stop), ), ("Reinit Soundcard (Ctrl-I)", Action::NotYetImplemented), ("Driver Screen (Shift-F5)", Action::NotYetImplemented), ("Calculate Length (Ctrl-P)", Action::NotYetImplemented), ], #[cfg(feature = "accesskit")] accesskit::NodeId(1_000_000_200), ) } pub const fn sample() -> Self { Self::new( "Sample Menu", CharPosition::new(25, 20), 29, &[ ( "Sample List (F3)", Action::Page(PagesEnum::SampleList), ), ("Sample Library (Ctrl-F3)", Action::NotYetImplemented), ], #[cfg(feature = "accesskit")] accesskit::NodeId(1_000_000_300), ) } pub const fn instrument() -> Self { Self::new( "Instrument Menu", CharPosition::new(20, 23), 33, &[ ("Instrument List (F4)", Action::NotYetImplemented), ("Instrument Library (Ctrl-F4)", Action::NotYetImplemented), ], #[cfg(feature = "accesskit")] accesskit::NodeId(1_000_000_400), ) } pub const fn settings() -> Self { Self::new( "Settings Menu", CharPosition::new(22, 25), 38, &[ ( "Preferences (Shift-F5)", Action::NotYetImplemented, ), ( "MIDI Configuration (Shift-F1)", Action::NotYetImplemented, ), ( "System Configuration (Ctrl-F1)", Action::NotYetImplemented, ), ( "Palette Editor (Ctrl-F12)", Action::NotYetImplemented, ), ( "Font Editor (Shift-F12)", Action::NotYetImplemented, ), ( "Toggle Fullscreen (Ctrl-Alt-Enter)", Action::NotYetImplemented, ), ], #[cfg(feature = "accesskit")] accesskit::NodeId(1_000_000_500), ) } }