diff --git a/Cargo.toml b/Cargo.toml index e491b53..934b268 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,10 +18,18 @@ members = [ ] [workspace.package] -version = "0.13.0" +version = "0.14.0" edition = "2024" [workspace.dependencies] +tengri = { path = "./tengri" } +tengri_core = { path = "./core" } +tengri_input = { path = "./input" } +tengri_output = { path = "./output" } +tengri_tui = { path = "./tui" } +tengri_dsl = { path = "./dsl" } +tengri_proc = { path = "./proc" } + anyhow = { version = "1.0" } atomic_float = { version = "1" } better-panic = { version = "0.3.0" } diff --git a/dsl/src/dsl.rs b/dsl/src/dsl.rs index d6cc366..a522258 100644 --- a/dsl/src/dsl.rs +++ b/dsl/src/dsl.rs @@ -366,7 +366,7 @@ pub trait DslNs<'t, T: 't>: 't { } } /// Resolve as literal if valid. - fn from_literal (&self, dsl: &D) -> Perhaps { + fn from_literal (&self, _: &D) -> Perhaps { Ok(None) } /// Resolve a symbol if known. diff --git a/input/Cargo.toml b/input/Cargo.toml index d3bf1ec..0c9218d 100644 --- a/input/Cargo.toml +++ b/input/Cargo.toml @@ -4,12 +4,11 @@ description = "UI metaframework, input layer." version = { workspace = true } edition = { workspace = true } -[features] -dsl = [ "tengri_dsl" ] +[lib] +path = "input.rs" [dependencies] tengri_core = { path = "../core" } -tengri_dsl = { optional = true, path = "../dsl" } [dev-dependencies] tengri_tui = { path = "../tui" } diff --git a/input/README.md b/input/README.md index 4261a22..2fa3ef8 100644 --- a/input/README.md +++ b/input/README.md @@ -1,16 +1,6 @@ -# `tengri_engine` +***tengri_input*** is where tengri's input handling is defined. -## rendering - -## input handling - -the **input thread** polls for keyboard events -and passes them onto the application's `Handle::handle` method. - -thus, for a type to be a valid application for engine `E`, -it must implement the trait `Handle`, which allows it -to respond to user input. - -this thread has write access to the application state, -and is responsible for mutating it in response to -user activity. +the following items are provided: +* `Input` trait, for defining for input sources +* `Handle` trait and `handle!` macro, for defining input handlers +* `Command` trait and the `command!` macro, for defining commands that inputs may result in diff --git a/input/input.rs b/input/input.rs new file mode 100644 index 0000000..2695a47 --- /dev/null +++ b/input/input.rs @@ -0,0 +1,77 @@ +#![feature(associated_type_defaults)] +#![feature(if_let_guard)] + +pub(crate) use tengri_core::*; + +#[cfg(test)] mod input_test; + +/// Event source +pub trait Input: Sized { + /// Type of input event + type Event; + /// Result of handling input + type Handled; // TODO: make this an Option> containing the undo + /// Currently handled event + fn event (&self) -> &Self::Event; + /// Whether component should exit + fn is_done (&self) -> bool; + /// Mark component as done + fn done (&self); +} + +flex_trait_mut!(Handle { + fn handle (&mut self, _input: &E) -> Perhaps { + Ok(None) + } +}); + +pub trait Command: Send + Sync + Sized { + fn execute (self, state: &mut S) -> Perhaps; + fn delegate (self, state: &mut S, wrap: impl Fn(Self)->T) -> Perhaps + where Self: Sized + { + Ok(self.execute(state)?.map(wrap)) + } +} + +impl> Command for Option { + fn execute (self, _: &mut S) -> Perhaps { + Ok(None) + } + fn delegate (self, _: &mut S, _: impl Fn(Self)->U) -> Perhaps + where Self: Sized + { + Ok(None) + } +} + +/// Implement [Command] for given `State` and `handler` +#[macro_export] macro_rules! command { + ($(<$($l:lifetime),+>)?|$self:ident:$Command:ty,$state:ident:$State:ty|$handler:expr) => { + impl$(<$($l),+>)? ::tengri::input::Command<$State> for $Command { + fn execute ($self, $state: &mut $State) -> Perhaps { + Ok($handler) + } + } + }; +} + +/// Implement [Handle] for given `State` and `handler`. +#[macro_export] macro_rules! handle { + (|$self:ident:$State:ty,$input:ident|$handler:expr) => { + impl ::tengri::input::Handle for $State { + fn handle (&mut $self, $input: &E) -> Perhaps { + $handler + } + } + }; + ($E:ty: |$self:ident:$State:ty,$input:ident|$handler:expr) => { + impl ::tengri::input::Handle<$E> for $State { + fn handle (&mut $self, $input: &$E) -> + Perhaps<<$E as ::tengri::input::Input>::Handled> + { + $handler + } + } + } +} diff --git a/input/src/input_test.rs b/input/input_test.rs similarity index 100% rename from input/src/input_test.rs rename to input/input_test.rs diff --git a/input/src/input.rs b/input/src/input.rs deleted file mode 100644 index 48e692f..0000000 --- a/input/src/input.rs +++ /dev/null @@ -1,37 +0,0 @@ -use crate::*; -/// Event source -pub trait Input: Sized { - /// Type of input event - type Event; - /// Result of handling input - type Handled; // TODO: make this an Option> containing the undo - /// Currently handled event - fn event (&self) -> &Self::Event; - /// Whether component should exit - fn is_done (&self) -> bool; - /// Mark component as done - fn done (&self); -} -flex_trait_mut!(Handle { - fn handle (&mut self, _input: &E) -> Perhaps { - Ok(None) - } -}); -pub trait Command: Send + Sync + Sized { - fn execute (self, state: &mut S) -> Perhaps; - fn delegate (self, state: &mut S, wrap: impl Fn(Self)->T) -> Perhaps - where Self: Sized - { - Ok(self.execute(state)?.map(wrap)) - } -} -impl> Command for Option { - fn execute (self, _: &mut S) -> Perhaps { - Ok(None) - } - fn delegate (self, _: &mut S, _: impl Fn(Self)->U) -> Perhaps - where Self: Sized - { - Ok(None) - } -} diff --git a/input/src/input_dsl.rs b/input/src/input_dsl.rs deleted file mode 100644 index 77c6c94..0000000 --- a/input/src/input_dsl.rs +++ /dev/null @@ -1,76 +0,0 @@ -use crate::*; -use std::{sync::Arc, collections::BTreeMap, path::PathBuf}; -/// A collection of input bindings. -/// -/// Each contained layer defines a mapping from input event to command invocation -/// over a given state. Furthermore, each layer may have an associated cond, -/// so that only certain layers are active at a given time depending on state. -/// -/// When a key is pressed, the bindings for it are checked in sequence. -/// When the first non-conditional or true conditional binding is executed, -/// that .event()binding's value is returned. -#[derive(Debug)] -pub struct EventMap( - /// Map of each event (e.g. key combination) to - /// all command expressions bound to it by - /// all loaded input layers. - pub BTreeMap>> -); -/// An input binding. -#[derive(Debug, Clone)] -pub struct Binding { - pub commands: Arc<[C]>, - pub condition: Option, - pub description: Option>, - pub source: Option>, -} -impl Binding { - pub fn from_dsl (dsl: impl Dsl) -> Usually { - let command: Option = None; - let condition: Option = None; - let description: Option> = None; - let source: Option> = None; - if let Some(command) = command { - Ok(Self { commands: [command].into(), condition, description, source }) - } else { - Err(format!("no command in {dsl:?}").into()) - } - } -} -/// Input bindings are only returned if this evaluates to true -#[derive(Clone)] -pub struct Condition(Arcbool + Send + Sync>>); -impl_debug!(Condition |self, w| { write!(w, "*") }); -/// Default is always empty map regardless if `E` and `C` implement [Default]. -impl Default for EventMap { - fn default () -> Self { Self(Default::default()) } -} -impl EventMap { - /// Create a new event map - pub fn new () -> Self { - Default::default() - } - /// Add a binding to an owned event map. - pub fn def (mut self, event: E, binding: Binding) -> Self { - self.add(event, binding); - self - } - /// Add a binding to an event map. - pub fn add (&mut self, event: E, binding: Binding) -> &mut Self { - if !self.0.contains_key(&event) { - self.0.insert(event.clone(), Default::default()); - } - self.0.get_mut(&event).unwrap().push(binding); - self - } - /// Return the binding(s) that correspond to an event. - pub fn query (&self, event: &E) -> Option<&[Binding]> { - self.0.get(event).map(|x|x.as_slice()) - } - /// Return the first binding that corresponds to an event, considering conditions. - pub fn dispatch (&self, event: &E) -> Option<&Binding> { - self.query(event) - .map(|bb|bb.iter().filter(|b|b.condition.as_ref().map(|c|(c.0)()).unwrap_or(true)).next()) - .flatten() - } -} diff --git a/input/src/input_macros.rs b/input/src/input_macros.rs deleted file mode 100644 index e47b902..0000000 --- a/input/src/input_macros.rs +++ /dev/null @@ -1,30 +0,0 @@ -/// Implement [Command] for given `State` and `handler` -#[macro_export] macro_rules! command { - ($(<$($l:lifetime),+>)?|$self:ident:$Command:ty,$state:ident:$State:ty|$handler:expr) => { - impl$(<$($l),+>)? ::tengri::input::Command<$State> for $Command { - fn execute ($self, $state: &mut $State) -> Perhaps { - Ok($handler) - } - } - }; -} - -/// Implement [Handle] for given `State` and `handler`. -#[macro_export] macro_rules! handle { - (|$self:ident:$State:ty,$input:ident|$handler:expr) => { - impl ::tengri::input::Handle for $State { - fn handle (&mut $self, $input: &E) -> Perhaps { - $handler - } - } - }; - ($E:ty: |$self:ident:$State:ty,$input:ident|$handler:expr) => { - impl ::tengri::input::Handle<$E> for $State { - fn handle (&mut $self, $input: &$E) -> - Perhaps<<$E as ::tengri::input::Input>::Handled> - { - $handler - } - } - } -} diff --git a/input/src/lib.rs b/input/src/lib.rs deleted file mode 100644 index 800ee2f..0000000 --- a/input/src/lib.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![feature(associated_type_defaults)] -#![feature(if_let_guard)] -pub(crate) use tengri_core::*; -mod input_macros; -mod input; pub use self::input::*; -#[cfg(feature = "dsl")] pub(crate) use ::tengri_dsl::*; -#[cfg(feature = "dsl")] mod input_dsl; -#[cfg(feature = "dsl")] pub use self::input_dsl::*; -#[cfg(test)] mod input_test; diff --git a/tengri/Cargo.toml b/tengri/Cargo.toml index cd87dfc..c14cdf8 100644 --- a/tengri/Cargo.toml +++ b/tengri/Cargo.toml @@ -9,10 +9,10 @@ default = [ "input", "output", "tui" ] input = [ "tengri_input" ] output = [ "tengri_output" ] tui = [ "tengri_tui" ] -dsl = [ "tengri_dsl", "tengri_input/dsl", "tengri_output/dsl", "tengri_tui/dsl" ] +dsl = [ "tengri_dsl", "tengri_output/dsl", "tengri_tui/dsl" ] [dependencies] -tengri_core = { path = "../core" } +tengri_core = { workspace = true } tengri_dsl = { optional = true, path = "../dsl" } tengri_input = { optional = true, path = "../input" } tengri_output = { optional = true, path = "../output" } diff --git a/tui/Cargo.toml b/tui/Cargo.toml index 5fe3f34..284ad75 100644 --- a/tui/Cargo.toml +++ b/tui/Cargo.toml @@ -5,13 +5,13 @@ version = { workspace = true } edition = { workspace = true } [features] -dsl = [ "tengri_dsl", "tengri_input/dsl", "tengri_output/dsl" ] +dsl = [ "tengri_dsl", "tengri_output/dsl" ] [dependencies] -tengri_core = { path = "../core" } -tengri_input = { path = "../input" } -tengri_output = { path = "../output" } -tengri_dsl = { optional = true, path = "../dsl" } +tengri_core = { workspace = true } +tengri_input = { workspace = true } +tengri_output = { workspace = true } +tengri_dsl = { workspace = true, optional = true } palette = { workspace = true } rand = { workspace = true } @@ -24,6 +24,6 @@ quanta = { workspace = true } unicode-width = { workspace = true } [dev-dependencies] -tengri = { path = "../tengri", features = [ "dsl" ] } -tengri_dsl = { path = "../dsl" } -tengri_proc = { path = "../proc" } +tengri = { workspace = true, features = [ "dsl" ] } +tengri_dsl = { workspace = true } +tengri_proc = { workspace = true }