Feat: ability to rename steps

This commit is contained in:
2026-01-30 11:58:16 +01:00
parent d25b1317fc
commit 77a6aa9eb7
9 changed files with 149 additions and 12 deletions

View File

@@ -735,6 +735,7 @@ impl App {
active: step.active,
source: step.source,
original_index: idx,
name: step.name.clone(),
});
}
}
@@ -773,6 +774,7 @@ impl App {
let source = if same_pattern { data.source } else { None };
step.active = data.active;
step.source = source;
step.name = data.name.clone();
if source.is_some() {
step.script.clear();
step.command = None;
@@ -1046,6 +1048,17 @@ impl App {
} => {
self.project_state.project.banks[bank].patterns[pattern].name = name;
}
AppCommand::RenameStep {
bank,
pattern,
step,
name,
} => {
if let Some(s) = self.project_state.project.banks[bank].patterns[pattern].step_mut(step) {
s.name = name;
}
self.project_state.mark_dirty(bank, pattern);
}
AppCommand::Save(path) => self.save(path, link),
AppCommand::Load(path) => self.load(path, link),

View File

@@ -97,6 +97,12 @@ pub enum AppCommand {
pattern: usize,
name: Option<String>,
},
RenameStep {
bank: usize,
pattern: usize,
step: usize,
name: Option<String>,
},
Save(PathBuf),
Load(PathBuf),

View File

@@ -324,6 +324,34 @@ fn handle_modal_input(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
KeyCode::Char(c) => name.push(c),
_ => {}
},
Modal::RenameStep {
bank,
pattern,
step,
name,
} => match key.code {
KeyCode::Enter => {
let (bank_idx, pattern_idx, step_idx) = (*bank, *pattern, *step);
let new_name = if name.trim().is_empty() {
None
} else {
Some(name.clone())
};
ctx.dispatch(AppCommand::RenameStep {
bank: bank_idx,
pattern: pattern_idx,
step: step_idx,
name: new_name,
});
ctx.dispatch(AppCommand::CloseModal);
}
KeyCode::Esc => ctx.dispatch(AppCommand::CloseModal),
KeyCode::Backspace => {
name.pop();
}
KeyCode::Char(c) => name.push(c),
_ => {}
},
Modal::SetPattern { field, input } => match key.code {
KeyCode::Enter => {
let field = *field;
@@ -871,6 +899,23 @@ fn handle_main_page(ctx: &mut InputContext, key: KeyEvent, ctrl: bool) -> InputR
}));
}
}
KeyCode::Char('r') => {
let (bank, pattern, step) = (
ctx.app.editor_ctx.bank,
ctx.app.editor_ctx.pattern,
ctx.app.editor_ctx.step,
);
let current_name = ctx.app.current_edit_pattern()
.step(step)
.and_then(|s| s.name.clone())
.unwrap_or_default();
ctx.dispatch(AppCommand::OpenModal(Modal::RenameStep {
bank,
pattern,
step,
name: current_name,
}));
}
KeyCode::Char('?') => {
ctx.dispatch(AppCommand::OpenModal(Modal::KeybindingsHelp { scroll: 0 }));
}

View File

