WIP: clap
This commit is contained in:
@@ -46,24 +46,31 @@ fn render_settings_section(frame: &mut Frame, app: &App, area: Rect) {
|
||||
};
|
||||
|
||||
// Calculate section heights
|
||||
let devices_lines = devices_section_height(app) as usize;
|
||||
let settings_lines: usize = 8; // header(1) + divider(1) + 6 rows
|
||||
let plugin_mode = app.plugin_mode;
|
||||
let devices_lines = if plugin_mode {
|
||||
0
|
||||
} else {
|
||||
devices_section_height(app) as usize
|
||||
};
|
||||
let settings_lines: usize = if plugin_mode { 5 } else { 8 }; // plugin: header(1) + divider(1) + 3 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 sections_gap = if plugin_mode { 1 } else { 2 }; // 1 gap without devices, 2 gaps with
|
||||
let total_lines = devices_lines + settings_lines + samples_lines + sections_gap;
|
||||
|
||||
let max_visible = padded.height as usize;
|
||||
|
||||
// Calculate scroll offset based on focused section
|
||||
let settings_start = if plugin_mode { 0 } else { devices_lines + 1 };
|
||||
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),
|
||||
EngineSection::Settings => (settings_start, settings_lines),
|
||||
EngineSection::Samples => (settings_start + 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
|
||||
@@ -75,25 +82,26 @@ fn render_settings_section(frame: &mut Frame, app: &App, area: Rect) {
|
||||
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);
|
||||
// Devices section (skip in plugin mode)
|
||||
if !plugin_mode {
|
||||
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;
|
||||
}
|
||||
y += devices_lines as i32 + 1; // +1 for blank line
|
||||
|
||||
// Settings section
|
||||
let settings_top = y;
|
||||
@@ -310,8 +318,6 @@ fn render_settings(frame: &mut Frame, app: &App, area: Rect) {
|
||||
let label_style = Style::new().fg(theme.engine.label);
|
||||
let value_style = Style::new().fg(theme.engine.value);
|
||||
|
||||
let channels_focused = section_focused && app.audio.setting_kind == SettingKind::Channels;
|
||||
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 nudge_ms = app.metrics.nudge_ms;
|
||||
@@ -321,8 +327,15 @@ fn render_settings(frame: &mut Frame, app: &App, area: Rect) {
|
||||
format!("{nudge_ms:+.1} ms")
|
||||
};
|
||||
|
||||
let rows = vec![
|
||||
Row::new(vec![
|
||||
let mut rows = Vec::new();
|
||||
|
||||
if !app.plugin_mode {
|
||||
let channels_focused =
|
||||
section_focused && app.audio.setting_kind == SettingKind::Channels;
|
||||
let buffer_focused =
|
||||
section_focused && app.audio.setting_kind == SettingKind::BufferSize;
|
||||
|
||||
rows.push(Row::new(vec![
|
||||
Span::styled(
|
||||
if channels_focused {
|
||||
"> Channels"
|
||||
@@ -337,8 +350,8 @@ fn render_settings(frame: &mut Frame, app: &App, area: Rect) {
|
||||
highlight,
|
||||
normal,
|
||||
),
|
||||
]),
|
||||
Row::new(vec![
|
||||
]));
|
||||
rows.push(Row::new(vec![
|
||||
Span::styled(
|
||||
if buffer_focused {
|
||||
"> Buffer"
|
||||
@@ -357,38 +370,42 @@ fn render_settings(frame: &mut Frame, app: &App, area: Rect) {
|
||||
highlight,
|
||||
normal,
|
||||
),
|
||||
]),
|
||||
Row::new(vec![
|
||||
Span::styled(
|
||||
if polyphony_focused {
|
||||
"> Voices"
|
||||
} else {
|
||||
" Voices"
|
||||
},
|
||||
label_style,
|
||||
),
|
||||
render_selector(
|
||||
&format!("{}", app.audio.config.max_voices),
|
||||
polyphony_focused,
|
||||
highlight,
|
||||
normal,
|
||||
),
|
||||
]),
|
||||
Row::new(vec![
|
||||
Span::styled(
|
||||
if nudge_focused { "> Nudge" } else { " Nudge" },
|
||||
label_style,
|
||||
),
|
||||
render_selector(&nudge_label, nudge_focused, highlight, normal),
|
||||
]),
|
||||
Row::new(vec![
|
||||
Span::styled(" Sample rate", label_style),
|
||||
Span::styled(
|
||||
format!("{:.0} Hz", app.audio.config.sample_rate),
|
||||
value_style,
|
||||
),
|
||||
]),
|
||||
Row::new(vec![
|
||||
]));
|
||||
}
|
||||
|
||||
rows.push(Row::new(vec![
|
||||
Span::styled(
|
||||
if polyphony_focused {
|
||||
"> Voices"
|
||||
} else {
|
||||
" Voices"
|
||||
},
|
||||
label_style,
|
||||
),
|
||||
render_selector(
|
||||
&format!("{}", app.audio.config.max_voices),
|
||||
polyphony_focused,
|
||||
highlight,
|
||||
normal,
|
||||
),
|
||||
]));
|
||||
rows.push(Row::new(vec![
|
||||
Span::styled(
|
||||
if nudge_focused { "> Nudge" } else { " Nudge" },
|
||||
label_style,
|
||||
),
|
||||
render_selector(&nudge_label, nudge_focused, highlight, normal),
|
||||
]));
|
||||
rows.push(Row::new(vec![
|
||||
Span::styled(" Sample rate", label_style),
|
||||
Span::styled(
|
||||
format!("{:.0} Hz", app.audio.config.sample_rate),
|
||||
value_style,
|
||||
),
|
||||
]));
|
||||
|
||||
if !app.plugin_mode {
|
||||
rows.push(Row::new(vec![
|
||||
Span::styled(" Audio host", label_style),
|
||||
Span::styled(
|
||||
if app.audio.config.host_name.is_empty() {
|
||||
@@ -398,8 +415,8 @@ fn render_settings(frame: &mut Frame, app: &App, area: Rect) {
|
||||
},
|
||||
value_style,
|
||||
),
|
||||
]),
|
||||
];
|
||||
]));
|
||||
}
|
||||
|
||||
let table = Table::new(rows, [Constraint::Length(14), Constraint::Fill(1)]);
|
||||
frame.render_widget(table, content_area);
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
use crate::page::Page;
|
||||
|
||||
pub fn bindings_for(page: Page) -> Vec<(&'static str, &'static str, &'static str)> {
|
||||
pub fn bindings_for(page: Page, plugin_mode: bool) -> Vec<(&'static str, &'static str, &'static str)> {
|
||||
let mut bindings = vec![
|
||||
("F1–F6", "Go to view", "Dict/Patterns/Options/Help/Sequencer/Engine"),
|
||||
("Ctrl+←→↑↓", "Navigate", "Switch between adjacent views"),
|
||||
("q", "Quit", "Quit application"),
|
||||
];
|
||||
if !plugin_mode {
|
||||
bindings.push(("q", "Quit", "Quit application"));
|
||||
}
|
||||
bindings.extend([
|
||||
("s", "Save", "Save project"),
|
||||
("l", "Load", "Load project"),
|
||||
("?", "Keybindings", "Show this help"),
|
||||
];
|
||||
]);
|
||||
|
||||
// Page-specific bindings
|
||||
match page {
|
||||
|
||||
@@ -78,7 +78,7 @@ pub fn render(
|
||||
frame.render_widget(Block::new().style(Style::default().bg(bg_color)), term);
|
||||
|
||||
if app.ui.show_title {
|
||||
title_view::render(frame, term, &app.ui);
|
||||
title_view::render(frame, term, &app.ui, app.plugin_mode);
|
||||
|
||||
let mut fx = app.ui.title_fx.borrow_mut();
|
||||
if let Some(effect) = fx.as_mut() {
|
||||
@@ -1036,7 +1036,7 @@ fn render_modal_keybindings(frame: &mut Frame, app: &App, scroll: usize, term: R
|
||||
.border_color(theme.modal.editor)
|
||||
.render_centered(frame, term);
|
||||
|
||||
let bindings = super::keybindings::bindings_for(app.page);
|
||||
let bindings = super::keybindings::bindings_for(app.page, app.plugin_mode);
|
||||
let visible_rows = inner.height.saturating_sub(2) as usize;
|
||||
|
||||
let rows: Vec<Row> = bindings
|
||||
|
||||
@@ -8,7 +8,7 @@ use tui_big_text::{BigText, PixelSize};
|
||||
use crate::state::ui::UiState;
|
||||
use crate::theme;
|
||||
|
||||
pub fn render(frame: &mut Frame, area: Rect, ui: &UiState) {
|
||||
pub fn render(frame: &mut Frame, area: Rect, ui: &UiState, plugin_mode: bool) {
|
||||
let theme = theme::get();
|
||||
frame.render_widget(&ui.sparkles, area);
|
||||
|
||||
@@ -39,15 +39,17 @@ pub fn render(frame: &mut Frame, area: Rect, ui: &UiState) {
|
||||
Line::from(Span::styled("AGPL-3.0", license_style)),
|
||||
];
|
||||
|
||||
let keybindings = [
|
||||
let mut keybindings = vec![
|
||||
("Ctrl+Arrows", "Navigate Views"),
|
||||
("Enter", "Edit Step"),
|
||||
("Space", "Play/Stop"),
|
||||
("s", "Save"),
|
||||
("l", "Load"),
|
||||
("q", "Quit"),
|
||||
("?", "Keybindings"),
|
||||
];
|
||||
if !plugin_mode {
|
||||
keybindings.push(("q", "Quit"));
|
||||
}
|
||||
keybindings.push(("?", "Keybindings"));
|
||||
|
||||
let key_style = Style::new().fg(theme.modal.confirm);
|
||||
let desc_style = Style::new().fg(theme.ui.text_primary);
|
||||
|
||||
Reference in New Issue
Block a user