mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-01-31 08:36:40 +01:00
separate Input and Output impls
This commit is contained in:
parent
a6efde40f8
commit
0e821e098f
77 changed files with 465 additions and 454 deletions
|
|
@ -17,23 +17,27 @@ fn main () -> Usually<()> {
|
||||||
pub struct Example(usize);
|
pub struct Example(usize);
|
||||||
|
|
||||||
impl EdnViewData<Tui> for &Example {
|
impl EdnViewData<Tui> for &Example {
|
||||||
fn get_content <'a> (&'a self, sym: &'a str) -> RenderBox<'a, Tui> {
|
fn get_content <'a> (&'a self, sym: EdnItem<&'a str>) -> RenderBox<'a, Tui> {
|
||||||
Box::new(Thunk::new(move||match sym {
|
Box::new(Thunk::new(move||match sym {
|
||||||
":hello-world" => "Hello world!",
|
EdnItem::Sym(":hello-world") => "Hello world!",
|
||||||
":hello" => "Hello",
|
EdnItem::Sym(":hello") => "Hello",
|
||||||
":world" => "world",
|
EdnItem::Sym(":world") => "world",
|
||||||
_ => ""
|
_ => ""
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Content<Tui> for Example {
|
impl Content<TuiOut> for Example {
|
||||||
fn content (&self) -> impl Render<Tui> {
|
fn content (&self) -> impl Render<TuiOut> {
|
||||||
EdnView::new(self, EDN[self.0]).unwrap()
|
Bsp::a(
|
||||||
|
&format!("{}", self.0),
|
||||||
|
EdnView::new(self, EDN[self.0])
|
||||||
|
.unwrap_or_else(|e|format!("Failed to render {}: {e}", self.0))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handle<Tui> for Example {
|
impl Handle<TuiIn> for Example {
|
||||||
fn handle (&mut self, input: &TuiIn) -> Perhaps<bool> {
|
fn handle (&mut self, input: &TuiIn) -> Perhaps<bool> {
|
||||||
match input.event() {
|
match input.event() {
|
||||||
kpat!(Right) => self.0 = (self.0 + 1) % EDN.len(),
|
kpat!(Right) => self.0 = (self.0 + 1) % EDN.len(),
|
||||||
|
|
@ -22,10 +22,10 @@ impl EdnViewData<Tui> for &Example {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Content<Tui> for Example {
|
impl Content<TuiOut> for Example {
|
||||||
fn content (&self) -> impl Render<Tui> {
|
fn content (&self) -> impl Render<TuiOut> {
|
||||||
EdnView::new(self, EDN).unwrap()
|
EdnView::new(self, EDN).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handle<Tui> for Example {}
|
impl Handle<TuiIn> for Example {}
|
||||||
|
|
|
||||||
|
|
@ -1,56 +1,62 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use ::tek_layout::{*, tek_engine::{Usually, Content, Render, RenderBox, Engine, Thunk}};
|
use ::tek_layout::{*, tek_engine::{Usually, Content, Render, RenderBox, Output, Thunk}};
|
||||||
use EdnItem::*;
|
use EdnItem::*;
|
||||||
|
|
||||||
pub type EdnCallback<'a, Engine, State> =
|
pub type EdnCallback<'a, Output, State> =
|
||||||
dyn Fn(&'a State)-> RenderBox<'a, Engine> + Send + Sync + 'a;
|
dyn Fn(&'a State)-> RenderBox<'a, Output> + Send + Sync + 'a;
|
||||||
|
|
||||||
pub type EdnRenderCallback<'a, Engine, State> =
|
pub type EdnRenderCallback<'a, Output, State> =
|
||||||
Box<EdnCallback<'a, Engine, State>>;
|
Box<EdnCallback<'a, Output, State>>;
|
||||||
|
|
||||||
pub trait EdnViewData<E: Engine> {
|
pub trait EdnViewData<E: Output> {
|
||||||
fn get_bool (&self, _sym: EdnItem<&str>) -> bool { false }
|
fn get_bool (&self, _sym: EdnItem<&str>) -> bool { false }
|
||||||
fn get_unit (&self, _sym: EdnItem<&str>) -> E::Unit { 0.into() }
|
fn get_unit (&self, _sym: EdnItem<&str>) -> E::Unit { 0.into() }
|
||||||
fn get_usize (&self, _sym: EdnItem<&str>) -> usize { 0 }
|
fn get_usize (&self, _sym: EdnItem<&str>) -> usize { 0 }
|
||||||
fn get_content <'a> (&'a self, _sym: EdnItem<&str>) -> RenderBox<'a, E> { Box::new(()) }
|
fn get_content <'a> (&'a self, _sym: EdnItem<&'a str>) -> RenderBox<'a, E> { Box::new(()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders from EDN source and context.
|
/// Renders from EDN source and context.
|
||||||
pub struct EdnView<E: Engine, T: EdnViewData<E>> {
|
pub enum EdnView<E: Output, T: EdnViewData<E>> {
|
||||||
_engine: PhantomData<E>,
|
_Unused(PhantomData<E>),
|
||||||
context: T,
|
Ok(T, EdnItem<String>),
|
||||||
layout: EdnItem<String>
|
|
||||||
//render: Box<dyn Fn(&'a T)->Box<dyn Render<E> + Send + Sync + 'a> + Send + Sync + 'a>
|
//render: Box<dyn Fn(&'a T)->Box<dyn Render<E> + Send + Sync + 'a> + Send + Sync + 'a>
|
||||||
|
Err(String)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine, T: EdnViewData<E>> EdnView<E, T> {
|
impl<E: Output, T: EdnViewData<E>> EdnView<E, T> {
|
||||||
pub fn new (context: T, source: &str) -> Usually<Self> {
|
pub fn new (context: T, source: &str) -> Usually<Self> {
|
||||||
let layout = EdnItem::read_one(&source)?.0;
|
let layout = EdnItem::read_one(&source)?.0;
|
||||||
Ok(Self { _engine: Default::default(), context, layout, })
|
Ok(Self::Ok(context, layout))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine, T: EdnViewData<E> + Send + Sync> Content<E> for EdnView<E, T> {
|
impl<E: Output, T: EdnViewData<E> + Send + Sync> Content<E> for EdnView<E, T> {
|
||||||
fn content (&self) -> impl Render<E> {
|
fn content (&self) -> impl Render<E> {
|
||||||
use EdnItem::*;
|
use EdnItem::*;
|
||||||
match &self.layout {
|
match self {
|
||||||
Nil => ().boxed(),
|
Self::Ok(state, layout) => match layout {
|
||||||
Sym(t) => self.context.get_content(Sym(t.as_str())).boxed(),
|
Nil => ().boxed(),
|
||||||
Key(t) => panic!("todo: add error handling to content() chain. unexpected key {t}"),
|
Sym(t) => state.get_content(Sym(t.as_str())).boxed(),
|
||||||
Num(n) => panic!("todo: add error handling to content() chain. unexpected num {n}"),
|
Key(t) => panic!("todo: add error handling to content() chain. unexpected key {t}"),
|
||||||
Exp(e) => if let [head, tail @ ..] = e.as_slice() {
|
Num(n) => panic!("todo: add error handling to content() chain. unexpected num {n}"),
|
||||||
let head = &head.to_ref();
|
Exp(e) => if let [head, tail @ ..] = e.as_slice() {
|
||||||
let state = &self.context;
|
let head = &head.to_ref();
|
||||||
match (head, tail) {
|
match (head, tail) {
|
||||||
(Key("when"), [c, a]) => When(state.get_bool(c.to_ref()), state.get_content(a.to_ref())).boxed(),
|
(Key("when"), [c, a]) => When(state.get_bool(c.to_ref()), state.get_content(a.to_ref())).boxed(),
|
||||||
(Key("bsp/s"), [a, b]) => Bsp::s(state.get_content(a.to_ref()), state.get_content(b.to_ref()),).boxed(),
|
(Key("bsp/s"), [a, b]) => Bsp::s(state.get_content(a.to_ref()), state.get_content(b.to_ref()),).boxed(),
|
||||||
_ => todo!("{:?} {:?}", &head, &tail)
|
_ => todo!("{:?} {:?}", &head, &tail)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
panic!("todo: add error handling to content() chain. invalid expression {e:?}")
|
panic!("todo: add error handling to content() chain. invalid expression {e:?}")
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
Self::Err(error) => {
|
||||||
|
Box::new(())//&format!("EdnView error: {error:?}"))
|
||||||
|
},
|
||||||
|
_ => todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
//let items = &self.layout;
|
//let items = &self.layout;
|
||||||
//if let (Some(first), rest) = (items.get(0).map(EdnItem::to_str), &items[1..]) {
|
//if let (Some(first), rest) = (items.get(0).map(EdnItem::to_str), &items[1..]) {
|
||||||
//match (first, rest) {
|
//match (first, rest) {
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,22 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
/// Build a [Render]able out of other [Render]ables,
|
/// Build a [Render]able out of other [Render]ables,
|
||||||
/// then apply optional custom render/layout on top.
|
/// then apply optional custom render/layout on top.
|
||||||
pub trait Content<E: Engine>: Send + Sync + Sized {
|
pub trait Content<E: Output>: Send + Sync + Sized {
|
||||||
fn content (&self) -> impl Render<E> { () }
|
fn content (&self) -> impl Render<E> { () }
|
||||||
fn layout (&self, area: E::Area) -> E::Area { self.content().layout(area) }
|
fn layout (&self, area: E::Area) -> E::Area { self.content().layout(area) }
|
||||||
fn render (&self, output: &mut E::Output) { self.content().render(output) }
|
fn render (&self, output: &mut E) { self.content().render(output) }
|
||||||
}
|
}
|
||||||
impl<E: Engine, C: Content<E>> Content<E> for &C {
|
impl<E: Output, C: Content<E>> Content<E> for &C {
|
||||||
fn content (&self) -> impl Render<E> { (*self).content() }
|
fn content (&self) -> impl Render<E> { (*self).content() }
|
||||||
fn layout (&self, area: E::Area) -> E::Area { (*self).layout(area) }
|
fn layout (&self, area: E::Area) -> E::Area { (*self).layout(area) }
|
||||||
fn render (&self, output: &mut E::Output) { (*self).render(output) }
|
fn render (&self, output: &mut E) { (*self).render(output) }
|
||||||
}
|
}
|
||||||
/// The platonic ideal unit of [Content]: total emptiness at dead center.
|
/// The platonic ideal unit of [Content]: total emptiness at dead center.
|
||||||
impl<E: Engine> Content<E> for () {
|
impl<E: Output> Content<E> for () {
|
||||||
fn layout (&self, area: E::Area) -> E::Area { area.center().to_area_pos().into() }
|
fn layout (&self, area: E::Area) -> E::Area { area.center().to_area_pos().into() }
|
||||||
fn render (&self, _: &mut E::Output) {}
|
fn render (&self, _: &mut E) {}
|
||||||
}
|
}
|
||||||
|
impl<E: Output, 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()
|
||||||
}
|
}
|
||||||
|
|
@ -26,8 +25,22 @@ impl<E: Engine, T: Content<E>> Content<E> for Option<T> {
|
||||||
.map(|content|content.layout(area))
|
.map(|content|content.layout(area))
|
||||||
.unwrap_or([0.into(), 0.into(), 0.into(), 0.into(),].into())
|
.unwrap_or([0.into(), 0.into(), 0.into(), 0.into(),].into())
|
||||||
}
|
}
|
||||||
fn render (&self, output: &mut E::Output) {
|
fn render (&self, output: &mut E) {
|
||||||
self.as_ref()
|
self.as_ref()
|
||||||
.map(|content|content.render(output));
|
.map(|content|content.render(output));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//impl<E: Output, T: Content<E>, E: Content<E>> Content<E> for Option<T> {
|
||||||
|
//fn content (&self) -> impl Render<E> {
|
||||||
|
//self.as_ref()
|
||||||
|
//}
|
||||||
|
//fn layout (&self, area: E::Area) -> E::Area {
|
||||||
|
//self.as_ref()
|
||||||
|
//.map(|content|content.layout(area))
|
||||||
|
//.unwrap_or([0.into(), 0.into(), 0.into(), 0.into(),].into())
|
||||||
|
//}
|
||||||
|
//fn render (&self, output: &mut E) {
|
||||||
|
//self.as_ref()
|
||||||
|
//.map(|content|content.render(output));
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
@ -3,17 +3,9 @@ use crate::*;
|
||||||
/// Platform backend.
|
/// Platform backend.
|
||||||
pub trait Engine: Send + Sync + Sized {
|
pub trait Engine: Send + Sync + Sized {
|
||||||
/// Input event type
|
/// Input event type
|
||||||
type Input: Input<Self>;
|
type Input: Input;
|
||||||
/// Result of handling input
|
|
||||||
type Handled;
|
|
||||||
/// Render target
|
/// Render target
|
||||||
type Output: Output<Self>;
|
type Output: Output;
|
||||||
/// Unit of length
|
|
||||||
type Unit: Coordinate;
|
|
||||||
/// Rectangle without offset
|
|
||||||
type Size: Size<Self::Unit>;
|
|
||||||
/// Rectangle with offset
|
|
||||||
type Area: Area<Self::Unit>;
|
|
||||||
/// Prepare before run
|
/// Prepare before run
|
||||||
fn setup (&mut self) -> Usually<()> { Ok(()) }
|
fn setup (&mut self) -> Usually<()> { Ok(()) }
|
||||||
/// True if done
|
/// True if done
|
||||||
|
|
@ -21,3 +13,38 @@ pub trait Engine: Send + Sync + Sized {
|
||||||
/// Clean up after run
|
/// Clean up after run
|
||||||
fn teardown (&mut self) -> Usually<()> { Ok(()) }
|
fn teardown (&mut self) -> Usually<()> { Ok(()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Event source
|
||||||
|
pub trait Input: Send + Sync + Sized {
|
||||||
|
/// Type of input event
|
||||||
|
type Event;
|
||||||
|
/// Result of handling input
|
||||||
|
type Handled;
|
||||||
|
/// Currently handled event
|
||||||
|
fn event (&self) -> &Self::Event;
|
||||||
|
/// Whether component should exit
|
||||||
|
fn is_done (&self) -> bool;
|
||||||
|
/// Mark component as done
|
||||||
|
fn done (&self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render target
|
||||||
|
pub trait Output: Send + Sync + Sized {
|
||||||
|
/// Unit of length
|
||||||
|
type Unit: Coordinate;
|
||||||
|
/// Rectangle without offset
|
||||||
|
type Size: Size<Self::Unit>;
|
||||||
|
/// Rectangle with offset
|
||||||
|
type Area: Area<Self::Unit>;
|
||||||
|
/// Current output area
|
||||||
|
fn area (&self) -> Self::Area;
|
||||||
|
/// Mutable pointer to area
|
||||||
|
fn area_mut (&mut self) -> &mut Self::Area;
|
||||||
|
/// Render widget in area
|
||||||
|
fn place (&mut self, area: Self::Area, content: &impl Render<Self>);
|
||||||
|
#[inline] fn x (&self) -> Self::Unit { self.area().x() }
|
||||||
|
#[inline] fn y (&self) -> Self::Unit { self.area().y() }
|
||||||
|
#[inline] fn w (&self) -> Self::Unit { self.area().w() }
|
||||||
|
#[inline] fn h (&self) -> Self::Unit { self.area().h() }
|
||||||
|
#[inline] fn wh (&self) -> Self::Size { self.area().wh().into() }
|
||||||
|
}
|
||||||
|
|
|
||||||
61
engine/src/handle.rs
Normal file
61
engine/src/handle.rs
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
use crate::*;
|
||||||
|
use std::sync::{Mutex, Arc, RwLock};
|
||||||
|
|
||||||
|
/// Implement the [Handle] trait.
|
||||||
|
#[macro_export] macro_rules! handle {
|
||||||
|
(|$self:ident:$Struct:ty,$input:ident|$handler:expr) => {
|
||||||
|
impl<E: Engine> Handle<E> for $Struct {
|
||||||
|
fn handle (&mut $self, $input: &E) -> Perhaps<E::Handled> {
|
||||||
|
$handler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($E:ty: |$self:ident:$Struct:ty,$input:ident|$handler:expr) => {
|
||||||
|
impl Handle<$E> for $Struct {
|
||||||
|
fn handle (&mut $self, $input: &$E) -> Perhaps<<$E as Input>::Handled> {
|
||||||
|
$handler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle input
|
||||||
|
pub trait Handle<E: Input>: Send + Sync {
|
||||||
|
fn handle (&mut self, _input: &E) -> Perhaps<E::Handled> {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<E: Input, H: Handle<E>> Handle<E> for &mut H {
|
||||||
|
fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
|
||||||
|
(*self).handle(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<E: Input, H: Handle<E>> Handle<E> for Option<H> {
|
||||||
|
fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
|
||||||
|
if let Some(ref mut handle) = self {
|
||||||
|
handle.handle(context)
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<H, E: Input> Handle<E> for Mutex<H> where H: Handle<E> {
|
||||||
|
fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
|
||||||
|
self.get_mut().unwrap().handle(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<H, E: Input> Handle<E> for Arc<Mutex<H>> where H: Handle<E> {
|
||||||
|
fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
|
||||||
|
self.lock().unwrap().handle(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<H, E: Input> Handle<E> for RwLock<H> where H: Handle<E> {
|
||||||
|
fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
|
||||||
|
self.write().unwrap().handle(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<H, E: Input> Handle<E> for Arc<RwLock<H>> where H: Handle<E> {
|
||||||
|
fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
|
||||||
|
self.write().unwrap().handle(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
mod handle; pub use self::handle::*;
|
|
||||||
mod command; pub use self::command::*;
|
|
||||||
mod event_map; pub use self::event_map::*;
|
|
||||||
|
|
||||||
/// Current input state
|
|
||||||
pub trait Input<E: Engine> {
|
|
||||||
/// Type of input event
|
|
||||||
type Event;
|
|
||||||
/// Currently handled event
|
|
||||||
fn event (&self) -> &Self::Event;
|
|
||||||
/// Whether component should exit
|
|
||||||
fn is_done (&self) -> bool;
|
|
||||||
/// Mark component as done
|
|
||||||
fn done (&self);
|
|
||||||
}
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
use std::sync::{Mutex, Arc, RwLock};
|
|
||||||
|
|
||||||
#[macro_export] macro_rules! handle {
|
|
||||||
(<$E:ty>|$self:ident:$Struct:ty,$input:ident|$handler:expr) => {
|
|
||||||
impl Handle<$E> for $Struct {
|
|
||||||
fn handle (&mut $self, $input: &<$E as Engine>::Input) -> Perhaps<<$E as Engine>::Handled> {
|
|
||||||
$handler
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handle input
|
|
||||||
pub trait Handle<E: Engine>: Send + Sync {
|
|
||||||
fn handle (&mut self, _input: &E::Input) -> Perhaps<E::Handled> {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<E: Engine, H: Handle<E>> Handle<E> for &mut H {
|
|
||||||
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled> {
|
|
||||||
(*self).handle(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<E: Engine, H: Handle<E>> Handle<E> for Option<H> {
|
|
||||||
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled> {
|
|
||||||
if let Some(ref mut handle) = self {
|
|
||||||
handle.handle(context)
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<H, E: Engine> Handle<E> for Mutex<H> where H: Handle<E> {
|
|
||||||
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled> {
|
|
||||||
self.get_mut().unwrap().handle(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<H, E: Engine> Handle<E> for Arc<Mutex<H>> where H: Handle<E> {
|
|
||||||
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled> {
|
|
||||||
self.lock().unwrap().handle(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<H, E: Engine> Handle<E> for RwLock<H> where H: Handle<E> {
|
|
||||||
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled> {
|
|
||||||
self.write().unwrap().handle(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<H, E: Engine> Handle<E> for Arc<RwLock<H>> where H: Handle<E> {
|
|
||||||
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled> {
|
|
||||||
self.write().unwrap().handle(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,8 +2,15 @@
|
||||||
|
|
||||||
//mod component; pub use self::component::*;
|
//mod component; pub use self::component::*;
|
||||||
mod engine; pub use self::engine::*;
|
mod engine; pub use self::engine::*;
|
||||||
mod input; pub use self::input::*;
|
mod handle; pub use self::handle::*;
|
||||||
mod output; pub use self::output::*;
|
mod command; pub use self::command::*;
|
||||||
|
mod event_map; pub use self::event_map::*;
|
||||||
|
mod coordinate; pub use self::coordinate::*;
|
||||||
|
mod size; pub use self::size::*;
|
||||||
|
mod area; pub use self::area::*;
|
||||||
|
mod render; pub use self::render::*;
|
||||||
|
mod content; pub use self::content::*;
|
||||||
|
mod thunk; pub use self::thunk::*;
|
||||||
|
|
||||||
pub use std::error::Error;
|
pub use std::error::Error;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
mod coordinate; pub use self::coordinate::*;
|
|
||||||
mod size; pub use self::size::*;
|
|
||||||
mod area; pub use self::area::*;
|
|
||||||
|
|
||||||
mod render; pub use self::render::*;
|
|
||||||
mod content; pub use self::content::*;
|
|
||||||
mod thunk; pub use self::thunk::*;
|
|
||||||
|
|
||||||
/// Rendering target
|
|
||||||
pub trait Output<E: Engine> {
|
|
||||||
/// Current output area
|
|
||||||
fn area (&self) -> E::Area;
|
|
||||||
/// Mutable pointer to area
|
|
||||||
fn area_mut (&mut self) -> &mut E::Area;
|
|
||||||
/// Render widget in area
|
|
||||||
fn place (&mut self, area: E::Area, content: &impl Render<E>);
|
|
||||||
#[inline] fn x (&self) -> E::Unit { self.area().x() }
|
|
||||||
#[inline] fn y (&self) -> E::Unit { self.area().y() }
|
|
||||||
#[inline] fn w (&self) -> E::Unit { self.area().w() }
|
|
||||||
#[inline] fn h (&self) -> E::Unit { self.area().h() }
|
|
||||||
#[inline] fn wh (&self) -> E::Size { self.area().wh().into() }
|
|
||||||
}
|
|
||||||
|
|
@ -2,64 +2,64 @@ use crate::*;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
/// Custom layout and rendering.
|
/// Custom layout and rendering.
|
||||||
pub trait Render<E: Engine>: Send + Sync {
|
pub trait Render<E: Output>: 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);
|
||||||
fn boxed <'a> (self) -> RenderBox<'a, E> where Self: Sized + 'a {
|
fn boxed <'a> (self) -> RenderBox<'a, E> where Self: Sized + 'a {
|
||||||
Box::new(self) as RenderBox<'a, E>
|
Box::new(self) as RenderBox<'a, E>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type RenderDyn<'a, Engine> = dyn Render<Engine> + 'a;
|
pub type RenderDyn<'a, Output> = dyn Render<Output> + 'a;
|
||||||
impl<'a, E: Engine> Content<E> for &RenderDyn<'a, E> where Self: Sized {
|
impl<'a, E: Output> Content<E> for &RenderDyn<'a, E> where Self: Sized {
|
||||||
fn content (&self) -> impl Render<E> { *self }
|
fn content (&self) -> impl Render<E> { *self }
|
||||||
fn layout (&self, area: E::Area) -> E::Area { Render::layout(self.deref(), area) }
|
fn layout (&self, area: E::Area) -> E::Area { Render::layout(self.deref(), area) }
|
||||||
fn render (&self, output: &mut E::Output) { Render::render(self.deref(), output) }
|
fn render (&self, output: &mut E) { Render::render(self.deref(), output) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type RenderBox<'a, E: Engine> = Box<RenderDyn<'a, E>>;
|
pub type RenderBox<'a, E: Output> = Box<RenderDyn<'a, E>>;
|
||||||
impl<'a, E: Engine> Content<E> for RenderBox<'a, E> {
|
impl<'a, E: Output> Content<E> for RenderBox<'a, E> {
|
||||||
fn content (&self) -> impl Render<E> { self.deref() }
|
fn content (&self) -> impl Render<E> { self.deref() }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine, C: Content<E>> Render<E> for C {
|
impl<E: Output, 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) { Content::render(self, output) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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: Output> Content<E> for $Struct {
|
||||||
fn content (&$self) -> impl Render<E> { Some($content) }
|
fn content (&$self) -> impl Render<E> { Some($content) }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
(|$self:ident:$Struct:ident $(<
|
(|$self:ident:$Struct:ident $(<
|
||||||
$($L:lifetime),* $($T:ident $(:$Trait:path)?),*
|
$($L:lifetime),* $($T:ident $(:$Trait:path)?),*
|
||||||
>)?, $to:ident | $render:expr) => {
|
>)?, $to:ident | $render:expr) => {
|
||||||
impl <$($($L),*)? E: Engine, $($T$(:$Trait)?),*> Content<E>
|
impl <$($($L),*)? E: Output, $($T$(:$Trait)?),*> Content<E>
|
||||||
for $Struct $(<$($L),* $($T),*>>)? {
|
for $Struct $(<$($L),* $($T),*>>)? {
|
||||||
fn render (&$self, $to: &mut E::Output) { $render }
|
fn render (&$self, $to: &mut E) { $render }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($Engine:ty:
|
($Output:ty:
|
||||||
($self:ident:$Struct:ident $(<$(
|
($self:ident:$Struct:ident $(<$(
|
||||||
$($L:lifetime)? $($T:ident)? $(:$Trait:path)?
|
$($L:lifetime)? $($T:ident)? $(:$Trait:path)?
|
||||||
),+>)?) => $content:expr
|
),+>)?) => $content:expr
|
||||||
) => {
|
) => {
|
||||||
impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Content<$Engine>
|
impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Content<$Output>
|
||||||
for $Struct $(<$($($L)? $($T)?),+>)? {
|
for $Struct $(<$($($L)? $($T)?),+>)? {
|
||||||
fn content (&$self) -> impl Render<$Engine> { $content }
|
fn content (&$self) -> impl Render<$Output> { $content }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
($Engine:ty:
|
($Output:ty:
|
||||||
|$self:ident : $Struct:ident $(<$(
|
|$self:ident : $Struct:ident $(<$(
|
||||||
$($L:lifetime)? $($T:ident)? $(:$Trait:path)?
|
$($L:lifetime)? $($T:ident)? $(:$Trait:path)?
|
||||||
),+>)?, $to:ident| $render:expr
|
),+>)?, $to:ident| $render:expr
|
||||||
) => {
|
) => {
|
||||||
impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Content<$Engine>
|
impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Content<$Output>
|
||||||
for $Struct $(<$($($L)? $($T)?),+>)? {
|
for $Struct $(<$($($L)? $($T)?),+>)? {
|
||||||
fn render (&$self, $to: &mut <$Engine as Engine>::Output) { $render }
|
fn render (&$self, $to: &mut $Output) { $render }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
/// Lazily-evaluated [Render]able.
|
/// Lazily-evaluated [Render]able.
|
||||||
pub struct Thunk<E: Engine, T: Render<E>, F: Fn()->T + Send + Sync>(F, PhantomData<E>);
|
pub struct Thunk<E: Output, T: Render<E>, F: Fn()->T + Send + Sync>(F, PhantomData<E>);
|
||||||
impl<E: Engine, T: Render<E>, F: Fn()->T + Send + Sync> Thunk<E, T, F> {
|
impl<E: Output, T: Render<E>, F: Fn()->T + Send + Sync> Thunk<E, T, F> {
|
||||||
pub fn new (thunk: F) -> Self { Self(thunk, Default::default()) }
|
pub fn new (thunk: F) -> Self { Self(thunk, Default::default()) }
|
||||||
}
|
}
|
||||||
impl<E: Engine, T: Render<E>, F: Fn()->T + Send + Sync> Content<E> for Thunk<E, T, F> {
|
impl<E: Output, T: Render<E>, F: Fn()->T + Send + Sync> Content<E> for Thunk<E, T, F> {
|
||||||
fn content (&self) -> impl Render<E> { (self.0)() }
|
fn content (&self) -> impl Render<E> { (self.0)() }
|
||||||
}
|
}
|
||||||
|
|
@ -104,7 +104,7 @@ impl Content for Demo<Tui> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handle<Tui> for Demo<Tui> {
|
impl Handle<TuiIn> for Demo<Tui> {
|
||||||
fn handle (&mut self, from: &TuiIn) -> Perhaps<bool> {
|
fn handle (&mut self, from: &TuiIn) -> Perhaps<bool> {
|
||||||
use KeyCode::{PageUp, PageDown};
|
use KeyCode::{PageUp, PageDown};
|
||||||
match from.event() {
|
match from.event() {
|
||||||
|
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
use tek::*;
|
|
||||||
use std::sync::{Arc, RwLock};
|
|
||||||
|
|
||||||
fn main () -> Usually<()> {
|
|
||||||
Tui::run(Arc::new(RwLock::new(BspDemo(Default::default()))))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct BspDemo<E: Engine>(std::marker::PhantomData<E>);
|
|
||||||
|
|
||||||
render!(<Tui>|self:BspDemo<Tui>|Fill::xy(Align::c(
|
|
||||||
Bsp::n(Bsp::s(Bsp::e(Bsp::w("00", "11"), "22"), "33"), "44")
|
|
||||||
)));
|
|
||||||
|
|
||||||
impl Handle<Tui> for BspDemo<Tui> {
|
|
||||||
fn handle (&mut self, from: &TuiIn) -> Perhaps<bool> {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -19,7 +19,7 @@ impl<T> Align<T> {
|
||||||
pub fn se (a: T) -> Self { Self(Alignment::SE, a) }
|
pub fn se (a: T) -> Self { Self(Alignment::SE, a) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine, T: Content<E>> Content<E> for Align<T> {
|
impl<E: Output, T: Content<E>> Content<E> for Align<T> {
|
||||||
fn content (&self) -> impl Render<E> {
|
fn content (&self) -> impl Render<E> {
|
||||||
&self.1
|
&self.1
|
||||||
}
|
}
|
||||||
|
|
@ -46,7 +46,7 @@ impl<E: Engine, T: Content<E>> Content<E> for Align<T> {
|
||||||
};
|
};
|
||||||
[x, y, centered.w(), centered.h()].into()
|
[x, y, centered.w(), centered.h()].into()
|
||||||
}
|
}
|
||||||
fn render (&self, render: &mut E::Output) {
|
fn render (&self, render: &mut E) {
|
||||||
let content = &self.content();
|
let content = &self.content();
|
||||||
let it = Render::layout(content, render.area()).xywh();
|
let it = Render::layout(content, render.area()).xywh();
|
||||||
render.place(it.into(), content)
|
render.place(it.into(), content)
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,12 @@ impl Direction {
|
||||||
|
|
||||||
pub struct Bsp<X, Y>(Direction, X, Y);
|
pub struct Bsp<X, Y>(Direction, X, Y);
|
||||||
|
|
||||||
impl<E: Engine, A: Content<E>, B: Content<E>> Content<E> for Bsp<A, B> {
|
impl<E: Output, A: Content<E>, B: Content<E>> Content<E> for Bsp<A, B> {
|
||||||
fn layout (&self, outer: E::Area) -> E::Area {
|
fn layout (&self, outer: E::Area) -> E::Area {
|
||||||
let [_, _, c] = self.areas(outer);
|
let [_, _, c] = self.areas(outer);
|
||||||
c
|
c
|
||||||
}
|
}
|
||||||
fn render (&self, to: &mut E::Output) {
|
fn render (&self, to: &mut E) {
|
||||||
let [area_a, area_b, _] = self.areas(to.area());
|
let [area_a, area_b, _] = self.areas(to.area());
|
||||||
let (a, b) = self.contents();
|
let (a, b) = self.contents();
|
||||||
match self.0 {
|
match self.0 {
|
||||||
|
|
@ -44,7 +44,7 @@ impl<A, B> Bsp<A, B> {
|
||||||
pub fn b (a: A, b: B) -> Self { Self(Below, a, b) }
|
pub fn b (a: A, b: B) -> Self { Self(Below, a, b) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait BspAreas<E: Engine, A: Content<E>, B: Content<E>> {
|
pub trait BspAreas<E: Output, A: Content<E>, B: Content<E>> {
|
||||||
fn direction (&self) -> Direction;
|
fn direction (&self) -> Direction;
|
||||||
fn contents (&self) -> (&A, &B);
|
fn contents (&self) -> (&A, &B);
|
||||||
fn areas (&self, outer: E::Area) -> [E::Area;3] {
|
fn areas (&self, outer: E::Area) -> [E::Area;3] {
|
||||||
|
|
@ -95,7 +95,7 @@ pub trait BspAreas<E: Engine, A: Content<E>, B: Content<E>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine, A: Content<E>, B: Content<E>> BspAreas<E, A, B> for Bsp<A, B> {
|
impl<E: Output, A: Content<E>, B: Content<E>> BspAreas<E, A, B> for Bsp<A, B> {
|
||||||
fn direction (&self) -> Direction { self. 0 }
|
fn direction (&self) -> Direction { self. 0 }
|
||||||
fn contents (&self) -> (&A, &B) { (&self.1, &self.2) }
|
fn contents (&self) -> (&A, &B) { (&self.1, &self.2) }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,17 +18,17 @@ pub(crate) use std::marker::PhantomData;
|
||||||
|
|
||||||
let unit = ();
|
let unit = ();
|
||||||
|
|
||||||
assert_eq!(Content::<Tui>::layout(&unit, area), [20, 20, 0, 0]);
|
assert_eq!(Content::<TuiOut>::layout(&unit, area), [20, 20, 0, 0]);
|
||||||
|
|
||||||
assert_eq!(Fill::<Tui>::x(unit).layout(area), [10, 20, 20, 0]);
|
assert_eq!(Fill::<TuiOut>::x(unit).layout(area), [10, 20, 20, 0]);
|
||||||
assert_eq!(Fill::<Tui>::y(unit).layout(area), [20, 10, 0, 20]);
|
assert_eq!(Fill::<TuiOut>::y(unit).layout(area), [20, 10, 0, 20]);
|
||||||
assert_eq!(Fill::<Tui>::xy(unit).layout(area), area);
|
assert_eq!(Fill::<TuiOut>::xy(unit).layout(area), area);
|
||||||
|
|
||||||
assert_eq!(Fixed::<Tui, u16>::x(4, unit).layout(area), [18, 20, 4, 0]);
|
assert_eq!(Fixed::<TuiOut, u16>::x(4, unit).layout(area), [18, 20, 4, 0]);
|
||||||
assert_eq!(Fixed::<Tui, u16>::y(4, unit).layout(area), [20, 18, 0, 4]);
|
assert_eq!(Fixed::<TuiOut, u16>::y(4, unit).layout(area), [20, 18, 0, 4]);
|
||||||
assert_eq!(Fixed::<Tui, u16>::xy(4, 4, unit).layout(area), [18, 18, 4, 4]);
|
assert_eq!(Fixed::<TuiOut, u16>::xy(4, 4, unit).layout(area), [18, 18, 4, 4]);
|
||||||
|
|
||||||
let four = ||Fixed::<Tui>::xy(4, 4, unit);
|
let four = ||Fixed::<TuiOut>::xy(4, 4, unit);
|
||||||
|
|
||||||
assert_eq!(Align::nw(four()).layout(area), [10, 10, 4, 4]);
|
assert_eq!(Align::nw(four()).layout(area), [10, 10, 4, 4]);
|
||||||
assert_eq!(Align::n(four()).layout(area), [18, 10, 4, 4]);
|
assert_eq!(Align::n(four()).layout(area), [18, 10, 4, 4]);
|
||||||
|
|
@ -39,7 +39,7 @@ pub(crate) use std::marker::PhantomData;
|
||||||
assert_eq!(Align::sw(four()).layout(area), [10, 26, 4, 4]);
|
assert_eq!(Align::sw(four()).layout(area), [10, 26, 4, 4]);
|
||||||
assert_eq!(Align::w(four()).layout(area), [10, 18, 4, 4]);
|
assert_eq!(Align::w(four()).layout(area), [10, 18, 4, 4]);
|
||||||
|
|
||||||
let two_by_four = ||Fixed::<Tui>::xy(4, 2, unit);
|
let two_by_four = ||Fixed::<TuiOut>::xy(4, 2, unit);
|
||||||
|
|
||||||
assert_eq!(Align::nw(two_by_four()).layout(area), [10, 10, 4, 2]);
|
assert_eq!(Align::nw(two_by_four()).layout(area), [10, 10, 4, 2]);
|
||||||
assert_eq!(Align::n(two_by_four()).layout(area), [18, 10, 4, 2]);
|
assert_eq!(Align::n(two_by_four()).layout(area), [18, 10, 4, 2]);
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use std::sync::{Arc, atomic::{AtomicUsize, Ordering::Relaxed}};
|
||||||
|
|
||||||
// TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small
|
// TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small
|
||||||
|
|
||||||
pub trait HasSize<E: Engine> {
|
pub trait HasSize<E: Output> {
|
||||||
fn size (&self) -> &Measure<E>;
|
fn size (&self) -> &Measure<E>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -18,20 +18,20 @@ pub trait HasSize<E: Engine> {
|
||||||
|
|
||||||
/// A widget that tracks its render width and height
|
/// A widget that tracks its render width and height
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Measure<E: Engine> {
|
pub struct Measure<E: Output> {
|
||||||
_engine: PhantomData<E>,
|
_engine: PhantomData<E>,
|
||||||
pub x: Arc<AtomicUsize>,
|
pub x: Arc<AtomicUsize>,
|
||||||
pub y: Arc<AtomicUsize>,
|
pub y: Arc<AtomicUsize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> Content<E> for Measure<E> {
|
impl<E: Output> Content<E> for Measure<E> {
|
||||||
fn render (&self, to: &mut E::Output) {
|
fn render (&self, to: &mut E) {
|
||||||
self.x.store(to.area().w().into(), Relaxed);
|
self.x.store(to.area().w().into(), Relaxed);
|
||||||
self.y.store(to.area().h().into(), Relaxed);
|
self.y.store(to.area().h().into(), Relaxed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> Clone for Measure<E> {
|
impl<E: Output> Clone for Measure<E> {
|
||||||
fn clone (&self) -> Self {
|
fn clone (&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
_engine: Default::default(),
|
_engine: Default::default(),
|
||||||
|
|
@ -41,7 +41,7 @@ impl<E: Engine> Clone for Measure<E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> std::fmt::Debug for Measure<E> {
|
impl<E: Output> std::fmt::Debug for Measure<E> {
|
||||||
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||||
f.debug_struct("Measure")
|
f.debug_struct("Measure")
|
||||||
.field("width", &self.x)
|
.field("width", &self.x)
|
||||||
|
|
@ -50,7 +50,7 @@ impl<E: Engine> std::fmt::Debug for Measure<E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> Measure<E> {
|
impl<E: Output> Measure<E> {
|
||||||
pub fn w (&self) -> usize { self.x.load(Relaxed) }
|
pub fn w (&self) -> usize { self.x.load(Relaxed) }
|
||||||
pub fn h (&self) -> usize { self.y.load(Relaxed) }
|
pub fn h (&self) -> usize { self.y.load(Relaxed) }
|
||||||
pub fn wh (&self) -> [usize;2] { [self.w(), self.h()] }
|
pub fn wh (&self) -> [usize;2] { [self.w(), self.h()] }
|
||||||
|
|
@ -73,18 +73,18 @@ impl<E: Engine> Measure<E> {
|
||||||
///// A scrollable area.
|
///// A scrollable area.
|
||||||
//pub struct Scroll<E, F>(pub F, pub Direction, pub u64, PhantomData<E>)
|
//pub struct Scroll<E, F>(pub F, pub Direction, pub u64, PhantomData<E>)
|
||||||
//where
|
//where
|
||||||
//E: Engine,
|
//E: Output,
|
||||||
//F: Send + Sync + Fn(&mut dyn FnMut(&dyn Content<E>)->Usually<()>)->Usually<()>;
|
//F: Send + Sync + Fn(&mut dyn FnMut(&dyn Content<E>)->Usually<()>)->Usually<()>;
|
||||||
|
|
||||||
//pub trait ContentDebug<E: Engine> {
|
//pub trait ContentDebug<E: Output> {
|
||||||
//fn debug <W: Content<E>> (other: W) -> DebugOverlay<E, W> {
|
//fn debug <W: Content<E>> (other: W) -> DebugOverlay<E, W> {
|
||||||
//DebugOverlay(Default::default(), other)
|
//DebugOverlay(Default::default(), other)
|
||||||
//}
|
//}
|
||||||
//}
|
//}
|
||||||
|
|
||||||
//impl<E: Engine> ContentDebug<E> for E {}
|
//impl<E: Output> ContentDebug<E> for E {}
|
||||||
|
|
||||||
//impl Render<Tui> for Measure<Tui> {
|
//impl Render<TuiOut> for Measure<TuiOut> {
|
||||||
//fn min_size (&self, _: [u16;2]) -> Perhaps<[u16;2]> {
|
//fn min_size (&self, _: [u16;2]) -> Perhaps<[u16;2]> {
|
||||||
//Ok(Some([0u16.into(), 0u16.into()].into()))
|
//Ok(Some([0u16.into(), 0u16.into()].into()))
|
||||||
//}
|
//}
|
||||||
|
|
@ -95,13 +95,13 @@ impl<E: Engine> Measure<E> {
|
||||||
//}
|
//}
|
||||||
//}
|
//}
|
||||||
|
|
||||||
//impl Measure<Tui> {
|
//impl Measure<TuiOut> {
|
||||||
//pub fn debug (&self) -> ShowMeasure {
|
//pub fn debug (&self) -> ShowMeasure {
|
||||||
//ShowMeasure(&self)
|
//ShowMeasure(&self)
|
||||||
//}
|
//}
|
||||||
//}
|
//}
|
||||||
|
|
||||||
//render!(<Tui>|self: ShowMeasure<'a>|render(|to: &mut TuiOut|Ok({
|
//render!(Tui: |self: ShowMeasure<'a>|render(|to: &mut TuiOut|Ok({
|
||||||
//let w = self.0.w();
|
//let w = self.0.w();
|
||||||
//let h = self.0.h();
|
//let h = self.0.h();
|
||||||
//to.blit(&format!(" {w} x {h} "), to.area.x(), to.area.y(), Some(
|
//to.blit(&format!(" {w} x {h} "), to.area.x(), to.area.y(), Some(
|
||||||
|
|
@ -109,11 +109,11 @@ impl<E: Engine> Measure<E> {
|
||||||
//))
|
//))
|
||||||
//})));
|
//})));
|
||||||
|
|
||||||
//pub struct ShowMeasure<'a>(&'a Measure<Tui>);
|
//pub struct ShowMeasure<'a>(&'a Measure<TuiOut>);
|
||||||
|
|
||||||
//pub struct DebugOverlay<E: Engine, W: Render<E>>(PhantomData<E>, pub W);
|
//pub struct DebugOverlay<E: Output, W: Render<E>>(PhantomData<E>, pub W);
|
||||||
|
|
||||||
//impl<T: Render<Tui>> Render<Tui> for DebugOverlay<Tui, T> {
|
//impl<T: Render<TuiOut>> Render<TuiOut> for DebugOverlay<Tui, T> {
|
||||||
//fn min_size (&self, to: [u16;2]) -> Perhaps<[u16;2]> {
|
//fn min_size (&self, to: [u16;2]) -> Perhaps<[u16;2]> {
|
||||||
//self.1.min_size(to)
|
//self.1.min_size(to)
|
||||||
//}
|
//}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use crate::*;
|
||||||
/// Show an item only when a condition is true.
|
/// Show an item only when a condition is true.
|
||||||
pub struct When<A>(pub bool, pub A);
|
pub struct When<A>(pub bool, pub A);
|
||||||
|
|
||||||
impl<E: Engine, A: Render<E>> Content<E> for When<A> {
|
impl<E: Output, A: Render<E>> Content<E> for When<A> {
|
||||||
fn layout (&self, to: E::Area) -> E::Area {
|
fn layout (&self, to: E::Area) -> E::Area {
|
||||||
let Self(cond, item) = self;
|
let Self(cond, item) = self;
|
||||||
let mut area = E::Area::zero();
|
let mut area = E::Area::zero();
|
||||||
|
|
@ -16,7 +16,7 @@ impl<E: Engine, A: Render<E>> Content<E> for When<A> {
|
||||||
}
|
}
|
||||||
area.into()
|
area.into()
|
||||||
}
|
}
|
||||||
fn render (&self, to: &mut E::Output) {
|
fn render (&self, to: &mut E) {
|
||||||
let Self(cond, item) = self;
|
let Self(cond, item) = self;
|
||||||
if *cond { item.render(to) }
|
if *cond { item.render(to) }
|
||||||
}
|
}
|
||||||
|
|
@ -25,12 +25,12 @@ impl<E: Engine, A: Render<E>> Content<E> for When<A> {
|
||||||
/// Show one item if a condition is true and another if the condition is false
|
/// Show one item if a condition is true and another if the condition is false
|
||||||
pub struct Either<A, B>(pub bool, pub A, pub B);
|
pub struct Either<A, B>(pub bool, pub A, pub B);
|
||||||
|
|
||||||
impl<E: Engine, A: Render<E>, B: Render<E>> Content<E> for Either<A, B> {
|
impl<E: Output, A: Render<E>, B: Render<E>> Content<E> for Either<A, B> {
|
||||||
fn layout (&self, to: E::Area) -> E::Area {
|
fn layout (&self, to: E::Area) -> E::Area {
|
||||||
let Self(cond, a, b) = self;
|
let Self(cond, a, b) = self;
|
||||||
if *cond { a.layout(to) } else { b.layout(to) }
|
if *cond { a.layout(to) } else { b.layout(to) }
|
||||||
}
|
}
|
||||||
fn render (&self, to: &mut E::Output) {
|
fn render (&self, to: &mut E) {
|
||||||
let Self(cond, a, b) = self;
|
let Self(cond, a, b) = self;
|
||||||
if *cond { a.render(to) } else { b.render(to) }
|
if *cond { a.render(to) } else { b.render(to) }
|
||||||
}
|
}
|
||||||
|
|
@ -42,7 +42,7 @@ pub struct Map<A, B, I, F, G>(pub F, pub G) where
|
||||||
G: Fn(A, usize)->B + Send + Sync;
|
G: Fn(A, usize)->B + Send + Sync;
|
||||||
|
|
||||||
impl<E, A, B, I, F, G> Content<E> for Map<A, B, I, F, G> where
|
impl<E, A, B, I, F, G> Content<E> for Map<A, B, I, F, G> where
|
||||||
E: Engine,
|
E: Output,
|
||||||
B: Render<E>,
|
B: Render<E>,
|
||||||
I: Iterator<Item = A> + Send + Sync,
|
I: Iterator<Item = A> + Send + Sync,
|
||||||
F: Fn() -> I + Send + Sync,
|
F: Fn() -> I + Send + Sync,
|
||||||
|
|
@ -67,7 +67,7 @@ impl<E, A, B, I, F, G> Content<E> for Map<A, B, I, F, G> where
|
||||||
//[min_x.into(), min_y.into(), w.into(), h.into()].into()
|
//[min_x.into(), min_y.into(), w.into(), h.into()].into()
|
||||||
area.center_xy([w.into(), h.into()].into()).into()
|
area.center_xy([w.into(), h.into()].into()).into()
|
||||||
}
|
}
|
||||||
fn render (&self, to: &mut E::Output) {
|
fn render (&self, to: &mut E) {
|
||||||
let Self(get_iterator, callback) = self;
|
let Self(get_iterator, callback) = self;
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
//let area = self.layout(to.area());
|
//let area = self.layout(to.area());
|
||||||
|
|
@ -83,7 +83,7 @@ impl<E, A, B, I, F, G> Content<E> for Map<A, B, I, F, G> where
|
||||||
/*
|
/*
|
||||||
|
|
||||||
//pub fn reduce <E, T, I, R, F>(iterator: I, callback: F) -> Reduce<E, T, I, R, F> where
|
//pub fn reduce <E, T, I, R, F>(iterator: I, callback: F) -> Reduce<E, T, I, R, F> where
|
||||||
//E: Engine,
|
//E: Output,
|
||||||
//I: Iterator<Item = T> + Send + Sync,
|
//I: Iterator<Item = T> + Send + Sync,
|
||||||
//R: Render<E>,
|
//R: Render<E>,
|
||||||
//F: Fn(R, T, usize) -> R + Send + Sync
|
//F: Fn(R, T, usize) -> R + Send + Sync
|
||||||
|
|
@ -91,24 +91,24 @@ impl<E, A, B, I, F, G> Content<E> for Map<A, B, I, F, G> where
|
||||||
//Reduce(Default::default(), iterator, callback)
|
//Reduce(Default::default(), iterator, callback)
|
||||||
//}
|
//}
|
||||||
pub struct Reduce<E, T, I, R, F>(PhantomData<(E, R)>, I, F) where
|
pub struct Reduce<E, T, I, R, F>(PhantomData<(E, R)>, I, F) where
|
||||||
E: Engine,
|
E: Output,
|
||||||
I: Iterator<Item = T> + Send + Sync,
|
I: Iterator<Item = T> + Send + Sync,
|
||||||
R: Render<E>,
|
R: Render<E>,
|
||||||
F: Fn(R, T, usize) -> R + Send + Sync;
|
F: Fn(R, T, usize) -> R + Send + Sync;
|
||||||
impl<E, T, I, R, F> Content<E> for Reduce<E, T, I, R, F> where
|
impl<E, T, I, R, F> Content<E> for Reduce<E, T, I, R, F> where
|
||||||
E: Engine,
|
E: Output,
|
||||||
I: Iterator<Item = T> + Send + Sync,
|
I: Iterator<Item = T> + Send + Sync,
|
||||||
R: Render<E>,
|
R: Render<E>,
|
||||||
F: Fn(R, T, usize) -> R + Send + Sync
|
F: Fn(R, T, usize) -> R + Send + Sync
|
||||||
{
|
{
|
||||||
fn render (&self, to: &mut E::Output) {
|
fn render (&self, to: &mut E) {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//macro_rules! define_ops {
|
//macro_rules! define_ops {
|
||||||
//($Trait:ident<$E:ident:$Engine:path> { $(
|
//($Trait:ident<$E:ident:$Output:path> { $(
|
||||||
//$(#[$attr:meta $($attr_args:tt)*])*
|
//$(#[$attr:meta $($attr_args:tt)*])*
|
||||||
//(
|
//(
|
||||||
//$fn:ident
|
//$fn:ident
|
||||||
|
|
@ -117,8 +117,8 @@ impl<E, T, I, R, F> Content<E> for Reduce<E, T, I, R, F> where
|
||||||
//($($arg:ident:$Arg:ty),*)
|
//($($arg:ident:$Arg:ty),*)
|
||||||
//)
|
//)
|
||||||
//)* }) => {
|
//)* }) => {
|
||||||
//impl<$E: $Engine> $Trait<E> for E {}
|
//impl<$E: $Output> $Trait<E> for E {}
|
||||||
//pub trait $Trait<$E: $Engine> {
|
//pub trait $Trait<$E: $Output> {
|
||||||
//$(
|
//$(
|
||||||
//$(#[$attr $($attr_args)*])*
|
//$(#[$attr $($attr_args)*])*
|
||||||
//fn $fn $(<$($G),+>)?
|
//fn $fn $(<$($G),+>)?
|
||||||
|
|
@ -131,7 +131,7 @@ impl<E, T, I, R, F> Content<E> for Reduce<E, T, I, R, F> where
|
||||||
//}
|
//}
|
||||||
|
|
||||||
//define_ops! {
|
//define_ops! {
|
||||||
//Layout<E: Engine> {
|
//Layout<E: Output> {
|
||||||
//(when <A: Render<E>,>
|
//(when <A: Render<E>,>
|
||||||
//When(cond: bool, item: A))
|
//When(cond: bool, item: A))
|
||||||
///// When `cond` is `true`, render `a`, otherwise render `b`.
|
///// When `cond` is `true`, render `a`, otherwise render `b`.
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use crate::*;
|
||||||
/// Defines an enum that transforms its content
|
/// Defines an enum that transforms its content
|
||||||
/// along either the X axis, the Y axis, or both.
|
/// along either the X axis, the Y axis, or both.
|
||||||
///
|
///
|
||||||
/// The `_Unused` variant wraps the `Engine` type
|
/// The `_Unused` variant wraps the `Output` type
|
||||||
/// using `PhantomData` to permit the double generic.
|
/// using `PhantomData` to permit the double generic.
|
||||||
macro_rules! transform_xy {
|
macro_rules! transform_xy {
|
||||||
($self:ident : $Enum:ident |$to:ident|$area:expr) => {
|
($self:ident : $Enum:ident |$to:ident|$area:expr) => {
|
||||||
|
|
@ -13,15 +13,15 @@ macro_rules! transform_xy {
|
||||||
pub fn y (item: T) -> Self { Self::Y(item) }
|
pub fn y (item: T) -> Self { Self::Y(item) }
|
||||||
pub fn xy (item: T) -> Self { Self::XY(item) }
|
pub fn xy (item: T) -> Self { Self::XY(item) }
|
||||||
}
|
}
|
||||||
impl<E: Engine, T: Content<E>> Content<E> for $Enum<T> {
|
impl<E: Output, T: Content<E>> Content<E> for $Enum<T> {
|
||||||
fn content (&self) -> impl Render<E> {
|
fn content (&self) -> impl Render<E> {
|
||||||
match self {
|
match self {
|
||||||
Self::X(item) => item,
|
Self::X(item) => item,
|
||||||
Self::Y(item) => item,
|
Self::Y(item) => item,
|
||||||
Self::XY(item) => item,
|
Self::XY(item) => item
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn layout (&$self, $to: <E as Engine>::Area) -> <E as Engine>::Area {
|
fn layout (&$self, $to: <E as Output>::Area) -> <E as Output>::Area {
|
||||||
use $Enum::*;
|
use $Enum::*;
|
||||||
$area
|
$area
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ macro_rules! transform_xy_unit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<E: Engine, T: Content<E>> Content<E> for $Enum<E::Unit, T> {
|
impl<E: Output, T: Content<E>> Content<E> for $Enum<E::Unit, T> {
|
||||||
fn content (&self) -> impl Render<E> {
|
fn content (&self) -> impl Render<E> {
|
||||||
Some(match self {
|
Some(match self {
|
||||||
Self::X(_, content) => content,
|
Self::X(_, content) => content,
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ pub struct ArrangerTui {
|
||||||
pub selected: ArrangerSelection,
|
pub selected: ArrangerSelection,
|
||||||
pub mode: ArrangerMode,
|
pub mode: ArrangerMode,
|
||||||
pub color: ItemPalette,
|
pub color: ItemPalette,
|
||||||
pub size: Measure<Tui>,
|
pub size: Measure<TuiOut>,
|
||||||
pub note_buf: Vec<u8>,
|
pub note_buf: Vec<u8>,
|
||||||
pub midi_buf: Vec<Vec<Vec<u8>>>,
|
pub midi_buf: Vec<Vec<Vec<u8>>>,
|
||||||
pub editor: MidiEditor,
|
pub editor: MidiEditor,
|
||||||
|
|
@ -97,14 +97,14 @@ from_jack!(|jack| ArrangerTui {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
impl ArrangerTui {
|
impl ArrangerTui {
|
||||||
fn render_mode (state: &Self) -> impl Content<Tui> + use<'_> {
|
fn render_mode (state: &Self) -> impl Content<TuiOut> + use<'_> {
|
||||||
match state.mode {
|
match state.mode {
|
||||||
ArrangerMode::H => todo!("horizontal arranger"),
|
ArrangerMode::H => todo!("horizontal arranger"),
|
||||||
ArrangerMode::V(factor) => Self::render_mode_v(state, factor),
|
ArrangerMode::V(factor) => Self::render_mode_v(state, factor),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
render!(Tui: (self: ArrangerTui) => {
|
render!(TuiOut: (self: ArrangerTui) => {
|
||||||
let pool_size = if self.pool.visible { self.splits[1] } else { 0 };
|
let pool_size = if self.pool.visible { self.splits[1] } else { 0 };
|
||||||
let with_pool = |x|Bsp::w(Fixed::x(pool_size, PoolView(self.pool.visible, &self.pool)), x);
|
let with_pool = |x|Bsp::w(Fixed::x(pool_size, PoolView(self.pool.visible, &self.pool)), x);
|
||||||
let status = ArrangerStatus::from(self);
|
let status = ArrangerStatus::from(self);
|
||||||
|
|
@ -164,4 +164,4 @@ audio!(|self: ArrangerTui, client, scope|{
|
||||||
has_clock!(|self: ArrangerTui|&self.clock);
|
has_clock!(|self: ArrangerTui|&self.clock);
|
||||||
has_phrases!(|self: ArrangerTui|self.pool.phrases);
|
has_phrases!(|self: ArrangerTui|self.pool.phrases);
|
||||||
has_editor!(|self: ArrangerTui|self.editor);
|
has_editor!(|self: ArrangerTui|self.editor);
|
||||||
handle!(<Tui>|self: ArrangerTui, input|ArrangerCommand::execute_with_state(self, input.event()));
|
handle!(TuiIn: |self: ArrangerTui, input|ArrangerCommand::execute_with_state(self, input.event()));
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ pub enum ArrangerClipCommand {
|
||||||
SetColor(usize, usize, ItemPalette),
|
SetColor(usize, usize, ItemPalette),
|
||||||
}
|
}
|
||||||
|
|
||||||
//handle!(<Tui>|self: ArrangerTui, input|ArrangerCommand::execute_with_state(self, input.event()));
|
//handle!(TuiIn: |self: ArrangerTui, input|ArrangerCommand::execute_with_state(self, input.event()));
|
||||||
//input_to_command!(ArrangerCommand: |state: ArrangerTui, input: Event|{KEYS_ARRANGER.handle(state, input)?});
|
//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 {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ pub enum ArrangerMode {
|
||||||
/// Tracks are rows
|
/// Tracks are rows
|
||||||
H,
|
H,
|
||||||
}
|
}
|
||||||
impl<E: Engine> Content<E> for ArrangerMode {}
|
impl<E: Output> Content<E> for ArrangerMode {}
|
||||||
/// Arranger display mode can be cycled
|
/// Arranger display mode can be cycled
|
||||||
impl ArrangerMode {
|
impl ArrangerMode {
|
||||||
/// Cycle arranger display mode
|
/// Cycle arranger display mode
|
||||||
|
|
@ -21,6 +21,6 @@ impl ArrangerMode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn any_size <E: Engine> (_: E::Size) -> Perhaps<E::Size>{
|
fn any_size <E: Output> (_: E::Size) -> Perhaps<E::Size>{
|
||||||
Ok(Some([0.into(),0.into()].into()))
|
Ok(Some([0.into(),0.into()].into()))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use crate::*;
|
||||||
pub(crate) const HEADER_H: u16 = 5;
|
pub(crate) const HEADER_H: u16 = 5;
|
||||||
pub(crate) const SCENES_W_OFFSET: u16 = 3;
|
pub(crate) const SCENES_W_OFFSET: u16 = 3;
|
||||||
impl ArrangerTui {
|
impl ArrangerTui {
|
||||||
pub fn render_mode_v (state: &ArrangerTui, factor: usize) -> impl Content<Tui> + use<'_> {
|
pub fn render_mode_v (state: &ArrangerTui, factor: usize) -> impl Content<TuiOut> + use<'_> {
|
||||||
lay!(
|
lay!(
|
||||||
ArrangerVColSep::from(state),
|
ArrangerVColSep::from(state),
|
||||||
ArrangerVRowSep::from((state, factor)),
|
ArrangerVRowSep::from((state, factor)),
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use crate::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub struct ArrangerVClips<'a> {
|
pub struct ArrangerVClips<'a> {
|
||||||
size: &'a Measure<Tui>,
|
size: &'a Measure<TuiOut>,
|
||||||
scenes: &'a Vec<ArrangerScene>,
|
scenes: &'a Vec<ArrangerScene>,
|
||||||
tracks: &'a Vec<ArrangerTrack>,
|
tracks: &'a Vec<ArrangerTrack>,
|
||||||
rows: Vec<(usize, usize)>,
|
rows: Vec<(usize, usize)>,
|
||||||
|
|
@ -17,8 +17,8 @@ impl<'a> ArrangerVClips<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a> Content<Tui> for ArrangerVClips<'a> {
|
impl<'a> Content<TuiOut> for ArrangerVClips<'a> {
|
||||||
fn content (&self) -> impl Render<Tui> {
|
fn content (&self) -> impl Render<TuiOut> {
|
||||||
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)
|
||||||
|
|
@ -28,7 +28,7 @@ impl<'a> ArrangerVClips<'a> {
|
||||||
|
|
||||||
fn format_scene (
|
fn format_scene (
|
||||||
tracks: &'a [ArrangerTrack], scene: &'a ArrangerScene, pulses: usize
|
tracks: &'a [ArrangerTrack], scene: &'a ArrangerScene, pulses: usize
|
||||||
) -> impl Content<Tui> + use<'a> {
|
) -> impl Content<TuiOut> + use<'a> {
|
||||||
let height = 1.max((pulses / PPQ) as u16);
|
let height = 1.max((pulses / PPQ) as u16);
|
||||||
let playing = scene.is_playing(tracks);
|
let playing = scene.is_playing(tracks);
|
||||||
let icon = Tui::bg(
|
let icon = Tui::bg(
|
||||||
|
|
@ -45,7 +45,7 @@ impl<'a> ArrangerVClips<'a> {
|
||||||
|
|
||||||
fn format_clip (
|
fn format_clip (
|
||||||
scene: &'a ArrangerScene, index: usize, track: &'a ArrangerTrack, w: u16, h: u16
|
scene: &'a ArrangerScene, index: usize, track: &'a ArrangerTrack, w: u16, h: u16
|
||||||
) -> impl Content<Tui> + use<'a> {
|
) -> impl Content<TuiOut> + use<'a> {
|
||||||
scene.clips.get(index).map(|clip|clip.as_ref().map(|phrase|{
|
scene.clips.get(index).map(|clip|clip.as_ref().map(|phrase|{
|
||||||
let phrase = phrase.read().unwrap();
|
let phrase = phrase.read().unwrap();
|
||||||
let mut bg = TuiTheme::border_bg();
|
let mut bg = TuiTheme::border_bg();
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ from!(|args:(&ArrangerTui, usize)|ArrangerVCursor = Self {
|
||||||
sub_modifier: Modifier::DIM
|
sub_modifier: Modifier::DIM
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
impl Content<Tui> for ArrangerVCursor {
|
impl Content<TuiOut> for ArrangerVCursor {
|
||||||
fn render (&self, to: &mut TuiOut) {
|
fn render (&self, to: &mut TuiOut) {
|
||||||
let area = to.area();
|
let area = to.area();
|
||||||
let focused = true;
|
let focused = true;
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ from!(<'a>|state: &'a ArrangerTui|ArrangerVHead<'a> = Self { // A
|
||||||
scenes_w: SCENES_W_OFFSET + ArrangerScene::longest_name(&state.scenes) as u16,
|
scenes_w: SCENES_W_OFFSET + ArrangerScene::longest_name(&state.scenes) as u16,
|
||||||
});
|
});
|
||||||
|
|
||||||
render!(Tui: (self: ArrangerVHead<'a>) => {
|
render!(TuiOut: (self: ArrangerVHead<'a>) => {
|
||||||
fn row <T: Content<Tui>> (color: ItemPalette, field: T) -> impl Content<Tui> {
|
fn row <T: Content<TuiOut>> (color: ItemPalette, field: T) -> impl Content<TuiOut> {
|
||||||
row!(Tui::fg(color.light.rgb, "▎"), Tui::fg(color.lightest.rgb, field))
|
row!(Tui::fg(color.light.rgb, "▎"), Tui::fg(color.lightest.rgb, field))
|
||||||
}
|
}
|
||||||
Some(Push::x(self.scenes_w,
|
Some(Push::x(self.scenes_w,
|
||||||
|
|
@ -38,22 +38,22 @@ render!(Tui: (self: ArrangerVHead<'a>) => {
|
||||||
|
|
||||||
impl ArrangerVHead<'_> {
|
impl ArrangerVHead<'_> {
|
||||||
/// name and width of track
|
/// name and width of track
|
||||||
fn format_name (track: &ArrangerTrack, _w: usize) -> impl Content<Tui> {
|
fn format_name (track: &ArrangerTrack, _w: usize) -> impl Content<TuiOut> {
|
||||||
let name = track.name().read().unwrap().clone();
|
let name = track.name().read().unwrap().clone();
|
||||||
Tui::bold(true, Tui::fg(track.color.lightest.rgb, name))
|
Tui::bold(true, Tui::fg(track.color.lightest.rgb, name))
|
||||||
}
|
}
|
||||||
/// input port
|
/// input port
|
||||||
fn format_input (track: &ArrangerTrack) -> Usually<impl Content<Tui>> {
|
fn format_input (track: &ArrangerTrack) -> Usually<impl Content<TuiOut>> {
|
||||||
Ok(format!(">{}", track.player.midi_ins().first().map(|port|port.short_name())
|
Ok(format!(">{}", track.player.midi_ins().first().map(|port|port.short_name())
|
||||||
.transpose()?.unwrap_or("?".into())))
|
.transpose()?.unwrap_or("?".into())))
|
||||||
}
|
}
|
||||||
/// output port
|
/// output port
|
||||||
fn format_output (track: &ArrangerTrack) -> Usually<impl Content<Tui>> {
|
fn format_output (track: &ArrangerTrack) -> Usually<impl Content<TuiOut>> {
|
||||||
Ok(format!("<{}", track.player.midi_outs().first().map(|port|port.short_name())
|
Ok(format!("<{}", track.player.midi_outs().first().map(|port|port.short_name())
|
||||||
.transpose()?.unwrap_or("?".into())))
|
.transpose()?.unwrap_or("?".into())))
|
||||||
}
|
}
|
||||||
/// beats elapsed
|
/// beats elapsed
|
||||||
fn format_elapsed (track: &ArrangerTrack, timebase: &Arc<Timebase>) -> impl Content<Tui> {
|
fn format_elapsed (track: &ArrangerTrack, timebase: &Arc<Timebase>) -> impl Content<TuiOut> {
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
if let Some((_, Some(phrase))) = track.player.play_phrase().as_ref() {
|
if let Some((_, Some(phrase))) = track.player.play_phrase().as_ref() {
|
||||||
let length = phrase.read().unwrap().length;
|
let length = phrase.read().unwrap().length;
|
||||||
|
|
@ -67,7 +67,7 @@ impl ArrangerVHead<'_> {
|
||||||
}
|
}
|
||||||
/// beats until switchover
|
/// beats until switchover
|
||||||
fn format_until_next (track: &ArrangerTrack, current: &Arc<Moment>)
|
fn format_until_next (track: &ArrangerTrack, current: &Arc<Moment>)
|
||||||
-> Option<impl Content<Tui>>
|
-> Option<impl Content<TuiOut>>
|
||||||
{
|
{
|
||||||
let timebase = ¤t.timebase;
|
let timebase = ¤t.timebase;
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use crate::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub struct ArrangerVIns<'a> {
|
pub struct ArrangerVIns<'a> {
|
||||||
size: &'a Measure<Tui>,
|
size: &'a Measure<TuiOut>,
|
||||||
tracks: &'a Vec<ArrangerTrack>,
|
tracks: &'a Vec<ArrangerTrack>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -11,10 +11,10 @@ from!(<'a>|args: &'a ArrangerTui|ArrangerVIns<'a> = Self {
|
||||||
tracks: &args.tracks,
|
tracks: &args.tracks,
|
||||||
});
|
});
|
||||||
|
|
||||||
render!(Tui: (self: ArrangerVIns<'a>) => "");
|
render!(TuiOut: (self: ArrangerVIns<'a>) => "");
|
||||||
|
|
||||||
pub struct ArrangerVOuts<'a> {
|
pub struct ArrangerVOuts<'a> {
|
||||||
size: &'a Measure<Tui>,
|
size: &'a Measure<TuiOut>,
|
||||||
tracks: &'a Vec<ArrangerTrack>,
|
tracks: &'a Vec<ArrangerTrack>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -23,4 +23,4 @@ from!(<'a>|args: &'a ArrangerTui|ArrangerVOuts<'a> = Self {
|
||||||
tracks: &args.tracks,
|
tracks: &args.tracks,
|
||||||
});
|
});
|
||||||
|
|
||||||
render!(Tui: (self: ArrangerVOuts<'a>) => "");
|
render!(TuiOut: (self: ArrangerVOuts<'a>) => "");
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ from!(|state:&ArrangerTui|ArrangerVColSep = Self {
|
||||||
cols: ArrangerTrack::widths(&state.tracks),
|
cols: ArrangerTrack::widths(&state.tracks),
|
||||||
scenes_w: SCENES_W_OFFSET + ArrangerScene::longest_name(&state.scenes) as u16,
|
scenes_w: SCENES_W_OFFSET + ArrangerScene::longest_name(&state.scenes) as u16,
|
||||||
});
|
});
|
||||||
render!(Tui: |self: ArrangerVColSep, to| {
|
render!(TuiOut: |self: ArrangerVColSep, to| {
|
||||||
let style = Some(Style::default().fg(self.fg));
|
let style = Some(Style::default().fg(self.fg));
|
||||||
for x in self.cols.iter().map(|col|col.1) {
|
for x in self.cols.iter().map(|col|col.1) {
|
||||||
let x = self.scenes_w + to.area().x() + x as u16;
|
let x = self.scenes_w + to.area().x() + x as u16;
|
||||||
|
|
@ -29,7 +29,7 @@ from!(|args:(&ArrangerTui, usize)|ArrangerVRowSep = Self {
|
||||||
fg: TuiTheme::separator_fg(false),
|
fg: TuiTheme::separator_fg(false),
|
||||||
rows: ArrangerScene::ppqs(&args.0.scenes, args.1),
|
rows: ArrangerScene::ppqs(&args.0.scenes, args.1),
|
||||||
});
|
});
|
||||||
render!(Tui: |self: ArrangerVRowSep, to|for y in self.rows.iter().map(|row|row.1) {
|
render!(TuiOut: |self: ArrangerVRowSep, to|for y in self.rows.iter().map(|row|row.1) {
|
||||||
let y = to.area().y() + (y / PPQ) as u16 + 1;
|
let y = to.area().y() + (y / PPQ) as u16 + 1;
|
||||||
if y >= to.buffer.area.height { break }
|
if y >= to.buffer.area.height { break }
|
||||||
for x in to.area().x()..to.area().x2().saturating_sub(2) {
|
for x in to.area().x()..to.area().x2().saturating_sub(2) {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ pub struct TransportTui {
|
||||||
}
|
}
|
||||||
has_clock!(|self: TransportTui|&self.clock);
|
has_clock!(|self: TransportTui|&self.clock);
|
||||||
audio!(|self: TransportTui, client, scope|ClockAudio(self).process(client, scope));
|
audio!(|self: TransportTui, client, scope|ClockAudio(self).process(client, scope));
|
||||||
render!(Tui: (self: TransportTui) => TransportView {
|
render!(TuiOut: (self: TransportTui) => TransportView {
|
||||||
compact: false,
|
compact: false,
|
||||||
clock: &self.clock
|
clock: &self.clock
|
||||||
});
|
});
|
||||||
|
|
@ -25,7 +25,7 @@ impl<'a> TransportView<'a> {
|
||||||
Self { compact, clock }
|
Self { compact, clock }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
render!(Tui: (self: TransportView<'a>) => Outer(
|
render!(TuiOut: (self: TransportView<'a>) => Outer(
|
||||||
Style::default().fg(TuiTheme::g(255))
|
Style::default().fg(TuiTheme::g(255))
|
||||||
).enclose(row!(
|
).enclose(row!(
|
||||||
OutputStats::new(self.compact, self.clock),
|
OutputStats::new(self.compact, self.clock),
|
||||||
|
|
@ -36,7 +36,7 @@ render!(Tui: (self: TransportView<'a>) => Outer(
|
||||||
)));
|
)));
|
||||||
|
|
||||||
pub struct PlayPause { pub compact: bool, pub playing: bool }
|
pub struct PlayPause { pub compact: bool, pub playing: bool }
|
||||||
render!(Tui: (self: PlayPause) => Tui::bg(
|
render!(TuiOut: (self: PlayPause) => Tui::bg(
|
||||||
if self.playing{Color::Rgb(0,128,0)}else{Color::Rgb(128,64,0)},
|
if self.playing{Color::Rgb(0,128,0)}else{Color::Rgb(128,64,0)},
|
||||||
Either(self.compact,
|
Either(self.compact,
|
||||||
Thunk::new(||Fixed::x(9, Either(self.playing,
|
Thunk::new(||Fixed::x(9, Either(self.playing,
|
||||||
|
|
@ -59,7 +59,7 @@ impl BeatStats {
|
||||||
Self { compact, bpm: format!("{:.3}", clock.timebase.bpm.get()), beat, time }
|
Self { compact, bpm: format!("{:.3}", clock.timebase.bpm.get()), beat, time }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
render!(Tui: (self: BeatStats) => Either(self.compact,
|
render!(TuiOut: (self: BeatStats) => Either(self.compact,
|
||||||
row!(
|
row!(
|
||||||
FieldV(TuiTheme::g(128).into(), "BPM", &self.bpm),
|
FieldV(TuiTheme::g(128).into(), "BPM", &self.bpm),
|
||||||
FieldV(TuiTheme::g(128).into(), "Beat", &self.beat),
|
FieldV(TuiTheme::g(128).into(), "Beat", &self.beat),
|
||||||
|
|
@ -88,7 +88,7 @@ impl OutputStats {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
render!(Tui: (self: OutputStats) => Either(self.compact,
|
render!(TuiOut: (self: OutputStats) => Either(self.compact,
|
||||||
row!(
|
row!(
|
||||||
FieldV(TuiTheme::g(128).into(), "SR", &self.sample_rate),
|
FieldV(TuiTheme::g(128).into(), "SR", &self.sample_rate),
|
||||||
FieldV(TuiTheme::g(128).into(), "Buf", &self.buffer_size),
|
FieldV(TuiTheme::g(128).into(), "Buf", &self.buffer_size),
|
||||||
|
|
@ -100,7 +100,7 @@ render!(Tui: (self: OutputStats) => Either(self.compact,
|
||||||
Bsp::e(Tui::fg(TuiTheme::g(255), format!("{:.3}ms", self.latency)), " latency"),
|
Bsp::e(Tui::fg(TuiTheme::g(255), format!("{:.3}ms", self.latency)), " latency"),
|
||||||
)));
|
)));
|
||||||
|
|
||||||
handle!(<Tui>|self: TransportTui, input|ClockCommand::execute_with_state(self, input.event()));
|
handle!(TuiIn: |self: TransportTui, input|ClockCommand::execute_with_state(self, input.event()));
|
||||||
keymap!(TRANSPORT_KEYS = |state: TransportTui, input: Event| ClockCommand {
|
keymap!(TRANSPORT_KEYS = |state: TransportTui, input: Event| ClockCommand {
|
||||||
key(Char(' ')) =>
|
key(Char(' ')) =>
|
||||||
if state.clock().is_stopped() { Play(None) } else { Pause(None) },
|
if state.clock().is_stopped() { Play(None) } else { Pause(None) },
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,10 @@ use crate::*;
|
||||||
pub struct Field<T, U>(pub ItemPalette, pub T, pub U)
|
pub struct Field<T, U>(pub ItemPalette, pub T, pub U)
|
||||||
where T: AsRef<str> + Send + Sync, U: AsRef<str> + Send + Sync;
|
where T: AsRef<str> + Send + Sync, U: AsRef<str> + Send + Sync;
|
||||||
|
|
||||||
impl<T, U> Content<Tui> for Field<T, U>
|
impl<T, U> Content<TuiOut> 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 Render<Tui> {
|
fn content (&self) -> impl Render<TuiOut> {
|
||||||
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, "▐"),
|
||||||
|
|
@ -20,10 +20,10 @@ impl<T, U> Content<Tui> for Field<T, U>
|
||||||
pub struct FieldV<T, U>(pub ItemPalette, pub T, pub U)
|
pub struct FieldV<T, U>(pub ItemPalette, pub T, pub U)
|
||||||
where T: AsRef<str> + Send + Sync, U: AsRef<str> + Send + Sync;
|
where T: AsRef<str> + Send + Sync, U: AsRef<str> + Send + Sync;
|
||||||
|
|
||||||
impl<T, U> Content<Tui> for FieldV<T, U>
|
impl<T, U> Content<TuiOut> 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 Render<Tui> {
|
fn content (&self) -> impl Render<TuiOut> {
|
||||||
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, "▌"));
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ pub struct FileBrowser {
|
||||||
pub filter: String,
|
pub filter: String,
|
||||||
pub index: usize,
|
pub index: usize,
|
||||||
pub scroll: usize,
|
pub scroll: usize,
|
||||||
pub size: Measure<Tui>
|
pub size: Measure<TuiOut>
|
||||||
}
|
}
|
||||||
/// Commands supported by [FileBrowser]
|
/// Commands supported by [FileBrowser]
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
|
@ -20,7 +20,7 @@ pub enum FileBrowserCommand {
|
||||||
Chdir(PathBuf),
|
Chdir(PathBuf),
|
||||||
Filter(String),
|
Filter(String),
|
||||||
}
|
}
|
||||||
render!(Tui: (self: FileBrowser) => /*Stack::down(|add|{
|
render!(TuiOut: (self: FileBrowser) => /*Stack::down(|add|{
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
for (_, name) in self.dirs.iter() {
|
for (_, name) in self.dirs.iter() {
|
||||||
if i >= self.scroll {
|
if i >= self.scroll {
|
||||||
|
|
|
||||||
|
|
@ -243,7 +243,7 @@ impl<T: FocusGrid + HasEnter> FocusOrder for T {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FocusWrap<T> {
|
pub trait FocusWrap<T> {
|
||||||
fn wrap <W: Content<Tui>> (self, focus: T, content: &'_ W) -> impl Content<Tui> + '_;
|
fn wrap <W: Content<TuiOut>> (self, focus: T, content: &'_ W) -> impl Content<TuiOut> + '_;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_focus_command <T: Send + Sync> (input: &TuiIn) -> Option<FocusCommand<T>> {
|
pub fn to_focus_command <T: Send + Sync> (input: &TuiIn) -> Option<FocusCommand<T>> {
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ pub struct Groovebox {
|
||||||
pub sampler: Sampler,
|
pub sampler: Sampler,
|
||||||
|
|
||||||
pub compact: bool,
|
pub compact: bool,
|
||||||
pub size: Measure<Tui>,
|
pub size: Measure<TuiOut>,
|
||||||
pub status: bool,
|
pub status: bool,
|
||||||
pub note_buf: Vec<u8>,
|
pub note_buf: Vec<u8>,
|
||||||
pub midi_buf: Vec<Vec<Vec<u8>>>,
|
pub midi_buf: Vec<Vec<Vec<u8>>>,
|
||||||
|
|
@ -61,7 +61,7 @@ impl Groovebox {
|
||||||
|
|
||||||
has_clock!(|self: Groovebox|self.player.clock());
|
has_clock!(|self: Groovebox|self.player.clock());
|
||||||
|
|
||||||
impl EdnViewData<Tui> for &Groovebox {
|
impl EdnViewData<TuiOut> for &Groovebox {
|
||||||
fn get_bool (&self, item: EdnItem<&str>) -> bool { todo!() }
|
fn get_bool (&self, item: EdnItem<&str>) -> bool { todo!() }
|
||||||
fn get_unit (&self, item: EdnItem<&str>) -> u16 {
|
fn get_unit (&self, item: EdnItem<&str>) -> u16 {
|
||||||
use EdnItem::*;
|
use EdnItem::*;
|
||||||
|
|
@ -76,7 +76,7 @@ impl EdnViewData<Tui> for &Groovebox {
|
||||||
_ => 0
|
_ => 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn get_content <'a> (&'a self, item: EdnItem<&str>) -> RenderBox<'a, Tui> {
|
fn get_content <'a> (&'a self, item: EdnItem<&str>) -> RenderBox<'a, TuiOut> {
|
||||||
use EdnItem::*;
|
use EdnItem::*;
|
||||||
match item.to_str() {
|
match item.to_str() {
|
||||||
":input-meter-l" => Box::new(Meter("L/", self.sampler.input_meter[0])),
|
":input-meter-l" => Box::new(Meter("L/", self.sampler.input_meter[0])),
|
||||||
|
|
@ -94,7 +94,7 @@ impl EdnViewData<Tui> for &Groovebox {
|
||||||
":sample-stat" => Box::new(SamplerStatus(&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)),
|
":samples-view" => Box::new(SampleList::new(self.compact, &self.sampler, &self.editor)),
|
||||||
|
|
||||||
_ => Box::new(())
|
_ => panic!("{item:?}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -119,12 +119,12 @@ from!(|state: &Groovebox|GrooveboxStatus = {
|
||||||
size: format!("{}x{}│", width, state.size.h()),
|
size: format!("{}x{}│", width, state.size.h()),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
render!(Tui: (self: GrooveboxStatus) => Fixed::y(2, lay!(
|
render!(TuiOut: (self: GrooveboxStatus) => Fixed::y(2, lay!(
|
||||||
Self::help(),
|
Self::help(),
|
||||||
Fill::xy(Align::se(Tui::fg_bg(TuiTheme::orange(), TuiTheme::g(25), self.stats()))),
|
Fill::xy(Align::se(Tui::fg_bg(TuiTheme::orange(), TuiTheme::g(25), self.stats()))),
|
||||||
)));
|
)));
|
||||||
impl GrooveboxStatus {
|
impl GrooveboxStatus {
|
||||||
fn help () -> impl Content<Tui> {
|
fn help () -> impl Content<TuiOut> {
|
||||||
let single = |binding, command|row!(" ", col!(
|
let single = |binding, command|row!(" ", col!(
|
||||||
Tui::fg(TuiTheme::yellow(), binding),
|
Tui::fg(TuiTheme::yellow(), binding),
|
||||||
command
|
command
|
||||||
|
|
@ -143,7 +143,7 @@ impl GrooveboxStatus {
|
||||||
double(("c", "color"), ("", ""),),
|
double(("c", "color"), ("", ""),),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
fn stats (&self) -> impl Content<Tui> + use<'_> {
|
fn stats (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
row!(&self.cpu, &self.size)
|
row!(&self.cpu, &self.size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ command!(|self: GrooveboxCommand, state: Groovebox|match self {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
handle!(<Tui>|self: Groovebox, input|
|
handle!(TuiIn: |self: Groovebox, input|
|
||||||
GrooveboxCommand::execute_with_state(self, input.event()));
|
GrooveboxCommand::execute_with_state(self, input.event()));
|
||||||
|
|
||||||
keymap!(<'a> KEYS_GROOVEBOX = |state: Groovebox, input: Event| GrooveboxCommand {
|
keymap!(<'a> KEYS_GROOVEBOX = |state: Groovebox, input: Event| GrooveboxCommand {
|
||||||
|
|
|
||||||
|
|
@ -6,28 +6,28 @@ use EdnItem::*;
|
||||||
|
|
||||||
const EDN: &'static str = include_str!("groovebox.edn");
|
const EDN: &'static str = include_str!("groovebox.edn");
|
||||||
|
|
||||||
//impl Content<Tui> for Groovebox {
|
impl Content<TuiOut> for Groovebox {
|
||||||
//fn content (&self) -> impl Render<Tui> {
|
fn content (&self) -> impl Render<TuiOut> {
|
||||||
//self.size.of(EdnView::new(self, EDN).expect("failed to build view"))
|
self.size.of(EdnView::new(self, EDN).expect("failed to build view"))
|
||||||
//}
|
}
|
||||||
//}
|
}
|
||||||
|
|
||||||
render!(Tui: (self: Groovebox) => self.size.of(
|
//render!(TuiOut: (self: Groovebox) => 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 Groovebox {
|
||||||
fn toolbar_view (&self) -> impl Content<Tui> + use<'_> {
|
fn toolbar_view (&self) -> impl Content<TuiOut> + 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])),
|
||||||
Align::e(Meter("R/", self.sampler.input_meter[1])),
|
Align::e(Meter("R/", self.sampler.input_meter[1])),
|
||||||
Align::x(TransportView::new(true, &self.player.clock)),
|
Align::x(TransportView::new(true, &self.player.clock)),
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
fn selector_view (&self) -> impl Content<Tui> + use<'_> {
|
fn selector_view (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
row!(
|
row!(
|
||||||
ClipSelected::play_phrase(&self.player),
|
ClipSelected::play_phrase(&self.player),
|
||||||
ClipSelected::next_phrase(&self.player),
|
ClipSelected::next_phrase(&self.player),
|
||||||
|
|
@ -35,23 +35,23 @@ impl Groovebox {
|
||||||
MidiEditStatus(&self.editor),
|
MidiEditStatus(&self.editor),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
fn sample_view (&self) -> impl Content<Tui> + use<'_> {
|
fn sample_view (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
let note_pt = self.editor.note_point();
|
let note_pt = self.editor.note_point();
|
||||||
let sample_h = if self.compact { 0 } else { 5 };
|
let sample_h = if self.compact { 0 } else { 5 };
|
||||||
Max::y(sample_h, Fill::xy(
|
Max::y(sample_h, Fill::xy(
|
||||||
SampleViewer::from_sampler(&self.sampler, note_pt)))
|
SampleViewer::from_sampler(&self.sampler, note_pt)))
|
||||||
}
|
}
|
||||||
fn status_view (&self) -> impl Content<Tui> + use<'_> {
|
fn status_view (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
let note_pt = self.editor.note_point();
|
let note_pt = self.editor.note_point();
|
||||||
Align::w(Fixed::y(1, SamplerStatus(&self.sampler, note_pt)))
|
Align::w(Fixed::y(1, SamplerStatus(&self.sampler, note_pt)))
|
||||||
}
|
}
|
||||||
fn pool_view (&self) -> impl Content<Tui> + use<'_> {
|
fn pool_view (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
let w = self.size.w();
|
let w = self.size.w();
|
||||||
let pool_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 };
|
let pool_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 };
|
||||||
Fixed::x(if self.compact { 5 } else { pool_w },
|
Fixed::x(if self.compact { 5 } else { pool_w },
|
||||||
PoolView(self.compact, &self.pool))
|
PoolView(self.compact, &self.pool))
|
||||||
}
|
}
|
||||||
fn sampler_view (&self) -> impl Content<Tui> + use<'_> {
|
fn sampler_view (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
let sampler_w = if self.compact { 4 } else { 11 };
|
let sampler_w = if self.compact { 4 } else { 11 };
|
||||||
let sampler_y = if self.compact { 1 } else { 0 };
|
let sampler_y = if self.compact { 1 } else { 0 };
|
||||||
Fixed::x(sampler_w, Push::y(sampler_y, Fill::y(
|
Fixed::x(sampler_w, Push::y(sampler_y, Fill::y(
|
||||||
|
|
@ -59,7 +59,7 @@ impl Groovebox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//render!(Tui: (self: Groovebox) => self.size.of(
|
//render!(TuiOut: (self: Groovebox) => 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(),
|
||||||
|
|
@ -68,8 +68,8 @@ impl Groovebox {
|
||||||
|
|
||||||
//const GROOVEBOX_EDN: &'static str = include_str!("groovebox.edn");
|
//const GROOVEBOX_EDN: &'static str = include_str!("groovebox.edn");
|
||||||
|
|
||||||
//impl Content<Tui> for Groovebox {
|
//impl Content<TuiOut> for Groovebox {
|
||||||
//fn content (&self) -> impl Content<Tui> {
|
//fn content (&self) -> impl Content<TuiOut> {
|
||||||
//EdnView::parse(self.edn.as_slice())
|
//EdnView::parse(self.edn.as_slice())
|
||||||
//}
|
//}
|
||||||
//}
|
//}
|
||||||
|
|
@ -119,17 +119,17 @@ impl Groovebox {
|
||||||
//}
|
//}
|
||||||
|
|
||||||
////impl Groovebox {
|
////impl Groovebox {
|
||||||
////fn status_view (&self) -> impl Content<Tui> + use<'_> {
|
////fn status_view (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
////let note_pt = self.editor.note_point();
|
////let note_pt = self.editor.note_point();
|
||||||
////Align::w(Fixed::y(1, ))
|
////Align::w(Fixed::y(1, ))
|
||||||
////}
|
////}
|
||||||
////fn pool_view (&self) -> impl Content<Tui> + use<'_> {
|
////fn pool_view (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
////let w = self.size.w();
|
////let w = self.size.w();
|
||||||
////let pool_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 };
|
////let pool_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 };
|
||||||
////Fixed::x(if self.compact { 5 } else { pool_w },
|
////Fixed::x(if self.compact { 5 } else { pool_w },
|
||||||
////)
|
////)
|
||||||
////}
|
////}
|
||||||
////fn sampler_view (&self) -> impl Content<Tui> + use<'_> {
|
////fn sampler_view (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
////let sampler_w = if self.compact { 4 } else { 11 };
|
////let sampler_w = if self.compact { 4 } else { 11 };
|
||||||
////let sampler_y = if self.compact { 1 } else { 0 };
|
////let sampler_y = if self.compact { 1 } else { 0 };
|
||||||
////Fixed::x(sampler_w, Push::y(sampler_y, Fill::y(
|
////Fixed::x(sampler_w, Push::y(sampler_y, Fill::y(
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub struct Meter<'a>(pub &'a str, pub f32);
|
pub struct Meter<'a>(pub &'a str, pub f32);
|
||||||
|
render!(TuiOut: (self: Meter<'a>) => col!(
|
||||||
render!(Tui: (self: Meter<'a>) => col!(
|
|
||||||
Field(TuiTheme::g(128).into(), self.0, format!("{:>+9.3}", self.1)),
|
Field(TuiTheme::g(128).into(), self.0, format!("{:>+9.3}", self.1)),
|
||||||
Fixed::xy(if self.1 >= 0.0 { 13 }
|
Fixed::xy(if self.1 >= 0.0 { 13 }
|
||||||
else if self.1 >= -1.0 { 12 }
|
else if self.1 >= -1.0 { 12 }
|
||||||
|
|
@ -22,7 +21,6 @@ render!(Tui: (self: Meter<'a>) => col!(
|
||||||
else { Color::Green }, ()))));
|
else { Color::Green }, ()))));
|
||||||
|
|
||||||
pub struct Meters<'a>(pub &'a[f32]);
|
pub struct Meters<'a>(pub &'a[f32]);
|
||||||
|
render!(TuiOut: (self: Meters<'a>) => col!(
|
||||||
render!(Tui: (self: Meters<'a>) => col!(
|
|
||||||
format!("L/{:>+9.3}", self.0[0]),
|
format!("L/{:>+9.3}", self.0[0]),
|
||||||
format!("R/{:>+9.3}", self.0[1])));
|
format!("R/{:>+9.3}", self.0[1])));
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ pub trait HasEditor {
|
||||||
/// Contains state for viewing and editing a phrase
|
/// Contains state for viewing and editing a phrase
|
||||||
pub struct MidiEditor {
|
pub struct MidiEditor {
|
||||||
pub mode: PianoHorizontal,
|
pub mode: PianoHorizontal,
|
||||||
pub size: Measure<Tui>
|
pub size: Measure<TuiOut>
|
||||||
}
|
}
|
||||||
|
|
||||||
from!(|phrase: &Arc<RwLock<MidiClip>>|MidiEditor = {
|
from!(|phrase: &Arc<RwLock<MidiClip>>|MidiEditor = {
|
||||||
|
|
@ -40,9 +40,9 @@ impl Default for MidiEditor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
has_size!(<Tui>|self: MidiEditor|&self.size);
|
has_size!(<TuiOut>|self: MidiEditor|&self.size);
|
||||||
|
|
||||||
render!(Tui: (self: MidiEditor) => {
|
render!(TuiOut: (self: MidiEditor) => {
|
||||||
self.autoscroll();
|
self.autoscroll();
|
||||||
self.autozoom();
|
self.autozoom();
|
||||||
Fill::xy(Bsp::b(&self.size, &self.mode))
|
Fill::xy(Bsp::b(&self.size, &self.mode))
|
||||||
|
|
@ -147,7 +147,7 @@ pub enum MidiEditCommand {
|
||||||
Show(Option<Arc<RwLock<MidiClip>>>),
|
Show(Option<Arc<RwLock<MidiClip>>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
handle!(<Tui>|self: MidiEditor, input|MidiEditCommand::execute_with_state(self, input.event()));
|
handle!(TuiIn: |self: MidiEditor, input|MidiEditCommand::execute_with_state(self, input.event()));
|
||||||
|
|
||||||
keymap!(KEYS_MIDI_EDITOR = |s: MidiEditor, _input: Event| MidiEditCommand {
|
keymap!(KEYS_MIDI_EDITOR = |s: MidiEditor, _input: Event| MidiEditCommand {
|
||||||
key(Up) => SetNoteCursor(s.note_point() + 1),
|
key(Up) => SetNoteCursor(s.note_point() + 1),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub struct MidiEditClip<'a>(pub &'a MidiEditor);
|
pub struct MidiEditClip<'a>(pub &'a MidiEditor);
|
||||||
render!(Tui: (self: MidiEditClip<'a>) => {
|
render!(TuiOut: (self: MidiEditClip<'a>) => {
|
||||||
let (color, name, length, looped) = if let Some(phrase) = self.0.phrase().as_ref().map(|p|p.read().unwrap()) {
|
let (color, name, length, looped) = if let Some(phrase) = self.0.phrase().as_ref().map(|p|p.read().unwrap()) {
|
||||||
(phrase.color, phrase.name.clone(), phrase.length, phrase.looped)
|
(phrase.color, phrase.name.clone(), phrase.length, phrase.looped)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -14,7 +14,7 @@ render!(Tui: (self: MidiEditClip<'a>) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
pub struct MidiEditStatus<'a>(pub &'a MidiEditor);
|
pub struct MidiEditStatus<'a>(pub &'a MidiEditor);
|
||||||
render!(Tui: (self: MidiEditStatus<'a>) => {
|
render!(TuiOut: (self: MidiEditStatus<'a>) => {
|
||||||
let (color, name, length, looped) = if let Some(phrase) = self.0.phrase().as_ref().map(|p|p.read().unwrap()) {
|
let (color, name, length, looped) = if let Some(phrase) = self.0.phrase().as_ref().map(|p|p.read().unwrap()) {
|
||||||
(phrase.color, phrase.name.clone(), phrase.length, phrase.looped)
|
(phrase.color, phrase.name.clone(), phrase.length, phrase.looped)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub trait MidiViewer: HasSize<Tui> + MidiRange + MidiPoint + Debug + Send + Sync {
|
pub trait MidiViewer: HasSize<TuiOut> + MidiRange + MidiPoint + Debug + Send + Sync {
|
||||||
fn buffer_size (&self, phrase: &MidiClip) -> (usize, usize);
|
fn buffer_size (&self, phrase: &MidiClip) -> (usize, usize);
|
||||||
fn redraw (&self);
|
fn redraw (&self);
|
||||||
fn phrase (&self) -> &Option<Arc<RwLock<MidiClip>>>;
|
fn phrase (&self) -> &Option<Arc<RwLock<MidiClip>>>;
|
||||||
|
|
|
||||||
14
src/mixer.rs
14
src/mixer.rs
|
|
@ -96,7 +96,7 @@ pub struct TrackView<'a> {
|
||||||
pub entered: bool,
|
pub entered: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Content<Tui> for TrackView<'a> {
|
impl<'a> Content<TuiOut> for TrackView<'a> {
|
||||||
fn render (&self, to: &mut TuiOut) {
|
fn render (&self, to: &mut TuiOut) {
|
||||||
todo!();
|
todo!();
|
||||||
//let mut area = to.area();
|
//let mut area = to.area();
|
||||||
|
|
@ -127,8 +127,8 @@ impl<'a> Content<Tui> for TrackView<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//impl Content<Tui> for Mixer<Tui> {
|
//impl Content<TuiOut> for Mixer<Tui> {
|
||||||
//fn content (&self) -> impl Content<Tui> {
|
//fn content (&self) -> impl Content<TuiOut> {
|
||||||
//Stack::right(|add| {
|
//Stack::right(|add| {
|
||||||
//for channel in self.tracks.iter() {
|
//for channel in self.tracks.iter() {
|
||||||
//add(channel)?;
|
//add(channel)?;
|
||||||
|
|
@ -138,8 +138,8 @@ impl<'a> Content<Tui> for TrackView<'a> {
|
||||||
//}
|
//}
|
||||||
//}
|
//}
|
||||||
|
|
||||||
//impl Content<Tui> for Track<Tui> {
|
//impl Content<TuiOut> for Track<Tui> {
|
||||||
//fn content (&self) -> impl Content<Tui> {
|
//fn content (&self) -> impl Content<TuiOut> {
|
||||||
//TrackView {
|
//TrackView {
|
||||||
//chain: Some(&self),
|
//chain: Some(&self),
|
||||||
//direction: tek_core::Direction::Right,
|
//direction: tek_core::Direction::Right,
|
||||||
|
|
@ -162,7 +162,7 @@ impl<'a> Content<Tui> for TrackView<'a> {
|
||||||
//}
|
//}
|
||||||
//}
|
//}
|
||||||
|
|
||||||
handle!(<Tui>|self: Mixer, engine|{
|
handle!(TuiIn: |self: Mixer, engine|{
|
||||||
if let crossterm::event::Event::Key(event) = engine.event() {
|
if let crossterm::event::Event::Key(event) = engine.event() {
|
||||||
|
|
||||||
match event.code {
|
match event.code {
|
||||||
|
|
@ -210,7 +210,7 @@ handle!(<Tui>|self: Mixer, engine|{
|
||||||
Ok(None)
|
Ok(None)
|
||||||
});
|
});
|
||||||
|
|
||||||
handle!(<Tui>|self:MixerTrack,from|{
|
handle!(TuiIn: |self:MixerTrack,from|{
|
||||||
match from.event() {
|
match from.event() {
|
||||||
//, NONE, "chain_cursor_up", "move cursor up", || {
|
//, NONE, "chain_cursor_up", "move cursor up", || {
|
||||||
kpat!(KeyCode::Up) => {
|
kpat!(KeyCode::Up) => {
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ pub struct PianoHorizontal {
|
||||||
/// Buffer where the whole phrase is rerendered on change
|
/// Buffer where the whole phrase is rerendered on change
|
||||||
buffer: Arc<RwLock<BigBuffer>>,
|
buffer: Arc<RwLock<BigBuffer>>,
|
||||||
/// Size of actual notes area
|
/// Size of actual notes area
|
||||||
size: Measure<Tui>,
|
size: Measure<TuiOut>,
|
||||||
/// The display window
|
/// The display window
|
||||||
range: MidiRangeModel,
|
range: MidiRangeModel,
|
||||||
/// The note cursor
|
/// The note cursor
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ pub(crate) fn note_y_iter (note_lo: usize, note_hi: usize, y0: u16) -> impl Iter
|
||||||
(note_lo..=note_hi).rev().enumerate().map(move|(y, n)|(y, y0 + y as u16, n))
|
(note_lo..=note_hi).rev().enumerate().map(move|(y, n)|(y, y0 + y as u16, n))
|
||||||
}
|
}
|
||||||
|
|
||||||
render!(Tui: (self: PianoHorizontal) => Bsp::s(
|
render!(TuiOut: (self: PianoHorizontal) => Bsp::s(
|
||||||
Fixed::y(1, Bsp::e(
|
Fixed::y(1, Bsp::e(
|
||||||
Fixed::x(self.keys_width, ""),
|
Fixed::x(self.keys_width, ""),
|
||||||
Fill::x(PianoHorizontalTimeline(self)),
|
Fill::x(PianoHorizontalTimeline(self)),
|
||||||
|
|
@ -81,7 +81,7 @@ impl PianoHorizontal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
has_size!(<Tui>|self:PianoHorizontal|&self.size);
|
has_size!(<TuiOut>|self:PianoHorizontal|&self.size);
|
||||||
|
|
||||||
impl TimeRange for PianoHorizontal {
|
impl TimeRange for PianoHorizontal {
|
||||||
fn time_len (&self) -> &AtomicUsize { self.range.time_len() }
|
fn time_len (&self) -> &AtomicUsize { self.range.time_len() }
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use crate::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub struct PianoHorizontalCursor<'a>(pub(crate) &'a PianoHorizontal);
|
pub struct PianoHorizontalCursor<'a>(pub(crate) &'a PianoHorizontal);
|
||||||
render!(Tui: |self: PianoHorizontalCursor<'a>, render|{
|
render!(TuiOut: |self: PianoHorizontalCursor<'a>, render|{
|
||||||
let style = Some(Style::default().fg(self.0.color.lightest.rgb));
|
let style = Some(Style::default().fg(self.0.color.lightest.rgb));
|
||||||
let note_hi = self.0.note_hi();
|
let note_hi = self.0.note_hi();
|
||||||
let note_len = self.0.note_len();
|
let note_len = self.0.note_len();
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use super::*;
|
||||||
|
|
||||||
pub struct PianoHorizontalKeys<'a>(pub(crate) &'a PianoHorizontal);
|
pub struct PianoHorizontalKeys<'a>(pub(crate) &'a PianoHorizontal);
|
||||||
|
|
||||||
render!(Tui: |self: PianoHorizontalKeys<'a>, to|{
|
render!(TuiOut: |self: PianoHorizontalKeys<'a>, to|{
|
||||||
let state = self.0;
|
let state = self.0;
|
||||||
let color = state.color;
|
let color = state.color;
|
||||||
let note_lo = state.note_lo().get();
|
let note_lo = state.note_lo().get();
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use super::*;
|
||||||
|
|
||||||
pub struct PianoHorizontalNotes<'a>(pub(crate) &'a PianoHorizontal);
|
pub struct PianoHorizontalNotes<'a>(pub(crate) &'a PianoHorizontal);
|
||||||
|
|
||||||
render!(Tui: |self: PianoHorizontalNotes<'a>, render|{
|
render!(TuiOut: |self: PianoHorizontalNotes<'a>, render|{
|
||||||
let time_start = self.0.time_start().get();
|
let time_start = self.0.time_start().get();
|
||||||
let note_axis = self.0.note_axis().get();
|
let note_axis = self.0.note_axis().get();
|
||||||
let note_lo = self.0.note_lo().get();
|
let note_lo = self.0.note_lo().get();
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use crate::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub struct PianoHorizontalTimeline<'a>(pub(crate) &'a PianoHorizontal);
|
pub struct PianoHorizontalTimeline<'a>(pub(crate) &'a PianoHorizontal);
|
||||||
render!(Tui: |self: PianoHorizontalTimeline<'a>, render|{
|
render!(TuiOut: |self: PianoHorizontalTimeline<'a>, render|{
|
||||||
let [x, y, w, h] = render.area();
|
let [x, y, w, h] = render.area();
|
||||||
let style = Some(Style::default().dim());
|
let style = Some(Style::default().dim());
|
||||||
let length = self.0.phrase.as_ref().map(|p|p.read().unwrap().length).unwrap_or(1);
|
let length = self.0.phrase.as_ref().map(|p|p.read().unwrap().length).unwrap_or(1);
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ impl Plugin {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Content<Tui> for Plugin {
|
impl Content<TuiOut> for Plugin {
|
||||||
fn render (&self, to: &mut TuiOut) {
|
fn render (&self, to: &mut TuiOut) {
|
||||||
let area = to.area();
|
let area = to.area();
|
||||||
let [x, y, _, height] = area;
|
let [x, y, _, height] = area;
|
||||||
|
|
@ -198,7 +198,7 @@ fn draw_header (state: &Plugin, to: &mut TuiOut, x: u16, y: u16, w: u16) {
|
||||||
//Ok(Rect { x, y, width: w, height: 1 })
|
//Ok(Rect { x, y, width: w, height: 1 })
|
||||||
}
|
}
|
||||||
|
|
||||||
handle!(<Tui>|self:Plugin, from|{
|
handle!(TuiIn: |self:Plugin, from|{
|
||||||
match from.event() {
|
match from.event() {
|
||||||
kpat!(KeyCode::Up) => {
|
kpat!(KeyCode::Up) => {
|
||||||
self.selected = self.selected.saturating_sub(1);
|
self.selected = self.selected.saturating_sub(1);
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ pub struct PoolModel {
|
||||||
/// Mode switch
|
/// Mode switch
|
||||||
pub(crate) mode: Option<PoolMode>,
|
pub(crate) mode: Option<PoolMode>,
|
||||||
/// Rendered size
|
/// Rendered size
|
||||||
size: Measure<Tui>,
|
size: Measure<TuiOut>,
|
||||||
/// Scroll offset
|
/// Scroll offset
|
||||||
scroll: usize,
|
scroll: usize,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ impl PhraseLengthFocus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render!(Tui: (self: PhraseLength) => {
|
render!(TuiOut: (self: PhraseLength) => {
|
||||||
let bars = ||self.bars_string();
|
let bars = ||self.bars_string();
|
||||||
let beats = ||self.beats_string();
|
let beats = ||self.beats_string();
|
||||||
let ticks = ||self.ticks_string();
|
let ticks = ||self.ticks_string();
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ pub struct ClipSelected {
|
||||||
pub(crate) time: String,
|
pub(crate) time: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
render!(Tui: (self: ClipSelected) =>
|
render!(TuiOut: (self: ClipSelected) =>
|
||||||
FieldV(self.color, self.title, format!("{} {}", self.time, self.name)));
|
FieldV(self.color, self.title, format!("{} {}", self.time, self.name)));
|
||||||
|
|
||||||
impl ClipSelected {
|
impl ClipSelected {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub struct PoolView<'a>(pub bool, pub &'a PoolModel);
|
pub struct PoolView<'a>(pub bool, pub &'a PoolModel);
|
||||||
render!(Tui: (self: PoolView<'a>) => {
|
render!(TuiOut: (self: PoolView<'a>) => {
|
||||||
let Self(compact, model) = self;
|
let Self(compact, model) = self;
|
||||||
let PoolModel { phrases, mode, .. } = self.1;
|
let PoolModel { phrases, mode, .. } = self.1;
|
||||||
let color = self.1.phrase().read().unwrap().color;
|
let color = self.1.phrase().read().unwrap().color;
|
||||||
|
|
|
||||||
|
|
@ -170,7 +170,7 @@ fn draw_sample (
|
||||||
Ok(label1.len() + label2.len() + 4)
|
Ok(label1.len() + label2.len() + 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Content<Tui> for AddSampleModal {
|
impl Content<TuiOut> for AddSampleModal {
|
||||||
fn render (&self, to: &mut TuiOut) {
|
fn render (&self, to: &mut TuiOut) {
|
||||||
todo!()
|
todo!()
|
||||||
//let area = to.area();
|
//let area = to.area();
|
||||||
|
|
@ -207,7 +207,7 @@ impl Content<Tui> for AddSampleModal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//impl Handle<Tui> for AddSampleModal {
|
//impl Handle<TuiIn> for AddSampleModal {
|
||||||
//fn handle (&mut self, from: &TuiIn) -> Perhaps<bool> {
|
//fn handle (&mut self, from: &TuiIn) -> Perhaps<bool> {
|
||||||
//if from.handle_keymap(self, KEYMAP_ADD_SAMPLE)? {
|
//if from.handle_keymap(self, KEYMAP_ADD_SAMPLE)? {
|
||||||
//return Ok(Some(true))
|
//return Ok(Some(true))
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ impl<'a> SampleList<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render!(Tui: (self: SampleList<'a>) => {
|
render!(TuiOut: (self: SampleList<'a>) => {
|
||||||
let Self { compact, sampler, editor } = self;
|
let Self { compact, sampler, editor } = self;
|
||||||
let note_lo = editor.note_lo().load(Relaxed);
|
let note_lo = editor.note_lo().load(Relaxed);
|
||||||
let note_pt = editor.note_point();
|
let note_pt = editor.note_point();
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ impl SampleViewer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render!(Tui: |self: SampleViewer, to|{
|
render!(TuiOut: |self: SampleViewer, to|{
|
||||||
|
|
||||||
let [x, y, width, height] = to.area();
|
let [x, y, width, height] = to.area();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
handle!(<Tui>|self: SamplerTui, input|SamplerTuiCommand::execute_with_state(self, input.event()));
|
handle!(TuiIn: |self: SamplerTui, input|SamplerTuiCommand::execute_with_state(self, input.event()));
|
||||||
|
|
||||||
pub enum SamplerTuiCommand {
|
pub enum SamplerTuiCommand {
|
||||||
Import(FileBrowserCommand),
|
Import(FileBrowserCommand),
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use crate::*;
|
||||||
|
|
||||||
pub struct SamplerStatus<'a>(pub &'a Sampler, pub usize);
|
pub struct SamplerStatus<'a>(pub &'a Sampler, pub usize);
|
||||||
|
|
||||||
render!(Tui: (self: SamplerStatus<'a>) => Tui::bold(true, Tui::fg(TuiTheme::g(224), self.0.mapped[self.1].as_ref().map(|sample|format!(
|
render!(TuiOut: (self: SamplerStatus<'a>) => Tui::bold(true, Tui::fg(TuiTheme::g(224), self.0.mapped[self.1].as_ref().map(|sample|format!(
|
||||||
"Sample {}-{}",
|
"Sample {}-{}",
|
||||||
sample.read().unwrap().start,
|
sample.read().unwrap().start,
|
||||||
sample.read().unwrap().end,
|
sample.read().unwrap().end,
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ pub struct SamplerTui {
|
||||||
pub editing: Option<Arc<RwLock<Sample>>>,
|
pub editing: Option<Arc<RwLock<Sample>>>,
|
||||||
pub mode: Option<SamplerMode>,
|
pub mode: Option<SamplerMode>,
|
||||||
/// Size of actual notes area
|
/// Size of actual notes area
|
||||||
pub size: Measure<Tui>,
|
pub size: Measure<TuiOut>,
|
||||||
/// Lowest note displayed
|
/// Lowest note displayed
|
||||||
pub note_lo: AtomicUsize,
|
pub note_lo: AtomicUsize,
|
||||||
pub note_pt: AtomicUsize,
|
pub note_pt: AtomicUsize,
|
||||||
|
|
@ -31,7 +31,7 @@ impl SamplerTui {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render!(Tui: (self: SamplerTui) => {
|
render!(TuiOut: (self: SamplerTui) => {
|
||||||
let keys_width = 5;
|
let keys_width = 5;
|
||||||
let keys = move||"";//SamplerKeys(self);
|
let keys = move||"";//SamplerKeys(self);
|
||||||
let fg = self.color.base.rgb;
|
let fg = self.color.base.rgb;
|
||||||
|
|
@ -59,7 +59,7 @@ struct SamplesTui {
|
||||||
note_pt: usize,
|
note_pt: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
}
|
}
|
||||||
render!(Tui: |self: SamplesTui, render|{
|
render!(TuiOut: |self: SamplesTui, render|{
|
||||||
let x = render.area.x();
|
let x = render.area.x();
|
||||||
let bg_base = self.color.darkest.rgb;
|
let bg_base = self.color.darkest.rgb;
|
||||||
let bg_selected = self.color.darker.rgb;
|
let bg_selected = self.color.darker.rgb;
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ pub struct SequencerTui {
|
||||||
pub compact: bool,
|
pub compact: bool,
|
||||||
|
|
||||||
pub clock: Clock,
|
pub clock: Clock,
|
||||||
pub size: Measure<Tui>,
|
pub size: Measure<TuiOut>,
|
||||||
pub status: bool,
|
pub status: bool,
|
||||||
pub note_buf: Vec<u8>,
|
pub note_buf: Vec<u8>,
|
||||||
pub midi_buf: Vec<Vec<Vec<u8>>>,
|
pub midi_buf: Vec<Vec<Vec<u8>>>,
|
||||||
|
|
@ -47,20 +47,20 @@ from_jack!(|jack|SequencerTui {
|
||||||
clock,
|
clock,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
render!(Tui: (self: SequencerTui) => self.size.of(
|
render!(TuiOut: (self: SequencerTui) => self.size.of(
|
||||||
Bsp::s(self.toolbar_view(),
|
Bsp::s(self.toolbar_view(),
|
||||||
Bsp::n(self.status_view(),
|
Bsp::n(self.status_view(),
|
||||||
Bsp::w(self.pool_view(), Fill::xy(&self.editor))))));
|
Bsp::w(self.pool_view(), Fill::xy(&self.editor))))));
|
||||||
impl SequencerTui {
|
impl SequencerTui {
|
||||||
fn toolbar_view (&self) -> impl Content<Tui> + use<'_> {
|
fn toolbar_view (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
self.transport.then(||TransportView::new(true, &self.clock))
|
self.transport.then(||TransportView::new(true, &self.clock))
|
||||||
}
|
}
|
||||||
fn status_view (&self) -> impl Content<Tui> + use<'_> {
|
fn status_view (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
let edit_clip = MidiEditClip(&self.editor);
|
let edit_clip = MidiEditClip(&self.editor);
|
||||||
let selectors = When(self.selectors, Bsp::e(ClipSelected::play_phrase(&self.player), ClipSelected::next_phrase(&self.player)));
|
let selectors = When(self.selectors, Bsp::e(ClipSelected::play_phrase(&self.player), ClipSelected::next_phrase(&self.player)));
|
||||||
row!(selectors, edit_clip, MidiEditStatus(&self.editor))
|
row!(selectors, edit_clip, MidiEditStatus(&self.editor))
|
||||||
}
|
}
|
||||||
fn pool_view (&self) -> impl Content<Tui> + use<'_> {
|
fn pool_view (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
let w = self.size.w();
|
let w = self.size.w();
|
||||||
let phrase_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 };
|
let phrase_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 };
|
||||||
let pool_w = if self.pool.visible { phrase_w } else { 0 };
|
let pool_w = if self.pool.visible { phrase_w } else { 0 };
|
||||||
|
|
@ -85,11 +85,11 @@ audio!(|self:SequencerTui, client, scope|{
|
||||||
self.perf.update(t0, scope);
|
self.perf.update(t0, scope);
|
||||||
Control::Continue
|
Control::Continue
|
||||||
});
|
});
|
||||||
has_size!(<Tui>|self:SequencerTui|&self.size);
|
has_size!(<TuiOut>|self:SequencerTui|&self.size);
|
||||||
has_clock!(|self:SequencerTui|&self.clock);
|
has_clock!(|self:SequencerTui|&self.clock);
|
||||||
has_phrases!(|self:SequencerTui|self.pool.phrases);
|
has_phrases!(|self:SequencerTui|self.pool.phrases);
|
||||||
has_editor!(|self:SequencerTui|self.editor);
|
has_editor!(|self:SequencerTui|self.editor);
|
||||||
handle!(<Tui>|self:SequencerTui,input|SequencerCommand::execute_with_state(self, input.event()));
|
handle!(TuiIn: |self:SequencerTui,input|SequencerCommand::execute_with_state(self, input.event()));
|
||||||
#[derive(Clone, Debug)] pub enum SequencerCommand {
|
#[derive(Clone, Debug)] pub enum SequencerCommand {
|
||||||
Compact(bool),
|
Compact(bool),
|
||||||
History(isize),
|
History(isize),
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,12 @@ from!(|state:&SequencerTui|SequencerStatus = {
|
||||||
size: format!("{}x{}│", width, state.size.h()),
|
size: format!("{}x{}│", width, state.size.h()),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
render!(Tui: (self: SequencerStatus) => Fixed::y(2, lay!(
|
render!(TuiOut: (self: SequencerStatus) => Fixed::y(2, lay!(
|
||||||
Self::help(),
|
Self::help(),
|
||||||
Fill::xy(Align::se(Tui::fg_bg(TuiTheme::orange(), TuiTheme::g(25), self.stats()))),
|
Fill::xy(Align::se(Tui::fg_bg(TuiTheme::orange(), TuiTheme::g(25), self.stats()))),
|
||||||
)));
|
)));
|
||||||
impl SequencerStatus {
|
impl SequencerStatus {
|
||||||
fn help () -> impl Content<Tui> {
|
fn help () -> impl Content<TuiOut> {
|
||||||
let single = |binding, command|row!(" ", col!(
|
let single = |binding, command|row!(" ", col!(
|
||||||
Tui::fg(TuiTheme::yellow(), binding),
|
Tui::fg(TuiTheme::yellow(), binding),
|
||||||
command
|
command
|
||||||
|
|
@ -44,7 +44,7 @@ impl SequencerStatus {
|
||||||
double(("c", "color"), ("", ""),),
|
double(("c", "color"), ("", ""),),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
fn stats (&self) -> impl Content<Tui> + use<'_> {
|
fn stats (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
row!(&self.cpu, &self.size)
|
row!(&self.cpu, &self.size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -69,12 +69,12 @@ from!(|state:&ArrangerTui|ArrangerStatus = {
|
||||||
size: format!("{}x{}│", width, state.size.h()),
|
size: format!("{}x{}│", width, state.size.h()),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
render!(Tui: (self: ArrangerStatus) => Fixed::y(2, lay!(
|
render!(TuiOut: (self: ArrangerStatus) => Fixed::y(2, lay!(
|
||||||
Self::help(),
|
Self::help(),
|
||||||
Fill::xy(Align::se(Tui::fg_bg(TuiTheme::orange(), TuiTheme::g(25), self.stats()))),
|
Fill::xy(Align::se(Tui::fg_bg(TuiTheme::orange(), TuiTheme::g(25), self.stats()))),
|
||||||
)));
|
)));
|
||||||
impl ArrangerStatus {
|
impl ArrangerStatus {
|
||||||
fn help () -> impl Content<Tui> {
|
fn help () -> impl Content<TuiOut> {
|
||||||
let single = |binding, command|row!(" ", col!(
|
let single = |binding, command|row!(" ", col!(
|
||||||
Tui::fg(TuiTheme::yellow(), binding),
|
Tui::fg(TuiTheme::yellow(), binding),
|
||||||
command
|
command
|
||||||
|
|
@ -95,7 +95,7 @@ impl ArrangerStatus {
|
||||||
double(("[]", "phrase"), ("{}", "order"),),
|
double(("[]", "phrase"), ("{}", "order"),),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
fn stats (&self) -> impl Content<Tui> + use<'_> {
|
fn stats (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
row!(&self.cpu, &self.size)
|
row!(&self.cpu, &self.size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,11 @@ pub use ::tek_edn;
|
||||||
pub(crate) use tek_layout::*;
|
pub(crate) use tek_layout::*;
|
||||||
pub(crate) use tek_engine::*;
|
pub(crate) use tek_engine::*;
|
||||||
|
|
||||||
mod tui_engine; pub use self::tui_engine::*;
|
mod tui_engine; pub use self::tui_engine::*;
|
||||||
mod tui_input; pub use self::tui_input::*;
|
mod tui_content; pub use self::tui_content::*;
|
||||||
mod tui_output; pub use self::tui_output::*;
|
mod tui_input; pub use self::tui_input::*;
|
||||||
mod tui_run; pub use self::tui_run::*;
|
mod tui_output; pub use self::tui_output::*;
|
||||||
|
mod tui_run; pub use self::tui_run::*;
|
||||||
|
|
||||||
mod tui_color; pub use self::tui_color::*;
|
mod tui_color; pub use self::tui_color::*;
|
||||||
mod tui_style; pub use self::tui_style::*;
|
mod tui_style; pub use self::tui_style::*;
|
||||||
|
|
@ -41,12 +42,12 @@ pub(crate) use ratatui::{
|
||||||
use crate::tui::*;
|
use crate::tui::*;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
struct TestComponent(String);
|
struct TestComponent(String);
|
||||||
impl Content<Tui> for TestComponent {
|
impl Content<TuiOut> for TestComponent {
|
||||||
fn content (&self) -> Option<impl Content<Tui>> {
|
fn content (&self) -> Option<impl Content<TuiOut>> {
|
||||||
Some(self.0.as_str())
|
Some(self.0.as_str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Handle<Tui> for TestComponent {
|
impl Handle<TuiIn> for TestComponent {
|
||||||
fn handle (&mut self, from: &TuiIn) -> Perhaps<bool> {
|
fn handle (&mut self, from: &TuiIn) -> Perhaps<bool> {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub struct Bordered<S: BorderStyle, W: Content<Tui>>(pub S, pub W);
|
pub struct Bordered<S: BorderStyle, W: Content<TuiOut>>(pub S, pub W);
|
||||||
|
|
||||||
render!(Tui: (self: Bordered<S: BorderStyle, W: Content<Tui>>) => {
|
render!(TuiOut: (self: Bordered<S: BorderStyle, W: Content<TuiOut>>) => {
|
||||||
Fill::xy(lay!(Border(self.0), Padding::xy(1, 1, &self.1)))
|
Fill::xy(lay!(Border(self.0), Padding::xy(1, 1, &self.1)))
|
||||||
});
|
});
|
||||||
|
|
||||||
pub struct Border<S: BorderStyle>(pub S);
|
pub struct Border<S: BorderStyle>(pub S);
|
||||||
|
|
||||||
render!(Tui: |self: Border<S: BorderStyle>, to| {
|
render!(TuiOut: |self: Border<S: BorderStyle>, to| {
|
||||||
let area = to.area();
|
let area = to.area();
|
||||||
if area.w() > 0 && area.y() > 0 {
|
if area.w() > 0 && area.y() > 0 {
|
||||||
to.blit(&self.0.nw(), area.x(), area.y(), self.0.style());
|
to.blit(&self.0.nw(), area.x(), area.y(), self.0.style());
|
||||||
|
|
@ -27,13 +27,13 @@ render!(Tui: |self: Border<S: BorderStyle>, to| {
|
||||||
});
|
});
|
||||||
|
|
||||||
pub trait BorderStyle: Send + Sync + Copy {
|
pub trait BorderStyle: Send + Sync + Copy {
|
||||||
fn wrap <W: Content<Tui>> (self, w: W) -> Bordered<Self, W> {
|
fn wrap <W: Content<TuiOut>> (self, w: W) -> Bordered<Self, W> {
|
||||||
Bordered(self, w)
|
Bordered(self, w)
|
||||||
}
|
}
|
||||||
fn enclose <W: Content<Tui>> (self, w: W) -> impl Content<Tui> {
|
fn enclose <W: Content<TuiOut>> (self, w: W) -> impl Content<TuiOut> {
|
||||||
lay!(Fill::xy(Border(self)), w)
|
lay!(Fill::xy(Border(self)), w)
|
||||||
}
|
}
|
||||||
fn enclose_bg <W: Content<Tui>> (self, w: W) -> impl Content<Tui> {
|
fn enclose_bg <W: Content<TuiOut>> (self, w: W) -> impl Content<TuiOut> {
|
||||||
Tui::bg(self.style().unwrap().bg.unwrap_or(Color::Reset), lay!(
|
Tui::bg(self.style().unwrap().bg.unwrap_or(Color::Reset), lay!(
|
||||||
Fill::xy(Border(self)),
|
Fill::xy(Border(self)),
|
||||||
w
|
w
|
||||||
|
|
@ -137,7 +137,7 @@ macro_rules! border {
|
||||||
}
|
}
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct $T(pub Style);
|
pub struct $T(pub Style);
|
||||||
impl Content<Tui> for $T {
|
impl Content<TuiOut> for $T {
|
||||||
fn render (&self, to: &mut TuiOut) { self.draw(to); }
|
fn render (&self, to: &mut TuiOut) { self.draw(to); }
|
||||||
}
|
}
|
||||||
)+}
|
)+}
|
||||||
|
|
|
||||||
19
tui/src/tui_content.rs
Normal file
19
tui/src/tui_content.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
impl Content<TuiOut> for &str {
|
||||||
|
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
||||||
|
to.center_xy([self.chars().count() as u16, 1])
|
||||||
|
}
|
||||||
|
fn render (&self, to: &mut TuiOut) {
|
||||||
|
to.blit(self, to.area.x(), to.area.y(), None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Content<TuiOut> for String {
|
||||||
|
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
||||||
|
to.center_xy([self.chars().count() as u16, 1])
|
||||||
|
}
|
||||||
|
fn render (&self, to: &mut TuiOut) {
|
||||||
|
to.blit(self, to.area.x(), to.area.y(), None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,12 +8,8 @@ pub struct Tui {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Engine for Tui {
|
impl Engine for Tui {
|
||||||
type Unit = u16;
|
type Input = TuiIn;
|
||||||
type Size = [Self::Unit;2];
|
type Output = TuiOut;
|
||||||
type Area = [Self::Unit;4];
|
|
||||||
type Input = TuiIn;
|
|
||||||
type Handled = bool;
|
|
||||||
type Output = TuiOut;
|
|
||||||
fn exited (&self) -> bool {
|
fn exited (&self) -> bool {
|
||||||
self.exited.fetch_and(true, Relaxed)
|
self.exited.fetch_and(true, Relaxed)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,9 @@ use Event as CrosstermEvent;
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TuiIn(pub Arc<AtomicBool>, pub CrosstermEvent);
|
pub struct TuiIn(pub Arc<AtomicBool>, pub CrosstermEvent);
|
||||||
|
|
||||||
impl Input<Tui> for TuiIn {
|
impl Input for TuiIn {
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
|
type Handled = bool;
|
||||||
fn event (&self) -> &CrosstermEvent { &self.1 }
|
fn event (&self) -> &CrosstermEvent { &self.1 }
|
||||||
fn is_done (&self) -> bool { self.0.fetch_and(true, Relaxed) }
|
fn is_done (&self) -> bool { self.0.fetch_and(true, Relaxed) }
|
||||||
fn done (&self) { self.0.store(true, Relaxed); }
|
fn done (&self) { self.0.store(true, Relaxed); }
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,13 @@ pub struct TuiOut {
|
||||||
pub area: [u16;4]
|
pub area: [u16;4]
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Output<Tui> for TuiOut {
|
impl Output for TuiOut {
|
||||||
|
type Unit = u16;
|
||||||
|
type Size = [Self::Unit;2];
|
||||||
|
type Area = [Self::Unit;4];
|
||||||
#[inline] fn area (&self) -> [u16;4] { self.area }
|
#[inline] fn area (&self) -> [u16;4] { self.area }
|
||||||
#[inline] fn area_mut (&mut self) -> &mut [u16;4] { &mut self.area }
|
#[inline] fn area_mut (&mut self) -> &mut [u16;4] { &mut self.area }
|
||||||
#[inline] fn place (&mut self, area: [u16;4], content: &impl Render<Tui>) {
|
#[inline] fn place (&mut self, area: [u16;4], content: &impl Render<TuiOut>) {
|
||||||
let last = self.area();
|
let last = self.area();
|
||||||
*self.area_mut() = area;
|
*self.area_mut() = area;
|
||||||
content.render(self);
|
content.render(self);
|
||||||
|
|
@ -65,24 +68,6 @@ impl TuiOut {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Content<Tui> for &str {
|
|
||||||
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
|
||||||
to.center_xy([self.chars().count() as u16, 1])
|
|
||||||
}
|
|
||||||
fn render (&self, to: &mut TuiOut) {
|
|
||||||
to.blit(self, to.area.x(), to.area.y(), None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Content<Tui> for String {
|
|
||||||
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
|
||||||
to.center_xy([self.chars().count() as u16, 1])
|
|
||||||
}
|
|
||||||
fn render (&self, to: &mut TuiOut) {
|
|
||||||
to.blit(self, to.area.x(), to.area.y(), None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn buffer_update (buf: &mut Buffer, area: [u16;4], callback: &impl Fn(&mut Cell, u16, u16)) {
|
pub fn buffer_update (buf: &mut Buffer, area: [u16;4], callback: &impl Fn(&mut Cell, u16, u16)) {
|
||||||
for row in 0..area.h() {
|
for row in 0..area.h() {
|
||||||
let y = area.y() + row;
|
let y = area.y() + row;
|
||||||
|
|
@ -113,4 +98,4 @@ pub fn half_block (lower: bool, upper: bool) -> Option<char> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//impl<T: Content<Tui>> Render<Tui> for T {}
|
//impl<T: Content<TuiOut>> Render<TuiOut> for T {}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use ratatui::prelude::Size;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::thread::{spawn, JoinHandle};
|
use std::thread::{spawn, JoinHandle};
|
||||||
|
|
||||||
pub trait TuiRun<R: Render<Tui> + Handle<Tui> + Sized + 'static> {
|
pub trait TuiRun<R: Render<TuiOut> + Handle<TuiIn> + Sized + 'static> {
|
||||||
/// Run an app in the main loop.
|
/// Run an app in the main loop.
|
||||||
fn run (&self, state: &Arc<RwLock<R>>) -> Usually<()>;
|
fn run (&self, state: &Arc<RwLock<R>>) -> Usually<()>;
|
||||||
/// Spawn the input thread.
|
/// Spawn the input thread.
|
||||||
|
|
@ -12,13 +12,21 @@ pub trait TuiRun<R: Render<Tui> + Handle<Tui> + Sized + 'static> {
|
||||||
fn run_output (&self, state: &Arc<RwLock<R>>, sleep: Duration) -> JoinHandle<()>;
|
fn run_output (&self, state: &Arc<RwLock<R>>, sleep: Duration) -> JoinHandle<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Render<Tui> + Handle<Tui> + Sized + 'static> TuiRun<T> for Arc<RwLock<Tui>> {
|
impl<T: Render<TuiOut> + Handle<TuiIn> + Sized + 'static> TuiRun<T> for Arc<RwLock<Tui>> {
|
||||||
fn run (&self, state: &Arc<RwLock<T>>) -> Usually<()> {
|
fn run (&self, state: &Arc<RwLock<T>>) -> Usually<()> {
|
||||||
let _input_thread = self.run_input(state, Duration::from_millis(100));
|
let _input_thread = self.run_input(state, Duration::from_millis(100));
|
||||||
self.write().unwrap().setup()?;
|
self.write().unwrap().setup()?;
|
||||||
let render_thread = self.run_output(state, Duration::from_millis(10));
|
let render_thread = self.run_output(state, Duration::from_millis(10));
|
||||||
render_thread.join().expect("main thread failed");
|
match render_thread.join() {
|
||||||
self.write().unwrap().teardown()?;
|
Ok(result) => {
|
||||||
|
self.write().unwrap().teardown()?;
|
||||||
|
println!("\n\rRan successfully: {result:?}\n\r");
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
self.write().unwrap().teardown()?;
|
||||||
|
panic!("\n\rRender thread failed.\n\r")
|
||||||
|
},
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn run_input (&self, state: &Arc<RwLock<T>>, poll: Duration) -> JoinHandle<()> {
|
fn run_input (&self, state: &Arc<RwLock<T>>, poll: Duration) -> JoinHandle<()> {
|
||||||
|
|
|
||||||
|
|
@ -1,86 +1,84 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub trait TuiStyle {
|
pub trait TuiStyle {
|
||||||
fn fg <R: Content<Tui>> (color: Color, w: R) -> Foreground<R> {
|
fn fg <R: Content<TuiOut>> (color: Color, w: R) -> Foreground<R> {
|
||||||
Foreground(color, w)
|
Foreground(color, w)
|
||||||
}
|
}
|
||||||
fn bg <R: Content<Tui>> (color: Color, w: R) -> Background<R> {
|
fn bg <R: Content<TuiOut>> (color: Color, w: R) -> Background<R> {
|
||||||
Background(color, w)
|
Background(color, w)
|
||||||
}
|
}
|
||||||
fn fg_bg <R: Content<Tui>> (fg: Color, bg: Color, w: R) -> Background<Foreground<R>> {
|
fn fg_bg <R: Content<TuiOut>> (fg: Color, bg: Color, w: R) -> Background<Foreground<R>> {
|
||||||
Background(bg, Foreground(fg, w))
|
Background(bg, Foreground(fg, w))
|
||||||
}
|
}
|
||||||
fn bold <R: Content<Tui>> (on: bool, w: R) -> Bold<R> {
|
fn bold <R: Content<TuiOut>> (on: bool, w: R) -> Bold<R> {
|
||||||
Bold(on, w)
|
Bold(on, w)
|
||||||
}
|
}
|
||||||
fn border <R: Content<Tui>, S: BorderStyle> (style: S, w: R) -> Bordered<S, R> {
|
fn border <R: Content<TuiOut>, S: BorderStyle> (style: S, w: R) -> Bordered<S, R> {
|
||||||
Bordered(style, w)
|
Bordered(style, w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TuiStyle for Tui {}
|
impl TuiStyle for Tui {}
|
||||||
|
|
||||||
pub struct Bold<R: Content<Tui>>(pub bool, R);
|
pub struct Bold<R: Content<TuiOut>>(pub bool, R);
|
||||||
impl<R: Content<Tui>> Content<Tui> for Bold<R> {
|
impl<R: Content<TuiOut>> Content<TuiOut> for Bold<R> {
|
||||||
fn content (&self) -> impl Render<Tui> { &self.1 }
|
fn content (&self) -> impl Render<TuiOut> { &self.1 }
|
||||||
fn render (&self, to: &mut TuiOut) {
|
fn render (&self, to: &mut TuiOut) {
|
||||||
to.fill_bold(to.area(), self.0);
|
to.fill_bold(to.area(), self.0);
|
||||||
self.1.render(to)
|
self.1.render(to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Foreground<R: Content<Tui>>(pub Color, R);
|
pub struct Foreground<R: Content<TuiOut>>(pub Color, R);
|
||||||
impl<R: Content<Tui>> Content<Tui> for Foreground<R> {
|
impl<R: Content<TuiOut>> Content<TuiOut> for Foreground<R> {
|
||||||
fn content (&self) -> impl Render<Tui> { &self.1 }
|
fn content (&self) -> impl Render<TuiOut> { &self.1 }
|
||||||
fn render (&self, to: &mut TuiOut) {
|
fn render (&self, to: &mut TuiOut) {
|
||||||
to.fill_fg(to.area(), self.0);
|
to.fill_fg(to.area(), self.0);
|
||||||
self.1.render(to)
|
self.1.render(to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Background<R: Content<Tui>>(pub Color, R);
|
pub struct Background<R: Content<TuiOut>>(pub Color, R);
|
||||||
impl<R: Content<Tui>> Content<Tui> for Background<R> {
|
impl<R: Content<TuiOut>> Content<TuiOut> for Background<R> {
|
||||||
fn content (&self) -> impl Render<Tui> { &self.1 }
|
fn content (&self) -> impl Render<TuiOut> { &self.1 }
|
||||||
fn render (&self, to: &mut TuiOut) {
|
fn render (&self, to: &mut TuiOut) {
|
||||||
to.fill_bg(to.area(), self.0);
|
to.fill_bg(to.area(), self.0);
|
||||||
self.1.render(to)
|
self.1.render(to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Styled<R: Content<Tui>>(pub Option<Style>, pub R);
|
pub struct Styled<R: Content<TuiOut>>(pub Option<Style>, pub R);
|
||||||
impl Content<Tui> for Styled<&str> {
|
impl<R: Content<TuiOut>> Content<TuiOut> for Styled<R> {
|
||||||
fn content (&self) -> impl Render<Tui> { &self.1 }
|
fn content (&self) -> impl Render<TuiOut> { &self.1 }
|
||||||
fn render (&self, to: &mut TuiOut) {
|
fn render (&self, to: &mut TuiOut) {
|
||||||
// FIXME
|
to.place(self.content().layout(to.area()), &self.content());
|
||||||
let [x, y, ..] = to.area();
|
// TODO write style over area
|
||||||
//let [w, h] = self.min_size(to.area().wh())?.unwrap();
|
|
||||||
to.blit(&self.1, x, y, None)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//pub trait TuiStyle: Render<Tui> + Sized {
|
//pub trait TuiStyle: Render<TuiOut> + Sized {
|
||||||
//fn fg (self, color: Color) -> impl Render<Tui> {
|
//fn fg (self, color: Color) -> impl Render<TuiOut> {
|
||||||
//Layers::new(move |add|{ add(&Foreground(color))?; add(&self) })
|
//Layers::new(move |add|{ add(&Foreground(color))?; add(&self) })
|
||||||
//}
|
//}
|
||||||
//fn bg (self, color: Color) -> impl Render<Tui> {
|
//fn bg (self, color: Color) -> impl Render<TuiOut> {
|
||||||
//Layers::new(move |add|{ add(&Background(color))?; add(&self) })
|
//Layers::new(move |add|{ add(&Background(color))?; add(&self) })
|
||||||
//}
|
//}
|
||||||
//fn bold (self, on: bool) -> impl Render<Tui> {
|
//fn bold (self, on: bool) -> impl Render<TuiOut> {
|
||||||
//Layers::new(move |add|{ add(&Bold(on))?; add(&self) })
|
//Layers::new(move |add|{ add(&Bold(on))?; add(&self) })
|
||||||
//}
|
//}
|
||||||
//fn border <S: BorderStyle> (self, style: S) -> impl Render<Tui> {
|
//fn border <S: BorderStyle> (self, style: S) -> impl Render<TuiOut> {
|
||||||
//Bordered(style, self)
|
//Bordered(style, self)
|
||||||
//}
|
//}
|
||||||
//}
|
//}
|
||||||
|
|
||||||
//impl<R: Content<Tui>> TuiStyle for R {}
|
//impl<R: Content<TuiOut>> TuiStyle for R {}
|
||||||
|
|
||||||
//impl<S: BorderStyle> Content<Tui> for Border<S> {
|
//impl<S: BorderStyle> Content<TuiOut> for Border<S> {
|
||||||
//}
|
//}
|
||||||
|
|
||||||
//impl<S: BorderStyle, R: Content<Tui>> Content<Tui> for Bordered<S, R> {
|
//impl<S: BorderStyle, R: Content<TuiOut>> Content<TuiOut> for Bordered<S, R> {
|
||||||
//fn content (&self) -> impl Render<Tui> {
|
//fn content (&self) -> impl Render<TuiOut> {
|
||||||
//let content: &dyn Content<Tui> = &self.1;
|
//let content: &dyn Content<TuiOut> = &self.1;
|
||||||
//lay! { content.padding_xy(1, 1), Border(self.0) }.fill_xy()
|
//lay! { content.padding_xy(1, 1), Border(self.0) }.fill_xy()
|
||||||
//}
|
//}
|
||||||
//}
|
//}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue