old school music tracker
at dev 253 lines 8.5 kB view raw
1mod help_page; 2pub mod order_list; 3pub mod pattern; 4mod sample_list; 5mod song_directory_config_page; 6 7use help_page::HelpPage; 8use order_list::{OrderListPage, OrderListPageEvent}; 9use pattern::{PatternPage, PatternPageEvent}; 10use sample_list::SampleList; 11use song_directory_config_page::{SDCChange, SongDirectoryConfigPage}; 12use winit::{ 13 event::{KeyEvent, Modifiers}, 14 event_loop::EventLoopProxy, 15 keyboard::{Key, NamedKey}, 16}; 17 18use crate::{ 19 EventQueue, GlobalEvent, 20 coordinates::{CharPosition, CharRect, WINDOW_SIZE_CHARS}, 21 draw_buffer::DrawBuffer, 22 pages::sample_list::SampleListEvent, 23}; 24 25pub trait Page { 26 fn draw(&mut self, draw_buffer: &mut DrawBuffer); 27 fn draw_constant(&mut self, draw_buffer: &mut DrawBuffer); 28 29 fn process_key_event( 30 &mut self, 31 modifiers: &Modifiers, 32 key_event: &KeyEvent, 33 // please give me reborrowing for custom structs rustc :3 34 events: &mut EventQueue<'_>, 35 ) -> PageResponse; 36 37 #[cfg(feature = "accesskit")] 38 fn build_tree( 39 &self, 40 tree: &mut Vec<(accesskit::NodeId, accesskit::Node)>, 41 ) -> crate::AccessResponse; 42} 43 44pub enum PageResponse { 45 RequestRedraw, 46 None, 47} 48 49#[derive(Clone, Copy, PartialEq, Eq, Debug)] 50pub enum PagesEnum { 51 Help, 52 SongDirectoryConfig, 53 Pattern, 54 OrderList, 55 SampleList, 56} 57 58#[derive(Debug, Clone)] 59pub enum PageEvent { 60 Sdc(SDCChange), 61 Pattern(PatternPageEvent), 62 OrderList(OrderListPageEvent), 63 SampleList(SampleListEvent), 64} 65 66impl PageEvent { 67 fn get_page(&self) -> PagesEnum { 68 match self { 69 PageEvent::Sdc(_) => PagesEnum::SongDirectoryConfig, 70 PageEvent::Pattern(_) => PagesEnum::Pattern, 71 PageEvent::OrderList(_) => PagesEnum::OrderList, 72 PageEvent::SampleList(_) => PagesEnum::SampleList, 73 } 74 } 75} 76 77pub struct AllPages { 78 help: HelpPage, 79 song_directory_config: SongDirectoryConfigPage, 80 pattern: PatternPage, 81 order_list: OrderListPage, 82 sample_list: SampleList, 83 84 const_draw_needed: bool, 85 current: PagesEnum, 86} 87 88impl AllPages { 89 pub fn new(proxy: EventLoopProxy<GlobalEvent>) -> Self { 90 AllPages { 91 help: HelpPage::new(), 92 song_directory_config: SongDirectoryConfigPage::new(), 93 pattern: PatternPage::new(proxy.clone()), 94 current: PagesEnum::SongDirectoryConfig, 95 order_list: OrderListPage::new(), 96 const_draw_needed: true, 97 sample_list: SampleList::new(proxy), 98 } 99 } 100 101 fn get_title(&self) -> &'static str { 102 match self.current { 103 PagesEnum::Help => "Help", 104 PagesEnum::SongDirectoryConfig => "Song Variables & Directory Configuration (F12)", 105 PagesEnum::Pattern => "Pattern Editor (F2)", 106 PagesEnum::OrderList => match self.order_list.mode() { 107 order_list::Mode::Volume => "Order List and Channel Volume (F11)", 108 order_list::Mode::Panning => "Order List and Panning (F11)", 109 }, 110 PagesEnum::SampleList => "Sample List (F3)", 111 } 112 } 113 114 fn get_page(&self) -> &dyn Page { 115 match self.current { 116 PagesEnum::Help => &self.help, 117 PagesEnum::SongDirectoryConfig => &self.song_directory_config, 118 PagesEnum::Pattern => &self.pattern, 119 PagesEnum::OrderList => &self.order_list, 120 PagesEnum::SampleList => &self.sample_list, 121 } 122 } 123 124 fn get_page_mut(&mut self) -> &mut dyn Page { 125 match self.current { 126 PagesEnum::Help => &mut self.help, 127 PagesEnum::SongDirectoryConfig => &mut self.song_directory_config, 128 PagesEnum::Pattern => &mut self.pattern, 129 PagesEnum::OrderList => &mut self.order_list, 130 PagesEnum::SampleList => &mut self.sample_list, 131 } 132 } 133 134 /// requests a redraw if it is needed 135 pub fn switch_page(&mut self, next_page: PagesEnum) -> PageResponse { 136 if next_page != self.current { 137 // when switching to the OrderListPage, reset to the pan mode 138 if next_page == PagesEnum::OrderList { 139 self.order_list.reset_mode(); 140 } 141 self.current = next_page; 142 self.request_draw_const(); 143 PageResponse::RequestRedraw 144 } else { 145 PageResponse::None 146 } 147 } 148 149 pub fn request_draw_const(&mut self) { 150 self.const_draw_needed = true; 151 } 152 153 pub fn draw(&mut self, draw_buffer: &mut DrawBuffer) { 154 if self.const_draw_needed { 155 self.draw_constant(draw_buffer); 156 } 157 self.get_page_mut().draw(draw_buffer); 158 } 159 160 pub fn draw_constant(&mut self, draw_buffer: &mut DrawBuffer) { 161 // draw page title 162 let title = self.get_title(); 163 let title_len = u8::try_from(title.len()).expect("title doesn't fit on the page"); 164 let middle = WINDOW_SIZE_CHARS.0 / 2; 165 let str_start = middle - (title_len / 2); 166 draw_buffer.draw_string(title, CharPosition::new(str_start, 11), 0, 2); 167 const DOTTED: [u8; 8] = [0, 0, 0, 0b01010101, 0, 0, 0, 0]; 168 draw_buffer.draw_rect(2, CharRect::new(11, 11, str_start - 1, str_start - 1)); 169 draw_buffer.draw_rect( 170 2, 171 CharRect::new(11, 11, str_start + title_len, str_start + title_len), 172 ); 173 for x in 1..=(str_start - 2) { 174 draw_buffer.draw_char(DOTTED, CharPosition::new(x, 11), 1, 2); 175 } 176 for x in (str_start + title_len + 1)..=(WINDOW_SIZE_CHARS.0 - 2) { 177 draw_buffer.draw_char(DOTTED, CharPosition::new(x, 11), 1, 2); 178 } 179 // draw page const 180 self.get_page_mut().draw_constant(draw_buffer); 181 self.const_draw_needed = false; 182 } 183 184 // add key_events for changing pages here 185 pub fn process_key_event( 186 &mut self, 187 modifiers: &Modifiers, 188 key_event: &KeyEvent, 189 events: &mut EventQueue<'_>, 190 ) -> PageResponse { 191 if key_event.state.is_pressed() && modifiers.state().is_empty() { 192 if key_event.logical_key == Key::Named(NamedKey::F1) { 193 self.switch_page(PagesEnum::Help); 194 return PageResponse::RequestRedraw; 195 } else if key_event.logical_key == Key::Named(NamedKey::F2) { 196 self.switch_page(PagesEnum::Pattern); 197 return PageResponse::RequestRedraw; 198 } else if key_event.logical_key == Key::Named(NamedKey::F11) { 199 if self.current == PagesEnum::OrderList { 200 self.order_list.switch_mode(); 201 self.request_draw_const(); 202 return PageResponse::RequestRedraw; 203 } else { 204 self.switch_page(PagesEnum::OrderList); 205 return PageResponse::RequestRedraw; 206 } 207 } else if key_event.logical_key == Key::Named(NamedKey::F12) { 208 self.switch_page(PagesEnum::SongDirectoryConfig); 209 return PageResponse::RequestRedraw; 210 } else if key_event.logical_key == Key::Named(NamedKey::F3) { 211 self.switch_page(PagesEnum::SampleList); 212 return PageResponse::RequestRedraw; 213 } 214 } 215 216 self.get_page_mut() 217 .process_key_event(modifiers, key_event, events) 218 } 219 220 pub fn process_page_event( 221 &mut self, 222 event: PageEvent, 223 events: &mut EventQueue<'_>, 224 ) -> PageResponse { 225 let page = event.get_page(); 226 let response = match event { 227 PageEvent::Sdc(change) => self.song_directory_config.ui_change(change), 228 PageEvent::Pattern(event) => self.pattern.process_event(event, events), 229 PageEvent::OrderList(event) => self.order_list.process_event(event), 230 PageEvent::SampleList(event) => self.sample_list.process_event(event, events), 231 }; 232 233 // if the page isn't shown a redraw isn't necessary 234 if page == self.current { 235 response 236 } else { 237 PageResponse::None 238 } 239 } 240 241 #[cfg(feature = "accesskit")] 242 pub fn build_tree( 243 &self, 244 tree: &mut Vec<(accesskit::NodeId, accesskit::Node)>, 245 ) -> crate::AccessResponse { 246 self.get_page().build_tree(tree) 247 } 248 249 #[cfg(feature = "accesskit")] 250 pub fn process_accesskit_event(&mut self, event: accesskit::ActionRequest) -> PageResponse { 251 todo!() 252 } 253}