mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
clone EdnItem
This commit is contained in:
parent
400fd9b6e9
commit
3dd8a7bc0d
8 changed files with 190 additions and 169 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
73
examples/edn99.edn
Normal 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(())
|
||||
//})))))
|
||||
//}))
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
44
src/groovebox/groovebox_edn.rs
Normal file
44
src/groovebox/groovebox_edn.rs
Normal 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:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue