So much better

This commit is contained in:
2026-01-26 02:24:04 +01:00
parent 2235a4b0a1
commit 223679acf8
16 changed files with 714 additions and 135 deletions

View File

@@ -16,7 +16,8 @@ use crate::services::pattern_editor;
use crate::settings::Settings;
use crate::state::{
AudioSettings, DictFocus, EditorContext, FlashKind, Focus, LiveKeyState, Metrics, Modal,
OptionsState, PanelState, PatternField, PatternsNav, PlaybackState, ProjectState, UiState,
OptionsState, PanelState, PatternField, PatternPropsField, PatternsNav, PlaybackState,
ProjectState, StagedChange, UiState,
};
use crate::views::{dict_view, help_view};
@@ -399,48 +400,76 @@ impl App {
}
}
pub fn toggle_pattern_playback(
pub fn stage_pattern_toggle(
&mut self,
bank: usize,
pattern: usize,
snapshot: &SequencerSnapshot,
) {
let is_playing = snapshot.is_playing(bank, pattern);
let pattern_data = self.project_state.project.pattern_at(bank, pattern);
let pending = self
let existing = self
.playback
.queued_changes
.staged_changes
.iter()
.position(|c| c.pattern_id().bank == bank && c.pattern_id().pattern == pattern);
.position(|c| {
c.change.pattern_id().bank == bank && c.change.pattern_id().pattern == pattern
});
if let Some(idx) = pending {
self.playback.queued_changes.remove(idx);
if let Some(idx) = existing {
self.playback.staged_changes.remove(idx);
self.ui.set_status(format!(
"B{:02}:P{:02} change cancelled",
"B{:02}:P{:02} unstaged",
bank + 1,
pattern + 1
));
} else if is_playing {
self.playback
.queued_changes
.push(PatternChange::Stop { bank, pattern });
self.playback.staged_changes.push(StagedChange {
change: PatternChange::Stop { bank, pattern },
quantization: pattern_data.quantization,
sync_mode: pattern_data.sync_mode,
});
self.ui.set_status(format!(
"B{:02}:P{:02} queued to stop",
"B{:02}:P{:02} staged to stop",
bank + 1,
pattern + 1
));
} else {
self.playback
.queued_changes
.push(PatternChange::Start { bank, pattern });
self.playback.staged_changes.push(StagedChange {
change: PatternChange::Start { bank, pattern },
quantization: pattern_data.quantization,
sync_mode: pattern_data.sync_mode,
});
self.ui.set_status(format!(
"B{:02}:P{:02} queued to play",
"B{:02}:P{:02} staged to play",
bank + 1,
pattern + 1
));
}
}
pub fn commit_staged_changes(&mut self) {
if self.playback.staged_changes.is_empty() {
self.ui.set_status("No changes to commit".to_string());
return;
}
let count = self.playback.staged_changes.len();
self.playback
.queued_changes
.append(&mut self.playback.staged_changes);
self.ui.set_status(format!("Committed {count} changes"));
}
pub fn clear_staged_changes(&mut self) {
if self.playback.staged_changes.is_empty() {
return;
}
let count = self.playback.staged_changes.len();
self.playback.staged_changes.clear();
self.ui.set_status(format!("Cleared {count} staged changes"));
}
pub fn select_edit_pattern(&mut self, pattern: usize) {
self.editor_ctx.pattern = pattern;
self.editor_ctx.step = 0;
@@ -724,6 +753,20 @@ impl App {
};
}
pub fn open_pattern_props_modal(&mut self, bank: usize, pattern: usize) {
let pat = self.project_state.project.pattern_at(bank, pattern);
self.ui.modal = Modal::PatternProps {
bank,
pattern,
field: PatternPropsField::default(),
name: pat.name.clone().unwrap_or_default(),
length: pat.length.to_string(),
speed: pat.speed,
quantization: pat.quantization,
sync_mode: pat.sync_mode,
};
}
pub fn dispatch(&mut self, cmd: AppCommand, link: &LinkState, snapshot: &SequencerSnapshot) {
match cmd {
// Playback
@@ -815,12 +858,15 @@ impl App {
AppCommand::LinkPasteStep => self.link_paste_step(),
AppCommand::HardenStep => self.harden_step(),
// Pattern playback
AppCommand::QueuePatternChange(change) => {
self.playback.queued_changes.push(change);
// Pattern playback (staging)
AppCommand::StagePatternToggle { bank, pattern } => {
self.stage_pattern_toggle(bank, pattern, snapshot);
}
AppCommand::TogglePatternPlayback { bank, pattern } => {
self.toggle_pattern_playback(bank, pattern, snapshot);
AppCommand::CommitStagedChanges => {
self.commit_staged_changes();
}
AppCommand::ClearStagedChanges => {
self.clear_staged_changes();
}
// Project
@@ -859,6 +905,28 @@ impl App {
}
AppCommand::CloseModal => self.ui.modal = Modal::None,
AppCommand::OpenPatternModal(field) => self.open_pattern_modal(field),
AppCommand::OpenPatternPropsModal { bank, pattern } => {
self.open_pattern_props_modal(bank, pattern);
}
AppCommand::SetPatternProps {
bank,
pattern,
name,
length,
speed,
quantization,
sync_mode,
} => {
let pat = self.project_state.project.pattern_at_mut(bank, pattern);
pat.name = name;
if let Some(len) = length {
pat.set_length(len);
}
pat.speed = speed;
pat.quantization = quantization;
pat.sync_mode = sync_mode;
self.project_state.mark_dirty(bank, pattern);
}
// Page navigation
AppCommand::PageLeft => self.page.left(),
@@ -953,19 +1021,28 @@ impl App {
AppCommand::PatternsTogglePlay => {
let bank = self.patterns_nav.selected_bank();
let pattern = self.patterns_nav.selected_pattern();
self.toggle_pattern_playback(bank, pattern, snapshot);
self.stage_pattern_toggle(bank, pattern, snapshot);
}
}
}
pub fn flush_queued_changes(&mut self, cmd_tx: &Sender<SeqCommand>) {
for change in self.playback.queued_changes.drain(..) {
match change {
for staged in self.playback.queued_changes.drain(..) {
match staged.change {
PatternChange::Start { bank, pattern } => {
let _ = cmd_tx.send(SeqCommand::PatternStart { bank, pattern });
let _ = cmd_tx.send(SeqCommand::PatternStart {
bank,
pattern,
quantization: staged.quantization,
sync_mode: staged.sync_mode,
});
}
PatternChange::Stop { bank, pattern } => {
let _ = cmd_tx.send(SeqCommand::PatternStop { bank, pattern });
let _ = cmd_tx.send(SeqCommand::PatternStop {
bank,
pattern,
quantization: staged.quantization,
});
}
}
}
@@ -987,6 +1064,8 @@ impl App {
source: s.source,
})
.collect(),
quantization: pat.quantization,
sync_mode: pat.sync_mode,
};
let _ = cmd_tx.send(SeqCommand::PatternUpdate {
bank,