@@ -79,6 +79,7 @@ pub struct CopiedStepData {
pub active: bool,
pub source: Option<usize>,
pub original_index: usize,
pub name: Option<String>,
}
impl EditorContext {

View File

@@ -39,6 +39,12 @@ pub enum Modal {
pattern: usize,
name: String,
},
RenameStep {
bank: usize,
pattern: usize,
step: usize,
name: String,
},
SetPattern {
field: PatternField,
input: String,

View File

@@ -184,19 +184,63 @@ fn render_tile(
(false, false, false, _, _) => (Color::Rgb(45, 48, 55), Color::Rgb(120, 125, 135)),
};
let source_idx = step.and_then(|s| s.source);
let symbol = if is_playing {
"".to_string()
} else if let Some(source) = step.and_then(|s| s.source) {
} else if let Some(source) = source_idx {
format!("{:02}", source + 1)
} else {
format!("{:02}", step_idx + 1)
};
let tile = Paragraph::new(symbol)
.alignment(Alignment::Center)
.style(Style::new().bg(bg).fg(fg).add_modifier(Modifier::BOLD));
// For linked steps, get the name from the source step
let step_name = if let Some(src) = source_idx {
pattern.step(src).and_then(|s| s.name.as_ref())
} else {
step.and_then(|s| s.name.as_ref())
};
let num_lines = if step_name.is_some() { 2u16 } else { 1u16 };
let content_height = num_lines;
let y_offset = area.height.saturating_sub(content_height) / 2;
frame.render_widget(tile, area);
// Fill background for entire tile
let bg_fill = Paragraph::new("").style(Style::new().bg(bg));
frame.render_widget(bg_fill, area);
if let Some(name) = step_name {
let name_area = Rect {
x: area.x,
y: area.y + y_offset,
width: area.width,
height: 1,
};
let name_widget = Paragraph::new(name.as_str())
.alignment(Alignment::Center)
.style(Style::new().bg(bg).fg(fg).add_modifier(Modifier::BOLD));
frame.render_widget(name_widget, name_area);
let symbol_area = Rect {
x: area.x,
y: area.y + y_offset + 1,
width: area.width,
height: 1,
};
let symbol_widget = Paragraph::new(symbol)
.alignment(Alignment::Center)
.style(Style::new().bg(bg).fg(fg).add_modifier(Modifier::BOLD));
frame.render_widget(symbol_widget, symbol_area);
} else {
let centered_area = Rect {
x: area.x,
y: area.y + y_offset,
width: area.width,
height: 1,
};
let tile = Paragraph::new(symbol)
.alignment(Alignment::Center)
.style(Style::new().bg(bg).fg(fg).add_modifier(Modifier::BOLD));
frame.render_widget(tile, centered_area);
}
}
fn render_scope(frame: &mut Frame, app: &App, area: Rect) {

View File

@@ -577,6 +577,12 @@ fn render_modal(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, term
.border_color(Color::Magenta)
.render_centered(frame, term);
}
Modal::RenameStep { step, name, .. } => {
TextInputModal::new(&format!("Name Step {:02}", step + 1), name)
.width(40)
.border_color(Color::Cyan)
.render_centered(frame, term);
}
Modal::SetPattern { field, input } => {
let (title, hint) = match field {
PatternField::Length => ("Set Length (1-128)", "Enter number"),
@@ -618,11 +624,13 @@ fn render_modal(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, term
let step_idx = app.editor_ctx.step;
let step = pattern.step(step_idx);
let source_idx = step.and_then(|s| s.source);
let step_name = step.and_then(|s| s.name.as_ref());
let title = if let Some(src) = source_idx {
format!("Step {:02}{:02}", step_idx + 1, src + 1)
} else {
format!("Step {:02}", step_idx + 1)
let title = match (source_idx, step_name) {
(Some(src), Some(name)) => format!("Step {:02}: {}{:02}", step_idx + 1, name, src + 1),
(None, Some(name)) => format!("Step {:02}: {}", step_idx + 1, name),
(Some(src), None) => format!("Step {:02}{:02}", step_idx + 1, src + 1),
(None, None) => format!("Step {:02}", step_idx + 1),
};
let inner = ModalFrame::new(&title)
@@ -686,6 +694,7 @@ fn render_modal(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, term
let width = (term.width * 80 / 100).max(40);
let height = (term.height * 60 / 100).max(10);
let step_num = app.editor_ctx.step + 1;
let step = app.current_edit_pattern().step(app.editor_ctx.step);
let flash_kind = app.ui.flash_kind();
let border_color = match flash_kind {
@@ -695,7 +704,13 @@ fn render_modal(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, term
None => Color::Rgb(100, 160, 180),
};
let inner = ModalFrame::new(&format!("Step {step_num:02} Script"))
let title = if let Some(ref name) = step.and_then(|s| s.name.as_ref()) {
format!("Step {step_num:02}: {name}")
} else {
format!("Step {step_num:02} Script")
};
let inner = ModalFrame::new(&title)
.width(width)
.height(height)
.border_color(border_color)