Files
Cagire/src/input/options_page.rs

228 lines
9.4 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(crate) fn cycle_option_value(ctx: &mut InputContext, right: bool) {
match ctx.app.options.focus {
OptionsFocus::ColorScheme => {
let new_scheme = if right {
ctx.app.ui.color_scheme.next()
} else {
ctx.app.ui.color_scheme.prev()
};
ctx.dispatch(AppCommand::SetColorScheme(new_scheme));
}
OptionsFocus::HueRotation => {
let delta = if right { 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::ShowPreview => ctx.dispatch(AppCommand::TogglePreview),
OptionsFocus::PerformanceMode => ctx.dispatch(AppCommand::TogglePerformanceMode),
OptionsFocus::Font => {
const FONTS: &[&str] = &["6x13", "7x13", "8x13", "9x15", "9x18", "10x20"];
let pos = FONTS.iter().position(|f| *f == ctx.app.ui.font).unwrap_or(2);
let new_pos = if right {
(pos + 1) % FONTS.len()
} else {
(pos + FONTS.len() - 1) % FONTS.len()
};
ctx.dispatch(AppCommand::SetFont(FONTS[new_pos].to_string()));
}
OptionsFocus::ZoomFactor => {
const ZOOMS: &[f32] = &[0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0];
let pos = ZOOMS
.iter()
.position(|z| (z - ctx.app.ui.zoom_factor).abs() < 0.01)
.unwrap_or(4);
let new_pos = if right {
(pos + 1) % ZOOMS.len()
} else {
(pos + ZOOMS.len() - 1) % ZOOMS.len()
};
ctx.dispatch(AppCommand::SetZoomFactor(ZOOMS[new_pos]));
}
OptionsFocus::WindowSize => {
const WINDOW_SIZES: &[(u32, u32)] = &[
(900, 600), (1050, 700), (1200, 800), (1350, 900), (1500, 1000),
];
let pos = WINDOW_SIZES
.iter()
.position(|&(w, h)| w == ctx.app.ui.window_width && h == ctx.app.ui.window_height)
.unwrap_or_else(|| {
WINDOW_SIZES
.iter()
.enumerate()
.min_by_key(|&(_, &(w, h))| {
let dw = w as i64 - ctx.app.ui.window_width as i64;
let dh = h as i64 - ctx.app.ui.window_height as i64;
dw * dw + dh * dh
})
.map(|(i, _)| i)
.unwrap_or(2)
});
let new_pos = if right {
(pos + 1) % WINDOW_SIZES.len()
} else {
(pos + WINDOW_SIZES.len() - 1) % WINDOW_SIZES.len()
};
let (w, h) = WINDOW_SIZES[new_pos];
ctx.dispatch(AppCommand::SetWindowSize(w, h));
}
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 right { 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 right {
(current_pos + 1) % total_options
} else if current_pos == 0 {
total_options - 1
} else {
current_pos - 1
};
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::ResetOnboarding => {
ctx.dispatch(AppCommand::ResetOnboarding);
}
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 right {
(current_pos + 1) % total_options
} else if current_pos == 0 {
total_options - 1
} else {
current_pos - 1
};
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);
}
pub(super) fn handle_options_page(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
match key.code {
KeyCode::Char('q') if !ctx.app.plugin_mode => {
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 => {
cycle_option_value(ctx, key.code == KeyCode::Right);
}
KeyCode::Char(' ') if !ctx.app.plugin_mode => {
ctx.dispatch(AppCommand::TogglePlaying);
ctx.playing
.store(ctx.app.playback.playing, Ordering::Relaxed);
}
KeyCode::Char('s') => super::open_save(ctx),
KeyCode::Char('l') => super::open_load(ctx),
KeyCode::Char('?') => {
ctx.dispatch(AppCommand::OpenModal(Modal::KeybindingsHelp { scroll: 0 }));
}
_ => {}
}
InputResult::Continue
}