From a93fe92a596b8a9bcec898e228fca909c0e2e9f4 Mon Sep 17 00:00:00 2001 From: i do not exist Date: Mon, 13 Apr 2026 17:30:43 +0300 Subject: [PATCH 1/4] fix Size, begin fixing View --- src/draw.rs | 30 +++++++++++++++++------------- src/space.rs | 21 +++++++++++++++++++++ src/text.rs | 5 ----- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/draw.rs b/src/draw.rs index 9b8ebdb..d1b476b 100644 --- a/src/draw.rs +++ b/src/draw.rs @@ -1,5 +1,17 @@ use crate::{*, lang::*, color::*, space::*}; +/// Emit a [Draw]able. +/// +/// Speculative. How to avoid conflicts with [Draw] proper? +pub trait View { + fn view (&self) -> impl Draw; +} +impl> Draw for &V { + fn draw (self, to: &mut T) -> Usually> { + self.view().draw(to) + } +} + /// Drawable that supports dynamic dispatch. /// /// Drawables are composable, e.g. the [when] and [either] conditionals @@ -7,6 +19,10 @@ 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::*; @@ -26,13 +42,12 @@ use crate::{*, lang::*, color::*, space::*}; pub trait Draw { fn draw (self, to: &mut T) -> Usually>; } - +/// Empty draw impl Draw for () { fn draw (self, __: &mut T) -> Usually> { Ok(Default::default()) } } - impl> Draw for Option { fn draw (self, to: &mut T) -> Usually> { Ok(self.map(|draw|draw.draw(to)).transpose()?.unwrap_or_default()) @@ -111,14 +126,3 @@ 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/space.rs b/src/space.rs index b942f74..e0aa498 100644 --- a/src/space.rs +++ b/src/space.rs @@ -491,3 +491,24 @@ pub fn iter_west < > (_items: V, _cb: F) -> impl Draw { thunk(move|_to: &mut T|{ 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) + }) + } +} diff --git a/src/text.rs b/src/text.rs index 9fe1dd4..c9752a4 100644 --- a/src/text.rs +++ b/src/text.rs @@ -17,11 +17,6 @@ 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) From 6c382e2627d5f688c8982a1efb5c5bd4357f63d9 Mon Sep 17 00:00:00 2001 From: i do not exist Date: Wed, 15 Apr 2026 11:11:36 +0300 Subject: [PATCH 2/4] re-add tui_main, fixing examples --- Cargo.toml | 2 +- examples/tui_00.rs | 73 +++++++++++++++++++++++++++++++--------------- src/draw.rs | 29 +++++++----------- src/exit.rs | 7 +++++ src/keys.rs | 4 +-- src/space.rs | 1 + src/term.rs | 17 +++++++++++ 7 files changed, 89 insertions(+), 44 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b0fd6fa..8657a68 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 = ".", features = [ "dsl" ] } +tengri = { path = "." } #tengri_proc = { path = "./proc" } [profile.coverage] diff --git a/examples/tui_00.rs b/examples/tui_00.rs index 80e7acd..ff2ca2d 100644 --- a/examples/tui_00.rs +++ b/examples/tui_00.rs @@ -1,31 +1,57 @@ -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 ]); +use ::{ + std::{io::stdout, sync::{Arc, RwLock}}, + tengri::{*, term::*, lang::*, keys::*, draw::*, space::*}, + ratatui::style::Color, +}; + +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: Tui: [ evaluate_output_expression, evaluate_output_expression_tui ]); + //draw!(State: Tui: [ draw_example ]); + +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()))) + } +} + #[derive(Debug, Default)] struct State { - /** Rendered window size */ size: Measure, - /** Command history (undo/redo) */ history: Vec, - /** User-controllable value */ cursor: usize, + /** Command history (undo/redo). */ + history: Vec, + /** User-controllable value. */ + cursor: usize, + /** Rendered window size. */ + size: crate::space::Size, } -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 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) + +fn draw_example (state: &State, to: &mut Tui) { } + impl Action { const BINDS: &'static str = stringify! { (@left prev) (@right next) }; fn eval (&self, state: &mut State) -> Perhaps { @@ -41,6 +67,7 @@ impl Action { Ok(Some(Self::Next)) } } + const VIEWS: &'static [&'static str] = &[ stringify! { :hello-world }, diff --git a/src/draw.rs b/src/draw.rs index d1b476b..ad47922 100644 --- a/src/draw.rs +++ b/src/draw.rs @@ -1,17 +1,5 @@ use crate::{*, lang::*, color::*, space::*}; -/// Emit a [Draw]able. -/// -/// Speculative. How to avoid conflicts with [Draw] proper? -pub trait View { - fn view (&self) -> impl Draw; -} -impl> Draw for &V { - fn draw (self, to: &mut T) -> Usually> { - self.view().draw(to) - } -} - /// Drawable that supports dynamic dispatch. /// /// Drawables are composable, e.g. the [when] and [either] conditionals @@ -42,18 +30,24 @@ impl> Draw for &V { pub trait Draw { fn draw (self, to: &mut T) -> Usually>; } -/// Empty draw -impl Draw for () { - fn draw (self, __: &mut T) -> Usually> { - Ok(Default::default()) +impl> Draw for &D { + fn draw (self, to: &mut T) -> Usually> { + todo!() } } impl> Draw for Option { fn draw (self, to: &mut T) -> Usually> { - Ok(self.map(|draw|draw.draw(to)).transpose()?.unwrap_or_default()) + todo!() } } +/// Emit a [Draw]able. +/// +/// Speculative. How to avoid conflicts with [Draw] proper? +pub trait View { + fn view (&self) -> impl Draw; +} + pub const fn thunk Usually>> (draw: F) -> Thunk { Thunk(draw, std::marker::PhantomData) } @@ -63,7 +57,6 @@ pub struct ThunkUsually>>( pub F, std::marker::PhantomData ); - implUsually>> Draw for Thunk { fn draw (self, to: &mut T) -> Usually> { (self.0)(to) diff --git a/src/exit.rs b/src/exit.rs index 2d07816..0b79708 100644 --- a/src/exit.rs +++ b/src/exit.rs @@ -8,3 +8,10 @@ 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 2d2037c..9cc54e1 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, Do, impl_from}; +use ::dizzle::{Usually, Apply, 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 e0aa498..b3916a5 100644 --- a/src/space.rs +++ b/src/space.rs @@ -1,4 +1,5 @@ use crate::{*, draw::*}; +#[cfg(test)] use proptest_derive::Arbitrary; /// Point with size. /// diff --git a/src/term.rs b/src/term.rs index 32081c4..ff8496d 100644 --- a/src/term.rs +++ b/src/term.rs @@ -21,6 +21,23 @@ 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 } } From c9b9ff15191ec3a37e8da35f382ce0d4af74d0d8 Mon Sep 17 00:00:00 2001 From: i do not exist Date: Fri, 17 Apr 2026 04:07:14 +0300 Subject: [PATCH 3/4] fix signatures of iter_ helpers --- examples/tui_00.rs | 24 ++++++++++----- src/color.rs | 4 +++ src/space.rs | 75 ++++++++++++++++++++++------------------------ 3 files changed, 56 insertions(+), 47 deletions(-) diff --git a/examples/tui_00.rs b/examples/tui_00.rs index ff2ca2d..929c56d 100644 --- a/examples/tui_00.rs +++ b/examples/tui_00.rs @@ -6,13 +6,6 @@ use ::{ 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: Tui: [ evaluate_output_expression, evaluate_output_expression_tui ]); - //draw!(State: Tui: [ draw_example ]); - impl Apply> for State { fn apply (&mut self, input: &TuiEvent) -> Usually { todo!() @@ -53,19 +46,27 @@ fn draw_example (state: &State, to: &mut Tui) { } 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] = &[ @@ -148,3 +149,10 @@ 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/src/color.rs b/src/color.rs index 13caf71..f16358a 100644 --- a/src/color.rs +++ b/src/color.rs @@ -11,6 +11,10 @@ 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/space.rs b/src/space.rs index b3916a5..0bb159a 100644 --- a/src/space.rs +++ b/src/space.rs @@ -424,22 +424,26 @@ 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 { - ($($tt:tt)*) => { unimplemented!() }; + ($head:expr $(,)?) => { $head }; + ($head:expr, $($tail:expr),* $(,)?) => { north($head, north!($($tail,)*)) }; } #[macro_export] macro_rules! south { - ($($tt:tt)*) => { unimplemented!() }; + ($head:expr $(,)?) => { $head }; + ($head:expr, $($tail:expr),* $(,)?) => { south($head, south!($($tail,)*)) }; } #[macro_export] macro_rules! east { - ($($tt:tt)*) => { unimplemented!() }; + ($head:expr $(,)?) => { $head }; + ($head:expr, $($tail:expr),* $(,)?) => { east($head, east!($($tail,)*)) }; } #[macro_export] macro_rules! west { - ($($tt:tt)*) => { unimplemented!() }; + ($head:expr $(, $tail:expr)* $(,)?) => { west($head, west!($($tail,)*)) }; } #[macro_export] macro_rules! above { - ($($tt:tt)*) => { unimplemented!() }; + ($head:expr $(, $tail:expr)* $(,)?) => { above($head, above!($($tail,)*)) }; } #[macro_export] macro_rules! below { - ($($tt:tt)*) => { unimplemented!() }; + ($head:expr $(,)?) => { $head }; + ($head:expr, $($tail:expr),* $(,)?) => { below($head, below!($($tail,)*)) }; } /// Iterate over a collection of renderables: @@ -453,44 +457,37 @@ pub const fn h_full (a: impl Draw) -> impl Draw { a } /// ].iter(), |x|x); /// ``` pub fn iter < - T: Screen, + S: Screen, + D: Draw, V: Fn()->I, - I: Iterator>, - F: Fn(&dyn Draw)->dyn Draw, -> (_items: V, _cb: F) -> impl Draw { - thunk(move|_to: &mut T|{ todo!() }) + I: Iterator, + F: Fn(&D)->dyn Draw, +> (_items: V, _cb: F) -> 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_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_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_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_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_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_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!() }) + +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)] From b0fb9f013d9fd993ba1507767dcecd0005a59a63 Mon Sep 17 00:00:00 2001 From: i do not exist Date: Thu, 23 Apr 2026 02:24:33 +0300 Subject: [PATCH 4/4] fix build environment --- .envrc | 1 + .gitignore | 5 +++-- Justfile | 8 +++++--- shell.nix | 4 ++-- 4 files changed, 11 insertions(+), 7 deletions(-) create mode 100644 .envrc diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..1d953f4 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use nix diff --git a/.gitignore b/.gitignore index 8899d91..4f90c40 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ -target -cov *.profraw +.direnv +cov +target diff --git a/Justfile b/Justfile index 1d5f794..8d7c790 100644 --- a/Justfile +++ b/Justfile @@ -2,8 +2,9 @@ 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 @@ -29,6 +30,7 @@ doc: cargo doc example-tui-00: - cargo run -p tengri_tui --example tui_00 + cargo run --example tui_00 + example-tui-01: - cargo run -p tengri_tui --example tui_01 + cargo run --example tui_01 diff --git a/shell.nix b/shell.nix index a4c3248..f34dc1f 100644 --- a/shell.nix +++ b/shell.nix @@ -2,8 +2,8 @@ {pkgs?import{}}:let stdenv = pkgs.clang19Stdenv; name = "tengri"; - nativeBuildInputs = [ pkgs.pkg-config pkgs.libclang pkgs.mold ]; - buildInputs = [ pkgs.libclang ]; + nativeBuildInputs = [ pkgs.pkg-config pkgs.clang pkgs.libclang pkgs.mold ]; + buildInputs = [ pkgs.libclang pkgs.jack2 ]; LIBCLANG_PATH = "${pkgs.libclang.lib.outPath}/lib"; LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath []; in pkgs.mkShell.override {