Feat: optimizations
This commit is contained in:
56
src/app.rs
56
src/app.rs
@@ -419,44 +419,16 @@ impl App {
|
||||
.unwrap_or_default();
|
||||
|
||||
if script.trim().is_empty() {
|
||||
if let Some(step) = self
|
||||
.project_state
|
||||
.project
|
||||
.pattern_at_mut(bank, pattern)
|
||||
.step_mut(step_idx)
|
||||
{
|
||||
step.command = None;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let ctx = self.create_step_context(step_idx, link);
|
||||
|
||||
match self.script_engine.evaluate(&script, &ctx) {
|
||||
Ok(cmds) => {
|
||||
if let Some(step) = self
|
||||
.project_state
|
||||
.project
|
||||
.pattern_at_mut(bank, pattern)
|
||||
.step_mut(step_idx)
|
||||
{
|
||||
step.command = if cmds.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(cmds.join("\n"))
|
||||
};
|
||||
}
|
||||
Ok(_) => {
|
||||
self.ui.flash("Script compiled", 150, FlashKind::Info);
|
||||
}
|
||||
Err(e) => {
|
||||
if let Some(step) = self
|
||||
.project_state
|
||||
.project
|
||||
.pattern_at_mut(bank, pattern)
|
||||
.step_mut(step_idx)
|
||||
{
|
||||
step.command = None;
|
||||
}
|
||||
self.ui
|
||||
.flash(&format!("Script error: {e}"), 300, FlashKind::Error);
|
||||
}
|
||||
@@ -477,33 +449,11 @@ impl App {
|
||||
.unwrap_or_default();
|
||||
|
||||
if script.trim().is_empty() {
|
||||
if let Some(step) = self
|
||||
.project_state
|
||||
.project
|
||||
.pattern_at_mut(bank, pattern)
|
||||
.step_mut(step_idx)
|
||||
{
|
||||
step.command = None;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let ctx = self.create_step_context(step_idx, link);
|
||||
|
||||
if let Ok(cmds) = self.script_engine.evaluate(&script, &ctx) {
|
||||
if let Some(step) = self
|
||||
.project_state
|
||||
.project
|
||||
.pattern_at_mut(bank, pattern)
|
||||
.step_mut(step_idx)
|
||||
{
|
||||
step.command = if cmds.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(cmds.join("\n"))
|
||||
};
|
||||
}
|
||||
}
|
||||
let _ = self.script_engine.evaluate(&script, &ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1054,7 +1004,7 @@ impl App {
|
||||
[self.editor_ctx.pattern];
|
||||
if let Some(source) = pattern.step(self.editor_ctx.step).and_then(|s| s.source)
|
||||
{
|
||||
self.editor_ctx.step = source;
|
||||
self.editor_ctx.step = source as usize;
|
||||
}
|
||||
self.load_step_to_editor();
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ pub struct PatternSnapshot {
|
||||
pub struct StepSnapshot {
|
||||
pub active: bool,
|
||||
pub script: String,
|
||||
pub source: Option<usize>,
|
||||
pub source: Option<u8>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default, Debug)]
|
||||
@@ -392,7 +392,7 @@ impl PatternSnapshot {
|
||||
for _ in 0..self.steps.len() {
|
||||
if let Some(step) = self.steps.get(current) {
|
||||
if let Some(source) = step.source {
|
||||
current = source;
|
||||
current = source as usize;
|
||||
} else {
|
||||
return current;
|
||||
}
|
||||
@@ -500,30 +500,32 @@ fn parse_chain_target(s: &str) -> Option<PatternId> {
|
||||
})
|
||||
}
|
||||
|
||||
struct KeyCache {
|
||||
speed_keys: [[String; MAX_PATTERNS]; MAX_BANKS],
|
||||
chain_keys: [[String; MAX_PATTERNS]; MAX_BANKS],
|
||||
struct KeyBuf {
|
||||
speed: String,
|
||||
chain: String,
|
||||
}
|
||||
|
||||
impl KeyCache {
|
||||
impl KeyBuf {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
speed_keys: std::array::from_fn(|bank| {
|
||||
std::array::from_fn(|pattern| format!("__speed_{bank}_{pattern}__"))
|
||||
}),
|
||||
chain_keys: std::array::from_fn(|bank| {
|
||||
std::array::from_fn(|pattern| format!("__chain_{bank}_{pattern}__"))
|
||||
}),
|
||||
speed: String::with_capacity(24),
|
||||
chain: String::with_capacity(24),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn speed_key(&self, bank: usize, pattern: usize) -> &str {
|
||||
&self.speed_keys[bank][pattern]
|
||||
}
|
||||
fn format_speed_key(buf: &mut String, bank: usize, pattern: usize) -> &str {
|
||||
use std::fmt::Write;
|
||||
buf.clear();
|
||||
write!(buf, "__speed_{bank}_{pattern}__").unwrap();
|
||||
buf
|
||||
}
|
||||
|
||||
fn chain_key(&self, bank: usize, pattern: usize) -> &str {
|
||||
&self.chain_keys[bank][pattern]
|
||||
}
|
||||
fn format_chain_key(buf: &mut String, bank: usize, pattern: usize) -> &str {
|
||||
use std::fmt::Write;
|
||||
buf.clear();
|
||||
write!(buf, "__chain_{bank}_{pattern}__").unwrap();
|
||||
buf
|
||||
}
|
||||
|
||||
pub(crate) struct SequencerState {
|
||||
@@ -537,7 +539,7 @@ pub(crate) struct SequencerState {
|
||||
variables: Variables,
|
||||
dict: Dictionary,
|
||||
speed_overrides: HashMap<(usize, usize), f64>,
|
||||
key_cache: KeyCache,
|
||||
key_buf: KeyBuf,
|
||||
buf_audio_commands: Vec<TimestampedCommand>,
|
||||
buf_activated: Vec<PatternId>,
|
||||
buf_stopped: Vec<PatternId>,
|
||||
@@ -566,7 +568,7 @@ impl SequencerState {
|
||||
variables,
|
||||
dict,
|
||||
speed_overrides: HashMap::with_capacity(MAX_PATTERNS),
|
||||
key_cache: KeyCache::new(),
|
||||
key_buf: KeyBuf::new(),
|
||||
buf_audio_commands: Vec::with_capacity(32),
|
||||
buf_activated: Vec::with_capacity(16),
|
||||
buf_stopped: Vec::with_capacity(16),
|
||||
@@ -834,7 +836,7 @@ impl SequencerState {
|
||||
{
|
||||
let vars = self.variables.load();
|
||||
for id in self.audio_state.active_patterns.keys() {
|
||||
let key = self.key_cache.speed_key(id.bank, id.pattern);
|
||||
let key = format_speed_key(&mut self.key_buf.speed, id.bank, id.pattern);
|
||||
if let Some(v) = vars.get(key).and_then(|v: &Value| v.as_float().ok()) {
|
||||
self.speed_overrides.insert((id.bank, id.pattern), v);
|
||||
}
|
||||
@@ -884,6 +886,8 @@ impl SequencerState {
|
||||
active.pattern,
|
||||
source_idx,
|
||||
);
|
||||
let speed_key = format_speed_key(&mut self.key_buf.speed, active.bank, active.pattern);
|
||||
let chain_key = format_chain_key(&mut self.key_buf.chain, active.bank, active.pattern);
|
||||
let ctx = StepContext {
|
||||
step: step_idx,
|
||||
beat: step_beat,
|
||||
@@ -897,9 +901,9 @@ impl SequencerState {
|
||||
speed: speed_mult,
|
||||
fill,
|
||||
nudge_secs,
|
||||
cc_access: self.cc_access.clone(),
|
||||
speed_key: self.key_cache.speed_key(active.bank, active.pattern),
|
||||
chain_key: self.key_cache.chain_key(active.bank, active.pattern),
|
||||
cc_access: self.cc_access.as_deref(),
|
||||
speed_key,
|
||||
chain_key,
|
||||
#[cfg(feature = "desktop")]
|
||||
mouse_x,
|
||||
#[cfg(feature = "desktop")]
|
||||
@@ -970,8 +974,9 @@ impl SequencerState {
|
||||
.and_then(|v: &Value| v.as_float().ok());
|
||||
|
||||
let mut chain_transitions = Vec::new();
|
||||
let mut buf = String::with_capacity(24);
|
||||
for id in completed {
|
||||
let chain_key = self.key_cache.chain_key(id.bank, id.pattern);
|
||||
let chain_key = format_chain_key(&mut buf, id.bank, id.pattern);
|
||||
if let Some(Value::Str(s, _)) = vars.get(chain_key) {
|
||||
if let Some(target) = parse_chain_target(s) {
|
||||
chain_transitions.push((*id, target));
|
||||
@@ -980,24 +985,24 @@ impl SequencerState {
|
||||
}
|
||||
|
||||
// Remove consumed variables (tempo and chain keys)
|
||||
let needs_removal = new_tempo.is_some()
|
||||
|| completed.iter().any(|id| {
|
||||
let chain_key = self.key_cache.chain_key(id.bank, id.pattern);
|
||||
vars.contains_key(chain_key)
|
||||
})
|
||||
|| stopped.iter().any(|id| {
|
||||
let chain_key = self.key_cache.chain_key(id.bank, id.pattern);
|
||||
vars.contains_key(chain_key)
|
||||
});
|
||||
let mut needs_removal = new_tempo.is_some();
|
||||
if !needs_removal {
|
||||
for id in completed.iter().chain(stopped.iter()) {
|
||||
if vars.contains_key(format_chain_key(&mut buf, id.bank, id.pattern)) {
|
||||
needs_removal = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if needs_removal {
|
||||
let mut new_vars = (**vars).clone();
|
||||
new_vars.remove("__tempo__");
|
||||
for id in completed {
|
||||
new_vars.remove(self.key_cache.chain_key(id.bank, id.pattern));
|
||||
new_vars.remove(format_chain_key(&mut buf, id.bank, id.pattern));
|
||||
}
|
||||
for id in stopped {
|
||||
new_vars.remove(self.key_cache.chain_key(id.bank, id.pattern));
|
||||
new_vars.remove(format_chain_key(&mut buf, id.bank, id.pattern));
|
||||
}
|
||||
self.variables.store(Arc::new(new_vars));
|
||||
}
|
||||
|
||||
@@ -979,7 +979,7 @@ fn handle_main_page(ctx: &mut InputContext, key: KeyEvent, ctrl: bool) -> InputR
|
||||
.map(|p| p.display().to_string())
|
||||
.unwrap_or_default();
|
||||
let state = FileBrowserState::new_save(initial);
|
||||
ctx.dispatch(AppCommand::OpenModal(Modal::FileBrowser(state)));
|
||||
ctx.dispatch(AppCommand::OpenModal(Modal::FileBrowser(Box::new(state))));
|
||||
}
|
||||
KeyCode::Char('c') if ctrl => {
|
||||
ctx.dispatch(AppCommand::CopySteps);
|
||||
@@ -1011,7 +1011,7 @@ fn handle_main_page(ctx: &mut InputContext, key: KeyEvent, ctrl: bool) -> InputR
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let state = FileBrowserState::new_load(default_dir);
|
||||
ctx.dispatch(AppCommand::OpenModal(Modal::FileBrowser(state)));
|
||||
ctx.dispatch(AppCommand::OpenModal(Modal::FileBrowser(Box::new(state))));
|
||||
}
|
||||
KeyCode::Char('+') | KeyCode::Char('=') => ctx.dispatch(AppCommand::TempoUp),
|
||||
KeyCode::Char('-') => ctx.dispatch(AppCommand::TempoDown),
|
||||
@@ -1422,7 +1422,7 @@ fn handle_engine_page(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
|
||||
KeyCode::Char('A') => {
|
||||
use crate::state::file_browser::FileBrowserState;
|
||||
let state = FileBrowserState::new_load(String::new());
|
||||
ctx.dispatch(AppCommand::OpenModal(Modal::AddSamplePath(state)));
|
||||
ctx.dispatch(AppCommand::OpenModal(Modal::AddSamplePath(Box::new(state))));
|
||||
}
|
||||
KeyCode::Char('D') => {
|
||||
if ctx.app.audio.section == EngineSection::Samples {
|
||||
|
||||
@@ -95,7 +95,6 @@ pub fn paste_steps(
|
||||
step.name = data.name.clone();
|
||||
if source.is_some() {
|
||||
step.script.clear();
|
||||
step.command = None;
|
||||
} else {
|
||||
step.script = data.script.clone();
|
||||
}
|
||||
@@ -130,15 +129,14 @@ pub fn link_paste_steps(
|
||||
let source_idx = if data.source.is_some() {
|
||||
data.source
|
||||
} else {
|
||||
Some(data.original_index)
|
||||
Some(data.original_index as u8)
|
||||
};
|
||||
if source_idx == Some(target) {
|
||||
if source_idx == Some(target as u8) {
|
||||
continue;
|
||||
}
|
||||
if let Some(step) = project.pattern_at_mut(bank, pattern).step_mut(target) {
|
||||
step.source = source_idx;
|
||||
step.script.clear();
|
||||
step.command = None;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +181,7 @@ pub fn duplicate_steps(
|
||||
let pat_len = pat.length;
|
||||
let paste_at = *indices.last().unwrap() + 1;
|
||||
|
||||
let dupe_data: Vec<(bool, String, Option<usize>)> = indices
|
||||
let dupe_data: Vec<(bool, String, Option<u8>)> = indices
|
||||
.iter()
|
||||
.filter_map(|&idx| {
|
||||
let step = pat.step(idx)?;
|
||||
@@ -204,10 +202,8 @@ pub fn duplicate_steps(
|
||||
step.source = source;
|
||||
if source.is_some() {
|
||||
step.script.clear();
|
||||
step.command = None;
|
||||
} else {
|
||||
step.script = script;
|
||||
step.command = None;
|
||||
}
|
||||
}
|
||||
compile_targets.push(target);
|
||||
|
||||
@@ -44,9 +44,8 @@ pub fn apply_distribution(
|
||||
}
|
||||
|
||||
if let Some(step) = project.pattern_at_mut(bank, pattern).step_mut(target) {
|
||||
step.source = Some(source_step);
|
||||
step.source = Some(source_step as u8);
|
||||
step.script.clear();
|
||||
step.command = None;
|
||||
step.active = true;
|
||||
}
|
||||
targets.push(target);
|
||||
|
||||
@@ -94,16 +94,14 @@ pub fn get_step_script(
|
||||
pub fn delete_step(project: &mut Project, bank: usize, pattern: usize, step: usize) -> PatternEdit {
|
||||
let pat = project.pattern_at_mut(bank, pattern);
|
||||
for s in &mut pat.steps {
|
||||
if s.source == Some(step) {
|
||||
if s.source == Some(step as u8) {
|
||||
s.source = None;
|
||||
s.script.clear();
|
||||
s.command = None;
|
||||
}
|
||||
}
|
||||
|
||||
set_step_script(project, bank, pattern, step, String::new());
|
||||
if let Some(s) = project.pattern_at_mut(bank, pattern).step_mut(step) {
|
||||
s.command = None;
|
||||
s.source = None;
|
||||
}
|
||||
PatternEdit::new(bank, pattern)
|
||||
|
||||
@@ -104,7 +104,7 @@ pub struct CopiedSteps {
|
||||
pub struct CopiedStepData {
|
||||
pub script: String,
|
||||
pub active: bool,
|
||||
pub source: Option<usize>,
|
||||
pub source: Option<u8>,
|
||||
pub original_index: usize,
|
||||
pub name: Option<String>,
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ pub enum Modal {
|
||||
bank: usize,
|
||||
selected: bool,
|
||||
},
|
||||
FileBrowser(FileBrowserState),
|
||||
FileBrowser(Box<FileBrowserState>),
|
||||
RenameBank {
|
||||
bank: usize,
|
||||
name: String,
|
||||
@@ -50,7 +50,7 @@ pub enum Modal {
|
||||
input: String,
|
||||
},
|
||||
SetTempo(String),
|
||||
AddSamplePath(FileBrowserState),
|
||||
AddSamplePath(Box<FileBrowserState>),
|
||||
Editor,
|
||||
Preview,
|
||||
PatternProps {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::model::{MAX_BANKS, MAX_PATTERNS};
|
||||
@@ -7,7 +6,7 @@ use crate::model::Project;
|
||||
pub struct ProjectState {
|
||||
pub project: Project,
|
||||
pub file_path: Option<PathBuf>,
|
||||
pub dirty_patterns: HashSet<(usize, usize)>,
|
||||
dirty_patterns: [[bool; MAX_PATTERNS]; MAX_BANKS],
|
||||
}
|
||||
|
||||
impl Default for ProjectState {
|
||||
@@ -15,7 +14,7 @@ impl Default for ProjectState {
|
||||
let mut state = Self {
|
||||
project: Project::default(),
|
||||
file_path: None,
|
||||
dirty_patterns: HashSet::new(),
|
||||
dirty_patterns: [[false; MAX_PATTERNS]; MAX_BANKS],
|
||||
};
|
||||
state.mark_all_dirty();
|
||||
state
|
||||
@@ -24,18 +23,23 @@ impl Default for ProjectState {
|
||||
|
||||
impl ProjectState {
|
||||
pub fn mark_dirty(&mut self, bank: usize, pattern: usize) {
|
||||
self.dirty_patterns.insert((bank, pattern));
|
||||
self.dirty_patterns[bank][pattern] = true;
|
||||
}
|
||||
|
||||
pub fn mark_all_dirty(&mut self) {
|
||||
for bank in 0..MAX_BANKS {
|
||||
for pattern in 0..MAX_PATTERNS {
|
||||
self.dirty_patterns.insert((bank, pattern));
|
||||
}
|
||||
}
|
||||
self.dirty_patterns = [[true; MAX_PATTERNS]; MAX_BANKS];
|
||||
}
|
||||
|
||||
pub fn take_dirty(&mut self) -> HashSet<(usize, usize)> {
|
||||
std::mem::take(&mut self.dirty_patterns)
|
||||
pub fn take_dirty(&mut self) -> Vec<(usize, usize)> {
|
||||
let mut result = Vec::new();
|
||||
for (bank, patterns) in self.dirty_patterns.iter_mut().enumerate() {
|
||||
for (pattern, dirty) in patterns.iter_mut().enumerate() {
|
||||
if *dirty {
|
||||
*dirty = false;
|
||||
result.push((bank, pattern));
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ use ratatui::style::{Color, Modifier, Style};
|
||||
use ratatui::text::{Line as RLine, Span};
|
||||
use ratatui::widgets::{Block, Borders, List, ListItem, Padding, Paragraph, Wrap};
|
||||
use ratatui::Frame;
|
||||
#[cfg(not(feature = "desktop"))]
|
||||
use tui_big_text::{BigText, PixelSize};
|
||||
|
||||
use crate::app::App;
|
||||
@@ -173,7 +174,10 @@ fn render_topics(frame: &mut Frame, app: &App, area: Rect) {
|
||||
}
|
||||
|
||||
const WELCOME_TOPIC: usize = 0;
|
||||
#[cfg(not(feature = "desktop"))]
|
||||
const BIG_TITLE_HEIGHT: u16 = 6;
|
||||
#[cfg(feature = "desktop")]
|
||||
const BIG_TITLE_HEIGHT: u16 = 3;
|
||||
|
||||
fn render_content(frame: &mut Frame, app: &App, area: Rect) {
|
||||
let theme = theme::get();
|
||||
@@ -186,19 +190,34 @@ fn render_content(frame: &mut Frame, app: &App, area: Rect) {
|
||||
let [title_area, rest] =
|
||||
Layout::vertical([Constraint::Length(BIG_TITLE_HEIGHT), Constraint::Fill(1)])
|
||||
.areas(area);
|
||||
|
||||
#[cfg(not(feature = "desktop"))]
|
||||
let big_title = BigText::builder()
|
||||
.pixel_size(PixelSize::Quadrant)
|
||||
.style(Style::new().fg(theme.markdown.h1).bold())
|
||||
.lines(vec!["CAGIRE".into()])
|
||||
.centered()
|
||||
.build();
|
||||
#[cfg(feature = "desktop")]
|
||||
let big_title = Paragraph::new(RLine::from(Span::styled(
|
||||
"CAGIRE",
|
||||
Style::new().fg(theme.markdown.h1).bold(),
|
||||
)))
|
||||
.alignment(ratatui::layout::Alignment::Center);
|
||||
|
||||
let subtitle = Paragraph::new(RLine::from(Span::styled(
|
||||
"A Forth Sequencer",
|
||||
Style::new().fg(theme.ui.text_primary),
|
||||
)))
|
||||
.alignment(ratatui::layout::Alignment::Center);
|
||||
|
||||
#[cfg(not(feature = "desktop"))]
|
||||
let [big_area, subtitle_area] =
|
||||
Layout::vertical([Constraint::Length(4), Constraint::Length(2)]).areas(title_area);
|
||||
#[cfg(feature = "desktop")]
|
||||
let [big_area, subtitle_area] =
|
||||
Layout::vertical([Constraint::Length(1), Constraint::Length(2)]).areas(title_area);
|
||||
|
||||
frame.render_widget(big_title, big_area);
|
||||
frame.render_widget(subtitle, subtitle_area);
|
||||
rest
|
||||
|
||||
@@ -212,10 +212,10 @@ pub fn highlight_line_with_runtime(
|
||||
|
||||
let is_selected = selected_spans
|
||||
.iter()
|
||||
.any(|span| overlaps(token.start, token.end, span.start, span.end));
|
||||
.any(|span| overlaps(token.start, token.end, span.start as usize, span.end as usize));
|
||||
let is_executed = executed_spans
|
||||
.iter()
|
||||
.any(|span| overlaps(token.start, token.end, span.start, span.end));
|
||||
.any(|span| overlaps(token.start, token.end, span.start as usize, span.end as usize));
|
||||
|
||||
let mut style = token.kind.style();
|
||||
if token.varargs {
|
||||
|
||||
@@ -196,7 +196,7 @@ fn render_tile(
|
||||
};
|
||||
|
||||
let link_color = step.and_then(|s| s.source).map(|src| {
|
||||
let i = src % 5;
|
||||
let i = src as usize % 5;
|
||||
(theme.tile.link_bright[i], theme.tile.link_dim[i])
|
||||
});
|
||||
|
||||
@@ -236,7 +236,7 @@ fn render_tile(
|
||||
|
||||
// For linked steps, get the name from the source step
|
||||
let step_name = if let Some(src) = source_idx {
|
||||
pattern.step(src).and_then(|s| s.name.as_ref())
|
||||
pattern.step(src as usize).and_then(|s| s.name.as_ref())
|
||||
} else {
|
||||
step.and_then(|s| s.name.as_ref())
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::time::{Duration, Instant};
|
||||
use ratatui::layout::{Alignment, Constraint, Layout, Rect};
|
||||
use ratatui::style::{Modifier, Style};
|
||||
use ratatui::text::{Line, Span};
|
||||
use ratatui::widgets::{Block, Borders, Cell, Paragraph, Row, Table};
|
||||
use ratatui::widgets::{Block, Borders, Cell, Padding, Paragraph, Row, Table};
|
||||
use ratatui::Frame;
|
||||
|
||||
use crate::app::App;
|
||||
@@ -28,15 +28,17 @@ fn adjust_spans_for_line(
|
||||
line_start: usize,
|
||||
line_len: usize,
|
||||
) -> Vec<SourceSpan> {
|
||||
let ls = line_start as u32;
|
||||
let ll = line_len as u32;
|
||||
spans
|
||||
.iter()
|
||||
.filter_map(|s| {
|
||||
if s.end <= line_start || s.start >= line_start + line_len {
|
||||
if s.end <= ls || s.start >= ls + ll {
|
||||
return None;
|
||||
}
|
||||
Some(SourceSpan {
|
||||
start: s.start.max(line_start) - line_start,
|
||||
end: (s.end.min(line_start + line_len)) - line_start,
|
||||
start: s.start.max(ls) - ls,
|
||||
end: (s.end.min(ls + ll)) - ls,
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
@@ -158,12 +160,8 @@ pub fn render(frame: &mut Frame, app: &App, link: &LinkState, snapshot: &Sequenc
|
||||
}
|
||||
}
|
||||
|
||||
fn header_height(width: u16) -> u16 {
|
||||
if width >= 80 {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
}
|
||||
fn header_height(_width: u16) -> u16 {
|
||||
3
|
||||
}
|
||||
|
||||
fn render_side_panel(frame: &mut Frame, app: &App, area: Rect) {
|
||||
@@ -231,35 +229,18 @@ fn render_header(
|
||||
let bank = &app.project_state.project.banks[app.editor_ctx.bank];
|
||||
let pattern = &bank.patterns[app.editor_ctx.pattern];
|
||||
|
||||
let (transport_area, live_area, tempo_area, bank_area, pattern_area, stats_area) =
|
||||
if area.height == 1 {
|
||||
let [t, l, tp, b, p, s] = Layout::horizontal([
|
||||
Constraint::Min(12),
|
||||
Constraint::Length(9),
|
||||
Constraint::Min(14),
|
||||
Constraint::Fill(1),
|
||||
Constraint::Fill(2),
|
||||
Constraint::Min(20),
|
||||
])
|
||||
.areas(area);
|
||||
(t, l, tp, b, p, s)
|
||||
} else {
|
||||
let [line1, line2] =
|
||||
Layout::vertical([Constraint::Length(1), Constraint::Length(1)]).areas(area);
|
||||
let pad = Padding::vertical(1);
|
||||
|
||||
let [t, l, tp, s] = Layout::horizontal([
|
||||
Constraint::Min(12),
|
||||
Constraint::Length(9),
|
||||
Constraint::Fill(1),
|
||||
Constraint::Min(20),
|
||||
])
|
||||
.areas(line1);
|
||||
|
||||
let [b, p] =
|
||||
Layout::horizontal([Constraint::Fill(1), Constraint::Fill(2)]).areas(line2);
|
||||
|
||||
(t, l, tp, b, p, s)
|
||||
};
|
||||
let [transport_area, live_area, tempo_area, bank_area, pattern_area, stats_area] =
|
||||
Layout::horizontal([
|
||||
Constraint::Min(12),
|
||||
Constraint::Length(9),
|
||||
Constraint::Min(14),
|
||||
Constraint::Fill(1),
|
||||
Constraint::Fill(2),
|
||||
Constraint::Min(20),
|
||||
])
|
||||
.areas(area);
|
||||
|
||||
// Transport block
|
||||
let (transport_bg, transport_text) = if app.playback.playing {
|
||||
@@ -270,7 +251,7 @@ fn render_header(
|
||||
let transport_style = Style::new().bg(transport_bg).fg(theme.ui.text_primary);
|
||||
frame.render_widget(
|
||||
Paragraph::new(transport_text)
|
||||
.style(transport_style)
|
||||
.block(Block::default().padding(pad).style(transport_style))
|
||||
.alignment(Alignment::Center),
|
||||
transport_area,
|
||||
);
|
||||
@@ -285,7 +266,7 @@ fn render_header(
|
||||
let fill_style = Style::new().bg(theme.status.fill_bg).fg(fill_fg);
|
||||
frame.render_widget(
|
||||
Paragraph::new(if fill { "F" } else { "·" })
|
||||
.style(fill_style)
|
||||
.block(Block::default().padding(pad).style(fill_style))
|
||||
.alignment(Alignment::Center),
|
||||
live_area,
|
||||
);
|
||||
@@ -297,7 +278,7 @@ fn render_header(
|
||||
.add_modifier(Modifier::BOLD);
|
||||
frame.render_widget(
|
||||
Paragraph::new(format!(" {:.1} BPM ", link.tempo()))
|
||||
.style(tempo_style)
|
||||
.block(Block::default().padding(pad).style(tempo_style))
|
||||
.alignment(Alignment::Center),
|
||||
tempo_area,
|
||||
);
|
||||
@@ -313,7 +294,7 @@ fn render_header(
|
||||
.fg(theme.ui.text_primary);
|
||||
frame.render_widget(
|
||||
Paragraph::new(bank_name)
|
||||
.style(bank_style)
|
||||
.block(Block::default().padding(pad).style(bank_style))
|
||||
.alignment(Alignment::Center),
|
||||
bank_area,
|
||||
);
|
||||
@@ -346,7 +327,7 @@ fn render_header(
|
||||
.fg(theme.ui.text_primary);
|
||||
frame.render_widget(
|
||||
Paragraph::new(pattern_text)
|
||||
.style(pattern_style)
|
||||
.block(Block::default().padding(pad).style(pattern_style))
|
||||
.alignment(Alignment::Center),
|
||||
pattern_area,
|
||||
);
|
||||
@@ -361,7 +342,7 @@ fn render_header(
|
||||
.fg(theme.header.stats_fg);
|
||||
frame.render_widget(
|
||||
Paragraph::new(stats_text)
|
||||
.style(stats_style)
|
||||
.block(Block::default().padding(pad).style(stats_style))
|
||||
.alignment(Alignment::Right),
|
||||
stats_area,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user