mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
update layout macro invocations
This commit is contained in:
parent
e677d1d7d4
commit
83eb9dd2fa
19 changed files with 153 additions and 169 deletions
|
|
@ -5,6 +5,8 @@ mod stack; pub use self::stack::*;
|
|||
|
||||
use crate::*;
|
||||
|
||||
use std::sync::RwLock;
|
||||
|
||||
/// Conditional rendering, in unary and binary forms.
|
||||
pub struct Cond;
|
||||
|
||||
|
|
@ -17,8 +19,14 @@ impl Cond {
|
|||
pub fn either <E: Engine, A: Content<E>, B: Content<E>> (cond: bool, a: A, b: B) -> Either<E, A, B> {
|
||||
Either(cond, a, b, Default::default())
|
||||
}
|
||||
|
||||
pub fn opt <E: Engine, A, F: Fn(A)->R, R: Content<E>> (option: Option<A>, cb: F) -> Opt<E, A, F, R> {
|
||||
Opt(option, cb, Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Opt<E: Engine, A, F: Fn(A)->R, R: Content<E>>(Option<A>, F, PhantomData<E>);
|
||||
|
||||
/// Contents `self.1` when `self.0` is true.
|
||||
pub struct When<E: Engine, A>(bool, A, PhantomData<E>);
|
||||
|
||||
|
|
@ -62,7 +70,6 @@ pub trait Collector<E: Engine>: Send + Sync + Fn(&mut dyn FnMut(&dyn Content<E>)
|
|||
impl<E, F> Collector<E> for F
|
||||
where E: Engine, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Content<E>)) {}
|
||||
|
||||
/*
|
||||
/// Rendering of iterable collections, one-to-one and many-to one.
|
||||
pub struct Coll;
|
||||
|
||||
|
|
@ -73,24 +80,25 @@ impl Coll {
|
|||
R: Content<E>,
|
||||
F: Fn(T, usize)->R + Send + Sync
|
||||
{
|
||||
Map(Default::default(), iterator, callback)
|
||||
}
|
||||
pub fn reduce <E, T, I, R, F>(iterator: I, callback: F) -> Reduce<E, T, I, R, F> where
|
||||
E: Engine,
|
||||
I: Iterator<Item = T> + Send + Sync,
|
||||
R: Content<E>,
|
||||
F: Fn(R, T, usize) -> R + Send + Sync
|
||||
{
|
||||
Reduce(Default::default(), iterator, callback)
|
||||
Map(Default::default(), RwLock::new(iterator), callback)
|
||||
}
|
||||
//pub fn reduce <E, T, I, R, F>(iterator: I, callback: F) -> Reduce<E, T, I, R, F> where
|
||||
//E: Engine,
|
||||
//I: Iterator<Item = T> + Send + Sync,
|
||||
//R: Content<E>,
|
||||
//F: Fn(R, T, usize) -> R + Send + Sync
|
||||
//{
|
||||
//Reduce(Default::default(), iterator, callback)
|
||||
//}
|
||||
}
|
||||
|
||||
pub struct Map<'a, E, T, I, R, F>(PhantomData<E>, &'a mut I, F) where
|
||||
pub struct Map<E, T, I, R, F>(PhantomData<E>, RwLock<I>, F) where
|
||||
E: Engine,
|
||||
I: Iterator<Item = T> + Send + Sync,
|
||||
R: Content<E>,
|
||||
F: Fn(T, usize)->R + Send + Sync;
|
||||
impl<'a, E, T, I, R, F> Content<E> for Map<'a, E, T, I, R, F> where
|
||||
|
||||
impl<E, T, I, R, F> Content<E> for Map<E, T, I, R, F> where
|
||||
E: Engine,
|
||||
I: Iterator<Item = T> + Send + Sync,
|
||||
R: Content<E>,
|
||||
|
|
@ -98,13 +106,14 @@ impl<'a, E, T, I, R, F> Content<E> for Map<'a, E, T, I, R, F> where
|
|||
{
|
||||
fn render (&self, to: &mut E::Output) {
|
||||
let mut index = 0;
|
||||
for item in self.1 {
|
||||
for item in &mut*self.1.write().unwrap() {
|
||||
(self.2)(item, index).render(to);
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
pub struct Reduce<E, T, I, R, F>(PhantomData<(E, R)>, I, F) where
|
||||
E: Engine,
|
||||
I: Iterator<Item = T> + Send + Sync,
|
||||
|
|
|
|||
|
|
@ -102,23 +102,23 @@ impl ArrangerTui {
|
|||
render!(Tui: (self: ArrangerTui) => {
|
||||
let play = PlayPause(self.clock.is_rolling());
|
||||
let transport = TransportView::new(self, Some(ItemPalette::from(TuiTheme::g(96))), true);
|
||||
let with_transport = |x|col!([row!(![&play, &transport]), &x]);
|
||||
let with_transport = |x|col!(row!(&play, &transport), &x);
|
||||
let pool_size = if self.phrases.visible { self.splits[1] } else { 0 };
|
||||
let with_pool = |x|Split::w(false, pool_size, PoolView(&self.phrases), x);
|
||||
let status = ArrangerStatus::from(self);
|
||||
let with_editbar = |x|Split::n(false, 1, MidiEditStatus(&self.editor), x);
|
||||
let with_status = |x|Split::n(false, 2, status, x);
|
||||
let with_size = |x|lay!([&self.size, x]);
|
||||
let with_size = |x|lay!(&self.size, x);
|
||||
let arranger = ||lay!(|add|{
|
||||
let color = self.color;
|
||||
add(&Fill::xy(Tui::bg(color.darkest.rgb, "")))?;
|
||||
add(&Fill::xy(Outer(Style::default().fg(color.dark.rgb).bg(color.darkest.rgb))))?;
|
||||
add(&Self::render_mode(self))
|
||||
});
|
||||
with_size(with_status(with_editbar(with_pool(with_transport(col!([
|
||||
with_size(with_status(with_editbar(with_pool(with_transport(col!(
|
||||
Fill::x(Fixed::y(20, arranger())),
|
||||
Fill::xy(&self.editor),
|
||||
]))))))
|
||||
))))))
|
||||
});
|
||||
audio!(|self: ArrangerTui, client, scope|{
|
||||
// Start profiling cycle
|
||||
|
|
|
|||
|
|
@ -4,20 +4,21 @@ mod v_cursor; pub(crate) use self::v_cursor::*;
|
|||
mod v_head; pub(crate) use self::v_head::*;
|
||||
mod v_io; pub(crate) use self::v_io::*;
|
||||
mod v_sep; pub(crate) use self::v_sep::*;
|
||||
// egyptian snakes den
|
||||
const HEADER_H: u16 = 5;
|
||||
const SCENES_W_OFFSET: u16 = 3;
|
||||
impl ArrangerTui {
|
||||
pub fn render_mode_v (state: &ArrangerTui, factor: usize) -> impl Render<Tui> + use<'_> {
|
||||
lay!([
|
||||
pub fn render_mode_v (state: &ArrangerTui, factor: usize) -> impl Content<Tui> + use<'_> {
|
||||
lay!(
|
||||
ArrangerVColSep::from(state),
|
||||
ArrangerVRowSep::from((state, factor)),
|
||||
col!([
|
||||
col!(
|
||||
ArrangerVHead::from(state),
|
||||
ArrangerVIns::from(state),
|
||||
ArrangerVClips::from((state, factor)),
|
||||
ArrangerVClips::new(state, factor),
|
||||
ArrangerVOuts::from(state),
|
||||
]),
|
||||
),
|
||||
ArrangerVCursor::from((state, factor)),
|
||||
])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ impl<'a> ArrangerVClips<'a> {
|
|||
impl<'a, E: Engine> Content<E> for ArrangerVClips<'a> {
|
||||
fn content (&self) -> Option<impl Content<E>> {
|
||||
let iter = self.scenes.iter().zip(self.rows.iter().map(|row|row.0));
|
||||
let col = col_iter!(iter => |(scene, pulses)|Self::format_scene(self.tracks, scene, pulses));
|
||||
let col = Coll::map(self.scenes.iter().zip(self.rows.iter().map(|row|row.0)), |(scene, pulses), i|
|
||||
Self::format_scene(self.tracks, scene, pulses));
|
||||
Some(Fill::xy(col))
|
||||
}
|
||||
}
|
||||
|
|
@ -36,9 +37,8 @@ impl<'a> ArrangerVClips<'a> {
|
|||
if playing { "▶ " } else { " " }),
|
||||
Tui::fg_bg(scene.color.lightest.rgb, scene.color.base.rgb,
|
||||
Expand::x(1, Tui::bold(true, scene.name.read().unwrap().as_str()))),
|
||||
row_iter!((index, track, x1, x2) in ArrangerTrack::with_widths(tracks) => {
|
||||
Self::format_clip(scene, index, track, (x2 - x1) as u16, height)
|
||||
})))}
|
||||
Coll::map(ArrangerTrack::with_widths(tracks), |(index, track, x1, x2), _|
|
||||
Push::x(Self::format_clip(scene, index, track, (x2 - x1) as u16, height)))))}
|
||||
|
||||
fn format_clip (
|
||||
scene: &'a ArrangerScene, index: usize, track: &'a ArrangerTrack, w: u16, h: u16
|
||||
|
|
|
|||
|
|
@ -15,29 +15,27 @@ from!(<'a>|state: &'a ArrangerTui|ArrangerVHead<'a> = Self { // A
|
|||
scenes_w: SCENES_W_OFFSET + ArrangerScene::longest_name(&state.scenes) as u16,
|
||||
});
|
||||
|
||||
impl Content<Tui> for ArrangerVCursor {
|
||||
fn content (&self) -> Option<impl Content<Tui>> {
|
||||
render!(Tui: (self: ArrangerVHead<'a>) => {
|
||||
|
||||
fn row <T: Content<Tui>> (color: ItemPalette, field: &T) -> impl Content<Tui> + use<'_, T> {
|
||||
row!([Tui::fg(color.light.rgb, "▎"), Tui::fg(color.lightest.rgb, field)])
|
||||
row!(Tui::fg(color.light.rgb, "▎"), Tui::fg(color.lightest.rgb, field))
|
||||
}
|
||||
|
||||
Some(Push::x(self.scenes_w, row_iter!(
|
||||
(_0, track, x1, x2) in ArrangerTrack::with_widths(self.tracks) => {
|
||||
Some(Push::x(self.scenes_w,
|
||||
Coll::map(ArrangerTrack::with_widths(self.tracks), |(_, track, x1, x2), i| {
|
||||
let (w, h) = (ArrangerTrack::MIN_WIDTH.max(x2 - x1), HEADER_H);
|
||||
let color = track.color();
|
||||
Tui::bg(color.base.rgb, Min::xy(w as u16, h, Fixed::xy(w as u16, 5, col!([
|
||||
Tui::bg(color.base.rgb, Min::xy(w as u16, h, Fixed::xy(w as u16, 5, col!(
|
||||
row(color, &Self::format_name(track, w)),
|
||||
row(color, &Self::format_input(track)?),
|
||||
row(color, &Self::format_output(track)?),
|
||||
row(color, &Self::format_elapsed(track, self.timebase)),
|
||||
row(color, &Self::format_until_next(track, self.current)),
|
||||
]))))
|
||||
}
|
||||
)))
|
||||
))))
|
||||
})
|
||||
))
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
impl ArrangerVHead<'_> {
|
||||
/// name and width of track
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use crate::*;
|
|||
pub struct Bordered<S: BorderStyle, W: Content<Tui>>(pub S, pub W);
|
||||
|
||||
render!(Tui: (self: Bordered<S: BorderStyle, W: Content<Tui>>) => {
|
||||
Fill::xy(lay!([Border(self.0), Padding::xy(1, 1, &self.1)]))
|
||||
Fill::xy(lay!(Border(self.0), Padding::xy(1, 1, &self.1)))
|
||||
});
|
||||
|
||||
pub struct Border<S: BorderStyle>(pub S);
|
||||
|
|
@ -130,8 +130,7 @@ macro_rules! border {
|
|||
#[derive(Copy, Clone)]
|
||||
pub struct $T(pub Style);
|
||||
impl Content<Tui> for $T {
|
||||
fn min_size (&self, _: [u16;2]) -> Perhaps<[u16;2]> { Ok(Some([0,0])) }
|
||||
fn render (&self, to: &mut TuiOutput) -> Usually<()> { self.draw(to) }
|
||||
fn render (&self, to: &mut TuiOutput) { self.draw(to) }
|
||||
}
|
||||
)+}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,29 +136,29 @@ render!(Tui: (self: Groovebox) => {
|
|||
GrooveboxSamples(self)
|
||||
)), x);
|
||||
let status = EditStatus(&self.sampler, &self.editor, note_pt, pool(sampler(&self.editor)));
|
||||
Fill::xy(lay!([
|
||||
Fill::xy(lay!(
|
||||
&self.size,
|
||||
Fill::xy(Align::s(Fixed::y(2, GrooveboxStatus::from(self)))),
|
||||
Shrink::y(2, col!(![
|
||||
Shrink::y(2, col!(
|
||||
transport,
|
||||
selector,
|
||||
status
|
||||
]))
|
||||
]))
|
||||
))
|
||||
))
|
||||
});
|
||||
|
||||
struct EditStatus<'a, T: Content<Tui>>(&'a Sampler, &'a MidiEditor, usize, T);
|
||||
impl<'a, T: Content<Tui>> Content<Tui> for EditStatus<'a, T> {
|
||||
fn content (&self) -> Option<impl Content<Tui>> {
|
||||
Some(Split::n(false, 9, col!([
|
||||
row!(|add|{
|
||||
if let Some(sample) = &self.0.mapped[self.2] {
|
||||
add(&format!("Sample {}", sample.read().unwrap().end))?;
|
||||
}
|
||||
add(&MidiEditStatus(&self.1))?;
|
||||
Ok(())
|
||||
}),
|
||||
lay!([
|
||||
Some(Split::n(false, 9, col!(
|
||||
row!(
|
||||
Cond::opt(
|
||||
&self.0.mapped[self.2],
|
||||
|sample|format!("Sample {}", sample.read().unwrap().end)
|
||||
),
|
||||
MidiEditStatus(&self.1),
|
||||
),
|
||||
lay!(
|
||||
Outer(Style::default().fg(TuiTheme::g(128))),
|
||||
Fill::x(Fixed::y(8, if let Some((_, sample)) = &self.0.recording {
|
||||
SampleViewer(Some(sample.clone()))
|
||||
|
|
@ -167,16 +167,8 @@ impl<'a, T: Content<Tui>> Content<Tui> for EditStatus<'a, T> {
|
|||
} else {
|
||||
SampleViewer(None)
|
||||
})),
|
||||
]),
|
||||
]), &self.3))
|
||||
}
|
||||
}
|
||||
impl<'a, T: Content<Tui>> Content<Tui> for EditStatus<'a, T> {
|
||||
fn min_size (&self, to: [u16;2]) -> Perhaps<[u16;2]> {
|
||||
self.content().unwrap().min_size(to)
|
||||
}
|
||||
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
|
||||
self.content().unwrap().render(to)
|
||||
),
|
||||
), &self.3))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -185,7 +177,7 @@ render!(Tui: (self: GrooveboxSamples<'a>) => {
|
|||
let note_lo = self.0.editor.note_lo().load(Relaxed);
|
||||
let note_pt = self.0.editor.note_point();
|
||||
let note_hi = self.0.editor.note_hi();
|
||||
Fill::xy(col_iter!((note_lo..=note_hi).rev() => |note| {
|
||||
Fill::xy(Coll::map((note_lo..=note_hi).rev(), |note, i| {
|
||||
let mut bg = if note == note_pt { TuiTheme::g(64) } else { Color::Reset };
|
||||
let mut fg = TuiTheme::g(160);
|
||||
if let Some((index, _)) = self.0.sampler.recording {
|
||||
|
|
@ -196,11 +188,11 @@ render!(Tui: (self: GrooveboxSamples<'a>) => {
|
|||
} else if self.0.sampler.mapped[note].is_some() {
|
||||
fg = TuiTheme::g(224);
|
||||
}
|
||||
Tui::bg(bg, if let Some(sample) = &self.0.sampler.mapped[note] {
|
||||
Push::y(i, Tui::bg(bg, if let Some(sample) = &self.0.sampler.mapped[note] {
|
||||
Tui::fg(fg, format!("{note:3} ?????? "))
|
||||
} else {
|
||||
Tui::fg(fg, format!("{note:3} (none) "))
|
||||
})
|
||||
}))
|
||||
}))
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use crate::*;
|
|||
|
||||
pub struct Meters<'a>(pub &'a[f32]);
|
||||
|
||||
render!(Tui: (self: Meters<'a>) => col!([
|
||||
render!(Tui: (self: Meters<'a>) => col!(
|
||||
&format!("L/{:>+9.3}", self.0[0]),
|
||||
&format!("R/{:>+9.3}", self.0[1]),
|
||||
]));
|
||||
));
|
||||
|
|
|
|||
|
|
@ -96,10 +96,7 @@ pub struct TrackView<'a> {
|
|||
pub entered: bool,
|
||||
}
|
||||
|
||||
impl<'a> Render<Tui> for TrackView<'a> {
|
||||
fn min_size (&self, area: [u16;2]) -> Perhaps<[u16;2]> {
|
||||
todo!()
|
||||
}
|
||||
impl<'a> Content<Tui> for TrackView<'a> {
|
||||
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
|
||||
todo!();
|
||||
//let mut area = to.area();
|
||||
|
|
@ -131,7 +128,7 @@ impl<'a> Render<Tui> for TrackView<'a> {
|
|||
}
|
||||
|
||||
//impl Content<Tui> for Mixer<Tui> {
|
||||
//fn content (&self) -> impl Render<Tui> {
|
||||
//fn content (&self) -> impl Content<Tui> {
|
||||
//Stack::right(|add| {
|
||||
//for channel in self.tracks.iter() {
|
||||
//add(channel)?;
|
||||
|
|
@ -142,7 +139,7 @@ impl<'a> Render<Tui> for TrackView<'a> {
|
|||
//}
|
||||
|
||||
//impl Content<Tui> for Track<Tui> {
|
||||
//fn content (&self) -> impl Render<Tui> {
|
||||
//fn content (&self) -> impl Content<Tui> {
|
||||
//TrackView {
|
||||
//chain: Some(&self),
|
||||
//direction: tek_core::Direction::Right,
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ render!(Tui: (self: PianoHorizontal) => {
|
|||
let notes = move||PianoHorizontalNotes(self);
|
||||
let cursor = move||PianoHorizontalCursor(self);
|
||||
let border = Fill::xy(Outer(Style::default().fg(self.color.dark.rgb).bg(self.color.darkest.rgb)));
|
||||
let with_border = |x|lay!([border, Padding::xy(0, 0, &x)]);
|
||||
let with_border = |x|lay!(border, Padding::xy(0, 0, &x));
|
||||
with_border(lay!(
|
||||
Push::x(0, row!(
|
||||
//" ",
|
||||
|
|
@ -84,13 +84,13 @@ render!(Tui: (self: PianoHorizontal) => {
|
|||
)),
|
||||
Bsp::e(
|
||||
Fixed::x(self.keys_width, keys()),
|
||||
Fill::xy(lay!([
|
||||
Fill::xy(lay!(
|
||||
&self.size,
|
||||
Fill::xy(lay!(
|
||||
Fill::xy(notes()),
|
||||
Fill::xy(cursor()),
|
||||
))
|
||||
])),
|
||||
)),
|
||||
),
|
||||
)))
|
||||
))
|
||||
|
|
|
|||
|
|
@ -150,11 +150,8 @@ impl Plugin {
|
|||
})
|
||||
}
|
||||
}
|
||||
impl Render<Tui> for Plugin {
|
||||
fn min_size (&self, to: [u16;2]) -> Perhaps<[u16;2]> {
|
||||
Ok(Some(to))
|
||||
}
|
||||
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
|
||||
impl Content<Tui> for Plugin {
|
||||
fn render (&self, to: &mut TuiOutput) {
|
||||
let area = to.area();
|
||||
let [x, y, _, height] = area;
|
||||
let mut width = 20u16;
|
||||
|
|
@ -186,12 +183,11 @@ impl Render<Tui> for Plugin {
|
|||
},
|
||||
_ => {}
|
||||
};
|
||||
draw_header(self, to, x, y, width)?;
|
||||
Ok(())
|
||||
draw_header(self, to, x, y, width);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_header (state: &Plugin, to: &mut TuiOutput, x: u16, y: u16, w: u16) -> Usually<()> {
|
||||
fn draw_header (state: &Plugin, to: &mut TuiOutput, x: u16, y: u16, w: u16) {
|
||||
let style = Style::default().gray();
|
||||
let label1 = format!(" {}", state.name);
|
||||
to.blit(&label1, x + 1, y, Some(style.white().bold()));
|
||||
|
|
@ -199,7 +195,6 @@ fn draw_header (state: &Plugin, to: &mut TuiOutput, x: u16, y: u16, w: u16) -> U
|
|||
let label2 = format!("{}…", &path[..((w as usize - 10).min(path.len()))]);
|
||||
to.blit(&label2, x + 2 + label1.len() as u16, y, Some(style.not_dim()));
|
||||
}
|
||||
Ok(())
|
||||
//Ok(Rect { x, y, width: w, height: 1 })
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ render!(Tui: (self: PoolView<'a>) => {
|
|||
length.focus = Some(*focus);
|
||||
}
|
||||
}
|
||||
add(&Tui::bg(color.base.rgb, Fill::x(col!([
|
||||
add(&Tui::bg(color.base.rgb, Fill::x(col!(
|
||||
Fill::x(lay!(|add|{
|
||||
add(&Fill::x(Align::w(format!(" {i}"))))?;
|
||||
add(&Fill::x(Align::e(Pull::x(1, length.clone()))))
|
||||
|
|
@ -237,7 +237,7 @@ render!(Tui: (self: PoolView<'a>) => {
|
|||
};
|
||||
row2
|
||||
}),
|
||||
]))))?;
|
||||
))))?;
|
||||
if i == self.0.phrase_index() {
|
||||
add(&CORNERS)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ pub struct PhraseSelector {
|
|||
}
|
||||
|
||||
// TODO: Display phrases always in order of appearance
|
||||
render!(Tui: (self: PhraseSelector) => Fixed::xy(24, 1, row!([
|
||||
render!(Tui: (self: PhraseSelector) => Fixed::xy(24, 1, row!(
|
||||
Tui::fg(self.color.lightest.rgb, Tui::bold(true, &self.title)),
|
||||
Tui::fg_bg(self.color.lighter.rgb, self.color.base.rgb, row!([
|
||||
Tui::fg_bg(self.color.lighter.rgb, self.color.base.rgb, row!(
|
||||
format!("{:8}", &self.name[0..8.min(self.name.len())]),
|
||||
Tui::bg(self.color.dark.rgb, &self.time),
|
||||
])),
|
||||
])));
|
||||
)),
|
||||
)));
|
||||
|
||||
impl PhraseSelector {
|
||||
|
||||
|
|
|
|||
|
|
@ -171,10 +171,6 @@ fn draw_sample (
|
|||
}
|
||||
|
||||
impl Content<Tui> for AddSampleModal {
|
||||
fn min_size (&self, to: [u16;2]) -> Perhaps<[u16;2]> {
|
||||
todo!()
|
||||
//Align::Center(()).layout(to)
|
||||
}
|
||||
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
|
||||
todo!()
|
||||
//let area = to.area();
|
||||
|
|
|
|||
|
|
@ -50,8 +50,8 @@ render!(Tui: (self: SamplerTui) => {
|
|||
let fg = self.color.base.rgb;
|
||||
let bg = self.color.darkest.rgb;
|
||||
let border = Fill::xy(Outer(Style::default().fg(fg).bg(bg)));
|
||||
let with_border = |x|lay!([border, Fill::xy(&x)]);
|
||||
let with_size = |x|lay!([self.size, x]);
|
||||
let with_border = |x|lay!(border, Fill::xy(&x));
|
||||
let with_size = |x|lay!(self.size, x);
|
||||
Tui::bg(bg, Fill::xy(with_border(Bsp::s(
|
||||
Tui::fg(self.color.light.rgb, Tui::bold(true, &"Sampler")),
|
||||
with_size(Shrink::y(1, Bsp::e(
|
||||
|
|
|
|||
|
|
@ -49,28 +49,28 @@ render!(Tui: (self: SequencerTui) => {
|
|||
let status = SequencerStatus::from(self);
|
||||
let with_status = |x|Split::n(false, if self.status { 2 } else { 0 }, status, x);
|
||||
let with_editbar = |x|Split::n(false, 1, MidiEditStatus(&self.editor), x);
|
||||
let with_size = |x|lay!([self.size, x]);
|
||||
let with_size = |x|lay!(self.size, x);
|
||||
let editor = with_editbar(with_pool(Fill::xy(&self.editor)));
|
||||
|
||||
let color = self.player.play_phrase().as_ref().map(|(_,p)|
|
||||
p.as_ref().map(|p|p.read().unwrap().color)
|
||||
).flatten().clone();
|
||||
|
||||
let toolbar = Cond::when(self.transport, row!([
|
||||
let toolbar = Cond::when(self.transport, row!(
|
||||
PlayPause(self.clock.is_rolling()),
|
||||
TransportView::new(self, color, true),
|
||||
]));
|
||||
));
|
||||
|
||||
let play_queue = Cond::when(self.selectors, row!([
|
||||
let play_queue = Cond::when(self.selectors, row!(
|
||||
PhraseSelector::play_phrase(&self.player),
|
||||
PhraseSelector::next_phrase(&self.player),
|
||||
]));
|
||||
));
|
||||
|
||||
Min::y(15, with_size(with_status(col!([
|
||||
Min::y(15, with_size(with_status(col!(
|
||||
toolbar,
|
||||
play_queue,
|
||||
editor,
|
||||
]))))
|
||||
))))
|
||||
});
|
||||
audio!(|self:SequencerTui, client, scope|{
|
||||
// Start profiling cycle
|
||||
|
|
|
|||
|
|
@ -2,22 +2,19 @@ use crate::*;
|
|||
|
||||
pub struct MidiEditStatus<'a>(pub &'a MidiEditor);
|
||||
render!(Tui: (self:MidiEditStatus<'a>) => {
|
||||
|
||||
let (color, name, length, looped) = if let Some(phrase) = self.0.phrase().as_ref().map(|p|p.read().unwrap()) {
|
||||
(phrase.color, phrase.name.clone(), phrase.length, phrase.looped)
|
||||
} else {
|
||||
(ItemPalette::from(TuiTheme::g(64)), String::new(), 0, false)
|
||||
};
|
||||
|
||||
let field = move|x, y|row!([
|
||||
let field = move|x, y|row!(
|
||||
Tui::fg_bg(color.lighter.rgb, color.darker.rgb, Tui::bold(true, x)),
|
||||
Tui::fg_bg(color.light.rgb, color.darker.rgb, Tui::bold(true, "│")),
|
||||
Tui::fg_bg(color.lightest.rgb, color.dark.rgb, &y),
|
||||
]);
|
||||
|
||||
);
|
||||
let bg = color.darkest.rgb;
|
||||
let fg = color.lightest.rgb;
|
||||
Tui::bg(bg, Fill::x(Tui::fg(fg, row!([
|
||||
Tui::bg(bg, Fill::x(Tui::fg(fg, row!(
|
||||
field(" Time", format!("{}/{}-{} ({}*{}) {}",
|
||||
self.0.time_point(), self.0.time_start().get(), self.0.time_end(),
|
||||
self.0.time_axis().get(), self.0.time_zoom().get(),
|
||||
|
|
@ -27,7 +24,7 @@ render!(Tui: (self:MidiEditStatus<'a>) => {
|
|||
self.0.note_point(), to_note_name(self.0.note_point()), self.0.note_len(),
|
||||
to_note_name(self.0.note_lo().get()), to_note_name(self.0.note_hi()),
|
||||
self.0.note_axis().get()))
|
||||
]))))
|
||||
))))
|
||||
});
|
||||
|
||||
/// Status bar for sequencer app
|
||||
|
|
@ -50,21 +47,21 @@ from!(|state:&SequencerTui|SequencerStatus = {
|
|||
size: format!("{}x{}│", width, state.size.h()),
|
||||
}
|
||||
});
|
||||
render!(Tui: (self: SequencerStatus) => Fixed::y(2, lay!([
|
||||
render!(Tui: (self: SequencerStatus) => Fixed::y(2, lay!(
|
||||
Self::help(),
|
||||
Fill::xy(Align::se(Tui::fg_bg(TuiTheme::orange(), TuiTheme::g(25), self.stats()))),
|
||||
])));
|
||||
)));
|
||||
impl SequencerStatus {
|
||||
fn help () -> impl Content<Tui> {
|
||||
let single = |binding, command|row!([" ", col!([
|
||||
let single = |binding, command|row!(" ", col!(
|
||||
Tui::fg(TuiTheme::yellow(), binding),
|
||||
command
|
||||
])]);
|
||||
let double = |(b1, c1), (b2, c2)|col!([
|
||||
row!([" ", Tui::fg(TuiTheme::yellow(), b1), " ", c1,]),
|
||||
row!([" ", Tui::fg(TuiTheme::yellow(), b2), " ", c2,]),
|
||||
]);
|
||||
Tui::fg_bg(TuiTheme::g(255), TuiTheme::g(50), row!([
|
||||
));
|
||||
let double = |(b1, c1), (b2, c2)|col!(
|
||||
row!(" ", Tui::fg(TuiTheme::yellow(), b1), " ", c1,),
|
||||
row!(" ", Tui::fg(TuiTheme::yellow(), b2), " ", c2,),
|
||||
);
|
||||
Tui::fg_bg(TuiTheme::g(255), TuiTheme::g(50), row!(
|
||||
single("SPACE", "play/pause"),
|
||||
double(("▲▼▶◀", "cursor"), ("Ctrl", "scroll"), ),
|
||||
double(("a", "append"), ("s", "set note"),),
|
||||
|
|
@ -72,10 +69,10 @@ impl SequencerStatus {
|
|||
double(("[]", "phrase"), ("{}", "order"), ),
|
||||
double(("q", "enqueue"), ("e", "edit"), ),
|
||||
double(("c", "color"), ("", ""),),
|
||||
]))
|
||||
))
|
||||
}
|
||||
fn stats (&self) -> impl Content<Tui> + use<'_> {
|
||||
row!([&self.cpu, &self.size])
|
||||
row!(&self.cpu, &self.size)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -99,10 +96,10 @@ from!(|state: &Groovebox|GrooveboxStatus = {
|
|||
size: format!("{}x{}│", width, state.size.h()),
|
||||
}
|
||||
});
|
||||
render!(Tui: (self: GrooveboxStatus) => Fixed::y(2, lay!([
|
||||
render!(Tui: (self: GrooveboxStatus) => Fixed::y(2, lay!(
|
||||
Self::help(),
|
||||
Fill::xy(Align::se(Tui::fg_bg(TuiTheme::orange(), TuiTheme::g(25), self.stats()))),
|
||||
])));
|
||||
)));
|
||||
impl GrooveboxStatus {
|
||||
fn help () -> impl Content<Tui> {
|
||||
let single = |binding, command|row!(" ", col!(
|
||||
|
|
@ -148,21 +145,21 @@ from!(|state:&ArrangerTui|ArrangerStatus = {
|
|||
size: format!("{}x{}│", width, state.size.h()),
|
||||
}
|
||||
});
|
||||
render!(Tui: (self: ArrangerStatus) => Fixed::y(2, lay!([
|
||||
render!(Tui: (self: ArrangerStatus) => Fixed::y(2, lay!(
|
||||
Self::help(),
|
||||
Fill::xy(Align::se(Tui::fg_bg(TuiTheme::orange(), TuiTheme::g(25), self.stats()))),
|
||||
])));
|
||||
)));
|
||||
impl ArrangerStatus {
|
||||
fn help () -> impl Content<Tui> {
|
||||
let single = |binding, command|row!([" ", col!([
|
||||
let single = |binding, command|row!(" ", col!(
|
||||
Tui::fg(TuiTheme::yellow(), binding),
|
||||
command
|
||||
])]);
|
||||
let double = |(b1, c1), (b2, c2)|col!([
|
||||
row!([" ", Tui::fg(TuiTheme::yellow(), b1), " ", c1,]),
|
||||
row!([" ", Tui::fg(TuiTheme::yellow(), b2), " ", c2,]),
|
||||
]);
|
||||
Tui::fg_bg(TuiTheme::g(255), TuiTheme::g(50), row!([
|
||||
));
|
||||
let double = |(b1, c1), (b2, c2)|col!(
|
||||
row!(" ", Tui::fg(TuiTheme::yellow(), b1), " ", c1,),
|
||||
row!(" ", Tui::fg(TuiTheme::yellow(), b2), " ", c2,),
|
||||
);
|
||||
Tui::fg_bg(TuiTheme::g(255), TuiTheme::g(50), row!(
|
||||
single("SPACE", "play/pause"),
|
||||
single(" Ctrl", " scroll"),
|
||||
single(" ▲▼▶◀", " cell"),
|
||||
|
|
@ -172,9 +169,9 @@ impl ArrangerStatus {
|
|||
double(("a", "append"), ("s", "set"),),
|
||||
double((",.", "length"), ("<>", "triplet"),),
|
||||
double(("[]", "phrase"), ("{}", "order"),),
|
||||
]))
|
||||
))
|
||||
}
|
||||
fn stats (&self) -> impl Content<Tui> + use<'_> {
|
||||
row!([&self.cpu, &self.size])
|
||||
row!(&self.cpu, &self.size)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ impl<W: Content<Tui>> Content<Tui> for Bold<W> {
|
|||
fn content (&self) -> Option<impl Content<Tui>> {
|
||||
Some(&self.1)
|
||||
}
|
||||
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
|
||||
fn render (&self, to: &mut TuiOutput) {
|
||||
to.fill_bold(to.area(), self.0);
|
||||
self.1.render(to)
|
||||
}
|
||||
|
|
@ -38,7 +38,7 @@ impl<W: Content<Tui>> Content<Tui> for Foreground<W> {
|
|||
fn content (&self) -> Option<impl Content<Tui>> {
|
||||
Some(&self.1)
|
||||
}
|
||||
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
|
||||
fn render (&self, to: &mut TuiOutput) {
|
||||
to.fill_fg(to.area(), self.0);
|
||||
self.1.render(to)
|
||||
}
|
||||
|
|
@ -50,7 +50,7 @@ impl<W: Content<Tui>> Content<Tui> for Background<W> {
|
|||
fn content (&self) -> Option<impl Content<Tui>> {
|
||||
Some(&self.1)
|
||||
}
|
||||
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
|
||||
fn render (&self, to: &mut TuiOutput) {
|
||||
to.fill_bg(to.area(), self.0);
|
||||
self.1.render(to)
|
||||
}
|
||||
|
|
@ -62,7 +62,7 @@ impl Content<Tui> for Styled<&str> {
|
|||
fn content (&self) -> Option<impl Content<Tui>> {
|
||||
Some(&self.1)
|
||||
}
|
||||
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
|
||||
fn render (&self, to: &mut TuiOutput) {
|
||||
// FIXME
|
||||
let [x, y, ..] = to.area();
|
||||
//let [w, h] = self.min_size(to.area().wh())?.unwrap();
|
||||
|
|
|
|||
|
|
@ -23,10 +23,10 @@ from_jack!(|jack|TransportTui Self {
|
|||
has_clock!(|self: TransportTui|&self.clock);
|
||||
audio!(|self: TransportTui, client, scope|ClockAudio(self).process(client, scope));
|
||||
handle!(<Tui>|self: TransportTui, from|TransportCommand::execute_with_state(self, from));
|
||||
render!(Tui: (self: TransportTui) => Fixed::y(3, row!([
|
||||
render!(Tui: (self: TransportTui) => Fixed::y(3, row!(
|
||||
" ", Fixed::x(5, PlayPause(false)),
|
||||
" ", Shrink::x(1, TransportView::new(self, Some(self.color), true)),
|
||||
])));
|
||||
)));
|
||||
impl std::fmt::Debug for TransportTui {
|
||||
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||
f.debug_struct("TransportTui")
|
||||
|
|
@ -89,42 +89,42 @@ impl TransportView {
|
|||
}
|
||||
render!(Tui: (self: TransportView) => {
|
||||
let color = self.color;
|
||||
Fixed::y(3, Tui::bg(color.base.rgb, Fill::x(row!([
|
||||
col!([
|
||||
Fixed::y(3, Tui::bg(color.base.rgb, Fill::x(row!(
|
||||
col!(
|
||||
TransportField(" Beat", self.beat.as_str(), &color),
|
||||
TransportField(" Time", format!("{:.1}s", self.current_second).as_str(), &color),
|
||||
TransportField(" BPM", self.bpm.as_str(), &color),
|
||||
]),
|
||||
col!([
|
||||
),
|
||||
col!(
|
||||
TransportField(" Rate", format!("{}", self.sr).as_str(), &color),
|
||||
TransportField(" Chunk", format!("{}", self.chunk).as_str(), &color),
|
||||
TransportField(" Lag", format!("{:.3}ms", self.latency).as_str(), &color),
|
||||
]),
|
||||
col!([
|
||||
),
|
||||
col!(
|
||||
//Field(" CPU%", format!("{:.1}ms", self.perf).as_str(), &color),
|
||||
]),
|
||||
]))))
|
||||
),
|
||||
))))
|
||||
});
|
||||
struct TransportField<'a>(&'a str, &'a str, &'a ItemPalette);
|
||||
render!(Tui: (self: TransportField<'a>) => row!([
|
||||
render!(Tui: (self: TransportField<'a>) => row!(
|
||||
Tui::fg_bg(self.2.lightest.rgb, self.2.base.rgb, Tui::bold(true, self.0)),
|
||||
Tui::fg_bg(self.2.base.rgb, self.2.darkest.rgb, "▌"),
|
||||
Tui::fg_bg(self.2.lightest.rgb, self.2.darkest.rgb, format!("{:>10}", self.1)),
|
||||
Tui::fg_bg(self.2.darkest.rgb, self.2.base.rgb, "▌"),
|
||||
]));
|
||||
));
|
||||
pub struct PlayPause(pub bool);
|
||||
render!(Tui: (self: PlayPause) => Tui::bg(
|
||||
if self.0{Color::Rgb(0,128,0)}else{Color::Rgb(128,64,0)},
|
||||
Fixed::x(5, col!(|add|if self.0 {
|
||||
add(&Tui::fg(Color::Rgb(0, 255, 0), col!([
|
||||
add(&Tui::fg(Color::Rgb(0, 255, 0), col!(
|
||||
" 🭍🭑🬽 ",
|
||||
" 🭞🭜🭘 ",
|
||||
])))
|
||||
)))
|
||||
} else {
|
||||
add(&Tui::fg(Color::Rgb(255, 128, 0), col!([
|
||||
add(&Tui::fg(Color::Rgb(255, 128, 0), col!(
|
||||
" ▗▄▖ ",
|
||||
" ▝▀▘ ",
|
||||
])))
|
||||
)))
|
||||
}))
|
||||
));
|
||||
impl HasFocus for TransportTui {
|
||||
|
|
@ -152,7 +152,7 @@ impl FocusWrap<TransportFocus> for TransportFocus {
|
|||
let focused = focus == self;
|
||||
let corners = focused.then_some(CORNERS);
|
||||
//let highlight = focused.then_some(Tui::bg(Color::Rgb(60, 70, 50)));
|
||||
lay!([corners, /*highlight,*/ *content])
|
||||
lay!(corners, /*highlight,*/ *content)
|
||||
}
|
||||
}
|
||||
impl FocusWrap<TransportFocus> for Option<TransportFocus> {
|
||||
|
|
@ -162,7 +162,7 @@ impl FocusWrap<TransportFocus> for Option<TransportFocus> {
|
|||
let focused = Some(focus) == self;
|
||||
let corners = focused.then_some(CORNERS);
|
||||
//let highlight = focused.then_some(Background(Color::Rgb(60, 70, 50)));
|
||||
lay!([corners, /*highlight,*/ *content])
|
||||
lay!(corners, /*highlight,*/ *content)
|
||||
}
|
||||
}
|
||||
pub trait TransportControl<T>: HasClock + {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue