diff --git a/edn/src/edn_item.rs b/edn/src/edn_item.rs index 6423af47..176b9da5 100644 --- a/edn/src/edn_item.rs +++ b/edn/src/edn_item.rs @@ -16,12 +16,22 @@ const fn digit (c: char) -> usize { #[derive(Debug)] pub enum ParseError { Empty, + Incomplete, Unexpected(char), - Incomplete } +impl std::fmt::Display for ParseError { + fn fmt (&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::Empty => write!(f, "empty"), + Self::Incomplete => write!(f, "incomplete"), + Self::Unexpected(c) => write!(f, "unexpected '{c}'"), + } + } +} +impl std::error::Error for ParseError {} #[derive(Debug, Clone, Default, PartialEq)] -pub enum Item> { +pub enum Item { #[default] Nil, Num(usize), Sym(T), @@ -30,7 +40,13 @@ pub enum Item> { } impl Item { - pub fn read_all <'a> (mut source: &'a str) -> Result, ParseError> { +} + +impl<'a> Item<&'a str> { +} + +impl<'a, T: std::fmt::Debug + Clone + Default + PartialEq + From<&'a str>> Item { + pub fn read_all (mut source: &'a str) -> Result, ParseError> { let mut items = vec![]; loop { if source.len() == 0 { @@ -42,19 +58,29 @@ impl Item { } Ok(items) } - pub fn read <'a> (token: Token<'a>) -> Result { + pub fn read (token: Token<'a>) -> Result { use Token::*; Ok(match token { Nil => Item::Nil, Num(chars, index, length) => Self::Num(number(&chars[index..index+length])), Sym(chars, index, length) => - Self::Sym(chars[index..index+length].to_string()), + Self::Sym(T::from(&chars[index..index+length])), Key(chars, index, length) => - Self::Key(chars[index..index+length].to_string()), + Self::Key(T::from(&chars[index..index+length])), Exp(chars, index, length, 0) => Self::Exp(Self::read_all(&chars[index+1..(index+length).saturating_sub(1)])?), _ => panic!("unclosed delimiter") }) } + //pub fn to_str <'a> (&'a self) -> Item<&'a str> { + //use Item::*; + //match self { + //Nil => Nil, + //Num(n) => Num(*n), + //Sym(t) => Sym(t.as_str()), + //Key(t) => Key(t.as_str()), + //Exp(t) => Exp(t.iter().map(|x|x.to_str()).collect()) + //} + //} } diff --git a/edn/src/edn_layout.rs b/edn/src/edn_layout.rs index 46deea52..fe00b06a 100644 --- a/edn/src/edn_layout.rs +++ b/edn/src/edn_layout.rs @@ -1,150 +1,68 @@ use crate::*; use std::marker::PhantomData; -use ::tek_layout::{*, tek_engine::{Content, Render, Engine, Thunk}}; +use ::tek_layout::{*, tek_engine::{Usually, Content, Render, Engine, Thunk}}; use Item::*; -pub struct EdnContent<'a, E, T: AsRef>(PhantomData, &'a [Item]); +//pub struct EdnContent<'a, E, T: AsRef>(PhantomData, &'a [Item]); -impl<'a, E, T: AsRef> From<&'a [Item]> for EdnContent<'a, E, T> { - fn from (items: &'a [Item]) -> Self { - Self(Default::default(), items) +//impl<'a, E, T: AsRef> From<&'a [Item]> for EdnContent<'a, E, T> { + //fn from (items: &'a [Item]) -> Self { + //Self(Default::default(), items) + //} +//} + +pub struct EdnView<'a, E: Engine + 'a, T: EdnLayout<'a, E> + 'a>( + PhantomData<&'a ()>, + BoxBox + Send + Sync> + Send + Sync> +); + +impl<'a, E: Engine + 'a, T: EdnLayout<'a, E> + 'a> EdnView<'a, E, T> { + fn from (source: &'a str) -> Usually { + let items: Vec> = Item::read_all(source)?; + let layout = T::parse(items.iter().map(|x|x.to_str()).collect::>().as_slice()); + Ok(Self(Default::default(), Box::new(move|_|{ + let _ = layout; + Box::new(()) + }))) } } -//impl<'a, E: Engine> Content for EdnContent<'a, E> { - /*todo*/ -//} - -//pub struct EdnContent<'a, T>(T, &'a [Item]); - -pub trait EdnLayout { - fn get_bool (&self, item: &Item<&str>) -> bool { false } - fn get_unit (&self, item: &Item<&str>) -> E::Unit { 0.into() } - fn get_usize (&self, key: &str) -> usize { 0 } - fn get_content (&self, item: &Item<&str>) -> Box + '_> { Box::new(()) } - fn parse <'a: 'static> (&'a self, items: &[Item<&str>]) -> Box + 'a> { - match items { - [Key("when"), c, a, ..] => - Box::new(When(self.get_bool(c), self.get_content(a))), - [Key("either"), c, a, b, ..] => - Box::new(Either(self.get_bool(c), self.get_content(a), self.get_content(b))), - - [Key("fill"), a, ..] => - Box::new(Fill::xy(self.get_content(a))), - [Key("fill/x"), a, ..] => Box::new(Fill::x(self.get_content(a))), - [Key("fill/y"), a, ..] => Box::new(Fill::y(self.get_content(a))), - - [Key("fixed"), x, y, a, ..] => - Box::new(Fixed::xy(self.get_unit(x), self.get_unit(y), self.get_content(a))), - [Key("fixed/x"), x, a, ..] => Box::new(Fixed::x(self.get_unit(x), self.get_content(a))), - [Key("fixed/y"), y, a, ..] => Box::new(Fixed::y(self.get_unit(y), self.get_content(a))), - - [Key("shrink"), x, y, a, ..] => - Box::new(Shrink::xy(self.get_unit(x), self.get_unit(y), self.get_content(a))), - [Key("shrink/x"), x, a, ..] => Box::new(Shrink::x(self.get_unit(x), self.get_content(a))), - [Key("shrink/y"), y, a, ..] => Box::new(Shrink::y(self.get_unit(y), self.get_content(a))), - - [Key("expand"), x, y, a, ..] => - Box::new(Expand::xy(self.get_unit(x), self.get_unit(y), self.get_content(a))), - [Key("expand/x"), x, a, ..] => Box::new(Expand::x(self.get_unit(x), self.get_content(a))), - [Key("expand/y"), y, a, ..] => Box::new(Expand::y(self.get_unit(y), self.get_content(a))), - - [Key("push"), x, y, a, ..] => - Box::new(Push::xy(self.get_unit(x), self.get_unit(y), self.get_content(a))), - [Key("push/x"), x, a, ..] => Box::new(Push::x(self.get_unit(x), self.get_content(a))), - [Key("push/y"), y, a, ..] => Box::new(Push::y(self.get_unit(y), self.get_content(a))), - - [Key("pull"), x, y, a, ..] => - Box::new(Pull::xy(self.get_unit(x), self.get_unit(y), self.get_content(a))), - [Key("pull/x"), x, a, ..] => Box::new(Pull::x(self.get_unit(x), self.get_content(a))), - [Key("pull/y"), y, a, ..] => Box::new(Pull::y(self.get_unit(y), self.get_content(a))), - - _ => Box::new(()) - //[Key("when"), Sym(condition), Sym(template)] => When( - //self.get_bool(condition), - //self.get_content(template)), - //[Key("when"), Sym(condition), Exp(template)] => When( - //self.get_bool(condition), - //Thunk::new(||EdnLayout::parse(template))), - +macro_rules! edn_ns { + (|$state:ident, $items:ident| { $($match:pat => $handle:expr),* $(,)? }) => { + match $items { + $($match => |$state|Box::new($handle),)* + _ => |$state|Box::new(()) } } } -//edn_ns! { EdnLayout |context, item| { - - //[Key("when"), Sym(condition), Sym(template)] => When( - //context.get_bool(condition), - //context.get_template(template)), - - //[Key("when"), Sym(condition), Exp(template)] => When( - //context.get_bool(condition), - //Thunk::new(||EdnLayout::parse(template))), - - //"when" => When(item.0[1].into(), item.0[2].into()) - - ////"either" = Either(args[0].into(), args[1].into(), args[2].into()), - - ////"map" = Map(args[0].into(), args[1].into()), - - ////"fill" = edn_ns! { - ////"x" = Fixed::X(args[0].into(), args[1].into()), - ////"y" = Fixed::Y(args[0].into(), args[1].into()), - ////"xy" = Fixed::XY(args[0].into(), args[1].into(), args[2].into()), - ////}, - - ////"fixed" = edn_ns! { - ////"x" = Fixed::X(args[0].into(), args[1].into()), - ////"y" = Fixed::Y(args[0].into(), args[1].into()), - ////"xy" = Fixed::XY(args[0].into(), args[1].into(), args[2].into()), - ////}, - - ////"shrink" = edn_ns! { - ////"x" = Shrink::X(args[0].into(), args[1].into()), - ////"y" = Shrink::Y(args[0].into(), args[1].into()), - ////"xy" = Shrink::XY(args[0].into(), args[1].into(), args[2].into()), - ////}, - - ////"expand" = edn_ns! { - ////"x" = Expand::X(args[0].into(), args[1].into()), - ////"y" = Expand::Y(args[0].into(), args[1].into()), - ////"xy" = Expand::XY(args[0].into(), args[1].into(), args[2].into()), - ////}, - - ////"min" = edn_ns! { - ////"x" = Min::X(args[0].into(), args[1].into()), - ////"y" = Min::Y(args[0].into(), args[1].into()), - ////"xy" = Min::XY(args[0].into(), args[1].into(), args[2].into()), - ////}, - - ////"max" = edn_ns! { - ////"x" = Max::X(args[0].into(), args[1].into()), - ////"y" = Max::Y(args[0].into(), args[1].into()), - ////"xy" = Max::XY(args[0].into(), args[1].into(), args[2].into()), - ////}, - - ////"push" = edn_ns! { - ////"x" = Push::X(args[0].into(), args[1].into()), - ////"y" = Push::Y(args[0].into(), args[1].into()), - ////"xy" = Push::XY(args[0].into(), args[1].into(), args[2].into()), - ////}, - - ////"pull" = edn_ns! { - ////"x" = Pull::X(args[0].into(), args[1].into()), - ////"y" = Pull::Y(args[0].into(), args[1].into()), - ////"xy" = Pull::XY(args[0].into(), args[1].into(), args[2].into()), - ////}, - - ////"margin" = edn_ns! { - ////"x" = Margin::X(args[0].into(), args[1].into()), - ////"y" = Margin::Y(args[0].into(), args[1].into()), - ////"xy" = Margin::XY(args[0].into(), args[1].into(), args[2].into()), - ////}, - - ////"padding" = edn_ns! { - ////"x" = Padding::X(args[0].into(), args[1].into()), - ////"y" = Padding::Y(args[0].into(), args[1].into()), - ////"xy" = Padding::XY(args[0].into(), args[1].into(), args[2].into()), - ////}, - -//} } +pub trait EdnLayout<'a, E: Engine> where Self: 'a { + fn get_bool (&self, _: &Item<&str>) -> bool { false } + fn get_unit (&self, _: &Item<&str>) -> E::Unit { 0.into() } + fn get_usize (&self, _: &Item<&str>) -> usize { 0 } + fn get_content (&self, _: &Item<&str>) -> Box> { Box::new(()) } + fn parse (items: &'a [Item<&'a str>]) -> impl Fn(&'a Self)->Box + 'a> + 'a { + edn_ns!(|state, items|{ + [Key("when"), c, a, ..] => When(state.get_bool(c), state.get_content(a)), + [Key("either"), c, a, b, ..] => Either(state.get_bool(c), state.get_content(a), state.get_content(b)), + [Key("fill"), a, ..] => Fill::xy(state.get_content(a)), + [Key("fill/x"), a, ..] => Fill::x(state.get_content(a)), + [Key("fill/y"), a, ..] => Fill::y(state.get_content(a)), + [Key("fixed"), x, y, a, ..] => Fixed::xy(state.get_unit(x), state.get_unit(y), state.get_content(a)), + [Key("fixed/x"), x, a, ..] => Fixed::x(state.get_unit(x), state.get_content(a)), + [Key("fixed/y"), y, a, ..] => Fixed::y(state.get_unit(y), state.get_content(a)), + [Key("shrink"), x, y, a, ..] => Shrink::xy(state.get_unit(x), state.get_unit(y), state.get_content(a)), + [Key("shrink/x"), x, a, ..] => Shrink::x(state.get_unit(x), state.get_content(a)), + [Key("shrink/y"), y, a, ..] => Shrink::y(state.get_unit(y), state.get_content(a)), + [Key("expand"), x, y, a, ..] => Expand::xy(state.get_unit(x), state.get_unit(y), state.get_content(a)), + [Key("expand/x"), x, a, ..] => Expand::x(state.get_unit(x), state.get_content(a)), + [Key("expand/y"), y, a, ..] => Expand::y(state.get_unit(y), state.get_content(a)), + [Key("push"), x, y, a, ..] => Push::xy(state.get_unit(x), state.get_unit(y), state.get_content(a)), + [Key("push/x"), x, a, ..] => Push::x(state.get_unit(x), state.get_content(a)), + [Key("push/y"), y, a, ..] => Push::y(state.get_unit(y), state.get_content(a)), + [Key("pull"), x, y, a, ..] => Pull::xy(state.get_unit(x), state.get_unit(y), state.get_content(a)), + [Key("pull/x"), x, a, ..] => Pull::x(state.get_unit(x), state.get_content(a)), + [Key("pull/y"), y, a, ..] => Pull::y(state.get_unit(y), state.get_content(a)), + }) + } +} diff --git a/engine/src/lib.rs b/engine/src/lib.rs index a294d35d..bf8961d2 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -6,7 +6,6 @@ mod input; pub use self::input::*; mod output; pub use self::output::*; pub mod tui; -#[cfg(feature = "edn")] pub mod edn; pub use std::error::Error; diff --git a/engine/src/tui.rs b/engine/src/tui.rs index 7158e9e6..02b5948f 100644 --- a/engine/src/tui.rs +++ b/engine/src/tui.rs @@ -125,7 +125,7 @@ impl + Handle + Sized + 'static> TuiRun for Arc { let exited = exited.clone(); - if let Err(e) = state.write().unwrap().handle(&TuiIn { event, exited }) { + if let Err(e) = state.write().unwrap().handle(&TuiIn(exited, event)) { panic!("{e}") } } diff --git a/engine/src/tui/tui_input.rs b/engine/src/tui/tui_input.rs index a15feeb3..9957fac1 100644 --- a/engine/src/tui/tui_input.rs +++ b/engine/src/tui/tui_input.rs @@ -1,23 +1,15 @@ use crate::{*, tui::*}; pub use crossterm::event::Event; +use Event as CrosstermEvent; #[derive(Debug, Clone)] -pub struct TuiIn { - pub(crate) exited: Arc, - pub(crate) event: Event, -} +pub struct TuiIn(pub Arc, pub CrosstermEvent); impl Input for TuiIn { type Event = Event; - fn event (&self) -> &Event { - &self.event - } - fn is_done (&self) -> bool { - self.exited.fetch_and(true, Relaxed) - } - fn done (&self) { - self.exited.store(true, Relaxed); - } + fn event (&self) -> &CrosstermEvent { &self.1 } + fn is_done (&self) -> bool { self.0.fetch_and(true, Relaxed) } + fn done (&self) { self.0.store(true, Relaxed); } } /// Define a key diff --git a/src/groovebox.rs b/src/groovebox.rs index d556ff9b..741adee4 100644 --- a/src/groovebox.rs +++ b/src/groovebox.rs @@ -10,22 +10,24 @@ mod groovebox_audio; pub use self::groovebox_audio::*; mod groovebox_command; pub use self::groovebox_command::*; mod groovebox_tui; pub use self::groovebox_tui::*; -pub struct Groovebox { +pub struct Groovebox<'a> { _jack: Arc>, + pub view: EdnView<'a, Tui, Self>, - pub player: MidiPlayer, - pub pool: PoolModel, - pub editor: MidiEditor, - pub sampler: Sampler, + pub player: MidiPlayer, + pub pool: PoolModel, + pub editor: MidiEditor, + pub sampler: Sampler, - pub compact: bool, - pub size: Measure, - pub status: bool, - pub note_buf: Vec, - pub midi_buf: Vec>>, - pub perf: PerfModel, + pub compact: bool, + pub size: Measure, + pub status: bool, + pub note_buf: Vec, + pub midi_buf: Vec>>, + pub perf: PerfModel, } -impl Groovebox { + +impl<'a> Groovebox<'a> { pub fn new ( jack: &Arc>, midi_from: &[impl AsRef], @@ -55,7 +57,49 @@ impl Groovebox { midi_buf: vec![vec![];65536], note_buf: vec![], perf: PerfModel::default(), + + view: EdnView::from(include_str!("groovebox/groovebox.edn")), }) } } + has_clock!(|self: Groovebox|self.player.clock()); + +impl<'a> EdnLayout<'a, Tui> for Groovebox { + fn get_bool (&self, item: &Item<&str>) -> bool { todo!() } + fn get_unit (&self, item: &Item<&str>) -> u16 { + match item { + Sym(":sample-h") => if self.compact { 0 } else { 5 }, + Sym(":samples-w") => if self.compact { 4 } else { 11 }, + Sym(":samples-y") => if self.compact { 1 } else { 0 }, + Sym(":pool-w") => if self.compact { 5 } else { + let w = self.size.w(); + if w > 60 { 20 } else if w > 40 { 15 } else { 10 } + }, + _ => 0 + } + } + fn get_content (&self, item: &Item<&str>) -> Box + '_> { + match item { + Sym(":input-meter-l") => Box::new(Meter("L/", self.sampler.input_meter[0])), + Sym(":input-meter-r") => Box::new(Meter("R/", self.sampler.input_meter[1])), + + Sym(":transport") => Box::new(TransportView::new(true, &self.player.clock)), + Sym(":clip-play") => Box::new(ClipSelected::play_phrase(&self.player)), + Sym(":clip-next") => Box::new(ClipSelected::next_phrase(&self.player)), + Sym(":clip-edit") => Box::new(MidiEditClip(&self.editor)), + Sym(":edit-stat") => Box::new(MidiEditStatus(&self.editor)), + Sym(":pool-view") => Box::new(PoolView(self.compact, &self.pool)), + Sym(":midi-view") => Box::new(&self.editor), + + Sym(":sample-view") => Box::new(SampleViewer::from_sampler( + &self.sampler, self.editor.note_point())), + Sym(":sample-stat") => Box::new(SamplerStatus( + &self.sampler, self.editor.note_point())), + Sym(":samples-view") => Box::new(SampleList::new( + self.compact, &self.sampler, &self.editor)), + + _ => Box::new(()) + } + } +} diff --git a/src/groovebox/groovebox_tui.rs b/src/groovebox/groovebox_tui.rs index 3aa1ac33..0c51f2be 100644 --- a/src/groovebox/groovebox_tui.rs +++ b/src/groovebox/groovebox_tui.rs @@ -4,45 +4,6 @@ use std::marker::ConstParamTy; use tek_engine::Render; use Item::*; -impl EdnLayout for Groovebox { - fn get_bool (&self, item: &Item<&str>) -> bool { todo!() } - fn get_unit (&self, item: &Item<&str>) -> u16 { - match item { - Sym(":sample-h") => if self.compact { 0 } else { 5 }, - Sym(":samples-w") => if self.compact { 4 } else { 11 }, - Sym(":samples-y") => if self.compact { 1 } else { 0 }, - Sym(":pool-w") => if self.compact { 5 } else { - let w = self.size.w(); - if w > 60 { 20 } else if w > 40 { 15 } else { 10 } - }, - _ => 0 - } - } - fn get_content (&self, item: &Item<&str>) -> Box + '_> { - match item { - Sym(":input-meter-l") => Box::new(Meter("L/", self.sampler.input_meter[0])), - Sym(":input-meter-r") => Box::new(Meter("R/", self.sampler.input_meter[1])), - - Sym(":transport") => Box::new(TransportView::new(true, &self.player.clock)), - Sym(":clip-play") => Box::new(ClipSelected::play_phrase(&self.player)), - Sym(":clip-next") => Box::new(ClipSelected::next_phrase(&self.player)), - Sym(":clip-edit") => Box::new(MidiEditClip(&self.editor)), - Sym(":edit-stat") => Box::new(MidiEditStatus(&self.editor)), - Sym(":pool-view") => Box::new(PoolView(self.compact, &self.pool)), - Sym(":midi-view") => Box::new(&self.editor), - - Sym(":sample-view") => Box::new(SampleViewer::from_sampler( - &self.sampler, self.editor.note_point())), - Sym(":sample-stat") => Box::new(SamplerStatus( - &self.sampler, self.editor.note_point())), - Sym(":samples-view") => Box::new(SampleList::new( - self.compact, &self.sampler, &self.editor)), - - _ => Box::new(()) - } - } -} - render!(Tui: (self: Groovebox) => self.size.of( Bsp::s(self.toolbar_view(), Bsp::n(self.selector_view(),