old school music tracker
1use std::{io::Write, str::from_utf8};
2
3use torque_tracker_engine::project::{
4 event_command::NoteCommand,
5 note_event::{Note, NoteEvent, VolumeEffect},
6 pattern::{InPatternPosition, Pattern, PatternOperation},
7 song::{Song, SongOperation},
8};
9use winit::{
10 event_loop::EventLoopProxy,
11 keyboard::{Key, NamedKey, SmolStr},
12};
13
14use crate::{
15 EXECUTOR, EventQueue, GlobalEvent, SONG_MANAGER,
16 coordinates::{CharPosition, CharRect},
17 header::HeaderEvent,
18 send_song_op,
19};
20
21use super::{Page, PageResponse};
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24enum InEventPosition {
25 Note,
26 Octave,
27 Sample1,
28 Sample2,
29 VolPan1,
30 VolPan2,
31 Effect1,
32 Effect2,
33 Effect3,
34}
35
36impl InEventPosition {
37 fn to_right(self) -> Option<Self> {
38 match self {
39 InEventPosition::Note => Some(Self::Octave),
40 InEventPosition::Octave => Some(Self::Sample1),
41 InEventPosition::Sample1 => Some(Self::Sample2),
42 InEventPosition::Sample2 => Some(Self::VolPan1),
43 InEventPosition::VolPan1 => Some(Self::VolPan2),
44 InEventPosition::VolPan2 => Some(Self::Effect1),
45 InEventPosition::Effect1 => Some(Self::Effect2),
46 InEventPosition::Effect2 => Some(Self::Effect3),
47 InEventPosition::Effect3 => None,
48 }
49 }
50
51 fn to_left(self) -> Option<Self> {
52 match self {
53 InEventPosition::Note => None,
54 InEventPosition::Octave => Some(Self::Note),
55 InEventPosition::Sample1 => Some(Self::Octave),
56 InEventPosition::Sample2 => Some(Self::Sample1),
57 InEventPosition::VolPan1 => Some(Self::Sample2),
58 InEventPosition::VolPan2 => Some(Self::VolPan1),
59 InEventPosition::Effect1 => Some(Self::VolPan2),
60 InEventPosition::Effect2 => Some(Self::Effect1),
61 InEventPosition::Effect3 => Some(Self::Effect2),
62 }
63 }
64}
65
66fn get_note_from_key(key: &winit::keyboard::SmolStr, octave: u8) -> Option<Note> {
67 // TODO: keyboard layouts fuckk me.
68 #[allow(clippy::identity_op, clippy::zero_prefixed_literal)]
69 let note = match key.as_str() {
70 // lower octave
71 "z" => Some(Note::new(00 + 12 * (octave - 1))), // C-1
72 "s" => Some(Note::new(01 + 12 * (octave - 1))),
73 "x" => Some(Note::new(02 + 12 * (octave - 1))),
74 "d" => Some(Note::new(03 + 12 * (octave - 1))),
75 "c" => Some(Note::new(04 + 12 * (octave - 1))),
76 "v" => Some(Note::new(05 + 12 * (octave - 1))),
77 "g" => Some(Note::new(06 + 12 * (octave - 1))),
78 "b" => Some(Note::new(07 + 12 * (octave - 1))),
79 "h" => Some(Note::new(08 + 12 * (octave - 1))),
80 "n" => Some(Note::new(09 + 12 * (octave - 1))),
81 "j" => Some(Note::new(10 + 12 * (octave - 1))),
82 "m" => Some(Note::new(11 + 12 * (octave - 1))),
83 // base octave
84 "q" => Some(Note::new(00 + 12 * octave)), // C
85 "2" => Some(Note::new(01 + 12 * octave)), // Db / C#
86 "w" => Some(Note::new(02 + 12 * octave)), // D
87 "3" => Some(Note::new(03 + 12 * octave)), // Eb / D#
88 "e" => Some(Note::new(04 + 12 * octave)), // E
89 "r" => Some(Note::new(05 + 12 * octave)), // F
90 "5" => Some(Note::new(06 + 12 * octave)), // Gb / F#
91 "t" => Some(Note::new(07 + 12 * octave)), // G
92 "6" => Some(Note::new(08 + 12 * octave)), // Ab / G#
93 "y" => Some(Note::new(09 + 12 * octave)), // A
94 "7" => Some(Note::new(10 + 12 * octave)), // Bb / A#
95 "u" => Some(Note::new(11 + 12 * octave)), // B
96 // higher octave
97 "i" => Some(Note::new(0 + 12 * (octave + 1))), // C+1
98 "9" => Some(Note::new(1 + 12 * (octave + 1))),
99 "o" => Some(Note::new(2 + 12 * (octave + 1))),
100 "0" => Some(Note::new(3 + 12 * (octave + 1))),
101 "p" => Some(Note::new(4 + 12 * (octave + 1))),
102 _ => None,
103 };
104 note.and_then(Result::ok)
105}
106
107#[derive(Debug, Clone)]
108pub enum PatternPageEvent {
109 Loaded(Pattern, u8),
110 SetSampleInstr(u8),
111 /// pattern, row
112 PlaybackPosition(Option<(u8, u16)>),
113}
114
115#[derive(Debug)]
116pub struct PatternPage {
117 pattern_index: u8,
118 pattern: Pattern,
119 cursor_position: (InPatternPosition, InEventPosition),
120 draw_position: InPatternPosition,
121 event_proxy: EventLoopProxy<GlobalEvent>,
122 /// Last used or last selected in the sample menu
123 selected_sample_instr: u8,
124 /// pattern, row
125 // storest the pattern index, because if i switch page i want to show the current position before i
126 // get the next event
127 playback: Option<(u8, u16)>,
128}
129
130impl PatternPage {
131 const MAX_PATTERN: usize = 199;
132 const DRAWN_ROWS: u16 = 32;
133 const DRAWN_CHANNELS: u8 = 5;
134 const MAX_CHANNELS: u8 = 64;
135 /// how many rows the cursor is moved when pressing pageup/down
136 // TODO: make configurable
137 const PAGE_AS_ROWS: u16 = 16;
138 const CHANNEL_WIDTH: u8 = 14;
139
140 // TODO: make configurable
141 const ROW_HIGHTLIGHT_MINOR: u16 = 4;
142 const ROW_HIGHTLIGHT_MAJOR: u16 = 16;
143
144 pub fn process_event(
145 &mut self,
146 event: PatternPageEvent,
147 events: &mut EventQueue<'_>,
148 ) -> PageResponse {
149 match event {
150 PatternPageEvent::Loaded(pattern, idx) => {
151 self.pattern = pattern;
152 self.pattern_index = idx;
153 events.push(GlobalEvent::Header(HeaderEvent::SetPattern(idx)));
154 events.push(GlobalEvent::Header(HeaderEvent::SetMaxCursorRow(
155 self.pattern.row_count(),
156 )));
157 PageResponse::RequestRedraw
158 }
159 PatternPageEvent::SetSampleInstr(i) => {
160 self.selected_sample_instr = i;
161 PageResponse::None
162 }
163 PatternPageEvent::PlaybackPosition(p) => {
164 self.playback = p;
165 // TODO: only return this if the change is actually visible
166 PageResponse::RequestRedraw
167 }
168 }
169 }
170
171 pub fn new(proxy: EventLoopProxy<GlobalEvent>) -> Self {
172 Self {
173 pattern_index: 0,
174 pattern: Pattern::default(),
175 cursor_position: (
176 InPatternPosition { row: 0, channel: 0 },
177 InEventPosition::Note,
178 ),
179 draw_position: InPatternPosition { row: 0, channel: 0 },
180 event_proxy: proxy,
181 selected_sample_instr: 0,
182 playback: None,
183 }
184 }
185
186 /// returns true if the position was changed
187 fn set_cursor(&mut self, mut pos: InPatternPosition, events: &mut EventQueue<'_>) -> bool {
188 if pos.row >= self.pattern.row_count() {
189 pos.row = self.pattern.row_count() - 1;
190 }
191 if pos.channel >= Self::MAX_CHANNELS {
192 pos.channel = Self::MAX_CHANNELS - 1;
193 }
194
195 if pos == self.cursor_position.0 {
196 return false;
197 }
198
199 if pos.row != self.cursor_position.0.row {
200 events.push(GlobalEvent::Header(HeaderEvent::SetCursorRow(pos.row)));
201 }
202
203 self.cursor_position.0 = pos;
204
205 // update draw position
206 if pos.channel >= self.draw_position.channel + Self::DRAWN_CHANNELS {
207 self.draw_position.channel = pos.channel - Self::DRAWN_CHANNELS + 1;
208 } else if pos.channel < self.draw_position.channel {
209 self.draw_position.channel = pos.channel
210 }
211
212 if pos.row <= (Self::DRAWN_ROWS / 2) {
213 self.draw_position.row = 0;
214 } else if pos.row >= self.pattern.row_count() - (Self::DRAWN_ROWS / 2) {
215 self.draw_position.row = self.pattern.row_count() - Self::DRAWN_ROWS
216 } else {
217 self.draw_position.row = pos.row - (Self::DRAWN_ROWS / 2);
218 }
219
220 true
221 }
222
223 /// returns true if the cursor was changed
224 fn cursor_next_row(&mut self, events: &mut EventQueue<'_>) -> bool {
225 let mut pos = self.cursor_position.0;
226 pos.row = pos.row.saturating_add(1);
227 self.set_cursor(pos, events)
228 }
229
230 fn load_pattern(&mut self, idx: u8) {
231 let proxy = self.event_proxy.clone();
232 EXECUTOR
233 .spawn(async move {
234 let lock = SONG_MANAGER.lock().await;
235 let pattern = lock.get_song().patterns[usize::from(idx)].clone();
236 drop(lock);
237 proxy
238 .send_event(GlobalEvent::Page(super::PageEvent::Pattern(
239 PatternPageEvent::Loaded(pattern, idx),
240 )))
241 .unwrap();
242 })
243 .detach();
244 }
245
246 fn set_event(&mut self, position: InPatternPosition, event: NoteEvent) {
247 self.pattern.set_event(position, event);
248 let op = SongOperation::PatternOperation(
249 self.pattern_index,
250 PatternOperation::SetEvent { position, event },
251 );
252 send_song_op(op);
253 }
254
255 fn remove_event(&mut self, position: InPatternPosition) {
256 self.pattern.remove_event(position);
257 let op = SongOperation::PatternOperation(
258 self.pattern_index,
259 PatternOperation::RemoveEvent { position },
260 );
261 send_song_op(op);
262 }
263
264 pub fn set_sample(&mut self, sample: u8, events: &mut EventQueue<'_>) {
265 self.selected_sample_instr = sample;
266 events.push(GlobalEvent::Page(super::PageEvent::SampleList(
267 super::sample_list::SampleListEvent::SelectSample(sample),
268 )));
269 }
270}
271
272impl Page for PatternPage {
273 fn draw(&mut self, draw_buffer: &mut super::DrawBuffer) {
274 // helper fns
275 fn visible_channels(page: &PatternPage) -> impl Iterator<Item = (usize, u8)> {
276 (page.draw_position.channel..page.draw_position.channel + PatternPage::DRAWN_CHANNELS)
277 .enumerate()
278 }
279
280 fn visible_rows(page: &PatternPage) -> impl Iterator<Item = (usize, u16)> {
281 (page.draw_position.row..page.draw_position.row + PatternPage::DRAWN_ROWS).enumerate()
282 }
283
284 // draw row numbers
285 assert!(self.draw_position.row + Self::DRAWN_ROWS <= 999);
286 let mut buf = [0; 3];
287 for (index, value) in visible_rows(self) {
288 const BASE_POS: CharPosition = CharPosition::new(1, 15);
289 let mut curse: std::io::Cursor<&mut [u8]> = std::io::Cursor::new(&mut buf);
290 write!(&mut curse, "{:03}", value).unwrap();
291 let text_color = if self.playback == Some((self.pattern_index, value)) {
292 3
293 } else {
294 0
295 };
296 draw_buffer.draw_string(
297 from_utf8(&buf).unwrap(),
298 BASE_POS + CharPosition::new(0, u8::try_from(index).unwrap()),
299 text_color,
300 2,
301 );
302 }
303
304 // draw channel headings
305 assert!(self.draw_position.channel + Self::DRAWN_CHANNELS <= 99);
306 let mut buf: [u8; 2] = [0; 2];
307 for (index, value) in visible_channels(self) {
308 const BASE_POS: CharPosition = CharPosition::new(14, 14);
309
310 let mut curse: std::io::Cursor<&mut [u8]> = std::io::Cursor::new(&mut buf);
311 write!(&mut curse, "{:02}", value).unwrap();
312
313 draw_buffer.draw_string(
314 from_utf8(&buf).unwrap(),
315 BASE_POS + (u8::try_from(index).unwrap() * Self::CHANNEL_WIDTH, 0),
316 3,
317 1,
318 );
319 }
320
321 // draw events
322 const BLOCK_CODE: [u8; 8] = [0b0, 0b0, 0b0, 0b11000, 0b11000, 0b0, 0b0, 0b0];
323 struct EventView {
324 note1: [u8; 8],
325 note2: [u8; 8],
326 octave: [u8; 8],
327 sample1: [u8; 8],
328 sample2: [u8; 8],
329 vol_pan1: [u8; 8],
330 vol_pan2: [u8; 8],
331 effect1: [u8; 8],
332 effect2: [u8; 8],
333 effect3: [u8; 8],
334 }
335
336 impl Default for EventView {
337 fn default() -> Self {
338 Self {
339 note1: BLOCK_CODE,
340 note2: BLOCK_CODE,
341 octave: BLOCK_CODE,
342 sample1: BLOCK_CODE,
343 sample2: BLOCK_CODE,
344 vol_pan1: BLOCK_CODE,
345 vol_pan2: BLOCK_CODE,
346 effect1: font8x8::UnicodeFonts::get(&font8x8::BASIC_FONTS, '.').unwrap(),
347 effect2: font8x8::UnicodeFonts::get(&font8x8::BASIC_FONTS, '0').unwrap(),
348 effect3: font8x8::UnicodeFonts::get(&font8x8::BASIC_FONTS, '0').unwrap(),
349 }
350 }
351 }
352
353 impl From<NoteEvent> for EventView {
354 fn from(value: NoteEvent) -> Self {
355 fn get_bitmap_digit(value: u8) -> [u8; 8] {
356 let char = match value {
357 0 => '0',
358 1 => '1',
359 2 => '2',
360 3 => '3',
361 4 => '4',
362 5 => '5',
363 6 => '6',
364 7 => '7',
365 8 => '8',
366 9 => '9',
367 _ => panic!("not a digit. number too large"),
368 };
369 font8x8::UnicodeFonts::get(&font8x8::BASIC_FONTS, char).unwrap()
370 }
371
372 let mut view = Self::default();
373 // note
374 let mut note_chars = value.note.get_note_name().chars();
375 let first = note_chars.next().unwrap();
376 let second = note_chars.next().unwrap_or('-');
377 view.note1 = font8x8::UnicodeFonts::get(&font8x8::BASIC_FONTS, first).unwrap();
378 view.note2 = font8x8::UnicodeFonts::get(&font8x8::BASIC_FONTS, second).unwrap();
379
380 view.octave = get_bitmap_digit(value.note.get_octave());
381 // sample_instr number
382 view.sample1 = get_bitmap_digit(value.sample_instr / 10);
383 view.sample2 = get_bitmap_digit(value.sample_instr % 10);
384 // TODO: rest noch
385 view
386 }
387 }
388
389 const EVENT_BASE_POS: CharPosition = CharPosition::new(5, 15);
390 const BACKGROUND: u8 = 0;
391 const FOREGROUND: u8 = 6;
392 for (c_idx, c_val) in visible_channels(self) {
393 for (r_idx, r_val) in visible_rows(self) {
394 let background_color = match r_val {
395 val if val == self.cursor_position.0.row => 1,
396 val if val % Self::ROW_HIGHTLIGHT_MAJOR == 0 => 14,
397 val if val % Self::ROW_HIGHTLIGHT_MINOR == 0 => 15,
398 _ => BACKGROUND,
399 };
400 let view: EventView = self
401 .pattern
402 .get_event(InPatternPosition {
403 row: r_val,
404 channel: c_val,
405 })
406 .map(|e| (*e).into())
407 .unwrap_or_default();
408 let pos = EVENT_BASE_POS
409 + (
410 u8::try_from(c_idx).unwrap() * Self::CHANNEL_WIDTH,
411 u8::try_from(r_idx).unwrap(),
412 );
413 draw_buffer.draw_char(view.note1, pos, 6, background_color);
414 draw_buffer.draw_char(view.note2, pos + (1, 0), FOREGROUND, background_color);
415 draw_buffer.draw_char(view.octave, pos + (2, 0), FOREGROUND, background_color);
416 draw_buffer.draw_rect(background_color, (pos + (3, 0)).into());
417 draw_buffer.draw_char(view.sample1, pos + (4, 0), FOREGROUND, background_color);
418 draw_buffer.draw_char(view.sample2, pos + (5, 0), FOREGROUND, background_color);
419 draw_buffer.draw_rect(background_color, (pos + (6, 0)).into());
420 draw_buffer.draw_char(view.vol_pan1, pos + (7, 0), FOREGROUND, background_color);
421 draw_buffer.draw_char(view.vol_pan2, pos + (8, 0), FOREGROUND, background_color);
422 draw_buffer.draw_rect(background_color, (pos + (9, 0)).into());
423 draw_buffer.draw_char(view.effect1, pos + (10, 0), FOREGROUND, background_color);
424 draw_buffer.draw_char(view.effect2, pos + (11, 0), FOREGROUND, background_color);
425 draw_buffer.draw_char(view.effect3, pos + (12, 0), FOREGROUND, background_color);
426 }
427 }
428
429 // draw cursor
430 let view: EventView = self
431 .pattern
432 .get_event(self.cursor_position.0)
433 .map(|e| (*e).into())
434 .unwrap_or_default();
435 assert!(self.cursor_position.0.channel >= self.draw_position.channel);
436 assert!(self.cursor_position.0.row >= self.draw_position.row);
437 let c_idx = self.cursor_position.0.channel - self.draw_position.channel;
438 let r_idx = u8::try_from(self.cursor_position.0.row - self.draw_position.row).unwrap();
439 let pos = EVENT_BASE_POS + (c_idx * Self::CHANNEL_WIDTH, r_idx);
440 match self.cursor_position.1 {
441 InEventPosition::Note => draw_buffer.draw_char(view.note1, pos, 0, 3),
442 InEventPosition::Octave => draw_buffer.draw_char(view.octave, pos + (2, 0), 0, 3),
443 InEventPosition::Sample1 => draw_buffer.draw_char(view.sample1, pos + (4, 0), 0, 3),
444 InEventPosition::Sample2 => draw_buffer.draw_char(view.sample2, pos + (5, 0), 0, 3),
445 InEventPosition::VolPan1 => draw_buffer.draw_char(view.vol_pan1, pos + (7, 0), 0, 3),
446 InEventPosition::VolPan2 => draw_buffer.draw_char(view.vol_pan2, pos + (8, 0), 0, 3),
447 InEventPosition::Effect1 => draw_buffer.draw_char(view.effect1, pos + (10, 0), 0, 3),
448 InEventPosition::Effect2 => draw_buffer.draw_char(view.effect2, pos + (11, 0), 0, 3),
449 InEventPosition::Effect3 => draw_buffer.draw_char(view.effect3, pos + (12, 0), 0, 3),
450 }
451 }
452
453 fn draw_constant(&mut self, draw_buffer: &mut super::DrawBuffer) {
454 draw_buffer.draw_rect(2, CharRect::PAGE_AREA);
455
456 // draw channel headers const parts
457 for index in 0..Self::DRAWN_CHANNELS {
458 const BASE_POS: CharPosition = CharPosition::new(5, 14);
459 let pos = BASE_POS + (index * Self::CHANNEL_WIDTH, 0);
460 draw_buffer.draw_rect(1, (pos + (11, 0)).into());
461
462 draw_buffer.draw_string(" Channel ", pos, 3, 1);
463 }
464 }
465
466 fn process_key_event(
467 &mut self,
468 modifiers: &winit::event::Modifiers,
469 key_event: &winit::event::KeyEvent,
470 events: &mut EventQueue<'_>,
471 ) -> PageResponse {
472 if !key_event.state.is_pressed() {
473 return PageResponse::None;
474 }
475
476 if key_event.logical_key == Key::Character(SmolStr::new_static("+")) {
477 if usize::from(self.pattern_index) != Self::MAX_PATTERN {
478 self.load_pattern(self.pattern_index + 1);
479 return PageResponse::RequestRedraw;
480 }
481 } else if key_event.logical_key == Key::Character(SmolStr::new_static("-")) {
482 if self.pattern_index != 0 {
483 self.load_pattern(self.pattern_index - 1);
484 return PageResponse::RequestRedraw;
485 }
486 } else if key_event.logical_key == Key::Named(NamedKey::ArrowDown) {
487 if self.cursor_next_row(events) {
488 return PageResponse::RequestRedraw;
489 }
490 } else if key_event.logical_key == Key::Named(NamedKey::ArrowUp) {
491 let mut pos = self.cursor_position.0;
492 pos.row = pos.row.saturating_sub(1);
493 if self.set_cursor(pos, events) {
494 return PageResponse::RequestRedraw;
495 }
496 } else if key_event.logical_key == Key::Named(NamedKey::ArrowRight) {
497 match self.cursor_position.1.to_right() {
498 Some(p) => {
499 self.cursor_position.1 = p;
500 return PageResponse::RequestRedraw;
501 }
502 None => {
503 let mut pos = self.cursor_position.0;
504 pos.channel = pos.channel.saturating_add(1);
505 if self.set_cursor(pos, events) {
506 self.cursor_position.1 = InEventPosition::Note;
507 return PageResponse::RequestRedraw;
508 }
509 }
510 }
511 } else if key_event.logical_key == Key::Named(NamedKey::ArrowLeft) {
512 match self.cursor_position.1.to_left() {
513 Some(p) => {
514 self.cursor_position.1 = p;
515 return PageResponse::RequestRedraw;
516 }
517 None => {
518 let mut pos = self.cursor_position.0;
519 pos.channel = pos.channel.saturating_sub(1);
520 if self.set_cursor(pos, events) {
521 self.cursor_position.1 = InEventPosition::Effect3;
522 return PageResponse::RequestRedraw;
523 }
524 }
525 }
526 } else if key_event.logical_key == Key::Named(NamedKey::Tab) {
527 // shift => move left
528 // not shift => move right
529 if modifiers.state().shift_key() {
530 if self.cursor_position.1 == InEventPosition::Note {
531 let mut pos = self.cursor_position.0;
532 pos.channel = pos.channel.saturating_sub(1);
533 if self.set_cursor(pos, events) {
534 return PageResponse::RequestRedraw;
535 }
536 } else {
537 self.cursor_position.1 = InEventPosition::Note;
538 return PageResponse::RequestRedraw;
539 }
540 } else {
541 let mut pos = self.cursor_position.0;
542 pos.channel = pos.channel.saturating_add(1);
543 self.set_cursor(pos, events);
544 self.cursor_position.1 = InEventPosition::Note;
545 return PageResponse::RequestRedraw;
546 }
547 } else if key_event.logical_key == Key::Named(NamedKey::PageDown) {
548 let mut pos = self.cursor_position.0;
549 pos.row = pos.row.saturating_add(Self::PAGE_AS_ROWS);
550 if self.set_cursor(pos, events) {
551 return PageResponse::RequestRedraw;
552 }
553 } else if key_event.logical_key == Key::Named(NamedKey::PageUp) {
554 // original has special behaviour on page up:
555 // when on last row it only goes up 15 rows
556 let mut pos = self.cursor_position.0;
557 pos.row = pos.row.saturating_sub(Self::PAGE_AS_ROWS);
558 if self.set_cursor(pos, events) {
559 return PageResponse::RequestRedraw;
560 }
561 } else if Key::Character(SmolStr::new_static(".")) == key_event.logical_key {
562 self.remove_event(self.cursor_position.0);
563 self.cursor_next_row(events);
564 return PageResponse::RequestRedraw;
565 } else if let Key::Character(char) = &key_event.logical_key {
566 // should be copied from the header, where this can already be set
567 const DEFAULT_OCTAVE: u8 = 5;
568 match self.cursor_position.1 {
569 InEventPosition::Note => {
570 // TODO: make octave configurable
571 if let Some(note) = get_note_from_key(char, DEFAULT_OCTAVE) {
572 let old_event = self.pattern.get_event(self.cursor_position.0);
573 let event = match old_event {
574 None => NoteEvent {
575 note,
576 sample_instr: self.selected_sample_instr,
577 vol: VolumeEffect::None,
578 command: NoteCommand::None,
579 },
580 Some(&event) => NoteEvent { note, ..event },
581 };
582 self.set_event(self.cursor_position.0, event);
583 }
584 // move to next row even if
585 self.cursor_next_row(events);
586 // always redraw is incorrect. I only need to redraw if either the cursor moved, or the event changed
587 return PageResponse::RequestRedraw;
588 }
589 InEventPosition::Octave => {
590 if let Some(event) = self.pattern.get_event(self.cursor_position.0) {
591 let mut new_event = *event;
592 // set octave fn needed
593 let octave: Result<u8, _> = char.as_str().parse();
594 if let Ok(octave) = octave {
595 new_event.note =
596 Note::new(event.note.get() % 12 + 12 * octave).unwrap();
597 self.set_event(self.cursor_position.0, new_event);
598 }
599 }
600 self.cursor_next_row(events);
601 // always redraw is incorrect.
602 return PageResponse::RequestRedraw;
603 }
604 InEventPosition::Sample1 => {
605 let num: Result<u8, _> = char.as_str().parse();
606 if let Ok(num) = num
607 && let Some(event) = self.pattern.get_event(self.cursor_position.0).copied()
608 {
609 let zeros = event.sample_instr % 10;
610 let sample_instr = zeros + num * 10;
611 if usize::from(sample_instr) < Song::MAX_SAMPLES_INSTR {
612 self.set_event(
613 self.cursor_position.0,
614 NoteEvent {
615 sample_instr,
616 ..event
617 },
618 );
619 self.set_sample(sample_instr, events);
620 }
621 }
622 // move cursor one step right
623 self.cursor_position.1 = InEventPosition::Sample2;
624 return PageResponse::RequestRedraw;
625 }
626 InEventPosition::Sample2 => {
627 let num: Result<u8, _> = char.as_str().parse();
628 if let Ok(num) = num
629 && let Some(event) = self.pattern.get_event(self.cursor_position.0).copied()
630 {
631 let tens = event.sample_instr / 10;
632 let sample_instr = tens * 10 + num;
633 if usize::from(sample_instr) < Song::MAX_SAMPLES_INSTR {
634 self.set_event(
635 self.cursor_position.0,
636 NoteEvent {
637 sample_instr,
638 ..event
639 },
640 );
641 self.set_sample(sample_instr, events);
642 }
643 }
644 self.cursor_next_row(events);
645 return PageResponse::RequestRedraw;
646 }
647 InEventPosition::VolPan1 => eprintln!("not yet implemented"),
648 InEventPosition::VolPan2 => eprintln!("not yet implemented"),
649 InEventPosition::Effect1 => eprintln!("not yet implemented"),
650 InEventPosition::Effect2 => eprintln!("not yet implemented"),
651 InEventPosition::Effect3 => eprintln!("not yet implemented"),
652 }
653 }
654
655 PageResponse::None
656 }
657
658 #[cfg(feature = "accesskit")]
659 fn build_tree(
660 &self,
661 tree: &mut Vec<(accesskit::NodeId, accesskit::Node)>,
662 ) -> crate::AccessResponse {
663 todo!()
664 }
665}