wip: 21 errors!

This commit is contained in:
🪞👃🪞 2024-09-05 17:38:32 +03:00
parent 694970bf0d
commit ea5bc2e3b1
30 changed files with 392 additions and 362 deletions

View file

@ -6,7 +6,7 @@ pub enum Collected<'a, E: Engine> {
}
impl<'a, E: Engine> Render<E> for Collected<'a, E> {
fn render (&self, to: &mut E) -> Perhaps<E::Rendered> {
fn render (&self, to: &mut E::RenderInput) -> Perhaps<E::Rendered> {
match self {
Self::Box(item) => (*item).render(to),
Self::Ref(item) => (*item).render(to),

View file

@ -6,12 +6,25 @@ pub trait App<T: Engine> {
}
/// Platform backend.
pub trait Engine {
type Handled;
type Rendered;
fn setup (&mut self) -> Usually<()> { Ok(()) }
pub trait Engine: Sized {
fn setup (&mut self) -> Usually<()> { Ok(()) }
fn exited (&self) -> bool;
fn teardown (&mut self) -> Usually<()> { Ok(()) }
fn handle (&self, _: &mut impl Handle<Self>) -> Usually<()> where Self: Sized;
fn render (&mut self, _: &impl Render<Self>) -> Usually<()> where Self: Sized;
fn exited (&self) -> bool;
type HandleInput;
fn handle (&self, _: &mut impl Handle<Self>) -> Usually<()> where Self: Sized;
type Handled;
type RenderInput;
fn render (&mut self, _: &impl Render<Self>) -> Usually<()> where Self: Sized;
type Rendered;
}
pub trait HandleContext {}
pub trait RenderContext {}
submod! {
render
handle
}

View file

@ -0,0 +1,46 @@
use crate::*;
/// Handle input
pub trait Handle<E: Engine>: Send + Sync {
fn handle (&mut self, context: &E::HandleInput) -> Perhaps<E::Handled>;
}
impl<H, E: Engine> Handle<E> for &mut H where H: Handle<E> {
fn handle (&mut self, context: &E::HandleInput) -> Perhaps<E::Handled> {
(*self).handle(context)
}
}
impl<H, E: Engine> Handle<E> for Option<H> where H: Handle<E> {
fn handle (&mut self, context: &E::HandleInput) -> 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::HandleInput) -> Perhaps<E::Handled> {
self.lock().unwrap().handle(context)
}
}
impl<H, E: Engine> Handle<E> for Arc<Mutex<H>> where H: Handle<E> {
fn handle (&mut self, context: &E::HandleInput) -> 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::HandleInput) -> 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::HandleInput) -> Perhaps<E::Handled> {
self.write().unwrap().handle(context)
}
}

View file

@ -0,0 +1,69 @@
use crate::*;
/// Render to output.
pub trait Render<E: Engine>: Send + Sync {
fn render (&self, to: &mut E::RenderInput) -> Perhaps<E::Rendered>;
}
/// Options can be rendered optionally.
impl<R, E: Engine> Render<E> for Option<R> where R: Render<E> {
fn render (&self, to: &mut E::RenderInput) -> Perhaps<E::Rendered> {
match self {
Some(component) => component.render(to),
None => Ok(None)
}
}
}
/// Boxed references can be rendered.
impl<'a, E: Engine> Render<E> for Box<dyn Render<E> + 'a> {
fn render (&self, to: &mut E::RenderInput) -> Perhaps<E::Rendered> {
(**self).render(to)
}
}
/// Immutable references can be rendered.
impl<R, E: Engine> Render<E> for &R where R: Render<E> {
fn render (&self, to: &mut E::RenderInput) -> Perhaps<E::Rendered> {
(*self).render(to)
}
}
/// Mutable references can be rendered.
impl<R, E: Engine> Render<E> for &mut R where R: Render<E> {
fn render (&self, to: &mut E::RenderInput) -> Perhaps<E::Rendered> {
(**self).render(to)
}
}
/// Counted references can be rendered.
impl<R, E: Engine> Render<E> for Arc<R> where R: Render<E> {
fn render (&self, to: &mut E::RenderInput) -> Perhaps<E::Rendered> {
self.as_ref().render(to)
}
}
/// References behind a [Mutex] can be rendered.
impl<R, E: Engine> Render<E> for Mutex<R> where R: Render<E> {
fn render (&self, to: &mut E::RenderInput) -> Perhaps<E::Rendered> {
self.lock().unwrap().render(to)
}
}
/// References behind a [RwLock] can be rendered.
impl<R, E: Engine> Render<E> for RwLock<R> where R: Render<E> {
fn render (&self, to: &mut E::RenderInput) -> Perhaps<E::Rendered> {
self.read().unwrap().render(to)
}
}
/// Boxed closures can be rendered.
///
/// Rendering unboxed closures should also be possible;
/// but in practice implementing the trait for an unboxed
/// `Fn` closure causes an impl conflict.
impl<'a, E: Engine> Render<E> for Box<dyn Fn(&mut E::RenderInput) -> Perhaps<E::Rendered> + Send + Sync + 'a> {
fn render (&self, to: &mut E::RenderInput) -> Perhaps<E::Rendered> {
(*self)(to)
}
}

View file

@ -1,16 +0,0 @@
#[derive(Debug)]
pub enum AppEvent {
/// Terminal input
Input(::crossterm::event::Event),
/// Update values but not the whole form.
Update,
/// Update the whole form.
Redraw,
/// Device gains focus
Focus,
/// Device loses focus
Blur,
// /// JACK notification
// Jack(JackEvent)
}

View file

@ -1,46 +1 @@
use crate::*;
/// Handle input
pub trait Handle<E: Engine>: Send + Sync {
fn handle (&mut self, context: &E) -> Perhaps<E::Handled>;
}
impl<H, E: Engine> Handle<E> for &mut H where H: Handle<E> {
fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
(*self).handle(context)
}
}
impl<H, E: Engine> Handle<E> for Option<H> where H: Handle<E> {
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: Engine> Handle<E> for Mutex<H> where H: Handle<E> {
fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
self.lock().unwrap().handle(context)
}
}
impl<H, E: Engine> 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: Engine> 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: Engine> Handle<E> for Arc<RwLock<H>> where H: Handle<E> {
fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
self.write().unwrap().handle(context)
}
}

