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

@@ -6,6 +6,7 @@ use ratatui::Frame;
use crate::app::App;
use crate::engine::SequencerSnapshot;
use crate::model::{MAX_BANKS, MAX_PATTERNS};
use crate::state::PatternsColumn;
pub fn render(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, area: Rect) {
@@ -44,25 +45,25 @@ fn render_banks(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, area
.map(|p| p.bank)
.collect();
let banks_with_queued: Vec<usize> = app
let banks_with_staged: Vec<usize> = app
.playback
.queued_changes
.staged_changes
.iter()
.filter_map(|c| match c {
.filter_map(|c| match &c.change {
crate::engine::PatternChange::Start { bank, .. } => Some(*bank),
_ => None,
})
.collect();
let row_height = (inner.height / 16).max(1);
let total_needed = row_height * 16;
let row_height = (inner.height / MAX_BANKS as u16).max(1);
let total_needed = row_height * MAX_BANKS as u16;
let top_padding = if inner.height > total_needed {
(inner.height - total_needed) / 2
} else {
0
};
for idx in 0..16 {
for idx in 0..MAX_BANKS {
let y = inner.y + top_padding + (idx as u16) * row_height;
if y >= inner.y + inner.height {
break;
@@ -79,12 +80,12 @@ fn render_banks(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, area
let is_selected = idx == app.patterns_nav.bank_cursor;
let is_edit = idx == app.editor_ctx.bank;
let is_playing = banks_with_playback.contains(&idx);
let is_queued = banks_with_queued.contains(&idx);
let is_staged = banks_with_staged.contains(&idx);
let (bg, fg, prefix) = match (is_cursor, is_playing, is_queued) {
let (bg, fg, prefix) = match (is_cursor, is_playing, is_staged) {
(true, _, _) => (Color::Cyan, Color::Black, ""),
(false, true, _) => (Color::Rgb(45, 80, 45), Color::Green, "> "),
(false, false, true) => (Color::Rgb(80, 80, 45), Color::Yellow, "? "),
(false, false, true) => (Color::Rgb(80, 60, 100), Color::Magenta, "+ "),
(false, false, false) if is_selected => (Color::Rgb(60, 65, 75), Color::White, ""),
(false, false, false) if is_edit => (Color::Rgb(45, 106, 95), Color::White, ""),
(false, false, false) => (Color::Reset, Color::Rgb(120, 125, 135), ""),
@@ -101,7 +102,7 @@ fn render_banks(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, area
};
let style = Style::new().bg(bg).fg(fg);
let style = if is_playing || is_queued {
let style = if is_playing || is_staged {
style.add_modifier(Modifier::BOLD)
} else {
style
@@ -159,11 +160,11 @@ fn render_patterns(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, a
.map(|p| p.pattern)
.collect();
let queued_to_play: Vec<usize> = app
let staged_to_play: Vec<usize> = app
.playback
.queued_changes
.staged_changes
.iter()
.filter_map(|c| match c {
.filter_map(|c| match &c.change {
crate::engine::PatternChange::Start {
bank: b, pattern, ..
} if *b == bank => Some(*pattern),
@@ -171,11 +172,11 @@ fn render_patterns(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, a
})
.collect();
let queued_to_stop: Vec<usize> = app
let staged_to_stop: Vec<usize> = app
.playback
.queued_changes
.staged_changes
.iter()
.filter_map(|c| match c {
.filter_map(|c| match &c.change {
crate::engine::PatternChange::Stop {
bank: b,
pattern,
@@ -190,15 +191,15 @@ fn render_patterns(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, a
None
};
let row_height = (inner.height / 16).max(1);
let total_needed = row_height * 16;
let row_height = (inner.height / MAX_PATTERNS as u16).max(1);
let total_needed = row_height * MAX_PATTERNS as u16;
let top_padding = if inner.height > total_needed {
(inner.height - total_needed) / 2
} else {
0
};
for idx in 0..16 {
for idx in 0..MAX_PATTERNS {
let y = inner.y + top_padding + (idx as u16) * row_height;
if y >= inner.y + inner.height {
break;
@@ -215,14 +216,14 @@ fn render_patterns(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, a
let is_selected = idx == app.patterns_nav.pattern_cursor;
let is_edit = edit_pattern == Some(idx);
let is_playing = playing_patterns.contains(&idx);
let is_queued_play = queued_to_play.contains(&idx);
let is_queued_stop = queued_to_stop.contains(&idx);
let is_staged_play = staged_to_play.contains(&idx);
let is_staged_stop = staged_to_stop.contains(&idx);
let (bg, fg, prefix) = match (is_cursor, is_playing, is_queued_play, is_queued_stop) {
let (bg, fg, prefix) = match (is_cursor, is_playing, is_staged_play, is_staged_stop) {
(true, _, _, _) => (Color::Cyan, Color::Black, ""),
(false, true, _, true) => (Color::Rgb(120, 90, 30), Color::Yellow, "x "),
(false, true, _, true) => (Color::Rgb(120, 60, 80), Color::Magenta, "- "),
(false, true, _, false) => (Color::Rgb(45, 80, 45), Color::Green, "> "),
(false, false, true, _) => (Color::Rgb(80, 80, 45), Color::Yellow, "? "),
(false, false, true, _) => (Color::Rgb(80, 60, 100), Color::Magenta, "+ "),
(false, false, false, _) if is_selected => (Color::Rgb(60, 65, 75), Color::White, ""),
(false, false, false, _) if is_edit => (Color::Rgb(45, 106, 95), Color::White, ""),
(false, false, false, _) => (Color::Reset, Color::Rgb(120, 125, 135), ""),
@@ -271,7 +272,7 @@ fn render_patterns(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, a
} else {
format!("{}{:02} {}", prefix, idx + 1, name)
};
let name_style = if is_playing || is_queued_play {
let name_style = if is_playing || is_staged_play {
bold_style
} else {
base_style

View File

@@ -3,7 +3,7 @@ use std::time::Instant;
use ratatui::layout::{Alignment, Constraint, Layout, Rect};
use ratatui::style::{Color, Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, Borders, Paragraph};
use ratatui::widgets::{Block, Borders, Clear, Paragraph};
use ratatui::Frame;
use crate::app::App;
@@ -626,5 +626,91 @@ fn render_modal(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, term
};
frame.render_widget(Paragraph::new(hint).alignment(Alignment::Right), hint_area);
}
Modal::PatternProps {
bank,
pattern,
field,
name,
length,
speed,
quantization,
sync_mode,
} => {
use crate::state::PatternPropsField;
let width = 50u16;
let height = 12u16;
let x = (term.width.saturating_sub(width)) / 2;
let y = (term.height.saturating_sub(height)) / 2;
let area = Rect::new(x, y, width, height);
let block = Block::bordered()
.title(format!(" Pattern B{:02}:P{:02} ", bank + 1, pattern + 1))
.border_style(Style::default().fg(Color::Cyan));
let inner = block.inner(area);
frame.render_widget(Clear, area);
frame.render_widget(block, area);
let fields = [
("Name", name.as_str(), *field == PatternPropsField::Name),
("Length", length.as_str(), *field == PatternPropsField::Length),
("Speed", speed.label(), *field == PatternPropsField::Speed),
(
"Quantization",
quantization.label(),
*field == PatternPropsField::Quantization,
),
(
"Sync Mode",
sync_mode.label(),
*field == PatternPropsField::SyncMode,
),
];
for (i, (label, value, selected)) in fields.iter().enumerate() {
let y = inner.y + i as u16;
if y >= inner.y + inner.height {
break;
}
let (label_style, value_style) = if *selected {
(
Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD),
Style::default().fg(Color::White).bg(Color::DarkGray),
)
} else {
(
Style::default().fg(Color::Gray),
Style::default().fg(Color::White),
)
};
let label_area = Rect::new(inner.x + 1, y, 14, 1);
let value_area = Rect::new(inner.x + 16, y, inner.width.saturating_sub(18), 1);
frame.render_widget(
Paragraph::new(format!("{label}:")).style(label_style),
label_area,
);
frame.render_widget(
Paragraph::new(*value).style(value_style),
value_area,
);
}
let hint_area = Rect::new(inner.x, inner.y + inner.height - 1, inner.width, 1);
let hint = Line::from(vec![
Span::styled("↑↓", Style::default().fg(Color::Yellow)),
Span::styled(" nav ", Style::default().fg(Color::DarkGray)),
Span::styled("←→", Style::default().fg(Color::Yellow)),
Span::styled(" change ", Style::default().fg(Color::DarkGray)),
Span::styled("Enter", Style::default().fg(Color::Yellow)),
Span::styled(" save ", Style::default().fg(Color::DarkGray)),
Span::styled("Esc", Style::default().fg(Color::Yellow)),
Span::styled(" cancel", Style::default().fg(Color::DarkGray)),
]);
frame.render_widget(Paragraph::new(hint), hint_area);
}
}
}