Fix layout
This commit is contained in:
@@ -1,65 +1,117 @@
|
||||
use ratatui::layout::{Alignment, Constraint, Layout, Rect};
|
||||
use ratatui::style::{Color, Modifier, Style};
|
||||
use ratatui::widgets::Paragraph;
|
||||
use ratatui::widgets::{Block, Borders, Paragraph};
|
||||
use ratatui::Frame;
|
||||
|
||||
use crate::app::App;
|
||||
use crate::engine::SequencerSnapshot;
|
||||
use crate::state::MainLayout;
|
||||
use crate::theme;
|
||||
use crate::widgets::{Orientation, Scope, Spectrum, VuMeter};
|
||||
use crate::widgets::{ActivePatterns, Orientation, Scope, Spectrum, VuMeter};
|
||||
|
||||
pub fn render(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, area: Rect) {
|
||||
let [left_area, _spacer, vu_area] = Layout::horizontal([
|
||||
let [patterns_area, _, main_area, _, vu_area] = Layout::horizontal([
|
||||
Constraint::Length(13),
|
||||
Constraint::Length(2),
|
||||
Constraint::Fill(1),
|
||||
Constraint::Length(2),
|
||||
Constraint::Length(8),
|
||||
Constraint::Length(10),
|
||||
])
|
||||
.areas(area);
|
||||
|
||||
let show_scope = app.audio.config.show_scope;
|
||||
let show_spectrum = app.audio.config.show_spectrum;
|
||||
let viz_height = if show_scope || show_spectrum { 14 } else { 0 };
|
||||
let has_viz = show_scope || show_spectrum;
|
||||
let layout = app.audio.config.layout;
|
||||
|
||||
let [viz_area, sequencer_area] = Layout::vertical([
|
||||
Constraint::Length(viz_height),
|
||||
Constraint::Fill(1),
|
||||
])
|
||||
.areas(left_area);
|
||||
let (viz_area, sequencer_area) = match layout {
|
||||
MainLayout::Top => {
|
||||
let viz_height = if has_viz { 16 } else { 0 };
|
||||
let [viz, seq] = Layout::vertical([
|
||||
Constraint::Length(viz_height),
|
||||
Constraint::Fill(1),
|
||||
])
|
||||
.areas(main_area);
|
||||
(viz, seq)
|
||||
}
|
||||
MainLayout::Bottom => {
|
||||
let viz_height = if has_viz { 16 } else { 0 };
|
||||
let [seq, viz] = Layout::vertical([
|
||||
Constraint::Fill(1),
|
||||
Constraint::Length(viz_height),
|
||||
])
|
||||
.areas(main_area);
|
||||
(viz, seq)
|
||||
}
|
||||
MainLayout::Left => {
|
||||
let viz_width = if has_viz { 33 } else { 0 };
|
||||
let [viz, _spacer, seq] = Layout::horizontal([
|
||||
Constraint::Percentage(viz_width),
|
||||
Constraint::Length(2),
|
||||
Constraint::Fill(1),
|
||||
])
|
||||
.areas(main_area);
|
||||
(viz, seq)
|
||||
}
|
||||
MainLayout::Right => {
|
||||
let viz_width = if has_viz { 33 } else { 0 };
|
||||
let [seq, _spacer, viz] = Layout::horizontal([
|
||||
Constraint::Fill(1),
|
||||
Constraint::Length(2),
|
||||
Constraint::Percentage(viz_width),
|
||||
])
|
||||
.areas(main_area);
|
||||
(viz, seq)
|
||||
}
|
||||
};
|
||||
|
||||
if show_scope && show_spectrum {
|
||||
let [scope_area, _, spectrum_area] = Layout::horizontal([
|
||||
Constraint::Percentage(50),
|
||||
Constraint::Length(2),
|
||||
Constraint::Percentage(50),
|
||||
])
|
||||
.areas(viz_area);
|
||||
render_scope(frame, app, scope_area);
|
||||
render_spectrum(frame, app, spectrum_area);
|
||||
} else if show_scope {
|
||||
render_scope(frame, app, viz_area);
|
||||
} else if show_spectrum {
|
||||
render_spectrum(frame, app, viz_area);
|
||||
if has_viz {
|
||||
render_viz_area(frame, app, viz_area, layout, show_scope, show_spectrum);
|
||||
}
|
||||
|
||||
render_sequencer(frame, app, snapshot, sequencer_area);
|
||||
render_vu_meter(frame, app, vu_area);
|
||||
render_active_patterns(frame, app, snapshot, patterns_area);
|
||||
}
|
||||
|
||||
// Calculate actual grid height to align VU meter
|
||||
let pattern = app.current_edit_pattern();
|
||||
let page = app.editor_ctx.step / STEPS_PER_PAGE;
|
||||
let page_start = page * STEPS_PER_PAGE;
|
||||
let steps_on_page = (page_start + STEPS_PER_PAGE).min(pattern.length) - page_start;
|
||||
let num_rows = steps_on_page.div_ceil(8);
|
||||
let spacing = num_rows.saturating_sub(1) as u16;
|
||||
let row_height = sequencer_area.height.saturating_sub(spacing) / num_rows as u16;
|
||||
let actual_grid_height = row_height * num_rows as u16 + spacing;
|
||||
fn render_viz_area(
|
||||
frame: &mut Frame,
|
||||
app: &App,
|
||||
area: Rect,
|
||||
layout: MainLayout,
|
||||
show_scope: bool,
|
||||
show_spectrum: bool,
|
||||
) {
|
||||
let is_vertical_layout = matches!(layout, MainLayout::Left | MainLayout::Right);
|
||||
|
||||
let aligned_vu_area = Rect {
|
||||
y: sequencer_area.y,
|
||||
height: actual_grid_height,
|
||||
..vu_area
|
||||
};
|
||||
|
||||
render_vu_meter(frame, app, aligned_vu_area);
|
||||
if show_scope && show_spectrum {
|
||||
if is_vertical_layout {
|
||||
let [scope_area, spectrum_area] = Layout::vertical([
|
||||
Constraint::Fill(1),
|
||||
Constraint::Fill(1),
|
||||
])
|
||||
.areas(area);
|
||||
render_scope(frame, app, scope_area, Orientation::Vertical);
|
||||
render_spectrum(frame, app, spectrum_area);
|
||||
} else {
|
||||
let [scope_area, spectrum_area] = Layout::horizontal([
|
||||
Constraint::Fill(1),
|
||||
Constraint::Fill(1),
|
||||
])
|
||||
.areas(area);
|
||||
render_scope(frame, app, scope_area, Orientation::Horizontal);
|
||||
render_spectrum(frame, app, spectrum_area);
|
||||
}
|
||||
} else if show_scope {
|
||||
let orientation = if is_vertical_layout {
|
||||
Orientation::Vertical
|
||||
} else {
|
||||
Orientation::Horizontal
|
||||
};
|
||||
render_scope(frame, app, area, orientation);
|
||||
} else if show_spectrum {
|
||||
render_spectrum(frame, app, area);
|
||||
}
|
||||
}
|
||||
|
||||
const STEPS_PER_PAGE: usize = 32;
|
||||
@@ -233,21 +285,65 @@ fn render_tile(
|
||||
}
|
||||
}
|
||||
|
||||
fn render_scope(frame: &mut Frame, app: &App, area: Rect) {
|
||||
fn render_scope(frame: &mut Frame, app: &App, area: Rect, orientation: Orientation) {
|
||||
let theme = theme::get();
|
||||
let block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_style(Style::new().fg(theme.ui.border));
|
||||
let inner = block.inner(area);
|
||||
frame.render_widget(block, area);
|
||||
|
||||
let scope = Scope::new(&app.metrics.scope)
|
||||
.orientation(Orientation::Horizontal)
|
||||
.orientation(orientation)
|
||||
.color(theme.meter.low);
|
||||
frame.render_widget(scope, area);
|
||||
frame.render_widget(scope, inner);
|
||||
}
|
||||
|
||||
fn render_spectrum(frame: &mut Frame, app: &App, area: Rect) {
|
||||
let area = Rect { height: area.height.saturating_sub(1), ..area };
|
||||
let theme = theme::get();
|
||||
let block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_style(Style::new().fg(theme.ui.border));
|
||||
let inner = block.inner(area);
|
||||
frame.render_widget(block, area);
|
||||
|
||||
let spectrum = Spectrum::new(&app.metrics.spectrum);
|
||||
frame.render_widget(spectrum, area);
|
||||
frame.render_widget(spectrum, inner);
|
||||
}
|
||||
|
||||
fn render_vu_meter(frame: &mut Frame, app: &App, area: Rect) {
|
||||
let theme = theme::get();
|
||||
let block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_style(Style::new().fg(theme.ui.border));
|
||||
let inner = block.inner(area);
|
||||
frame.render_widget(block, area);
|
||||
|
||||
let vu = VuMeter::new(app.metrics.peak_left, app.metrics.peak_right);
|
||||
frame.render_widget(vu, area);
|
||||
frame.render_widget(vu, inner);
|
||||
}
|
||||
|
||||
fn render_active_patterns(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, area: Rect) {
|
||||
let theme = theme::get();
|
||||
let block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_style(Style::new().fg(theme.ui.border));
|
||||
let inner = block.inner(area);
|
||||
frame.render_widget(block, area);
|
||||
|
||||
let patterns: Vec<(usize, usize, usize)> = snapshot
|
||||
.active_patterns
|
||||
.iter()
|
||||
.map(|p| (p.bank, p.pattern, p.iter))
|
||||
.collect();
|
||||
|
||||
let step_info = snapshot
|
||||
.get_step(app.editor_ctx.bank, app.editor_ctx.pattern)
|
||||
.map(|step| (step, app.current_edit_pattern().length));
|
||||
|
||||
let mut widget = ActivePatterns::new(&patterns);
|
||||
if let Some((step, total)) = step_info {
|
||||
widget = widget.with_step(step, total);
|
||||
}
|
||||
frame.render_widget(widget, inner);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ use ratatui::style::Style;
|
||||
use ratatui::text::{Line, Span};
|
||||
use ratatui::widgets::Paragraph;
|
||||
use ratatui::Frame;
|
||||
#[cfg(not(feature = "desktop"))]
|
||||
use tui_big_text::{BigText, PixelSize};
|
||||
|
||||
use crate::state::ui::UiState;
|
||||
@@ -16,13 +17,21 @@ pub fn render(frame: &mut Frame, area: Rect, ui: &UiState) {
|
||||
let link_style = Style::new().fg(theme.title.link);
|
||||
let license_style = Style::new().fg(theme.title.license);
|
||||
|
||||
#[cfg(not(feature = "desktop"))]
|
||||
let big_title = BigText::builder()
|
||||
.pixel_size(PixelSize::Quadrant)
|
||||
.pixel_size(PixelSize::Full)
|
||||
.style(Style::new().fg(theme.title.big_title).bold())
|
||||
.lines(vec!["CAGIRE".into()])
|
||||
.centered()
|
||||
.build();
|
||||
|
||||
#[cfg(feature = "desktop")]
|
||||
let big_title = Paragraph::new(Line::from(Span::styled(
|
||||
"CAGIRE",
|
||||
Style::new().fg(theme.title.big_title).bold(),
|
||||
)))
|
||||
.alignment(Alignment::Center);
|
||||
|
||||
let version_style = Style::new().fg(theme.title.subtitle);
|
||||
|
||||
let subtitle_lines = vec![
|
||||
@@ -49,21 +58,43 @@ pub fn render(frame: &mut Frame, area: Rect, ui: &UiState) {
|
||||
)),
|
||||
];
|
||||
|
||||
let big_text_height = 4;
|
||||
#[cfg(not(feature = "desktop"))]
|
||||
let big_text_height = 8;
|
||||
#[cfg(feature = "desktop")]
|
||||
let big_text_height = 1;
|
||||
let min_title_width = 30;
|
||||
let subtitle_height = subtitle_lines.len() as u16;
|
||||
let total_height = big_text_height + subtitle_height;
|
||||
let vertical_padding = area.height.saturating_sub(total_height) / 2;
|
||||
|
||||
let [_, title_area, subtitle_area, _] = Layout::vertical([
|
||||
Constraint::Length(vertical_padding),
|
||||
Constraint::Length(big_text_height),
|
||||
Constraint::Length(subtitle_height),
|
||||
Constraint::Fill(1),
|
||||
])
|
||||
.areas(area);
|
||||
let show_big_title =
|
||||
area.height >= (big_text_height + subtitle_height) && area.width >= min_title_width;
|
||||
|
||||
frame.render_widget(big_title, title_area);
|
||||
if show_big_title {
|
||||
let total_height = big_text_height + subtitle_height;
|
||||
let vertical_padding = area.height.saturating_sub(total_height) / 2;
|
||||
|
||||
let subtitle = Paragraph::new(subtitle_lines).alignment(Alignment::Center);
|
||||
frame.render_widget(subtitle, subtitle_area);
|
||||
let [_, title_area, subtitle_area, _] = Layout::vertical([
|
||||
Constraint::Length(vertical_padding),
|
||||
Constraint::Length(big_text_height),
|
||||
Constraint::Length(subtitle_height),
|
||||
Constraint::Fill(1),
|
||||
])
|
||||
.areas(area);
|
||||
|
||||
frame.render_widget(big_title, title_area);
|
||||
|
||||
let subtitle = Paragraph::new(subtitle_lines).alignment(Alignment::Center);
|
||||
frame.render_widget(subtitle, subtitle_area);
|
||||
} else {
|
||||
let vertical_padding = area.height.saturating_sub(subtitle_height) / 2;
|
||||
|
||||
let [_, subtitle_area, _] = Layout::vertical([
|
||||
Constraint::Length(vertical_padding),
|
||||
Constraint::Length(subtitle_height),
|
||||
Constraint::Fill(1),
|
||||
])
|
||||
.areas(area);
|
||||
|
||||
let subtitle = Paragraph::new(subtitle_lines).alignment(Alignment::Center);
|
||||
frame.render_widget(subtitle, subtitle_area);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user