diff --git a/.envrc b/.envrc deleted file mode 100644 index 1d953f4..0000000 --- a/.envrc +++ /dev/null @@ -1 +0,0 @@ -use nix diff --git a/.gitignore b/.gitignore index 4f90c40..8899d91 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -*.profraw -.direnv -cov target +cov +*.profraw diff --git a/Cargo.toml b/Cargo.toml index 8657a68..b0fd6fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ winit = { optional = true, version = "0.30.4", features = [ "x11" ]} [dev-dependencies] proptest = { version = "^1" } proptest-derive = { version = "^0.5.1" } -tengri = { path = "." } +tengri = { path = ".", features = [ "dsl" ] } #tengri_proc = { path = "./proc" } [profile.coverage] diff --git a/Justfile b/Justfile index 8d7c790..1d5f794 100644 --- a/Justfile +++ b/Justfile @@ -2,9 +2,8 @@ export LLVM_PROFILE_FILE := "cov/cargo-test-%p-%m.profraw" grcov-binary := "--binary-path ./target/coverage/deps/" grcov-ignore := "--ignore-not-existing --ignore '../*' --ignore \"/*\" --ignore 'target/*'" -[private] default: - @just -l + just -l bacon: bacon -s @@ -30,7 +29,6 @@ doc: cargo doc example-tui-00: - cargo run --example tui_00 - + cargo run -p tengri_tui --example tui_00 example-tui-01: - cargo run --example tui_01 + cargo run -p tengri_tui --example tui_01 diff --git a/examples/tui_00.rs b/examples/tui_00.rs index 929c56d..80e7acd 100644 --- a/examples/tui_00.rs +++ b/examples/tui_00.rs @@ -1,74 +1,46 @@ -use ::{ - std::{io::stdout, sync::{Arc, RwLock}}, - tengri::{*, term::*, lang::*, keys::*, draw::*, space::*}, - ratatui::style::Color, -}; - -tui_main!(State { cursor: 10, ..Default::default() }); - -impl Apply> for State { - fn apply (&mut self, input: &TuiEvent) -> Usually { - todo!() - } -} - -impl View for State { - fn view (&self) -> impl Draw { - let index = self.cursor + 1; - let wh = (self.size.w(), self.size.h()); - let src = VIEWS.get(self.cursor).unwrap_or(&""); - let heading = format!("State {}/{} in {:?}", index, VIEWS.len(), &wh); - let title = bg(Color::Rgb(60, 10, 10), push_y(1, align_n(heading))); - let code = bg(Color::Rgb(10, 60, 10), push_y(2, align_n(format!("{}", src)))); - //let content = ;//();//bg(Color::Rgb(10, 10, 60), View(self, CstIter::new(src))); - self.size.of(bsp_s(title, bsp_n(code, self.understand(to, &src).unwrap()))) - } -} - +use ::{std::{io::stdout, sync::{Arc, RwLock}}, ratatui::style::Color, tengri::*}; + tui_main!(State { cursor: 10, ..Default::default() }); +namespace!(State: bool {}); +namespace!(State: u16 {}); +namespace!(State: Color {}); + handle!(TuiIn: |self: State, input|Action::from(input).eval(self).map(|_|None)); + view!(State: TuiOut: [ evaluate_output_expression, evaluate_output_expression_tui ]); + draw!(State: TuiOut: [ draw_example ]); #[derive(Debug, Default)] struct State { - /** Command history (undo/redo). */ - history: Vec, - /** User-controllable value. */ - cursor: usize, - /** Rendered window size. */ - size: crate::space::Size, + /** Rendered window size */ size: Measure, + /** Command history (undo/redo) */ history: Vec, + /** User-controllable value */ cursor: usize, } - -//impl_from!(Action: |input: &TuiIn| todo!()); +impl_from!(Action: |input: &TuiIn| todo!()); #[derive(Debug)] enum Action { - /** Increment cursor */ - Next, - /** Decrement cursor */ - Prev + /** Increment cursor */ Next, + /** Decrement cursor */ Prev } - -fn draw_example (state: &State, to: &mut Tui) { +fn draw_example (state: &State, to: &mut TuiOut) { + let index = state.cursor + 1; + let wh = state.size.wh(); + let src = VIEWS.get(state.cursor).unwrap_or(&""); + let heading = format!("State {}/{} in {:?}", index, VIEWS.len(), &wh); + let title = Tui::bg(Color::Rgb(60, 10, 10), Push::Y(1, Align::n(heading))); + let code = Tui::bg(Color::Rgb(10, 60, 10), Push::Y(2, Align::n(format!("{}", src)))); + //let content = ;//();//Tui::bg(Color::Rgb(10, 10, 60), View(state, CstIter::new(src))); + state.size.of(Bsp::s(title, Bsp::n(code, state.understand(to, &src).unwrap()))).draw(to) } - impl Action { - - const BINDS: &'static str = stringify! { - (@left prev) - (@right next) - }; - + const BINDS: &'static str = stringify! { (@left prev) (@right next) }; fn eval (&self, state: &mut State) -> Perhaps { use Action::*; match self { Next => Self::next(state), Prev => Self::prev(state), } } - fn next (state: &mut State) -> Perhaps { state.cursor = (state.cursor + 1) % VIEWS.len(); Ok(Some(Self::Prev)) } - fn prev (state: &mut State) -> Perhaps { state.cursor = if state.cursor > 0 { state.cursor - 1 } else { VIEWS.len() - 1 }; Ok(Some(Self::Next)) } - } - const VIEWS: &'static [&'static str] = &[ stringify! { :hello-world }, @@ -149,10 +121,3 @@ const VIEWS: &'static [&'static str] = &[ }, ]; - -//namespace!(State: bool {}); -//namespace!(State: u16 {}); -//namespace!(State: Color {}); - //handle!(TuiIn: |self: State, input|Action::from(input).eval(self).map(|_|None)); - //view!(State: Tui: [ evaluate_output_expression, evaluate_output_expression_tui ]); - //draw!(State: Tui: [ draw_example ]); diff --git a/shell.nix b/shell.nix index f34dc1f..a4c3248 100644 --- a/shell.nix +++ b/shell.nix @@ -2,8 +2,8 @@ {pkgs?import{}}:let stdenv = pkgs.clang19Stdenv; name = "tengri"; - nativeBuildInputs = [ pkgs.pkg-config pkgs.clang pkgs.libclang pkgs.mold ]; - buildInputs = [ pkgs.libclang pkgs.jack2 ]; + nativeBuildInputs = [ pkgs.pkg-config pkgs.libclang pkgs.mold ]; + buildInputs = [ pkgs.libclang ]; LIBCLANG_PATH = "${pkgs.libclang.lib.outPath}/lib"; LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath []; in pkgs.mkShell.override { diff --git a/src/color.rs b/src/color.rs index f16358a..13caf71 100644 --- a/src/color.rs +++ b/src/color.rs @@ -11,10 +11,6 @@ pub fn rgb (r: u8, g: u8, b: u8) -> ItemColor { ItemColor { okhsl: rgb_to_okhsl(term), term } } -pub fn g (g: u8) -> Color { - Color::Rgb(g, g, g) -} - pub fn okhsl_to_rgb (color: Okhsl) -> Color { let Srgb { red, green, blue, .. }: Srgb = Srgb::from_color_unclamped(color); Color::Rgb((red * 255.0) as u8, (green * 255.0) as u8, (blue * 255.0) as u8,) diff --git a/src/draw.rs b/src/draw.rs index ad47922..9b8ebdb 100644 --- a/src/draw.rs +++ b/src/draw.rs @@ -7,10 +7,6 @@ use crate::{*, lang::*, color::*, space::*}; /// /// Drawables are consumable, i.e. the [Draw::draw] method receives an /// owned `self` and does not return it, consuming the drawable. -/// -/// To draw a thing multiple times, instead of explicitly constructing it -/// every time, implement the [View] trait instead, which will construct -/// a [Draw]able. /// /// ``` /// use tengri::draw::*; @@ -30,22 +26,17 @@ use crate::{*, lang::*, color::*, space::*}; pub trait Draw { fn draw (self, to: &mut T) -> Usually>; } -impl> Draw for &D { - fn draw (self, to: &mut T) -> Usually> { - todo!() - } -} -impl> Draw for Option { - fn draw (self, to: &mut T) -> Usually> { - todo!() + +impl Draw for () { + fn draw (self, __: &mut T) -> Usually> { + Ok(Default::default()) } } -/// Emit a [Draw]able. -/// -/// Speculative. How to avoid conflicts with [Draw] proper? -pub trait View { - fn view (&self) -> impl Draw; +impl> Draw for Option { + fn draw (self, to: &mut T) -> Usually> { + Ok(self.map(|draw|draw.draw(to)).transpose()?.unwrap_or_default()) + } } pub const fn thunk Usually>> (draw: F) -> Thunk { @@ -57,6 +48,7 @@ pub struct ThunkUsually>>( pub F, std::marker::PhantomData ); + implUsually>> Draw for Thunk { fn draw (self, to: &mut T) -> Usually> { (self.0)(to) @@ -119,3 +111,14 @@ pub trait Screen: Space + Send + Sync + Sized { unimplemented!() } } + +/// Emit a [Draw]able. +pub trait View { + fn view (&self) -> impl Draw; +} + +impl> Draw for &V { + fn draw (self, to: &mut T) -> Usually> { + self.view().draw(to) + } +} diff --git a/src/exit.rs b/src/exit.rs index 0b79708..2d07816 100644 --- a/src/exit.rs +++ b/src/exit.rs @@ -8,10 +8,3 @@ impl Exit { run(Self(Arc::new(AtomicBool::new(false)))) } } - -impl AsRef> for Exit { - fn as_ref (&self) -> &Arc { - &self.0 - } -} - diff --git a/src/keys.rs b/src/keys.rs index 9cc54e1..2d2037c 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -2,13 +2,13 @@ use crate::task::Task; use ::std::sync::{Arc, RwLock, atomic::{AtomicBool, Ordering::*}}; use ::std::time::Duration; -use ::dizzle::{Usually, Apply, impl_from}; +use ::dizzle::{Usually, Do, impl_from}; use ::crossterm::event::{ read, Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState }; /// Spawn the TUI input thread which reads keys from the terminal. -pub fn tui_input > + Send + Sync + 'static> ( +pub fn tui_input > + Send + Sync + 'static> ( exited: &Arc, state: &Arc>, poll: Duration ) -> Result { let exited = exited.clone(); diff --git a/src/space.rs b/src/space.rs index 0bb159a..b942f74 100644 --- a/src/space.rs +++ b/src/space.rs @@ -1,5 +1,4 @@ use crate::{*, draw::*}; -#[cfg(test)] use proptest_derive::Arbitrary; /// Point with size. /// @@ -424,26 +423,22 @@ pub const fn w_full (a: impl Draw) -> impl Draw { a } pub const fn h_full (a: impl Draw) -> impl Draw { a } #[macro_export] macro_rules! north { - ($head:expr $(,)?) => { $head }; - ($head:expr, $($tail:expr),* $(,)?) => { north($head, north!($($tail,)*)) }; + ($($tt:tt)*) => { unimplemented!() }; } #[macro_export] macro_rules! south { - ($head:expr $(,)?) => { $head }; - ($head:expr, $($tail:expr),* $(,)?) => { south($head, south!($($tail,)*)) }; + ($($tt:tt)*) => { unimplemented!() }; } #[macro_export] macro_rules! east { - ($head:expr $(,)?) => { $head }; - ($head:expr, $($tail:expr),* $(,)?) => { east($head, east!($($tail,)*)) }; + ($($tt:tt)*) => { unimplemented!() }; } #[macro_export] macro_rules! west { - ($head:expr $(, $tail:expr)* $(,)?) => { west($head, west!($($tail,)*)) }; + ($($tt:tt)*) => { unimplemented!() }; } #[macro_export] macro_rules! above { - ($head:expr $(, $tail:expr)* $(,)?) => { above($head, above!($($tail,)*)) }; + ($($tt:tt)*) => { unimplemented!() }; } #[macro_export] macro_rules! below { - ($head:expr $(,)?) => { $head }; - ($head:expr, $($tail:expr),* $(,)?) => { below($head, below!($($tail,)*)) }; + ($($tt:tt)*) => { unimplemented!() }; } /// Iterate over a collection of renderables: @@ -457,56 +452,42 @@ pub const fn h_full (a: impl Draw) -> impl Draw { a } /// ].iter(), |x|x); /// ``` pub fn iter < - S: Screen, - D: Draw, + T: Screen, V: Fn()->I, - I: Iterator, - F: Fn(&D)->dyn Draw, -> (_items: V, _cb: F) -> impl Draw { - thunk(move|_to: &mut S|{ todo!() }) + I: Iterator>, + F: Fn(&dyn Draw)->dyn Draw, +> (_items: V, _cb: F) -> impl Draw { + thunk(move|_to: &mut T|{ todo!() }) } - -pub fn iter_north <'a, S: Screen, D: 'a, I: Iterator, U: Draw> ( - _iter: impl Fn()->I, _draw: impl Fn(D)->U, -) -> impl Draw { - thunk(move|_to: &mut S|{ todo!() }) +pub fn iter_north < + T: Screen, + V: Fn()->I, + I: Iterator>, + F: Fn(&dyn Draw)->dyn Draw, +> (_items: V, _cb: F) -> impl Draw { + thunk(move|_to: &mut T|{ todo!() }) } - -pub fn iter_east <'a, S: Screen, D: 'a, I: Iterator, U: Draw> ( - _iter: impl Fn()->I, _draw: impl Fn(D)->U, -) -> impl Draw { - thunk(move|_to: &mut S|{ todo!() }) +pub fn iter_east < + T: Screen, + V: Fn()->I, + I: Iterator>, + F: Fn(&dyn Draw)->dyn Draw, +> (_items: V, _cb: F) -> impl Draw { + thunk(move|_to: &mut T|{ todo!() }) } - -pub fn iter_south <'a, S: Screen, D: 'a, I: Iterator, U: Draw> ( - _iter: impl Fn()->I, _draw: impl Fn(D)->U, -) -> impl Draw { - thunk(move|_to: &mut S|{ todo!() }) +pub fn iter_south < + T: Screen, + V: Fn()->I, + I: Iterator>, + F: Fn(&dyn Draw)->dyn Draw, +> (_items: V, _cb: F) -> impl Draw { + thunk(move|_to: &mut T|{ todo!() }) } - -pub fn iter_west <'a, S: Screen, D: 'a, I: Iterator, U: Draw> ( - _iter: impl Fn()->I, _draw: impl Fn(D)->U, -) -> impl Draw { - thunk(move|_to: &mut S|{ todo!() }) -} - -#[derive(Default, Debug)] -pub struct Size(AtomicUsize, AtomicUsize); -impl X for Size { - fn x (&self) -> u16 { 0 } - fn w (&self) -> u16 { self.0.load(Relaxed) as u16 } -} -impl Y for Size { - fn y (&self) -> u16 { 0 } - fn h (&self) -> u16 { self.1.load(Relaxed) as u16 } -} -impl Size { - pub const fn of (&self, of: impl Draw) -> impl Draw { - thunk(move|to: &mut T|{ - let area = of.draw(to)?; - self.0.store(area.w().into(), Relaxed); - self.1.store(area.h().into(), Relaxed); - Ok(area) - }) - } +pub fn iter_west < + T: Screen, + V: Fn()->I, + I: Iterator>, + F: Fn(&dyn Draw)->dyn Draw, +> (_items: V, _cb: F) -> impl Draw { + thunk(move|_to: &mut T|{ todo!() }) } diff --git a/src/term.rs b/src/term.rs index ff8496d..32081c4 100644 --- a/src/term.rs +++ b/src/term.rs @@ -21,23 +21,6 @@ use ::{ event::{poll, read, Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState}, } }; -#[macro_export] macro_rules! tui_main { - ($state:expr) => { - pub fn main () -> Usually<()> { - tengri::exit::Exit::run(|exit|{ - let state = Arc::new(RwLock::new($state)); - Ok(( - ::tengri::keys::tui_input( - exit.as_ref(), &state, std::time::Duration::from_millis(100) - )?, - ::tengri::term::tui_output( - stdout(), exit.as_ref(), &state, std::time::Duration::from_millis(10) - )? - )) - }) - } - } -} pub struct Tui(pub Buffer, pub XYWH); impl Screen for Tui { type Unit = u16; } impl Deref for Tui { type Target = Buffer; fn deref (&self) -> &Buffer { &self.0 } } diff --git a/src/text.rs b/src/text.rs index c9752a4..9fe1dd4 100644 --- a/src/text.rs +++ b/src/text.rs @@ -17,6 +17,11 @@ pub(crate) use ::unicode_width::*; self.as_str().draw(to) } } + impl Draw for std::sync::Arc { + fn draw (self, to: &mut Tui) -> Usually> { + self.as_ref().draw(to) + } + } impl Draw for &std::sync::Arc { fn draw (self, to: &mut Tui) -> Usually> { self.as_ref().draw(to)