mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 04:06:45 +01:00
refactor engine and layout into input and output
This commit is contained in:
parent
f052891473
commit
4d0f98acd2
40 changed files with 104 additions and 109 deletions
45
input/src/command.rs
Normal file
45
input/src/command.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
use crate::*;
|
||||
pub trait Command<S>: Send + Sync + Sized {
|
||||
fn execute (self, state: &mut S) -> Perhaps<Self>;
|
||||
fn delegate <T> (self, state: &mut S, wrap: impl Fn(Self)->T) -> Perhaps<T> {
|
||||
Ok(self.execute(state)?.map(wrap))
|
||||
}
|
||||
}
|
||||
#[macro_export] macro_rules! input_to_command {
|
||||
(<$($l:lifetime),+> $Command:ty: |$state:ident:$State:ty, $input:ident:$Input:ty| $handler:expr) => {
|
||||
impl<$($l),+> InputToCommand<$Input, $State> for $Command {
|
||||
fn input_to_command ($state: &$State, $input: &$Input) -> Option<Self> {
|
||||
Some($handler)
|
||||
}
|
||||
}
|
||||
};
|
||||
($Command:ty: |$state:ident:$State:ty, $input:ident:$Input:ty| $handler:expr) => {
|
||||
impl InputToCommand<$Input, $State> for $Command {
|
||||
fn input_to_command ($state: &$State, $input: &$Input) -> Option<Self> {
|
||||
Some($handler)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait InputToCommand<I, S>: Command<S> + Sized {
|
||||
fn input_to_command (state: &S, input: &I) -> Option<Self>;
|
||||
fn execute_with_state (state: &mut S, input: &I) -> Perhaps<bool> {
|
||||
Ok(if let Some(command) = Self::input_to_command(state, input) {
|
||||
let _undo = command.execute(state)?;
|
||||
Some(true)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! command {
|
||||
($(<$($l:lifetime),+>)?|$self:ident:$Command:ty,$state:ident:$State:ty|$handler:expr) => {
|
||||
impl$(<$($l),+>)? Command<$State> for $Command {
|
||||
fn execute ($self, $state: &mut $State) -> Perhaps<Self> {
|
||||
Ok($handler)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
15
input/src/engine.rs
Normal file
15
input/src/engine.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
use crate::*;
|
||||
|
||||
/// 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);
|
||||
}
|
||||
43
input/src/event_map.rs
Normal file
43
input/src/event_map.rs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
use crate::*;
|
||||
|
||||
pub struct EventMap<'a, S, I: PartialEq, C> {
|
||||
pub bindings: &'a [(I, &'a dyn Fn(&S) -> Option<C>)],
|
||||
pub fallback: Option<&'a dyn Fn(&S, &I) -> Option<C>>
|
||||
}
|
||||
impl<'a, S, I: PartialEq, C> EventMap<'a, S, I, C> {
|
||||
pub fn handle (&self, state: &S, input: &I) -> Option<C> {
|
||||
for (binding, handler) in self.bindings.iter() {
|
||||
if input == binding {
|
||||
return handler(state)
|
||||
}
|
||||
}
|
||||
if let Some(fallback) = self.fallback {
|
||||
fallback(state, input)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! keymap {
|
||||
(
|
||||
$(<$lt:lifetime>)? $KEYS:ident = |$state:ident: $State:ty, $input:ident: $Input:ty| $Command:ty
|
||||
{ $($key:expr => $handler:expr),* $(,)? } $(,)?
|
||||
) => {
|
||||
pub const $KEYS: EventMap<'static, $State, $Input, $Command> = EventMap {
|
||||
fallback: None,
|
||||
bindings: &[ $(($key, &|$state|Some($handler)),)* ]
|
||||
};
|
||||
input_to_command!($(<$lt>)? $Command: |$state: $State, input: $Input|$KEYS.handle($state, input)?);
|
||||
};
|
||||
(
|
||||
$(<$lt:lifetime>)? $KEYS:ident = |$state:ident: $State:ty, $input:ident: $Input:ty| $Command:ty
|
||||
{ $($key:expr => $handler:expr),* $(,)? }, $default:expr
|
||||
) => {
|
||||
pub const $KEYS: EventMap<'static, $State, $Input, $Command> = EventMap {
|
||||
fallback: Some(&|$state, $input|Some($default)),
|
||||
bindings: &[ $(($key, &|$state|Some($handler)),)* ]
|
||||
};
|
||||
input_to_command!($(<$lt>)? $Command: |$state: $State, input: $Input|$KEYS.handle($state, input)?);
|
||||
};
|
||||
}
|
||||
61
input/src/handle.rs
Normal file
61
input/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)
|
||||
}
|
||||
}
|
||||
65
input/src/lib.rs
Normal file
65
input/src/lib.rs
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#![feature(associated_type_defaults)]
|
||||
|
||||
//mod component; pub use self::component::*;
|
||||
mod engine; pub use self::engine::*;
|
||||
mod handle; pub use self::handle::*;
|
||||
mod command; pub use self::command::*;
|
||||
mod event_map; pub use self::event_map::*;
|
||||
|
||||
pub(crate) use std::marker::PhantomData;
|
||||
pub(crate) use std::error::Error;
|
||||
|
||||
/// Standard result type.
|
||||
pub(crate) type Usually<T> = Result<T, Box<dyn Error>>;
|
||||
|
||||
/// Standard optional result type.
|
||||
pub(crate) type Perhaps<T> = Result<Option<T>, Box<dyn Error>>;
|
||||
|
||||
#[cfg(test)] #[test] fn test_dimensions () {
|
||||
assert_eq!(Area::center(&[10u16, 10, 20, 20]), [20, 20]);
|
||||
}
|
||||
|
||||
#[cfg(test)] #[test] fn test_stub_engine () -> Usually<()> {
|
||||
struct TestEngine(bool);
|
||||
struct TestInput(bool);
|
||||
struct TestOutput([u16;4]);
|
||||
enum TestEvent { Test1 }
|
||||
impl Engine for TestEngine {
|
||||
type Input = TestInput;
|
||||
type Handled = ();
|
||||
type Output = TestOutput;
|
||||
type Unit = u16;
|
||||
type Size = [u16;2];
|
||||
type Area = [u16;4];
|
||||
fn exited (&self) -> bool {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
impl Input<TestEngine> for TestInput {
|
||||
type Event = TestEvent;
|
||||
fn event (&self) -> &Self::Event {
|
||||
&TestEvent::Test1
|
||||
}
|
||||
fn is_done (&self) -> bool {
|
||||
self.0
|
||||
}
|
||||
fn done (&self) {}
|
||||
}
|
||||
impl Output<TestEngine> for TestOutput {
|
||||
fn area (&self) -> [u16;4] {
|
||||
self.0
|
||||
}
|
||||
fn area_mut (&mut self) -> &mut [u16;4] {
|
||||
&mut self.0
|
||||
}
|
||||
fn place (&mut self, _: [u16;4], _: &impl Render<TestEngine>) {
|
||||
()
|
||||
}
|
||||
}
|
||||
impl Content<TestEngine> for String {
|
||||
fn render (&self, to: &mut TestOutput) {
|
||||
to.area_mut().set_w(self.len() as u16);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue