mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-09 05:06:43 +01:00
refactor bsp, rebalance color, BIG PLAY BUTTON
This commit is contained in:
parent
dcd6bc24a7
commit
d401870b2d
9 changed files with 215 additions and 152 deletions
|
|
@ -86,7 +86,7 @@ impl Default for MidiPointModel {
|
||||||
fn default () -> Self {
|
fn default () -> Self {
|
||||||
Self {
|
Self {
|
||||||
time_point: Arc::new(0.into()),
|
time_point: Arc::new(0.into()),
|
||||||
note_point: Arc::new(0.into()),
|
note_point: Arc::new(36.into()),
|
||||||
note_len: Arc::new(24.into()),
|
note_len: Arc::new(24.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,11 +61,11 @@ impl From<Color> for ItemPalette {
|
||||||
impl From<ItemColor> for ItemPalette {
|
impl From<ItemColor> for ItemPalette {
|
||||||
fn from (base: ItemColor) -> Self {
|
fn from (base: ItemColor) -> Self {
|
||||||
let mut light = base.okhsl.clone();
|
let mut light = base.okhsl.clone();
|
||||||
light.lightness = (light.lightness * 1.33).min(Okhsl::<f32>::max_lightness());
|
light.lightness = (light.lightness * 4. / 3.).min(Okhsl::<f32>::max_lightness());
|
||||||
let mut lighter = light.clone();
|
let mut lighter = light.clone();
|
||||||
lighter.lightness = (lighter.lightness * 1.33).min(Okhsl::<f32>::max_lightness());
|
lighter.lightness = (lighter.lightness * 5. / 3.).min(Okhsl::<f32>::max_lightness());
|
||||||
let mut lightest = lighter.clone();
|
let mut lightest = lighter.clone();
|
||||||
lightest.lightness = (lightest.lightness * 1.33).min(Okhsl::<f32>::max_lightness());
|
lightest.lightness = (lightest.lightness * 4. / 3.).min(Okhsl::<f32>::max_lightness());
|
||||||
|
|
||||||
let mut dark = base.okhsl.clone();
|
let mut dark = base.okhsl.clone();
|
||||||
dark.lightness = (dark.lightness * 0.75).max(Okhsl::<f32>::min_lightness());
|
dark.lightness = (dark.lightness * 0.75).max(Okhsl::<f32>::min_lightness());
|
||||||
|
|
|
||||||
|
|
@ -1,105 +1,150 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
impl<E: Engine> LayoutBspStatic<E> for E {}
|
pub enum Bsp<E: Engine, X: Render<E>, Y: Render<E>> {
|
||||||
|
/// X is north of Y
|
||||||
|
N(Option<X>, Option<Y>),
|
||||||
|
/// X is south of Y
|
||||||
|
S(Option<X>, Option<Y>),
|
||||||
|
/// X is east of Y
|
||||||
|
E(Option<X>, Option<Y>),
|
||||||
|
/// X is west of Y
|
||||||
|
W(Option<X>, Option<Y>),
|
||||||
|
/// X is above Y
|
||||||
|
A(Option<X>, Option<Y>),
|
||||||
|
/// X is below Y
|
||||||
|
B(Option<X>, Option<Y>),
|
||||||
|
/// Should be avoided.
|
||||||
|
Null(PhantomData<E>),
|
||||||
|
}
|
||||||
|
|
||||||
pub trait LayoutBspStatic<E: Engine>: {
|
render!(|self:Bsp<E: Engine, X: Render<E>, Y: Render<E>>|match self {
|
||||||
fn over <A: Render<E>, B: Render<E>> (a: A, b: B) -> Over<E, A, B> {
|
Bsp::Null(_) => { () },
|
||||||
Over(Default::default(), a, b)
|
Bsp::N(a, b) => { todo!("") },
|
||||||
|
Bsp::S(a, b) => { todo!("") },
|
||||||
|
Bsp::E(a, b) => { todo!("") },
|
||||||
|
Bsp::W(a, b) => { todo!("") },
|
||||||
|
Bsp::A(a, b) => { todo!("") },
|
||||||
|
Bsp::B(a, b) => { todo!("") },
|
||||||
|
});
|
||||||
|
|
||||||
|
impl<E: Engine, X: Render<E>, Y: Render<E>> Bsp<E, X, Y> {
|
||||||
|
pub fn new (x: X) -> Self { Self::A(Some(x), None) }
|
||||||
|
pub fn n (x: X, y: Y) -> Self { Self::N(Some(x), Some(y)) }
|
||||||
|
pub fn s (x: X, y: Y) -> Self { Self::S(Some(x), Some(y)) }
|
||||||
|
pub fn e (x: X, y: Y) -> Self { Self::E(Some(x), Some(y)) }
|
||||||
|
pub fn w (x: X, y: Y) -> Self { Self::W(Some(x), Some(y)) }
|
||||||
|
pub fn a (x: X, y: Y) -> Self { Self::A(Some(x), Some(y)) }
|
||||||
|
pub fn b (x: X, y: Y) -> Self { Self::B(Some(x), Some(y)) }
|
||||||
}
|
}
|
||||||
fn under <A: Render<E>, B: Render<E>> (a: A, b: B) -> Under<E, A, B> {
|
|
||||||
Under(Default::default(), a, b)
|
impl<E: Engine, X: Render<E>, Y: Render<E>> Default for Bsp<E, X, Y> {
|
||||||
}
|
fn default () -> Self {
|
||||||
fn to_north <A: Render<E>, B: Render<E>> (a: A, b: B) -> ToNorth<E, A, B> {
|
Self::Null(Default::default())
|
||||||
ToNorth(None, a, b)
|
|
||||||
}
|
|
||||||
fn to_south <A: Render<E>, B: Render<E>> (a: A, b: B) -> ToSouth<E, A, B> {
|
|
||||||
ToSouth(None, a, b)
|
|
||||||
}
|
|
||||||
fn to_east <A: Render<E>, B: Render<E>> (a: A, b: B) -> ToEast<E, A, B> {
|
|
||||||
ToEast(None, a, b)
|
|
||||||
}
|
|
||||||
fn to_west <A: Render<E>, B: Render<E>> (a: A, b: B) -> ToWest<E, A, B> {
|
|
||||||
ToWest(None, a, b)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait LayoutBspFixedStatic<E: Engine>: {
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
fn to_north <A: Render<E>, B: Render<E>> (n: E::Unit, a: A, b: B) -> ToNorth<E, A, B> {
|
|
||||||
ToNorth(Some(n), a, b)
|
|
||||||
}
|
|
||||||
fn to_south <A: Render<E>, B: Render<E>> (n: E::Unit, a: A, b: B) -> ToSouth<E, A, B> {
|
|
||||||
ToSouth(Some(n), a, b)
|
|
||||||
}
|
|
||||||
fn to_east <A: Render<E>, B: Render<E>> (n: E::Unit, a: A, b: B) -> ToEast<E, A, B> {
|
|
||||||
ToEast(Some(n), a, b)
|
|
||||||
}
|
|
||||||
fn to_west <A: Render<E>, B: Render<E>> (n: E::Unit, a: A, b: B) -> ToWest<E, A, B> {
|
|
||||||
ToWest(Some(n), a, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Over<E: Engine, A: Render<E>, B: Render<E>>(PhantomData<E>, A, B);
|
//impl<E: Engine> LayoutBspStatic<E> for E {}
|
||||||
|
|
||||||
pub struct Under<E: Engine, A: Render<E>, B: Render<E>>(PhantomData<E>, A, B);
|
//pub trait LayoutBspStatic<E: Engine>: {
|
||||||
|
//fn n <A: Render<E>, B: Render<E>> (a: A, b: B) -> ToNorth<E, A, B> {
|
||||||
|
//ToNorth(None, a, b)
|
||||||
|
//}
|
||||||
|
//fn s <A: Render<E>, B: Render<E>> (a: A, b: B) -> ToSouth<E, A, B> {
|
||||||
|
//ToSouth(None, a, b)
|
||||||
|
//}
|
||||||
|
//fn e <A: Render<E>, B: Render<E>> (a: A, b: B) -> ToEast<E, A, B> {
|
||||||
|
//ToEast(None, a, b)
|
||||||
|
//}
|
||||||
|
//fn w <A: Render<E>, B: Render<E>> (a: A, b: B) -> ToWest<E, A, B> {
|
||||||
|
//ToWest(None, a, b)
|
||||||
|
//}
|
||||||
|
//fn i <A: Render<E>, B: Render<E>> (a: A, b: B) -> Over<E, A, B> {
|
||||||
|
//Over(Default::default(), a, b)
|
||||||
|
//}
|
||||||
|
//fn o <A: Render<E>, B: Render<E>> (a: A, b: B) -> Under<E, A, B> {
|
||||||
|
//Under(Default::default(), a, b)
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
||||||
pub struct ToNorth<E: Engine, A: Render<E>, B: Render<E>>(Option<E::Unit>, A, B);
|
//pub trait LayoutBspFixedStatic<E: Engine>: {
|
||||||
|
//fn to_north <A: Render<E>, B: Render<E>> (n: E::Unit, a: A, b: B) -> ToNorth<E, A, B> {
|
||||||
|
//ToNorth(Some(n), a, b)
|
||||||
|
//}
|
||||||
|
//fn to_south <A: Render<E>, B: Render<E>> (n: E::Unit, a: A, b: B) -> ToSouth<E, A, B> {
|
||||||
|
//ToSouth(Some(n), a, b)
|
||||||
|
//}
|
||||||
|
//fn to_east <A: Render<E>, B: Render<E>> (n: E::Unit, a: A, b: B) -> ToEast<E, A, B> {
|
||||||
|
//ToEast(Some(n), a, b)
|
||||||
|
//}
|
||||||
|
//fn to_west <A: Render<E>, B: Render<E>> (n: E::Unit, a: A, b: B) -> ToWest<E, A, B> {
|
||||||
|
//ToWest(Some(n), a, b)
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
||||||
pub struct ToSouth<E: Engine, A: Render<E>, B: Render<E>>(Option<E::Unit>, A, B);
|
//pub struct Over<E: Engine, A: Render<E>, B: Render<E>>(PhantomData<E>, A, B);
|
||||||
|
|
||||||
pub struct ToEast<E: Engine, A, B>(Option<E::Unit>, A, B);
|
//pub struct Under<E: Engine, A: Render<E>, B: Render<E>>(PhantomData<E>, A, B);
|
||||||
|
|
||||||
pub struct ToWest<E: Engine, A: Render<E>, B: Render<E>>(Option<E::Unit>, A, B);
|
//pub struct ToNorth<E: Engine, A: Render<E>, B: Render<E>>(Option<E::Unit>, A, B);
|
||||||
|
|
||||||
impl<E: Engine, A: Render<E>, B: Render<E>> Render<E> for Over<E, A, B> {
|
//pub struct ToSouth<E: Engine, A: Render<E>, B: Render<E>>(Option<E::Unit>, A, B);
|
||||||
fn min_size (&self, _: E::Size) -> Perhaps<E::Size> {
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
fn render (&self, _: &mut E::Output) -> Usually<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Engine, A: Render<E>, B: Render<E>> Render<E> for Under<E, A, B> {
|
//pub struct ToEast<E: Engine, A, B>(Option<E::Unit>, A, B);
|
||||||
fn min_size (&self, _: E::Size) -> Perhaps<E::Size> {
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
fn render (&self, _: &mut E::Output) -> Usually<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Engine, A: Render<E>, B: Render<E>> Render<E> for ToNorth<E, A, B> {
|
//pub struct ToWest<E: Engine, A: Render<E>, B: Render<E>>(Option<E::Unit>, A, B);
|
||||||
fn min_size (&self, _: E::Size) -> Perhaps<E::Size> {
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
fn render (&self, _: &mut E::Output) -> Usually<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Engine, A: Render<E>, B: Render<E>> Render<E> for ToSouth<E, A, B> {
|
//impl<E: Engine, A: Render<E>, B: Render<E>> Render<E> for Over<E, A, B> {
|
||||||
fn min_size (&self, _: E::Size) -> Perhaps<E::Size> {
|
//fn min_size (&self, _: E::Size) -> Perhaps<E::Size> {
|
||||||
todo!();
|
//todo!();
|
||||||
}
|
//}
|
||||||
fn render (&self, _: &mut E::Output) -> Usually<()> {
|
//fn render (&self, _: &mut E::Output) -> Usually<()> {
|
||||||
Ok(())
|
//Ok(())
|
||||||
}
|
//}
|
||||||
}
|
//}
|
||||||
|
|
||||||
impl<E: Engine, A: Render<E>, B: Render<E>> Render<E> for ToWest<E, A, B> {
|
//impl<E: Engine, A: Render<E>, B: Render<E>> Render<E> for Under<E, A, B> {
|
||||||
fn min_size (&self, _: E::Size) -> Perhaps<E::Size> {
|
//fn min_size (&self, _: E::Size) -> Perhaps<E::Size> {
|
||||||
todo!();
|
//todo!();
|
||||||
}
|
//}
|
||||||
fn render (&self, _: &mut E::Output) -> Usually<()> {
|
//fn render (&self, _: &mut E::Output) -> Usually<()> {
|
||||||
Ok(())
|
//Ok(())
|
||||||
}
|
//}
|
||||||
}
|
//}
|
||||||
|
|
||||||
impl<E: Engine, A: Render<E>, B: Render<E>> Render<E> for ToEast<E, A, B> {
|
//impl<E: Engine, A: Render<E>, B: Render<E>> Render<E> for ToNorth<E, A, B> {
|
||||||
fn min_size (&self, _: E::Size) -> Perhaps<E::Size> {
|
//fn min_size (&self, _: E::Size) -> Perhaps<E::Size> {
|
||||||
todo!();
|
//todo!();
|
||||||
}
|
//}
|
||||||
fn render (&self, _: &mut E::Output) -> Usually<()> {
|
//fn render (&self, _: &mut E::Output) -> Usually<()> {
|
||||||
Ok(())
|
//Ok(())
|
||||||
}
|
//}
|
||||||
}
|
//}
|
||||||
|
|
||||||
|
//impl<E: Engine, A: Render<E>, B: Render<E>> Render<E> for ToSouth<E, A, B> {
|
||||||
|
//fn min_size (&self, _: E::Size) -> Perhaps<E::Size> {
|
||||||
|
//todo!();
|
||||||
|
//}
|
||||||
|
//fn render (&self, _: &mut E::Output) -> Usually<()> {
|
||||||
|
//Ok(())
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
||||||
|
//impl<E: Engine, A: Render<E>, B: Render<E>> Render<E> for ToWest<E, A, B> {
|
||||||
|
//fn min_size (&self, _: E::Size) -> Perhaps<E::Size> {
|
||||||
|
//todo!();
|
||||||
|
//}
|
||||||
|
//fn render (&self, _: &mut E::Output) -> Usually<()> {
|
||||||
|
//Ok(())
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
||||||
|
//impl<E: Engine, A: Render<E>, B: Render<E>> Render<E> for ToEast<E, A, B> {
|
||||||
|
//fn min_size (&self, _: E::Size) -> Perhaps<E::Size> {
|
||||||
|
//todo!();
|
||||||
|
//}
|
||||||
|
//fn render (&self, _: &mut E::Output) -> Usually<()> {
|
||||||
|
//Ok(())
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
|
||||||
|
|
@ -173,14 +173,16 @@ impl Tui {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Field(&'static str, String);
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
render!(|self: Field|{
|
//struct Field(&'static str, String);
|
||||||
Tui::to_east("│", Tui::to_east(
|
|
||||||
Tui::bold(true, self.0),
|
//render!(|self: Field|{
|
||||||
Tui::bg(Color::Rgb(0, 0, 0), self.1.as_str()),
|
//Tui::to_east("│", Tui::to_east(
|
||||||
))
|
//Tui::bold(true, self.0),
|
||||||
});
|
//Tui::bg(Color::Rgb(0, 0, 0), self.1.as_str()),
|
||||||
|
//))
|
||||||
|
//});
|
||||||
|
|
||||||
//pub struct TransportView {
|
//pub struct TransportView {
|
||||||
//pub(crate) state: Option<TransportState>,
|
//pub(crate) state: Option<TransportState>,
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,10 @@ impl Command<SequencerTui> for SequencerCommand {
|
||||||
impl InputToCommand<Tui, SequencerTui> for SequencerCommand {
|
impl InputToCommand<Tui, SequencerTui> for SequencerCommand {
|
||||||
fn input_to_command (state: &SequencerTui, input: &TuiInput) -> Option<Self> {
|
fn input_to_command (state: &SequencerTui, input: &TuiInput) -> Option<Self> {
|
||||||
Some(match input.event() {
|
Some(match input.event() {
|
||||||
|
key_pat!(Char('u')) => { todo!("undo") },
|
||||||
|
key_pat!(Char('U')) => { todo!("redo") },
|
||||||
|
key_pat!(Ctrl-Char('k')) => { todo!("keyboard") },
|
||||||
|
|
||||||
// Toggle visibility of phrase pool column
|
// Toggle visibility of phrase pool column
|
||||||
key_pat!(Tab) => ShowPool(!state.show_pool),
|
key_pat!(Tab) => ShowPool(!state.show_pool),
|
||||||
// Enqueue currently edited phrase
|
// Enqueue currently edited phrase
|
||||||
|
|
@ -156,14 +160,16 @@ render!(|self: SequencerTui|{
|
||||||
let with_size = |x|lay!([self.size, x]);
|
let with_size = |x|lay!([self.size, x]);
|
||||||
let editor = with_bar(with_pool(Tui::fill_xy(&self.editor)));
|
let editor = with_bar(with_pool(Tui::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 color = self.player.play_phrase().as_ref().map(|(_,p)|p.as_ref().map(|p|p.read().unwrap().color)).flatten().clone();
|
||||||
let play = Tui::fixed_xy(11, 2, PlayPause(self.clock.is_rolling()));
|
let play = Tui::fixed_xy(5, 2, PlayPause(self.clock.is_rolling()));
|
||||||
let playing = Tui::fixed_xy(14, 2, PhraseSelector::play_phrase(&self.player));
|
|
||||||
let next = Tui::fixed_xy(14, 2, PhraseSelector::next_phrase(&self.player));
|
|
||||||
let transport = Tui::fixed_y(2, TransportView::from((self, color, true)));
|
let transport = Tui::fixed_y(2, TransportView::from((self, color, true)));
|
||||||
let toolbar = row!([play, playing, next, transport]);
|
let toolbar = row!([play, col!([
|
||||||
|
PhraseSelector::play_phrase(&self.player),
|
||||||
|
PhraseSelector::next_phrase(&self.player),
|
||||||
|
]), transport]);
|
||||||
with_size(with_status(col!([ toolbar, editor, ])))
|
with_size(with_status(col!([ toolbar, editor, ])))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
has_size!(<Tui>|self:SequencerTui|&self.size);
|
||||||
has_clock!(|self:SequencerTui|&self.clock);
|
has_clock!(|self:SequencerTui|&self.clock);
|
||||||
has_phrases!(|self:SequencerTui|self.phrases.phrases);
|
has_phrases!(|self:SequencerTui|self.phrases.phrases);
|
||||||
has_editor!(|self:SequencerTui|self.editor);
|
has_editor!(|self:SequencerTui|self.editor);
|
||||||
|
|
@ -176,16 +182,12 @@ pub struct PhraseSelector {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Display phrases always in order of appearance
|
// TODO: Display phrases always in order of appearance
|
||||||
render!(|self: PhraseSelector|Tui::fixed_y(2, col!([
|
render!(|self: PhraseSelector|Tui::fixed_xy(24, 1, row!([
|
||||||
lay!(move|add|{
|
Tui::fg(self.color.lighter.rgb, Tui::bold(true, &self.title)),
|
||||||
add(&Tui::push_x(1, Tui::fg(TuiTheme::g(240), self.title)))?;
|
Tui::bg(self.color.base.rgb, Tui::fg(self.color.lighter.rgb, row!([
|
||||||
add(&Tui::bg(self.color.base.rgb, Tui::fill_x(Tui::inset_x(1, Tui::fill_x(Tui::at_e(
|
format!("{:8}", &self.name[0..8.min(self.name.len())]),
|
||||||
Tui::fg(self.color.lightest.rgb, &self.time)))))))?;
|
Tui::bg(self.color.dark.rgb, &self.time),
|
||||||
Ok(())
|
]))),
|
||||||
}),
|
|
||||||
Tui::bg(self.color.base.rgb,
|
|
||||||
Tui::fg(self.color.lightest.rgb,
|
|
||||||
Tui::bold(true, self.name.clone()))),
|
|
||||||
])));
|
])));
|
||||||
|
|
||||||
impl PhraseSelector {
|
impl PhraseSelector {
|
||||||
|
|
@ -202,7 +204,7 @@ impl PhraseSelector {
|
||||||
} else {
|
} else {
|
||||||
String::from(" ")
|
String::from(" ")
|
||||||
};
|
};
|
||||||
Self { title: "Now:", time, name, color, }
|
Self { title: " Now|", time, name, color, }
|
||||||
}
|
}
|
||||||
// beats until switchover
|
// beats until switchover
|
||||||
pub fn next_phrase <T: HasPlayPhrase> (state: &T) -> Self {
|
pub fn next_phrase <T: HasPlayPhrase> (state: &T) -> Self {
|
||||||
|
|
@ -219,10 +221,17 @@ impl PhraseSelector {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
(time, name.clone(), color)
|
(time, name.clone(), color)
|
||||||
|
} else if let Some((_, Some(phrase))) = state.play_phrase() {
|
||||||
|
let phrase = phrase.read().unwrap();
|
||||||
|
if phrase.loop_on {
|
||||||
|
(" ".into(), phrase.name.clone(), phrase.color.clone())
|
||||||
|
} else {
|
||||||
|
(" ".into(), " ".into(), TuiTheme::g(64).into())
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
(" ".into(), " ".into(), TuiTheme::g(64).into())
|
(" ".into(), " ".into(), TuiTheme::g(64).into())
|
||||||
};
|
};
|
||||||
Self { title: "Next:", time, name, color, }
|
Self { title: " Next|", time, name, color, }
|
||||||
}
|
}
|
||||||
pub fn edit_phrase (phrase: &Option<Arc<RwLock<Phrase>>>) -> Self {
|
pub fn edit_phrase (phrase: &Option<Arc<RwLock<Phrase>>>) -> Self {
|
||||||
let (time, name, color) = if let Some(phrase) = phrase {
|
let (time, name, color) = if let Some(phrase) = phrase {
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ audio!(|self:TransportTui,client,scope|ClockAudio(self).process(client, scope));
|
||||||
render!(|self: TransportTui|TransportView::from((self, None, true)));
|
render!(|self: TransportTui|TransportView::from((self, None, true)));
|
||||||
|
|
||||||
pub struct TransportView {
|
pub struct TransportView {
|
||||||
bg: Color,
|
color: ItemPalette,
|
||||||
focused: bool,
|
focused: bool,
|
||||||
|
|
||||||
sr: String,
|
sr: String,
|
||||||
|
|
@ -66,13 +66,12 @@ impl<T: HasClock> From<(&T, Option<ItemPalette>, bool)> for TransportView {
|
||||||
let bpm = format!("{:.3}", clock.timebase.bpm.get());
|
let bpm = format!("{:.3}", clock.timebase.bpm.get());
|
||||||
let ppq = format!("{:.0}", clock.timebase.ppq.get());
|
let ppq = format!("{:.0}", clock.timebase.ppq.get());
|
||||||
let color = color.unwrap_or(ItemPalette::from(TuiTheme::g(32)));
|
let color = color.unwrap_or(ItemPalette::from(TuiTheme::g(32)));
|
||||||
let bg = color.dark.rgb;
|
|
||||||
if let Some(started) = clock.started.read().unwrap().as_ref() {
|
if let Some(started) = clock.started.read().unwrap().as_ref() {
|
||||||
let current_sample = (clock.global.sample.get() - started.sample.get())/1000.;
|
let current_sample = (clock.global.sample.get() - started.sample.get())/1000.;
|
||||||
let current_usec = clock.global.usec.get() - started.usec.get();
|
let current_usec = clock.global.usec.get() - started.usec.get();
|
||||||
let current_second = current_usec/1000000.;
|
let current_second = current_usec/1000000.;
|
||||||
Self {
|
Self {
|
||||||
bg, focused, sr, bpm, ppq,
|
color, focused, sr, bpm, ppq,
|
||||||
started: true,
|
started: true,
|
||||||
global_sample: format!("{:.0}k", started.sample.get()/1000.),
|
global_sample: format!("{:.0}k", started.sample.get()/1000.),
|
||||||
global_second: format!("{:.1}s", started.usec.get()/1000.),
|
global_second: format!("{:.1}s", started.usec.get()/1000.),
|
||||||
|
|
@ -84,7 +83,7 @@ impl<T: HasClock> From<(&T, Option<ItemPalette>, bool)> for TransportView {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Self {
|
Self {
|
||||||
bg, focused, sr, bpm, ppq,
|
color, focused, sr, bpm, ppq,
|
||||||
started: false,
|
started: false,
|
||||||
global_sample: format!("{:.0}k", clock.global.sample.get()/1000.),
|
global_sample: format!("{:.0}k", clock.global.sample.get()/1000.),
|
||||||
global_second: format!("{:.1}s", clock.global.usec.get()/1000000.),
|
global_second: format!("{:.1}s", clock.global.usec.get()/1000000.),
|
||||||
|
|
@ -99,23 +98,25 @@ impl<T: HasClock> From<(&T, Option<ItemPalette>, bool)> for TransportView {
|
||||||
|
|
||||||
render!(|self: TransportView|{
|
render!(|self: TransportView|{
|
||||||
|
|
||||||
struct Field<'a>(&'a str, &'a str);
|
let color = self.color;
|
||||||
|
|
||||||
|
struct Field<'a>(&'a str, &'a str, &'a ItemPalette);
|
||||||
render!(|self: Field<'a>|row!([
|
render!(|self: Field<'a>|row!([
|
||||||
Tui::fg(Color::Rgb(200, 200, 200), self.0),
|
Tui::bg(Color::Reset, Tui::bold(true,
|
||||||
" ",
|
Tui::fg(self.2.lighter.rgb, self.0))),
|
||||||
Tui::bold(true, Tui::fg(Color::Rgb(220, 220, 220), self.1)),
|
Tui::fg(self.2.lighter.rgb, format!("{:>10}", self.1)),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
Tui::bg(self.bg, Tui::fill_x(row!([
|
Tui::bg(color.base.rgb, Tui::fill_x(row!([
|
||||||
//PlayPause(self.started), " ",
|
//PlayPause(self.started), " ",
|
||||||
col!([
|
col!([
|
||||||
Field(" Beat", self.beat.as_str()),
|
Field(" Beat|", self.beat.as_str(), &color),
|
||||||
Field(" BPM ", self.bpm.as_str()),
|
Field(" BPM|", self.bpm.as_str(), &color),
|
||||||
]),
|
]),
|
||||||
" ",
|
" ",
|
||||||
col!([
|
col!([
|
||||||
Field("Time ", format!("{:.1}s", self.current_second).as_str()),
|
Field(" Time|", format!("{:.1}s", self.current_second).as_str(), &color),
|
||||||
Field("Sample", format!("{:.0}k", self.current_sample).as_str()),
|
Field(" Smpl|", format!("{:.1}k", self.current_sample).as_str(), &color),
|
||||||
]),
|
]),
|
||||||
])))
|
])))
|
||||||
|
|
||||||
|
|
@ -124,11 +125,17 @@ render!(|self: TransportView|{
|
||||||
pub struct PlayPause(pub bool);
|
pub struct PlayPause(pub bool);
|
||||||
render!(|self: PlayPause|Tui::bg(
|
render!(|self: PlayPause|Tui::bg(
|
||||||
if self.0{Color::Rgb(0,128,0)}else{Color::Rgb(128,64,0)},
|
if self.0{Color::Rgb(0,128,0)}else{Color::Rgb(128,64,0)},
|
||||||
Tui::outset_x(1, Tui::fixed_x(9, col!(|add|if self.0 {
|
Tui::fixed_x(5, col!(|add|if self.0 {
|
||||||
add(&Tui::fg(Color::Rgb(0, 255, 0), col!(["▶ PLAYING", "▒ ▒ ▒ ▒ ▒"])))
|
add(&Tui::fg(Color::Rgb(0, 255, 0), col!([
|
||||||
|
" 🭍🭑🬽 ",
|
||||||
|
" 🭞🭜🭘 "
|
||||||
|
])))
|
||||||
} else {
|
} else {
|
||||||
add(&Tui::fg(Color::Rgb(255, 128, 0), col!(["▒ ▒ ▒ ▒ ▒", "⏹ STOPPED"])))
|
add(&Tui::fg(Color::Rgb(255, 128, 0), col!([
|
||||||
})))
|
" ▗▄▖ ",
|
||||||
|
" ▝▀▘ "
|
||||||
|
])))
|
||||||
|
}))
|
||||||
));
|
));
|
||||||
|
|
||||||
impl HasFocus for TransportTui {
|
impl HasFocus for TransportTui {
|
||||||
|
|
|
||||||
|
|
@ -107,15 +107,19 @@ impl Command<PhraseEditorModel> for PhraseCommand {
|
||||||
pub struct PhraseEditorModel {
|
pub struct PhraseEditorModel {
|
||||||
/// Renders the phrase
|
/// Renders the phrase
|
||||||
pub mode: Box<dyn PhraseViewMode>,
|
pub mode: Box<dyn PhraseViewMode>,
|
||||||
|
|
||||||
|
pub size: Measure<Tui>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PhraseEditorModel {
|
impl Default for PhraseEditorModel {
|
||||||
fn default () -> Self {
|
fn default () -> Self {
|
||||||
Self { mode: Box::new(PianoHorizontal::new(None)) }
|
Self { mode: Box::new(PianoHorizontal::new(None)), size: Measure::new() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render!(|self: PhraseEditorModel|self.mode);
|
has_size!(<Tui>|self:PhraseEditorModel|&self.size);
|
||||||
|
render!(|self: PhraseEditorModel|&self.mode);
|
||||||
|
//render!(|self: PhraseEditorModel|lay!(|add|{add(&self.size)?;add(self.mode)}));//bollocks
|
||||||
|
|
||||||
pub trait PhraseViewMode: Render<Tui> + HasSize<Tui> + MidiRange + MidiPoint + Debug + Send + Sync {
|
pub trait PhraseViewMode: Render<Tui> + HasSize<Tui> + MidiRange + MidiPoint + Debug + Send + Sync {
|
||||||
fn buffer_size (&self, phrase: &Phrase) -> (usize, usize);
|
fn buffer_size (&self, phrase: &Phrase) -> (usize, usize);
|
||||||
|
|
@ -130,8 +134,6 @@ pub trait PhraseViewMode: Render<Tui> + HasSize<Tui> + MidiRange + MidiPoint + D
|
||||||
|
|
||||||
impl MidiViewport<Tui> for PhraseEditorModel {}
|
impl MidiViewport<Tui> for PhraseEditorModel {}
|
||||||
|
|
||||||
has_size!(<Tui>|self:PhraseEditorModel|self.mode.size());
|
|
||||||
|
|
||||||
impl MidiRange for PhraseEditorModel {
|
impl MidiRange for PhraseEditorModel {
|
||||||
fn time_zoom (&self) -> usize { self.mode.time_zoom() }
|
fn time_zoom (&self) -> usize { self.mode.time_zoom() }
|
||||||
fn set_time_zoom (&self, x: usize) { self.mode.set_time_zoom(x); }
|
fn set_time_zoom (&self, x: usize) { self.mode.set_time_zoom(x); }
|
||||||
|
|
|
||||||
|
|
@ -13,16 +13,11 @@ pub trait StatusBar: Render<Tui> {
|
||||||
row!([a, b, c] in commands.iter() => {
|
row!([a, b, c] in commands.iter() => {
|
||||||
row!([a, Tui::fg(hotkey_fg, Tui::bold(true, b)), c])
|
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>
|
fn with <'a> (state: &'a Self::State, content: impl Render<Tui>) -> impl Render<Tui>
|
||||||
where Self: Sized, &'a Self::State: Into<Self>
|
where Self: Sized, &'a Self::State: Into<Self>
|
||||||
{
|
{
|
||||||
Tui::to_north(state.into(), content)
|
Bsp::n(state.into(), content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,9 @@ impl Tui {
|
||||||
pub(crate) fn bg <W: Render<Tui>> (color: Color, w: W) -> Background<W> {
|
pub(crate) fn bg <W: Render<Tui>> (color: Color, w: W) -> Background<W> {
|
||||||
Background(color, w)
|
Background(color, w)
|
||||||
}
|
}
|
||||||
|
pub(crate) fn fg_bg <W: Render<Tui>> (fg: Color, bg: Color, w: W) -> Background<Foreground<W>> {
|
||||||
|
Background(bg, Foreground(fg, w))
|
||||||
|
}
|
||||||
pub(crate) fn bold <W: Render<Tui>> (on: bool, w: W) -> Bold<W> {
|
pub(crate) fn bold <W: Render<Tui>> (on: bool, w: W) -> Bold<W> {
|
||||||
Bold(on, w)
|
Bold(on, w)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue