Feat: begin slight refactoring

This commit is contained in:
2026-02-01 12:38:48 +01:00
parent 5b4a6ddd14
commit c356aebfde
39 changed files with 4699 additions and 3168 deletions

View File

@@ -52,11 +52,11 @@ pub fn handle_key(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
KeyCode::Left | KeyCode::Right | KeyCode::Up | KeyCode::Down
);
if ctx.app.ui.minimap_until.is_some() && !(ctrl && is_arrow) {
ctx.app.ui.minimap_until = None;
ctx.dispatch(AppCommand::ClearMinimap);
}
if ctx.app.ui.show_title {
ctx.app.ui.show_title = false;
ctx.dispatch(AppCommand::HideTitle);
return InputResult::Continue;
}
@@ -73,7 +73,7 @@ fn handle_live_keys(ctx: &mut InputContext, key: &KeyEvent) -> bool {
match (key.code, key.kind) {
_ if !matches!(ctx.app.ui.modal, Modal::None) => false,
(KeyCode::Char('f'), KeyEventKind::Press) => {
ctx.app.live_keys.flip_fill();
ctx.dispatch(AppCommand::ToggleLiveKeysFill);
true
}
_ => false,
@@ -506,13 +506,23 @@ fn handle_modal_input(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
editor.search_prev();
}
KeyCode::Char('s') if ctrl => {
ctx.app.editor_ctx.show_stack = !ctx.app.editor_ctx.show_stack;
ctx.dispatch(AppCommand::ToggleEditorStack);
}
KeyCode::Char('r') if ctrl => {
let script = ctx.app.editor_ctx.editor.lines().join("\n");
match ctx.app.execute_script_oneshot(&script, ctx.link, ctx.audio_tx) {
Ok(()) => ctx.app.ui.flash("Executed", 100, crate::state::FlashKind::Info),
Err(e) => ctx.app.ui.flash(&format!("Error: {e}"), 200, crate::state::FlashKind::Error),
match ctx
.app
.execute_script_oneshot(&script, ctx.link, ctx.audio_tx)
{
Ok(()) => ctx
.app
.ui
.flash("Executed", 100, crate::state::FlashKind::Info),
Err(e) => ctx.app.ui.flash(
&format!("Error: {e}"),
200,
crate::state::FlashKind::Error,
),
}
}
KeyCode::Char('a') if ctrl => {
@@ -745,8 +755,7 @@ fn handle_panel_input(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
KeyCode::Left => state.collapse_at_cursor(),
KeyCode::Char('/') => state.activate_search(),
KeyCode::Esc | KeyCode::Tab => {
ctx.app.panel.visible = false;
ctx.app.panel.focus = PanelFocus::Main;
ctx.dispatch(AppCommand::ClosePanel);
}
_ => {}
}
@@ -783,25 +792,25 @@ fn handle_main_page(ctx: &mut InputContext, key: KeyEvent, ctrl: bool) -> InputR
}
KeyCode::Left if shift && !ctrl => {
if ctx.app.editor_ctx.selection_anchor.is_none() {
ctx.app.editor_ctx.selection_anchor = Some(ctx.app.editor_ctx.step);
ctx.dispatch(AppCommand::SetSelectionAnchor(ctx.app.editor_ctx.step));
}
ctx.dispatch(AppCommand::PrevStep);
}
KeyCode::Right if shift && !ctrl => {
if ctx.app.editor_ctx.selection_anchor.is_none() {
ctx.app.editor_ctx.selection_anchor = Some(ctx.app.editor_ctx.step);
ctx.dispatch(AppCommand::SetSelectionAnchor(ctx.app.editor_ctx.step));
}
ctx.dispatch(AppCommand::NextStep);
}
KeyCode::Up if shift && !ctrl => {
if ctx.app.editor_ctx.selection_anchor.is_none() {
ctx.app.editor_ctx.selection_anchor = Some(ctx.app.editor_ctx.step);
ctx.dispatch(AppCommand::SetSelectionAnchor(ctx.app.editor_ctx.step));
}
ctx.dispatch(AppCommand::StepUp);
}
KeyCode::Down if shift && !ctrl => {
if ctx.app.editor_ctx.selection_anchor.is_none() {
ctx.app.editor_ctx.selection_anchor = Some(ctx.app.editor_ctx.step);
ctx.dispatch(AppCommand::SetSelectionAnchor(ctx.app.editor_ctx.step));
}
ctx.dispatch(AppCommand::StepDown);
}
@@ -910,9 +919,19 @@ fn handle_main_page(ctx: &mut InputContext, key: KeyEvent, ctrl: bool) -> InputR
let pattern = ctx.app.current_edit_pattern();
if let Some(script) = pattern.resolve_script(ctx.app.editor_ctx.step) {
if !script.trim().is_empty() {
match ctx.app.execute_script_oneshot(script, ctx.link, ctx.audio_tx) {
Ok(()) => ctx.app.ui.flash("Executed", 100, crate::state::FlashKind::Info),
Err(e) => ctx.app.ui.flash(&format!("Error: {e}"), 200, crate::state::FlashKind::Error),
match ctx
.app
.execute_script_oneshot(script, ctx.link, ctx.audio_tx)
{
Ok(()) => ctx
.app
.ui
.flash("Executed", 100, crate::state::FlashKind::Info),
Err(e) => ctx.app.ui.flash(
&format!("Error: {e}"),
200,
crate::state::FlashKind::Error,
),
}
}
}
@@ -923,7 +942,9 @@ fn handle_main_page(ctx: &mut InputContext, key: KeyEvent, ctrl: bool) -> InputR
ctx.app.editor_ctx.pattern,
ctx.app.editor_ctx.step,
);
let current_name = ctx.app.current_edit_pattern()
let current_name = ctx
.app
.current_edit_pattern()
.step(step)
.and_then(|s| s.name.clone())
.unwrap_or_default();
@@ -1063,15 +1084,15 @@ fn handle_engine_page(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
selected: false,
}));
}
KeyCode::Tab => ctx.app.audio.next_section(),
KeyCode::BackTab => ctx.app.audio.prev_section(),
KeyCode::Tab => ctx.dispatch(AppCommand::AudioNextSection),
KeyCode::BackTab => ctx.dispatch(AppCommand::AudioPrevSection),
KeyCode::Up => match ctx.app.audio.section {
EngineSection::Devices => match ctx.app.audio.device_kind {
DeviceKind::Output => ctx.app.audio.output_list.move_up(),
DeviceKind::Input => ctx.app.audio.input_list.move_up(),
DeviceKind::Output => ctx.dispatch(AppCommand::AudioOutputListUp),
DeviceKind::Input => ctx.dispatch(AppCommand::AudioInputListUp),
},
EngineSection::Settings => {
ctx.app.audio.setting_kind = ctx.app.audio.setting_kind.prev();
ctx.dispatch(AppCommand::AudioSettingPrev);
}
EngineSection::Samples => {}
},
@@ -1079,22 +1100,22 @@ fn handle_engine_page(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
EngineSection::Devices => match ctx.app.audio.device_kind {
DeviceKind::Output => {
let count = ctx.app.audio.output_devices.len();
ctx.app.audio.output_list.move_down(count);
ctx.dispatch(AppCommand::AudioOutputListDown(count));
}
DeviceKind::Input => {
let count = ctx.app.audio.input_devices.len();
ctx.app.audio.input_list.move_down(count);
ctx.dispatch(AppCommand::AudioInputListDown(count));
}
},
EngineSection::Settings => {
ctx.app.audio.setting_kind = ctx.app.audio.setting_kind.next();
ctx.dispatch(AppCommand::AudioSettingNext);
}
EngineSection::Samples => {}
},
KeyCode::PageUp => {
if ctx.app.audio.section == EngineSection::Devices {
match ctx.app.audio.device_kind {
DeviceKind::Output => ctx.app.audio.output_list.page_up(),
DeviceKind::Output => ctx.dispatch(AppCommand::AudioOutputPageUp),
DeviceKind::Input => ctx.app.audio.input_list.page_up(),
}
}
@@ -1104,11 +1125,11 @@ fn handle_engine_page(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
match ctx.app.audio.device_kind {
DeviceKind::Output => {
let count = ctx.app.audio.output_devices.len();
ctx.app.audio.output_list.page_down(count);
ctx.dispatch(AppCommand::AudioOutputPageDown(count));
}
DeviceKind::Input => {
let count = ctx.app.audio.input_devices.len();
ctx.app.audio.input_list.page_down(count);
ctx.dispatch(AppCommand::AudioInputPageDown(count));
}
}
}
@@ -1119,16 +1140,16 @@ fn handle_engine_page(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
DeviceKind::Output => {
let cursor = ctx.app.audio.output_list.cursor;
if cursor < ctx.app.audio.output_devices.len() {
ctx.app.audio.config.output_device =
Some(ctx.app.audio.output_devices[cursor].name.clone());
let name = ctx.app.audio.output_devices[cursor].name.clone();
ctx.dispatch(AppCommand::SetOutputDevice(name));
ctx.app.save_settings(ctx.link);
}
}
DeviceKind::Input => {
let cursor = ctx.app.audio.input_list.cursor;
if cursor < ctx.app.audio.input_devices.len() {
ctx.app.audio.config.input_device =
Some(ctx.app.audio.input_devices[cursor].name.clone());
let name = ctx.app.audio.input_devices[cursor].name.clone();
ctx.dispatch(AppCommand::SetInputDevice(name));
ctx.app.save_settings(ctx.link);
}
}
@@ -1137,20 +1158,32 @@ fn handle_engine_page(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
}
KeyCode::Left => match ctx.app.audio.section {
EngineSection::Devices => {
ctx.app.audio.device_kind = DeviceKind::Output;
ctx.dispatch(AppCommand::SetDeviceKind(DeviceKind::Output));
}
EngineSection::Settings => {
match ctx.app.audio.setting_kind {
SettingKind::Channels => ctx.app.audio.adjust_channels(-1),
SettingKind::BufferSize => ctx.app.audio.adjust_buffer_size(-64),
SettingKind::Polyphony => ctx.app.audio.adjust_max_voices(-1),
SettingKind::Channels => ctx.dispatch(AppCommand::AdjustAudioSetting {
setting: SettingKind::Channels,
delta: -1,
}),
SettingKind::BufferSize => ctx.dispatch(AppCommand::AdjustAudioSetting {
setting: SettingKind::BufferSize,
delta: -64,
}),
SettingKind::Polyphony => ctx.dispatch(AppCommand::AdjustAudioSetting {
setting: SettingKind::Polyphony,
delta: -1,
}),
SettingKind::Nudge => {
let prev = ctx.nudge_us.load(Ordering::Relaxed);
ctx.nudge_us
.store((prev - 1000).max(-100_000), Ordering::Relaxed);
}
SettingKind::Lookahead => {
ctx.app.audio.adjust_lookahead(-1);
ctx.dispatch(AppCommand::AdjustAudioSetting {
setting: SettingKind::Lookahead,
delta: -1,
});
ctx.lookahead_ms
.store(ctx.app.audio.config.lookahead_ms, Ordering::Relaxed);
}
@@ -1161,20 +1194,32 @@ fn handle_engine_page(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
},
KeyCode::Right => match ctx.app.audio.section {
EngineSection::Devices => {
ctx.app.audio.device_kind = DeviceKind::Input;
ctx.dispatch(AppCommand::SetDeviceKind(DeviceKind::Input));
}
EngineSection::Settings => {
match ctx.app.audio.setting_kind {
SettingKind::Channels => ctx.app.audio.adjust_channels(1),
SettingKind::BufferSize => ctx.app.audio.adjust_buffer_size(64),
SettingKind::Polyphony => ctx.app.audio.adjust_max_voices(1),
SettingKind::Channels => ctx.dispatch(AppCommand::AdjustAudioSetting {
setting: SettingKind::Channels,
delta: 1,
}),
SettingKind::BufferSize => ctx.dispatch(AppCommand::AdjustAudioSetting {
setting: SettingKind::BufferSize,
delta: 64,
}),
SettingKind::Polyphony => ctx.dispatch(AppCommand::AdjustAudioSetting {
setting: SettingKind::Polyphony,
delta: 1,
}),
SettingKind::Nudge => {
let prev = ctx.nudge_us.load(Ordering::Relaxed);
ctx.nudge_us
.store((prev + 1000).min(100_000), Ordering::Relaxed);
}
SettingKind::Lookahead => {
ctx.app.audio.adjust_lookahead(1);
ctx.dispatch(AppCommand::AdjustAudioSetting {
setting: SettingKind::Lookahead,
delta: 1,
});
ctx.lookahead_ms
.store(ctx.app.audio.config.lookahead_ms, Ordering::Relaxed);
}
@@ -1183,7 +1228,7 @@ fn handle_engine_page(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
}
EngineSection::Samples => {}
},
KeyCode::Char('R') => ctx.app.audio.trigger_restart(),
KeyCode::Char('R') => ctx.dispatch(AppCommand::AudioTriggerRestart),
KeyCode::Char('A') => {
use crate::state::file_browser::FileBrowserState;
let state = FileBrowserState::new_load(String::new());
@@ -1191,9 +1236,9 @@ fn handle_engine_page(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
}
KeyCode::Char('D') => {
if ctx.app.audio.section == EngineSection::Samples {
ctx.app.audio.remove_last_sample_path();
ctx.dispatch(AppCommand::RemoveLastSamplePath);
} else {
ctx.app.audio.refresh_devices();
ctx.dispatch(AppCommand::AudioRefreshDevices);
let out_count = ctx.app.audio.output_devices.len();
let in_count = ctx.app.audio.input_devices.len();
ctx.dispatch(AppCommand::SetStatus(format!(
@@ -1209,7 +1254,7 @@ fn handle_engine_page(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
let _ = ctx.audio_tx.load().send(AudioCommand::Panic);
let _ = ctx.seq_cmd_tx.send(SeqCommand::StopAll);
}
KeyCode::Char('r') => ctx.app.metrics.peak_voices = 0,
KeyCode::Char('r') => ctx.dispatch(AppCommand::ResetPeakVoices),
KeyCode::Char('t') => {
let _ = ctx.audio_tx.load().send(AudioCommand::Evaluate {
cmd: "/sound/sine/dur/0.5/decay/0.2".into(),
@@ -1231,8 +1276,8 @@ fn handle_options_page(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
selected: false,
}));
}
KeyCode::Down | KeyCode::Tab => ctx.app.options.next_focus(),
KeyCode::Up | KeyCode::BackTab => ctx.app.options.prev_focus(),
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 => {
@@ -1241,26 +1286,24 @@ fn handle_options_page(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
} else {
ctx.app.ui.color_scheme.next()
};
ctx.app.ui.color_scheme = new_scheme;
crate::theme::set(new_scheme.to_theme());
ctx.dispatch(AppCommand::SetColorScheme(new_scheme));
}
OptionsFocus::RefreshRate => ctx.app.audio.toggle_refresh_rate(),
OptionsFocus::RefreshRate => ctx.dispatch(AppCommand::ToggleRefreshRate),
OptionsFocus::RuntimeHighlight => {
ctx.app.ui.runtime_highlight = !ctx.app.ui.runtime_highlight
ctx.dispatch(AppCommand::ToggleRuntimeHighlight);
}
OptionsFocus::ShowScope => {
ctx.app.audio.config.show_scope = !ctx.app.audio.config.show_scope
ctx.dispatch(AppCommand::ToggleScope);
}
OptionsFocus::ShowSpectrum => {
ctx.app.audio.config.show_spectrum = !ctx.app.audio.config.show_spectrum
ctx.dispatch(AppCommand::ToggleSpectrum);
}
OptionsFocus::ShowCompletion => {
ctx.app.ui.show_completion = !ctx.app.ui.show_completion
ctx.dispatch(AppCommand::ToggleCompletion);
}
OptionsFocus::FlashBrightness => {
let delta = if key.code == KeyCode::Left { -0.1 } else { 0.1 };
ctx.app.ui.flash_brightness =
(ctx.app.ui.flash_brightness + delta).clamp(0.0, 1.0);
ctx.dispatch(AppCommand::AdjustFlashBrightness(delta));
}
OptionsFocus::LinkEnabled => ctx.link.set_enabled(!ctx.link.is_enabled()),
OptionsFocus::StartStopSync => ctx
@@ -1270,8 +1313,10 @@ fn handle_options_page(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
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 => {
OptionsFocus::MidiOutput0
| OptionsFocus::MidiOutput1
| OptionsFocus::MidiOutput2
| OptionsFocus::MidiOutput3 => {
let slot = match ctx.app.options.focus {
OptionsFocus::MidiOutput0 => 0,
OptionsFocus::MidiOutput1 => 1,
@@ -1285,7 +1330,12 @@ fn handle_options_page(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
.enumerate()
.filter(|(idx, _)| {
ctx.app.midi.selected_outputs[slot] == Some(*idx)
|| !ctx.app.midi.selected_outputs.iter().enumerate()
|| !ctx
.app
.midi
.selected_outputs
.iter()
.enumerate()
.any(|(s, sel)| s != slot && *sel == Some(*idx))
})
.collect();
@@ -1295,7 +1345,11 @@ fn handle_options_page(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
.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 }
if current_pos == 0 {
total_options - 1
} else {
current_pos - 1
}
} else {
(current_pos + 1) % total_options
};
@@ -1308,13 +1362,16 @@ fn handle_options_page(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
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
"MIDI output {}: {}",
slot, device.name
)));
}
}
}
OptionsFocus::MidiInput0 | OptionsFocus::MidiInput1 |
OptionsFocus::MidiInput2 | OptionsFocus::MidiInput3 => {
OptionsFocus::MidiInput0
| OptionsFocus::MidiInput1
| OptionsFocus::MidiInput2
| OptionsFocus::MidiInput3 => {
let slot = match ctx.app.options.focus {
OptionsFocus::MidiInput0 => 0,
OptionsFocus::MidiInput1 => 1,
@@ -1328,7 +1385,12 @@ fn handle_options_page(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
.enumerate()
.filter(|(idx, _)| {
ctx.app.midi.selected_inputs[slot] == Some(*idx)
|| !ctx.app.midi.selected_inputs.iter().enumerate()
|| !ctx
.app
.midi
.selected_inputs
.iter()
.enumerate()
.any(|(s, sel)| s != slot && *sel == Some(*idx))
})
.collect();
@@ -1338,7 +1400,11 @@ fn handle_options_page(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
.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 }
if current_pos == 0 {
total_options - 1
} else {
current_pos - 1
}
} else {
(current_pos + 1) % total_options
};
@@ -1351,7 +1417,8 @@ fn handle_options_page(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
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
"MIDI input {}: {}",
slot, device.name
)));
}
}