mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
wip: Content and Widget traits
This commit is contained in:
parent
5fc7da3aca
commit
b944dd5f9e
13 changed files with 209 additions and 36 deletions
9
Cargo.lock
generated
9
Cargo.lock
generated
|
|
@ -2546,6 +2546,15 @@ dependencies = [
|
|||
"tek_proc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tek_test"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"tek_core",
|
||||
"tek_mixer",
|
||||
"tek_sequencer",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.61"
|
||||
|
|
|
|||
|
|
@ -4,4 +4,5 @@ members = [
|
|||
"crates/tek_core",
|
||||
"crates/tek_mixer",
|
||||
"crates/tek_sequencer",
|
||||
"crates/tek_test",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ pub trait Device<E: Engine>: Component<E> + Process {
|
|||
/// All things that implement the required traits can be treated as `Device`.
|
||||
impl<D, E: Engine> Device<E> for D where D: Component<E> + Process {}
|
||||
|
||||
impl<E: Engine> Render<E> for Box<dyn Device<E>> {
|
||||
impl<'a, E: Engine> Render<E> for Box<dyn Device<E> + 'a> {
|
||||
fn render (&self, to: &mut E) -> Perhaps<E::Rendered> {
|
||||
(**self).render(to)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ pub trait Engine: Sized {
|
|||
|
||||
/// Unit of distance.
|
||||
type Unit: Number;
|
||||
type Area: Area<Self::Unit> + From<[Self::Unit;4]>;
|
||||
type Area: Area<Self::Unit> + From<[Self::Unit;4]> + Debug;
|
||||
|
||||
type HandleInput;
|
||||
type Handled;
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ impl<'a, E: Engine> Collect<'a, E> for Collection<'a, E> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Layers<'a, E: Engine>(pub &'a [&'a dyn Render<E>]);
|
||||
pub struct Layers<'a, E: Engine>(pub &'a [&'a dyn Layout<E>]);
|
||||
|
||||
// this actually works, except for the type inference
|
||||
//pub struct Layers<'a, E: Engine + 'a, I: std::iter::IntoIterator<Item = &'a dyn Render<E>>>(
|
||||
|
|
|
|||
|
|
@ -15,3 +15,97 @@ pub trait ExitableComponent<E>: Exit + Component<E> where E: Engine {
|
|||
}
|
||||
|
||||
impl<E: Engine, C: Component<E> + Exit> ExitableComponent<E> for C {}
|
||||
|
||||
pub trait Widget {
|
||||
type Engine: Engine;
|
||||
fn layout (&self, to: <<Self as Widget>::Engine as Engine>::Area) ->
|
||||
Perhaps<<<Self as Widget>::Engine as Engine>::Area>;
|
||||
fn render (&self, to: &mut Self::Engine) ->
|
||||
Perhaps<<<Self as Widget>::Engine as Engine>::Area>;
|
||||
}
|
||||
impl<E: Engine> Widget for Box<dyn Widget<Engine = E>> {
|
||||
type Engine = E;
|
||||
fn layout (&self, to: E::Area) -> Perhaps<E::Area> {
|
||||
(**self).layout(to)
|
||||
}
|
||||
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
|
||||
(**self).render(to)
|
||||
}
|
||||
}
|
||||
impl<E: Engine> Widget for &dyn Widget<Engine = E> {
|
||||
type Engine = E;
|
||||
fn layout (&self, to: E::Area) -> Perhaps<E::Area> {
|
||||
(*self).layout(to)
|
||||
}
|
||||
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
|
||||
(*self).render(to)
|
||||
}
|
||||
}
|
||||
impl<E: Engine> Widget for &mut dyn Widget<Engine = E> {
|
||||
type Engine = E;
|
||||
fn layout (&self, to: E::Area) -> Perhaps<E::Area> {
|
||||
(**self).layout(to)
|
||||
}
|
||||
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
|
||||
(**self).render(to)
|
||||
}
|
||||
}
|
||||
impl<E: Engine, W: Widget<Engine = E>> Widget for Arc<W> {
|
||||
type Engine = E;
|
||||
fn layout (&self, to: E::Area) -> Perhaps<E::Area> {
|
||||
self.as_ref().layout(to)
|
||||
}
|
||||
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
|
||||
self.as_ref().render(to)
|
||||
}
|
||||
}
|
||||
impl<E: Engine, W: Widget<Engine = E>> Widget for Mutex<W> {
|
||||
type Engine = E;
|
||||
fn layout (&self, to: E::Area) -> Perhaps<E::Area> {
|
||||
self.lock().unwrap().layout(to)
|
||||
}
|
||||
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
|
||||
self.lock().unwrap().render(to)
|
||||
}
|
||||
}
|
||||
impl<E: Engine, W: Widget<Engine = E>> Widget for RwLock<W> {
|
||||
type Engine = E;
|
||||
fn layout (&self, to: E::Area) -> Perhaps<E::Area> {
|
||||
self.read().unwrap().layout(to)
|
||||
}
|
||||
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
|
||||
self.read().unwrap().render(to)
|
||||
}
|
||||
}
|
||||
impl<E: Engine, W: Widget<Engine = E>> Widget for Option<W> {
|
||||
type Engine = E;
|
||||
fn layout (&self, to: E::Area) -> Perhaps<E::Area> {
|
||||
Ok(self.as_ref().map(|widget|widget.layout(to)).transpose()?.flatten())
|
||||
}
|
||||
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
|
||||
Ok(self.as_ref().map(|widget|widget.render(to)).transpose()?.flatten())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Content {
|
||||
type Engine: Engine;
|
||||
fn content (&self) -> impl Widget<Engine = <Self as Content>::Engine>;
|
||||
}
|
||||
//impl<E> Content<E> for () where E: Engine {
|
||||
//fn content (&self) -> impl Widget<E> {
|
||||
//()
|
||||
//}
|
||||
//}
|
||||
impl<W> Widget for W where W: Content {
|
||||
type Engine = <Self as Content>::Engine;
|
||||
fn layout (&self, to: <<Self as Content>::Engine as Engine>::Area)
|
||||
-> Perhaps<<<Self as Content>::Engine as Engine>::Area>
|
||||
{
|
||||
self.content().layout(to)
|
||||
}
|
||||
fn render (&self, to: &mut <Self as Content>::Engine)
|
||||
-> Perhaps<<<Self as Content>::Engine as Engine>::Area>
|
||||
{
|
||||
self.content().render(to)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,11 +14,26 @@ pub fn center_box (area: [u16;4], w: u16, h: u16) -> [u16;4] {
|
|||
pub trait Layout<E: Engine>: Render<E> {
|
||||
fn layout (&self, area: E::Area) -> Perhaps<E::Area>;
|
||||
}
|
||||
impl<'a, E: Engine> Layout<E> for Box<dyn Layout<E> + 'a> {
|
||||
fn layout (&self, area: E::Area) -> Perhaps<E::Area> {
|
||||
(**self).layout(area)
|
||||
}
|
||||
}
|
||||
impl<'a, E: Engine> Layout<E> for Box<dyn Component<E> + 'a> {
|
||||
fn layout (&self, area: E::Area) -> Perhaps<E::Area> {
|
||||
(**self).layout(area)
|
||||
}
|
||||
}
|
||||
impl<E: Engine, T: Layout<E>> Layout<E> for &T {
|
||||
fn layout (&self, area: E::Area) -> Perhaps<E::Area> {
|
||||
(*self).layout(area)
|
||||
}
|
||||
}
|
||||
impl<E: Engine, T: Layout<E>> Layout<E> for &mut T {
|
||||
fn layout (&self, area: E::Area) -> Perhaps<E::Area> {
|
||||
(**self).layout(area)
|
||||
}
|
||||
}
|
||||
impl<E: Engine, T: Layout<E>> Layout<E> for Option<T> {
|
||||
fn layout (&self, area: E::Area) -> Perhaps<E::Area> {
|
||||
match self {
|
||||
|
|
@ -127,6 +142,7 @@ impl<E: Engine, L: Layout<E>> Layout<E> for Offset<E::Unit, L> {
|
|||
|
||||
impl<E: Engine, R: Render<E> + Layout<E>> Render<E> for Min<E::Unit, R> {
|
||||
fn render (&self, to: &mut E) -> Perhaps<E::Rendered> {
|
||||
// 🡘 🡙 ←🡙→
|
||||
self.layout(to.area())?
|
||||
.map(|area|to.with_area(area.x(), area.y(), area.w(), area.h()))
|
||||
.map(|to|match self {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,16 @@ impl<'a, E: Engine> Render<E> for Box<dyn Render<E> + 'a> {
|
|||
(**self).render(to)
|
||||
}
|
||||
}
|
||||
impl<'a, E: Engine> Render<E> for Box<dyn Layout<E> + 'a> {
|
||||
fn render (&self, to: &mut E) -> Perhaps<E::Rendered> {
|
||||
(**self).render(to)
|
||||
}
|
||||
}
|
||||
impl<'a, E: Engine> Render<E> for Box<dyn Component<E> + 'a> {
|
||||
fn render (&self, to: &mut E) -> Perhaps<E::Rendered> {
|
||||
(**self).render(to)
|
||||
}
|
||||
}
|
||||
|
||||
/// Immutable references can be rendered.
|
||||
impl<R, E: Engine> Render<E> for &R where R: Render<E> {
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ impl Tui {
|
|||
let _input_thread = {
|
||||
let engine = engine.clone();
|
||||
let state = state.clone();
|
||||
let poll = Duration::from_millis(100);
|
||||
let poll = Duration::from_millis(20);
|
||||
spawn(move || loop {
|
||||
if ::crossterm::event::poll(poll).is_ok() {
|
||||
let event = TuiEvent::Input(::crossterm::event::read().unwrap());
|
||||
|
|
|
|||
|
|
@ -98,6 +98,11 @@ macro_rules! border {
|
|||
const SE: &'static str = $se;
|
||||
$($x)*
|
||||
}
|
||||
impl Layout<Tui> for $T {
|
||||
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
|
||||
Ok(Some(area))
|
||||
}
|
||||
}
|
||||
impl Render<Tui> for $T {
|
||||
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
|
||||
self.draw(to)
|
||||
|
|
|
|||
|
|
@ -33,9 +33,14 @@ impl Render<Tui> for Styled<&str> {
|
|||
|
||||
pub struct FillBg(pub Color);
|
||||
|
||||
impl Layout<Tui> for FillBg {
|
||||
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
|
||||
Ok(Some(area))
|
||||
}
|
||||
}
|
||||
impl Render<Tui> for FillBg {
|
||||
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
|
||||
to.fill_bg(to.area, self.0);
|
||||
to.fill_bg(to.area(), self.0);
|
||||
Ok(Some(to.area))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,29 @@
|
|||
use crate::*;
|
||||
|
||||
impl<'a> Layout<Tui> for Layers<'a, Tui> {
|
||||
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
|
||||
let [x, y, ..] = area;
|
||||
let mut w = 0;
|
||||
let mut h = 0;
|
||||
for layer in self.0.iter() {
|
||||
if let Some(layer_area) = layer.layout(area)? {
|
||||
w = w.max(layer_area.w());
|
||||
h = h.max(layer_area.h());
|
||||
}
|
||||
}
|
||||
Ok(Some([x, y, w, h]))
|
||||
}
|
||||
}
|
||||
impl<'a> Render<Tui> for Layers<'a, Tui> {
|
||||
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
|
||||
let area = to.area();
|
||||
for layer in self.0.iter() {
|
||||
layer.render(to)?;
|
||||
}
|
||||
Ok(Some(area))
|
||||
self.layout(to.area())?
|
||||
.map(|area| {
|
||||
for layer in self.0.iter() {
|
||||
layer.render(to.with_rect(area))?;
|
||||
}
|
||||
Ok(area)
|
||||
})
|
||||
.transpose()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -20,18 +37,16 @@ impl<'a> Render<Tui> for Layered<'a, Tui> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Render<Tui> for Split<'a, Tui> {
|
||||
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
|
||||
Ok(Some(self.render_areas(to)?.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Layout<Tui> for Split<'a, Tui> {
|
||||
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Render<Tui> for Split<'a, Tui> {
|
||||
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
|
||||
Ok(Some(self.render_areas(to)?.0))
|
||||
}
|
||||
}
|
||||
// TODO
|
||||
impl<'a> Split<'a, Tui> {
|
||||
pub fn render_areas (&self, to: &mut Tui) -> Usually<([u16;4], Vec<Option<[u16;4]>>)> {
|
||||
|
|
@ -73,7 +88,7 @@ impl<'a> Split<'a, Tui> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<L: Layout<Tui>> Layout<Tui> for Align<L> where Self: Render<Tui> {
|
||||
impl<T: Render<Tui> + Layout<Tui>> Layout<Tui> for Align<T> {
|
||||
fn layout (&self, outer_area: [u16;4]) -> Perhaps<[u16;4]> {
|
||||
Ok(match self {
|
||||
Self::Center(inner) => inner,
|
||||
|
|
@ -106,7 +121,7 @@ impl<L: Layout<Tui>> Layout<Tui> for Align<L> where Self: Render<Tui> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Render<Tui> + Layout<Tui>> Render<Tui> for Align<R> {
|
||||
impl<T: Render<Tui> + Layout<Tui>> Render<Tui> for Align<T> {
|
||||
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
|
||||
self.layout(to.area())?
|
||||
.map(|area|to.with_area(area.x(), area.y(), area.w(), area.h()))
|
||||
|
|
|
|||
|
|
@ -1,23 +1,53 @@
|
|||
use tek_core::*;
|
||||
use tek_core::jack::*;
|
||||
|
||||
pub fn main () -> Usually<()> {
|
||||
Tui::run(Arc::new(RwLock::new(Demo::new())))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub struct Demo {
|
||||
pub struct Demo<E: Engine> {
|
||||
index: usize,
|
||||
items: Vec<Box<dyn Component<Tui>>>
|
||||
items: Vec<Box<dyn Component<E>>>
|
||||
}
|
||||
|
||||
impl Demo {
|
||||
impl Demo<Tui> {
|
||||
fn new () -> Self {
|
||||
let items = vec![];
|
||||
let mut items: Vec<Box<dyn Component<Tui>>> = vec![];
|
||||
items.push(Box::new(tek_sequencer::TransportPlayPauseButton {
|
||||
value: Some(TransportState::Stopped),
|
||||
focused: true
|
||||
}));
|
||||
items.push(Box::new(tek_sequencer::TransportPlayPauseButton {
|
||||
value: Some(TransportState::Rolling),
|
||||
focused: false
|
||||
}));
|
||||
Self { index: 0, items }
|
||||
}
|
||||
fn content <'a> (&'a self) -> impl Layout<Tui> + 'a {
|
||||
Align::Center(Layers(&[
|
||||
&Outset::WH(2, 1, FillBg(Color::Rgb(0,128,128))),
|
||||
&Layers(&[
|
||||
&"---------",
|
||||
&Align::Center("...")
|
||||
])
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
||||
impl Handle<Tui> for Demo {
|
||||
impl Layout<Tui> for Demo<Tui> {
|
||||
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
|
||||
self.content().layout(area)
|
||||
}
|
||||
}
|
||||
|
||||
impl Render<Tui> for Demo<Tui> {
|
||||
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
|
||||
self.content().render(to)
|
||||
}
|
||||
}
|
||||
|
||||
impl Handle<Tui> for Demo<Tui> {
|
||||
fn handle (&mut self, from: &Tui) -> Perhaps<bool> {
|
||||
match from.event() {
|
||||
key!(KeyCode::PageUp) => {
|
||||
|
|
@ -36,15 +66,3 @@ impl Handle<Tui> for Demo {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Layout<Tui> for Demo {
|
||||
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
|
||||
Align::Center(self.items[self.index]).layout(area)
|
||||
}
|
||||
}
|
||||
|
||||
impl Render<Tui> for Demo {
|
||||
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
|
||||
Align::Center(self.items[self.index]).render(to)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue