This commit is contained in:
🪞👃🪞 2025-01-16 16:49:53 +01:00
parent 968441850f
commit c08d1bee5d
7 changed files with 177 additions and 150 deletions

View file

@ -94,11 +94,11 @@ impl Default for MidiEditor {
}
}
impl MidiEditor {
pub fn clip_length (&self) -> usize {
self.clip().as_ref().map(|p|p.read().unwrap().length).unwrap_or(1)
}
//fn clip_length (&self) -> usize {
//self.clip().as_ref().map(|p|p.read().unwrap().length).unwrap_or(1)
//}
/// Put note at current position
pub fn put_note (&mut self, advance: bool) {
fn put_note (&mut self, advance: bool) {
let mut redraw = false;
if let Some(clip) = self.clip() {
let mut clip = clip.write().unwrap();
@ -127,6 +127,34 @@ impl MidiEditor {
self.mode.redraw();
}
}
pub fn clip_status (&self) -> impl Content<TuiOut> + '_ {
let (color, name, length, looped) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) {
(clip.color, clip.name.clone(), clip.length, clip.looped)
} else {
(ItemPalette::from(TuiTheme::g(64)), String::new().into(), 0, false)
};
row!(
FieldV(color, "Edit", format!("{name} ({length})")),
FieldV(color, "Loop", looped.to_string())
)
}
pub fn edit_status (&self) -> impl Content<TuiOut> + '_ {
let (color, length) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) {
(clip.color, clip.length)
} else {
(ItemPalette::from(TuiTheme::g(64)), 0)
};
let time_pos = self.time_pos();
let time_zoom = self.time_zoom().get();
let time_lock = if self.time_lock().get() { "[lock]" } else { " " };
let note_pos = format!("{:>3}", self.note_pos());
let note_name = format!("{:4}", Note::pitch_to_name(self.note_pos()));
let note_len = format!("{:>4}", self.note_len());
Bsp::e(
FieldV(color, "Time", format!("{length}/{time_zoom}+{time_pos} {time_lock}")),
FieldV(color, "Note", format!("{note_name} {note_pos} {note_len}")),
)
}
}
impl TimeRange for MidiEditor {
fn time_len (&self) -> &AtomicUsize { self.mode.time_len() }
@ -156,39 +184,9 @@ impl MidiViewer for MidiEditor {
fn clip_mut (&mut self) -> &mut Option<Arc<RwLock<MidiClip>>> { self.mode.clip_mut() }
fn set_clip (&mut self, p: Option<&Arc<RwLock<MidiClip>>>) { self.mode.set_clip(p) }
}
impl MidiEditor {
pub fn clip_status (&self) -> impl Content<TuiOut> + '_ {
let (color, name, length, looped) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) {
(clip.color, clip.name.clone(), clip.length, clip.looped)
} else {
(ItemPalette::from(TuiTheme::g(64)), String::new().into(), 0, false)
};
row!(
FieldV(color, "Edit", format!("{name} ({length})")),
FieldV(color, "Loop", looped.to_string())
)
}
pub fn edit_status (&self) -> impl Content<TuiOut> + '_ {
let (color, length) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) {
(clip.color, clip.length)
} else {
(ItemPalette::from(TuiTheme::g(64)), 0)
};
let time_pos = self.time_pos();
let time_zoom = self.time_zoom().get();
let time_lock = if self.time_lock().get() { "[lock]" } else { " " };
let note_pos = format!("{:>3}", self.note_pos());
let note_name = format!("{:4}", Note::pitch_to_name(self.note_pos()));
let note_len = format!("{:>4}", self.note_len());
Bsp::e(
FieldV(color, "Time", format!("{length}/{time_zoom}+{time_pos} {time_lock}")),
FieldV(color, "Note", format!("{note_name} {note_pos} {note_len}")),
)
}
}
edn_command!(MidiEditCommand: |state: MidiEditor| {
("note/put" [a: bool] Self::PutNote)
("note/del" [a: bool] Self::PutNote)
("note/put" [_a: bool] Self::PutNote)
("note/del" [_a: bool] Self::PutNote)
("note/pos" [a: usize] Self::SetNoteCursor(a.expect("no note cursor")))
("note/len" [a: usize] Self::SetNoteLength(a.expect("no note length")))
("time/pos" [a: usize] Self::SetTimeCursor(a.expect("no time cursor")))
@ -210,7 +208,7 @@ edn_command!(MidiEditCommand: |state: MidiEditor| {
Show(Option<Arc<RwLock<MidiClip>>>),
}
impl MidiEditCommand {
pub fn from_tui_event (state: &MidiEditor, input: &impl EdnInput) -> Usually<Option<Self>> {
fn from_tui_event (state: &MidiEditor, input: &impl EdnInput) -> Usually<Option<Self>> {
use EdnItem::*;
let edns = EdnItem::<&str>::read_all(KEYS_EDIT)?;
for item in edns.iter() {

View file

@ -144,10 +144,10 @@ impl PianoHorizontal {
}
fn cursor (&self) -> impl Content<TuiOut> {
let style = Some(Style::default().fg(self.color.lightest.rgb));
let note_hi = self.note_hi();
let note_len = self.note_len();
let note_lo = self.note_lo().get();
let note_hi = self.note_hi();
let note_lo = self.note_lo().get();
let note_pos = self.note_pos();
let note_len = self.note_len();
let time_pos = self.time_pos();
let time_start = self.time_start().get();
let time_zoom = self.time_zoom().get();

View file

@ -2,7 +2,7 @@
(@shift-u redo 1)
(@space clock toggle)
(@shift-space clock toggle 0)
(@e editor show :pool-clip)
(@ctrl-a scene add)
(@ctrl-t track add)
(@tab edit)
(@c color)

View file

@ -3,7 +3,7 @@
(@left select :track-prev :scene)
(@right select :track-next :scene)
(@q clip launch)
(@q enqueue :clip)
(@c clip color)
(@g clip get)
(@p clip put)

View file

@ -98,17 +98,22 @@ impl TekCli {
Ok(match self.mode {
TekMode::Clock =>
engine.run(&jack.activate_with(|jack|Tek::new_clock(
jack, self.bpm))?)?,
jack, self.bpm, self.sync_lead, self.sync_follow))?)?,
TekMode::Sequencer =>
engine.run(&jack.activate_with(|jack|Tek::new_sequencer(
jack, self.bpm, &midi_froms, &midi_tos))?)?,
jack, self.bpm, self.sync_lead, self.sync_follow,
&midi_froms, &midi_tos))?)?,
TekMode::Groovebox =>
engine.run(&jack.activate_with(|jack|Tek::new_groovebox(
jack, self.bpm, &midi_froms, &midi_tos, &audio_froms, &audio_tos))?)?,
jack, self.bpm, self.sync_lead, self.sync_follow,
&midi_froms, &midi_tos,
&audio_froms, &audio_tos))?)?,
TekMode::Arranger { scenes, tracks, track_width, .. } =>
engine.run(&jack.activate_with(|jack|Tek::new_arranger(
jack, self.bpm, &midi_froms, &midi_tos, &audio_froms, &audio_tos,
scenes, tracks, track_width,
jack, self.bpm, self.sync_lead, self.sync_follow,
&midi_froms, &midi_tos,
&audio_froms, &audio_tos,
scenes, tracks, track_width
))?)?,
_ => todo!()
})
@ -198,6 +203,8 @@ impl Tek {
fn new_arranger (
jack: &Arc<RwLock<JackConnection>>,
bpm: Option<f64>,
sync_lead: bool,
sync_follow: bool,
midi_froms: &[PortConnection],
midi_tos: &[PortConnection],
audio_froms: &[&[PortConnection];2],
@ -208,7 +215,10 @@ impl Tek {
) -> Usually<Self> {
let mut arranger = Self {
edn: include_str!("./view_arranger.edn").to_string(),
..Self::new_groovebox(jack, bpm, midi_froms, midi_tos, audio_froms, audio_tos)?
..Self::new_groovebox(
jack, bpm, sync_lead, sync_follow,
midi_froms, midi_tos, audio_froms, audio_tos,
)?
};
arranger.scenes_add(scenes);
arranger.tracks_add(tracks, track_width, &[], &[]);
@ -217,6 +227,8 @@ impl Tek {
fn new_groovebox (
jack: &Arc<RwLock<JackConnection>>,
bpm: Option<f64>,
sync_lead: bool,
sync_follow: bool,
midi_froms: &[PortConnection],
midi_tos: &[PortConnection],
audio_froms: &[&[PortConnection];2],
@ -225,7 +237,7 @@ impl Tek {
let app = Self {
edn: include_str!("./view_groovebox.edn").to_string(),
sampler: Some(Sampler::new(jack, &"sampler", midi_froms, audio_froms, audio_tos)?),
..Self::new_sequencer(jack, bpm, midi_froms, midi_tos)?
..Self::new_sequencer(jack, bpm, sync_lead, sync_follow, midi_froms, midi_tos)?
};
if let Some(sampler) = app.sampler.as_ref().unwrap().midi_in.as_ref() {
jack.connect_ports(&app.player.as_ref().unwrap().midi_outs[0].port, &sampler.port)?;
@ -235,6 +247,8 @@ impl Tek {
fn new_sequencer (
jack: &Arc<RwLock<JackConnection>>,
bpm: Option<f64>,
sync_lead: bool,
sync_follow: bool,
midi_froms: &[PortConnection],
midi_tos: &[PortConnection],
) -> Usually<Self> {
@ -247,32 +261,25 @@ impl Tek {
editing: false.into(),
midi_buf: vec![vec![];65536],
player: Some(MidiPlayer::new(&jack, "sequencer", Some(&clip), &midi_froms, &midi_tos)?),
..Self::new_clock(jack, bpm)?
..Self::new_clock(jack, bpm, sync_lead, sync_follow)?
})
}
fn new_clock (
jack: &Arc<RwLock<JackConnection>>,
bpm: Option<f64>,
sync_lead: bool,
sync_follow: bool,
) -> Usually<Self> {
// TODO: enable sync master/follow
//let sync_clock = |jack: &Arc<RwLock<JackConnection>>, app|{
//if cli.sync_lead {
//} else if cli.sync_follow {
//jack.read().unwrap().client().register_timebase_callback(false, |state|{
//app.clock().playhead.update_from_sample(state.position.frame() as f64);
//state.position
//})
//} else {
//Ok(())
//}
//};
Ok(Self {
let tek = Self {
edn: include_str!("./view_transport.edn").to_string(),
jack: jack.clone(),
color: ItemPalette::random(),
clock: Clock::new(jack, bpm),
..Default::default()
})
};
tek.sync_lead(sync_lead);
tek.sync_follow(sync_follow);
Ok(tek)
}
fn sync_lead (&self, enable: bool) -> Usually<()> {
if enable {
@ -285,8 +292,9 @@ impl Tek {
}
Ok(())
}
fn sync_follow (&self) {
// TODO
fn sync_follow (&self, enable: bool) -> Usually<()> {
// TODO: sync follow
Ok(())
}
fn editor (&self) -> impl Content<TuiOut> + '_ { &self.editor }
fn view_clock (&self) -> impl Content<TuiOut> + use<'_> {
@ -297,6 +305,7 @@ impl Tek {
))
}
fn view_beat_stats (&self) -> impl Content<TuiOut> + use<'_> {
let compact = self.size.w() > 80;
let clock = self.clock();
let now = clock.started.read().unwrap().as_ref().map(|start|clock.global.usec.get() - start.usec.get());
let beat = ||now.map(|now|clock.timebase.format_beats_1(clock.timebase.usecs_to_pulse(now)))
@ -304,29 +313,29 @@ impl Tek {
let time = ||now.map(|now|format!("{:.3}s", now/1000000.))
.unwrap_or("-.---s".into());
let bpm = ||format!("{:.3}", clock.timebase.bpm.get());
Either::new(self.is_editing(),
Either::new(compact,
row!(Field(TuiTheme::g(128).into(), "BPM", bpm()),
Field(TuiTheme::g(128).into(), "Beat", beat()),
Field(TuiTheme::g(128).into(), "Time", time())),
row!(FieldV(TuiTheme::g(128).into(), "BPM", bpm()),
FieldV(TuiTheme::g(128).into(), "Beat", beat()),
FieldV(TuiTheme::g(128).into(), "Time", time()),),
col!(Bsp::e(Tui::fg(TuiTheme::g(255), bpm()), " BPM"),
Bsp::e("Beat ", Tui::fg(TuiTheme::g(255), beat())),
Bsp::e("Time ", Tui::fg(TuiTheme::g(255), time()))))
FieldV(TuiTheme::g(128).into(), "Time", time())))
}
fn view_engine_stats (&self) -> impl Content<TuiOut> + use<'_> {
let clock = self.clock();
let compact = self.is_editing();
let rate = clock.timebase.sr.get();
let chunk = clock.chunk.load(Relaxed);
let sr = move||format!("{}", if compact {format!("{:.1}kHz", rate / 1000.)} else {format!("{:.0}Hz", rate)});
let buffer_size = move||format!("{chunk}");
let latency = move||format!("{:.1}ms", chunk as f64 / rate * 1000.);
let compact = self.size.w() > 80;
let clock = self.clock();
let rate = clock.timebase.sr.get();
let chunk = clock.chunk.load(Relaxed);
let sr = move||format!("{}", if compact {format!("{:.1}kHz", rate / 1000.)} else {format!("{:.0}Hz", rate)});
let buf = move||format!("{chunk}");
let latency = move||format!("{:.1}ms", chunk as f64 / rate * 1000.);
Either::new(compact,
row!(Field(TuiTheme::g(128).into(), "SR", sr()),
Field(TuiTheme::g(128).into(), "Buf", buf()),
Field(TuiTheme::g(128).into(), "Lat", latency())),
row!(FieldV(TuiTheme::g(128).into(), "SR", sr()),
FieldV(TuiTheme::g(128).into(), "Buf", buffer_size()),
FieldV(TuiTheme::g(128).into(), "Lat", latency())),
col!(Bsp::e(Tui::fg(TuiTheme::g(255), format!("{}", sr())), " sample rate"),
Bsp::e(Tui::fg(TuiTheme::g(255), format!("{}", buffer_size())), " sample buffer"),
Bsp::e(Tui::fg(TuiTheme::g(255), format!("{:.3}ms", latency())), " latency")))
FieldV(TuiTheme::g(128).into(), "Buf", buf()),
FieldV(TuiTheme::g(128).into(), "Lat", latency())))
}
fn view_meter <'a> (&'a self, label: &'a str, value: f32) -> impl Content<TuiOut> + 'a {
col!(
@ -356,9 +365,10 @@ impl Tek {
}
fn view_play_pause (&self) -> impl Content<TuiOut> + use<'_> {
let playing = self.clock.is_rolling();
let compact = self.is_editing();
Tui::bg(
if playing{Color::Rgb(0,128,0)}else{Color::Rgb(128,64,0)},
Either::new(self.is_editing(),
Either::new(compact,
Thunk::new(move||Fixed::x(9, Either::new(playing,
Tui::fg(Color::Rgb(0, 255, 0), " PLAYING "),
Tui::fg(Color::Rgb(255, 128, 0), " STOPPED ")))),
@ -404,12 +414,6 @@ impl Tek {
clip.write().unwrap().toggle_loop()
}
}
fn track_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
(||Tui::bg(TuiTheme::g(32), Bsp::s(
Fill::x(Align::w(button(" C-a ".to_string(), format!(" add scene ({})", self.scenes().len())))),
Fill::x(Align::w(button(" C-t ".to_string(), format!(" add track ({})", self.tracks().len())))),
)).boxed()).into()
}
fn track_add (
&mut self,
name: Option<&str>,
@ -435,8 +439,8 @@ impl Tek {
}
fn tracks_add (
&mut self,
count: usize,
width: usize,
count: usize,
width: usize,
midi_from: &[PortConnection],
midi_to: &[PortConnection],
) -> Usually<()> {
@ -508,7 +512,9 @@ impl Tek {
let neighbor = selected_track == Some(t) && selected_scene.map(|s|s+1) == Some(s);
map_south(y1 as u16, h, Push::y(1, Fixed::y(h, Either::new(
active,
Thunk::new(||self.editor()),
Thunk::new(||Bsp::a(
Fill::xy(Align::nw(button(" Tab ".into(), "".into()))),
self.editor())),
Thunk::new(move||Bsp::a(
When::new(selected, Fill::y(Align::n(button(" Tab ".into(), "edit".into())))),
phat_sel_3(
@ -549,6 +555,37 @@ impl Tek {
}
Ok(())
}
fn button (&self, key: String, label: String) -> impl Content<TuiOut> {
let compact = !self.is_editing();
Tui::bold(true, Bsp::e(
Margin::x(1, Tui::fg_bg(TuiTheme::g(0), TuiTheme::orange(), key)),
When::new(compact, Margin::x(1, Tui::fg_bg(TuiTheme::g(255), TuiTheme::g(96), label))),
))
}
fn input_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
let fg = TuiTheme::g(224);
let bg = TuiTheme::g(64);
(move||Bsp::s(Fill::x(Align::w(self.button(" I ".to_string(), format!(" midi ins ({})", self.midi_ins().len())))), self.midi_ins().get(0).map(|inp|Bsp::s(
Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(inp.name.clone())))),
inp.connect.get(0).map(|connect|Fill::x(Align::w(Tui::bold(false,
Tui::fg_bg(fg, bg, connect.info()))))),
))).boxed()).into()
}
fn output_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
let fg = TuiTheme::g(224);
let bg = TuiTheme::g(64);
(move||Bsp::s(Fill::x(Align::w(self.button(" O ".to_string(), format!(" midi outs ({}) ", self.midi_outs().len())))), self.midi_outs().get(0).map(|out|Bsp::s(
Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(out.name.clone())))),
out.connect.get(0).map(|connect|Fill::x(Align::w(Tui::bold(false,
Tui::fg_bg(fg, bg, connect.info()))))),
))).boxed()).into()
}
fn track_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
(||Tui::bg(TuiTheme::g(32), Bsp::s(
Fill::x(Align::w(self.button(" C-a ".to_string(), format!(" add scene ({})", self.scenes().len())))),
Fill::x(Align::w(self.button(" C-t ".to_string(), format!(" add track ({})", self.tracks().len())))),
)).boxed()).into()
}
}
const KEYS_APP: &str = include_str!("keys.edn");
const KEYS_CLIP: &str = include_str!("keys_clip.edn");
@ -600,17 +637,13 @@ handle!(TuiIn: |self: Tek, input|Ok({
Zoom(Option<usize>),
}
edn_command!(TekCommand: |app: Tek| {
("edit" [] Self::Edit(None))
("edit" [c: bool] Self::Edit(c))
("color" [c: Color] Self::Color(c.map(ItemPalette::from).unwrap_or_default()))
("stop-all" [] Self::StopAll)
("undo" [d: usize] Self::History(-(d.unwrap_or(0)as isize)))
("redo" [d: usize] Self::History(d.unwrap_or(0) as isize))
("zoom" [z: usize] Self::Zoom(z))
("stop-all" [] Self::StopAll)
("edit" [] Self::Edit(None))
("edit" [c: bool] Self::Edit(c))
("color" [c: Color] Self::Color(c.map(ItemPalette::from).unwrap_or_default()))
("enqueue" [c: Arc<RwLock<MidiClip>>] Self::Enqueue(c))
("select" [t: usize, s: usize] match (t.expect("no track"), s.expect("no scene")) {
(0, 0) => Self::Select(Selection::Mix),
@ -618,7 +651,6 @@ edn_command!(TekCommand: |app: Tek| {
(0, s) => Self::Select(Selection::Scene(s)),
(t, s) => Self::Select(Selection::Clip(t, s)),
})
("clip" [a, ..b] Self::Clip(ClipCommand::from_edn(app, &a.to_ref(), b)
.expect("invalid command")))
("clock" [a, ..b] Self::Clock(ClockCommand::from_edn(app.clock(), &a.to_ref(), b)
@ -635,18 +667,42 @@ edn_command!(TekCommand: |app: Tek| {
.expect("invalid command")))
});
command!(|self: TekCommand, app: Tek|match self {
Self::Zoom(_) => { println!("\n\rtodo: global zoom"); None },
Self::Zoom(_) => { println!("\n\rtodo: global zoom"); None },
Self::History(delta) => { println!("\n\rtodo: undo/redo"); None },
Self::Select(s) => { app.selected = s; None },
Self::Clock(cmd) => cmd.delegate(app, Self::Clock)?,
Self::Scene(cmd) => cmd.delegate(app, Self::Scene)?,
Self::Track(cmd) => cmd.delegate(app, Self::Track)?,
Self::Clip(cmd) => cmd.delegate(app, Self::Clip)?,
Self::Editor(cmd) => app.editor.as_mut()
Self::Select(s) => {
app.selected = s;
// autoedit: load focused clip in editor.
if let Some(ref mut editor) = app.editor {
editor.set_clip(match app.selected {
Selection::Clip(t, s) if let Some(Some(Some(clip))) = app
.scenes
.get(s)
.map(|s|s.clips.get(t)) => Some(clip),
_ => None
});
}
None
},
Self::Edit(value) => {
// TODO: autocreate: create new clip when entering empty cell
if let Some(value) = value {
if app.is_editing() != value {
app.editing.store(value, Relaxed);
}
} else {
app.editing.store(!app.is_editing(), Relaxed);
};
None
},
Self::Clock(cmd) => cmd.delegate(app, Self::Clock)?,
Self::Scene(cmd) => cmd.delegate(app, Self::Scene)?,
Self::Track(cmd) => cmd.delegate(app, Self::Track)?,
Self::Clip(cmd) => cmd.delegate(app, Self::Clip)?,
Self::Editor(cmd) => app.editor.as_mut()
.map(|editor|cmd.delegate(editor, Self::Editor)).transpose()?.flatten(),
Self::Sampler(cmd) => app.sampler.as_mut()
Self::Sampler(cmd) => app.sampler.as_mut()
.map(|sampler|cmd.delegate(sampler, Self::Sampler)).transpose()?.flatten(),
Self::Enqueue(clip) => app.player.as_mut()
Self::Enqueue(clip) => app.player.as_mut()
.map(|player|{player.enqueue_next(clip.as_ref());None}).flatten(),
Self::Color(palette) => {
let old = app.color;
@ -672,17 +728,6 @@ command!(|self: TekCommand, app: Tek|match self {
} else {
None
},
Self::Edit(value) => if let Some(value) = value {
if app.is_editing() != value {
app.editing.store(value, Relaxed);
Some(Self::Edit(Some(!value)))
} else {
None
}
} else {
app.editing.store(!app.is_editing(), Relaxed);
Some(Self::Edit(Some(!app.is_editing())))
}
});
/// Represents the current user selection in the arranger
#[derive(PartialEq, Clone, Copy, Debug, Default)] pub enum Selection {
@ -736,19 +781,19 @@ trait HasSelection {
}
#[derive(Debug, Default)] struct Track {
/// Name of track
name: Arc<str>,
name: Arc<str>,
/// Preferred width of track column
width: usize,
width: usize,
/// Identifying color of track
color: ItemPalette,
color: ItemPalette,
/// MIDI player state
player: MidiPlayer,
player: MidiPlayer,
/// Device chain
devices: Vec<Box<dyn Device>>,
/// Inputs of 1st device
audio_ins: Vec<JackPort<AudioIn>>,
/// Outputs of last device
audio_outs: Vec<JackPort<AudioOut>>,
/// Device chain
devices: Vec<Box<dyn Device>>,
}
has_clock!(|self: Track|self.player.clock);
has_player!(|self: Track|self.player);
@ -840,15 +885,6 @@ trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync {
))
})).boxed()).into()
}
fn input_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
let fg = TuiTheme::g(224);
let bg = TuiTheme::g(64);
(move||Bsp::s(Fill::x(Align::w(button(" I ".to_string(), format!(" midi ins ({})", self.midi_ins().len())))), self.midi_ins().get(0).map(|inp|Bsp::s(
Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(inp.name.clone())))),
inp.connect.get(0).map(|connect|Fill::x(Align::w(Tui::bold(false,
Tui::fg_bg(fg, bg, connect.info()))))),
))).boxed()).into()
}
fn input_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
(move||Align::x(Map::new(||self.tracks_sizes(self.is_editing(), self.editor_w()), move|(_, track, x1, x2), i| {
let w = (x2 - x1) as u16;
@ -868,15 +904,6 @@ trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync {
Tui::fg_bg(if mon { Color::White } else { bg }, bg, ""),
)
}
fn output_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
let fg = TuiTheme::g(224);
let bg = TuiTheme::g(64);
(move||Bsp::s(Fill::x(Align::w(button(" O ".to_string(), format!(" midi outs ({}) ", self.midi_outs().len())))), self.midi_outs().get(0).map(|out|Bsp::s(
Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(out.name.clone())))),
out.connect.get(0).map(|connect|Fill::x(Align::w(Tui::bold(false,
Tui::fg_bg(fg, bg, connect.info()))))),
))).boxed()).into()
}
fn output_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
(move||Align::x(Map::new(||self.tracks_sizes(self.is_editing(), self.editor_w()), move|(_, track, x1, x2), i| {
let w = (x2 - x1) as u16;

View file

@ -1,3 +1,5 @@
(bsp/s (fixed/y 2 :toolbar)
(fill/x (align/c (bsp/w (fixed/x :pool-w :pool)
(bsp/n (fixed/y 3 :outputs) (bsp/n (fixed/y 3 :inputs) (bsp/n (fixed/y 3 :tracks) :scenes)))))))
(bsp/n (fixed/y 3 :outputs)
(bsp/n (fixed/y 3 :inputs)
(bsp/n (fixed/y 3 :tracks) :scenes)))))))

View file

@ -38,7 +38,7 @@ impl Content<TuiOut> for Repeat<'_> {
fn render (&self, to: &mut TuiOut) {
let [x, y, w, h] = to.area().xywh();
let a = self.0.len();
for (v, y) in (y..y+h).enumerate() {
for (_v, y) in (y..y+h).enumerate() {
for (u, x) in (x..x+w).enumerate() {
if let Some(cell) = to.buffer.cell_mut(ratatui::prelude::Position::from((x, y))) {
let u = u % a;
@ -56,7 +56,7 @@ impl Content<TuiOut> for RepeatV<'_> {
to
}
fn render (&self, to: &mut TuiOut) {
let [x, y, w, h] = to.area().xywh();
let [x, y, _w, h] = to.area().xywh();
for y in y..y+h {
if let Some(cell) = to.buffer.cell_mut(ratatui::prelude::Position::from((x, y))) {
cell.set_symbol(&self.0);
@ -72,7 +72,7 @@ impl Content<TuiOut> for RepeatH<'_> {
to
}
fn render (&self, to: &mut TuiOut) {
let [x, y, w, h] = to.area().xywh();
let [x, y, w, _h] = to.area().xywh();
for x in x..x+w {
if let Some(cell) = to.buffer.cell_mut(ratatui::prelude::Position::from((x, y))) {
cell.set_symbol(&self.0);