mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
wip6 (60e):
This commit is contained in:
parent
7d78811f68
commit
3b73da86e0
25 changed files with 212 additions and 227 deletions
|
|
@ -1,3 +1,5 @@
|
|||
// manja s grozde; ikebana s chiaroscuro
|
||||
|
||||
use crate::*;
|
||||
|
||||
submod! {
|
||||
|
|
@ -18,7 +20,3 @@ submod! {
|
|||
split
|
||||
stack
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! lay {
|
||||
($($expr:expr),* $(,)?) => { Layers::new(move|add|{ $(add(&$expr)?;)* Ok(()) }) }
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
manja s grozde i ikebana s chiaroscuro
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::*;
|
||||
|
||||
impl<E: Engine, W: Render<E>> LayoutFixed<E> for W {}
|
||||
impl<E: Engine> LayoutFixed<E> for E {}
|
||||
|
||||
pub trait LayoutFixed<E: Engine> {
|
||||
fn fixed_x <W: Render<E>> (x: E::Unit, w: W) -> Fixed<E, W> {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ pub trait LayoutInsetOutset<E: Engine>: LayoutPushPull<E> + LayoutShrinkGrow<E>
|
|||
|
||||
/// Shrink from each side
|
||||
pub enum Inset<E: Engine, T> {
|
||||
_Unused(PhantomData<E>),
|
||||
/// Decrease width
|
||||
X(E::Unit, T),
|
||||
/// Decrease height
|
||||
|
|
@ -40,7 +39,6 @@ impl<E: Engine, T: Render<E>> Inset<E, T> {
|
|||
Self::X(_, i) => i,
|
||||
Self::Y(_, 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::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)),
|
||||
_ => unreachable!(),
|
||||
}.render(to)
|
||||
}
|
||||
}
|
||||
|
||||
/// Grow on each side
|
||||
pub enum Outset<E: Engine, T: Render<E>> {
|
||||
_Unused(PhantomData<E>),
|
||||
/// Increase width
|
||||
X(E::Unit, T),
|
||||
/// Increase height
|
||||
|
|
@ -74,7 +70,6 @@ impl<E: Engine, T: Render<E>> Outset<E, T> {
|
|||
Self::X(_, i) => i,
|
||||
Self::Y(_, 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::Y(y, ref inner) => E::grow_y(y + y, inner),
|
||||
Self::XY(x, y, ref inner) => E::grow_xy(x + x, y + y, inner),
|
||||
_ => unreachable!(),
|
||||
}.min_size(to)
|
||||
}
|
||||
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::Y(y, ref inner) => E::push_y(y, inner),
|
||||
Self::XY(x, y, ref inner) => E::push_xy(x, y, inner),
|
||||
_ => unreachable!(),
|
||||
}.render(to)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
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<
|
||||
E: Engine,
|
||||
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)->Usually<()>)->Usually<()>
|
||||
|
|
|
|||
|
|
@ -3,41 +3,57 @@ use crate::*;
|
|||
impl<E: Engine> LayoutMinMax<E> for E {}
|
||||
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
/// Enforce minimum size of drawing area
|
||||
pub enum Min<U: Coordinate, T> {
|
||||
pub enum Min<E: Engine, T: Render<E>> {
|
||||
/// Enforce minimum width
|
||||
X(U, T),
|
||||
X(E::Unit, T),
|
||||
/// Enforce minimum height
|
||||
Y(U, T),
|
||||
Y(E::Unit, T),
|
||||
/// 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 {
|
||||
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> {
|
||||
Ok(self.inner().min_size(to)?.map(|to|match *self {
|
||||
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
|
||||
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> {
|
||||
impl<E: Engine, T: Render<E>> Max<E, 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> {
|
||||
Ok(self.inner().min_size(to)?.map(|to|match *self {
|
||||
Self::X(w, _) => [to.w().min(w), to.h()],
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ impl<E: Engine, T: Render<E>> Push<E, T> {
|
|||
Self::X(_, i) => i,
|
||||
Self::Y(_, i) => i,
|
||||
Self::XY(_, _, i) => i,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
pub fn x (&self) -> E::Unit {
|
||||
|
|
@ -47,7 +46,6 @@ impl<E: Engine, T: Render<E>> Push<E, T> {
|
|||
Self::X(x, _) => *x,
|
||||
Self::Y(_, _) => E::Unit::default(),
|
||||
Self::XY(x, _, _) => *x,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
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::Y(y, _) => *y,
|
||||
Self::XY(_, y, _) => *y,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,31 +1,35 @@
|
|||
use crate::*;
|
||||
|
||||
#[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) => {
|
||||
Stack::down(move |add|{
|
||||
for $pat in $collection { add(&$item)?; }
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
};
|
||||
($($expr:expr),* $(,)?) => { Stack::down(move|add|{ $(add(&$expr)?;)* Ok(()) }) };
|
||||
}
|
||||
#[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) => {
|
||||
Stack::up(move |add|{
|
||||
for $pat in $collection { add(&$item)?; }
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
};
|
||||
($($expr:expr),* $(,)?) => { Stack::up(move|add|{ $(add(&$expr)?;)* Ok(()) }) };
|
||||
}
|
||||
#[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) => {
|
||||
Stack::right(move |add|{
|
||||
for $pat in $collection { add(&$item)?; }
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
Stack::right(move |add|{ for $pat in $collection { add(&$item)?; } Ok(()) })
|
||||
};
|
||||
($($expr:expr),* $(,)?) => {
|
||||
Stack::right(move|add|{ $(add(&$expr)?;)* Ok(()) })
|
||||
};
|
||||
}
|
||||
|
||||
pub struct Stack<
|
||||
|
|
@ -100,7 +104,7 @@ where
|
|||
(self.0)(&mut |component: &dyn Render<E>| {
|
||||
let max = to.h().minus(h);
|
||||
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());
|
||||
if let Some([width, height]) = size {
|
||||
h = h + height.into();
|
||||
|
|
@ -134,7 +138,7 @@ where
|
|||
Direction::Down => {
|
||||
(self.0)(&mut |item| {
|
||||
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());
|
||||
if let Some([width, height]) = show {
|
||||
item.render(to)?;
|
||||
|
|
@ -148,7 +152,7 @@ where
|
|||
Direction::Right => {
|
||||
(self.0)(&mut |item| {
|
||||
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());
|
||||
if let Some([width, height]) = show {
|
||||
item.render(to)?;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ use crate::*;
|
|||
submod! {
|
||||
engine_focus
|
||||
engine_input
|
||||
engine_layout
|
||||
engine_output
|
||||
engine_style
|
||||
|
||||
|
|
@ -128,7 +128,7 @@ impl StatusBar for SequencerStatusBar {
|
|||
fn hotkey_fg () -> Color {
|
||||
TuiTheme::hotkey_fg()
|
||||
}
|
||||
fn update (&mut self, state: &SequencerTui) {
|
||||
fn update (&mut self, _: &SequencerTui) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,12 +90,11 @@ impl StatusBar for TransportStatusBar {
|
|||
fn hotkey_fg () -> Color {
|
||||
TuiTheme::hotkey_fg()
|
||||
}
|
||||
fn update (&mut self, state: &()) {
|
||||
fn update (&mut self, _: &()) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
render!(|self: TransportStatusBar|{
|
||||
todo!();
|
||||
""
|
||||
"todo"
|
||||
});
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ impl Command<ArrangerTui> for ArrangerCommand {
|
|||
Phrases(cmd) => cmd.execute(&mut state.phrases)?.map(Phrases),
|
||||
Editor(cmd) => cmd.execute(&mut state.editor)?.map(Editor),
|
||||
Clock(cmd) => cmd.execute(state)?.map(Clock),
|
||||
Zoom(zoom) => { todo!(); },
|
||||
Zoom(_) => { todo!(); },
|
||||
Select(selected) => {
|
||||
*state.selected_mut() = selected;
|
||||
None
|
||||
|
|
@ -45,22 +45,22 @@ impl Command<ArrangerTui> for ArrangerCommand {
|
|||
}
|
||||
|
||||
impl Command<ArrangerTui> for ArrangerSceneCommand {
|
||||
fn execute (self, state: &mut ArrangerTui) -> Perhaps<Self> {
|
||||
todo!();
|
||||
fn execute (self, _state: &mut ArrangerTui) -> Perhaps<Self> {
|
||||
//todo!();
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl Command<ArrangerTui> for ArrangerTrackCommand {
|
||||
fn execute (self, state: &mut ArrangerTui) -> Perhaps<Self> {
|
||||
todo!();
|
||||
fn execute (self, _state: &mut ArrangerTui) -> Perhaps<Self> {
|
||||
//todo!();
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl Command<ArrangerTui> for ArrangerClipCommand {
|
||||
fn execute (self, state: &mut ArrangerTui) -> Perhaps<Self> {
|
||||
todo!();
|
||||
fn execute (self, _state: &mut ArrangerTui) -> Perhaps<Self> {
|
||||
//todo!();
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ impl Command<PhraseListModel> for FileBrowserCommand {
|
|||
}
|
||||
},
|
||||
_ => todo!(),
|
||||
_ => unreachable!()
|
||||
},
|
||||
Some(PhrasesMode::Export(index, ref mut browser)) => match self {
|
||||
Cancel => {
|
||||
|
|
@ -62,7 +61,7 @@ impl InputToCommand<Tui, PhraseListModel> for FileBrowserCommand {
|
|||
fn input_to_command (state: &PhraseListModel, from: &TuiInput) -> Option<Self> {
|
||||
use KeyCode::{Up, Down, Right, Left, Enter, Esc, Char, Backspace};
|
||||
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() {
|
||||
key!(Up) => Select(
|
||||
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!(Left) => Chdir(browser.cwd.clone()),
|
||||
key!(Enter) => Confirm,
|
||||
key!(Char(c)) => { todo!() },
|
||||
key!(Char(_)) => { todo!() },
|
||||
key!(Backspace) => { todo!() },
|
||||
key!(Esc) => Self::Cancel,
|
||||
_ => 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() {
|
||||
key!(Up) => Select(browser.index.overflowing_sub(1).0.min(browser.len())),
|
||||
key!(Down) => Select(browser.index.saturating_add(1) % browser.len()),
|
||||
key!(Right) => Chdir(browser.cwd.clone()),
|
||||
key!(Left) => Chdir(browser.cwd.clone()),
|
||||
key!(Enter) => Confirm,
|
||||
key!(Char(c)) => { todo!() },
|
||||
key!(Char(_)) => { todo!() },
|
||||
key!(Backspace) => { todo!() },
|
||||
key!(Esc) => Self::Cancel,
|
||||
_ => return None
|
||||
|
|
|
|||
|
|
@ -60,7 +60,6 @@ impl InputToCommand<Tui, PhraseEditorModel> for PhraseCommand {
|
|||
_ => return None
|
||||
},
|
||||
}
|
||||
_ => return None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -119,7 +118,6 @@ impl Command<PhraseEditorModel> for PhraseCommand {
|
|||
}
|
||||
None
|
||||
},
|
||||
_ => unreachable!()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,16 +14,9 @@ pub enum TransportCommand {
|
|||
|
||||
impl<T: TransportControl> Command<T> for TransportCommand {
|
||||
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
||||
use TransportCommand::{Focus, Clock};
|
||||
use FocusCommand::{Next, Prev};
|
||||
use ClockCommand::{SetBpm, SetQuant, SetSync};
|
||||
Ok(match self {
|
||||
Focus(cmd) => cmd.execute(state)?.map(Focus),
|
||||
Clock(cmd) => cmd.execute(state)?.map(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)
|
||||
Self::Focus(cmd) => cmd.execute(state)?.map(Self::Focus),
|
||||
Self::Clock(cmd) => cmd.execute(state)?.map(Self::Clock),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
use crate::*;
|
||||
|
|
@ -105,95 +105,91 @@ pub fn arranger_content_vertical (
|
|||
let any_size = |_|Ok(Some([0,0]));
|
||||
|
||||
// track titles
|
||||
let header = Tui::reduce(
|
||||
tracks.iter().zip(cols.iter().map(|col|col.0)),
|
||||
|prev, (track, w)|{
|
||||
// name and width of track
|
||||
let name = track.name().read().unwrap();
|
||||
let max_w = w.saturating_sub(1).min(name.len()).max(2);
|
||||
let name = format!("▎{}", &name[0..max_w]);
|
||||
let name = Tui::bold(true, name);
|
||||
// beats elapsed
|
||||
let elapsed = if let Some((_, Some(phrase))) = track.player.play_phrase().as_ref() {
|
||||
let length = phrase.read().unwrap().length;
|
||||
let elapsed = track.player.pulses_since_start().unwrap();
|
||||
let elapsed = timebase.format_beats_1_short(
|
||||
(elapsed as usize % length) as f64
|
||||
);
|
||||
format!("▎+{elapsed:>}")
|
||||
let header = row!((track, w) in tracks.iter().zip(cols.iter().map(|col|col.0)) => {
|
||||
// name and width of track
|
||||
let name = track.name().read().unwrap();
|
||||
let max_w = w.saturating_sub(1).min(name.len()).max(2);
|
||||
let name = format!("▎{}", &name[0..max_w]);
|
||||
let name = Tui::bold(true, name);
|
||||
// beats elapsed
|
||||
let elapsed = if let Some((_, Some(phrase))) = track.player.play_phrase().as_ref() {
|
||||
let length = phrase.read().unwrap().length;
|
||||
let elapsed = track.player.pulses_since_start().unwrap();
|
||||
let elapsed = timebase.format_beats_1_short(
|
||||
(elapsed as usize % length) as f64
|
||||
);
|
||||
format!("▎+{elapsed:>}")
|
||||
} else {
|
||||
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 {
|
||||
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 {
|
||||
String::new()
|
||||
}
|
||||
}).unwrap_or(String::from("▎"));
|
||||
let timer = Tui::to_south(until_next, elapsed);
|
||||
// name of active MIDI input
|
||||
let input = format!("▎>{}", track.player.midi_ins().get(0)
|
||||
.map(|port|port.short_name())
|
||||
.transpose()?
|
||||
.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
|
||||
}
|
||||
);
|
||||
String::new()
|
||||
}
|
||||
}).unwrap_or(String::from("▎"));
|
||||
let timer = Tui::to_south(until_next, elapsed);
|
||||
// name of active MIDI input
|
||||
let input = format!("▎>{}", track.player.midi_ins().get(0)
|
||||
.map(|port|port.short_name())
|
||||
.transpose()?
|
||||
.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::push_x(scenes_w,
|
||||
Tui::bg(track.color().rgb,
|
||||
Tui::min_xy(w as u16, header_h,
|
||||
Tui::to_south(name, timer))))
|
||||
});
|
||||
|
||||
let height = (view.size.h() as u16).saturating_sub(header_h);
|
||||
let scene_rows = scenes.iter().zip(rows.iter().map(|row|row.0));
|
||||
let content = Tui::fixed_y(height, Tui::reduce(scene_rows, |prev, (scene, pulses)| {
|
||||
let height = 1.max((pulses / PPQ) as u16);
|
||||
let playing = scene.is_playing(tracks);
|
||||
Tui::fixed_y(
|
||||
height,
|
||||
Tui::to_east(
|
||||
let content = Tui::fixed_y(
|
||||
(view.size.h() as u16).saturating_sub(header_h),
|
||||
col!((scene, pulses) in scenes.iter().zip(rows.iter().map(|row|row.0)) => {
|
||||
let height = 1.max((pulses / PPQ) as u16);
|
||||
let playing = scene.is_playing(tracks);
|
||||
Tui::fixed_y(
|
||||
height,
|
||||
Tui::to_east(
|
||||
if playing { "▶ " } else { " " },
|
||||
Tui::bold(true, scene.name.read().unwrap().as_str())
|
||||
),
|
||||
Tui::iter(
|
||||
cols.iter().map(|col|col.0).enumerate(),
|
||||
|(track, w)|Tui::fixed_xy(w as u16, height, Layers::new(move |add|{
|
||||
let mut bg = clip_bg;
|
||||
match (tracks.get(track), scene.clips.get(track)) {
|
||||
(Some(track), Some(Some(phrase))) => {
|
||||
let name = &(phrase as &Arc<RwLock<Phrase>>).read().unwrap().name;
|
||||
let name = format!("{}", name);
|
||||
let max_w = name.len().min((w as usize).saturating_sub(2));
|
||||
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;
|
||||
if let Some((_, Some(ref playing))) = track.player.play_phrase() {
|
||||
if *playing.read().unwrap() == *phrase.read().unwrap() {
|
||||
bg = color.light.rgb
|
||||
}
|
||||
};
|
||||
},
|
||||
_ => {}
|
||||
};
|
||||
add(&Background(bg))
|
||||
}))
|
||||
Tui::to_east(
|
||||
if playing { "▶ " } else { " " },
|
||||
Tui::bold(true, scene.name.read().unwrap().as_str())
|
||||
),
|
||||
row!((track, w) in cols.iter().map(|col|col.0).enumerate() => {
|
||||
Tui::fixed_xy(w as u16, height, Layers::new(move |add|{
|
||||
let mut bg = clip_bg;
|
||||
match (tracks.get(track), scene.clips.get(track)) {
|
||||
(Some(track), Some(Some(phrase))) => {
|
||||
let name = &(phrase as &Arc<RwLock<Phrase>>).read().unwrap().name;
|
||||
let name = format!("{}", name);
|
||||
let max_w = name.len().min((w as usize).saturating_sub(2));
|
||||
let color = phrase.read().unwrap().color;
|
||||
bg = color.dark.rgb;
|
||||
if let Some((_, Some(ref playing))) = track.player.play_phrase() {
|
||||
if *playing.read().unwrap() == *phrase.read().unwrap() {
|
||||
bg = color.light.rgb
|
||||
}
|
||||
};
|
||||
add(&Tui::fixed_x(w as u16, Tui::push_x(1, &name.as_str()[0..max_w])))?;
|
||||
},
|
||||
_ => {}
|
||||
};
|
||||
//add(&Background(bg))
|
||||
Ok(())
|
||||
}))
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
}));
|
||||
}));
|
||||
|
||||
let arrangement = Layers::new(move |add|{
|
||||
let arrangement = Tui::bg(bg.rgb, lay!(move|add|{
|
||||
// column separators
|
||||
add(&Widget::new(any_size, move|to: &mut TuiOutput|{
|
||||
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)?
|
||||
})
|
||||
}))
|
||||
}).bg(bg.rgb);
|
||||
}));
|
||||
let color = TuiTheme::title_fg(view.arranger_focused());
|
||||
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))));
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ impl Content<Tui> for FileBrowser {
|
|||
let mut i = 0;
|
||||
for (_, name) in self.dirs.iter() {
|
||||
if i >= self.scroll {
|
||||
add(&TuiStyle::bold(name.as_str(), i == self.index))?;
|
||||
add(&Tui::bold(i == self.index, name.as_str()))?;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
for (_, name) in self.files.iter() {
|
||||
if i >= self.scroll {
|
||||
add(&TuiStyle::bold(name.as_str(), i == self.index))?;
|
||||
add(&Tui::bold(i == self.index, name.as_str()))?;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -229,7 +229,7 @@ impl PhraseViewMode {
|
|||
let style = Some(Style::default().fg(Color::Rgb(192, 192, 192)).bg(Color::Rgb(0, 0, 0)));
|
||||
match self {
|
||||
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() {
|
||||
to.blit(&match note % 12 {
|
||||
11 => "██",
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
use crate::*;
|
||||
|
||||
render!(|self:PhraseLength|{
|
||||
let bars = self.bars_string().as_str();
|
||||
let beats = self.beats_string().as_str();
|
||||
let ticks = self.ticks_string().as_str();
|
||||
Tui::reduce(match self.focus {
|
||||
let bars = ||self.bars_string();
|
||||
let beats = ||self.beats_string();
|
||||
let ticks = ||self.ticks_string();
|
||||
row!(|add|(match self.focus {
|
||||
None =>
|
||||
[" ", bars, "B", beats, "b", ticks, "T"],
|
||||
add(&row!(" ", bars(), "B", beats(), "b", ticks(), "T")),
|
||||
Some(PhraseLengthFocus::Bar) =>
|
||||
["[", bars, "]", beats, "b", ticks, "T"],
|
||||
add(&row!("[", bars(), "]", beats(), "b", ticks(), "T")),
|
||||
Some(PhraseLengthFocus::Beat) =>
|
||||
[" ", bars, "[", beats, "]", ticks, "T"],
|
||||
add(&row!(" ", bars(), "[", beats(), "]", ticks(), "T")),
|
||||
Some(PhraseLengthFocus::Tick) =>
|
||||
[" ", bars, "B", beats, "[", ticks, "]"],
|
||||
}.iter(), Tui::to_east)
|
||||
add(&row!(" ", bars(), "B", beats(), "[", ticks(), "]")),
|
||||
}))
|
||||
//Layers::new(move|add|{
|
||||
//match self.focus {
|
||||
//None => add(&row!(
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ render!(|self: SequencerTui|{
|
|||
TransportView::from(self),
|
||||
Tui::min_y(
|
||||
20,
|
||||
Tui::split_right(
|
||||
Tui::to_east(
|
||||
20,
|
||||
Tui::to_south(
|
||||
Tui::fixed_y(4, play),
|
||||
|
|
@ -35,46 +35,39 @@ render!(|self: SequencerTui|{
|
|||
});
|
||||
|
||||
render!(|self: SequencerStatusBar|{
|
||||
|
||||
let orange = Color::Rgb(255,128,0);
|
||||
let yellow = Color::Rgb(255,255,0);
|
||||
let light = Color::Rgb(100,100,100);
|
||||
let dark = Color::Rgb(100,100,100);
|
||||
let black = Color::Rgb(0,0,0);
|
||||
let modeline = Tui::to_east(
|
||||
Tui::bg(
|
||||
orange,
|
||||
Tui::fg(
|
||||
black,
|
||||
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 modeline = {
|
||||
let light = Color::Rgb(100,100,100);
|
||||
let yellow = Color::Rgb(255,255,0);
|
||||
let black = Color::Rgb(0,0,0);
|
||||
Tui::to_east(
|
||||
Tui::bg(orange, Tui::fg(black, Tui::bold(true, self.mode))),
|
||||
Tui::bg(light, row!((prefix, hotkey, suffix) in self.help.iter() => {
|
||||
row!(" ", prefix, Tui::fg(yellow, *hotkey), suffix)
|
||||
}))
|
||||
)
|
||||
);
|
||||
let statusbar = Tui::bg(
|
||||
dark,
|
||||
Tui::reduce(
|
||||
Tui::to_east,
|
||||
[
|
||||
Tui::fg(orange, widget(&self.cpu)),
|
||||
Tui::fg(orange, widget(&self.res)),
|
||||
Tui::fg(orange, widget(&self.size)),
|
||||
]
|
||||
)
|
||||
);
|
||||
if self.width > 60 {
|
||||
return Stack::Right(modeline, statusbar);
|
||||
}
|
||||
if self.width > 0 {
|
||||
return Stack::Down(modeline, statusbar);
|
||||
}
|
||||
return Stack::None
|
||||
};
|
||||
|
||||
let statusbar = {
|
||||
let dark = Color::Rgb(100,100,100);
|
||||
let cpu = &self.cpu;
|
||||
let res = &self.res;
|
||||
let size = &self.size;
|
||||
Tui::bg(dark, row!(
|
||||
Tui::fg(orange, cpu),
|
||||
Tui::fg(orange, res),
|
||||
Tui::fg(orange, size),
|
||||
))
|
||||
};
|
||||
|
||||
lay!(|add|if self.width > 60 {
|
||||
add(&row!(modeline, statusbar))
|
||||
} else if self.width > 0 {
|
||||
add(&col!(modeline, statusbar))
|
||||
} else {
|
||||
Ok(())
|
||||
})
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,11 +10,14 @@ pub trait StatusBar: Render<Tui> {
|
|||
Self: Sized
|
||||
{
|
||||
let hotkey_fg = Self::hotkey_fg();
|
||||
Tui::reduce(commands.iter(), |prev, [a, b, c]|
|
||||
Tui::to_east(prev,
|
||||
Tui::to_east(a,
|
||||
Tui::to_east(Tui::fg(hotkey_fg, Tui::bold(true, b)),
|
||||
c))))
|
||||
row!([a, b, c] in commands.iter() => {
|
||||
row!(a, Tui::fg(hotkey_fg, Tui::bold(true, b)), c)
|
||||
})
|
||||
//Tui::reduce(commands.iter(), |prev, [a, b, 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>
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ pub struct 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)
|
||||
Tui::to_east(format!("│00m00s000u"), /*msu*/ format!("│00B 0b 00/00"), /*bbt*/)));
|
||||
let timer = Tui::either(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue