Feat: saving screen during perfs
This commit is contained in:
@@ -13,40 +13,18 @@ use crate::widgets::{render_search_bar, CategoryItem, CategoryList, Selection};
|
||||
|
||||
use CatEntry::{Category, Section};
|
||||
|
||||
pub fn layout(area: Rect) -> (Rect, [Rect; 2]) {
|
||||
let [header_area, body_area] =
|
||||
Layout::vertical([Constraint::Length(5), Constraint::Fill(1)]).areas(area);
|
||||
let body = Layout::horizontal([Constraint::Length(16), Constraint::Fill(1)]).areas(body_area);
|
||||
(header_area, body)
|
||||
pub fn layout(area: Rect) -> [Rect; 2] {
|
||||
Layout::horizontal([Constraint::Length(16), Constraint::Fill(1)]).areas(area)
|
||||
}
|
||||
|
||||
pub fn render(frame: &mut Frame, app: &App, area: Rect) {
|
||||
let (header_area, [cat_area, words_area]) = layout(area);
|
||||
|
||||
render_header(frame, header_area);
|
||||
let [cat_area, words_area] = layout(area);
|
||||
|
||||
let is_searching = !app.ui.dict_search_query.is_empty();
|
||||
render_categories(frame, app, cat_area, is_searching);
|
||||
render_words(frame, app, words_area, is_searching);
|
||||
}
|
||||
|
||||
fn render_header(frame: &mut Frame, area: Rect) {
|
||||
use ratatui::widgets::Wrap;
|
||||
let theme = theme::get();
|
||||
let desc = "Forth uses a stack: values are pushed, functions (called words) consume and \
|
||||
produce values. Read left to right: 3 4 + -> push 3, push 4, + pops both, \
|
||||
pushes 7. This page lists all words with their signature ( inputs -- outputs ).";
|
||||
let block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_style(Style::new().fg(theme.dict.border_normal))
|
||||
.title("Dictionary");
|
||||
let para = Paragraph::new(desc)
|
||||
.style(Style::new().fg(theme.dict.header_desc))
|
||||
.wrap(Wrap { trim: false })
|
||||
.block(block);
|
||||
frame.render_widget(para, area);
|
||||
}
|
||||
|
||||
fn render_categories(frame: &mut Frame, app: &App, area: Rect, dimmed: bool) {
|
||||
let theme = theme::get();
|
||||
let focused = app.ui.dict_focus == DictFocus::Categories && !dimmed;
|
||||
|
||||
@@ -13,12 +13,10 @@ use crate::state::MainLayout;
|
||||
use crate::theme;
|
||||
use crate::views::highlight::highlight_line_with_runtime;
|
||||
use crate::views::render::{adjust_resolved_for_line, adjust_spans_for_line};
|
||||
use crate::widgets::{ActivePatterns, Orientation, Scope, Spectrum, VuMeter};
|
||||
use crate::widgets::{Orientation, Scope, Spectrum, VuMeter};
|
||||
|
||||
pub fn layout(area: Rect) -> [Rect; 5] {
|
||||
pub fn layout(area: Rect) -> [Rect; 3] {
|
||||
Layout::horizontal([
|
||||
Constraint::Length(13),
|
||||
Constraint::Length(2),
|
||||
Constraint::Fill(1),
|
||||
Constraint::Length(2),
|
||||
Constraint::Length(10),
|
||||
@@ -27,7 +25,7 @@ pub fn layout(area: Rect) -> [Rect; 5] {
|
||||
}
|
||||
|
||||
pub fn render(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, area: Rect) {
|
||||
let [patterns_area, _, main_area, _, vu_area] = layout(area);
|
||||
let [main_area, _, vu_area] = layout(area);
|
||||
|
||||
let show_scope = app.audio.config.show_scope;
|
||||
let show_spectrum = app.audio.config.show_spectrum;
|
||||
@@ -82,7 +80,6 @@ pub fn render(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, area:
|
||||
|
||||
render_sequencer(frame, app, snapshot, sequencer_area);
|
||||
render_vu_meter(frame, app, vu_area);
|
||||
render_active_patterns(frame, app, snapshot, patterns_area);
|
||||
}
|
||||
|
||||
enum VizPanel {
|
||||
@@ -428,45 +425,3 @@ fn render_vu_meter(frame: &mut Frame, app: &App, area: Rect) {
|
||||
frame.render_widget(vu, inner);
|
||||
}
|
||||
|
||||
fn render_active_patterns(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, area: Rect) {
|
||||
use crate::widgets::MuteStatus;
|
||||
|
||||
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 mute_status: Vec<MuteStatus> = snapshot
|
||||
.active_patterns
|
||||
.iter()
|
||||
.map(|p| {
|
||||
if app.mute.is_soloed(p.bank, p.pattern) {
|
||||
MuteStatus::Soloed
|
||||
} else if app.mute.is_muted(p.bank, p.pattern) {
|
||||
MuteStatus::Muted
|
||||
} else if app.mute.is_effectively_muted(p.bank, p.pattern) {
|
||||
MuteStatus::EffectivelyMuted
|
||||
} else {
|
||||
MuteStatus::Normal
|
||||
}
|
||||
})
|
||||
.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).with_mute_status(&mute_status);
|
||||
if let Some((step, total)) = step_info {
|
||||
widget = widget.with_step(step, total);
|
||||
}
|
||||
frame.render_widget(widget, inner);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::app::App;
|
||||
use crate::engine::LinkState;
|
||||
use crate::midi;
|
||||
use crate::state::OptionsFocus;
|
||||
use crate::theme;
|
||||
use crate::theme::{self, ThemeColors};
|
||||
use crate::widgets::{render_scroll_indicators, IndicatorAlign};
|
||||
|
||||
pub fn render(frame: &mut Frame, app: &App, link: &LinkState, area: Rect) {
|
||||
@@ -90,6 +90,12 @@ pub fn render(frame: &mut Frame, app: &App, link: &LinkState, area: Rect) {
|
||||
focus == OptionsFocus::ShowPreview,
|
||||
&theme,
|
||||
),
|
||||
render_option_line(
|
||||
"Performance mode",
|
||||
if app.ui.performance_mode { "On" } else { "Off" },
|
||||
focus == OptionsFocus::PerformanceMode,
|
||||
&theme,
|
||||
),
|
||||
];
|
||||
if app.plugin_mode {
|
||||
let zoom_str = format!("{:.0}%", app.ui.zoom_factor * 100.0);
|
||||
@@ -246,6 +252,14 @@ pub fn render(frame: &mut Frame, app: &App, link: &LinkState, area: Rect) {
|
||||
]);
|
||||
}
|
||||
|
||||
// Insert description below focused option
|
||||
let focus_vec_idx = focus.line_index(app.plugin_mode);
|
||||
if let Some(desc) = option_description(focus) {
|
||||
if focus_vec_idx < lines.len() {
|
||||
lines.insert(focus_vec_idx + 1, render_description_line(desc, &theme));
|
||||
}
|
||||
}
|
||||
|
||||
let total_lines = lines.len();
|
||||
let max_visible = padded.height as usize;
|
||||
|
||||
@@ -318,6 +332,42 @@ fn render_option_line(label: &str, value: &str, focused: bool, theme: &theme::Th
|
||||
])
|
||||
}
|
||||
|
||||
fn option_description(focus: OptionsFocus) -> Option<&'static str> {
|
||||
match focus {
|
||||
OptionsFocus::ColorScheme => Some("Color scheme for the entire interface"),
|
||||
OptionsFocus::HueRotation => Some("Shift all theme colors by a hue angle"),
|
||||
OptionsFocus::RefreshRate => Some("Lower values reduce CPU usage"),
|
||||
OptionsFocus::RuntimeHighlight => Some("Highlight executed code spans during playback"),
|
||||
OptionsFocus::ShowScope => Some("Oscilloscope on the engine page"),
|
||||
OptionsFocus::ShowSpectrum => Some("Spectrum analyzer on the engine page"),
|
||||
OptionsFocus::ShowCompletion => Some("Word completion popup in the editor"),
|
||||
OptionsFocus::ShowPreview => Some("Step script preview on the sequencer grid"),
|
||||
OptionsFocus::PerformanceMode => Some("Hide header and footer bars"),
|
||||
OptionsFocus::Font => Some("Bitmap font for the plugin window"),
|
||||
OptionsFocus::ZoomFactor => Some("Scale factor for the plugin window"),
|
||||
OptionsFocus::WindowSize => Some("Default size for the plugin window"),
|
||||
OptionsFocus::LinkEnabled => Some("Join an Ableton Link session on the local network"),
|
||||
OptionsFocus::StartStopSync => Some("Sync transport start/stop with other Link peers"),
|
||||
OptionsFocus::Quantum => Some("Number of beats per phase cycle"),
|
||||
OptionsFocus::MidiOutput0 => Some("MIDI output device for channel group 1"),
|
||||
OptionsFocus::MidiOutput1 => Some("MIDI output device for channel group 2"),
|
||||
OptionsFocus::MidiOutput2 => Some("MIDI output device for channel group 3"),
|
||||
OptionsFocus::MidiOutput3 => Some("MIDI output device for channel group 4"),
|
||||
OptionsFocus::MidiInput0 => Some("MIDI input device for channel group 1"),
|
||||
OptionsFocus::MidiInput1 => Some("MIDI input device for channel group 2"),
|
||||
OptionsFocus::MidiInput2 => Some("MIDI input device for channel group 3"),
|
||||
OptionsFocus::MidiInput3 => Some("MIDI input device for channel group 4"),
|
||||
OptionsFocus::ResetOnboarding => Some("Re-enable all dismissed guide popups"),
|
||||
}
|
||||
}
|
||||
|
||||
fn render_description_line(desc: &str, theme: &ThemeColors) -> Line<'static> {
|
||||
Line::from(Span::styled(
|
||||
format!(" {desc}"),
|
||||
Style::new().fg(theme.ui.text_dim),
|
||||
))
|
||||
}
|
||||
|
||||
fn render_readonly_line(label: &str, value: &str, value_style: Style, theme: &theme::ThemeColors) -> Line<'static> {
|
||||
let label_style = Style::new().fg(theme.ui.text_muted);
|
||||
let label_width = 20;
|
||||
|
||||
@@ -97,16 +97,20 @@ pub fn render(
|
||||
height: term.height.saturating_sub(2),
|
||||
};
|
||||
|
||||
let perf = app.ui.performance_mode;
|
||||
|
||||
let [header_area, _padding, body_area, _bottom_padding, footer_area] = Layout::vertical([
|
||||
Constraint::Length(header_height(padded.width)),
|
||||
Constraint::Length(1),
|
||||
Constraint::Length(if perf { 0 } else { header_height(padded.width) }),
|
||||
Constraint::Length(if perf { 0 } else { 1 }),
|
||||
Constraint::Fill(1),
|
||||
Constraint::Length(1),
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(if perf { 0 } else { 1 }),
|
||||
Constraint::Length(if perf { 0 } else { 3 }),
|
||||
])
|
||||
.areas(padded);
|
||||
|
||||
render_header(frame, app, link, snapshot, header_area);
|
||||
if !perf {
|
||||
render_header(frame, app, link, snapshot, header_area);
|
||||
}
|
||||
|
||||
let (page_area, panel_area) = if app.panel.visible && app.panel.side.is_some() {
|
||||
if body_area.width >= 120 {
|
||||
@@ -139,7 +143,9 @@ pub fn render(
|
||||
render_side_panel(frame, app, side_area);
|
||||
}
|
||||
|
||||
render_footer(frame, app, footer_area);
|
||||
if !perf {
|
||||
render_footer(frame, app, footer_area);
|
||||
}
|
||||
let modal_area = render_modal(frame, app, snapshot, term);
|
||||
|
||||
if app.ui.show_minimap() {
|
||||
|
||||
Reference in New Issue
Block a user