mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +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 {
|
||||
Self {
|
||||
time_point: Arc::new(0.into()),
|
||||
note_point: Arc::new(0.into()),
|
||||
note_point: Arc::new(36.into()),
|
||||
note_len: Arc::new(24.into()),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,11 +61,11 @@ impl From<Color> for ItemPalette {
|
|||
impl From<ItemColor> for ItemPalette {
|
||||
fn from (base: ItemColor) -> Self {
|
||||
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();
|
||||
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();
|
||||
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();
|
||||
dark.lightness = (dark.lightness * 0.75).max(Okhsl::<f32>::min_lightness());
|
||||
|
|
|
|||
|
|
@ -1,105 +1,150 @@
|
|||
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>: {
|
||||
fn over <A: Render<E>, B: Render<E>> (a: A, b: B) -> Over<E, A, B> {
|
||||
Over(Default::default(), a, b)
|
||||
}
|
||||
fn under <A: Render<E>, B: Render<E>> (a: A, b: B) -> Under<E, A, B> {
|
||||
Under(Default::default(), a, b)
|
||||
}
|
||||
fn to_north <A: Render<E>, B: Render<E>> (a: A, b: B) -> ToNorth<E, A, B> {
|
||||
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)
|
||||
render!(|self:Bsp<E: Engine, X: Render<E>, Y: Render<E>>|match self {
|
||||
Bsp::Null(_) => { () },
|
||||
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)) }
|
||||
}
|
||||
|
||||
impl<E: Engine, X: Render<E>, Y: Render<E>> Default for Bsp<E, X, Y> {
|
||||
fn default () -> Self {
|
||||
Self::Null(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
fn min_size (&self, _: E::Size) -> Perhaps<E::Size> {
|
||||
todo!();
|
||||
}
|
||||
fn render (&self, _: &mut E::Output) -> Usually<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
//pub struct ToSouth<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 Under<E, A, B> {
|
||||
fn min_size (&self, _: E::Size) -> Perhaps<E::Size> {
|
||||
todo!();
|
||||
}
|
||||
fn render (&self, _: &mut E::Output) -> Usually<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
//pub struct ToEast<E: Engine, A, B>(Option<E::Unit>, 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> {
|
||||
todo!();
|
||||
}
|
||||
fn render (&self, _: &mut E::Output) -> Usually<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
//pub struct ToWest<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 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 Over<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 Under<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(())
|
||||
}
|
||||
}
|
||||
//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> {
|
||||
//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> {
|
||||
//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|{
|
||||
Tui::to_east("│", Tui::to_east(
|
||||
Tui::bold(true, self.0),
|
||||
Tui::bg(Color::Rgb(0, 0, 0), self.1.as_str()),
|
||||
))
|
||||
});
|
||||
//struct Field(&'static str, String);
|
||||
|
||||
//render!(|self: Field|{
|
||||
//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(crate) state: Option<TransportState>,
|
||||
|
|
|
|||
|
|
@ -85,6 +85,10 @@ impl Command<SequencerTui> for SequencerCommand {
|
|||
impl InputToCommand<Tui, SequencerTui> for SequencerCommand {
|
||||
fn input_to_command (state: &SequencerTui, input: &TuiInput) -> Option<Self> {
|
||||
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
|
||||
key_pat!(Tab) => ShowPool(!state.show_pool),
|
||||
// Enqueue currently edited phrase
|
||||
|
|
@ -156,14 +160,16 @@ render!(|self: SequencerTui|{
|
|||
let with_size = |x|lay!([self.size, x]);
|
||||
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 play = Tui::fixed_xy(11, 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 play = Tui::fixed_xy(5, 2, PlayPause(self.clock.is_rolling()));
|
||||
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, ])))
|
||||
});
|
||||
|
||||
has_size!(<Tui>|self:SequencerTui|&self.size);
|
||||
has_clock!(|self:SequencerTui|&self.clock);
|
||||
has_phrases!(|self:SequencerTui|self.phrases.phrases);
|
||||
has_editor!(|self:SequencerTui|self.editor);
|
||||
|
|
@ -176,16 +182,12 @@ pub struct PhraseSelector {
|
|||
}
|
||||
|
||||
// TODO: Display phrases always in order of appearance
|
||||
render!(|self: PhraseSelector|Tui::fixed_y(2, col!([
|
||||
lay!(move|add|{
|
||||
add(&Tui::push_x(1, Tui::fg(TuiTheme::g(240), self.title)))?;
|
||||
add(&Tui::bg(self.color.base.rgb, Tui::fill_x(Tui::inset_x(1, Tui::fill_x(Tui::at_e(
|
||||
Tui::fg(self.color.lightest.rgb, &self.time)))))))?;
|
||||
Ok(())
|
||||
}),
|
||||
Tui::bg(self.color.base.rgb,
|
||||
Tui::fg(self.color.lightest.rgb,
|
||||
Tui::bold(true, self.name.clone()))),
|
||||
render!(|self: PhraseSelector|Tui::fixed_xy(24, 1, row!([
|
||||
Tui::fg(self.color.lighter.rgb, Tui::bold(true, &self.title)),
|
||||
Tui::bg(self.color.base.rgb, Tui::fg(self.color.lighter.rgb, row!([
|
||||
format!("{:8}", &self.name[0..8.min(self.name.len())]),
|
||||
Tui::bg(self.color.dark.rgb, &self.time),
|
||||
]))),
|
||||
])));
|
||||
|
||||
impl PhraseSelector {
|
||||
|
|
@ -200,9 +202,9 @@ impl PhraseSelector {
|
|||
let time = if let Some(elapsed) = state.pulses_since_start_looped() {
|
||||
format!("+{:>}", state.clock().timebase.format_beats_0(elapsed))
|
||||
} else {
|
||||
String::from("")
|
||||
String::from(" ")
|
||||
};
|
||||
Self { title: "Now:", time, name, color, }
|
||||
Self { title: " Now|", time, name, color, }
|
||||
}
|
||||
// beats until switchover
|
||||
pub fn next_phrase <T: HasPlayPhrase> (state: &T) -> Self {
|
||||
|
|
@ -219,17 +221,24 @@ impl PhraseSelector {
|
|||
}
|
||||
};
|
||||
(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 {
|
||||
("".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 {
|
||||
let (time, name, color) = if let Some(phrase) = phrase {
|
||||
let phrase = phrase.read().unwrap();
|
||||
(format!("{}", phrase.length), phrase.name.clone(), phrase.color)
|
||||
} else {
|
||||
("".to_string(), "".to_string(), ItemPalette::from(TuiTheme::g(64)))
|
||||
("".to_string(), " ".to_string(), ItemPalette::from(TuiTheme::g(64)))
|
||||
};
|
||||
Self { title: "Editing:", time, name, color }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,18 +42,18 @@ audio!(|self:TransportTui,client,scope|ClockAudio(self).process(client, scope));
|
|||
render!(|self: TransportTui|TransportView::from((self, None, true)));
|
||||
|
||||
pub struct TransportView {
|
||||
bg: Color,
|
||||
focused: bool,
|
||||
color: ItemPalette,
|
||||
focused: bool,
|
||||
|
||||
sr: String,
|
||||
bpm: String,
|
||||
ppq: String,
|
||||
beat: String,
|
||||
sr: String,
|
||||
bpm: String,
|
||||
ppq: String,
|
||||
beat: String,
|
||||
|
||||
global_sample: String,
|
||||
global_second: String,
|
||||
global_sample: String,
|
||||
global_second: String,
|
||||
|
||||
started: bool,
|
||||
started: bool,
|
||||
|
||||
current_sample: f64,
|
||||
current_second: f64,
|
||||
|
|
@ -66,13 +66,12 @@ impl<T: HasClock> From<(&T, Option<ItemPalette>, bool)> for TransportView {
|
|||
let bpm = format!("{:.3}", clock.timebase.bpm.get());
|
||||
let ppq = format!("{:.0}", clock.timebase.ppq.get());
|
||||
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() {
|
||||
let current_sample = (clock.global.sample.get() - started.sample.get())/1000.;
|
||||
let current_usec = clock.global.usec.get() - started.usec.get();
|
||||
let current_second = current_usec/1000000.;
|
||||
Self {
|
||||
bg, focused, sr, bpm, ppq,
|
||||
color, focused, sr, bpm, ppq,
|
||||
started: true,
|
||||
global_sample: format!("{:.0}k", started.sample.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 {
|
||||
Self {
|
||||
bg, focused, sr, bpm, ppq,
|
||||
color, focused, sr, bpm, ppq,
|
||||
started: false,
|
||||
global_sample: format!("{:.0}k", clock.global.sample.get()/1000.),
|
||||
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|{
|
||||
|
||||
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!([
|
||||
Tui::fg(Color::Rgb(200, 200, 200), self.0),
|
||||
" ",
|
||||
Tui::bold(true, Tui::fg(Color::Rgb(220, 220, 220), self.1)),
|
||||
Tui::bg(Color::Reset, Tui::bold(true,
|
||||
Tui::fg(self.2.lighter.rgb, self.0))),
|
||||
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), " ",
|
||||
col!([
|
||||
Field(" Beat", self.beat.as_str()),
|
||||
Field(" BPM ", self.bpm.as_str()),
|
||||
Field(" Beat|", self.beat.as_str(), &color),
|
||||
Field(" BPM|", self.bpm.as_str(), &color),
|
||||
]),
|
||||
" ",
|
||||
col!([
|
||||
Field("Time ", format!("{:.1}s", self.current_second).as_str()),
|
||||
Field("Sample", format!("{:.0}k", self.current_sample).as_str()),
|
||||
Field(" Time|", format!("{:.1}s", self.current_second).as_str(), &color),
|
||||
Field(" Smpl|", format!("{:.1}k", self.current_sample).as_str(), &color),
|
||||
]),
|
||||
])))
|
||||
|
||||
|
|
@ -124,11 +125,17 @@ render!(|self: TransportView|{
|
|||
pub struct PlayPause(pub bool);
|
||||
render!(|self: PlayPause|Tui::bg(
|
||||
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 {
|
||||
add(&Tui::fg(Color::Rgb(0, 255, 0), col!(["▶ PLAYING", "▒ ▒ ▒ ▒ ▒"])))
|
||||
Tui::fixed_x(5, col!(|add|if self.0 {
|
||||
add(&Tui::fg(Color::Rgb(0, 255, 0), col!([
|
||||
" 🭍🭑🬽 ",
|
||||
" 🭞🭜🭘 "
|
||||
])))
|
||||
} else {
|
||||
add(&Tui::fg(Color::Rgb(255, 128, 0), col!(["▒ ▒ ▒ ▒ ▒", "⏹ STOPPED"])))
|
||||
})))
|
||||
add(&Tui::fg(Color::Rgb(255, 128, 0), col!([
|
||||
" ▗▄▖ ",
|
||||
" ▝▀▘ "
|
||||
])))
|
||||
}))
|
||||
));
|
||||
|
||||
impl HasFocus for TransportTui {
|
||||
|
|
|
|||
|
|
@ -107,15 +107,19 @@ impl Command<PhraseEditorModel> for PhraseCommand {
|
|||
pub struct PhraseEditorModel {
|
||||
/// Renders the phrase
|
||||
pub mode: Box<dyn PhraseViewMode>,
|
||||
|
||||
pub size: Measure<Tui>
|
||||
}
|
||||
|
||||
impl Default for PhraseEditorModel {
|
||||
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 {
|
||||
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 {}
|
||||
|
||||
has_size!(<Tui>|self:PhraseEditorModel|self.mode.size());
|
||||
|
||||
impl MidiRange for PhraseEditorModel {
|
||||
fn time_zoom (&self) -> usize { self.mode.time_zoom() }
|
||||
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, 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>
|
||||
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> {
|
||||
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> {
|
||||
Bold(on, w)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue