Feat: comfort features

This commit is contained in:
2026-02-08 00:46:56 +01:00
parent 20c32ce0d8
commit 8ffe2c22c7
31 changed files with 578 additions and 72 deletions

View File

@@ -58,8 +58,8 @@ pub struct App {
pub rng: Rng,
pub live_keys: Arc<LiveKeyState>,
pub clipboard: Option<arboard::Clipboard>,
pub copied_pattern: Option<Pattern>,
pub copied_bank: Option<Bank>,
pub copied_patterns: Option<Vec<Pattern>>,
pub copied_banks: Option<Vec<Bank>>,
pub audio: AudioSettings,
pub options: OptionsState,
@@ -100,8 +100,8 @@ impl App {
live_keys,
script_engine,
clipboard: arboard::Clipboard::new().ok(),
copied_pattern: None,
copied_bank: None,
copied_patterns: None,
copied_banks: None,
audio: AudioSettings::default(),
options: OptionsState::default(),
@@ -682,12 +682,17 @@ impl App {
}
pub fn copy_pattern(&mut self, bank: usize, pattern: usize) {
self.copied_pattern = Some(clipboard::copy_pattern(&self.project_state.project, bank, pattern));
self.copied_patterns = Some(vec![clipboard::copy_pattern(
&self.project_state.project,
bank,
pattern,
)]);
self.ui.flash("Pattern copied", 150, FlashKind::Success);
}
pub fn paste_pattern(&mut self, bank: usize, pattern: usize) {
if let Some(src) = self.copied_pattern.clone() {
if let Some(src) = self.copied_patterns.as_ref().and_then(|v| v.first()) {
let src = src.clone();
clipboard::paste_pattern(&mut self.project_state.project, bank, pattern, &src);
self.project_state.mark_dirty(bank, pattern);
if self.editor_ctx.bank == bank && self.editor_ctx.pattern == pattern {
@@ -697,14 +702,55 @@ impl App {
}
}
pub fn copy_patterns(&mut self, bank: usize, patterns: &[usize]) {
self.copied_patterns = Some(clipboard::copy_patterns(
&self.project_state.project,
bank,
patterns,
));
let n = patterns.len();
self.ui.flash(
&format!("{n} pattern{} copied", if n == 1 { "" } else { "s" }),
150,
FlashKind::Success,
);
}
pub fn paste_patterns(&mut self, bank: usize, start: usize) {
if let Some(sources) = self.copied_patterns.clone() {
let count = clipboard::paste_patterns(
&mut self.project_state.project,
bank,
start,
&sources,
);
for i in 0..count {
self.project_state.mark_dirty(bank, start + i);
}
if self.editor_ctx.bank == bank {
self.load_step_to_editor();
}
self.ui.flash(
&format!("{count} pattern{} pasted", if count == 1 { "" } else { "s" }),
150,
FlashKind::Success,
);
}
}
pub fn copy_bank(&mut self, bank: usize) {
self.copied_bank = Some(clipboard::copy_bank(&self.project_state.project, bank));
self.copied_banks = Some(vec![clipboard::copy_bank(
&self.project_state.project,
bank,
)]);
self.ui.flash("Bank copied", 150, FlashKind::Success);
}
pub fn paste_bank(&mut self, bank: usize) {
if let Some(src) = self.copied_bank.clone() {
let pat_count = clipboard::paste_bank(&mut self.project_state.project, bank, &src);
if let Some(src) = self.copied_banks.as_ref().and_then(|v| v.first()) {
let src = src.clone();
let pat_count =
clipboard::paste_bank(&mut self.project_state.project, bank, &src);
for pattern in 0..pat_count {
self.project_state.mark_dirty(bank, pattern);
}
@@ -715,6 +761,79 @@ impl App {
}
}
pub fn copy_banks(&mut self, banks: &[usize]) {
self.copied_banks = Some(clipboard::copy_banks(
&self.project_state.project,
banks,
));
let n = banks.len();
self.ui.flash(
&format!("{n} bank{} copied", if n == 1 { "" } else { "s" }),
150,
FlashKind::Success,
);
}
pub fn paste_banks(&mut self, start: usize) {
if let Some(sources) = self.copied_banks.clone() {
let count = clipboard::paste_banks(
&mut self.project_state.project,
start,
&sources,
);
for i in 0..count {
let bank = start + i;
for pattern in 0..model::MAX_PATTERNS {
self.project_state.mark_dirty(bank, pattern);
}
}
if (start..start + count).contains(&self.editor_ctx.bank) {
self.load_step_to_editor();
}
self.ui.flash(
&format!("{count} bank{} pasted", if count == 1 { "" } else { "s" }),
150,
FlashKind::Success,
);
}
}
pub fn reset_patterns(&mut self, bank: usize, patterns: &[usize]) {
for &pattern in patterns {
let edit =
pattern_editor::reset_pattern(&mut self.project_state.project, bank, pattern);
self.project_state.mark_dirty(edit.bank, edit.pattern);
}
if self.editor_ctx.bank == bank && patterns.contains(&self.editor_ctx.pattern) {
self.load_step_to_editor();
}
let n = patterns.len();
self.ui.flash(
&format!("{n} pattern{} reset", if n == 1 { "" } else { "s" }),
150,
FlashKind::Success,
);
}
pub fn reset_banks(&mut self, banks: &[usize]) {
for &bank in banks {
let pat_count =
pattern_editor::reset_bank(&mut self.project_state.project, bank);
for pattern in 0..pat_count {
self.project_state.mark_dirty(bank, pattern);
}
}
if banks.contains(&self.editor_ctx.bank) {
self.load_step_to_editor();
}
let n = banks.len();
self.ui.flash(
&format!("{n} bank{} reset", if n == 1 { "" } else { "s" }),
150,
FlashKind::Success,
);
}
pub fn harden_steps(&mut self) {
let (bank, pattern) = self.current_bank_pattern();
let indices = self.selected_steps();
@@ -944,12 +1063,30 @@ impl App {
AppCommand::PastePattern { bank, pattern } => {
self.paste_pattern(bank, pattern);
}
AppCommand::CopyPatterns { bank, patterns } => {
self.copy_patterns(bank, &patterns);
}
AppCommand::PastePatterns { bank, start } => {
self.paste_patterns(bank, start);
}
AppCommand::CopyBank { bank } => {
self.copy_bank(bank);
}
AppCommand::PasteBank { bank } => {
self.paste_bank(bank);
}
AppCommand::CopyBanks { banks } => {
self.copy_banks(&banks);
}
AppCommand::PasteBanks { start } => {
self.paste_banks(start);
}
AppCommand::ResetPatterns { bank, patterns } => {
self.reset_patterns(bank, &patterns);
}
AppCommand::ResetBanks { banks } => {
self.reset_banks(&banks);
}
// Clipboard
AppCommand::HardenSteps => self.harden_steps(),
@@ -1090,11 +1227,6 @@ impl App {
AppCommand::PatternsBack => {
self.page.down();
}
AppCommand::PatternsTogglePlay => {
let bank = self.patterns_nav.selected_bank();
let pattern = self.patterns_nav.selected_pattern();
self.stage_pattern_toggle(bank, pattern, snapshot);
}
// Mute/Solo (staged)
AppCommand::StageMute { bank, pattern } => {