mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2026-03-13 12:10:44 +01:00
it's a song!
This commit is contained in:
parent
0d8503cc05
commit
5d0dc40fdc
11 changed files with 846 additions and 828 deletions
5
Cargo.lock
generated
5
Cargo.lock
generated
|
|
@ -1164,8 +1164,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tengri"
|
name = "tengri"
|
||||||
version = "0.14.0"
|
version = "0.15.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"atomic_float",
|
"atomic_float",
|
||||||
"better-panic",
|
"better-panic",
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
|
|
@ -1184,7 +1185,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tengri_proc"
|
name = "tengri_proc"
|
||||||
version = "0.14.0"
|
version = "0.15.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dizzle",
|
"dizzle",
|
||||||
"heck",
|
"heck",
|
||||||
|
|
|
||||||
|
|
@ -18,14 +18,10 @@ atomic_float = { version = "1" }
|
||||||
better-panic = { version = "0.3.0" }
|
better-panic = { version = "0.3.0" }
|
||||||
bumpalo = { version = "3.19.0", optional = true }
|
bumpalo = { version = "3.19.0", optional = true }
|
||||||
crossterm = { version = "0.29.0" }
|
crossterm = { version = "0.29.0" }
|
||||||
heck = { version = "0.5" }
|
|
||||||
palette = { version = "0.7.6", features = [ "random" ] }
|
palette = { version = "0.7.6", features = [ "random" ] }
|
||||||
proc-macro2 = { version = "1", features = ["span-locations"] }
|
|
||||||
quanta = { version = "0.12.3" }
|
quanta = { version = "0.12.3" }
|
||||||
quote = { version = "1" }
|
|
||||||
rand = { version = "0.8.5" }
|
rand = { version = "0.8.5" }
|
||||||
ratatui = { version = "0.29.0", features = [ "unstable-widget-ref", "underline-color" ] }
|
ratatui = { version = "0.29.0", features = [ "unstable-widget-ref", "underline-color" ] }
|
||||||
syn = { version = "2", features = ["full", "extra-traits"] }
|
|
||||||
unicode-width = { version = "0.2" }
|
unicode-width = { version = "0.2" }
|
||||||
dizzle = { path = "../dizzle" }
|
dizzle = { path = "../dizzle" }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
use ::{std::sync::{Arc, RwLock}, ratatui::style::Color,
|
use ::{std::{io::stdout, sync::{Arc, RwLock}}, ratatui::style::Color, tengri::*};
|
||||||
tengri::{*, input::*, output::*}, tengri_tui::*};
|
|
||||||
tui_main!(State { cursor: 10, ..Default::default() });
|
tui_main!(State { cursor: 10, ..Default::default() });
|
||||||
namespace!(State: bool {});
|
namespace!(State: bool {});
|
||||||
namespace!(State: u16 {});
|
namespace!(State: u16 {});
|
||||||
|
|
@ -25,7 +24,7 @@ fn draw_example (state: &State, to: &mut TuiOut) {
|
||||||
let title = Tui::bg(Color::Rgb(60, 10, 10), Push::Y(1, Align::n(heading)));
|
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 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)));
|
//let content = ;//();//Tui::bg(Color::Rgb(10, 10, 60), View(state, CstIter::new(src)));
|
||||||
state.size.of(Bsp::s(title, Bsp::n(code, state.view(to, &src).unwrap()))).draw(to)
|
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) };
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use ::{std::sync::{Arc, RwLock}, ratatui::style::Color, tengri::*};
|
||||||
fn main () {}
|
fn main () {}
|
||||||
|
|
||||||
//#[tengri_proc::expose]
|
//#[tengri_proc::expose]
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
[package]
|
[package]
|
||||||
name = "tengri_proc"
|
name = "tengri_proc"
|
||||||
description = "UI metaframework, procedural macros."
|
description = "UI metaframework, procedural macros."
|
||||||
version = { workspace = true }
|
version = "0.15.0"
|
||||||
edition = { workspace = true }
|
edition = "2024"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
dizzle = { path = "../../dizzle" }
|
dizzle = { path = "../../dizzle" }
|
||||||
syn = { workspace = true }
|
quote = { version = "1" }
|
||||||
quote = { workspace = true }
|
syn = { version = "2", features = ["full", "extra-traits"] }
|
||||||
proc-macro2 = { workspace = true }
|
heck = { version = "0.5" }
|
||||||
heck = { workspace = true }
|
proc-macro2 = { version = "1", features = ["span-locations"] }
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")']
|
[target.'cfg(target_os = "linux")']
|
||||||
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
||||||
|
|
|
||||||
1
proc/README.md
Normal file
1
proc/README.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
currently unused.
|
||||||
135
src/tengri.rs
135
src/tengri.rs
|
|
@ -4,18 +4,20 @@
|
||||||
#![feature(const_option_ops)]
|
#![feature(const_option_ops)]
|
||||||
#![feature(const_precise_live_drops)]
|
#![feature(const_precise_live_drops)]
|
||||||
#![feature(const_trait_impl)]
|
#![feature(const_trait_impl)]
|
||||||
#![feature(if_let_guard)]
|
|
||||||
#![feature(impl_trait_in_assoc_type)]
|
#![feature(impl_trait_in_assoc_type)]
|
||||||
#![feature(step_trait)]
|
#![feature(step_trait)]
|
||||||
#![feature(trait_alias)]
|
#![feature(trait_alias)]
|
||||||
#![feature(type_alias_impl_trait)]
|
#![feature(type_alias_impl_trait)]
|
||||||
#![feature(type_changing_struct_update)]
|
#![feature(type_changing_struct_update)]
|
||||||
|
mod tengri_impl;
|
||||||
//pub(crate) use quanta::Clock;
|
mod tengri_trait; pub use self::tengri_trait::*;
|
||||||
|
mod tengri_struct; pub use self::tengri_struct::*;
|
||||||
pub extern crate atomic_float;
|
#[cfg(test)] pub(crate) use proptest_derive::Arbitrary;
|
||||||
pub(crate) use atomic_float::AtomicF64;
|
pub extern crate dizzle; pub use dizzle::*;
|
||||||
|
pub extern crate atomic_float; pub(crate) use atomic_float::AtomicF64;
|
||||||
|
pub extern crate palette; pub(crate) use ::palette::{*, convert::*, okhsl::*};
|
||||||
|
pub extern crate better_panic; pub(crate) use better_panic::{Settings, Verbosity};
|
||||||
|
pub extern crate unicode_width; pub(crate) use unicode_width::*;
|
||||||
pub extern crate ratatui; pub(crate) use ::ratatui::{
|
pub extern crate ratatui; pub(crate) use ::ratatui::{
|
||||||
prelude::{Color, Style, Buffer, Position},
|
prelude::{Color, Style, Buffer, Position},
|
||||||
style::{Stylize, Modifier, Color::*},
|
style::{Stylize, Modifier, Color::*},
|
||||||
|
|
@ -23,39 +25,19 @@ pub extern crate ratatui; pub(crate) use ::ratatui::{
|
||||||
layout::{Size, Rect},
|
layout::{Size, Rect},
|
||||||
buffer::Cell
|
buffer::Cell
|
||||||
};
|
};
|
||||||
|
pub extern crate crossterm; pub(crate) use ::crossterm::{
|
||||||
pub extern crate crossterm;
|
|
||||||
pub(crate) use ::crossterm::{
|
|
||||||
ExecutableCommand,
|
ExecutableCommand,
|
||||||
terminal::{EnterAlternateScreen, LeaveAlternateScreen, enable_raw_mode, disable_raw_mode},
|
terminal::{EnterAlternateScreen, LeaveAlternateScreen, enable_raw_mode, disable_raw_mode},
|
||||||
event::{poll, read, Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState},
|
event::{poll, read, Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub extern crate palette;
|
|
||||||
pub(crate) use ::palette::{*, convert::*, okhsl::*};
|
|
||||||
|
|
||||||
pub extern crate better_panic;
|
|
||||||
pub(crate) use better_panic::{Settings, Verbosity};
|
|
||||||
|
|
||||||
pub extern crate unicode_width;
|
|
||||||
pub(crate) use unicode_width::*;
|
|
||||||
|
|
||||||
//#[cfg(test)] extern crate tengri_proc;
|
|
||||||
mod tengri_impl;
|
|
||||||
mod tengri_type; pub use self::tengri_type::*;
|
|
||||||
mod tengri_trait; pub use self::tengri_trait::*;
|
|
||||||
mod tengri_struct; pub use self::tengri_struct::*;
|
|
||||||
|
|
||||||
#[macro_export] pub extern crate dizzle;
|
|
||||||
pub use dizzle::*;
|
|
||||||
|
|
||||||
use std::{time::Duration, thread::{spawn, JoinHandle}, io::Write};
|
|
||||||
pub(crate) use ::std::{
|
pub(crate) use ::std::{
|
||||||
io::{stdout, Stdout},
|
io::{stdout, Stdout, Write},
|
||||||
sync::{Arc, RwLock, atomic::{AtomicBool, AtomicUsize, Ordering::*}},
|
sync::{Arc, RwLock, atomic::{AtomicBool, AtomicUsize, Ordering::*}},
|
||||||
fmt::{Debug, Display},
|
fmt::{Debug, Display},
|
||||||
ops::{Add, Sub, Mul, Div},
|
ops::{Add, Sub, Mul, Div},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
|
time::Duration,
|
||||||
|
thread::{spawn, JoinHandle}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Define macros first, so that private macros are available in private modules:
|
// Define macros first, so that private macros are available in private modules:
|
||||||
|
|
@ -92,16 +74,24 @@ pub(crate) use ::std::{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stack things on top of each other,
|
/// Stack things on top of each other,
|
||||||
#[macro_export] macro_rules! lay (($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::b(bsp, $expr);)*; bsp }});
|
#[macro_export] macro_rules! lay (($($expr:expr),* $(,)?) => {{
|
||||||
|
let bsp = (); $(let bsp = Bsp::b(bsp, $expr);)*; bsp
|
||||||
|
}});
|
||||||
|
|
||||||
/// Stack southward.
|
/// Stack southward.
|
||||||
#[macro_export] macro_rules! col (($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::s(bsp, $expr);)*; bsp }});
|
#[macro_export] macro_rules! col (($($expr:expr),* $(,)?) => {{
|
||||||
|
let bsp = (); $(let bsp = Bsp::s(bsp, $expr);)*; bsp
|
||||||
|
}});
|
||||||
|
|
||||||
/// Stack northward.
|
/// Stack northward.
|
||||||
#[macro_export] macro_rules! col_up (($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::n(bsp, $expr);)*; bsp }});
|
#[macro_export] macro_rules! col_up (($($expr:expr),* $(,)?) => {{
|
||||||
|
let bsp = (); $(let bsp = Bsp::n(bsp, $expr);)*; bsp
|
||||||
|
}});
|
||||||
|
|
||||||
/// Stack eastward.
|
/// Stack eastward.
|
||||||
#[macro_export] macro_rules! row (($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::e(bsp, $expr);)*; bsp }});
|
#[macro_export] macro_rules! row (($($expr:expr),* $(,)?) => {{
|
||||||
|
let bsp = (); $(let bsp = Bsp::e(bsp, $expr);)*; bsp
|
||||||
|
}});
|
||||||
|
|
||||||
/// Implement [Command] for given `State` and `handler`
|
/// Implement [Command] for given `State` and `handler`
|
||||||
#[macro_export] macro_rules! command {
|
#[macro_export] macro_rules! command {
|
||||||
|
|
@ -154,7 +144,9 @@ pub(crate) use ::std::{
|
||||||
#[macro_export] macro_rules! tui_main {
|
#[macro_export] macro_rules! tui_main {
|
||||||
($expr:expr) => {
|
($expr:expr) => {
|
||||||
fn main () -> Usually<()> {
|
fn main () -> Usually<()> {
|
||||||
tengri::Tui::new(stdout()).run(true, $expr)?;
|
let engine = ::tengri::Tui::new(Box::new(stdout()))?;
|
||||||
|
let state = ::std::sync::Arc::new(std::sync::RwLock::new($expr));
|
||||||
|
engine.run(true, &state)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -328,20 +320,28 @@ pub fn tui_input <T: Handle<TuiIn> + Send + Sync + 'static> (
|
||||||
/// Define layout operation.
|
/// Define layout operation.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// struct Target;
|
/// # use tengri::*;
|
||||||
/// impl tengri::Output for Target { type Unit = u16; }
|
/// struct Target { xywh: XYWH<u16> /*platform-specific*/}
|
||||||
|
/// impl tengri::Out for Target {
|
||||||
|
/// type Unit = u16;
|
||||||
|
/// fn area (&self) -> XYWH<u16> { self.xywh }
|
||||||
|
/// fn area_mut (&mut self) -> &mut XYWH<u16> { &mut self.xywh }
|
||||||
|
/// fn place_at <'t, T: Draw<Self> + ?Sized> (&mut self, area: XYWH<u16>, content: &'t T) {}
|
||||||
|
/// }
|
||||||
///
|
///
|
||||||
/// struct State;
|
/// struct State {/*app-specific*/}
|
||||||
/// impl<'b> Namespace<'b, bool> for State {}
|
/// impl<'b> Namespace<'b, bool> for State {}
|
||||||
/// impl<'b> Namespace<'b, u16> for State {}
|
/// impl<'b> Namespace<'b, u16> for State {}
|
||||||
/// impl Understand<Target, ()> for State {}
|
/// impl Understand<Target, ()> for State {}
|
||||||
///
|
///
|
||||||
/// let state = State;
|
/// # fn main () -> tengri::Usually<()> {
|
||||||
/// let target = Target::default();
|
/// let state = State {};
|
||||||
/// tengri::evaluate_output_expression(&state, &mut out, "")?;
|
/// let mut target = Target { xywh: Default::default() };
|
||||||
/// tengri::evaluate_output_expression(&state, &mut out, "(when true (text hello))")?;
|
/// evaluate_output_expression(&state, &mut target, &"")?;
|
||||||
/// tengri::evaluate_output_expression(&state, &mut out, "(either true (text hello) (text world)"))?;
|
/// evaluate_output_expression(&state, &mut target, &"(when true (text hello))")?;
|
||||||
|
/// evaluate_output_expression(&state, &mut target, &"(either true (text hello) (text world))")?;
|
||||||
/// // TODO test all
|
/// // TODO test all
|
||||||
|
/// # Ok(()) }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(feature = "dsl")] pub fn evaluate_output_expression <'a, O: Out + 'a, S> (
|
#[cfg(feature = "dsl")] pub fn evaluate_output_expression <'a, O: Out + 'a, S> (
|
||||||
state: &S, output: &mut O, expr: &'a impl Expression
|
state: &S, output: &mut O, expr: &'a impl Expression
|
||||||
|
|
@ -473,18 +473,22 @@ pub fn tui_input <T: Handle<TuiIn> + Send + Sync + 'static> (
|
||||||
/// Should be impl something or other...
|
/// Should be impl something or other...
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
/// use tengri::{Namespace, Understand, TuiOut, ratatui::prelude::Color};
|
||||||
|
///
|
||||||
/// struct State;
|
/// struct State;
|
||||||
/// impl<'b> Namespace<'b, bool> for State {}
|
/// impl<'b> Namespace<'b, bool> for State {}
|
||||||
/// impl<'b> Namespace<'b, u16> for State {}
|
/// impl<'b> Namespace<'b, u16> for State {}
|
||||||
/// impl<'b> Namespace<'b, Color> for State {}
|
/// impl<'b> Namespace<'b, Color> for State {}
|
||||||
/// impl Understand<TuiOut, ()> for State {}
|
/// impl Understand<TuiOut, ()> for State {}
|
||||||
|
/// # fn main () -> tengri::Usually<()> {
|
||||||
/// let state = State;
|
/// let state = State;
|
||||||
/// let out = TuiOut::default();
|
/// let mut out = TuiOut::default();
|
||||||
/// tengri::evaluate_output_expression_tui(&state, &mut out, "")?;
|
/// tengri::evaluate_output_expression_tui(&state, &mut out, "")?;
|
||||||
/// tengri::evaluate_output_expression_tui(&state, &mut out, "text Hello world!")?;
|
/// tengri::evaluate_output_expression_tui(&state, &mut out, "text Hello world!")?;
|
||||||
/// tengri::evaluate_output_expression_tui(&state, &mut out, "fg (g 0) (text Hello world!)")?;
|
/// tengri::evaluate_output_expression_tui(&state, &mut out, "fg (g 0) (text Hello world!)")?;
|
||||||
/// tengri::evaluate_output_expression_tui(&state, &mut out, "bg (g 2) (text Hello world!)")?;
|
/// tengri::evaluate_output_expression_tui(&state, &mut out, "bg (g 2) (text Hello world!)")?;
|
||||||
/// tengri::evaluate_output_expression_tui(&state, &mut out, "(bg (g 3) (fg (g 4) (text Hello world!)))")?;
|
/// tengri::evaluate_output_expression_tui(&state, &mut out, "(bg (g 3) (fg (g 4) (text Hello world!)))")?;
|
||||||
|
/// # Ok(()) }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(feature = "dsl")] pub fn evaluate_output_expression_tui <'a, S> (
|
#[cfg(feature = "dsl")] pub fn evaluate_output_expression_tui <'a, S> (
|
||||||
state: &S, output: &mut TuiOut, expr: impl Expression + 'a
|
state: &S, output: &mut TuiOut, expr: impl Expression + 'a
|
||||||
|
|
@ -577,8 +581,8 @@ pub fn named_key (token: &str) -> Option<KeyCode> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ```
|
/// ```
|
||||||
/// let _ = button_2("", "", true);
|
/// let _ = tengri::button_2("", "", true);
|
||||||
/// let _ = button_2("", "", false);
|
/// let _ = tengri::button_2("", "", false);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn button_2 <'a> (key: impl Content<TuiOut>, label: impl Content<TuiOut>, editing: bool) -> impl Content<TuiOut> {
|
pub fn button_2 <'a> (key: impl Content<TuiOut>, label: impl Content<TuiOut>, editing: bool) -> impl Content<TuiOut> {
|
||||||
Tui::bold(true, Bsp::e(
|
Tui::bold(true, Bsp::e(
|
||||||
|
|
@ -587,8 +591,8 @@ pub fn button_2 <'a> (key: impl Content<TuiOut>, label: impl Content<TuiOut>, ed
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ```
|
/// ```
|
||||||
/// let _ = button_3("", "", "", true);
|
/// let _ = tengri::button_3("", "", "", true);
|
||||||
/// let _ = button_3("", "", "", false);
|
/// let _ = tengri::button_3("", "", "", false);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn button_3 <'a> (
|
pub fn button_3 <'a> (
|
||||||
key: impl Content<TuiOut>, label: impl Content<TuiOut>, value: impl Content<TuiOut>, editing: bool,
|
key: impl Content<TuiOut>, label: impl Content<TuiOut>, value: impl Content<TuiOut>, editing: bool,
|
||||||
|
|
@ -776,6 +780,7 @@ pub(crate) fn width_chars_max (max: u16, text: impl AsRef<str>) -> u16 {
|
||||||
use proptest::{prelude::*, option::of};
|
use proptest::{prelude::*, option::of};
|
||||||
use proptest_derive::Arbitrary;
|
use proptest_derive::Arbitrary;
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
use Direction::*;
|
||||||
|
|
||||||
proptest! {
|
proptest! {
|
||||||
#[test] fn proptest_direction (
|
#[test] fn proptest_direction (
|
||||||
|
|
@ -902,22 +907,18 @@ pub(crate) fn width_chars_max (max: u16, text: impl AsRef<str>) -> u16 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test] fn test_tui_engine () -> Usually<()> {
|
//#[test] fn test_tui_engine () -> Usually<()> {
|
||||||
//use std::sync::{Arc, RwLock};
|
////use std::sync::{Arc, RwLock};
|
||||||
struct TestComponent(String);
|
//struct TestComponent(String);
|
||||||
impl Draw<TuiOut> for TestComponent {
|
//impl Draw<TuiOut> for TestComponent {
|
||||||
fn draw (&self, _to: &mut TuiOut) {
|
//fn draw (&self, _to: &mut TuiOut) {}
|
||||||
}
|
//}
|
||||||
}
|
//impl Handle<TuiIn> for TestComponent {
|
||||||
impl Handle<TuiIn> for TestComponent {
|
//fn handle (&mut self, _from: &TuiIn) -> Perhaps<bool> { Ok(None) }
|
||||||
fn handle (&mut self, _from: &TuiIn) -> Perhaps<bool> {
|
//}
|
||||||
Ok(None)
|
//let engine = Tui::new(Box::<&mut Vec<u8>>::new(vec![0u8;0].as_mut()))?;
|
||||||
}
|
//let state = engine.run(false, &Arc::new(RwLock::new(TestComponent("hello world".into()))))?;
|
||||||
}
|
//state.read().unwrap().exited.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
let mut output = String::new();
|
//Ok(())
|
||||||
let engine = Tui::new(&mut output).run(false, TestComponent("hello world".into()))?;
|
//}
|
||||||
engine.read().unwrap().exited.store(true, std::sync::atomic::Ordering::Relaxed);
|
|
||||||
//engine.run(&state)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -270,15 +270,11 @@ impl<O: Out, D: Draw<O>> Draw<O> for Option<D> {
|
||||||
//}
|
//}
|
||||||
|
|
||||||
impl<O: Out, T: Content<O>, F: Fn()->T> Lazy<O, T, F> {
|
impl<O: Out, T: Content<O>, F: Fn()->T> Lazy<O, T, F> {
|
||||||
pub const fn new (thunk: F) -> Self {
|
pub const fn new (thunk: F) -> Self { Self(thunk, PhantomData) }
|
||||||
Self(thunk, PhantomData)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<O: Out, F: Fn(&mut O)> Thunk<O, F> {
|
impl<O: Out, F: Fn(&mut O)> Thunk<O, F> {
|
||||||
pub const fn new (draw: F) -> Self {
|
pub const fn new (draw: F) -> Self { Self(PhantomData, draw) }
|
||||||
Self(PhantomData, draw)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<O: Out, F: Fn(&mut O)> Layout<O> for Thunk<O, F> {}
|
impl<O: Out, F: Fn(&mut O)> Layout<O> for Thunk<O, F> {}
|
||||||
|
|
@ -672,57 +668,15 @@ impl<Head, Tail> Bsp<Head, Tail> {
|
||||||
|
|
||||||
impl<O: Out, Head: Content<O>, Tail: Content<O>> Draw<O> for Bsp<Head, Tail> {
|
impl<O: Out, Head: Content<O>, Tail: Content<O>> Draw<O> for Bsp<Head, Tail> {
|
||||||
fn draw (&self, to: &mut O) {
|
fn draw (&self, to: &mut O) {
|
||||||
match self.0 {
|
let [a, b, _] = bsp_areas(to.area(), self.0, &self.1, &self.2);
|
||||||
North => { // FIXME
|
|
||||||
let area_1 = self.1.layout(to.area());
|
|
||||||
let area_2 = self.2.layout(XYWH(
|
|
||||||
to.area().x(), to.area().y().plus(area_1.h()),
|
|
||||||
to.area().w(), to.area().h().minus(area_1.h())
|
|
||||||
));
|
|
||||||
to.place_at(area_1, &self.1);
|
|
||||||
to.place_at(area_2, &self.2);
|
|
||||||
},
|
|
||||||
South => {
|
|
||||||
let area_1 = self.1.layout(to.area());
|
|
||||||
let area_2 = self.2.layout(XYWH(
|
|
||||||
to.area().x(), to.area().y().plus(area_1.h()),
|
|
||||||
to.area().w(), to.area().h().minus(area_1.h())
|
|
||||||
));
|
|
||||||
to.place_at(area_1, &self.1);
|
|
||||||
to.place_at(area_2, &self.2);
|
|
||||||
},
|
|
||||||
East => {
|
|
||||||
let area_1 = self.1.layout(to.area());
|
|
||||||
let area_2 = self.2.layout(XYWH(
|
|
||||||
to.area().x().plus(area_1.w()), to.area().y(),
|
|
||||||
to.area().w().minus(area_1.w()), to.area().h()
|
|
||||||
));
|
|
||||||
to.place_at(area_1, &self.1);
|
|
||||||
to.place_at(area_2, &self.2);
|
|
||||||
},
|
|
||||||
Above => {
|
|
||||||
let area_1 = self.1.layout(to.area());
|
|
||||||
let area_2 = self.2.layout(to.area());
|
|
||||||
to.place_at(area_2, &self.2);
|
|
||||||
to.place_at(area_1, &self.1);
|
|
||||||
},
|
|
||||||
Below => {
|
|
||||||
let area_1 = self.1.layout(to.area());
|
|
||||||
let area_2 = self.2.layout(to.area());
|
|
||||||
to.place_at(area_1, &self.1);
|
|
||||||
to.place_at(area_2, &self.2);
|
|
||||||
},
|
|
||||||
_ => todo!("{:?}", self.0)
|
|
||||||
}
|
|
||||||
//let [a, b, _] = bsp_areas(to.area(), self.0, &self.1, &self.2);
|
|
||||||
//panic!("{a:?} {b:?}");
|
//panic!("{a:?} {b:?}");
|
||||||
//if self.0 == Below {
|
if self.0 == Below {
|
||||||
//to.place_at(a, &self.1);
|
to.place_at(a, &self.1);
|
||||||
//to.place_at(b, &self.2);
|
to.place_at(b, &self.2);
|
||||||
//} else {
|
} else {
|
||||||
//to.place_at(b, &self.2);
|
to.place_at(b, &self.2);
|
||||||
//to.place_at(a, &self.1);
|
to.place_at(a, &self.1);
|
||||||
//}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1011,7 +965,7 @@ impl PerfModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tui {
|
impl Tui {
|
||||||
pub fn new (output: Stdout) -> Usually<Self> {
|
pub fn new (output: Box<dyn Write + Send + Sync>) -> Usually<Self> {
|
||||||
let backend = CrosstermBackend::new(output);
|
let backend = CrosstermBackend::new(output);
|
||||||
let Size { width, height } = backend.size()?;
|
let Size { width, height } = backend.size()?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
|
@ -1061,6 +1015,8 @@ impl Input for TuiIn {
|
||||||
type Event = TuiEvent;
|
type Event = TuiEvent;
|
||||||
type Handled = bool;
|
type Handled = bool;
|
||||||
fn event (&self) -> &TuiEvent { &self.event }
|
fn event (&self) -> &TuiEvent { &self.event }
|
||||||
|
}
|
||||||
|
impl Done for TuiIn {
|
||||||
fn done (&self) { self.exited.store(true, Relaxed); }
|
fn done (&self) { self.exited.store(true, Relaxed); }
|
||||||
fn is_done (&self) -> bool { self.exited.fetch_and(true, Relaxed) }
|
fn is_done (&self) -> bool { self.exited.fetch_and(true, Relaxed) }
|
||||||
}
|
}
|
||||||
|
|
@ -1076,6 +1032,11 @@ impl TuiEvent {
|
||||||
Ok(TuiKey::from_dsl(dsl)?.to_crossterm().map(Self))
|
Ok(TuiKey::from_dsl(dsl)?.to_crossterm().map(Self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl From<Event> for TuiEvent {
|
||||||
|
fn from (e: Event) -> Self {
|
||||||
|
Self(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
impl From<char> for TuiEvent {
|
impl From<char> for TuiEvent {
|
||||||
fn from (c: char) -> Self {
|
fn from (c: char) -> Self {
|
||||||
Self(Event::Key(KeyEvent::new(KeyCode::Char(c), KeyModifiers::NONE)))
|
Self(Event::Key(KeyEvent::new(KeyCode::Char(c), KeyModifiers::NONE)))
|
||||||
|
|
|
||||||
|
|
@ -1,55 +1,282 @@
|
||||||
#[cfg(test)] use proptest_derive::Arbitrary;
|
pub use self::logical::*; mod logical {
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
/// The `Tui` struct (the *engine*) implements the
|
/// Thunks can be natural error boundaries!
|
||||||
/// `tengri_input::Input` and `tengri_output::Out` traits.
|
pub struct ErrorBoundary<O: Out, T: Draw<O>>(
|
||||||
/// At launch, the `Tui` engine spawns two threads, the render thread and the input thread.
|
pub std::marker::PhantomData<O>,
|
||||||
/// the application may further spawn other threads. All threads communicate using shared ownership:
|
pub Perhaps<T>
|
||||||
/// `Arc<RwLock<T>>` and `Arc<AtomicT>`. Thus, at launch the engine and application instances are expected to be wrapped in `Arc<RwLock>`.
|
);
|
||||||
pub struct Tui {
|
|
||||||
pub exited: Arc<AtomicBool>,
|
// TODO DOCUMENTME
|
||||||
pub backend: CrosstermBackend<Stdout>,
|
pub struct Lazy<O, T, F>(
|
||||||
pub buffer: Buffer,
|
pub F,
|
||||||
pub area: [u16;4],
|
pub PhantomData<(O, T)>
|
||||||
pub perf: PerfModel,
|
);
|
||||||
|
|
||||||
|
// TODO DOCUMENTME
|
||||||
|
pub struct Thunk<O: Out, F: Fn(&mut O)>(
|
||||||
|
pub PhantomData<O>,
|
||||||
|
pub F
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO DOCUMENTME
|
||||||
|
#[derive(Debug, Default)] pub struct Memo<T, U> {
|
||||||
|
pub value: T,
|
||||||
|
pub view: Arc<RwLock<U>>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub use self::temporal::*; mod temporal {
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone)] pub struct TuiIn {
|
/// Performance counter
|
||||||
/// Input event
|
#[derive(Debug)]
|
||||||
pub event: TuiEvent,
|
pub struct PerfModel {
|
||||||
/// Exit flag
|
pub enabled: bool,
|
||||||
pub exited: Arc<AtomicBool>,
|
|
||||||
|
pub clock: quanta::Clock,
|
||||||
|
// In nanoseconds. Time used by last iteration.
|
||||||
|
pub used: AtomicF64,
|
||||||
|
// In microseconds. Max prescribed time for iteration (frame, chunk...).
|
||||||
|
pub window: AtomicF64,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd)] pub struct TuiEvent(
|
pub use self::spatial::*; mod spatial {
|
||||||
pub Event
|
use crate::*;
|
||||||
);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd)] pub struct TuiKey(
|
// TODO DOCUMENTME
|
||||||
pub Option<KeyCode>,
|
pub struct Bordered<S, W>(pub bool, pub S, pub W);
|
||||||
pub KeyModifiers
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive(Default)] pub struct TuiOut {
|
// TODO DOCUMENTME
|
||||||
pub buffer: Buffer,
|
pub struct Border<S>(pub bool, pub S);
|
||||||
pub area: XYWH<u16>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// TUI buffer sized by `usize` instead of `u16`.
|
/// A binary split or layer.
|
||||||
#[derive(Default)] pub struct BigBuffer {
|
pub struct Bsp<Head, Tail>(
|
||||||
pub width: usize,
|
/// Direction of split
|
||||||
pub height: usize,
|
pub(crate) Direction,
|
||||||
pub content: Vec<Cell>
|
/// First element.
|
||||||
}
|
pub(crate) Head,
|
||||||
|
/// Second element.
|
||||||
|
pub(crate) Tail,
|
||||||
|
);
|
||||||
|
|
||||||
/// A color in OKHSL and RGB representations.
|
/// A point (X, Y).
|
||||||
#[derive(Debug, Default, Copy, Clone, PartialEq)] pub struct ItemColor {
|
///
|
||||||
|
/// ```
|
||||||
|
/// let xy = tengri::XY(0u16, 0);
|
||||||
|
/// ```
|
||||||
|
#[cfg_attr(test, derive(Arbitrary))]
|
||||||
|
#[derive(Copy, Clone, Debug, Default, PartialEq)] pub struct XY<C: Coord>(
|
||||||
|
pub C, pub C
|
||||||
|
);
|
||||||
|
|
||||||
|
/// A size (Width, Height).
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let wh = tengri::WH(0u16, 0);
|
||||||
|
/// ```
|
||||||
|
#[cfg_attr(test, derive(Arbitrary))]
|
||||||
|
#[derive(Copy, Clone, Debug, Default, PartialEq)] pub struct WH<C: Coord>(
|
||||||
|
pub C, pub C
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Point with size.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let xywh = tengri::XYWH(0u16, 0, 0, 0);
|
||||||
|
/// assert_eq!(tengri::XYWH(10u16, 10, 20, 20).center(), tengri::XY(20, 20));
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// * [ ] TODO: anchor field (determines at which corner/side is X0 Y0)
|
||||||
|
///
|
||||||
|
#[cfg_attr(test, derive(Arbitrary))]
|
||||||
|
#[derive(Copy, Clone, Debug, Default, PartialEq)] pub struct XYWH<C: Coord>(
|
||||||
|
pub C, pub C, pub C, pub C
|
||||||
|
);
|
||||||
|
|
||||||
|
/// A cardinal direction.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let direction = tengri::Direction::Above;
|
||||||
|
/// ```
|
||||||
|
#[cfg_attr(test, derive(Arbitrary))]
|
||||||
|
#[derive(Copy, Clone, PartialEq, Debug)] pub enum Direction {
|
||||||
|
North, South, East, West, Above, Below
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 9th of area to place.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let alignment = tengri::Alignment::Center;
|
||||||
|
/// ```
|
||||||
|
#[cfg_attr(test, derive(Arbitrary))]
|
||||||
|
#[derive(Debug, Copy, Clone, Default)] pub enum Alignment {
|
||||||
|
#[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A widget that tracks its rendered width and height.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let measure = tengri::Measure::<tengri::TuiOut>::default();
|
||||||
|
/// ```
|
||||||
|
#[derive(Default)] pub struct Measure<O: Out> {
|
||||||
|
pub __: PhantomData<O>,
|
||||||
|
pub x: Arc<AtomicUsize>,
|
||||||
|
pub y: Arc<AtomicUsize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Show an item only when a condition is true.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// fn test () -> impl tengri::Draw<tengri::TuiOut> {
|
||||||
|
/// tengri::when(true, "Yes")
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub struct When<O, T>(pub bool, pub T, pub PhantomData<O>);
|
||||||
|
pub const fn when<O, T>(condition: bool, content: T) -> When<O, T> {
|
||||||
|
When(condition, content, PhantomData)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Show one item if a condition is true and another if the condition is false.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// fn test () -> impl tengri::Draw<tengri::TuiOut> {
|
||||||
|
/// tengri::either(true, "Yes", "No")
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub struct Either<E, A, B>(pub bool, pub A, pub B, pub PhantomData<E>);
|
||||||
|
pub const fn either<E, A, B>(condition: bool, content_a: A, content_b: B) -> Either<E, A, B> {
|
||||||
|
Either(condition, content_a, content_b, PhantomData)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Increment X and/or Y coordinate.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let pushed = tengri::Push::XY(2, 2, "Hello");
|
||||||
|
/// ```
|
||||||
|
pub enum Push<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
||||||
|
|
||||||
|
/// Decrement X and/or Y coordinate.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let pulled = tengri::Pull::XY(2, 2, "Hello");
|
||||||
|
/// ```
|
||||||
|
pub enum Pull<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
||||||
|
|
||||||
|
/// Set the content to fill the container.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let filled = tengri::Fill::XY("Hello");
|
||||||
|
/// ```
|
||||||
|
pub enum Fill<A> { X(A), Y(A), XY(A) }
|
||||||
|
|
||||||
|
/// Set fixed size for content.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let fixed = tengri::Fixed::XY(3, 5, "Hello"); // 3x5
|
||||||
|
/// ```
|
||||||
|
pub enum Fixed<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
||||||
|
|
||||||
|
/// Set the maximum width and/or height of the content.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let maximum = tengri::Min::XY(3, 5, "Hello"); // 3x1
|
||||||
|
/// ```
|
||||||
|
pub enum Max<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
||||||
|
|
||||||
|
/// Set the minimum width and/or height of the content.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let minimam = tengri::Min::XY(3, 5, "Hello"); // 5x5
|
||||||
|
/// ```
|
||||||
|
pub enum Min<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
||||||
|
|
||||||
|
/// Decrease the width and/or height of the content.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let shrunk = tengri::Shrink::XY(2, 0, "Hello"); // 1x1
|
||||||
|
/// ```
|
||||||
|
pub enum Shrink<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
||||||
|
|
||||||
|
/// Increaase the width and/or height of the content.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let expanded = tengri::Expand::XY(5, 3, "HELLO"); // 15x3
|
||||||
|
/// ```
|
||||||
|
pub enum Expand<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
||||||
|
|
||||||
|
/// Align position of inner area to middle, side, or corner of outer area.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use ::tengri::*;
|
||||||
|
/// let area = XYWH(10u16, 10, 20, 20);
|
||||||
|
/// fn test (area: XYWH<u16>, item: &impl Draw<TuiOut>, expected: [u16;4]) {
|
||||||
|
/// //assert_eq!(Lay::layout(item, area), expected);
|
||||||
|
/// //assert_eq!(Draw::layout(item, area), expected);
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// let four = ||Fixed::XY(4, 4, "");
|
||||||
|
/// test(area, &Align::nw(four()), [10, 10, 4, 4]);
|
||||||
|
/// test(area, &Align::n(four()), [18, 10, 4, 4]);
|
||||||
|
/// test(area, &Align::ne(four()), [26, 10, 4, 4]);
|
||||||
|
/// test(area, &Align::e(four()), [26, 18, 4, 4]);
|
||||||
|
/// test(area, &Align::se(four()), [26, 26, 4, 4]);
|
||||||
|
/// test(area, &Align::s(four()), [18, 26, 4, 4]);
|
||||||
|
/// test(area, &Align::sw(four()), [10, 26, 4, 4]);
|
||||||
|
/// test(area, &Align::w(four()), [10, 18, 4, 4]);
|
||||||
|
///
|
||||||
|
/// let two_by_four = ||Fixed::XY(4, 2, "");
|
||||||
|
/// test(area, &Align::nw(two_by_four()), [10, 10, 4, 2]);
|
||||||
|
/// test(area, &Align::n(two_by_four()), [18, 10, 4, 2]);
|
||||||
|
/// test(area, &Align::ne(two_by_four()), [26, 10, 4, 2]);
|
||||||
|
/// test(area, &Align::e(two_by_four()), [26, 19, 4, 2]);
|
||||||
|
/// test(area, &Align::se(two_by_four()), [26, 28, 4, 2]);
|
||||||
|
/// test(area, &Align::s(two_by_four()), [18, 28, 4, 2]);
|
||||||
|
/// test(area, &Align::sw(two_by_four()), [10, 28, 4, 2]);
|
||||||
|
/// test(area, &Align::w(two_by_four()), [10, 19, 4, 2]);
|
||||||
|
/// ```
|
||||||
|
pub struct Align<T>(pub Alignment, pub T);
|
||||||
|
|
||||||
|
// TODO DOCUMENTME
|
||||||
|
pub enum Pad<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
||||||
|
|
||||||
|
/// TODO DOCUMENTME
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use tengri::{Bounded, XYWH};
|
||||||
|
/// let area = XYWH(0, 0, 0, 0);
|
||||||
|
/// let content = "";
|
||||||
|
/// let bounded: Bounded<tengri::TuiOut, _> = Bounded(area, content);
|
||||||
|
/// ```
|
||||||
|
pub struct Bounded<O: Out, D>(pub XYWH<O::Unit>, pub D);
|
||||||
|
|
||||||
|
/// Draws items from an iterator.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// // FIXME let map = tengri::Map(||[].iter(), |_|{});
|
||||||
|
/// ```
|
||||||
|
pub struct Map<O, A, B, I, F, G>
|
||||||
|
where
|
||||||
|
I: Iterator<Item = A> + Send + Sync,
|
||||||
|
F: Fn() -> I + Send + Sync,
|
||||||
|
{
|
||||||
|
/// Function that returns iterator over stacked components
|
||||||
|
pub get_iter: F,
|
||||||
|
/// Function that returns each stacked component
|
||||||
|
pub get_item: G,
|
||||||
|
|
||||||
|
pub __: PhantomData<(O, B)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A color in OKHSL and RGB representations.
|
||||||
|
#[derive(Debug, Default, Copy, Clone, PartialEq)] pub struct ItemColor {
|
||||||
pub okhsl: Okhsl<f32>,
|
pub okhsl: Okhsl<f32>,
|
||||||
pub rgb: Color,
|
pub rgb: Color,
|
||||||
}
|
}
|
||||||
/// A color in OKHSL and RGB with lighter and darker variants.
|
/// A color in OKHSL and RGB with lighter and darker variants.
|
||||||
#[derive(Debug, Default, Copy, Clone, PartialEq)] pub struct ItemTheme {
|
#[derive(Debug, Default, Copy, Clone, PartialEq)] pub struct ItemTheme {
|
||||||
pub base: ItemColor,
|
pub base: ItemColor,
|
||||||
pub light: ItemColor,
|
pub light: ItemColor,
|
||||||
pub lighter: ItemColor,
|
pub lighter: ItemColor,
|
||||||
|
|
@ -57,295 +284,106 @@ pub struct Tui {
|
||||||
pub dark: ItemColor,
|
pub dark: ItemColor,
|
||||||
pub darker: ItemColor,
|
pub darker: ItemColor,
|
||||||
pub darkest: ItemColor,
|
pub darkest: ItemColor,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Modify<T>(pub bool, pub Modifier, pub T);
|
pub use self::terminal::*; mod terminal {
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
pub struct Styled<T>(pub Option<Style>, pub T);
|
/// The TUI engine.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # fn main () -> tengri::Usually<()> {
|
||||||
|
/// let tui = tengri::Tui::new(Box::new(vec![0u8;0]))?;
|
||||||
|
/// # Ok(()) }
|
||||||
|
/// ```
|
||||||
|
pub struct Tui {
|
||||||
|
pub exited: Arc<AtomicBool>,
|
||||||
|
pub backend: CrosstermBackend<Box<dyn Write + Send + Sync>>,
|
||||||
|
pub buffer: Buffer,
|
||||||
|
pub area: [u16;4],
|
||||||
|
pub perf: PerfModel,
|
||||||
|
}
|
||||||
|
|
||||||
/// Displays an owned [str]-like with fixed maximum width.
|
/// The TUI input event source.
|
||||||
///
|
///
|
||||||
/// Width is computed using [unicode_width].
|
/// ```
|
||||||
pub struct TrimString<T: AsRef<str>>(pub u16, pub T);
|
/// use ::{tengri::{TuiIn, TuiEvent}, std::sync::Arc};
|
||||||
|
/// let tui_in = TuiIn { event: 'f'.into(), exited: Arc::new(false.into()) };
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone)] pub struct TuiIn {
|
||||||
|
/// Input event
|
||||||
|
pub event: TuiEvent,
|
||||||
|
/// Exit flag
|
||||||
|
pub exited: Arc<AtomicBool>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Displays a borrowed [str]-like with fixed maximum width
|
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd)] pub struct TuiEvent(
|
||||||
///
|
pub Event
|
||||||
/// Width is computed using [unicode_width].
|
);
|
||||||
pub struct TrimStringRef<'a, T: AsRef<str>>(pub u16, pub &'a T);
|
|
||||||
|
|
||||||
/// Thunks can be natural error boundaries!
|
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd)] pub struct TuiKey(
|
||||||
pub struct ErrorBoundary<O: Out, T: Draw<O>>(
|
pub Option<KeyCode>,
|
||||||
pub std::marker::PhantomData<O>,
|
pub KeyModifiers
|
||||||
pub Perhaps<T>
|
);
|
||||||
);
|
|
||||||
|
|
||||||
|
/// The TUI output render target.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let tui_out = tengri::TuiOut::default();
|
||||||
|
/// ```
|
||||||
|
#[derive(Default)] pub struct TuiOut {
|
||||||
|
pub buffer: Buffer,
|
||||||
|
pub area: XYWH<u16>,
|
||||||
|
}
|
||||||
|
|
||||||
/// A point (X, Y).
|
/// TUI buffer sized by `usize` instead of `u16`.
|
||||||
///
|
#[derive(Default)] pub struct BigBuffer {
|
||||||
/// ```
|
pub width: usize,
|
||||||
/// let xy = tengri::XY(0u16, 0);
|
pub height: usize,
|
||||||
/// ```
|
pub content: Vec<Cell>
|
||||||
#[cfg_attr(test, derive(Arbitrary))]
|
}
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq)] pub struct XY<C: Coord>(
|
|
||||||
pub C, pub C
|
|
||||||
);
|
|
||||||
|
|
||||||
/// A size (Width, Height).
|
// TODO DOCUMENTME
|
||||||
///
|
pub struct Foreground<Color, Item>(pub Color, pub Item);
|
||||||
/// ```
|
|
||||||
/// let wh = tengri::WH(0u16, 0);
|
|
||||||
/// ```
|
|
||||||
#[cfg_attr(test, derive(Arbitrary))]
|
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq)] pub struct WH<C: Coord>(
|
|
||||||
pub C, pub C
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Point with size.
|
// TODO DOCUMENTME
|
||||||
///
|
pub struct Background<Color, Item>(pub Color, pub Item);
|
||||||
/// ```
|
|
||||||
/// let xywh = tengri::XYWH(0u16, 0, 0, 0);
|
|
||||||
/// assert_eq!(XYWH(10u16, 10, 20, 20).center(), XY(20, 20));
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// * [ ] TODO: anchor field (determines at which corner/side is X0 Y0)
|
|
||||||
///
|
|
||||||
#[cfg_attr(test, derive(Arbitrary))]
|
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq)] pub struct XYWH<C: Coord>(
|
|
||||||
pub C, pub C, pub C, pub C
|
|
||||||
);
|
|
||||||
|
|
||||||
/// A cardinal direction.
|
pub struct Modify<T>(pub bool, pub Modifier, pub T);
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// let direction = tengri::Direction::Above;
|
|
||||||
/// ```
|
|
||||||
#[cfg_attr(test, derive(Arbitrary))]
|
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)] pub enum Direction {
|
|
||||||
North, South, East, West, Above, Below
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 9th of area to place.
|
pub struct Styled<T>(pub Option<Style>, pub T);
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// let alignment = tengri::Alignment::Center;
|
|
||||||
/// ```
|
|
||||||
#[cfg_attr(test, derive(Arbitrary))]
|
|
||||||
#[derive(Debug, Copy, Clone, Default)] pub enum Alignment {
|
|
||||||
#[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A widget that tracks its rendered width and height.
|
/// Displays an owned [str]-like with fixed maximum width.
|
||||||
///
|
///
|
||||||
/// ```
|
/// Width is computed using [unicode_width].
|
||||||
/// let measure = tengri::Measure::<tengri::tui::TuiOut>::default();
|
pub struct TrimString<T: AsRef<str>>(pub u16, pub T);
|
||||||
/// ```
|
|
||||||
#[derive(Default)] pub struct Measure<O: Out> {
|
|
||||||
pub __: PhantomData<O>,
|
|
||||||
pub x: Arc<AtomicUsize>,
|
|
||||||
pub y: Arc<AtomicUsize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Show an item only when a condition is true.
|
/// Displays a borrowed [str]-like with fixed maximum width
|
||||||
///
|
///
|
||||||
/// ```
|
/// Width is computed using [unicode_width].
|
||||||
/// fn test () -> impl tengri::Draw<tengri::tui::TuiOut> {
|
pub struct TrimStringRef<'a, T: AsRef<str>>(pub u16, pub &'a T);
|
||||||
/// tengri::when(true, "Yes")
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub struct When<O, T>(pub bool, pub T, pub PhantomData<O>);
|
|
||||||
pub const fn when<O, T>(condition: bool, content: T) -> When<O, T> {
|
|
||||||
When(condition, content, PhantomData)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Show one item if a condition is true and another if the condition is false.
|
/// A cell that takes up 3 rows on its own,
|
||||||
///
|
/// but stacks, giving (N+1)*2 rows per N cells.
|
||||||
/// ```
|
pub struct Phat<T> {
|
||||||
/// fn test () -> impl tengri::Draw<tengri::tui::TuiOut> {
|
pub width: u16,
|
||||||
/// tengri::either(true, "Yes", "No")
|
pub height: u16,
|
||||||
/// }
|
pub content: T,
|
||||||
/// ```
|
pub colors: [Color;4],
|
||||||
pub struct Either<E, A, B>(pub bool, pub A, pub B, pub PhantomData<E>);
|
}
|
||||||
pub const fn either<E, A, B>(condition: bool, content_a: A, content_b: B) -> Either<E, A, B> {
|
|
||||||
Either(condition, content_a, content_b, PhantomData)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Increment X and/or Y coordinate.
|
/// A three-column layout.
|
||||||
///
|
pub struct Tryptich<A, B, C> {
|
||||||
/// ```
|
|
||||||
/// let pushed = tengri::Push::XY(2, 2, "Hello");
|
|
||||||
/// ```
|
|
||||||
pub enum Push<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
|
||||||
|
|
||||||
/// Decrement X and/or Y coordinate.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// let pulled = tengri::Pull::XY(2, 2, "Hello");
|
|
||||||
/// ```
|
|
||||||
pub enum Pull<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
|
||||||
|
|
||||||
/// Set the content to fill the container.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// let filled = tengri::Fill::XY("Hello");
|
|
||||||
/// ```
|
|
||||||
pub enum Fill<A> { X(A), Y(A), XY(A) }
|
|
||||||
|
|
||||||
/// Set fixed size for content.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// let fixed = tengri::Fixed::XY(3, 5, "Hello"); // 3x5
|
|
||||||
/// ```
|
|
||||||
pub enum Fixed<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
|
||||||
|
|
||||||
/// Set the maximum width and/or height of the content.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// let maximum = tengri::Min::XY(3, 5, "Hello"); // 3x1
|
|
||||||
/// ```
|
|
||||||
pub enum Max<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
|
||||||
|
|
||||||
/// Set the minimum width and/or height of the content.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// let minimam = tengri::Min::XY(3, 5, "Hello"); // 5x5
|
|
||||||
/// ```
|
|
||||||
pub enum Min<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
|
||||||
|
|
||||||
/// Decrease the width and/or height of the content.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// let shrunk = tengri::Shrink::XY(2, 0, "Hello"); // 1x1
|
|
||||||
/// ```
|
|
||||||
pub enum Shrink<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
|
||||||
|
|
||||||
/// Increaase the width and/or height of the content.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// let expanded = tengri::Expand::XY(5, 3, "HELLO"); // 15x3
|
|
||||||
/// ```
|
|
||||||
pub enum Expand<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
|
||||||
|
|
||||||
/// Align position of inner area to middle, side, or corner of outer area.
|
|
||||||
///
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use ::tengri::{output::*, tui::*};
|
|
||||||
/// let area = XYWH(10u16, 10, 20, 20);
|
|
||||||
/// fn test (area: XYWH<u16>, item: &impl Draw<TuiOut>, expected: [u16;4]) {
|
|
||||||
/// //assert_eq!(Lay::layout(item, area), expected);
|
|
||||||
/// //assert_eq!(Draw::layout(item, area), expected);
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// let four = ||Fixed::XY(4, 4, "");
|
|
||||||
/// test(area, &Align::nw(four()), [10, 10, 4, 4]);
|
|
||||||
/// test(area, &Align::n(four()), [18, 10, 4, 4]);
|
|
||||||
/// test(area, &Align::ne(four()), [26, 10, 4, 4]);
|
|
||||||
/// test(area, &Align::e(four()), [26, 18, 4, 4]);
|
|
||||||
/// test(area, &Align::se(four()), [26, 26, 4, 4]);
|
|
||||||
/// test(area, &Align::s(four()), [18, 26, 4, 4]);
|
|
||||||
/// test(area, &Align::sw(four()), [10, 26, 4, 4]);
|
|
||||||
/// test(area, &Align::w(four()), [10, 18, 4, 4]);
|
|
||||||
///
|
|
||||||
/// let two_by_four = ||Fixed::XY(4, 2, "");
|
|
||||||
/// test(area, &Align::nw(two_by_four()), [10, 10, 4, 2]);
|
|
||||||
/// test(area, &Align::n(two_by_four()), [18, 10, 4, 2]);
|
|
||||||
/// test(area, &Align::ne(two_by_four()), [26, 10, 4, 2]);
|
|
||||||
/// test(area, &Align::e(two_by_four()), [26, 19, 4, 2]);
|
|
||||||
/// test(area, &Align::se(two_by_four()), [26, 28, 4, 2]);
|
|
||||||
/// test(area, &Align::s(two_by_four()), [18, 28, 4, 2]);
|
|
||||||
/// test(area, &Align::sw(two_by_four()), [10, 28, 4, 2]);
|
|
||||||
/// test(area, &Align::w(two_by_four()), [10, 19, 4, 2]);
|
|
||||||
/// ```
|
|
||||||
pub struct Align<T>(pub Alignment, pub T);
|
|
||||||
|
|
||||||
// TODO DOCUMENTME
|
|
||||||
pub enum Pad<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
|
||||||
|
|
||||||
/// TODO DOCUMENTME
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use tengri::{Bounded, XYWH};
|
|
||||||
/// let area = XYWH(0, 0, 0, 0);
|
|
||||||
/// let content = "";
|
|
||||||
/// let bounded: Bounded<tengri::tui::TuiOut, _> = Bounded(area, content);
|
|
||||||
/// ```
|
|
||||||
pub struct Bounded<O: Out, D>(pub XYWH<O::Unit>, pub D);
|
|
||||||
|
|
||||||
/// Draws items from an iterator.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// // FIXME let map = tengri::Map(||[].iter(), |_|{});
|
|
||||||
/// ```
|
|
||||||
pub struct Map<O, A, B, I, F, G>
|
|
||||||
where
|
|
||||||
I: Iterator<Item = A> + Send + Sync,
|
|
||||||
F: Fn() -> I + Send + Sync,
|
|
||||||
{
|
|
||||||
/// Function that returns iterator over stacked components
|
|
||||||
pub get_iter: F,
|
|
||||||
/// Function that returns each stacked component
|
|
||||||
pub get_item: G,
|
|
||||||
|
|
||||||
pub __: PhantomData<(O, B)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO DOCUMENTME
|
|
||||||
pub struct Lazy<O, T, F>(
|
|
||||||
pub F,
|
|
||||||
pub PhantomData<(O, T)>
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO DOCUMENTME
|
|
||||||
pub struct Thunk<O: Out, F: Fn(&mut O)>(
|
|
||||||
pub PhantomData<O>,
|
|
||||||
pub F
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO DOCUMENTME
|
|
||||||
#[derive(Debug, Default)] pub struct Memo<T, U> {
|
|
||||||
pub value: T,
|
|
||||||
pub view: Arc<RwLock<U>>
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A binary split or layer.
|
|
||||||
pub struct Bsp<Head, Tail>(
|
|
||||||
/// Direction of split
|
|
||||||
pub(crate) Direction,
|
|
||||||
/// First element.
|
|
||||||
pub(crate) Head,
|
|
||||||
/// Second element.
|
|
||||||
pub(crate) Tail,
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO DOCUMENTME
|
|
||||||
pub struct Bordered<S, W>(pub bool, pub S, pub W);
|
|
||||||
|
|
||||||
// TODO DOCUMENTME
|
|
||||||
pub struct Border<S>(pub bool, pub S);
|
|
||||||
|
|
||||||
// TODO DOCUMENTME
|
|
||||||
pub struct Foreground<Color, Item>(pub Color, pub Item);
|
|
||||||
|
|
||||||
// TODO DOCUMENTME
|
|
||||||
pub struct Background<Color, Item>(pub Color, pub Item);
|
|
||||||
|
|
||||||
// TODO DOCUMENTME
|
|
||||||
pub struct FieldH<Theme, Label, Value>(pub Theme, pub Label, pub Value);
|
|
||||||
|
|
||||||
// TODO DOCUMENTME
|
|
||||||
pub struct FieldV<Theme, Label, Value>(pub Theme, pub Label, pub Value);
|
|
||||||
|
|
||||||
/// A three-column layout.
|
|
||||||
pub struct Tryptich<A, B, C> {
|
|
||||||
pub top: bool,
|
pub top: bool,
|
||||||
pub h: u16,
|
pub h: u16,
|
||||||
pub left: (u16, A),
|
pub left: (u16, A),
|
||||||
pub middle: (u16, B),
|
pub middle: (u16, B),
|
||||||
pub right: (u16, C),
|
pub right: (u16, C),
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
pub struct Field<C, T, U> {
|
pub struct Field<C, T, U> {
|
||||||
pub direction: Direction,
|
pub direction: Direction,
|
||||||
pub label: Option<T>,
|
pub label: Option<T>,
|
||||||
pub label_fg: Option<C>,
|
pub label_fg: Option<C>,
|
||||||
|
|
@ -355,40 +393,26 @@ pub struct Field<C, T, U> {
|
||||||
pub value_fg: Option<C>,
|
pub value_fg: Option<C>,
|
||||||
pub value_bg: Option<C>,
|
pub value_bg: Option<C>,
|
||||||
pub value_align: Option<Direction>,
|
pub value_align: Option<Direction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performance counter
|
// TODO DOCUMENTME
|
||||||
#[derive(Debug)]
|
pub struct FieldH<Theme, Label, Value>(pub Theme, pub Label, pub Value);
|
||||||
pub struct PerfModel {
|
|
||||||
pub enabled: bool,
|
|
||||||
|
|
||||||
pub clock: quanta::Clock,
|
// TODO DOCUMENTME
|
||||||
// In nanoseconds. Time used by last iteration.
|
pub struct FieldV<Theme, Label, Value>(pub Theme, pub Label, pub Value);
|
||||||
pub used: AtomicF64,
|
|
||||||
// In microseconds. Max prescribed time for iteration (frame, chunk...).
|
|
||||||
pub window: AtomicF64,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Repeat a string, e.g. for background
|
/// Repeat a string, e.g. for background
|
||||||
pub enum Repeat<'a> {
|
pub enum Repeat<'a> {
|
||||||
X(&'a str),
|
X(&'a str),
|
||||||
Y(&'a str),
|
Y(&'a str),
|
||||||
XY(&'a str)
|
XY(&'a str)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Scroll indicator
|
/// Scroll indicator
|
||||||
pub enum Scrollbar {
|
pub enum Scrollbar {
|
||||||
/// Horizontal scrollbar
|
/// Horizontal scrollbar
|
||||||
X { offset: usize, length: usize, total: usize, },
|
X { offset: usize, length: usize, total: usize, },
|
||||||
/// Vertical scrollbar
|
/// Vertical scrollbar
|
||||||
Y { offset: usize, length: usize, total: usize, }
|
Y { offset: usize, length: usize, total: usize, }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A cell that takes up 3 rows on its own,
|
|
||||||
/// but stacks, giving (N+1)*2 rows per N cells.
|
|
||||||
pub struct Phat<T> {
|
|
||||||
pub width: u16,
|
|
||||||
pub height: u16,
|
|
||||||
pub content: T,
|
|
||||||
pub colors: [Color;4],
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,72 +1,143 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
/// Source of [Input::Event]s: keyboard, mouse...
|
pub use self::input::*;
|
||||||
///
|
mod input {
|
||||||
/// ```
|
use crate::*;
|
||||||
///
|
|
||||||
/// use crate::*;
|
/// Something that will exit and not resume, e.g. the main input loop.
|
||||||
/// struct TestInput(bool);
|
/// ```
|
||||||
/// enum TestEvent { Test1 }
|
/// use ::tengri::Done;
|
||||||
/// impl Input for TestInput {
|
/// use ::std::sync::atomic::{AtomicBool, Ordering};
|
||||||
/// type Event = TestEvent;
|
/// use Ordering::Relaxed;
|
||||||
/// type Handled = ();
|
///
|
||||||
/// fn event (&self) -> &Self::Event {
|
/// struct Example(AtomicBool);
|
||||||
/// &TestEvent::Test1
|
/// impl Done for Example {
|
||||||
/// }
|
/// fn is_done (&self) -> bool { self.0.load(Relaxed) }
|
||||||
/// fn is_done (&self) -> bool {
|
/// fn done (&self) { self.0.store(true, Relaxed) }
|
||||||
/// self.0
|
/// }
|
||||||
/// }
|
///
|
||||||
/// fn done (&self) {}
|
/// assert!(Example(true.into()).is_done());
|
||||||
/// }
|
/// assert!(!Example(false.into()).is_done());
|
||||||
/// let _ = TestInput(true).event();
|
///
|
||||||
/// assert!(TestInput(true).is_done());
|
/// let state = Example(false.into());
|
||||||
/// assert!(!TestInput(false).is_done());
|
/// while !state.is_done() {
|
||||||
/// Ok(())
|
/// state.done(); // exit immediately
|
||||||
/// ```
|
/// }
|
||||||
pub trait Input: Sized {
|
/// ```
|
||||||
|
pub trait Done {
|
||||||
|
fn is_done (&self) -> bool;
|
||||||
|
fn done (&self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Source of [Input::Event]s: keyboard, mouse...
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use ::tengri::{Done, Input};
|
||||||
|
/// use ::std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
/// use Ordering::Relaxed;
|
||||||
|
///
|
||||||
|
/// struct Example(AtomicBool);
|
||||||
|
/// impl Done for Example {
|
||||||
|
/// fn is_done (&self) -> bool { self.0.load(Relaxed) }
|
||||||
|
/// fn done (&self) { self.0.store(true, Relaxed) }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// enum TestEvent { Test1 }
|
||||||
|
/// impl Input for Example {
|
||||||
|
/// type Event = TestEvent;
|
||||||
|
/// type Handled = ();
|
||||||
|
/// fn event (&self) -> &Self::Event {
|
||||||
|
/// &TestEvent::Test1
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let _ = Example(true.into()).event();
|
||||||
|
/// ```
|
||||||
|
pub trait Input: Done + Sized {
|
||||||
/// Type of input event
|
/// Type of input event
|
||||||
type Event;
|
type Event;
|
||||||
/// Result of handling input
|
/// Result of handling input
|
||||||
type Handled; // TODO: make this an Option<Box dyn Command<Self>> containing the undo
|
type Handled; // TODO: make this an Option<Box dyn Command<Self>> containing the undo
|
||||||
/// Currently handled event
|
/// Currently handled event
|
||||||
fn event (&self) -> &Self::Event;
|
fn event (&self) -> &Self::Event;
|
||||||
/// Whether component should exit
|
}
|
||||||
fn is_done (&self) -> bool;
|
|
||||||
/// Mark component as done
|
|
||||||
fn done (&self);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// State mutation.
|
/// State mutation.
|
||||||
pub trait Command<S>: Send + Sync + Sized {
|
pub trait Command<S>: Send + Sync + Sized {
|
||||||
fn execute (&self, state: &mut S) -> Perhaps<Self>;
|
fn execute (&self, state: &mut S) -> Perhaps<Self>;
|
||||||
fn delegate <T> (&self, state: &mut S, wrap: impl Fn(Self)->T) -> Perhaps<T>
|
fn delegate <T> (&self, state: &mut S, wrap: impl Fn(Self)->T) -> Perhaps<T>
|
||||||
where Self: Sized
|
where Self: Sized
|
||||||
{
|
{
|
||||||
Ok(self.execute(state)?.map(wrap))
|
Ok(self.execute(state)?.map(wrap))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Define a trait an implement it for various mutation-enabled wrapper types. */
|
||||||
|
#[macro_export] macro_rules! flex_trait_mut (
|
||||||
|
($Trait:ident $(<$($A:ident:$T:ident),+>)? {
|
||||||
|
$(fn $fn:ident (&mut $self:ident $(, $arg:ident:$ty:ty)*) -> $ret:ty $body:block)*
|
||||||
|
})=>{
|
||||||
|
pub trait $Trait $(<$($A: $T),+>)? {
|
||||||
|
$(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret $body)*
|
||||||
|
}
|
||||||
|
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for &mut _T_ {
|
||||||
|
$(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { (*$self).$fn($($arg),*) })*
|
||||||
|
}
|
||||||
|
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for Option<_T_> {
|
||||||
|
$(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret {
|
||||||
|
if let Some(this) = $self { this.$fn($($arg),*) } else { Ok(None) }
|
||||||
|
})*
|
||||||
|
}
|
||||||
|
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Mutex<_T_> {
|
||||||
|
$(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { $self.get_mut().unwrap().$fn($($arg),*) })*
|
||||||
|
}
|
||||||
|
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Arc<::std::sync::Mutex<_T_>> {
|
||||||
|
$(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { $self.lock().unwrap().$fn($($arg),*) })*
|
||||||
|
}
|
||||||
|
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::RwLock<_T_> {
|
||||||
|
$(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { $self.write().unwrap().$fn($($arg),*) })*
|
||||||
|
}
|
||||||
|
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Arc<::std::sync::RwLock<_T_>> {
|
||||||
|
$(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { $self.write().unwrap().$fn($($arg),*) })*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
);
|
||||||
|
|
||||||
|
flex_trait_mut!(Handle <E: Input> {
|
||||||
|
fn handle (&mut self, _input: &E) -> Perhaps<E::Handled> {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Drawing target.
|
pub use self::output::*;
|
||||||
///
|
mod output {
|
||||||
/// ```
|
use crate::*;
|
||||||
/// use tengri::output::*;
|
|
||||||
/// struct TestOut(XYWH<u16>);
|
/// Drawing target.
|
||||||
/// impl Out for TestOut {
|
///
|
||||||
/// type Unit = u16;
|
/// ```
|
||||||
/// fn area (&self) -> XYWH<u16> { self.0 }
|
/// use tengri::*;
|
||||||
/// fn area_mut (&mut self) -> &mut XYWH<u16> { &mut self.0 }
|
///
|
||||||
/// fn place_at <T: Draw<Self> + ?Sized> (&mut self, area: XYWH<u16>, _: &T) {
|
/// struct TestOut(XYWH<u16>);
|
||||||
/// println!("place_at: {area:?}");
|
///
|
||||||
/// ()
|
/// impl tengri::Out for TestOut {
|
||||||
/// }
|
/// type Unit = u16;
|
||||||
/// }
|
/// fn area (&self) -> XYWH<u16> { self.0 }
|
||||||
/// impl Draw<TestOut> for String {
|
/// fn area_mut (&mut self) -> &mut XYWH<u16> { &mut self.0 }
|
||||||
/// fn draw (&self, to: &mut TestOut) {
|
/// fn place_at <T: Draw<Self> + ?Sized> (&mut self, area: XYWH<u16>, _: &T) {
|
||||||
/// //to.area_mut().set_w(self.len() as u16);
|
/// println!("place_at: {area:?}");
|
||||||
/// }
|
/// ()
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// }
|
||||||
pub trait Out: Send + Sync + Sized {
|
///
|
||||||
|
/// impl tengri::Draw<TestOut> for String {
|
||||||
|
/// fn draw (&self, to: &mut TestOut) {
|
||||||
|
/// //to.area_mut().set_w(self.len() as u16);
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub trait Out: Send + Sync + Sized {
|
||||||
/// Unit of length
|
/// Unit of length
|
||||||
type Unit: Coord;
|
type Unit: Coord;
|
||||||
/// Current output area
|
/// Current output area
|
||||||
|
|
@ -79,12 +150,12 @@ pub trait Out: Send + Sync + Sized {
|
||||||
#[inline] fn place <'t, T: Content<Self> + ?Sized> (&mut self, content: &'t T) {
|
#[inline] fn place <'t, T: Content<Self> + ?Sized> (&mut self, content: &'t T) {
|
||||||
self.place_at(content.layout(self.area()), content)
|
self.place_at(content.layout(self.area()), content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A numeric type that can be used as coordinate.
|
/// A numeric type that can be used as coordinate.
|
||||||
///
|
///
|
||||||
/// FIXME: Replace this ad-hoc trait with `num` crate.
|
/// FIXME: Replace this ad-hoc trait with `num` crate.
|
||||||
pub trait Coord: Send + Sync + Copy
|
pub trait Coord: Send + Sync + Copy
|
||||||
+ Add<Self, Output=Self>
|
+ Add<Self, Output=Self>
|
||||||
+ Sub<Self, Output=Self>
|
+ Sub<Self, Output=Self>
|
||||||
+ Mul<Self, Output=Self>
|
+ Mul<Self, Output=Self>
|
||||||
|
|
@ -94,7 +165,7 @@ pub trait Coord: Send + Sync + Copy
|
||||||
+ From<u16> + Into<u16>
|
+ From<u16> + Into<u16>
|
||||||
+ Into<usize>
|
+ Into<usize>
|
||||||
+ Into<f64>
|
+ Into<f64>
|
||||||
{
|
{
|
||||||
fn plus (self, other: Self) -> Self;
|
fn plus (self, other: Self) -> Self;
|
||||||
fn minus (self, other: Self) -> Self {
|
fn minus (self, other: Self) -> Self {
|
||||||
if self >= other { self - other } else { 0.into() }
|
if self >= other { self - other } else { 0.into() }
|
||||||
|
|
@ -105,18 +176,18 @@ pub trait Coord: Send + Sync + Copy
|
||||||
fn zero () -> Self {
|
fn zero () -> Self {
|
||||||
0.into()
|
0.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Drawable with dynamic dispatch.
|
/// Drawable with dynamic dispatch.
|
||||||
pub trait Draw<O: Out> {
|
pub trait Draw<O: Out> {
|
||||||
fn draw (&self, to: &mut O);
|
fn draw (&self, to: &mut O);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Outputs combinator.
|
/// Outputs combinator.
|
||||||
pub trait Lay<O: Out>: Sized {}
|
pub trait Lay<O: Out>: Sized {}
|
||||||
|
|
||||||
/// Drawable area of display.
|
/// Drawable area of display.
|
||||||
pub trait Layout<O: Out> {
|
pub trait Layout<O: Out> {
|
||||||
fn layout_x (&self, to: XYWH<O::Unit>) -> O::Unit { to.x() }
|
fn layout_x (&self, to: XYWH<O::Unit>) -> O::Unit { to.x() }
|
||||||
fn layout_y (&self, to: XYWH<O::Unit>) -> O::Unit { to.y() }
|
fn layout_y (&self, to: XYWH<O::Unit>) -> O::Unit { to.y() }
|
||||||
fn layout_w_min (&self, _t: XYWH<O::Unit>) -> O::Unit { 0.into() }
|
fn layout_w_min (&self, _t: XYWH<O::Unit>) -> O::Unit { 0.into() }
|
||||||
|
|
@ -128,33 +199,33 @@ pub trait Layout<O: Out> {
|
||||||
fn layout (&self, to: XYWH<O::Unit>) -> XYWH<O::Unit> {
|
fn layout (&self, to: XYWH<O::Unit>) -> XYWH<O::Unit> {
|
||||||
XYWH(self.layout_x(to), self.layout_y(to), self.layout_w(to), self.layout_h(to))
|
XYWH(self.layout_x(to), self.layout_y(to), self.layout_w(to), self.layout_h(to))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait HasContent<O: Out> {
|
pub trait HasContent<O: Out> {
|
||||||
fn content (&self) -> impl Content<O>;
|
fn content (&self) -> impl Content<O>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO DOCUMENTME
|
// TODO DOCUMENTME
|
||||||
pub trait Content<O: Out>: Draw<O> + Layout<O> {}
|
pub trait Content<O: Out>: Draw<O> + Layout<O> {}
|
||||||
|
|
||||||
// Something that has an origin point (X, Y).
|
// Something that has an origin point (X, Y).
|
||||||
pub trait HasXY<N: Coord> {
|
pub trait HasXY<N: Coord> {
|
||||||
fn x (&self) -> N;
|
fn x (&self) -> N;
|
||||||
fn y (&self) -> N;
|
fn y (&self) -> N;
|
||||||
fn xy (&self) -> XY<N> { XY(self.x(), self.y()) }
|
fn xy (&self) -> XY<N> { XY(self.x(), self.y()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Something that has a size (W, H).
|
// Something that has a size (W, H).
|
||||||
pub trait HasWH<N: Coord> {
|
pub trait HasWH<N: Coord> {
|
||||||
fn w (&self) -> N;
|
fn w (&self) -> N;
|
||||||
fn h (&self) -> N;
|
fn h (&self) -> N;
|
||||||
fn wh (&self) -> WH<N> { WH(self.w(), self.h()) }
|
fn wh (&self) -> WH<N> { WH(self.w(), self.h()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Something that has a 2D bounding box (X, Y, W, H).
|
// Something that has a 2D bounding box (X, Y, W, H).
|
||||||
//
|
//
|
||||||
// FIXME: The other way around?
|
// FIXME: The other way around?
|
||||||
pub trait HasXYWH<N: Coord>: HasXY<N> + HasWH<N> {
|
pub trait HasXYWH<N: Coord>: HasXY<N> + HasWH<N> {
|
||||||
fn x2 (&self) -> N { self.x().plus(self.w()) }
|
fn x2 (&self) -> N { self.x().plus(self.w()) }
|
||||||
fn y2 (&self) -> N { self.y().plus(self.h()) }
|
fn y2 (&self) -> N { self.y().plus(self.h()) }
|
||||||
fn xywh (&self) -> XYWH<N> { XYWH(self.x(), self.y(), self.w(), self.h()) }
|
fn xywh (&self) -> XYWH<N> { XYWH(self.x(), self.y(), self.w(), self.h()) }
|
||||||
|
|
@ -165,32 +236,22 @@ pub trait HasXYWH<N: Coord>: HasXY<N> + HasWH<N> {
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Something that has a [Measure] of its rendered size.
|
// Something that has a [Measure] of its rendered size.
|
||||||
pub trait Measured<O: Out> {
|
pub trait Measured<O: Out> {
|
||||||
fn measure (&self) -> &Measure<O>;
|
fn measure (&self) -> &Measure<O>;
|
||||||
fn measure_width (&self) -> O::Unit { self.measure().w() }
|
fn measure_width (&self) -> O::Unit { self.measure().w() }
|
||||||
fn measure_height (&self) -> O::Unit { self.measure().h() }
|
fn measure_height (&self) -> O::Unit { self.measure().h() }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait HasPerf {
|
pub trait HasPerf {
|
||||||
fn perf (&self) -> &PerfModel;
|
fn perf (&self) -> &PerfModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait TuiDraw = Draw<TuiOut>;
|
pub trait HasColor { fn color (&self) -> ItemColor; }
|
||||||
|
|
||||||
pub trait TuiLayout = Layout<TuiOut>;
|
pub trait BorderStyle: Content<TuiOut> + Copy {
|
||||||
|
|
||||||
pub trait TuiContent = Content<TuiOut>;
|
|
||||||
|
|
||||||
pub trait TuiHandle = Handle<TuiIn>;
|
|
||||||
|
|
||||||
pub trait TuiWidget = TuiDraw + TuiHandle;
|
|
||||||
|
|
||||||
pub trait HasColor { fn color (&self) -> ItemColor; }
|
|
||||||
|
|
||||||
pub trait BorderStyle: Content<TuiOut> + Copy {
|
|
||||||
fn enabled (&self) -> bool;
|
fn enabled (&self) -> bool;
|
||||||
fn enclose (self, w: impl Content<TuiOut>) -> impl Content<TuiOut> {
|
fn enclose (self, w: impl Content<TuiOut>) -> impl Content<TuiOut> {
|
||||||
Bsp::b(Fill::XY(Border(self.enabled(), self)), w)
|
Bsp::b(Fill::XY(Border(self.enabled(), self)), w)
|
||||||
|
|
@ -282,42 +343,15 @@ pub trait BorderStyle: Content<TuiOut> + Copy {
|
||||||
#[inline] fn style_horizontal (&self) -> Option<Style> { self.style() }
|
#[inline] fn style_horizontal (&self) -> Option<Style> { self.style() }
|
||||||
#[inline] fn style_vertical (&self) -> Option<Style> { self.style() }
|
#[inline] fn style_vertical (&self) -> Option<Style> { self.style() }
|
||||||
#[inline] fn style_corners (&self) -> Option<Style> { self.style() }
|
#[inline] fn style_corners (&self) -> Option<Style> { self.style() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Define a trait an implement it for various mutation-enabled wrapper types. */
|
pub use self::tui::*;
|
||||||
#[macro_export] macro_rules! flex_trait_mut (
|
mod tui {
|
||||||
($Trait:ident $(<$($A:ident:$T:ident),+>)? {
|
use crate::*;
|
||||||
$(fn $fn:ident (&mut $self:ident $(, $arg:ident:$ty:ty)*) -> $ret:ty $body:block)*
|
pub trait TuiDraw = Draw<TuiOut>;
|
||||||
})=>{
|
pub trait TuiLayout = Layout<TuiOut>;
|
||||||
pub trait $Trait $(<$($A: $T),+>)? {
|
pub trait TuiContent = Content<TuiOut>;
|
||||||
$(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret $body)*
|
pub trait TuiHandle = Handle<TuiIn>;
|
||||||
}
|
pub trait TuiWidget = TuiDraw + TuiHandle;
|
||||||
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for &mut _T_ {
|
}
|
||||||
$(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { (*$self).$fn($($arg),*) })*
|
|
||||||
}
|
|
||||||
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for Option<_T_> {
|
|
||||||
$(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret {
|
|
||||||
if let Some(this) = $self { this.$fn($($arg),*) } else { Ok(None) }
|
|
||||||
})*
|
|
||||||
}
|
|
||||||
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Mutex<_T_> {
|
|
||||||
$(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { $self.get_mut().unwrap().$fn($($arg),*) })*
|
|
||||||
}
|
|
||||||
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Arc<::std::sync::Mutex<_T_>> {
|
|
||||||
$(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { $self.lock().unwrap().$fn($($arg),*) })*
|
|
||||||
}
|
|
||||||
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::RwLock<_T_> {
|
|
||||||
$(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { $self.write().unwrap().$fn($($arg),*) })*
|
|
||||||
}
|
|
||||||
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Arc<::std::sync::RwLock<_T_>> {
|
|
||||||
$(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { $self.write().unwrap().$fn($($arg),*) })*
|
|
||||||
}
|
|
||||||
};
|
|
||||||
);
|
|
||||||
|
|
||||||
flex_trait_mut!(Handle <E: Input> {
|
|
||||||
fn handle (&mut self, _input: &E) -> Perhaps<E::Handled> {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue