mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
fix keymap macros. rendering issue
This commit is contained in:
parent
6f51872856
commit
f3fd88a199
15 changed files with 303 additions and 180 deletions
|
|
@ -15,6 +15,7 @@ const fn digit (c: char) -> usize {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ParseError {
|
pub enum ParseError {
|
||||||
|
Unknown(u8),
|
||||||
Empty,
|
Empty,
|
||||||
Incomplete,
|
Incomplete,
|
||||||
Unexpected(char),
|
Unexpected(char),
|
||||||
|
|
@ -25,27 +26,90 @@ impl std::fmt::Display for ParseError {
|
||||||
Self::Empty => write!(f, "empty"),
|
Self::Empty => write!(f, "empty"),
|
||||||
Self::Incomplete => write!(f, "incomplete"),
|
Self::Incomplete => write!(f, "incomplete"),
|
||||||
Self::Unexpected(c) => write!(f, "unexpected '{c}'"),
|
Self::Unexpected(c) => write!(f, "unexpected '{c}'"),
|
||||||
|
Self::Unknown(i) => write!(f, "unknown #{i}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl std::error::Error for ParseError {}
|
impl std::error::Error for ParseError {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq)]
|
pub enum EdnItem<T> {
|
||||||
pub enum Item<T: std::fmt::Debug + Clone + Default + PartialEq> {
|
Nil,
|
||||||
#[default] Nil,
|
|
||||||
Num(usize),
|
Num(usize),
|
||||||
Sym(T),
|
Sym(T),
|
||||||
Key(T),
|
Key(T),
|
||||||
Exp(Vec<Item<T>>),
|
Exp(Vec<EdnItem<T>>),
|
||||||
|
}
|
||||||
|
impl<T> Default for EdnItem<T> {
|
||||||
|
fn default () -> Self {
|
||||||
|
Self::Nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Item<String> {
|
impl<T: AsRef<str> + PartialEq + Default + Clone + std::fmt::Debug> EdnItem<T> {
|
||||||
|
pub fn to_ref (&self) -> EdnItem<&str> {
|
||||||
|
match self {
|
||||||
|
Self::Key(x) => EdnItem::Key(x.as_ref()),
|
||||||
|
_ => todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn to_str (&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::Key(x) => x.as_ref(),
|
||||||
|
_ => todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Item<&'a str> {
|
impl EdnItem<String> {
|
||||||
|
fn from (other: EdnItem<impl AsRef<str>>) -> EdnItem<String> {
|
||||||
|
use EdnItem::*;
|
||||||
|
match other {
|
||||||
|
Nil => Nil,
|
||||||
|
Key(t) => Key(t.as_ref().to_string()),
|
||||||
|
_ => todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: std::fmt::Debug + Clone + Default + PartialEq + From<&'a str>> Item<T> {
|
impl<'a> TryFrom<Token<'a>> for EdnItem<String> {
|
||||||
|
type Error = ParseError;
|
||||||
|
fn try_from (token: Token<'a>) -> Result<Self, Self::Error> {
|
||||||
|
use Token::*;
|
||||||
|
Ok(match token {
|
||||||
|
Nil => Self::Nil,
|
||||||
|
Num(chars, index, length) =>
|
||||||
|
Self::Num(number(&chars[index..index+length])),
|
||||||
|
Sym(chars, index, length) =>
|
||||||
|
Self::Sym(String::from(&chars[index..index+length])),
|
||||||
|
Key(chars, index, length) =>
|
||||||
|
Self::Key(String::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")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TryFrom<Token<'a>> for EdnItem<&'a str> {
|
||||||
|
type Error = ParseError;
|
||||||
|
fn try_from (token: Token<'a>) -> Result<Self, Self::Error> {
|
||||||
|
use Token::*;
|
||||||
|
Ok(match token {
|
||||||
|
Nil => EdnItem::Nil,
|
||||||
|
Num(chars, index, length) =>
|
||||||
|
Self::Num(number(&chars[index..index+length])),
|
||||||
|
Sym(chars, index, length) =>
|
||||||
|
Self::Sym(&chars[index..index+length]),
|
||||||
|
Key(chars, index, length) =>
|
||||||
|
Self::Key(&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")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: std::fmt::Debug + Clone + Default + PartialEq + From<&'a str>> EdnItem<T> {
|
||||||
pub fn read_all (mut source: &'a str) -> Result<Vec<Self>, ParseError> {
|
pub fn read_all (mut source: &'a str) -> Result<Vec<Self>, ParseError> {
|
||||||
let mut items = vec![];
|
let mut items = vec![];
|
||||||
loop {
|
loop {
|
||||||
|
|
@ -53,15 +117,24 @@ impl<'a, T: std::fmt::Debug + Clone + Default + PartialEq + From<&'a str>> Item<
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
let (remaining, token) = Token::chomp(source)?;
|
let (remaining, token) = Token::chomp(source)?;
|
||||||
match Item::read(token)? { Item::Nil => {}, item => items.push(item) };
|
match Self::read(token)? { Self::Nil => {}, item => items.push(item) };
|
||||||
source = remaining
|
source = remaining
|
||||||
}
|
}
|
||||||
Ok(items)
|
Ok(items)
|
||||||
}
|
}
|
||||||
|
pub fn read_one (source: &'a str) -> Result<(Self, &'a str), ParseError> {
|
||||||
|
Ok({
|
||||||
|
if source.len() == 0 {
|
||||||
|
return Err(ParseError::Unknown(5))
|
||||||
|
}
|
||||||
|
let (remaining, token) = Token::chomp(source)?;
|
||||||
|
(Self::read(token)?, remaining)
|
||||||
|
})
|
||||||
|
}
|
||||||
pub fn read (token: Token<'a>) -> Result<Self, ParseError> {
|
pub fn read (token: Token<'a>) -> Result<Self, ParseError> {
|
||||||
use Token::*;
|
use Token::*;
|
||||||
Ok(match token {
|
Ok(match token {
|
||||||
Nil => Item::Nil,
|
Nil => EdnItem::Nil,
|
||||||
Num(chars, index, length) =>
|
Num(chars, index, length) =>
|
||||||
Self::Num(number(&chars[index..index+length])),
|
Self::Num(number(&chars[index..index+length])),
|
||||||
Sym(chars, index, length) =>
|
Sym(chars, index, length) =>
|
||||||
|
|
@ -73,8 +146,8 @@ impl<'a, T: std::fmt::Debug + Clone + Default + PartialEq + From<&'a str>> Item<
|
||||||
_ => panic!("unclosed delimiter")
|
_ => panic!("unclosed delimiter")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
//pub fn to_str <'a> (&'a self) -> Item<&'a str> {
|
//pub fn to_str <'a> (&'a self) -> EdnItem<&'a str> {
|
||||||
//use Item::*;
|
//use EdnItem::*;
|
||||||
//match self {
|
//match self {
|
||||||
//Nil => Nil,
|
//Nil => Nil,
|
||||||
//Num(n) => Num(*n),
|
//Num(n) => Num(*n),
|
||||||
|
|
|
||||||
|
|
@ -1,68 +1,69 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use ::tek_layout::{*, tek_engine::{Usually, Content, Render, Engine, Thunk}};
|
use ::tek_layout::{*, tek_engine::{Usually, Content, Render, Engine, Thunk}};
|
||||||
use Item::*;
|
use EdnItem::*;
|
||||||
|
|
||||||
//pub struct EdnContent<'a, E, T: AsRef<str>>(PhantomData<E>, &'a [Item<T>]);
|
|
||||||
|
|
||||||
//impl<'a, E, T: AsRef<str>> From<&'a [Item<T>]> for EdnContent<'a, E, T> {
|
|
||||||
//fn from (items: &'a [Item<T>]) -> Self {
|
|
||||||
//Self(Default::default(), items)
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
/// Compiles view from EDN form to Thunk
|
||||||
pub struct EdnView<'a, E: Engine + 'a, T: EdnLayout<'a, E> + 'a>(
|
pub struct EdnView<'a, E: Engine + 'a, T: EdnLayout<'a, E> + 'a>(
|
||||||
PhantomData<&'a ()>,
|
PhantomData<&'a (E, T)>,
|
||||||
Box<dyn Fn(T)->Box<dyn Render<E> + Send + Sync> + Send + Sync>
|
EdnItem<String>,
|
||||||
|
//Box<dyn Fn(&'a T)->Box<dyn Render<E> + Send + Sync + 'a> + Send + Sync + 'a>
|
||||||
);
|
);
|
||||||
|
|
||||||
impl<'a, E: Engine + 'a, T: EdnLayout<'a, E> + 'a> EdnView<'a, E, T> {
|
impl<'a, E: Engine + 'a, T: EdnLayout<'a, E> + 'a> EdnView<'a, E, T> {
|
||||||
fn from (source: &'a str) -> Usually<Self> {
|
pub fn new (source: &'a str) -> Usually<Self> {
|
||||||
let items: Vec<Item<&'a str>> = Item::read_all(source)?;
|
let (item, _): (EdnItem<String>, _) = EdnItem::read_one(&source)?;
|
||||||
let layout = T::parse(items.iter().map(|x|x.to_str()).collect::<Vec<_>>().as_slice());
|
Ok(Self(Default::default(), item))
|
||||||
Ok(Self(Default::default(), Box::new(move|_|{
|
|
||||||
let _ = layout;
|
|
||||||
Box::new(())
|
|
||||||
})))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! edn_ns {
|
pub type EdnRender<'a, Engine> =
|
||||||
(|$state:ident, $items:ident| { $($match:pat => $handle:expr),* $(,)? }) => {
|
dyn Render<Engine> + Send + Sync + 'a;
|
||||||
match $items {
|
|
||||||
$($match => |$state|Box::new($handle),)*
|
|
||||||
_ => |$state|Box::new(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait EdnLayout<'a, E: Engine> where Self: 'a {
|
pub type EdnCallback<'a, Engine, State> =
|
||||||
fn get_bool (&self, _: &Item<&str>) -> bool { false }
|
dyn Fn(&'a State)->Box<EdnRender<'a, Engine>> + Send + Sync + 'a;
|
||||||
fn get_unit (&self, _: &Item<&str>) -> E::Unit { 0.into() }
|
|
||||||
fn get_usize (&self, _: &Item<&str>) -> usize { 0 }
|
pub type EdnRenderCallback<'a, Engine, State> =
|
||||||
fn get_content (&self, _: &Item<&str>) -> Box<dyn Render<E>> { Box::new(()) }
|
Box<EdnCallback<'a, Engine, State>>;
|
||||||
fn parse (items: &'a [Item<&'a str>]) -> impl Fn(&'a Self)->Box<dyn Render<E> + 'a> + 'a {
|
|
||||||
edn_ns!(|state, items|{
|
pub trait EdnLayout<'a, E: Engine + 'a> where Self: 'a {
|
||||||
[Key("when"), c, a, ..] => When(state.get_bool(c), state.get_content(a)),
|
fn get_bool (&self, _: &EdnItem<&str>) -> bool { false }
|
||||||
[Key("either"), c, a, b, ..] => Either(state.get_bool(c), state.get_content(a), state.get_content(b)),
|
fn get_unit (&self, _: &EdnItem<&str>) -> E::Unit { 0.into() }
|
||||||
[Key("fill"), a, ..] => Fill::xy(state.get_content(a)),
|
fn get_usize (&self, _: &EdnItem<&str>) -> usize { 0 }
|
||||||
[Key("fill/x"), a, ..] => Fill::x(state.get_content(a)),
|
fn get_content (&'a self, _: &EdnItem<&str>) -> Box<EdnRender<'a, E>> { Box::new(()) }
|
||||||
[Key("fill/y"), a, ..] => Fill::y(state.get_content(a)),
|
fn parse (items: &'a [EdnItem<String>]) -> EdnRenderCallback<'a, E, Self> {
|
||||||
[Key("fixed"), x, y, a, ..] => Fixed::xy(state.get_unit(x), state.get_unit(y), state.get_content(a)),
|
if let (Some(first), rest) = (items.get(0).map(EdnItem::to_str), &items[1..]) {
|
||||||
[Key("fixed/x"), x, a, ..] => Fixed::x(state.get_unit(x), state.get_content(a)),
|
match (first, rest) {
|
||||||
[Key("fixed/y"), y, a, ..] => Fixed::y(state.get_unit(y), state.get_content(a)),
|
("when", [c, a, ..]) => Box::new(move|state|Box::new(
|
||||||
[Key("shrink"), x, y, a, ..] => Shrink::xy(state.get_unit(x), state.get_unit(y), state.get_content(a)),
|
When(state.get_bool(&c.to_ref()), state.get_content(&a.to_ref())))),
|
||||||
[Key("shrink/x"), x, a, ..] => Shrink::x(state.get_unit(x), state.get_content(a)),
|
_ => Box::new(|_|Box::new(()))
|
||||||
[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)),
|
} else {
|
||||||
[Key("expand/x"), x, a, ..] => Expand::x(state.get_unit(x), state.get_content(a)),
|
Box::new(|_|Box::new(()))
|
||||||
[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)),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
//Box::new(match [items.get(0).map(|x|x.to_str()), items[1..]] {
|
||||||
|
//["when", [c, a, ..]] => When(state.get_bool(c), state.get_content(a)),
|
||||||
|
//["either", [c, a, b, ..]] => Either(state.get_bool(c), state.get_content(a), state.get_content(b)),
|
||||||
|
//["fill", [a, ..]] => Fill::xy(state.get_content(a)),
|
||||||
|
//["fill/x", [a, ..]] => Fill::x(state.get_content(a)),
|
||||||
|
//["fill/y", [a, ..]] => Fill::y(state.get_content(a)),
|
||||||
|
//["fixed", [x, y, a, ..]] => Fixed::xy(state.get_unit(x), state.get_unit(y), state.get_content(a)),
|
||||||
|
//["fixed/x", [x, a, ..]] => Fixed::x(state.get_unit(x), state.get_content(a)),
|
||||||
|
//["fixed/y", [y, a, ..]] => Fixed::y(state.get_unit(y), state.get_content(a)),
|
||||||
|
//["shrink", [x, y, a, ..]] => Shrink::xy(state.get_unit(x), state.get_unit(y), state.get_content(a)),
|
||||||
|
//["shrink/x", [x, a, ..]] => Shrink::x(state.get_unit(x), state.get_content(a)),
|
||||||
|
//["shrink/y", [y, a, ..]] => Shrink::y(state.get_unit(y), state.get_content(a)),
|
||||||
|
//["expand", [x, y, a, ..]] => Expand::xy(state.get_unit(x), state.get_unit(y), state.get_content(a)),
|
||||||
|
//["expand/x", [x, a, ..]] => Expand::x(state.get_unit(x), state.get_content(a)),
|
||||||
|
//["expand/y", [y, a, ..]] => Expand::y(state.get_unit(y), state.get_content(a)),
|
||||||
|
//["push", [x, y, a, ..]] => Push::xy(state.get_unit(x), state.get_unit(y), state.get_content(a)),
|
||||||
|
//["push/x", [x, a, ..]] => Push::x(state.get_unit(x), state.get_content(a)),
|
||||||
|
//["push/y", [y, a, ..]] => Push::y(state.get_unit(y), state.get_content(a)),
|
||||||
|
//["pull", [x, y, a, ..]] => Pull::xy(state.get_unit(x), state.get_unit(y), state.get_content(a)),
|
||||||
|
//["pull/x", [x, a, ..]] => Pull::x(state.get_unit(x), state.get_content(a)),
|
||||||
|
//["pull/y", [y, a, ..]] => Pull::y(state.get_unit(y), state.get_content(a)),
|
||||||
|
//_ => Box::
|
||||||
|
//})
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,29 +7,29 @@ mod edn_layout; pub use self::edn_layout::*;
|
||||||
mod edn_token; pub use self::edn_token::*;
|
mod edn_token; pub use self::edn_token::*;
|
||||||
|
|
||||||
#[cfg(test)] #[test] fn test_edn () -> Result<(), ParseError> {
|
#[cfg(test)] #[test] fn test_edn () -> Result<(), ParseError> {
|
||||||
use Item::*;
|
use EdnItem::*;
|
||||||
assert_eq!(Item::read_all("")?,
|
assert_eq!(EdnItem::read_all("")?,
|
||||||
vec![]);
|
vec![]);
|
||||||
assert_eq!(Item::read_all(" ")?,
|
assert_eq!(EdnItem::read_all(" ")?,
|
||||||
vec![]);
|
vec![]);
|
||||||
assert_eq!(Item::read_all("1234")?,
|
assert_eq!(EdnItem::read_all("1234")?,
|
||||||
vec![Num(1234)]);
|
vec![Num(1234)]);
|
||||||
assert_eq!(Item::read_all("1234 5 67")?,
|
assert_eq!(EdnItem::read_all("1234 5 67")?,
|
||||||
vec![Num(1234), Num(5), Num(67)]);
|
vec![Num(1234), Num(5), Num(67)]);
|
||||||
assert_eq!(Item::read_all("foo/bar")?,
|
assert_eq!(EdnItem::read_all("foo/bar")?,
|
||||||
vec![Key("foo/bar".into())]);
|
vec![Key("foo/bar".into())]);
|
||||||
assert_eq!(Item::read_all(":symbol")?,
|
assert_eq!(EdnItem::read_all(":symbol")?,
|
||||||
vec![Sym(":symbol".into())]);
|
vec![Sym(":symbol".into())]);
|
||||||
assert_eq!(Item::read_all(" foo/bar :baz 456")?,
|
assert_eq!(EdnItem::read_all(" foo/bar :baz 456")?,
|
||||||
vec![Key("foo/bar".into()), Sym(":baz".into()), Num(456)]);
|
vec![Key("foo/bar".into()), Sym(":baz".into()), Num(456)]);
|
||||||
assert_eq!(Item::read_all(" (foo/bar :baz 456) ")?,
|
assert_eq!(EdnItem::read_all(" (foo/bar :baz 456) ")?,
|
||||||
vec![Exp(vec![Key("foo/bar".into()), Sym(":baz".into()), Num(456)])]);
|
vec![Exp(vec![Key("foo/bar".into()), Sym(":baz".into()), Num(456)])]);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)] #[test] fn test_edn_layout () -> Result<(), ParseError> {
|
#[cfg(test)] #[test] fn test_edn_layout () -> Result<(), ParseError> {
|
||||||
let source = include_str!("example.edn");
|
let source = include_str!("example.edn");
|
||||||
let layout = Item::read_all(source)?;
|
let layout = EdnItem::read_all(source)?;
|
||||||
//panic!("{layout:?}");
|
//panic!("{layout:?}");
|
||||||
//let content = <dyn EdnLayout<::tek_engine::tui::Tui>>::from(&layout);
|
//let content = <dyn EdnLayout<::tek_engine::tui::Tui>>::from(&layout);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,6 @@ pub trait Output<E: Engine> {
|
||||||
#[inline] fn h (&self) -> E::Unit { self.area().h() }
|
#[inline] fn h (&self) -> E::Unit { self.area().h() }
|
||||||
#[inline] fn wh (&self) -> E::Size { self.area().wh().into() }
|
#[inline] fn wh (&self) -> E::Size { self.area().wh().into() }
|
||||||
}
|
}
|
||||||
pub struct Thunk<E: Engine, T: Content<E>, F: Fn()->T + Send + Sync>(F, PhantomData<E>);
|
|
||||||
impl<E: Engine, T: Content<E>, F: Fn()->T + Send + Sync> Thunk<E, T, F> {
|
|
||||||
pub fn new (thunk: F) -> Self { Self(thunk, Default::default()) }
|
|
||||||
}
|
|
||||||
impl<E: Engine, T: Content<E>, F: Fn()->T + Send + Sync> Content<E> for Thunk<E, T, F> {
|
|
||||||
fn content (&self) -> impl Content<E> { (self.0)() }
|
|
||||||
}
|
|
||||||
pub trait Render<E: Engine>: Send + Sync {
|
pub trait Render<E: Engine>: Send + Sync {
|
||||||
fn layout (&self, area: E::Area) -> E::Area;
|
fn layout (&self, area: E::Area) -> E::Area;
|
||||||
fn render (&self, output: &mut E::Output);
|
fn render (&self, output: &mut E::Output);
|
||||||
|
|
@ -34,21 +27,19 @@ impl<E: Engine, C: Content<E>> Render<E> for C {
|
||||||
fn layout (&self, area: E::Area) -> E::Area { Content::layout(self, area) }
|
fn layout (&self, area: E::Area) -> E::Area { Content::layout(self, area) }
|
||||||
fn render (&self, output: &mut E::Output) { Content::render(self, output) }
|
fn render (&self, output: &mut E::Output) { Content::render(self, output) }
|
||||||
}
|
}
|
||||||
impl<E: Engine> Content<E> for Box<dyn Render<E>> {
|
impl<'a, E: Engine> Content<E> for Box<dyn Render<E> + Send + Sync + 'a> {
|
||||||
fn content (&self) -> impl Render<E> { self }
|
fn content (&self) -> impl Render<E> { self }
|
||||||
}
|
}
|
||||||
impl<E: Engine> Content<E> for &dyn Render<E> {
|
impl<E: Engine> Content<E> for &(dyn Render<E> + '_) {
|
||||||
fn content (&self) -> impl Render<E> { self }
|
fn content (&self) -> impl Render<E> { self }
|
||||||
}
|
}
|
||||||
/// The platonic ideal unit of [Content]: total emptiness at dead center.
|
pub struct Thunk<E: Engine, T: Content<E>, F: Fn()->T + Send + Sync>(F, PhantomData<E>);
|
||||||
impl<E: Engine> Content<E> for () {
|
impl<E: Engine, T: Content<E>, F: Fn()->T + Send + Sync> Thunk<E, T, F> {
|
||||||
fn layout (&self, area: E::Area) -> E::Area {
|
pub fn new (thunk: F) -> Self { Self(thunk, Default::default()) }
|
||||||
let [x, y] = area.center();
|
|
||||||
[x, y, 0.into(), 0.into()].into()
|
|
||||||
}
|
}
|
||||||
fn render (&self, _: &mut E::Output) {}
|
impl<E: Engine, T: Content<E>, F: Fn()->T + Send + Sync> Content<E> for Thunk<E, T, F> {
|
||||||
|
fn content (&self) -> impl Render<E> { (self.0)() }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine, T: Content<E>> Content<E> for &T {
|
impl<E: Engine, T: Content<E>> Content<E> for &T {
|
||||||
fn content (&self) -> impl Render<E> {
|
fn content (&self) -> impl Render<E> {
|
||||||
(*self).content()
|
(*self).content()
|
||||||
|
|
@ -60,7 +51,14 @@ impl<E: Engine, T: Content<E>> Content<E> for &T {
|
||||||
(*self).render(output)
|
(*self).render(output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// The platonic ideal unit of [Content]: total emptiness at dead center.
|
||||||
|
impl<E: Engine> Content<E> for () {
|
||||||
|
fn layout (&self, area: E::Area) -> E::Area {
|
||||||
|
let [x, y] = area.center();
|
||||||
|
[x, y, 0.into(), 0.into()].into()
|
||||||
|
}
|
||||||
|
fn render (&self, _: &mut E::Output) {}
|
||||||
|
}
|
||||||
impl<E: Engine, T: Content<E>> Content<E> for Option<T> {
|
impl<E: Engine, T: Content<E>> Content<E> for Option<T> {
|
||||||
fn content (&self) -> impl Render<E> {
|
fn content (&self) -> impl Render<E> {
|
||||||
self.as_ref()
|
self.as_ref()
|
||||||
|
|
@ -76,11 +74,10 @@ impl<E: Engine, T: Content<E>> Content<E> for Option<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[macro_export] macro_rules! render {
|
#[macro_export] macro_rules! render {
|
||||||
(($self:ident:$Struct:ty) => $content:expr) => {
|
(($self:ident:$Struct:ty) => $content:expr) => {
|
||||||
impl <E: Engine> Content<E> for $Struct {
|
impl <E: Engine> Content<E> for $Struct {
|
||||||
fn content (&$self) -> impl Content<E> { Some($content) }
|
fn content (&$self) -> impl Render<E> { Some($content) }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
(|$self:ident:$Struct:ident $(<
|
(|$self:ident:$Struct:ident $(<
|
||||||
|
|
@ -98,7 +95,7 @@ impl<E: Engine, T: Content<E>> Content<E> for Option<T> {
|
||||||
) => {
|
) => {
|
||||||
impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Content<$Engine>
|
impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Content<$Engine>
|
||||||
for $Struct $(<$($($L)? $($T)?),+>)? {
|
for $Struct $(<$($($L)? $($T)?),+>)? {
|
||||||
fn content (&$self) -> impl Content<$Engine> { $content }
|
fn content (&$self) -> impl Render<$Engine> { $content }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use ClockCommand::{Play, Pause};
|
use ClockCommand::{Play, Pause};
|
||||||
|
use ArrangerCommand as Cmd;
|
||||||
|
|
||||||
#[derive(Clone, Debug)] pub enum ArrangerCommand {
|
#[derive(Clone, Debug)] pub enum ArrangerCommand {
|
||||||
History(isize),
|
History(isize),
|
||||||
|
|
@ -15,6 +16,7 @@ use ClockCommand::{Play, Pause};
|
||||||
StopAll,
|
StopAll,
|
||||||
Clear,
|
Clear,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum ArrangerTrackCommand {
|
pub enum ArrangerTrackCommand {
|
||||||
Add,
|
Add,
|
||||||
|
|
@ -25,6 +27,7 @@ pub enum ArrangerTrackCommand {
|
||||||
SetZoom(usize),
|
SetZoom(usize),
|
||||||
SetColor(usize, ItemPalette),
|
SetColor(usize, ItemPalette),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum ArrangerSceneCommand {
|
pub enum ArrangerSceneCommand {
|
||||||
Enqueue(usize),
|
Enqueue(usize),
|
||||||
|
|
@ -35,6 +38,7 @@ pub enum ArrangerSceneCommand {
|
||||||
SetZoom(usize),
|
SetZoom(usize),
|
||||||
SetColor(usize, ItemPalette),
|
SetColor(usize, ItemPalette),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum ArrangerClipCommand {
|
pub enum ArrangerClipCommand {
|
||||||
Get(usize, usize),
|
Get(usize, usize),
|
||||||
|
|
@ -45,7 +49,9 @@ pub enum ArrangerClipCommand {
|
||||||
SetColor(usize, usize, ItemPalette),
|
SetColor(usize, usize, ItemPalette),
|
||||||
}
|
}
|
||||||
|
|
||||||
use ArrangerCommand as Cmd;
|
//handle!(<Tui>|self: ArrangerTui, input|ArrangerCommand::execute_with_state(self, input.event()));
|
||||||
|
//input_to_command!(ArrangerCommand: |state: ArrangerTui, input: Event|{KEYS_ARRANGER.handle(state, input)?});
|
||||||
|
|
||||||
keymap!(KEYS_ARRANGER = |state: ArrangerTui, input: Event| ArrangerCommand {
|
keymap!(KEYS_ARRANGER = |state: ArrangerTui, input: Event| ArrangerCommand {
|
||||||
key(Char('u')) => Cmd::History(-1),
|
key(Char('u')) => Cmd::History(-1),
|
||||||
key(Char('U')) => Cmd::History(1),
|
key(Char('U')) => Cmd::History(1),
|
||||||
|
|
|
||||||
40
src/arranger/arranger_keys.edn
Normal file
40
src/arranger/arranger_keys.edn
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
(def keys
|
||||||
|
(u :undo 1)
|
||||||
|
(shift-u :redo 1)
|
||||||
|
(ctrl-k :todo "keyboard")
|
||||||
|
(space :play-toggle)
|
||||||
|
(shift-space :play-start-toggle)
|
||||||
|
(e :editor-show :pool-phrase)
|
||||||
|
(ctrl-a :scene-add)
|
||||||
|
(ctrl-t :track-add)
|
||||||
|
(tab :pool-toggle))
|
||||||
|
|
||||||
|
(def keys-clip
|
||||||
|
(q :clip-launch)
|
||||||
|
(c :clip-color)
|
||||||
|
(g :clip-get)
|
||||||
|
(p :clip-put)
|
||||||
|
(del :clip-del)
|
||||||
|
(, :clip-prev)
|
||||||
|
(. :clip-next)
|
||||||
|
(< :clip-swap-prev)
|
||||||
|
(> :clip-swap-next)
|
||||||
|
(l :clip-loop-toggle))
|
||||||
|
|
||||||
|
(def keys-scene
|
||||||
|
(q :scene-launch)
|
||||||
|
(c :scene-color)
|
||||||
|
(, :scene-prev)
|
||||||
|
(. :scene-next)
|
||||||
|
(< :scene-swap-prev)
|
||||||
|
(> :scene-swap-next)
|
||||||
|
(del :scene-delete))
|
||||||
|
|
||||||
|
(def keys-track
|
||||||
|
(q :track-launch)
|
||||||
|
(c :track-color)
|
||||||
|
(, :track-prev)
|
||||||
|
(. :track-next)
|
||||||
|
(< :track-swap-prev)
|
||||||
|
(> :track-swap-next)
|
||||||
|
(del :track-delete))
|
||||||
|
|
@ -18,7 +18,7 @@ impl<'a> ArrangerVClips<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a> Content<Tui> for ArrangerVClips<'a> {
|
impl<'a> Content<Tui> for ArrangerVClips<'a> {
|
||||||
fn content (&self) -> impl Content<Tui> {
|
fn content (&self) -> impl Render<Tui> {
|
||||||
let iter = ||self.scenes.iter().zip(self.rows.iter().map(|row|row.0));
|
let iter = ||self.scenes.iter().zip(self.rows.iter().map(|row|row.0));
|
||||||
let col = Map(iter, |(scene, pulses), i|Self::format_scene(self.tracks, scene, pulses));
|
let col = Map(iter, |(scene, pulses), i|Self::format_scene(self.tracks, scene, pulses));
|
||||||
Fill::xy(col)
|
Fill::xy(col)
|
||||||
|
|
|
||||||
|
|
@ -2,25 +2,24 @@ use crate::*;
|
||||||
|
|
||||||
#[macro_export] macro_rules! keymap {
|
#[macro_export] macro_rules! keymap {
|
||||||
(
|
(
|
||||||
$KEYS:ident = |$state:ident: $State:ty, $input:ident: $Input:ty| $Command:ty
|
$(<$lt:lifetime>)? $KEYS:ident = |$state:ident: $State:ty, $input:ident: $Input:ty| $Command:ty
|
||||||
{ $($key:expr => $handler:expr),* $(,)? } $(,)?
|
{ $($key:expr => $handler:expr),* $(,)? } $(,)?
|
||||||
) => {
|
) => {
|
||||||
pub const $KEYS: EventMap<'static, $State, $Input, $Command> = EventMap {
|
pub const $KEYS: EventMap<'static, $State, $Input, $Command> = EventMap {
|
||||||
fallback: None,
|
fallback: None,
|
||||||
bindings: &[ $(($key, &|$state|Some($handler)),)* ]
|
bindings: &[ $(($key, &|$state|Some($handler)),)* ]
|
||||||
};
|
};
|
||||||
input_to_command!($Command: |state: $State, input: $Input|$KEYS.handle(state, input)?);
|
input_to_command!($(<$lt>)? $Command: |$state: $State, input: $Input|$KEYS.handle($state, input)?);
|
||||||
};
|
};
|
||||||
(
|
(
|
||||||
$KEYS:ident = |$state:ident: $State:ty, $input:ident: $Input:ty| $Command:ty
|
$(<$lt:lifetime>)? $KEYS:ident = |$state:ident: $State:ty, $input:ident: $Input:ty| $Command:ty
|
||||||
{ $($key:expr => $handler:expr),* $(,)? },
|
{ $($key:expr => $handler:expr),* $(,)? }, $default:expr
|
||||||
$default:expr
|
|
||||||
) => {
|
) => {
|
||||||
pub const $KEYS: EventMap<'static, $State, $Input, $Command> = EventMap {
|
pub const $KEYS: EventMap<'static, $State, $Input, $Command> = EventMap {
|
||||||
fallback: Some(&|$state, $input|Some($default)),
|
fallback: Some(&|$state, $input|Some($default)),
|
||||||
bindings: &[ $(($key, &|$state|Some($handler)),)* ]
|
bindings: &[ $(($key, &|$state|Some($handler)),)* ]
|
||||||
};
|
};
|
||||||
input_to_command!($Command: |state: $State, input: $Input|$KEYS.handle(state, input)?);
|
input_to_command!($(<$lt>)? $Command: |$state: $State, input: $Input|$KEYS.handle($state, input)?);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -28,7 +27,6 @@ pub struct EventMap<'a, S, I: PartialEq, C> {
|
||||||
pub bindings: &'a [(I, &'a dyn Fn(&S) -> Option<C>)],
|
pub bindings: &'a [(I, &'a dyn Fn(&S) -> Option<C>)],
|
||||||
pub fallback: Option<&'a dyn Fn(&S, &I) -> Option<C>>
|
pub fallback: Option<&'a dyn Fn(&S, &I) -> Option<C>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, S, I: PartialEq, C> EventMap<'a, S, I, C> {
|
impl<'a, S, I: PartialEq, C> EventMap<'a, S, I, C> {
|
||||||
pub fn handle (&self, state: &S, input: &I) -> Option<C> {
|
pub fn handle (&self, state: &S, input: &I) -> Option<C> {
|
||||||
for (binding, handler) in self.bindings.iter() {
|
for (binding, handler) in self.bindings.iter() {
|
||||||
|
|
@ -43,15 +41,20 @@ impl<'a, S, I: PartialEq, C> EventMap<'a, S, I, C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Command<S>: Send + Sync + Sized {
|
pub trait Command<S>: Send + Sync + Sized {
|
||||||
fn execute (self, state: &mut S) -> Perhaps<Self>;
|
fn execute (self, state: &mut S) -> Perhaps<Self>;
|
||||||
fn delegate <T> (self, state: &mut S, wrap: impl Fn(Self)->T) -> Perhaps<T> {
|
fn delegate <T> (self, state: &mut S, wrap: impl Fn(Self)->T) -> Perhaps<T> {
|
||||||
Ok(self.execute(state)?.map(wrap))
|
Ok(self.execute(state)?.map(wrap))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export] macro_rules! input_to_command {
|
#[macro_export] macro_rules! input_to_command {
|
||||||
|
(<$($l:lifetime),+> $Command:ty: |$state:ident:$State:ty, $input:ident:$Input:ty| $handler:expr) => {
|
||||||
|
impl<$($l),+> InputToCommand<$Input, $State> for $Command {
|
||||||
|
fn input_to_command ($state: &$State, $input: &$Input) -> Option<Self> {
|
||||||
|
Some($handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
($Command:ty: |$state:ident:$State:ty, $input:ident:$Input:ty| $handler:expr) => {
|
($Command:ty: |$state:ident:$State:ty, $input:ident:$Input:ty| $handler:expr) => {
|
||||||
impl InputToCommand<$Input, $State> for $Command {
|
impl InputToCommand<$Input, $State> for $Command {
|
||||||
fn input_to_command ($state: &$State, $input: &$Input) -> Option<Self> {
|
fn input_to_command ($state: &$State, $input: &$Input) -> Option<Self> {
|
||||||
|
|
@ -74,11 +77,11 @@ pub trait InputToCommand<I, S>: Command<S> + Sized {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export] macro_rules! command {
|
#[macro_export] macro_rules! command {
|
||||||
(|$self:ident:$Command:ty,$state:ident:$State:ty|$handler:expr) => {
|
($(<$($l:lifetime),+>)?|$self:ident:$Command:ty,$state:ident:$State:ty|$handler:expr) => {
|
||||||
impl Command<$State> for $Command {
|
impl$(<$($l),+>)? Command<$State> for $Command {
|
||||||
fn execute ($self, $state: &mut $State) -> Perhaps<Self> {
|
fn execute ($self, $state: &mut $State) -> Perhaps<Self> {
|
||||||
Ok($handler)
|
Ok($handler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ pub struct Field<T, U>(pub ItemPalette, pub T, pub U)
|
||||||
impl<T, U> Content<Tui> for Field<T, U>
|
impl<T, U> Content<Tui> for Field<T, U>
|
||||||
where T: AsRef<str> + Send + Sync, U: AsRef<str> + Send + Sync
|
where T: AsRef<str> + Send + Sync, U: AsRef<str> + Send + Sync
|
||||||
{
|
{
|
||||||
fn content (&self) -> impl Content<Tui> {
|
fn content (&self) -> impl Render<Tui> {
|
||||||
let ItemPalette { darkest, dark, lighter, lightest, .. } = self.0;
|
let ItemPalette { darkest, dark, lighter, lightest, .. } = self.0;
|
||||||
row!(
|
row!(
|
||||||
Tui::fg_bg(dark.rgb, darkest.rgb, "▐"),
|
Tui::fg_bg(dark.rgb, darkest.rgb, "▐"),
|
||||||
|
|
@ -23,7 +23,7 @@ pub struct FieldV<T, U>(pub ItemPalette, pub T, pub U)
|
||||||
impl<T, U> Content<Tui> for FieldV<T, U>
|
impl<T, U> Content<Tui> for FieldV<T, U>
|
||||||
where T: AsRef<str> + Send + Sync, U: AsRef<str> + Send + Sync
|
where T: AsRef<str> + Send + Sync, U: AsRef<str> + Send + Sync
|
||||||
{
|
{
|
||||||
fn content (&self) -> impl Content<Tui> {
|
fn content (&self) -> impl Render<Tui> {
|
||||||
let ItemPalette { darkest, dark, lighter, lightest, .. } = self.0;
|
let ItemPalette { darkest, dark, lighter, lightest, .. } = self.0;
|
||||||
let sep1 = Tui::bg(darkest.rgb, Tui::fg(dark.rgb, "▐"));
|
let sep1 = Tui::bg(darkest.rgb, Tui::fg(dark.rgb, "▐"));
|
||||||
let sep2 = Tui::bg(darkest.rgb, Tui::fg(dark.rgb, "▌"));
|
let sep2 = Tui::bg(darkest.rgb, Tui::fg(dark.rgb, "▌"));
|
||||||
|
|
|
||||||
|
|
@ -58,16 +58,17 @@ impl<'a> Groovebox<'a> {
|
||||||
note_buf: vec![],
|
note_buf: vec![],
|
||||||
perf: PerfModel::default(),
|
perf: PerfModel::default(),
|
||||||
|
|
||||||
view: EdnView::from(include_str!("groovebox/groovebox.edn")),
|
view: EdnView::new(include_str!("groovebox/groovebox.edn"))?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
has_clock!(|self: Groovebox|self.player.clock());
|
has_clock!(|self: Groovebox<'a>|self.player.clock());
|
||||||
|
|
||||||
impl<'a> EdnLayout<'a, Tui> for Groovebox {
|
impl<'a> EdnLayout<'a, Tui> for Groovebox<'a> {
|
||||||
fn get_bool (&self, item: &Item<&str>) -> bool { todo!() }
|
fn get_bool (&self, item: &EdnItem<&str>) -> bool { todo!() }
|
||||||
fn get_unit (&self, item: &Item<&str>) -> u16 {
|
fn get_unit (&self, item: &EdnItem<&str>) -> u16 {
|
||||||
|
use EdnItem::*;
|
||||||
match item {
|
match item {
|
||||||
Sym(":sample-h") => if self.compact { 0 } else { 5 },
|
Sym(":sample-h") => if self.compact { 0 } else { 5 },
|
||||||
Sym(":samples-w") => if self.compact { 4 } else { 11 },
|
Sym(":samples-w") => if self.compact { 4 } else { 11 },
|
||||||
|
|
@ -79,7 +80,8 @@ impl<'a> EdnLayout<'a, Tui> for Groovebox {
|
||||||
_ => 0
|
_ => 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn get_content (&self, item: &Item<&str>) -> Box<dyn Render<Tui> + '_> {
|
fn get_content (&'a self, item: &EdnItem<&str>) -> Box<EdnRender<'a, Tui>> {
|
||||||
|
use EdnItem::*;
|
||||||
match item {
|
match item {
|
||||||
Sym(":input-meter-l") => Box::new(Meter("L/", self.sampler.input_meter[0])),
|
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(":input-meter-r") => Box::new(Meter("R/", self.sampler.input_meter[1])),
|
||||||
|
|
@ -103,3 +105,52 @@ impl<'a> EdnLayout<'a, Tui> for 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!(Tui: (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<Tui> {
|
||||||
|
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<Tui> + use<'_> {
|
||||||
|
row!(&self.cpu, &self.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
audio!(|self: Groovebox<'a>, client, scope|{
|
||||||
audio!(|self: Groovebox, client, scope|{
|
|
||||||
let t0 = self.perf.get_t0();
|
let t0 = self.perf.get_t0();
|
||||||
if Control::Quit == ClockAudio(&mut self.player).process(client, scope) {
|
if Control::Quit == ClockAudio(&mut self.player).process(client, scope) {
|
||||||
return Control::Quit
|
return Control::Quit
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ pub enum GrooveboxCommand {
|
||||||
Sampler(SamplerCommand),
|
Sampler(SamplerCommand),
|
||||||
}
|
}
|
||||||
|
|
||||||
command!(|self: GrooveboxCommand, state: Groovebox|match self {
|
command!(<'a>|self: GrooveboxCommand, state: Groovebox<'a>|match self {
|
||||||
Self::Enqueue(phrase) => {
|
Self::Enqueue(phrase) => {
|
||||||
state.player.enqueue_next(phrase.as_ref());
|
state.player.enqueue_next(phrase.as_ref());
|
||||||
None
|
None
|
||||||
|
|
@ -46,8 +46,10 @@ command!(|self: GrooveboxCommand, state: Groovebox|match self {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
handle!(<Tui>|self: Groovebox, input|GrooveboxCommand::execute_with_state(self, input.event()));
|
handle!(<Tui>|self: Groovebox<'static>, input|
|
||||||
keymap!(KEYS_GROOVEBOX = |state: Groovebox, input: Event| GrooveboxCommand {
|
GrooveboxCommand::execute_with_state(self, input.event()));
|
||||||
|
|
||||||
|
keymap!(<'a> KEYS_GROOVEBOX = |state: Groovebox<'static>, input: Event| GrooveboxCommand {
|
||||||
// Tab: Toggle compact mode
|
// Tab: Toggle compact mode
|
||||||
key(Tab) => Cmd::Compact(!state.compact),
|
key(Tab) => Cmd::Compact(!state.compact),
|
||||||
// q: Enqueue currently edited phrase
|
// q: Enqueue currently edited phrase
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,16 @@ use crate::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::marker::ConstParamTy;
|
use std::marker::ConstParamTy;
|
||||||
use tek_engine::Render;
|
use tek_engine::Render;
|
||||||
use Item::*;
|
use EdnItem::*;
|
||||||
|
|
||||||
render!(Tui: (self: Groovebox) => self.size.of(
|
render!(Tui: (self: Groovebox<'a>) => self.size.of(
|
||||||
Bsp::s(self.toolbar_view(),
|
Bsp::s(self.toolbar_view(),
|
||||||
Bsp::n(self.selector_view(),
|
Bsp::n(self.selector_view(),
|
||||||
Bsp::n(self.sample_view(),
|
Bsp::n(self.sample_view(),
|
||||||
Bsp::n(self.status_view(),
|
Bsp::n(self.status_view(),
|
||||||
Bsp::w(self.pool_view(), Fill::xy(Bsp::e(self.sampler_view(), &self.editor)))))))));
|
Bsp::w(self.pool_view(), Fill::xy(Bsp::e(self.sampler_view(), &self.editor)))))))));
|
||||||
|
|
||||||
impl Groovebox {
|
impl<'a> Groovebox<'a> {
|
||||||
fn toolbar_view (&self) -> impl Content<Tui> + use<'_> {
|
fn toolbar_view (&self) -> impl Content<Tui> + use<'_> {
|
||||||
Fill::x(Fixed::y(2, lay!(
|
Fill::x(Fixed::y(2, lay!(
|
||||||
Align::w(Meter("L/", self.sampler.input_meter[0])),
|
Align::w(Meter("L/", self.sampler.input_meter[0])),
|
||||||
|
|
|
||||||
|
|
@ -49,55 +49,6 @@ impl SequencerStatus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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!(Tui: (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<Tui> {
|
|
||||||
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<Tui> + use<'_> {
|
|
||||||
row!(&self.cpu, &self.size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Status bar for arranger app
|
/// Status bar for arranger app
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ArrangerStatus {
|
pub struct ArrangerStatus {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue