WIP: better precision?

This commit is contained in:
2026-01-29 18:50:54 +01:00
parent 00a90f1c15
commit 89e4795e86
13 changed files with 477 additions and 224 deletions

View File

@@ -11,6 +11,7 @@ use crate::widgets::{Orientation, Scope, Spectrum};
const HEADER_COLOR: Color = Color::Rgb(100, 160, 180);
const DIVIDER_COLOR: Color = Color::Rgb(60, 65, 70);
const SCROLL_INDICATOR_COLOR: Color = Color::Rgb(80, 85, 95);
pub fn render(frame: &mut Frame, app: &App, area: Rect) {
let [left_col, _, right_col] = Layout::horizontal([
@@ -40,20 +41,105 @@ fn render_settings_section(frame: &mut Frame, app: &App, area: Rect) {
height: inner.height.saturating_sub(1),
};
let devices_height = devices_section_height(app);
// Calculate section heights
let devices_lines = devices_section_height(app) as usize;
let settings_lines: usize = 8; // header(1) + divider(1) + 6 rows
let samples_lines: usize = 6; // header(1) + divider(1) + content(3) + hint(1)
let total_lines = devices_lines + 1 + settings_lines + 1 + samples_lines;
let [devices_area, _, settings_area, _, samples_area] = Layout::vertical([
Constraint::Length(devices_height),
Constraint::Length(1),
Constraint::Length(8),
Constraint::Length(1),
Constraint::Min(6),
])
.areas(padded);
let max_visible = padded.height as usize;
render_devices(frame, app, devices_area);
render_settings(frame, app, settings_area);
render_samples(frame, app, samples_area);
// Calculate scroll offset based on focused section
let (focus_start, focus_height) = match app.audio.section {
EngineSection::Devices => (0, devices_lines),
EngineSection::Settings => (devices_lines + 1, settings_lines),
EngineSection::Samples => (devices_lines + 1 + settings_lines + 1, samples_lines),
};
let scroll_offset = if total_lines <= max_visible {
0
} else {
// Keep focused section in view (top-aligned when possible)
let focus_end = focus_start + focus_height;
if focus_end <= max_visible {
0
} else {
focus_start.min(total_lines.saturating_sub(max_visible))
}
};
let viewport_top = padded.y as i32;
let viewport_bottom = (padded.y + padded.height) as i32;
// Render each section at adjusted position
let mut y = viewport_top - scroll_offset as i32;
// Devices section
let devices_top = y;
let devices_bottom = y + devices_lines as i32;
if devices_bottom > viewport_top && devices_top < viewport_bottom {
let clipped_y = devices_top.max(viewport_top) as u16;
let clipped_height =
(devices_bottom.min(viewport_bottom) - devices_top.max(viewport_top)) as u16;
let devices_area = Rect {
x: padded.x,
y: clipped_y,
width: padded.width,
height: clipped_height,
};
render_devices(frame, app, devices_area);
}
y += devices_lines as i32 + 1; // +1 for blank line
// Settings section
let settings_top = y;
let settings_bottom = y + settings_lines as i32;
if settings_bottom > viewport_top && settings_top < viewport_bottom {
let clipped_y = settings_top.max(viewport_top) as u16;
let clipped_height =
(settings_bottom.min(viewport_bottom) - settings_top.max(viewport_top)) as u16;
let settings_area = Rect {
x: padded.x,
y: clipped_y,
width: padded.width,
height: clipped_height,
};
render_settings(frame, app, settings_area);
}
y += settings_lines as i32 + 1;
// Samples section
let samples_top = y;
let samples_bottom = y + samples_lines as i32;
if samples_bottom > viewport_top && samples_top < viewport_bottom {
let clipped_y = samples_top.max(viewport_top) as u16;
let clipped_height =
(samples_bottom.min(viewport_bottom) - samples_top.max(viewport_top)) as u16;
let samples_area = Rect {
x: padded.x,
y: clipped_y,
width: padded.width,
height: clipped_height,
};
render_samples(frame, app, samples_area);
}
// Scroll indicators
let indicator_style = Style::new().fg(SCROLL_INDICATOR_COLOR);
let indicator_x = padded.x + padded.width.saturating_sub(1);
if scroll_offset > 0 {
let up_indicator = Paragraph::new("").style(indicator_style);
frame.render_widget(up_indicator, Rect::new(indicator_x, padded.y, 1, 1));
}
if scroll_offset + max_visible < total_lines {
let down_indicator = Paragraph::new("").style(indicator_style);
frame.render_widget(
down_indicator,
Rect::new(indicator_x, padded.y + padded.height.saturating_sub(1), 1, 1),
);
}
}
fn render_visualizers(frame: &mut Frame, app: &App, area: Rect) {
@@ -241,6 +327,7 @@ fn render_settings(frame: &mut Frame, app: &App, area: Rect) {
let buffer_focused = section_focused && app.audio.setting_kind == SettingKind::BufferSize;
let polyphony_focused = section_focused && app.audio.setting_kind == SettingKind::Polyphony;
let nudge_focused = section_focused && app.audio.setting_kind == SettingKind::Nudge;
let lookahead_focused = section_focused && app.audio.setting_kind == SettingKind::Lookahead;
let nudge_ms = app.metrics.nudge_ms;
let nudge_label = if nudge_ms == 0.0 {
@@ -249,6 +336,12 @@ fn render_settings(frame: &mut Frame, app: &App, area: Rect) {
format!("{nudge_ms:+.1} ms")
};
let lookahead_label = if app.audio.config.lookahead_ms == 0 {
"off".to_string()
} else {
format!("{} ms", app.audio.config.lookahead_ms)
};
let rows = vec![
Row::new(vec![
Span::styled(
@@ -305,6 +398,17 @@ fn render_settings(frame: &mut Frame, app: &App, area: Rect) {
),
render_selector(&nudge_label, nudge_focused, highlight, normal),
]),
Row::new(vec![
Span::styled(
if lookahead_focused {
"> Lookahead"
} else {
" Lookahead"
},
label_style,
),
render_selector(&lookahead_label, lookahead_focused, highlight, normal),
]),
Row::new(vec![
Span::styled(" Sample rate", label_style),
Span::styled(