clone EdnItem

This commit is contained in:
🪞👃🪞 2025-01-05 22:52:35 +01:00
parent 400fd9b6e9
commit 3dd8a7bc0d
8 changed files with 190 additions and 169 deletions

View file

@ -38,6 +38,18 @@ impl<T: PartialEq> PartialEq for EdnItem<T> {
}
}
}
impl EdnItem<&str> {
pub fn clone (&self) -> EdnItem<String> {
use EdnItem::*;
match self {
Nil => Nil,
Num(u) => Num(*u),
Sym(u) => Sym(u.to_string()),
Key(u) => Key(u.to_string()),
Exp(e) => Exp(e.iter().map(|i|i.clone()).collect()),
}
}
}
impl<T: AsRef<str> + PartialEq + Default + Clone + std::fmt::Debug> EdnItem<T> {
pub fn to_ref (&self) -> EdnItem<&str> {
match self {

View file

@ -26,12 +26,15 @@ pub enum EdnView<E: Output, T: EdnViewData<E>> {
}
impl<E: Output, T: EdnViewData<E>> EdnView<E, T> {
pub fn new (state: T, source: &str) -> Self {
pub fn from_source (state: T, source: &str) -> Self {
match EdnItem::read_one(&source) {
Ok((layout, _)) => Self::Ok(state, layout),
Err(error) => Self::Err(format!("{error}"))
}
}
pub fn from_items (state: T, items: &[EdnItem<&str>]) -> Self {
Self::Ok(state, EdnItem::Exp(items.iter().map(|i|(*i).clone()).collect()))
}
}
impl<E: Output, T: EdnViewData<E> + Send + Sync> Content<E> for EdnView<E, T> {

View file

@ -31,7 +31,7 @@ impl Content<TuiOut> for Example {
fn content (&self) -> impl Render<TuiOut> {
Bsp::a(
Push::xy(1, 1, Align::n(format!("Example {}/{}:", self.0 + 1, EDN.len()))),
EdnView::new(self, EDN[self.0])
EdnView::from_source(self, EDN[self.0])
)
}
}

View file

@ -1,73 +0,0 @@
(align/c (bg/behind :bg0 (margin/xy 1 1 (col
(bg/behind :bg1 (border/around :border1 (margin/xy 2 1 :label1)))
(bg/behind :bg2 (border/around :border2 (margin/xy 4 2 :label2)))
(bg/behind :bg3 (border/around :border3 (margin/xy 6 3 :label3)))))))
fn content (&self) -> dyn Render<Engine = Tui> {
let border_style = Style::default().fg(Color::Rgb(0,0,0));
Align::Center(Layers::new(move|add|{
add(&Background(Color::Rgb(0,128,128)))?;
add(&Margin::XY(1, 1, Stack::down(|add|{
add(&Layers::new(|add|{
add(&Background(Color::Rgb(128,96,0)))?;
add(&Border(Square(border_style)))?;
add(&Margin::XY(2, 1, "..."))?;
Ok(())
}).debug())?;
add(&Layers::new(|add|{
add(&Background(Color::Rgb(128,64,0)))?;
add(&Border(Lozenge(border_style)))?;
add(&Margin::XY(4, 2, "---"))?;
Ok(())
}).debug())?;
add(&Layers::new(|add|{
add(&Background(Color::Rgb(96,64,0)))?;
add(&Border(SquareBold(border_style)))?;
add(&Margin::XY(6, 3, "~~~"))?;
Ok(())
}).debug())?;
Ok(())
})).debug())?;
Ok(())
}))
//Align::Center(Margin::X(1, Layers::new(|add|{
//add(&Background(Color::Rgb(128,0,0)))?;
//add(&Stack::down(|add|{
//add(&Margin::Y(1, Layers::new(|add|{
//add(&Background(Color::Rgb(0,128,0)))?;
//add(&Align::Center("12345"))?;
//add(&Align::Center("FOO"))
//})))?;
//add(&Margin::XY(1, 1, Layers::new(|add|{
//add(&Align::Center("1234567"))?;
//add(&Align::Center("BAR"))?;
//add(&Background(Color::Rgb(0,0,128)))
//})))
//}))
//})))
//Align::Y(Layers::new(|add|{
//add(&Background(Color::Rgb(128,0,0)))?;
//add(&Margin::X(1, Align::Center(Stack::down(|add|{
//add(&Align::X(Margin::Y(1, Layers::new(|add|{
//add(&Background(Color::Rgb(0,128,0)))?;
//add(&Align::Center("12345"))?;
//add(&Align::Center("FOO"))
//})))?;
//add(&Margin::XY(1, 1, Layers::new(|add|{
//add(&Align::Center("1234567"))?;
//add(&Align::Center("BAR"))?;
//add(&Background(Color::Rgb(0,0,128)))
//})))?;
//Ok(())
//})))))
//}))
}

73
examples/edn99.edn Normal file
View file

@ -0,0 +1,73 @@
(align/c (bg/behind :bg0 (margin/xy 1 1 (col
(bg/behind :bg1 (border/around :border1 (margin/xy 2 1 :label1)))
(bg/behind :bg2 (border/around :border2 (margin/xy 4 2 :label2)))
(bg/behind :bg3 (border/around :border3 (margin/xy 6 3 :label3)))))))
fn content (&self) -> dyn Render<Engine = Tui> {
let border_style = Style::default().fg(Color::Rgb(0,0,0));
Align::Center(Layers::new(move|add|{
add(&Background(Color::Rgb(0,128,128)))?;
add(&Margin::XY(1, 1, Stack::down(|add|{
add(&Layers::new(|add|{
add(&Background(Color::Rgb(128,96,0)))?;
add(&Border(Square(border_style)))?;
add(&Margin::XY(2, 1, "..."))?;
Ok(())
}).debug())?;
add(&Layers::new(|add|{
add(&Background(Color::Rgb(128,64,0)))?;
add(&Border(Lozenge(border_style)))?;
add(&Margin::XY(4, 2, "---"))?;
Ok(())
}).debug())?;
add(&Layers::new(|add|{
add(&Background(Color::Rgb(96,64,0)))?;
add(&Border(SquareBold(border_style)))?;
add(&Margin::XY(6, 3, "~~~"))?;
Ok(())
}).debug())?;
Ok(())
})).debug())?;
Ok(())
}))
//Align::Center(Margin::X(1, Layers::new(|add|{
//add(&Background(Color::Rgb(128,0,0)))?;
//add(&Stack::down(|add|{
//add(&Margin::Y(1, Layers::new(|add|{
//add(&Background(Color::Rgb(0,128,0)))?;
//add(&Align::Center("12345"))?;
//add(&Align::Center("FOO"))
//})))?;
//add(&Margin::XY(1, 1, Layers::new(|add|{
//add(&Align::Center("1234567"))?;
//add(&Align::Center("BAR"))?;
//add(&Background(Color::Rgb(0,0,128)))
//})))
//}))
//})))
//Align::Y(Layers::new(|add|{
//add(&Background(Color::Rgb(128,0,0)))?;
//add(&Margin::X(1, Align::Center(Stack::down(|add|{
//add(&Align::X(Margin::Y(1, Layers::new(|add|{
//add(&Background(Color::Rgb(0,128,0)))?;
//add(&Align::Center("12345"))?;
//add(&Align::Center("FOO"))
//})))?;
//add(&Margin::XY(1, 1, Layers::new(|add|{
//add(&Align::Center("1234567"))?;
//add(&Align::Center("BAR"))?;
//add(&Background(Color::Rgb(0,0,128)))
//})))?;
//Ok(())
//})))))
//}))
}

View file

@ -1,3 +1,8 @@
mod groovebox_audio; pub use self::groovebox_audio::*;
mod groovebox_command; pub use self::groovebox_command::*;
mod groovebox_tui; pub use self::groovebox_tui::*;
mod groovebox_edn; pub use self::groovebox_edn::*;
use crate::*;
use super::*;
use KeyCode::{Char, Delete, Tab, Up, Down, Left, Right};
@ -6,10 +11,6 @@ use GrooveboxCommand as Cmd;
use MidiEditCommand::*;
use PhrasePoolCommand::*;
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 {
_jack: Arc<RwLock<JackConnection>>,
pub player: MidiPlayer,
@ -24,7 +25,7 @@ pub struct Groovebox {
pub midi_buf: Vec<Vec<Vec<u8>>>,
pub perf: PerfModel,
}
has_clock!(|self: Groovebox|self.player.clock());
impl Groovebox {
pub fn new (
jack: &Arc<RwLock<JackConnection>>,
@ -59,91 +60,3 @@ impl Groovebox {
}
}
has_clock!(|self: Groovebox|self.player.clock());
impl EdnViewData<TuiOut> for &Groovebox {
fn get_bool (&self, item: EdnItem<&str>) -> bool { todo!() }
fn get_unit (&self, item: EdnItem<&str>) -> u16 {
use EdnItem::*;
match item.to_str() {
":sample-h" => if self.compact { 0 } else { 5 },
":samples-w" => if self.compact { 4 } else { 11 },
":samples-y" => if self.compact { 1 } else { 0 },
":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 <'a> (&'a self, item: EdnItem<&str>) -> RenderBox<'a, TuiOut> {
use EdnItem::*;
match item.to_str() {
":input-meter-l" => Box::new(Meter("L/", self.sampler.input_meter[0])),
":input-meter-r" => Box::new(Meter("R/", self.sampler.input_meter[1])),
":transport" => Box::new(TransportView::new(true, &self.player.clock)),
":clip-play" => Box::new(ClipSelected::play_phrase(&self.player)),
":clip-next" => Box::new(ClipSelected::next_phrase(&self.player)),
":clip-edit" => Box::new(MidiEditClip(&self.editor)),
":edit-stat" => Box::new(MidiEditStatus(&self.editor)),
":pool-view" => Box::new(PoolView(self.compact, &self.pool)),
":midi-view" => Box::new(&self.editor),
":sample-view" => Box::new(SampleViewer::from_sampler(&self.sampler, self.editor.note_point())),
":sample-stat" => Box::new(SamplerStatus(&self.sampler, self.editor.note_point())),
":samples-view" => Box::new(SampleList::new(self.compact, &self.sampler, &self.editor)),
_ => panic!("{item:?}")
}
}
}
/// Status bar for sequencer app
#[derive(Clone)]
pub struct GrooveboxStatus {
pub(crate) width: usize,
pub(crate) cpu: Option<String>,
pub(crate) size: String,
pub(crate) playing: bool,
}
from!(|state: &Groovebox|GrooveboxStatus = {
let samples = state.clock().chunk.load(Relaxed);
let rate = state.clock().timebase.sr.get();
let buffer = samples as f64 / rate;
let width = state.size.w();
Self {
width,
playing: state.clock().is_rolling(),
cpu: state.perf.percentage().map(|cpu|format!("{cpu:.01}%")),
size: format!("{}x{}│", width, state.size.h()),
}
});
render!(TuiOut: (self: GrooveboxStatus) => Fixed::y(2, lay!(
Self::help(),
Fill::xy(Align::se(Tui::fg_bg(TuiTheme::orange(), TuiTheme::g(25), self.stats()))),
)));
impl GrooveboxStatus {
fn help () -> impl Content<TuiOut> {
let single = |binding, command|row!(" ", col!(
Tui::fg(TuiTheme::yellow(), binding),
command
));
let double = |(b1, c1), (b2, c2)|col!(
row!(" ", Tui::fg(TuiTheme::yellow(), b1), " ", c1,),
row!(" ", Tui::fg(TuiTheme::yellow(), b2), " ", c2,),
);
Tui::fg_bg(TuiTheme::g(255), TuiTheme::g(50), row!(
single("SPACE", "play/pause"),
double(("▲▼▶◀", "cursor"), ("Ctrl", "scroll"), ),
double(("a", "append"), ("s", "set note"),),
double((",.", "length"), ("<>", "triplet"), ),
double(("[]", "phrase"), ("{}", "order"), ),
double(("q", "enqueue"), ("e", "edit"), ),
double(("c", "color"), ("", ""),),
))
}
fn stats (&self) -> impl Content<TuiOut> + use<'_> {
row!(&self.cpu, &self.size)
}
}

View file

@ -0,0 +1,44 @@
use crate::*;
impl EdnViewData<TuiOut> for &Groovebox {
fn get_unit (&self, item: EdnItem<&str>) -> u16 {
use EdnItem::*;
match item.to_str() {
":sample-h" => if self.compact { 0 } else { 5 },
":samples-w" => if self.compact { 4 } else { 11 },
":samples-y" => if self.compact { 1 } else { 0 },
":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 <'a> (&'a self, item: EdnItem<&str>) -> RenderBox<'a, TuiOut> {
use EdnItem::*;
match item {
Nil => Box::new(()),
Sym(bol) => match bol {
":input-meter-l" => Meter("L/", self.sampler.input_meter[0]).boxed(),
":input-meter-r" => Box::new(Meter("R/", self.sampler.input_meter[1])),
":transport" => Box::new(TransportView::new(true, &self.player.clock)),
":clip-play" => Box::new(ClipSelected::play_phrase(&self.player)),
":clip-next" => Box::new(ClipSelected::next_phrase(&self.player)),
":clip-edit" => Box::new(MidiEditClip(&self.editor)),
":edit-stat" => Box::new(MidiEditStatus(&self.editor)),
":pool-view" => Box::new(PoolView(self.compact, &self.pool)),
":midi-view" => Box::new(&self.editor),
":sample-view" => Box::new(SampleViewer::from_sampler(&self.sampler, self.editor.note_point())),
":sample-stat" => Box::new(SamplerStatus(&self.sampler, self.editor.note_point())),
":samples-view" => Box::new(SampleList::new(self.compact, &self.sampler, &self.editor)),
_ => panic!("unknown sym {bol:?}")
},
Exp(items) => Box::new(EdnView::from_items(*self, items.as_slice())),
_ => panic!("no content for {item:?}")
}
}
}

View file

@ -8,10 +8,11 @@ const EDN: &'static str = include_str!("groovebox.edn");
impl Content<TuiOut> for Groovebox {
fn content (&self) -> impl Render<TuiOut> {
self.size.of(EdnView::new(self, EDN))
self.size.of(EdnView::from_source(self, EDN))
}
}
// this works:
//render!(TuiOut: (self: Groovebox) => self.size.of(
//Bsp::s(self.toolbar_view(),
//Bsp::n(self.selector_view(),
@ -59,6 +60,54 @@ impl Groovebox {
}
}
///// Status bar for sequencer app
//#[derive(Clone)]
//pub struct GrooveboxStatus {
//pub(crate) width: usize,
//pub(crate) cpu: Option<String>,
//pub(crate) size: String,
//pub(crate) playing: bool,
//}
//from!(|state: &Groovebox|GrooveboxStatus = {
//let samples = state.clock().chunk.load(Relaxed);
//let rate = state.clock().timebase.sr.get();
//let buffer = samples as f64 / rate;
//let width = state.size.w();
//Self {
//width,
//playing: state.clock().is_rolling(),
//cpu: state.perf.percentage().map(|cpu|format!("│{cpu:.01}%")),
//size: format!("{}x{}│", width, state.size.h()),
//}
//});
//render!(TuiOut: (self: GrooveboxStatus) => Fixed::y(2, lay!(
//Self::help(),
//Fill::xy(Align::se(Tui::fg_bg(TuiTheme::orange(), TuiTheme::g(25), self.stats()))),
//)));
//impl GrooveboxStatus {
//fn help () -> impl Content<TuiOut> {
//let single = |binding, command|row!(" ", col!(
//Tui::fg(TuiTheme::yellow(), binding),
//command
//));
//let double = |(b1, c1), (b2, c2)|col!(
//row!(" ", Tui::fg(TuiTheme::yellow(), b1), " ", c1,),
//row!(" ", Tui::fg(TuiTheme::yellow(), b2), " ", c2,),
//);
//Tui::fg_bg(TuiTheme::g(255), TuiTheme::g(50), row!(
//single("SPACE", "play/pause"),
//double(("▲▼▶◀", "cursor"), ("Ctrl", "scroll"), ),
//double(("a", "append"), ("s", "set note"),),
//double((",.", "length"), ("<>", "triplet"), ),
//double(("[]", "phrase"), ("{}", "order"), ),
//double(("q", "enqueue"), ("e", "edit"), ),
//double(("c", "color"), ("", ""),),
//))
//}
//fn stats (&self) -> impl Content<TuiOut> + use<'_> {
//row!(&self.cpu, &self.size)
//}
//}
//render!(TuiOut: (self: Groovebox) => self.size.of(
//Bsp::s(self.toolbar_view(),
//Bsp::n(self.selector_view(),