wip6 (60e):

This commit is contained in:
🪞👃🪞 2024-12-09 18:31:53 +01:00
parent 7d78811f68
commit 3b73da86e0
25 changed files with 212 additions and 227 deletions

View file

@ -1,3 +1,5 @@
// manja s grozde; ikebana s chiaroscuro
use crate::*; use crate::*;
submod! { submod! {
@ -18,7 +20,3 @@ submod! {
split split
stack stack
} }
#[macro_export] macro_rules! lay {
($($expr:expr),* $(,)?) => { Layers::new(move|add|{ $(add(&$expr)?;)* Ok(()) }) }
}

View file

@ -1 +0,0 @@
manja s grozde i ikebana s chiaroscuro

View file

@ -1,6 +1,6 @@
use crate::*; use crate::*;
impl<E: Engine, W: Render<E>> LayoutFixed<E> for W {} impl<E: Engine> LayoutFixed<E> for E {}
pub trait LayoutFixed<E: Engine> { pub trait LayoutFixed<E: Engine> {
fn fixed_x <W: Render<E>> (x: E::Unit, w: W) -> Fixed<E, W> { fn fixed_x <W: Render<E>> (x: E::Unit, w: W) -> Fixed<E, W> {

View file

@ -25,7 +25,6 @@ pub trait LayoutInsetOutset<E: Engine>: LayoutPushPull<E> + LayoutShrinkGrow<E>
/// Shrink from each side /// Shrink from each side
pub enum Inset<E: Engine, T> { pub enum Inset<E: Engine, T> {
_Unused(PhantomData<E>),
/// Decrease width /// Decrease width
X(E::Unit, T), X(E::Unit, T),
/// Decrease height /// Decrease height
@ -40,7 +39,6 @@ impl<E: Engine, T: Render<E>> Inset<E, T> {
Self::X(_, i) => i, Self::X(_, i) => i,
Self::Y(_, i) => i, Self::Y(_, i) => i,
Self::XY(_, _, i) => i, Self::XY(_, _, i) => i,
_ => unreachable!(),
} }
} }
} }
@ -51,14 +49,12 @@ impl<E: Engine, T: Render<E>> Render<E> for Inset<E, T> {
Self::X(x, inner) => E::push_x(*x, E::shrink_x(*x, inner)), Self::X(x, inner) => E::push_x(*x, E::shrink_x(*x, inner)),
Self::Y(y, inner) => E::push_y(*y, E::shrink_y(*y, inner)), Self::Y(y, inner) => E::push_y(*y, E::shrink_y(*y, inner)),
Self::XY(x, y, inner) => E::push_xy(*x, *y, E::shrink_xy(*x, *y, inner)), Self::XY(x, y, inner) => E::push_xy(*x, *y, E::shrink_xy(*x, *y, inner)),
_ => unreachable!(),
}.render(to) }.render(to)
} }
} }
/// Grow on each side /// Grow on each side
pub enum Outset<E: Engine, T: Render<E>> { pub enum Outset<E: Engine, T: Render<E>> {
_Unused(PhantomData<E>),
/// Increase width /// Increase width
X(E::Unit, T), X(E::Unit, T),
/// Increase height /// Increase height
@ -74,7 +70,6 @@ impl<E: Engine, T: Render<E>> Outset<E, T> {
Self::X(_, i) => i, Self::X(_, i) => i,
Self::Y(_, i) => i, Self::Y(_, i) => i,
Self::XY(_, _, i) => i, Self::XY(_, _, i) => i,
_ => unreachable!(),
} }
} }
} }
@ -85,7 +80,6 @@ impl<E: Engine, T: Render<E>> Render<E> for Outset<E, T> {
Self::X(x, ref inner) => E::grow_x(x + x, inner), Self::X(x, ref inner) => E::grow_x(x + x, inner),
Self::Y(y, ref inner) => E::grow_y(y + y, inner), Self::Y(y, ref inner) => E::grow_y(y + y, inner),
Self::XY(x, y, ref inner) => E::grow_xy(x + x, y + y, inner), Self::XY(x, y, ref inner) => E::grow_xy(x + x, y + y, inner),
_ => unreachable!(),
}.min_size(to) }.min_size(to)
} }
fn render (&self, to: &mut E::Output) -> Usually<()> { fn render (&self, to: &mut E::Output) -> Usually<()> {
@ -93,7 +87,6 @@ impl<E: Engine, T: Render<E>> Render<E> for Outset<E, T> {
Self::X(x, ref inner) => E::push_x(x, inner), Self::X(x, ref inner) => E::push_x(x, inner),
Self::Y(y, ref inner) => E::push_y(y, inner), Self::Y(y, ref inner) => E::push_y(y, inner),
Self::XY(x, y, ref inner) => E::push_xy(x, y, inner), Self::XY(x, y, ref inner) => E::push_xy(x, y, inner),
_ => unreachable!(),
}.render(to) }.render(to)
} }
} }

View file

@ -1,5 +1,10 @@
use crate::*; use crate::*;
#[macro_export] macro_rules! lay {
($(move)*|$add:ident|$expr:expr) => { Layers::new($(move)*|$add|$expr) };
($($expr:expr),* $(,)?) => { Layers::new(move|add|{ $(add(&$expr)?;)* Ok(()) }) }
}
pub struct Layers< pub struct Layers<
E: Engine, E: Engine,
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)->Usually<()>)->Usually<()> F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)->Usually<()>)->Usually<()>

View file

@ -3,41 +3,57 @@ use crate::*;
impl<E: Engine> LayoutMinMax<E> for E {} impl<E: Engine> LayoutMinMax<E> for E {}
pub trait LayoutMinMax<E: Engine> { pub trait LayoutMinMax<E: Engine> {
fn min_x <W: Render<E>> (x: E::Unit, w: W) -> Min<E::Unit, W> { fn min_x <W: Render<E>> (x: E::Unit, w: W) -> Min<E, W> {
Min::X(x, w) Min::X(x, w)
} }
fn min_y <W: Render<E>> (y: E::Unit, w: W) -> Min<E::Unit, W> { fn min_y <W: Render<E>> (y: E::Unit, w: W) -> Min<E, W> {
Min::Y(y, w) Min::Y(y, w)
} }
fn min_xy <W: Render<E>> (x: E::Unit, y: E::Unit, w: W) -> Min<E::Unit, W> { fn min_xy <W: Render<E>> (x: E::Unit, y: E::Unit, w: W) -> Min<E, W> {
Min::XY(x, y, w) Min::XY(x, y, w)
} }
fn max_x <W: Render<E>> (x: E::Unit, w: W) -> Max<E::Unit, W> { fn max_x <W: Render<E>> (x: E::Unit, w: W) -> Max<E, W> {
Max::X(x, w) Max::X(x, w)
} }
fn max_y <W: Render<E>> (y: E::Unit, w: W) -> Max<E::Unit, W> { fn max_y <W: Render<E>> (y: E::Unit, w: W) -> Max<E, W> {
Max::Y(y, w) Max::Y(y, w)
} }
fn max_xy <W: Render<E>> (x: E::Unit, y: E::Unit, w: W) -> Max<E::Unit, W> { fn max_xy <W: Render<E>> (x: E::Unit, y: E::Unit, w: W) -> Max<E, W> {
Max::XY(x, y, w) Max::XY(x, y, w)
} }
} }
/// Enforce minimum size of drawing area /// Enforce minimum size of drawing area
pub enum Min<U: Coordinate, T> { pub enum Min<E: Engine, T: Render<E>> {
/// Enforce minimum width /// Enforce minimum width
X(U, T), X(E::Unit, T),
/// Enforce minimum height /// Enforce minimum height
Y(U, T), Y(E::Unit, T),
/// Enforce minimum width and height /// Enforce minimum width and height
XY(U, U, T), XY(E::Unit, E::Unit, T),
} }
impl<N: Coordinate, T> Min<N, T> {
/// Enforce maximum size of drawing area
pub enum Max<E: Engine, T: Render<E>> {
/// Enforce maximum width
X(E::Unit, T),
/// Enforce maximum height
Y(E::Unit, T),
/// Enforce maximum width and height
XY(E::Unit, E::Unit, T),
}
impl<E: Engine, T: Render<E>> Min<E, T> {
pub fn inner (&self) -> &T { pub fn inner (&self) -> &T {
match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, } match self {
Self::X(_, i) => i,
Self::Y(_, i) => i,
Self::XY(_, _, i) => i,
}
} }
} }
impl<E: Engine, T: Render<E>> Render<E> for Min<E::Unit, T> {
impl<E: Engine, T: Render<E>> Render<E> for Min<E, T> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> { fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
Ok(self.inner().min_size(to)?.map(|to|match *self { Ok(self.inner().min_size(to)?.map(|to|match *self {
Self::X(w, _) => [to.w().max(w), to.h()], Self::X(w, _) => [to.w().max(w), to.h()],
@ -53,23 +69,17 @@ impl<E: Engine, T: Render<E>> Render<E> for Min<E::Unit, T> {
} }
} }
/// Enforce maximum size of drawing area impl<E: Engine, T: Render<E>> Max<E, T> {
pub enum Max<U: Coordinate, T> {
/// Enforce maximum width
X(U, T),
/// Enforce maximum height
Y(U, T),
/// Enforce maximum width and height
XY(U, U, T),
}
impl<N: Coordinate, T> Max<N, T> {
fn inner (&self) -> &T { fn inner (&self) -> &T {
match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, } match self {
Self::X(_, i) => i,
Self::Y(_, i) => i,
Self::XY(_, _, i) => i,
}
} }
} }
impl<E: Engine, T: Render<E>> Render<E> for Max<E:: Unit, T> { impl<E: Engine, T: Render<E>> Render<E> for Max<E, T> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> { fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
Ok(self.inner().min_size(to)?.map(|to|match *self { Ok(self.inner().min_size(to)?.map(|to|match *self {
Self::X(w, _) => [to.w().min(w), to.h()], Self::X(w, _) => [to.w().min(w), to.h()],

View file

@ -39,7 +39,6 @@ impl<E: Engine, T: Render<E>> Push<E, T> {
Self::X(_, i) => i, Self::X(_, i) => i,
Self::Y(_, i) => i, Self::Y(_, i) => i,
Self::XY(_, _, i) => i, Self::XY(_, _, i) => i,
_ => unreachable!(),
} }
} }
pub fn x (&self) -> E::Unit { pub fn x (&self) -> E::Unit {
@ -47,7 +46,6 @@ impl<E: Engine, T: Render<E>> Push<E, T> {
Self::X(x, _) => *x, Self::X(x, _) => *x,
Self::Y(_, _) => E::Unit::default(), Self::Y(_, _) => E::Unit::default(),
Self::XY(x, _, _) => *x, Self::XY(x, _, _) => *x,
_ => unreachable!(),
} }
} }
pub fn y (&self) -> E::Unit { pub fn y (&self) -> E::Unit {
@ -55,7 +53,6 @@ impl<E: Engine, T: Render<E>> Push<E, T> {
Self::X(_, _) => E::Unit::default(), Self::X(_, _) => E::Unit::default(),
Self::Y(y, _) => *y, Self::Y(y, _) => *y,
Self::XY(_, y, _) => *y, Self::XY(_, y, _) => *y,
_ => unreachable!(),
} }
} }
} }

View file

@ -1,31 +1,35 @@
use crate::*; use crate::*;
#[macro_export] macro_rules! col { #[macro_export] macro_rules! col {
($($expr:expr),* $(,)?) => { Stack::down(move|add|{ $(add(&$expr)?;)* Ok(()) }) }; ($(move)?|$add:ident|$expr:expr) => { Stack::down($(move)?|$add|$expr) };
($pat:pat in $collection:expr => $item:expr) => { ($pat:pat in $collection:expr => $item:expr) => {
Stack::down(move |add|{ Stack::down(move |add|{
for $pat in $collection { add(&$item)?; } for $pat in $collection { add(&$item)?; }
Ok(()) Ok(())
}) })
} };
($($expr:expr),* $(,)?) => { Stack::down(move|add|{ $(add(&$expr)?;)* Ok(()) }) };
} }
#[macro_export] macro_rules! col_up { #[macro_export] macro_rules! col_up {
($($expr:expr),* $(,)?) => { Stack::down(move|add|{ $(add(&$expr)?;)* Ok(()) }) }; ($(move)?|$add:ident|$expr:expr) => { Stack::up($(move)?|$add|$expr) };
($pat:pat in $collection:expr => $item:expr) => { ($pat:pat in $collection:expr => $item:expr) => {
Stack::up(move |add|{ Stack::up(move |add|{
for $pat in $collection { add(&$item)?; } for $pat in $collection { add(&$item)?; }
Ok(()) Ok(())
}) })
} };
($($expr:expr),* $(,)?) => { Stack::up(move|add|{ $(add(&$expr)?;)* Ok(()) }) };
} }
#[macro_export] macro_rules! row { #[macro_export] macro_rules! row {
($($expr:expr),* $(,)?) => { Stack::right(move|add|{ $(add(&$expr)?;)* Ok(()) }) }; ($(move)?|$add:ident|$expr:expr) => {
Stack::right($(move)?|$add|$expr)
};
($pat:pat in $collection:expr => $item:expr) => { ($pat:pat in $collection:expr => $item:expr) => {
Stack::right(move |add|{ Stack::right(move |add|{ for $pat in $collection { add(&$item)?; } Ok(()) })
for $pat in $collection { add(&$item)?; } };
Ok(()) ($($expr:expr),* $(,)?) => {
}) Stack::right(move|add|{ $(add(&$expr)?;)* Ok(()) })
} };
} }
pub struct Stack< pub struct Stack<
@ -100,7 +104,7 @@ where
(self.0)(&mut |component: &dyn Render<E>| { (self.0)(&mut |component: &dyn Render<E>| {
let max = to.h().minus(h); let max = to.h().minus(h);
if max > E::Unit::ZERO() { if max > E::Unit::ZERO() {
let item = component.max_y(to.h() - h); let item = Tui::max_y(to.h() - h, component);
let size = item.min_size(to)?.map(|size|size.wh()); let size = item.min_size(to)?.map(|size|size.wh());
if let Some([width, height]) = size { if let Some([width, height]) = size {
h = h + height.into(); h = h + height.into();
@ -134,7 +138,7 @@ where
Direction::Down => { Direction::Down => {
(self.0)(&mut |item| { (self.0)(&mut |item| {
if h < area.h() { if h < area.h() {
let item = item.push_y(h).max_y(area.h() - h); let item = Tui::max_y(area.h() - h, Tui::push_y(h, item));
let show = item.min_size(area.wh().into())?.map(|s|s.wh()); let show = item.min_size(area.wh().into())?.map(|s|s.wh());
if let Some([width, height]) = show { if let Some([width, height]) = show {
item.render(to)?; item.render(to)?;
@ -148,7 +152,7 @@ where
Direction::Right => { Direction::Right => {
(self.0)(&mut |item| { (self.0)(&mut |item| {
if w < area.w() { if w < area.w() {
let item = item.push_x(w).max_x(area.w() - w); let item = Tui::max_x(area.w() - w, Tui::push_x(w, item));
let show = item.min_size(area.wh().into())?.map(|s|s.wh()); let show = item.min_size(area.wh().into())?.map(|s|s.wh());
if let Some([width, height]) = show { if let Some([width, height]) = show {
item.render(to)?; item.render(to)?;

View file

@ -3,7 +3,6 @@ use crate::*;
submod! { submod! {
engine_focus engine_focus
engine_input engine_input
engine_layout
engine_output engine_output
engine_style engine_style

View file

@ -128,7 +128,7 @@ impl StatusBar for SequencerStatusBar {
fn hotkey_fg () -> Color { fn hotkey_fg () -> Color {
TuiTheme::hotkey_fg() TuiTheme::hotkey_fg()
} }
fn update (&mut self, state: &SequencerTui) { fn update (&mut self, _: &SequencerTui) {
todo!() todo!()
} }
} }

View file

@ -90,12 +90,11 @@ impl StatusBar for TransportStatusBar {
fn hotkey_fg () -> Color { fn hotkey_fg () -> Color {
TuiTheme::hotkey_fg() TuiTheme::hotkey_fg()
} }
fn update (&mut self, state: &()) { fn update (&mut self, _: &()) {
todo!() todo!()
} }
} }
render!(|self: TransportStatusBar|{ render!(|self: TransportStatusBar|{
todo!(); "todo"
""
}); });

View file

@ -34,7 +34,7 @@ impl Command<ArrangerTui> for ArrangerCommand {
Phrases(cmd) => cmd.execute(&mut state.phrases)?.map(Phrases), Phrases(cmd) => cmd.execute(&mut state.phrases)?.map(Phrases),
Editor(cmd) => cmd.execute(&mut state.editor)?.map(Editor), Editor(cmd) => cmd.execute(&mut state.editor)?.map(Editor),
Clock(cmd) => cmd.execute(state)?.map(Clock), Clock(cmd) => cmd.execute(state)?.map(Clock),
Zoom(zoom) => { todo!(); }, Zoom(_) => { todo!(); },
Select(selected) => { Select(selected) => {
*state.selected_mut() = selected; *state.selected_mut() = selected;
None None
@ -45,22 +45,22 @@ impl Command<ArrangerTui> for ArrangerCommand {
} }
impl Command<ArrangerTui> for ArrangerSceneCommand { impl Command<ArrangerTui> for ArrangerSceneCommand {
fn execute (self, state: &mut ArrangerTui) -> Perhaps<Self> { fn execute (self, _state: &mut ArrangerTui) -> Perhaps<Self> {
todo!(); //todo!();
Ok(None) Ok(None)
} }
} }
impl Command<ArrangerTui> for ArrangerTrackCommand { impl Command<ArrangerTui> for ArrangerTrackCommand {
fn execute (self, state: &mut ArrangerTui) -> Perhaps<Self> { fn execute (self, _state: &mut ArrangerTui) -> Perhaps<Self> {
todo!(); //todo!();
Ok(None) Ok(None)
} }
} }
impl Command<ArrangerTui> for ArrangerClipCommand { impl Command<ArrangerTui> for ArrangerClipCommand {
fn execute (self, state: &mut ArrangerTui) -> Perhaps<Self> { fn execute (self, _state: &mut ArrangerTui) -> Perhaps<Self> {
todo!(); //todo!();
Ok(None) Ok(None)
} }
} }

View file

@ -38,7 +38,6 @@ impl Command<PhraseListModel> for FileBrowserCommand {
} }
}, },
_ => todo!(), _ => todo!(),
_ => unreachable!()
}, },
Some(PhrasesMode::Export(index, ref mut browser)) => match self { Some(PhrasesMode::Export(index, ref mut browser)) => match self {
Cancel => { Cancel => {
@ -62,7 +61,7 @@ impl InputToCommand<Tui, PhraseListModel> for FileBrowserCommand {
fn input_to_command (state: &PhraseListModel, from: &TuiInput) -> Option<Self> { fn input_to_command (state: &PhraseListModel, from: &TuiInput) -> Option<Self> {
use KeyCode::{Up, Down, Right, Left, Enter, Esc, Char, Backspace}; use KeyCode::{Up, Down, Right, Left, Enter, Esc, Char, Backspace};
use FileBrowserCommand::*; use FileBrowserCommand::*;
if let Some(PhrasesMode::Import(index, browser)) = state.phrases_mode() { if let Some(PhrasesMode::Import(_index, browser)) = state.phrases_mode() {
Some(match from.event() { Some(match from.event() {
key!(Up) => Select( key!(Up) => Select(
browser.index.overflowing_sub(1).0.min(browser.len().saturating_sub(1)) browser.index.overflowing_sub(1).0.min(browser.len().saturating_sub(1))
@ -73,19 +72,19 @@ impl InputToCommand<Tui, PhraseListModel> for FileBrowserCommand {
key!(Right) => Chdir(browser.cwd.clone()), key!(Right) => Chdir(browser.cwd.clone()),
key!(Left) => Chdir(browser.cwd.clone()), key!(Left) => Chdir(browser.cwd.clone()),
key!(Enter) => Confirm, key!(Enter) => Confirm,
key!(Char(c)) => { todo!() }, key!(Char(_)) => { todo!() },
key!(Backspace) => { todo!() }, key!(Backspace) => { todo!() },
key!(Esc) => Self::Cancel, key!(Esc) => Self::Cancel,
_ => return None _ => return None
}) })
} else if let Some(PhrasesMode::Export(index, browser)) = state.phrases_mode() { } else if let Some(PhrasesMode::Export(_index, browser)) = state.phrases_mode() {
Some(match from.event() { Some(match from.event() {
key!(Up) => Select(browser.index.overflowing_sub(1).0.min(browser.len())), key!(Up) => Select(browser.index.overflowing_sub(1).0.min(browser.len())),
key!(Down) => Select(browser.index.saturating_add(1) % browser.len()), key!(Down) => Select(browser.index.saturating_add(1) % browser.len()),
key!(Right) => Chdir(browser.cwd.clone()), key!(Right) => Chdir(browser.cwd.clone()),
key!(Left) => Chdir(browser.cwd.clone()), key!(Left) => Chdir(browser.cwd.clone()),
key!(Enter) => Confirm, key!(Enter) => Confirm,
key!(Char(c)) => { todo!() }, key!(Char(_)) => { todo!() },
key!(Backspace) => { todo!() }, key!(Backspace) => { todo!() },
key!(Esc) => Self::Cancel, key!(Esc) => Self::Cancel,
_ => return None _ => return None

View file

@ -60,7 +60,6 @@ impl InputToCommand<Tui, PhraseEditorModel> for PhraseCommand {
_ => return None _ => return None
}, },
} }
_ => return None
}) })
} }
} }
@ -119,7 +118,6 @@ impl Command<PhraseEditorModel> for PhraseCommand {
} }
None None
}, },
_ => unreachable!()
}) })
} }
} }

View file

@ -14,16 +14,9 @@ pub enum TransportCommand {
impl<T: TransportControl> Command<T> for TransportCommand { impl<T: TransportControl> Command<T> for TransportCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> { fn execute (self, state: &mut T) -> Perhaps<Self> {
use TransportCommand::{Focus, Clock};
use FocusCommand::{Next, Prev};
use ClockCommand::{SetBpm, SetQuant, SetSync};
Ok(match self { Ok(match self {
Focus(cmd) => cmd.execute(state)?.map(Focus), Self::Focus(cmd) => cmd.execute(state)?.map(Self::Focus),
Clock(cmd) => cmd.execute(state)?.map(Clock), Self::Clock(cmd) => cmd.execute(state)?.map(Self::Clock),
//Clock(SetBpm(bpm)) => Some(Clock(SetBpm(state.bpm().set(bpm)))),
//Clock(SetQuant(quant)) => Some(Clock(SetQuant(state.quant().set(quant)))),
//Clock(SetSync(sync)) => Some(Clock(SetSync(state.sync().set(sync)))),
_ => return Ok(None)
}) })
} }
} }

View file

@ -1 +0,0 @@
use crate::*;

View file

@ -105,95 +105,91 @@ pub fn arranger_content_vertical (
let any_size = |_|Ok(Some([0,0])); let any_size = |_|Ok(Some([0,0]));
// track titles // track titles
let header = Tui::reduce( let header = row!((track, w) in tracks.iter().zip(cols.iter().map(|col|col.0)) => {
tracks.iter().zip(cols.iter().map(|col|col.0)), // name and width of track
|prev, (track, w)|{ let name = track.name().read().unwrap();
// name and width of track let max_w = w.saturating_sub(1).min(name.len()).max(2);
let name = track.name().read().unwrap(); let name = format!("{}", &name[0..max_w]);
let max_w = w.saturating_sub(1).min(name.len()).max(2); let name = Tui::bold(true, name);
let name = format!("{}", &name[0..max_w]); // beats elapsed
let name = Tui::bold(true, name); let elapsed = if let Some((_, Some(phrase))) = track.player.play_phrase().as_ref() {
// beats elapsed let length = phrase.read().unwrap().length;
let elapsed = if let Some((_, Some(phrase))) = track.player.play_phrase().as_ref() { let elapsed = track.player.pulses_since_start().unwrap();
let length = phrase.read().unwrap().length; let elapsed = timebase.format_beats_1_short(
let elapsed = track.player.pulses_since_start().unwrap(); (elapsed as usize % length) as f64
let elapsed = timebase.format_beats_1_short( );
(elapsed as usize % length) as f64 format!("▎+{elapsed:>}")
); } else {
format!("▎+{elapsed:>}") String::from("")
};
// beats until switchover
let until_next = track.player.next_phrase().as_ref().map(|(t, _)|{
let target = t.pulse.get();
let current = current.pulse.get();
if target > current {
let remaining = target - current;
format!("▎-{:>}", timebase.format_beats_0_short(remaining))
} else { } else {
String::from("") String::new()
}; }
// beats until switchover }).unwrap_or(String::from(""));
let until_next = track.player.next_phrase().as_ref().map(|(t, _)|{ let timer = Tui::to_south(until_next, elapsed);
let target = t.pulse.get(); // name of active MIDI input
let current = current.pulse.get(); let input = format!("▎>{}", track.player.midi_ins().get(0)
if target > current { .map(|port|port.short_name())
let remaining = target - current; .transpose()?
format!("▎-{:>}", timebase.format_beats_0_short(remaining)) .unwrap_or("(none)".into()));
} else { // name of active MIDI output
String::new() let output = format!("▎<{}", track.player.midi_outs().get(0)
} .map(|port|port.short_name())
}).unwrap_or(String::from("")); .transpose()?
let timer = Tui::to_south(until_next, elapsed); .unwrap_or("(none)".into()));
// name of active MIDI input Tui::push_x(scenes_w,
let input = format!("▎>{}", track.player.midi_ins().get(0) Tui::bg(track.color().rgb,
.map(|port|port.short_name()) Tui::min_xy(w as u16, header_h,
.transpose()? Tui::to_south(name, timer))))
.unwrap_or("(none)".into())); });
// name of active MIDI output
let output = format!("▎<{}", track.player.midi_outs().get(0)
.map(|port|port.short_name())
.transpose()?
.unwrap_or("(none)".into()));
//Tui::to_east(prev, Tui::push_x(scenes_w,
//Tui::bg(track.color().rgb,
//Tui::min_xy(w as u16, header_h,
//Tui::to_south(name, timer)))))
prev
}
);
let height = (view.size.h() as u16).saturating_sub(header_h); let content = Tui::fixed_y(
let scene_rows = scenes.iter().zip(rows.iter().map(|row|row.0)); (view.size.h() as u16).saturating_sub(header_h),
let content = Tui::fixed_y(height, Tui::reduce(scene_rows, |prev, (scene, pulses)| { col!((scene, pulses) in scenes.iter().zip(rows.iter().map(|row|row.0)) => {
let height = 1.max((pulses / PPQ) as u16); let height = 1.max((pulses / PPQ) as u16);
let playing = scene.is_playing(tracks); let playing = scene.is_playing(tracks);
Tui::fixed_y( Tui::fixed_y(
height, height,
Tui::to_east(
Tui::to_east( Tui::to_east(
if playing { "" } else { " " }, Tui::to_east(
Tui::bold(true, scene.name.read().unwrap().as_str()) if playing { "" } else { " " },
), Tui::bold(true, scene.name.read().unwrap().as_str())
Tui::iter( ),
cols.iter().map(|col|col.0).enumerate(), row!((track, w) in cols.iter().map(|col|col.0).enumerate() => {
|(track, w)|Tui::fixed_xy(w as u16, height, Layers::new(move |add|{ Tui::fixed_xy(w as u16, height, Layers::new(move |add|{
let mut bg = clip_bg; let mut bg = clip_bg;
match (tracks.get(track), scene.clips.get(track)) { match (tracks.get(track), scene.clips.get(track)) {
(Some(track), Some(Some(phrase))) => { (Some(track), Some(Some(phrase))) => {
let name = &(phrase as &Arc<RwLock<Phrase>>).read().unwrap().name; let name = &(phrase as &Arc<RwLock<Phrase>>).read().unwrap().name;
let name = format!("{}", name); let name = format!("{}", name);
let max_w = name.len().min((w as usize).saturating_sub(2)); let max_w = name.len().min((w as usize).saturating_sub(2));
let color = phrase.read().unwrap().color; let color = phrase.read().unwrap().color;
add(&name.as_str()[0..max_w].push_x(1).fixed_x(w as u16))?; bg = color.dark.rgb;
bg = color.dark.rgb; if let Some((_, Some(ref playing))) = track.player.play_phrase() {
if let Some((_, Some(ref playing))) = track.player.play_phrase() { if *playing.read().unwrap() == *phrase.read().unwrap() {
if *playing.read().unwrap() == *phrase.read().unwrap() { bg = color.light.rgb
bg = color.light.rgb }
} };
}; add(&Tui::fixed_x(w as u16, Tui::push_x(1, &name.as_str()[0..max_w])))?;
}, },
_ => {} _ => {}
}; };
add(&Background(bg)) //add(&Background(bg))
})) Ok(())
}))
})
) )
) )
) }));
}));
let arrangement = Layers::new(move |add|{ let arrangement = Tui::bg(bg.rgb, lay!(move|add|{
// column separators // column separators
add(&Widget::new(any_size, move|to: &mut TuiOutput|{ add(&Widget::new(any_size, move|to: &mut TuiOutput|{
let style = Some(Style::default().fg(sep_fg)); let style = Some(Style::default().fg(sep_fg));
@ -273,7 +269,7 @@ pub fn arranger_content_vertical (
else { area.clip_w(scenes_w).clip_h(header_h) }, &CORNERS)? else { area.clip_w(scenes_w).clip_h(header_h) }, &CORNERS)?
}) })
})) }))
}).bg(bg.rgb); }));
let color = TuiTheme::title_fg(view.arranger_focused()); let color = TuiTheme::title_fg(view.arranger_focused());
let size = format!("{}x{}", view.size.w(), view.size.h()); let size = format!("{}x{}", view.size.w(), view.size.h());
let lower_right = Tui::at_se(Tui::fill_xy(Tui::pull_x(1, Tui::fg(color, size)))); let lower_right = Tui::at_se(Tui::fill_xy(Tui::pull_x(1, Tui::fg(color, size))));

View file

@ -6,13 +6,13 @@ impl Content<Tui> for FileBrowser {
let mut i = 0; let mut i = 0;
for (_, name) in self.dirs.iter() { for (_, name) in self.dirs.iter() {
if i >= self.scroll { if i >= self.scroll {
add(&TuiStyle::bold(name.as_str(), i == self.index))?; add(&Tui::bold(i == self.index, name.as_str()))?;
} }
i += 1; i += 1;
} }
for (_, name) in self.files.iter() { for (_, name) in self.files.iter() {
if i >= self.scroll { if i >= self.scroll {
add(&TuiStyle::bold(name.as_str(), i == self.index))?; add(&Tui::bold(i == self.index, name.as_str()))?;
} }
i += 1; i += 1;
} }

View file

@ -229,7 +229,7 @@ impl PhraseViewMode {
let style = Some(Style::default().fg(Color::Rgb(192, 192, 192)).bg(Color::Rgb(0, 0, 0))); let style = Some(Style::default().fg(Color::Rgb(192, 192, 192)).bg(Color::Rgb(0, 0, 0)));
match self { match self {
Self::PianoHorizontal { .. } => { Self::PianoHorizontal { .. } => {
let [x0, y0, _, h] = to.area().xywh(); let [x0, y0, _, _] = to.area().xywh();
for (y, note) in (note_lo..=note_hi).rev().enumerate() { for (y, note) in (note_lo..=note_hi).rev().enumerate() {
to.blit(&match note % 12 { to.blit(&match note % 12 {
11 => "██", 11 => "██",

View file

@ -1,19 +1,19 @@
use crate::*; use crate::*;
render!(|self:PhraseLength|{ render!(|self:PhraseLength|{
let bars = self.bars_string().as_str(); let bars = ||self.bars_string();
let beats = self.beats_string().as_str(); let beats = ||self.beats_string();
let ticks = self.ticks_string().as_str(); let ticks = ||self.ticks_string();
Tui::reduce(match self.focus { row!(|add|(match self.focus {
None => None =>
[" ", bars, "B", beats, "b", ticks, "T"], add(&row!(" ", bars(), "B", beats(), "b", ticks(), "T")),
Some(PhraseLengthFocus::Bar) => Some(PhraseLengthFocus::Bar) =>
["[", bars, "]", beats, "b", ticks, "T"], add(&row!("[", bars(), "]", beats(), "b", ticks(), "T")),
Some(PhraseLengthFocus::Beat) => Some(PhraseLengthFocus::Beat) =>
[" ", bars, "[", beats, "]", ticks, "T"], add(&row!(" ", bars(), "[", beats(), "]", ticks(), "T")),
Some(PhraseLengthFocus::Tick) => Some(PhraseLengthFocus::Tick) =>
[" ", bars, "B", beats, "[", ticks, "]"], add(&row!(" ", bars(), "B", beats(), "[", ticks(), "]")),
}.iter(), Tui::to_east) }))
//Layers::new(move|add|{ //Layers::new(move|add|{
//match self.focus { //match self.focus {
//None => add(&row!( //None => add(&row!(

View file

@ -18,7 +18,7 @@ render!(|self: SequencerTui|{
TransportView::from(self), TransportView::from(self),
Tui::min_y( Tui::min_y(
20, 20,
Tui::split_right( Tui::to_east(
20, 20,
Tui::to_south( Tui::to_south(
Tui::fixed_y(4, play), Tui::fixed_y(4, play),
@ -35,46 +35,39 @@ render!(|self: SequencerTui|{
}); });
render!(|self: SequencerStatusBar|{ render!(|self: SequencerStatusBar|{
let orange = Color::Rgb(255,128,0); let orange = Color::Rgb(255,128,0);
let yellow = Color::Rgb(255,255,0);
let light = Color::Rgb(100,100,100); let modeline = {
let dark = Color::Rgb(100,100,100); let light = Color::Rgb(100,100,100);
let black = Color::Rgb(0,0,0); let yellow = Color::Rgb(255,255,0);
let modeline = Tui::to_east( let black = Color::Rgb(0,0,0);
Tui::bg( Tui::to_east(
orange, Tui::bg(orange, Tui::fg(black, Tui::bold(true, self.mode))),
Tui::fg( Tui::bg(light, row!((prefix, hotkey, suffix) in self.help.iter() => {
black, row!(" ", prefix, Tui::fg(yellow, *hotkey), suffix)
Tui::bold( }))
true,
Tui::text(self.mode)))),
Tui::bg(
light,
Tui::reduce(
self.help.iter(),
|(prefix, hotkey, suffix)|Tui::reduce(
Tui::to_east,
[widget(&" "), widget(*prefix), widget(Tui::fg(yellow, *hotkey)), widget(*suffix)]
),
)
) )
); };
let statusbar = Tui::bg(
dark, let statusbar = {
Tui::reduce( let dark = Color::Rgb(100,100,100);
Tui::to_east, let cpu = &self.cpu;
[ let res = &self.res;
Tui::fg(orange, widget(&self.cpu)), let size = &self.size;
Tui::fg(orange, widget(&self.res)), Tui::bg(dark, row!(
Tui::fg(orange, widget(&self.size)), Tui::fg(orange, cpu),
] Tui::fg(orange, res),
) Tui::fg(orange, size),
); ))
if self.width > 60 { };
return Stack::Right(modeline, statusbar);
} lay!(|add|if self.width > 60 {
if self.width > 0 { add(&row!(modeline, statusbar))
return Stack::Down(modeline, statusbar); } else if self.width > 0 {
} add(&col!(modeline, statusbar))
return Stack::None } else {
Ok(())
})
}); });

View file

@ -10,11 +10,14 @@ pub trait StatusBar: Render<Tui> {
Self: Sized Self: Sized
{ {
let hotkey_fg = Self::hotkey_fg(); let hotkey_fg = Self::hotkey_fg();
Tui::reduce(commands.iter(), |prev, [a, b, c]| row!([a, b, c] in commands.iter() => {
Tui::to_east(prev, row!(a, Tui::fg(hotkey_fg, Tui::bold(true, b)), c)
Tui::to_east(a, })
Tui::to_east(Tui::fg(hotkey_fg, Tui::bold(true, b)), //Tui::reduce(commands.iter(), |prev, [a, b, c]|
c)))) //Tui::to_east(prev,
//Tui::to_east(a,
//Tui::to_east(Tui::fg(hotkey_fg, Tui::bold(true, b)),
//c))))
} }
fn with <'a> (state: &'a Self::State, content: impl Render<Tui>) -> impl Render<Tui> fn with <'a> (state: &'a Self::State, content: impl Render<Tui>) -> impl Render<Tui>

View file

@ -21,7 +21,7 @@ pub struct TransportView {
} }
render!(|self: TransportView|{ render!(|self: TransportView|{
let Self { state, selected, focused, bpm, sync, quant, beat, msu, } = self; let Self { state, .. } = self;// selected, focused, bpm, sync, quant, beat, msu, } = self;
let world = Tui::to_east("│World ", Tui::to_east(format!("│0 (0)"), //sample(chunk) let world = Tui::to_east("│World ", Tui::to_east(format!("│0 (0)"), //sample(chunk)
Tui::to_east(format!("│00m00s000u"), /*msu*/ format!("│00B 0b 00/00"), /*bbt*/))); Tui::to_east(format!("│00m00s000u"), /*msu*/ format!("│00B 0b 00/00"), /*bbt*/)));
let timer = Tui::either( let timer = Tui::either(