old school music tracker
at dev 575 lines 24 kB view raw
1use std::str::from_utf8; 2use std::{array, io::Write}; 3 4use torque_tracker_engine::project::song::{Pan, SongOperation}; 5use torque_tracker_engine::{file::impulse_format::header::PatternOrder, project::song::Song}; 6use winit::keyboard::{Key, ModifiersState, NamedKey}; 7 8use crate::header::HeaderEvent; 9use crate::widgets::{NextWidget, Widget, WidgetResponse}; 10use crate::{EventQueue, GlobalEvent, send_song_op}; 11use crate::{ 12 coordinates::{CharPosition, CharRect}, 13 widgets::slider::Slider, 14}; 15 16use super::{Page, PageEvent, PageResponse}; 17 18#[derive(Debug, Clone)] 19pub enum OrderListPageEvent { 20 SetVolumeCurrent(u8), 21 SetPanCurrent(u8), 22 SetPlayback(Option<u16>), 23} 24 25#[derive(Debug, Copy, Clone)] 26pub enum Mode { 27 Panning, 28 Volume, 29} 30 31#[derive(Debug, Clone, Copy, PartialEq, Eq)] 32enum Cursor { 33 Order, 34 VolPan(u8), 35} 36 37#[derive(Debug)] 38struct OrderCursor { 39 order: u16, 40 // 0 - 2 valid 41 digit: u8, 42} 43 44#[derive(Debug)] 45pub struct OrderListPage { 46 mode: Mode, 47 cursor: Cursor, 48 order_cursor: OrderCursor, 49 order_draw: u16, 50 order_playback: Option<u16>, 51 pattern_order: [PatternOrder; Song::MAX_ORDERS], 52 volume: [Slider<0, 64>; 64], 53 pan: [Slider<0, 64>; 64], 54} 55 56impl OrderListPage { 57 const PAGE_ID: u64 = 11_000_000_000; 58 59 pub fn new() -> Self { 60 Self { 61 cursor: Cursor::Order, 62 mode: Mode::Panning, 63 order_cursor: OrderCursor { order: 0, digit: 0 }, 64 order_draw: 0, 65 pattern_order: [PatternOrder::EndOfSong; Song::MAX_ORDERS], 66 order_playback: None, 67 volume: array::from_fn(|idx| { 68 let idx = u8::try_from(idx).unwrap(); 69 let pos = if idx >= 32 { 70 CharPosition::new(65, 15 + idx - 32) 71 } else { 72 CharPosition::new(31, 15 + idx) 73 }; 74 Slider::new( 75 64, 76 pos, 77 8, 78 NextWidget::default(), 79 |value| { 80 GlobalEvent::Page(PageEvent::OrderList( 81 OrderListPageEvent::SetVolumeCurrent(value.try_into().unwrap()), 82 )) 83 }, 84 #[cfg(feature = "accesskit")] 85 ( 86 accesskit::NodeId(Self::PAGE_ID + u64::from(idx)), 87 format!("Volume Channel {idx}").into_boxed_str(), 88 ), 89 ) 90 }), 91 pan: array::from_fn(|idx| { 92 let idx = u8::try_from(idx).unwrap(); 93 let pos = if idx >= 32 { 94 CharPosition::new(65, 15 + idx - 32) 95 } else { 96 CharPosition::new(31, 15 + idx) 97 }; 98 Slider::new( 99 32, 100 pos, 101 8, 102 NextWidget::default(), 103 |value| { 104 GlobalEvent::Page(PageEvent::OrderList(OrderListPageEvent::SetPanCurrent( 105 u8::try_from(value).unwrap(), 106 ))) 107 }, 108 #[cfg(feature = "accesskit")] 109 ( 110 accesskit::NodeId(Self::PAGE_ID + u64::from(idx)), 111 format!("Pan Channel {idx}").into_boxed_str(), 112 ), 113 ) 114 }), 115 } 116 } 117 118 pub fn process_event(&mut self, event: OrderListPageEvent) -> PageResponse { 119 match event { 120 OrderListPageEvent::SetVolumeCurrent(vol) => { 121 let cursor = match self.cursor { 122 Cursor::Order => unreachable!( 123 "when a set volume event is created a volume slider has to be selected" 124 ), 125 Cursor::VolPan(c) => c, 126 }; 127 self.volume[usize::from(cursor - 1)] 128 .try_set(i16::from(vol)) 129 .expect("the value was created from the slider, so it has to fit."); 130 send_song_op(SongOperation::SetVolume(cursor - 1, vol)); 131 } 132 OrderListPageEvent::SetPanCurrent(pan) => { 133 let cursor = match self.cursor { 134 Cursor::Order => unreachable!( 135 "when a set pan event is created a pan slider has to be selected" 136 ), 137 Cursor::VolPan(c) => c, 138 }; 139 self.pan[usize::from(cursor - 1)] 140 .try_set(i16::from(pan)) 141 .expect("the event was created from the slider, so has to fit."); 142 send_song_op(SongOperation::SetPan(cursor - 1, Pan::Value(pan))); 143 } 144 OrderListPageEvent::SetPlayback(o) => self.order_playback = o, 145 }; 146 PageResponse::RequestRedraw 147 } 148 149 pub fn switch_mode(&mut self) { 150 self.mode = match self.mode { 151 Mode::Panning => Mode::Volume, 152 Mode::Volume => Mode::Panning, 153 } 154 } 155 156 pub fn reset_mode(&mut self) { 157 self.mode = Mode::Panning 158 } 159 160 pub fn mode(&self) -> Mode { 161 self.mode 162 } 163 164 fn send_order_position(&self, events: &mut EventQueue<'_>) { 165 events.push(GlobalEvent::Header(HeaderEvent::SetOrder( 166 self.order_cursor.order, 167 ))); 168 } 169 170 fn send_order_len(&self, events: &mut EventQueue<'_>) { 171 let order_len = self 172 .pattern_order 173 .iter() 174 .take_while(|o| **o != PatternOrder::EndOfSong) 175 .count(); 176 events.push(GlobalEvent::Header(HeaderEvent::SetOrderLen( 177 u16::try_from(order_len).unwrap(), 178 ))); 179 } 180 181 fn order_cursor_up(&mut self, count: u16, events: &mut EventQueue<'_>) -> PageResponse { 182 debug_assert!(count != 0, "why would you do this"); 183 if self.order_cursor.order == 0 { 184 return PageResponse::None; 185 } 186 187 self.order_cursor.order = self.order_cursor.order.saturating_sub(count); 188 self.order_draw = self.order_draw.min(self.order_cursor.order); 189 self.send_order_position(events); 190 PageResponse::RequestRedraw 191 } 192 193 fn order_cursor_down(&mut self, count: u16, events: &mut EventQueue<'_>) -> PageResponse { 194 debug_assert!(count != 0, "why would you do this"); 195 if self.order_cursor.order == 255 { 196 return PageResponse::None; 197 } 198 199 self.order_cursor.order = self.order_cursor.order.saturating_add(count); 200 self.order_draw = self 201 .order_draw 202 .max(self.order_cursor.order.saturating_sub(31)); 203 self.send_order_position(events); 204 PageResponse::RequestRedraw 205 } 206} 207 208impl Page for OrderListPage { 209 fn draw(&mut self, draw_buffer: &mut crate::draw_buffer::DrawBuffer) { 210 fn write_pattern_order(order: PatternOrder, buf: &mut [u8; 3]) -> &str { 211 let mut curse: std::io::Cursor<&mut [u8]> = std::io::Cursor::new(buf); 212 match order { 213 PatternOrder::Number(n) => write!(&mut curse, "{:03}", n).unwrap(), 214 PatternOrder::EndOfSong => write!(&mut curse, "---").unwrap(), 215 PatternOrder::SkipOrder => write!(&mut curse, "+++").unwrap(), 216 } 217 from_utf8(buf).unwrap() 218 } 219 220 // draw heading for vol pan columns 221 match self.mode { 222 Mode::Panning => { 223 draw_buffer.draw_string("L M R", CharPosition::new(31, 14), 0, 3); 224 draw_buffer.draw_string("L M R", CharPosition::new(65, 14), 0, 3); 225 } 226 Mode::Volume => { 227 draw_buffer.draw_string(" Volumes ", CharPosition::new(31, 14), 0, 3); 228 draw_buffer.draw_string(" Volumes ", CharPosition::new(65, 14), 0, 3); 229 } 230 } 231 232 const ORDER_BASE_POS: CharPosition = CharPosition::new(2, 15); 233 let mut buf = [0; 3]; 234 for (pos, order) in (self.order_draw..=self.order_draw + 31).enumerate() { 235 let pos = u8::try_from(pos).unwrap(); 236 // row index 237 let mut curse: std::io::Cursor<&mut [u8]> = std::io::Cursor::new(&mut buf); 238 write!(&mut curse, "{:03}", order).unwrap(); 239 let text_color = if Some(order) == self.order_playback { 240 3 241 } else { 242 0 243 }; 244 draw_buffer.draw_string( 245 from_utf8(&buf).unwrap(), 246 ORDER_BASE_POS + CharPosition::new(0, pos), 247 text_color, 248 2, 249 ); 250 // row value 251 draw_buffer.draw_string( 252 write_pattern_order(self.pattern_order[usize::from(order)], &mut buf), 253 ORDER_BASE_POS + CharPosition::new(4, pos), 254 2, 255 0, 256 ); 257 } 258 259 // draw channel and numbers 260 // TODO: make the channel number strings const 261 const CHANNEL_BASE_LEFT: CharPosition = CharPosition::new(20, 15); 262 const CHANNEL_BASE_RIGHT: CharPosition = CharPosition::new(54, 15); 263 const CHANNEL: &str = "Channel"; 264 let mut buf = [0; 2]; 265 for row in 0..32 { 266 draw_buffer.draw_string(CHANNEL, CHANNEL_BASE_LEFT + CharPosition::new(0, row), 0, 2); 267 draw_buffer.draw_string( 268 CHANNEL, 269 CHANNEL_BASE_RIGHT + CharPosition::new(0, row), 270 0, 271 2, 272 ); 273 let mut curse: std::io::Cursor<&mut [u8]> = std::io::Cursor::new(&mut buf); 274 write!(&mut curse, "{:02}", row + 1).unwrap(); 275 draw_buffer.draw_string( 276 from_utf8(&buf).unwrap(), 277 CHANNEL_BASE_LEFT + CharPosition::new(8, row), 278 0, 279 2, 280 ); 281 let mut curse: std::io::Cursor<&mut [u8]> = std::io::Cursor::new(&mut buf); 282 write!(&mut curse, "{:02}", row + 33).unwrap(); 283 draw_buffer.draw_string( 284 from_utf8(&buf).unwrap(), 285 CHANNEL_BASE_RIGHT + CharPosition::new(8, row), 286 0, 287 2, 288 ); 289 } 290 291 // draw cursor 292 match self.cursor { 293 Cursor::Order => { 294 let mut buf = [0; 3]; 295 let row = write_pattern_order( 296 self.pattern_order[usize::from(self.order_cursor.order)], 297 &mut buf, 298 ); 299 draw_buffer.draw_char( 300 font8x8::UnicodeFonts::get( 301 &font8x8::BASIC_FONTS, 302 row.chars() 303 .nth(usize::from(self.order_cursor.digit)) 304 .unwrap(), 305 ) 306 .unwrap(), 307 ORDER_BASE_POS 308 + CharPosition::new( 309 4 + self.order_cursor.digit, 310 u8::try_from(self.order_cursor.order - self.order_draw).unwrap(), 311 ), 312 0, 313 3, 314 ); 315 } 316 Cursor::VolPan(c) => { 317 let mut buf = [0; 2]; 318 let mut curse: std::io::Cursor<&mut [u8]> = std::io::Cursor::new(&mut buf); 319 write!(&mut curse, "{:02}", c).unwrap(); 320 let pos = if c <= 32 { 321 CHANNEL_BASE_LEFT + CharPosition::new(0, c - 1) 322 } else { 323 CHANNEL_BASE_RIGHT + CharPosition::new(0, c - 33) 324 }; 325 draw_buffer.draw_string(CHANNEL, pos, 3, 2); 326 draw_buffer.draw_string( 327 from_utf8(&buf).unwrap(), 328 pos + CharPosition::new(8, 0), 329 3, 330 2, 331 ); 332 } 333 } 334 335 // draw sliders 336 match self.mode { 337 Mode::Panning => { 338 for (idx, pan) in self.pan.iter().enumerate() { 339 pan.draw( 340 draw_buffer, 341 self.cursor == Cursor::VolPan(u8::try_from(idx + 1).unwrap()), 342 ); 343 } 344 } 345 Mode::Volume => { 346 for (idx, vol) in self.volume.iter().enumerate() { 347 vol.draw( 348 draw_buffer, 349 self.cursor == Cursor::VolPan(u8::try_from(idx + 1).unwrap()), 350 ); 351 } 352 } 353 } 354 } 355 356 fn draw_constant(&mut self, draw_buffer: &mut crate::draw_buffer::DrawBuffer) { 357 // background fill 358 draw_buffer.draw_rect(2, CharRect::PAGE_AREA); 359 // box around order list 360 draw_buffer.draw_in_box(CharRect::new(14, 14 + 33, 5, 9), 2, 1, 3, 2); 361 // boxes aroung pan or vol 362 draw_buffer.draw_in_box(CharRect::new(13, 14 + 33, 30, 30 + 10), 2, 3, 3, 2); 363 draw_buffer.draw_in_box(CharRect::new(13, 14 + 33, 64, 64 + 10), 2, 3, 3, 2); 364 } 365 366 fn process_key_event( 367 &mut self, 368 modifiers: &winit::event::Modifiers, 369 key_event: &winit::event::KeyEvent, 370 events: &mut EventQueue<'_>, 371 ) -> PageResponse { 372 match self.cursor { 373 Cursor::Order => { 374 if key_event.logical_key == Key::Named(NamedKey::Tab) 375 && key_event.state.is_pressed() 376 { 377 if modifiers.state() == ModifiersState::SHIFT { 378 self.cursor = Cursor::VolPan(33); 379 return PageResponse::RequestRedraw; 380 } else if modifiers.state().is_empty() { 381 self.cursor = Cursor::VolPan(1); 382 return PageResponse::RequestRedraw; 383 } 384 } 385 if modifiers.state().is_empty() && key_event.state.is_pressed() { 386 if key_event.logical_key == Key::Named(NamedKey::ArrowUp) { 387 return self.order_cursor_up(1, events); 388 } else if key_event.logical_key == Key::Named(NamedKey::ArrowDown) { 389 return self.order_cursor_down(1, events); 390 } else if key_event.logical_key == Key::Named(NamedKey::PageDown) { 391 return self.order_cursor_down(16, events); 392 } else if key_event.logical_key == Key::Named(NamedKey::PageUp) { 393 return self.order_cursor_up(16, events); 394 } else if key_event.logical_key == Key::Named(NamedKey::ArrowLeft) { 395 self.order_cursor.digit = if self.order_cursor.digit == 0 { 396 2 397 } else { 398 self.order_cursor.digit - 1 399 }; 400 return PageResponse::RequestRedraw; 401 } else if key_event.logical_key == Key::Named(NamedKey::ArrowRight) { 402 self.order_cursor.digit = if self.order_cursor.digit == 2 { 403 0 404 } else { 405 self.order_cursor.digit + 1 406 }; 407 return PageResponse::RequestRedraw; 408 } else if key_event.logical_key == Key::Named(NamedKey::Tab) { 409 self.cursor = Cursor::VolPan(1); // go to channel 1 410 return PageResponse::RequestRedraw; 411 } else if let Key::Character(text) = &key_event.logical_key { 412 let mut chars = text.chars(); 413 let first = chars.next(); 414 assert!(chars.next().is_none()); 415 match first { 416 Some('+') => { 417 self.pattern_order[usize::from(self.order_cursor.order)] = 418 PatternOrder::SkipOrder; 419 send_song_op(SongOperation::SetOrder( 420 self.order_cursor.order, 421 PatternOrder::SkipOrder, 422 )); 423 self.order_cursor_down(1, events); 424 self.order_cursor.digit = 0; 425 return PageResponse::RequestRedraw; 426 } 427 Some('-') | Some('.') => { 428 self.pattern_order[usize::from(self.order_cursor.order)] = 429 PatternOrder::EndOfSong; 430 send_song_op(SongOperation::SetOrder( 431 self.order_cursor.order, 432 PatternOrder::EndOfSong, 433 )); 434 self.order_cursor_down(1, events); 435 self.order_cursor.digit = 0; 436 return PageResponse::RequestRedraw; 437 } 438 Some(num) if num.is_ascii_digit() => { 439 let mut buf = [0; 1]; 440 let string = num.encode_utf8(&mut buf); 441 let num: u8 = string.parse().unwrap(); 442 assert!(num <= 9); 443 let cursor = usize::from(self.order_cursor.order); 444 let new_num = 445 match (self.pattern_order[cursor], self.order_cursor.digit) { 446 // hooooly shit, i love match 447 (PatternOrder::Number(old_num), 0) => u8::try_from( 448 (u16::from(num) * 100 + u16::from(old_num % 100)) 449 .min(199), 450 ) 451 .unwrap(), 452 (PatternOrder::Number(old_num), 1) => { 453 num * 10 + 100 * (old_num / 100) + old_num % 10 454 } 455 (PatternOrder::Number(old_num), 2) => { 456 num + 10 * (old_num / 10) 457 } 458 (PatternOrder::Number(_), _) => unreachable!(), 459 (_, 0) => num.checked_mul(100).unwrap_or(199).min(199), 460 (_, 1) => 10 * num, 461 (_, 2) => num, 462 _ => unreachable!(), 463 }; 464 self.pattern_order[cursor] = PatternOrder::Number(new_num); 465 send_song_op(SongOperation::SetOrder( 466 self.order_cursor.order, 467 PatternOrder::Number(new_num), 468 )); 469 match self.order_cursor.digit { 470 0 => self.order_cursor.digit = 1, 471 1 => self.order_cursor.digit = 2, 472 2 => { 473 self.order_cursor.digit = 0; 474 self.order_cursor_down(1, events); 475 } 476 _ => unreachable!(), 477 } 478 self.send_order_len(events); 479 return PageResponse::RequestRedraw; 480 } 481 _ => return PageResponse::None, 482 } 483 } 484 } 485 } 486 Cursor::VolPan(c) => { 487 if key_event.logical_key == Key::Named(NamedKey::Tab) 488 && key_event.state.is_pressed() 489 { 490 if modifiers.state() == ModifiersState::SHIFT { 491 if c <= 32 { 492 self.cursor = Cursor::Order; 493 return PageResponse::RequestRedraw; 494 } else { 495 self.cursor = Cursor::VolPan(c - 32); 496 return PageResponse::RequestRedraw; 497 } 498 } else if modifiers.state().is_empty() { 499 if c <= 32 { 500 self.cursor = Cursor::VolPan(c + 32); 501 return PageResponse::RequestRedraw; 502 } else { 503 self.cursor = Cursor::Order; 504 return PageResponse::RequestRedraw; 505 } 506 } 507 return PageResponse::None; 508 } 509 if modifiers.state().is_empty() && key_event.state.is_pressed() { 510 if key_event.logical_key == Key::Named(NamedKey::ArrowUp) { 511 if c == 1 { 512 return PageResponse::None; 513 } else { 514 self.cursor = Cursor::VolPan(c - 1); 515 return PageResponse::RequestRedraw; 516 } 517 } else if key_event.logical_key == Key::Named(NamedKey::ArrowDown) { 518 if c == 64 { 519 return PageResponse::None; 520 } else { 521 self.cursor = Cursor::VolPan(c + 1); 522 return PageResponse::RequestRedraw; 523 } 524 } 525 } 526 527 match self.mode { 528 Mode::Panning => { 529 let slider = &mut self.pan[usize::from(c - 1)]; 530 match slider.process_input(modifiers, key_event, events) { 531 // i catch these earlier 532 WidgetResponse::SwitchFocus(_) => return PageResponse::None, 533 WidgetResponse::RequestRedraw(changed) => { 534 if changed { 535 send_song_op(SongOperation::SetPan( 536 c, 537 Pan::Value(u8::try_from(slider.get()).unwrap()), 538 )); 539 } 540 return PageResponse::RequestRedraw; 541 } 542 WidgetResponse::None => return PageResponse::None, 543 } 544 } 545 Mode::Volume => { 546 let slider = &mut self.volume[usize::from(c - 1)]; 547 match slider.process_input(modifiers, key_event, events) { 548 // i catch these earlier 549 WidgetResponse::SwitchFocus(_) => return PageResponse::None, 550 WidgetResponse::RequestRedraw(changed) => { 551 if changed { 552 send_song_op(SongOperation::SetVolume( 553 c, 554 u8::try_from(slider.get()).unwrap(), 555 )); 556 } 557 return PageResponse::RequestRedraw; 558 } 559 WidgetResponse::None => return PageResponse::None, 560 } 561 } 562 } 563 } 564 } 565 PageResponse::None 566 } 567 568 #[cfg(feature = "accesskit")] 569 fn build_tree( 570 &self, 571 tree: &mut Vec<(accesskit::NodeId, accesskit::Node)>, 572 ) -> crate::AccessResponse { 573 todo!() 574 } 575}