WIP: better precision?
This commit is contained in:
@@ -9,6 +9,8 @@ use crate::engine::SequencerSnapshot;
|
||||
use crate::model::{MAX_BANKS, MAX_PATTERNS};
|
||||
use crate::state::PatternsColumn;
|
||||
|
||||
const MIN_ROW_HEIGHT: u16 = 1;
|
||||
|
||||
pub fn render(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, area: Rect) {
|
||||
let [banks_area, gap, patterns_area] = Layout::horizontal([
|
||||
Constraint::Fill(1),
|
||||
@@ -55,16 +57,25 @@ fn render_banks(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, area
|
||||
})
|
||||
.collect();
|
||||
|
||||
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 {
|
||||
let cursor = app.patterns_nav.bank_cursor;
|
||||
let max_visible = (inner.height / MIN_ROW_HEIGHT) as usize;
|
||||
let max_visible = max_visible.max(1);
|
||||
|
||||
let scroll_offset = if MAX_BANKS <= max_visible {
|
||||
0
|
||||
} else {
|
||||
cursor
|
||||
.saturating_sub(max_visible / 2)
|
||||
.min(MAX_BANKS - max_visible)
|
||||
};
|
||||
|
||||
for idx in 0..MAX_BANKS {
|
||||
let y = inner.y + top_padding + (idx as u16) * row_height;
|
||||
let visible_count = MAX_BANKS.min(max_visible);
|
||||
let row_height = inner.height / visible_count as u16;
|
||||
let row_height = row_height.max(MIN_ROW_HEIGHT);
|
||||
|
||||
for visible_idx in 0..visible_count {
|
||||
let idx = scroll_offset + visible_idx;
|
||||
let y = inner.y + (visible_idx as u16) * row_height;
|
||||
if y >= inner.y + inner.height {
|
||||
break;
|
||||
}
|
||||
@@ -126,6 +137,22 @@ fn render_banks(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, area
|
||||
let para = Paragraph::new(label).style(style);
|
||||
frame.render_widget(para, text_area);
|
||||
}
|
||||
|
||||
// Scroll indicators
|
||||
let indicator_style = Style::new().fg(Color::Rgb(120, 125, 135));
|
||||
if scroll_offset > 0 {
|
||||
let indicator = Paragraph::new("▲")
|
||||
.style(indicator_style)
|
||||
.alignment(ratatui::layout::Alignment::Center);
|
||||
frame.render_widget(indicator, Rect { height: 1, ..inner });
|
||||
}
|
||||
if scroll_offset + visible_count < MAX_BANKS {
|
||||
let y = inner.y + inner.height.saturating_sub(1);
|
||||
let indicator = Paragraph::new("▼")
|
||||
.style(indicator_style)
|
||||
.alignment(ratatui::layout::Alignment::Center);
|
||||
frame.render_widget(indicator, Rect { y, height: 1, ..inner });
|
||||
}
|
||||
}
|
||||
|
||||
fn render_patterns(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, area: Rect) {
|
||||
@@ -191,16 +218,25 @@ fn render_patterns(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, a
|
||||
None
|
||||
};
|
||||
|
||||
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 {
|
||||
let cursor = app.patterns_nav.pattern_cursor;
|
||||
let max_visible = (inner.height / MIN_ROW_HEIGHT) as usize;
|
||||
let max_visible = max_visible.max(1);
|
||||
|
||||
let scroll_offset = if MAX_PATTERNS <= max_visible {
|
||||
0
|
||||
} else {
|
||||
cursor
|
||||
.saturating_sub(max_visible / 2)
|
||||
.min(MAX_PATTERNS - max_visible)
|
||||
};
|
||||
|
||||
for idx in 0..MAX_PATTERNS {
|
||||
let y = inner.y + top_padding + (idx as u16) * row_height;
|
||||
let visible_count = MAX_PATTERNS.min(max_visible);
|
||||
let row_height = inner.height / visible_count as u16;
|
||||
let row_height = row_height.max(MIN_ROW_HEIGHT);
|
||||
|
||||
for visible_idx in 0..visible_count {
|
||||
let idx = scroll_offset + visible_idx;
|
||||
let y = inner.y + (visible_idx as u16) * row_height;
|
||||
if y >= inner.y + inner.height {
|
||||
break;
|
||||
}
|
||||
@@ -247,52 +283,56 @@ fn render_patterns(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, a
|
||||
row_area.y
|
||||
};
|
||||
|
||||
// Split row into columns: [index+name] [length] [speed]
|
||||
let speed_width: u16 = 14; // "Speed: 1/4x "
|
||||
let length_width: u16 = 13; // "Length: 16 "
|
||||
let name_width = row_area
|
||||
.width
|
||||
.saturating_sub(speed_width + length_width + 2);
|
||||
|
||||
let [name_area, length_area, speed_area] = Layout::horizontal([
|
||||
Constraint::Length(name_width),
|
||||
Constraint::Length(length_width),
|
||||
Constraint::Length(speed_width),
|
||||
])
|
||||
.areas(Rect {
|
||||
let text_area = Rect {
|
||||
x: row_area.x,
|
||||
y: text_y,
|
||||
width: row_area.width,
|
||||
height: 1,
|
||||
});
|
||||
|
||||
// Column 1: prefix + index + name (left-aligned)
|
||||
let name_text = if name.is_empty() {
|
||||
format!("{}{:02}", prefix, idx + 1)
|
||||
} else {
|
||||
format!("{}{:02} {}", prefix, idx + 1, name)
|
||||
};
|
||||
|
||||
// Build the line: [prefix][idx] [name] ... [length] [speed]
|
||||
let name_style = if is_playing || is_staged_play {
|
||||
bold_style
|
||||
} else {
|
||||
base_style
|
||||
};
|
||||
frame.render_widget(Paragraph::new(name_text).style(name_style), name_area);
|
||||
let dim_style = base_style.remove_modifier(Modifier::BOLD);
|
||||
|
||||
// Column 2: length
|
||||
let length_line = Line::from(vec![
|
||||
Span::styled("Length: ", bold_style),
|
||||
Span::styled(format!("{length}"), base_style),
|
||||
]);
|
||||
frame.render_widget(Paragraph::new(length_line), length_area);
|
||||
|
||||
// Column 3: speed (only if non-default)
|
||||
if speed != PatternSpeed::NORMAL {
|
||||
let speed_line = Line::from(vec![
|
||||
Span::styled("Speed: ", bold_style),
|
||||
Span::styled(speed.label(), base_style),
|
||||
]);
|
||||
frame.render_widget(Paragraph::new(speed_line), speed_area);
|
||||
let mut spans = vec![Span::styled(format!("{}{:02}", prefix, idx + 1), name_style)];
|
||||
if !name.is_empty() {
|
||||
spans.push(Span::styled(format!(" {name}"), name_style));
|
||||
}
|
||||
|
||||
// Right-aligned info: length and speed
|
||||
let speed_str = if speed != PatternSpeed::NORMAL {
|
||||
format!(" {}", speed.label())
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let right_info = format!("{length}{speed_str}");
|
||||
let left_width: usize = spans.iter().map(|s| s.content.chars().count()).sum();
|
||||
let right_width = right_info.chars().count();
|
||||
let padding = (text_area.width as usize).saturating_sub(left_width + right_width + 1);
|
||||
|
||||
spans.push(Span::raw(" ".repeat(padding)));
|
||||
spans.push(Span::styled(right_info, dim_style));
|
||||
|
||||
frame.render_widget(Paragraph::new(Line::from(spans)), text_area);
|
||||
}
|
||||
|
||||
// Scroll indicators
|
||||
let indicator_style = Style::new().fg(Color::Rgb(120, 125, 135));
|
||||
if scroll_offset > 0 {
|
||||
let indicator = Paragraph::new("▲")
|
||||
.style(indicator_style)
|
||||
.alignment(ratatui::layout::Alignment::Center);
|
||||
frame.render_widget(indicator, Rect { height: 1, ..inner });
|
||||
}
|
||||
if scroll_offset + visible_count < MAX_PATTERNS {
|
||||
let y = inner.y + inner.height.saturating_sub(1);
|
||||
let indicator = Paragraph::new("▼")
|
||||
.style(indicator_style)
|
||||
.alignment(ratatui::layout::Alignment::Center);
|
||||
frame.render_widget(indicator, Rect { y, height: 1, ..inner });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user