View file

@ -12,7 +12,7 @@ pub trait Device<E: Engine>: Component<E> + Process {
impl<D, E: Engine> Device<E> for D where D: Component<E> + Process {}
impl<E: Engine> Render<E> for Box<dyn Device<E>> {
fn render (&self, to: &mut E) -> Perhaps<E::Rendered> {
fn render (&self, to: &mut E::RenderInput) -> Perhaps<E::Rendered> {
(**self).render(to)
}
}

View file

@ -16,12 +16,12 @@ impl<E: Engine> std::fmt::Debug for JackDevice<E> {
}
}
impl<E: Engine> Render<E> for JackDevice<E> {
fn render (&self, to: &mut E) -> Perhaps<E::Rendered> {
fn render (&self, to: &mut E::RenderInput) -> Perhaps<E::Rendered> {
self.state.read().unwrap().render(to)
}
}
impl<E: Engine> Handle<E> for JackDevice<E> {
fn handle (&mut self, from: &E) -> Perhaps<E::Handled> {
fn handle (&mut self, from: &E::HandleInput) -> Perhaps<E::Handled> {
self.state.write().unwrap().handle(from)
}
}

View file

@ -9,10 +9,10 @@ pub type KeyBinding<T> = (
pub type KeyMap<T> = [KeyBinding<T>];
pub fn handle_keymap <T> (
state: &mut T, event: &AppEvent, keymap: &KeyMap<T>,
state: &mut T, event: &TuiEvent, keymap: &KeyMap<T>,
) -> Usually<bool> {
match event {
AppEvent::Input(crossterm::event::Event::Key(event)) => {
TuiEvent::Input(crossterm::event::Event::Key(event)) => {
for (code, modifiers, _, _, command) in keymap.iter() {
if *code == event.code && modifiers.bits() == event.modifiers.bits() {
return command(state)

View file

@ -30,17 +30,14 @@ submod! {
component
edn
engine
event
exit
focus
handle
jack_core
jack_device
jack_event
jack_ports
keymap
space
render
time
tui
}

View file

@ -1,69 +1 @@
use crate::*;
/// Render to output.
pub trait Render<E: Engine>: Send + Sync {
fn render (&self, to: &mut E) -> Perhaps<E::Rendered>;
}
/// Options can be rendered optionally.
impl<R, E: Engine> Render<E> for Option<R> where R: Render<E> {
fn render (&self, to: &mut E) -> Perhaps<E::Rendered> {
match self {
Some(component) => component.render(to),
None => Ok(None)
}
}
}
/// Boxed references can be rendered.
impl<'a, E: Engine> Render<E> for Box<dyn Render<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> {
fn render (&self, to: &mut E) -> Perhaps<E::Rendered> {
(*self).render(to)
}
}
/// Mutable references can be rendered.
impl<R, E: Engine> Render<E> for &mut R where R: Render<E> {
fn render (&self, to: &mut E) -> Perhaps<E::Rendered> {
(**self).render(to)
}
}
/// Counted references can be rendered.
impl<R, E: Engine> Render<E> for Arc<R> where R: Render<E> {
fn render (&self, to: &mut E) -> Perhaps<E::Rendered> {
self.as_ref().render(to)
}
}
/// References behind a [Mutex] can be rendered.
impl<R, E: Engine> Render<E> for Mutex<R> where R: Render<E> {
fn render (&self, to: &mut E) -> Perhaps<E::Rendered> {
self.lock().unwrap().render(to)
}
}
/// References behind a [RwLock] can be rendered.
impl<R, E: Engine> Render<E> for RwLock<R> where R: Render<E> {
fn render (&self, to: &mut E) -> Perhaps<E::Rendered> {
self.read().unwrap().render(to)
}
}
/// Boxed closures can be rendered.
///
/// Rendering unboxed closures should also be possible;
/// but in practice implementing the trait for an unboxed
/// `Fn` closure causes an impl conflict.
impl<'a, E: Engine> Render<E> for Box<dyn Fn(&mut E) -> Perhaps<E::Rendered> + Send + Sync + 'a> {
fn render (&self, to: &mut E) -> Perhaps<E::Rendered> {
(*self)(to)
}
}

View file

@ -15,12 +15,15 @@ pub struct Tui {
buffer: usize,
buffers: [Buffer;2],
backend: CrosstermBackend<Stdout>,
event: RwLock<Option<TuiEvent>>,
area: Rect,
sleep: Duration,
poll: Duration,
}
impl Engine for Tui {
type HandleInput = Self;
type Handled = bool;
type RenderInput = Self;
type Rendered = Rect;
fn exited (&self) -> bool {
self.exited.fetch_and(true, Ordering::Relaxed)
@ -46,8 +49,12 @@ impl Engine for Tui {
code: KeyCode::Char('c'), modifiers: KeyModifiers::CONTROL, ..
}) = event {
self.exited.store(true, Ordering::Relaxed);
} else if let Err(e) = state.handle(self) {
panic!("{e}")
} else {
*self.event.write().unwrap() = Some(TuiEvent::Input(event));
let result = state.handle(self);
if let Err(e) = result {
panic!("{e}")
}
}
}
Ok(())
@ -69,6 +76,7 @@ impl Tui {
sleep: Duration::from_millis(20),
poll: Duration::from_millis(100),
exited: Arc::new(AtomicBool::new(false)),
event: RwLock::new(None),
buffer: 0,
buffers: [Buffer::empty(area), Buffer::empty(area)],
backend,
@ -102,6 +110,15 @@ impl Tui {
main_thread.join().expect("main thread failed");
Ok(state)
}
pub fn event (&self) -> TuiEvent {
self.event.read().unwrap().clone().unwrap()
}
pub fn area (&self) -> Rect {
self.area
}
pub fn buffer (&mut self) -> &mut Buffer {
&mut self.buffers[self.buffer]
}
fn flip (&mut self) {
let previous_buffer = &self.buffers[1 - self.buffer];
let current_buffer = &self.buffers[self.buffer];
@ -110,12 +127,6 @@ impl Tui {
self.buffers[1 - self.buffer].reset();
self.buffer = 1 - self.buffer;
}
pub fn area (&self) -> Rect {
self.area
}
pub fn buffer (&mut self) -> &mut Buffer {
&mut self.buffers[self.buffer]
}
pub fn buffer_update (&mut self, area: Rect, callback: &impl Fn(&mut Cell, u16, u16)) {
buffer_update(self.buffer(), area, callback)
}
@ -151,15 +162,40 @@ impl Tui {
}
Ok(Some(Rect { x, y, width: text.len() as u16, height: 1 }))
}
#[inline]
pub fn alter_area (
&mut self, alter: impl Fn(u16, u16, u16, u16)->(u16, u16, u16, u16)
) -> &mut Self {
let (x, y, width, height) = alter(
self.area.x, self.area.y, self.area.width, self.area.height
);
self.area = Rect { x, y, width, height };
self.with_area(x, y, width, height)
}
#[inline]
pub fn with_area (&mut self, x: u16, y: u16, w: u16, h: u16) -> &mut Self {
self.with_rect(Rect { x, y, width: w, height: h });
self
}
#[inline]
pub fn with_rect (&mut self, area: Rect) -> &mut Self {
self.area = area;
self
}
}
#[derive(Debug, Clone)]
pub enum TuiEvent {
/// Terminal input
Input(::crossterm::event::Event),
/// Update values but not the whole form.
Update,
/// Update the whole form.
Redraw,
/// Device gains focus
Focus,
/// Device loses focus
Blur,
// /// JACK notification
// Jack(JackEvent)
}
/// Rendering unit struct to Ratatui returns zero-sized [Rect] at render coordinates.