vastly improved selection system

This commit is contained in:
2026-01-28 02:29:17 +01:00
parent 322885b908
commit c7a9f7bc5a
13 changed files with 1507 additions and 437 deletions

View File

@@ -7,7 +7,7 @@ use std::time::{Duration, Instant};
use crate::app::App;
use crate::commands::AppCommand;
use crate::engine::{AudioCommand, LinkState, SequencerSnapshot};
use crate::engine::{AudioCommand, LinkState, SeqCommand, SequencerSnapshot};
use crate::model::PatternSpeed;
use crate::page::Page;
use crate::state::{
@@ -26,6 +26,7 @@ pub struct InputContext<'a> {
pub snapshot: &'a SequencerSnapshot,
pub playing: &'a Arc<AtomicBool>,
pub audio_tx: &'a ArcSwap<Sender<AudioCommand>>,
pub seq_cmd_tx: &'a Sender<SeqCommand>,
pub nudge_us: &'a Arc<AtomicI64>,
}
@@ -140,6 +141,49 @@ fn handle_modal_input(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
_ => {}
}
}
Modal::ConfirmDeleteSteps {
bank,
pattern,
steps,
selected: _,
} => {
let (bank, pattern, steps) = (*bank, *pattern, steps.clone());
match key.code {
KeyCode::Char('y') | KeyCode::Char('Y') => {
ctx.dispatch(AppCommand::DeleteSteps {
bank,
pattern,
steps,
});
ctx.dispatch(AppCommand::CloseModal);
}
KeyCode::Char('n') | KeyCode::Char('N') | KeyCode::Esc => {
ctx.dispatch(AppCommand::CloseModal);
}
KeyCode::Left | KeyCode::Right => {
if let Modal::ConfirmDeleteSteps { selected, .. } = &mut ctx.app.ui.modal {
*selected = !*selected;
}
}
KeyCode::Enter => {
let do_delete =
if let Modal::ConfirmDeleteSteps { selected, .. } = &ctx.app.ui.modal {
*selected
} else {
false
};
if do_delete {
ctx.dispatch(AppCommand::DeleteSteps {
bank,
pattern,
steps,
});
}
ctx.dispatch(AppCommand::CloseModal);
}
_ => {}
}
}
Modal::ConfirmResetPattern {
bank,
pattern,
@@ -650,6 +694,8 @@ fn handle_panel_input(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
}
fn handle_main_page(ctx: &mut InputContext, key: KeyEvent, ctrl: bool) -> InputResult {
let shift = key.modifiers.contains(KeyModifiers::SHIFT);
match key.code {
KeyCode::Tab => {
if ctx.app.panel.visible {
@@ -674,12 +720,54 @@ fn handle_main_page(ctx: &mut InputContext, key: KeyEvent, ctrl: bool) -> InputR
ctx.playing
.store(ctx.app.playback.playing, Ordering::Relaxed);
}
KeyCode::Left => ctx.dispatch(AppCommand::PrevStep),
KeyCode::Right => ctx.dispatch(AppCommand::NextStep),
KeyCode::Up => ctx.dispatch(AppCommand::StepUp),
KeyCode::Down => ctx.dispatch(AppCommand::StepDown),
KeyCode::Enter => ctx.dispatch(AppCommand::OpenModal(Modal::Editor)),
KeyCode::Char('t') => ctx.dispatch(AppCommand::ToggleStep),
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::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::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::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::StepDown);
}
KeyCode::Left => {
ctx.app.editor_ctx.clear_selection();
ctx.dispatch(AppCommand::PrevStep);
}
KeyCode::Right => {
ctx.app.editor_ctx.clear_selection();
ctx.dispatch(AppCommand::NextStep);
}
KeyCode::Up => {
ctx.app.editor_ctx.clear_selection();
ctx.dispatch(AppCommand::StepUp);
}
KeyCode::Down => {
ctx.app.editor_ctx.clear_selection();
ctx.dispatch(AppCommand::StepDown);
}
KeyCode::Esc => {
ctx.app.editor_ctx.clear_selection();
}
KeyCode::Enter => {
ctx.app.editor_ctx.clear_selection();
ctx.dispatch(AppCommand::OpenModal(Modal::Editor));
}
KeyCode::Char('t') => ctx.dispatch(AppCommand::ToggleSteps),
KeyCode::Char('s') => {
use crate::state::file_browser::FileBrowserState;
let initial = ctx
@@ -692,10 +780,19 @@ fn handle_main_page(ctx: &mut InputContext, key: KeyEvent, ctrl: bool) -> InputR
let state = FileBrowserState::new_save(initial);
ctx.dispatch(AppCommand::OpenModal(Modal::FileBrowser(state)));
}
KeyCode::Char('c') if ctrl => ctx.dispatch(AppCommand::CopyStep),
KeyCode::Char('v') if ctrl => ctx.dispatch(AppCommand::PasteStep),
KeyCode::Char('b') if ctrl => ctx.dispatch(AppCommand::LinkPasteStep),
KeyCode::Char('h') if ctrl => ctx.dispatch(AppCommand::HardenStep),
KeyCode::Char('c') if ctrl => {
ctx.dispatch(AppCommand::CopySteps);
}
KeyCode::Char('v') if ctrl => {
ctx.dispatch(AppCommand::PasteSteps);
}
KeyCode::Char('b') if ctrl => {
ctx.dispatch(AppCommand::LinkPasteSteps);
}
KeyCode::Char('d') if ctrl => {
ctx.dispatch(AppCommand::DuplicateSteps);
}
KeyCode::Char('h') if ctrl => ctx.dispatch(AppCommand::HardenSteps),
KeyCode::Char('l') => {
use crate::state::file_browser::FileBrowserState;
let default_dir = ctx
@@ -730,13 +827,23 @@ fn handle_main_page(ctx: &mut InputContext, key: KeyEvent, ctrl: bool) -> InputR
KeyCode::Char('p') => ctx.dispatch(AppCommand::OpenModal(Modal::Preview)),
KeyCode::Delete | KeyCode::Backspace => {
let (bank, pattern) = (ctx.app.editor_ctx.bank, ctx.app.editor_ctx.pattern);
let step = ctx.app.editor_ctx.step;
ctx.dispatch(AppCommand::OpenModal(Modal::ConfirmDeleteStep {
bank,
pattern,
step,
selected: false,
}));
if let Some(range) = ctx.app.editor_ctx.selection_range() {
let steps: Vec<usize> = range.collect();
ctx.dispatch(AppCommand::OpenModal(Modal::ConfirmDeleteSteps {
bank,
pattern,
steps,
selected: false,
}));
} else {
let step = ctx.app.editor_ctx.step;
ctx.dispatch(AppCommand::OpenModal(Modal::ConfirmDeleteStep {
bank,
pattern,
step,
selected: false,
}));
}
}
_ => {}
}
@@ -987,9 +1094,11 @@ fn handle_engine_page(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
}
KeyCode::Char('h') => {
let _ = ctx.audio_tx.load().send(AudioCommand::Hush);
let _ = ctx.seq_cmd_tx.send(SeqCommand::StopAll);
}
KeyCode::Char('p') => {
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('t') => {