Feat: better user feedback on patterns page
All checks were successful
Deploy Website / deploy (push) Has been skipped
All checks were successful
Deploy Website / deploy (push) Has been skipped
This commit is contained in:
@@ -13,6 +13,20 @@ use crate::widgets::{render_scroll_indicators, IndicatorAlign};
|
||||
|
||||
const MIN_ROW_HEIGHT: u16 = 1;
|
||||
|
||||
fn pulse_value(phase: f32) -> f32 {
|
||||
phase.sin() * 0.5 + 0.5
|
||||
}
|
||||
|
||||
fn pulse_color(from: Color, to: Color, t: f32) -> Color {
|
||||
match (from, to) {
|
||||
(Color::Rgb(r1, g1, b1), Color::Rgb(r2, g2, b2)) => {
|
||||
let l = |a: u8, b: u8| (a as f32 + (b as f32 - a as f32) * t) as u8;
|
||||
Color::Rgb(l(r1, r2), l(g1, g2), l(b1, b2))
|
||||
}
|
||||
_ => from,
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces the background color of spans beyond `filled_cols` with `unfilled_bg`.
|
||||
fn apply_progress_bg(spans: Vec<Span<'_>>, filled_cols: usize, unfilled_bg: Color) -> Vec<Span<'_>> {
|
||||
let mut result = Vec::with_capacity(spans.len() + 1);
|
||||
@@ -54,7 +68,32 @@ pub fn render(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, area:
|
||||
Layout::horizontal([Constraint::Fill(1), Constraint::Length(22)]).areas(bottom_area);
|
||||
|
||||
render_banks(frame, app, snapshot, banks_area);
|
||||
render_patterns(frame, app, snapshot, patterns_area);
|
||||
|
||||
let armed_summary = app.playback.armed_summary();
|
||||
let (patterns_main, launch_bar_area) = if armed_summary.is_some() {
|
||||
let [main, bar] =
|
||||
Layout::vertical([Constraint::Fill(1), Constraint::Length(1)]).areas(patterns_area);
|
||||
(main, Some(bar))
|
||||
} else {
|
||||
(patterns_area, None)
|
||||
};
|
||||
|
||||
render_patterns(frame, app, snapshot, patterns_main);
|
||||
|
||||
if let (Some(bar_area), Some(summary)) = (launch_bar_area, armed_summary) {
|
||||
let pulse = pulse_value(app.ui.pulse_phase);
|
||||
let pulsed_fg = pulse_color(theme.list.staged_play_fg, theme.list.staged_play_bg, pulse * 0.6);
|
||||
let text = format!("\u{25b6} {summary} \u{2014} c to launch");
|
||||
let bar = Paragraph::new(text)
|
||||
.alignment(Alignment::Center)
|
||||
.style(
|
||||
Style::new()
|
||||
.fg(pulsed_fg)
|
||||
.bg(theme.list.staged_play_bg)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
);
|
||||
frame.render_widget(bar, bar_area);
|
||||
}
|
||||
|
||||
let bank = app.patterns_nav.bank_cursor;
|
||||
let pattern_idx = app.patterns_nav.pattern_cursor;
|
||||
@@ -82,6 +121,7 @@ pub fn render(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, area:
|
||||
|
||||
fn render_banks(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, area: Rect) {
|
||||
let theme = theme::get();
|
||||
let pulse = pulse_value(app.ui.pulse_phase);
|
||||
let is_focused = matches!(app.patterns_nav.column, PatternsColumn::Banks);
|
||||
|
||||
let border_color = if is_focused { theme.ui.header } else { theme.ui.border };
|
||||
@@ -215,6 +255,12 @@ fn render_banks(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, area
|
||||
} else {
|
||||
style
|
||||
};
|
||||
let style = if (is_staged || has_staged_mute_solo) && !is_cursor && !is_in_range {
|
||||
let pulsed = pulse_color(fg, bg, pulse * 0.6);
|
||||
style.fg(pulsed)
|
||||
} else {
|
||||
style
|
||||
};
|
||||
|
||||
let bg_block = Block::default().style(Style::new().bg(bg));
|
||||
frame.render_widget(bg_block, row_area);
|
||||
@@ -247,6 +293,7 @@ fn render_banks(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, area
|
||||
|
||||
fn render_patterns(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, area: Rect) {
|
||||
use crate::model::PatternSpeed;
|
||||
let pulse = pulse_value(app.ui.pulse_phase);
|
||||
|
||||
let theme = theme::get();
|
||||
let is_focused = matches!(app.patterns_nav.column, PatternsColumn::Patterns);
|
||||
@@ -454,6 +501,14 @@ fn render_patterns(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, a
|
||||
};
|
||||
let dim_style = base_style.remove_modifier(Modifier::BOLD);
|
||||
|
||||
let is_armed = is_staged_play || is_staged_stop || has_staged_mute || has_staged_solo || has_staged_props;
|
||||
let (name_style, dim_style) = if is_armed && !is_cursor && !is_in_range {
|
||||
let pulsed = pulse_color(fg, bg, pulse * 0.6);
|
||||
(name_style.fg(pulsed), dim_style.fg(pulsed))
|
||||
} else {
|
||||
(name_style, dim_style)
|
||||
};
|
||||
|
||||
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));
|
||||
|
||||
Reference in New Issue
Block a user