use crossterm::event::{KeyCode, KeyEvent}; use std::sync::atomic::Ordering; use super::{InputContext, InputResult}; use crate::commands::AppCommand; use crate::state::{ConfirmAction, Modal, OptionsFocus}; pub(super) fn handle_options_page(ctx: &mut InputContext, key: KeyEvent) -> InputResult { match key.code { KeyCode::Char('q') => { ctx.dispatch(AppCommand::OpenModal(Modal::Confirm { action: ConfirmAction::Quit, selected: false, })); } KeyCode::Down | KeyCode::Tab => ctx.dispatch(AppCommand::OptionsNextFocus), KeyCode::Up | KeyCode::BackTab => ctx.dispatch(AppCommand::OptionsPrevFocus), KeyCode::Left | KeyCode::Right => { match ctx.app.options.focus { OptionsFocus::ColorScheme => { let new_scheme = if key.code == KeyCode::Left { ctx.app.ui.color_scheme.prev() } else { ctx.app.ui.color_scheme.next() }; ctx.dispatch(AppCommand::SetColorScheme(new_scheme)); } OptionsFocus::HueRotation => { let delta = if key.code == KeyCode::Left { -5.0 } else { 5.0 }; let new_rotation = (ctx.app.ui.hue_rotation + delta).rem_euclid(360.0); ctx.dispatch(AppCommand::SetHueRotation(new_rotation)); } OptionsFocus::RefreshRate => ctx.dispatch(AppCommand::ToggleRefreshRate), OptionsFocus::RuntimeHighlight => { ctx.dispatch(AppCommand::ToggleRuntimeHighlight); } OptionsFocus::ShowScope => { ctx.dispatch(AppCommand::ToggleScope); } OptionsFocus::ShowSpectrum => { ctx.dispatch(AppCommand::ToggleSpectrum); } OptionsFocus::ShowCompletion => { ctx.dispatch(AppCommand::ToggleCompletion); } OptionsFocus::LinkEnabled => ctx.link.set_enabled(!ctx.link.is_enabled()), OptionsFocus::StartStopSync => ctx .link .set_start_stop_sync_enabled(!ctx.link.is_start_stop_sync_enabled()), OptionsFocus::Quantum => { let delta = if key.code == KeyCode::Left { -1.0 } else { 1.0 }; ctx.link.set_quantum(ctx.link.quantum() + delta); } OptionsFocus::MidiOutput0 | OptionsFocus::MidiOutput1 | OptionsFocus::MidiOutput2 | OptionsFocus::MidiOutput3 => { let slot = match ctx.app.options.focus { OptionsFocus::MidiOutput0 => 0, OptionsFocus::MidiOutput1 => 1, OptionsFocus::MidiOutput2 => 2, OptionsFocus::MidiOutput3 => 3, _ => 0, }; let all_devices = crate::midi::list_midi_outputs(); let available: Vec<(usize, &crate::midi::MidiDeviceInfo)> = all_devices .iter() .enumerate() .filter(|(idx, _)| { ctx.app.midi.selected_outputs[slot] == Some(*idx) || !ctx .app .midi .selected_outputs .iter() .enumerate() .any(|(s, sel)| s != slot && *sel == Some(*idx)) }) .collect(); let total_options = available.len() + 1; let current_pos = ctx.app.midi.selected_outputs[slot] .and_then(|idx| available.iter().position(|(i, _)| *i == idx)) .map(|p| p + 1) .unwrap_or(0); let new_pos = if key.code == KeyCode::Left { if current_pos == 0 { total_options - 1 } else { current_pos - 1 } } else { (current_pos + 1) % total_options }; if new_pos == 0 { ctx.app.midi.disconnect_output(slot); ctx.dispatch(AppCommand::SetStatus(format!( "MIDI output {slot}: disconnected" ))); } else { let (device_idx, device) = available[new_pos - 1]; if ctx.app.midi.connect_output(slot, device_idx).is_ok() { ctx.dispatch(AppCommand::SetStatus(format!( "MIDI output {}: {}", slot, device.name ))); } } } OptionsFocus::MidiInput0 | OptionsFocus::MidiInput1 | OptionsFocus::MidiInput2 | OptionsFocus::MidiInput3 => { let slot = match ctx.app.options.focus { OptionsFocus::MidiInput0 => 0, OptionsFocus::MidiInput1 => 1, OptionsFocus::MidiInput2 => 2, OptionsFocus::MidiInput3 => 3, _ => 0, }; let all_devices = crate::midi::list_midi_inputs(); let available: Vec<(usize, &crate::midi::MidiDeviceInfo)> = all_devices .iter() .enumerate() .filter(|(idx, _)| { ctx.app.midi.selected_inputs[slot] == Some(*idx) || !ctx .app .midi .selected_inputs .iter() .enumerate() .any(|(s, sel)| s != slot && *sel == Some(*idx)) }) .collect(); let total_options = available.len() + 1; let current_pos = ctx.app.midi.selected_inputs[slot] .and_then(|idx| available.iter().position(|(i, _)| *i == idx)) .map(|p| p + 1) .unwrap_or(0); let new_pos = if key.code == KeyCode::Left { if current_pos == 0 { total_options - 1 } else { current_pos - 1 } } else { (current_pos + 1) % total_options }; if new_pos == 0 { ctx.app.midi.disconnect_input(slot); ctx.dispatch(AppCommand::SetStatus(format!( "MIDI input {slot}: disconnected" ))); } else { let (device_idx, device) = available[new_pos - 1]; if ctx.app.midi.connect_input(slot, device_idx).is_ok() { ctx.dispatch(AppCommand::SetStatus(format!( "MIDI input {}: {}", slot, device.name ))); } } } } ctx.app.save_settings(ctx.link); } KeyCode::Char(' ') => { ctx.dispatch(AppCommand::TogglePlaying); ctx.playing .store(ctx.app.playback.playing, Ordering::Relaxed); } KeyCode::Char('?') => { ctx.dispatch(AppCommand::OpenModal(Modal::KeybindingsHelp { scroll: 0 })); } _ => {} } InputResult::Continue }