Compare commits

..

No commits in common. "b0fb9f013d9fd993ba1507767dcecd0005a59a63" and "a06ea2ac139d09ab9eba5931455c43a3a75f4151" have entirely different histories.

13 changed files with 98 additions and 176 deletions

1
.envrc
View file

@ -1 +0,0 @@
use nix

5
.gitignore vendored
View file

@ -1,4 +1,3 @@
*.profraw
.direnv
cov
target target
cov
*.profraw

View file

@ -37,7 +37,7 @@ winit = { optional = true, version = "0.30.4", features = [ "x11" ]}
[dev-dependencies] [dev-dependencies]
proptest = { version = "^1" } proptest = { version = "^1" }
proptest-derive = { version = "^0.5.1" } proptest-derive = { version = "^0.5.1" }
tengri = { path = "." } tengri = { path = ".", features = [ "dsl" ] }
#tengri_proc = { path = "./proc" } #tengri_proc = { path = "./proc" }
[profile.coverage] [profile.coverage]

View file

@ -2,9 +2,8 @@ export LLVM_PROFILE_FILE := "cov/cargo-test-%p-%m.profraw"
grcov-binary := "--binary-path ./target/coverage/deps/" grcov-binary := "--binary-path ./target/coverage/deps/"
grcov-ignore := "--ignore-not-existing --ignore '../*' --ignore \"/*\" --ignore 'target/*'" grcov-ignore := "--ignore-not-existing --ignore '../*' --ignore \"/*\" --ignore 'target/*'"
[private]
default: default:
@just -l just -l
bacon: bacon:
bacon -s bacon -s
@ -30,7 +29,6 @@ doc:
cargo doc cargo doc
example-tui-00: example-tui-00:
cargo run --example tui_00 cargo run -p tengri_tui --example tui_00
example-tui-01: example-tui-01:
cargo run --example tui_01 cargo run -p tengri_tui --example tui_01

View file

@ -1,74 +1,46 @@
use ::{ use ::{std::{io::stdout, sync::{Arc, RwLock}}, ratatui::style::Color, tengri::*};
std::{io::stdout, sync::{Arc, RwLock}}, tui_main!(State { cursor: 10, ..Default::default() });
tengri::{*, term::*, lang::*, keys::*, draw::*, space::*}, namespace!(State: bool {});
ratatui::style::Color, namespace!(State: u16 {});
}; namespace!(State: Color {});
handle!(TuiIn: |self: State, input|Action::from(input).eval(self).map(|_|None));
tui_main!(State { cursor: 10, ..Default::default() }); view!(State: TuiOut: [ evaluate_output_expression, evaluate_output_expression_tui ]);
draw!(State: TuiOut: [ draw_example ]);
impl Apply<TuiEvent, Usually<Self>> for State {
fn apply (&mut self, input: &TuiEvent) -> Usually<Self> {
todo!()
}
}
impl View<Tui> for State {
fn view (&self) -> impl Draw<Tui> {
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 { #[derive(Debug, Default)] struct State {
/** Command history (undo/redo). */ /** Rendered window size */ size: Measure<TuiOut>,
history: Vec<Action>, /** Command history (undo/redo) */ history: Vec<Action>,
/** User-controllable value. */ /** User-controllable value */ cursor: usize,
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 { #[derive(Debug)] enum Action {
/** Increment cursor */ /** Increment cursor */ Next,
Next, /** Decrement cursor */ Prev
/** Decrement cursor */
Prev
} }
fn draw_example (state: &State, to: &mut TuiOut) {
fn draw_example (state: &State, to: &mut Tui) { 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 { 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<Self> { fn eval (&self, state: &mut State) -> Perhaps<Self> {
use Action::*; use Action::*;
match self { Next => Self::next(state), Prev => Self::prev(state), } match self { Next => Self::next(state), Prev => Self::prev(state), }
} }
fn next (state: &mut State) -> Perhaps<Self> { fn next (state: &mut State) -> Perhaps<Self> {
state.cursor = (state.cursor + 1) % VIEWS.len(); state.cursor = (state.cursor + 1) % VIEWS.len();
Ok(Some(Self::Prev)) Ok(Some(Self::Prev))
} }
fn prev (state: &mut State) -> Perhaps<Self> { fn prev (state: &mut State) -> Perhaps<Self> {
state.cursor = if state.cursor > 0 { state.cursor - 1 } else { VIEWS.len() - 1 }; state.cursor = if state.cursor > 0 { state.cursor - 1 } else { VIEWS.len() - 1 };
Ok(Some(Self::Next)) Ok(Some(Self::Next))
} }
} }
const VIEWS: &'static [&'static str] = &[ const VIEWS: &'static [&'static str] = &[
stringify! { :hello-world }, 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 ]);

View file

@ -2,8 +2,8 @@
{pkgs?import<nixpkgs>{}}:let {pkgs?import<nixpkgs>{}}:let
stdenv = pkgs.clang19Stdenv; stdenv = pkgs.clang19Stdenv;
name = "tengri"; name = "tengri";
nativeBuildInputs = [ pkgs.pkg-config pkgs.clang pkgs.libclang pkgs.mold ]; nativeBuildInputs = [ pkgs.pkg-config pkgs.libclang pkgs.mold ];
buildInputs = [ pkgs.libclang pkgs.jack2 ]; buildInputs = [ pkgs.libclang ];
LIBCLANG_PATH = "${pkgs.libclang.lib.outPath}/lib"; LIBCLANG_PATH = "${pkgs.libclang.lib.outPath}/lib";
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath []; LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [];
in pkgs.mkShell.override { in pkgs.mkShell.override {

View file

@ -11,10 +11,6 @@ pub fn rgb (r: u8, g: u8, b: u8) -> ItemColor {
ItemColor { okhsl: rgb_to_okhsl(term), term } 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<f32>) -> Color { pub fn okhsl_to_rgb (color: Okhsl<f32>) -> Color {
let Srgb { red, green, blue, .. }: Srgb<f32> = Srgb::from_color_unclamped(color); let Srgb { red, green, blue, .. }: Srgb<f32> = Srgb::from_color_unclamped(color);
Color::Rgb((red * 255.0) as u8, (green * 255.0) as u8, (blue * 255.0) as u8,) Color::Rgb((red * 255.0) as u8, (green * 255.0) as u8, (blue * 255.0) as u8,)

View file

@ -7,10 +7,6 @@ use crate::{*, lang::*, color::*, space::*};
/// ///
/// Drawables are consumable, i.e. the [Draw::draw] method receives an /// Drawables are consumable, i.e. the [Draw::draw] method receives an
/// owned `self` and does not return it, consuming the drawable. /// 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::*; /// use tengri::draw::*;
@ -30,22 +26,17 @@ use crate::{*, lang::*, color::*, space::*};
pub trait Draw<T: Screen> { pub trait Draw<T: Screen> {
fn draw (self, to: &mut T) -> Usually<XYWH<T::Unit>>; fn draw (self, to: &mut T) -> Usually<XYWH<T::Unit>>;
} }
impl<T: Screen, D: Draw<T>> Draw<T> for &D {
fn draw (self, to: &mut T) -> Usually<XYWH<T::Unit>> { impl<T: Screen> Draw<T> for () {
todo!() fn draw (self, __: &mut T) -> Usually<XYWH<T::Unit>> {
} Ok(Default::default())
}
impl<T: Screen, D: Draw<T>> Draw<T> for Option<D> {
fn draw (self, to: &mut T) -> Usually<XYWH<T::Unit>> {
todo!()
} }
} }
/// Emit a [Draw]able. impl<T: Screen, D: Draw<T>> Draw<T> for Option<D> {
/// fn draw (self, to: &mut T) -> Usually<XYWH<T::Unit>> {
/// Speculative. How to avoid conflicts with [Draw] proper? Ok(self.map(|draw|draw.draw(to)).transpose()?.unwrap_or_default())
pub trait View<T: Screen> { }
fn view (&self) -> impl Draw<T>;
} }
pub const fn thunk <T: Screen, F: FnOnce(&mut T)->Usually<XYWH<T::Unit>>> (draw: F) -> Thunk<T, F> { pub const fn thunk <T: Screen, F: FnOnce(&mut T)->Usually<XYWH<T::Unit>>> (draw: F) -> Thunk<T, F> {
@ -57,6 +48,7 @@ pub struct Thunk<T: Screen, F: FnOnce(&mut T)->Usually<XYWH<T::Unit>>>(
pub F, pub F,
std::marker::PhantomData<T> std::marker::PhantomData<T>
); );
impl<T: Screen, F: FnOnce(&mut T)->Usually<XYWH<T::Unit>>> Draw<T> for Thunk<T, F> { impl<T: Screen, F: FnOnce(&mut T)->Usually<XYWH<T::Unit>>> Draw<T> for Thunk<T, F> {
fn draw (self, to: &mut T) -> Usually<XYWH<T::Unit>> { fn draw (self, to: &mut T) -> Usually<XYWH<T::Unit>> {
(self.0)(to) (self.0)(to)
@ -119,3 +111,14 @@ pub trait Screen: Space<Self::Unit> + Send + Sync + Sized {
unimplemented!() unimplemented!()
} }
} }
/// Emit a [Draw]able.
pub trait View<T: Screen> {
fn view (&self) -> impl Draw<T>;
}
impl<T: Screen, V: View<T>> Draw<T> for &V {
fn draw (self, to: &mut T) -> Usually<XYWH<T::Unit>> {
self.view().draw(to)
}
}

View file

@ -8,10 +8,3 @@ impl Exit {
run(Self(Arc::new(AtomicBool::new(false)))) run(Self(Arc::new(AtomicBool::new(false))))
} }
} }
impl AsRef<Arc<AtomicBool>> for Exit {
fn as_ref (&self) -> &Arc<AtomicBool> {
&self.0
}
}

View file

@ -2,13 +2,13 @@ use crate::task::Task;
use ::std::sync::{Arc, RwLock, atomic::{AtomicBool, Ordering::*}}; use ::std::sync::{Arc, RwLock, atomic::{AtomicBool, Ordering::*}};
use ::std::time::Duration; use ::std::time::Duration;
use ::dizzle::{Usually, Apply, impl_from}; use ::dizzle::{Usually, Do, impl_from};
use ::crossterm::event::{ use ::crossterm::event::{
read, Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState read, Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState
}; };
/// Spawn the TUI input thread which reads keys from the terminal. /// Spawn the TUI input thread which reads keys from the terminal.
pub fn tui_input <T: Apply<TuiEvent, Usually<T>> + Send + Sync + 'static> ( pub fn tui_input <T: Do<TuiEvent, Usually<T>> + Send + Sync + 'static> (
exited: &Arc<AtomicBool>, state: &Arc<RwLock<T>>, poll: Duration exited: &Arc<AtomicBool>, state: &Arc<RwLock<T>>, poll: Duration
) -> Result<Task, std::io::Error> { ) -> Result<Task, std::io::Error> {
let exited = exited.clone(); let exited = exited.clone();

View file

@ -1,5 +1,4 @@
use crate::{*, draw::*}; use crate::{*, draw::*};
#[cfg(test)] use proptest_derive::Arbitrary;
/// Point with size. /// Point with size.
/// ///
@ -424,26 +423,22 @@ pub const fn w_full <T: Screen> (a: impl Draw<T>) -> impl Draw<T> { a }
pub const fn h_full <T: Screen> (a: impl Draw<T>) -> impl Draw<T> { a } pub const fn h_full <T: Screen> (a: impl Draw<T>) -> impl Draw<T> { a }
#[macro_export] macro_rules! north { #[macro_export] macro_rules! north {
($head:expr $(,)?) => { $head }; ($($tt:tt)*) => { unimplemented!() };
($head:expr, $($tail:expr),* $(,)?) => { north($head, north!($($tail,)*)) };
} }
#[macro_export] macro_rules! south { #[macro_export] macro_rules! south {
($head:expr $(,)?) => { $head }; ($($tt:tt)*) => { unimplemented!() };
($head:expr, $($tail:expr),* $(,)?) => { south($head, south!($($tail,)*)) };
} }
#[macro_export] macro_rules! east { #[macro_export] macro_rules! east {
($head:expr $(,)?) => { $head }; ($($tt:tt)*) => { unimplemented!() };
($head:expr, $($tail:expr),* $(,)?) => { east($head, east!($($tail,)*)) };
} }
#[macro_export] macro_rules! west { #[macro_export] macro_rules! west {
($head:expr $(, $tail:expr)* $(,)?) => { west($head, west!($($tail,)*)) }; ($($tt:tt)*) => { unimplemented!() };
} }
#[macro_export] macro_rules! above { #[macro_export] macro_rules! above {
($head:expr $(, $tail:expr)* $(,)?) => { above($head, above!($($tail,)*)) }; ($($tt:tt)*) => { unimplemented!() };
} }
#[macro_export] macro_rules! below { #[macro_export] macro_rules! below {
($head:expr $(,)?) => { $head }; ($($tt:tt)*) => { unimplemented!() };
($head:expr, $($tail:expr),* $(,)?) => { below($head, below!($($tail,)*)) };
} }
/// Iterate over a collection of renderables: /// Iterate over a collection of renderables:
@ -457,56 +452,42 @@ pub const fn h_full <T: Screen> (a: impl Draw<T>) -> impl Draw<T> { a }
/// ].iter(), |x|x); /// ].iter(), |x|x);
/// ``` /// ```
pub fn iter < pub fn iter <
S: Screen, T: Screen,
D: Draw<S>,
V: Fn()->I, V: Fn()->I,
I: Iterator<Item = D>, I: Iterator<Item = dyn Draw<T>>,
F: Fn(&D)->dyn Draw<S>, F: Fn(&dyn Draw<T>)->dyn Draw<T>,
> (_items: V, _cb: F) -> impl Draw<S> { > (_items: V, _cb: F) -> impl Draw<T> {
thunk(move|_to: &mut S|{ todo!() }) thunk(move|_to: &mut T|{ todo!() })
} }
pub fn iter_north <
pub fn iter_north <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> ( T: Screen,
_iter: impl Fn()->I, _draw: impl Fn(D)->U, V: Fn()->I,
) -> impl Draw<S> { I: Iterator<Item = dyn Draw<T>>,
thunk(move|_to: &mut S|{ todo!() }) F: Fn(&dyn Draw<T>)->dyn Draw<T>,
> (_items: V, _cb: F) -> impl Draw<T> {
thunk(move|_to: &mut T|{ todo!() })
} }
pub fn iter_east <
pub fn iter_east <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> ( T: Screen,
_iter: impl Fn()->I, _draw: impl Fn(D)->U, V: Fn()->I,
) -> impl Draw<S> { I: Iterator<Item = dyn Draw<T>>,
thunk(move|_to: &mut S|{ todo!() }) F: Fn(&dyn Draw<T>)->dyn Draw<T>,
> (_items: V, _cb: F) -> impl Draw<T> {
thunk(move|_to: &mut T|{ todo!() })
} }
pub fn iter_south <
pub fn iter_south <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> ( T: Screen,
_iter: impl Fn()->I, _draw: impl Fn(D)->U, V: Fn()->I,
) -> impl Draw<S> { I: Iterator<Item = dyn Draw<T>>,
thunk(move|_to: &mut S|{ todo!() }) F: Fn(&dyn Draw<T>)->dyn Draw<T>,
> (_items: V, _cb: F) -> impl Draw<T> {
thunk(move|_to: &mut T|{ todo!() })
} }
pub fn iter_west <
pub fn iter_west <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> ( T: Screen,
_iter: impl Fn()->I, _draw: impl Fn(D)->U, V: Fn()->I,
) -> impl Draw<S> { I: Iterator<Item = dyn Draw<T>>,
thunk(move|_to: &mut S|{ todo!() }) F: Fn(&dyn Draw<T>)->dyn Draw<T>,
} > (_items: V, _cb: F) -> impl Draw<T> {
thunk(move|_to: &mut T|{ todo!() })
#[derive(Default, Debug)]
pub struct Size(AtomicUsize, AtomicUsize);
impl X<u16> for Size {
fn x (&self) -> u16 { 0 }
fn w (&self) -> u16 { self.0.load(Relaxed) as u16 }
}
impl Y<u16> for Size {
fn y (&self) -> u16 { 0 }
fn h (&self) -> u16 { self.1.load(Relaxed) as u16 }
}
impl Size {
pub const fn of <T: Screen> (&self, of: impl Draw<T>) -> impl Draw<T> {
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)
})
}
} }

View file

@ -21,23 +21,6 @@ use ::{
event::{poll, read, Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState}, 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<u16>); pub struct Tui(pub Buffer, pub XYWH<u16>);
impl Screen for Tui { type Unit = u16; } impl Screen for Tui { type Unit = u16; }
impl Deref for Tui { type Target = Buffer; fn deref (&self) -> &Buffer { &self.0 } } impl Deref for Tui { type Target = Buffer; fn deref (&self) -> &Buffer { &self.0 } }

View file

@ -17,6 +17,11 @@ pub(crate) use ::unicode_width::*;
self.as_str().draw(to) self.as_str().draw(to)
} }
} }
impl Draw<Tui> for std::sync::Arc<str> {
fn draw (self, to: &mut Tui) -> Usually<XYWH<u16>> {
self.as_ref().draw(to)
}
}
impl Draw<Tui> for &std::sync::Arc<str> { impl Draw<Tui> for &std::sync::Arc<str> {
fn draw (self, to: &mut Tui) -> Usually<XYWH<u16>> { fn draw (self, to: &mut Tui) -> Usually<XYWH<u16>> {
self.as_ref().draw(to) self.as_ref().draw(to)