179 lines
8.1 KiB
Rust
179 lines
8.1 KiB
Rust
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
|
|
}
|