Compare commits

..

14 commits

Author SHA1 Message Date
facile pop culture reference
94c26f06cc update example modes
Some checks failed
/ build (push) Has been cancelled
2026-07-30 22:07:42 +03:00
facile pop culture reference
859b173a1e test: add get_color (bulky!) 2026-07-30 18:05:32 +03:00
facile pop culture reference
8d2445d728 wip: layout doctests 2 2026-07-30 18:01:19 +03:00
facile pop culture reference
cd5b0cc113 wip: doctest layouts 2026-07-30 17:44:40 +03:00
facile pop culture reference
5ca329292f fix warns 2026-07-30 16:38:32 +03:00
facile pop culture reference
81bc0c67a3 bump dizzle 2026-07-30 14:52:41 +03:00
facile pop culture reference
0a92184c44 disable unstable feature flags 2026-07-30 14:52:35 +03:00
facile pop culture reference
8970ae5d5d stylistic 2026-07-30 14:52:22 +03:00
facile pop culture reference
33e135c614 don't use std::iter::Step on Coord 2026-07-30 14:52:02 +03:00
facile pop culture reference
49dcb4f3ba add missing try_to_u8 in example 2026-07-30 14:51:29 +03:00
facile pop culture reference
d9d6340503 fix example names in justfile 2026-07-30 14:51:09 +03:00
facile pop culture reference
25354099fe draw multiline strings 2026-07-28 04:14:56 +03:00
facile pop culture reference
dee3d33453 add ShowSize 2026-07-28 04:14:13 +03:00
facile pop culture reference
815bfe9379 hoist connect_ fns 2026-07-28 04:14:04 +03:00
25 changed files with 569 additions and 406 deletions

View file

@ -29,11 +29,9 @@ doc:
CARGO_INCREMENTAL=0 RUSTFLAGS='-Cinstrument-coverage' RUSTDOCFLAGS='-Cinstrument-coverage' \ CARGO_INCREMENTAL=0 RUSTFLAGS='-Cinstrument-coverage' RUSTDOCFLAGS='-Cinstrument-coverage' \
cargo doc cargo doc
example-tui-00: mode-00:
cargo run --example mode_0 cargo run --example mode_00
mode-01:
example-tui-01: cargo run --example mode_01
cargo run --example mode_1 mode-02:
cargo run --example mode_02
example-tui-02:
cargo run --example mode_2

2
dizzle

@ -1 +1 @@
Subproject commit 4424ef7fc3fd8bfbea1fb55b4922f7a8cfd2a8ef Subproject commit 0f06571f7fc7e5f87aadb82d531726d24bf769e8

View file

@ -1,3 +1,37 @@
//! Mode 0: Direct draw //! Mode 00: Direct draw, direct control
use ::{std::sync::{Arc, RwLock}, ratatui::style::Color, tengri::*};
fn main () {} use ::std::sync::{Arc, RwLock};
use ::crossterm::event::{Event::*, KeyEvent, KeyCode::*};
use ::ratatui::style::Color;
use ::tengri::{*, lang::*};
tui_app!(State {
/** User-controllable value. */
cursor: usize,
});
tui_view!(self: State {
thunk(|to: &mut Tui|{
let cursor = format!("Cursor: {}", self.cursor);
let _ = "DEMO [MODE 00]".align_sw().draw(to);
let _ = ShowSize.align_se().draw(to);
let _ = format!("Cursor: {}", self.cursor).align_c().draw(to);
Ok(Some(to.area()))
})
});
tui_keys!(self: State, input {
Ok(if let Key(KeyEvent { code, .. }) = input.0 {
match code {
Up | Right => {
self.cursor = (self.cursor + 1) % 10;
()
},
Down | Left => {
self.cursor = if self.cursor > 0 { self.cursor - 1 } else { 10 - 1 };
()
},
_ => {}
}
})
});

View file

@ -1,153 +1,63 @@
//! Mode 01 //! Mode 01: Direct view, actions with history
use ::std::sync::{Arc, RwLock}; use ::std::sync::{Arc, RwLock};
use ::crossterm::event::{Event::*, KeyEvent, KeyCode::*}; use ::crossterm::event::{Event::*, KeyEvent, KeyCode::*};
use ::ratatui::style::Color; use ::ratatui::style::Color;
use ::tengri::{*, lang::*}; use ::tengri::{*, lang::*};
use itertools::Itertools;
tui_app!(State { tui_app!(State {
/** Command history (undo/redo). */ /** Command history (undo/redo). */
history: Vec<Action>, history: Vec<Action>,
/** User-controllable value. */ /** User-controllable value. */
cursor: usize, cursor: usize,
/** Rendered window size. */
size: Sizer,
}); });
tui_keys!(self: State, input { tui_keys!(self: State, input {
Ok(if let Key(KeyEvent { code, .. }) = input.0 { Ok(if let Key(KeyEvent { code, .. }) = input.0 {
match code { match code {
Up | Right => { self.next()?.map(|x|self.history.push(x)); }, Down | Right => Action::Next,
Down | Left => { self.prev()?.map(|x|self.history.push(x)); }, Up | Left => Action::Prev,
_ => {} _ => { return Ok(()) }
} }
.apply(self)?
.map(|x|self.history.push(x));
}) })
}); });
tui_view!(self: State { tui_view!(self: State {
let index = self.cursor + 1; let title = "Demo Mode 00";
let wh = (self.size.w(), self.size.h()); let items = self.history.iter().take(10).map(|x|format!("{x:?}")).join("\n");
let src = VIEWS.get(self.cursor).unwrap_or(&""); let history = format!("History: {}\n{items}", self.history.len());
let heading = format!("State {}/{} in {:?}", index, VIEWS.len(), &wh); let cursor = format!("Cursor: {}", self.cursor);
let title = bg(Color::Rgb(60, 10, 10), heading.align_n().push_y(1)); north(
let code = bg(Color::Rgb(10, 60, 10), format!("{}", src).align_n().push_y(2)); east(title.align_sw(), ShowSize.align_se()),
let widget = thunk(move|to: &mut Tui|self.interpret(to, &src)); east(history.align_c(), cursor.align_c()),
self.size.of(south(title, north(code, widget))) )
}); });
impl Interpret<Tui, Color> for State {
fn interpret_expr (&self, to: &mut Tui, expr: &impl Language) -> Usually<Color> { #[derive(Debug)] enum Action {
let expr = expr.expr()?;
match expr.head()? {
Some("g") if let Some(tail) = expr.tail()? => {
Color::new_g(tail.head()?, try_to_u8)
},
Some("rgb") if let Some(tail) = expr.tail()? => {
Color::new_rgb(tail.head()?)
},
_ => Err(format!("not a color").into())
}
}
}
impl Interpret<Tui, Option<XYWH<u16>>> for State {
fn interpret_word (&self, to: &mut Tui, sym: &impl Language) -> Perhaps<XYWH<u16>> {
match sym.src()? {
Some(":foo") => "foo".draw(to),
Some(":bar") => "bar".draw(to),
Some(":foobar") => "FOOBAR".draw(to),
_ => todo!()
}
}
fn interpret_expr (&self, to: &mut Tui, src: &impl Expression) -> Perhaps<XYWH<u16>> {
Ok(Some(if let Some(area) = eval_view(self, to, src)? {
area
} else if let Some(area) = eval_view_tui(self, to, src)? {
area
} else {
return Err(format!("App::interpret_expr: unexpected: {src:?}").into())
}))
}
}
impl State {
fn next (&mut self) -> Perhaps<Action> {
self.cursor = (self.cursor + 1) % VIEWS.len();
Ok(Some(Action::Prev))
}
fn prev (&mut self) -> Perhaps<Action> {
self.cursor = if self.cursor > 0 { self.cursor - 1 } else { VIEWS.len() - 1 };
Ok(Some(Action::Next))
}
}
#[derive(Debug)]
enum Action {
/** Increment cursor */ Next, /** Increment cursor */ Next,
/** Decrement cursor */ Prev, /** Decrement cursor */ Prev,
} }
impl Action { impl Action {
fn eval (&self, state: &mut State) -> Perhaps<Self> { fn apply (&self, state: &mut State) -> Perhaps<Self> {
use Action::*; use Action::*;
match self { Next => state.next(), Prev => state.prev(), } match self {
Next => state.next(),
Prev => state.prev(),
}
}
}
impl State {
fn next (&mut self) -> Perhaps<Action> {
self.cursor = (self.cursor + 1) % 10;
Ok(Some(Action::Prev))
}
fn prev (&mut self) -> Perhaps<Action> {
self.cursor = if self.cursor > 0 { self.cursor - 1 } else { 10 - 1 };
Ok(Some(Action::Next))
} }
} }
const VIEWS: &'static [&'static str] = &[
stringify! { :foobar },
stringify! { (bg (g 8) :foobar) },
stringify! { (fill/xy :foobar) },
stringify! { (bsp/s :foo :bar) },
stringify! { (fixed/xy 20 10 :foobar) },
stringify! { (bsp/s (fixed/xy 5 6 :foo) (fixed/xy 7 8 :bar)) },
stringify! { (bsp/e (fixed/xy 5 6 :foo) (fixed/xy 7 8 :bar)) },
stringify! { (bsp/n (fixed/xy 5 6 :foo) (fixed/xy 7 8 :bar)) },
stringify! { (bsp/w (fixed/xy 5 6 :foo) (fixed/xy 7 8 :bar)) },
stringify! { (bsp/a (fixed/xy 5 6 :foo) (fixed/xy 7 8 :bar)) },
stringify! { (bsp/b (fixed/xy 5 6 :foo) (fixed/xy 7 8 :bar)) },
stringify! {
(bsp/s
(bsp/e (align/nw (fixed/xy 5 3 :foo))
(bsp/e (align/n (fixed/xy 5 3 :foo))
(align/ne (fixed/xy 5 3 :foo))))
(bsp/s
(bsp/e (align/w (fixed/xy 5 3 :foo))
(bsp/e (align/c (fixed/xy 5 3 :foo))
(align/e (fixed/xy 5 3 :foo))))
(bsp/e (align/sw (fixed/xy 5 3 :foo))
(bsp/e (align/s (fixed/xy 5 3 :foo))
(align/se (fixed/xy 5 3 :foo))))))
},
stringify! {
(bsp/s
(bsp/e (fixed/xy 8 5 (align/nw :foo))
(bsp/e (fixed/xy 8 5 (align/n :foo))
(fixed/xy 8 5 (align/ne :foo))))
(bsp/s
(bsp/e (fixed/xy 8 5 (align/w :foo))
(bsp/e (fixed/xy 8 5 (align/c :foo))
(fixed/xy 8 5 (align/e :foo))))
(bsp/e (fixed/xy 8 5 (align/sw :foo))
(bsp/e (fixed/xy 8 5 (align/s :foo))
(fixed/xy 8 5 (align/se :foo))))))
},
stringify! {
(bsp/s
(bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/nw :foo)))
(bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/n :foo)))
(grow/xy 1 1 (fixed/xy 8 5 (align/ne :foo)))))
(bsp/s
(bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/w :foo)))
(bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/c :foo)))
(grow/xy 1 1 (fixed/xy 8 5 (align/e :foo)))))
(bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/sw :foo)))
(bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/s :foo)))
(grow/xy 1 1 (fixed/xy 8 5 (align/se :foo)))))))
},
stringify! { :map-e },
stringify! { (align/c :map-e) },
stringify! { :map-s },
stringify! { (align/c :map-s) },
stringify! {
(align/c (bg/behind :bg0 (margin/xy 1 1 (col
(bg/behind :bg1 (border/around :border1 (margin/xy 2 1 :label1)))
(bg/behind :bg2 (border/around :border2 (margin/xy 4 2 :label2)))
(bg/behind :bg3 (border/around :border3 (margin/xy 6 3 :label3)))))))
},
];
//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_from!(Action: |input: &TuiIn| todo!());
//fn draw_example (state: &State, to: &mut Tui) {}

View file

@ -1,107 +1,162 @@
//! Mode 02 //! Mode 02: Inline Dizzle config
use ::{std::sync::{Arc, RwLock}, ratatui::style::Color, tengri::*};
fn main () {}
//#[tengri_proc::expose] use ::std::sync::{Arc, RwLock};
//impl Example { use ::crossterm::event::{Event::*, KeyEvent, KeyCode::*};
//fn _todo_u16_stub (&self) -> u16 { todo!() } use ::ratatui::style::Color;
//fn _todo_bool_stub (&self) -> bool { todo!() } use ::tengri::{*, lang::*};
//fn _todo_usize_stub (&self) -> usize { todo!() } tui_app!(State {
////[bool] => {} /** Command history (undo/redo). */
////[u16] => {} history: Vec<Action>,
////[usize] => {} /** User-controllable value. */
//} cursor: usize,
/** Rendered window size. */
//#[tengri_proc::view(TuiOut)] size: Sizer,
//impl Example { });
//pub fn title (&self) -> impl Content<TuiOut> + use<'_> { tui_keys!(self: State, input {
//Tui::bg(Color::Rgb(60, 10, 10), Push::y(1, Align::n(format!("Example {}/{}:", self.0 + 1, VIEWS.len())))).boxed() Ok(if let Key(KeyEvent { code, .. }) = input.0 {
//} match code {
//pub fn code (&self) -> impl Content<TuiOut> + use<'_> { Up | Right => { self.next()?.map(|x|self.history.push(x)); },
//Tui::bg(Color::Rgb(10, 60, 10), Push::y(2, Align::n(format!("{}", VIEWS[self.0])))).boxed() Down | Left => { self.prev()?.map(|x|self.history.push(x)); },
//} _ => {}
//pub fn hello (&self) -> impl Content<TuiOut> + use<'_> { }
//Tui::bg(Color::Rgb(10, 100, 10), "Hello").boxed() })
//} });
//pub fn world (&self) -> impl Content<TuiOut> + use<'_> { tui_view!(self: State {
//Tui::bg(Color::Rgb(100, 10, 10), "world").boxed() let index = self.cursor + 1;
//} let wh = (self.size.w(), self.size.h());
//pub fn hello_world (&self) -> impl Content<TuiOut> + use<'_> { let src = VIEWS.get(self.cursor).unwrap_or(&"");
//"Hello world!".boxed() let heading = format!("State {}/{} in {:?}", index, VIEWS.len(), &wh);
//} let title = bg(Color::Rgb(60, 10, 10), heading.align_n().push_y(1));
//pub fn map_e (&self) -> impl Content<TuiOut> + use<'_> { let code = bg(Color::Rgb(10, 60, 10), format!("{}", src).align_n().push_y(2));
//Map::east(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed() let widget = thunk(move|to: &mut Tui|self.interpret(to, &src));
//} self.size.of(south(title, north(code, widget)))
//pub fn map_s (&self) -> impl Content<TuiOut> + use<'_> { });
//Map::south(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed() impl Interpret<Tui, Color> for State {
//} fn interpret_expr (&self, to: &mut Tui, expr: &impl Language) -> Usually<Color> {
//} let expr = expr.expr()?;
match expr.head()? {
//fn content (&self) -> dyn Draw<Engine = Tui> { Some("g") if let Some(tail) = expr.tail()? => {
//let border_style = Style::default().fg(Color::Rgb(0,0,0)); Color::new_g(tail.head()?, try_to_u8)
//Align::Center(Layers::new(move|add|{ },
Some("rgb") if let Some(tail) = expr.tail()? => {
//add(&Background(Color::Rgb(0,128,128)))?; Color::new_rgb(tail.head()?, try_to_u8)
},
//add(&Margin::XY(1, 1, Stack::down(|add|{ _ => Err(format!("not a color").into())
}
//add(&Layers::new(|add|{ }
//add(&Background(Color::Rgb(128,96,0)))?; }
//add(&Border(Square(border_style)))?; fn try_to_u8 (src: Perhaps<&str>) -> Perhaps<u8> {
//add(&Margin::XY(2, 1, "..."))?; use std::str::FromStr;
//Ok(()) if let Some(src) = src? {
//}).debug())?; Ok(Some(u8::from_str(src)?))
} else {
//add(&Layers::new(|add|{ Ok(None)
//add(&Background(Color::Rgb(128,64,0)))?; }
//add(&Border(Lozenge(border_style)))?; }
//add(&Margin::XY(4, 2, "---"))?; impl Interpret<Tui, Option<XYWH<u16>>> for State {
//Ok(()) fn interpret_word (&self, to: &mut Tui, sym: &impl Language) -> Perhaps<XYWH<u16>> {
//}).debug())?; match sym.src()? {
Some(":foo") => "foo".draw(to),
//add(&Layers::new(|add|{ Some(":bar") => "bar".draw(to),
//add(&Background(Color::Rgb(96,64,0)))?; Some(":foobar") => "FOOBAR".draw(to),
//add(&Border(SquareBold(border_style)))?; _ => todo!()
//add(&Margin::XY(6, 3, "~~~"))?; }
//Ok(()) }
//}).debug())?; fn interpret_expr (&self, to: &mut Tui, src: &impl Expression) -> Perhaps<XYWH<u16>> {
Ok(Some(if let Some(area) = eval_view(self, to, src)? {
//Ok(()) area
//})).debug())?; } else if let Some(area) = eval_view_tui(self, to, src)? {
area
//Ok(()) } else {
return Err(format!("App::interpret_expr: unexpected: {src:?}").into())
//})) }))
////Align::Center(Margin::X(1, Layers::new(|add|{ }
////add(&Background(Color::Rgb(128,0,0)))?; }
////add(&Stack::down(|add|{ impl State {
////add(&Margin::Y(1, Layers::new(|add|{ fn next (&mut self) -> Perhaps<Action> {
////add(&Background(Color::Rgb(0,128,0)))?; self.cursor = (self.cursor + 1) % VIEWS.len();
////add(&Align::Center("12345"))?; Ok(Some(Action::Prev))
////add(&Align::Center("FOO")) }
////})))?; fn prev (&mut self) -> Perhaps<Action> {
////add(&Margin::XY(1, 1, Layers::new(|add|{ self.cursor = if self.cursor > 0 { self.cursor - 1 } else { VIEWS.len() - 1 };
////add(&Align::Center("1234567"))?; Ok(Some(Action::Next))
////add(&Align::Center("BAR"))?; }
////add(&Background(Color::Rgb(0,0,128))) }
////}))) #[derive(Debug)]
////})) enum Action {
////}))) /** Increment cursor */ Next,
/** Decrement cursor */ Prev,
////Align::Y(Layers::new(|add|{ }
////add(&Background(Color::Rgb(128,0,0)))?; impl Action {
////add(&Margin::X(1, Align::Center(Stack::down(|add|{ fn eval (&self, state: &mut State) -> Perhaps<Self> {
////add(&Align::X(Margin::Y(1, Layers::new(|add|{ use Action::*;
////add(&Background(Color::Rgb(0,128,0)))?; match self { Next => state.next(), Prev => state.prev(), }
////add(&Align::Center("12345"))?; }
////add(&Align::Center("FOO")) }
////})))?; const VIEWS: &'static [&'static str] = &[
////add(&Margin::XY(1, 1, Layers::new(|add|{ stringify! { :foobar },
////add(&Align::Center("1234567"))?; stringify! { (bg (g 8) :foobar) },
////add(&Align::Center("BAR"))?; stringify! { (fill/xy :foobar) },
////add(&Background(Color::Rgb(0,0,128))) stringify! { (bsp/s :foo :bar) },
////})))?; stringify! { (fixed/xy 20 10 :foobar) },
////Ok(()) stringify! { (bsp/s (fixed/xy 5 6 :foo) (fixed/xy 7 8 :bar)) },
////}))))) stringify! { (bsp/e (fixed/xy 5 6 :foo) (fixed/xy 7 8 :bar)) },
////})) stringify! { (bsp/n (fixed/xy 5 6 :foo) (fixed/xy 7 8 :bar)) },
//} stringify! { (bsp/w (fixed/xy 5 6 :foo) (fixed/xy 7 8 :bar)) },
stringify! { (bsp/a (fixed/xy 5 6 :foo) (fixed/xy 7 8 :bar)) },
stringify! { (bsp/b (fixed/xy 5 6 :foo) (fixed/xy 7 8 :bar)) },
stringify! {
(bsp/s
(bsp/e (align/nw (fixed/xy 5 3 :foo))
(bsp/e (align/n (fixed/xy 5 3 :foo))
(align/ne (fixed/xy 5 3 :foo))))
(bsp/s
(bsp/e (align/w (fixed/xy 5 3 :foo))
(bsp/e (align/c (fixed/xy 5 3 :foo))
(align/e (fixed/xy 5 3 :foo))))
(bsp/e (align/sw (fixed/xy 5 3 :foo))
(bsp/e (align/s (fixed/xy 5 3 :foo))
(align/se (fixed/xy 5 3 :foo))))))
},
stringify! {
(bsp/s
(bsp/e (fixed/xy 8 5 (align/nw :foo))
(bsp/e (fixed/xy 8 5 (align/n :foo))
(fixed/xy 8 5 (align/ne :foo))))
(bsp/s
(bsp/e (fixed/xy 8 5 (align/w :foo))
(bsp/e (fixed/xy 8 5 (align/c :foo))
(fixed/xy 8 5 (align/e :foo))))
(bsp/e (fixed/xy 8 5 (align/sw :foo))
(bsp/e (fixed/xy 8 5 (align/s :foo))
(fixed/xy 8 5 (align/se :foo))))))
},
stringify! {
(bsp/s
(bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/nw :foo)))
(bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/n :foo)))
(grow/xy 1 1 (fixed/xy 8 5 (align/ne :foo)))))
(bsp/s
(bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/w :foo)))
(bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/c :foo)))
(grow/xy 1 1 (fixed/xy 8 5 (align/e :foo)))))
(bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/sw :foo)))
(bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/s :foo)))
(grow/xy 1 1 (fixed/xy 8 5 (align/se :foo)))))))
},
stringify! { :map-e },
stringify! { (align/c :map-e) },
stringify! { :map-s },
stringify! { (align/c :map-s) },
stringify! {
(align/c (bg/behind :bg0 (margin/xy 1 1 (col
(bg/behind :bg1 (border/around :border1 (margin/xy 2 1 :label1)))
(bg/behind :bg2 (border/around :border2 (margin/xy 4 2 :label2)))
(bg/behind :bg3 (border/around :border3 (margin/xy 6 3 :label3)))))))
},
];
//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_from!(Action: |input: &TuiIn| todo!());
//fn draw_example (state: &State, to: &mut Tui) {}

108
examples/mode_03.rs Normal file
View file

@ -0,0 +1,108 @@
//! Mode 03: Hot reloaded Dizzle config
use ::{std::sync::{Arc, RwLock}, ratatui::style::Color, tengri::*};
fn main () {}
//#[tengri_proc::expose]
//impl Example {
//fn _todo_u16_stub (&self) -> u16 { todo!() }
//fn _todo_bool_stub (&self) -> bool { todo!() }
//fn _todo_usize_stub (&self) -> usize { todo!() }
////[bool] => {}
////[u16] => {}
////[usize] => {}
//}
//#[tengri_proc::view(TuiOut)]
//impl Example {
//pub fn title (&self) -> impl Content<TuiOut> + use<'_> {
//Tui::bg(Color::Rgb(60, 10, 10), Push::y(1, Align::n(format!("Example {}/{}:", self.0 + 1, VIEWS.len())))).boxed()
//}
//pub fn code (&self) -> impl Content<TuiOut> + use<'_> {
//Tui::bg(Color::Rgb(10, 60, 10), Push::y(2, Align::n(format!("{}", VIEWS[self.0])))).boxed()
//}
//pub fn hello (&self) -> impl Content<TuiOut> + use<'_> {
//Tui::bg(Color::Rgb(10, 100, 10), "Hello").boxed()
//}
//pub fn world (&self) -> impl Content<TuiOut> + use<'_> {
//Tui::bg(Color::Rgb(100, 10, 10), "world").boxed()
//}
//pub fn hello_world (&self) -> impl Content<TuiOut> + use<'_> {
//"Hello world!".boxed()
//}
//pub fn map_e (&self) -> impl Content<TuiOut> + use<'_> {
//Map::east(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed()
//}
//pub fn map_s (&self) -> impl Content<TuiOut> + use<'_> {
//Map::south(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed()
//}
//}
//fn content (&self) -> dyn Draw<Engine = Tui> {
//let border_style = Style::default().fg(Color::Rgb(0,0,0));
//Align::Center(Layers::new(move|add|{
//add(&Background(Color::Rgb(0,128,128)))?;
//add(&Margin::XY(1, 1, Stack::down(|add|{
//add(&Layers::new(|add|{
//add(&Background(Color::Rgb(128,96,0)))?;
//add(&Border(Square(border_style)))?;
//add(&Margin::XY(2, 1, "..."))?;
//Ok(())
//}).debug())?;
//add(&Layers::new(|add|{
//add(&Background(Color::Rgb(128,64,0)))?;
//add(&Border(Lozenge(border_style)))?;
//add(&Margin::XY(4, 2, "---"))?;
//Ok(())
//}).debug())?;
//add(&Layers::new(|add|{
//add(&Background(Color::Rgb(96,64,0)))?;
//add(&Border(SquareBold(border_style)))?;
//add(&Margin::XY(6, 3, "~~~"))?;
//Ok(())
//}).debug())?;
//Ok(())
//})).debug())?;
//Ok(())
//}))
////Align::Center(Margin::X(1, Layers::new(|add|{
////add(&Background(Color::Rgb(128,0,0)))?;
////add(&Stack::down(|add|{
////add(&Margin::Y(1, Layers::new(|add|{
////add(&Background(Color::Rgb(0,128,0)))?;
////add(&Align::Center("12345"))?;
////add(&Align::Center("FOO"))
////})))?;
////add(&Margin::XY(1, 1, Layers::new(|add|{
////add(&Align::Center("1234567"))?;
////add(&Align::Center("BAR"))?;
////add(&Background(Color::Rgb(0,0,128)))
////})))
////}))
////})))
////Align::Y(Layers::new(|add|{
////add(&Background(Color::Rgb(128,0,0)))?;
////add(&Margin::X(1, Align::Center(Stack::down(|add|{
////add(&Align::X(Margin::Y(1, Layers::new(|add|{
////add(&Background(Color::Rgb(0,128,0)))?;
////add(&Align::Center("12345"))?;
////add(&Align::Center("FOO"))
////})))?;
////add(&Margin::XY(1, 1, Layers::new(|add|{
////add(&Align::Center("1234567"))?;
////add(&Align::Center("BAR"))?;
////add(&Background(Color::Rgb(0,0,128)))
////})))?;
////Ok(())
////})))))
////}))
//}

View file

@ -13,16 +13,20 @@ use crate::*;
/// } /// }
/// impl Screen for TestOut { /// impl Screen for TestOut {
/// type Unit = u16; /// type Unit = u16;
/// fn show (&mut self, _: impl Draw<Self>) -> Perhaps<XYWH<u16>> /// fn show (&mut self, _: impl Draw<Self>) -> Perhaps<XYWH<u16>> {
/// { println!("placed"); Ok(None) } /// println!("placed");
/// fn area (&self) -> XYWH<Self::Unit> /// Ok(None)
/// { Default::default() } /// }
/// fn area (&self) -> XYWH<Self::Unit> {
/// Default::default()
/// }
/// fn clip <T> ( /// fn clip <T> (
/// &mut self, /// &mut self,
/// area: impl Into<Option<XYWH<u16>>>, /// area: impl Into<Option<XYWH<u16>>>,
/// draw: impl FnOnce(&mut Self)->T /// draw: impl FnOnce(&mut Self)->T
/// ) -> T /// ) -> T {
/// { draw(self } /// draw(self)
/// }
/// } /// }
/// ///
/// impl_draw!(|self: String, to: TestOut|{ /// impl_draw!(|self: String, to: TestOut|{
@ -150,7 +154,6 @@ features! {
layout, layout,
lrtb, lrtb,
sizer, sizer,
space,
split, split,
thunk, thunk,
xywh xywh

View file

@ -22,7 +22,7 @@ pub trait Coord: Send + Sync + Copy
+ From<u16> + Into<u16> + From<u16> + Into<u16>
+ Into<usize> + Into<usize>
+ Into<f64> + Into<f64>
+ std::iter::Step //+ std::iter::Step
{ {
/// Zero in own type. /// Zero in own type.
fn zero () -> Self { 0.into() } fn zero () -> Self { 0.into() }

View file

@ -1,3 +1,5 @@
#![allow(unused)]
use crate::*; use crate::*;
impl<S: Screen, T: Draw<S>> Layout<S> for T {} impl<S: Screen, T: Draw<S>> Layout<S> for T {}
@ -158,10 +160,14 @@ pub trait Layout<S: Screen>: Draw<S> + Sized {
/// Use whole drawing area along one or both axes. /// Use whole drawing area along one or both axes.
/// ///
/// ``` /// ```
/// use tengri::Layout; /// # fn doctest_layout_full () -> Result<(), Box<dyn std::error::Error>> {
/// let _ = "".full_w(); /// use tengri::{Layout, Draw, XYWH};
/// let _ = "".full_h(); /// let area = XYWH(0u16, 0, 80, 25);
/// let _ = "".full_wh(); /// assert_eq!("1".layout(area)?, Some(XYWH(0u16, 0, 1, 1)));
/// assert_eq!("1".full_w().layout(area)?, Some(XYWH(0u16, 0, 80, 1)));
/// assert_eq!("1".full_h().layout(area)?, Some(XYWH(0u16, 0, 1, 25)));
/// assert_eq!("1".full_wh().layout(area)?, Some(XYWH(0u16, 0, 80, 25)));
/// # Ok(()) }
/// ``` /// ```
pub enum Full<T: Screen, I: Draw<T>> { pub enum Full<T: Screen, I: Draw<T>> {
__(PhantomData<T>), __(PhantomData<T>),
@ -169,7 +175,6 @@ pub enum Full<T: Screen, I: Draw<T>> {
H(I), H(I),
WH(I), WH(I),
} }
impl_draw!(<T: Screen, I: Draw<T>,>|self: Full<T, I>, to: T|{ impl_draw!(<T: Screen, I: Draw<T>,>|self: Full<T, I>, to: T|{
let XYWH(x0, y0, w0, h0) = to.area(); let XYWH(x0, y0, w0, h0) = to.area();
match self { match self {
@ -195,10 +200,14 @@ impl_draw!(<T: Screen, I: Draw<T>,>|self: Full<T, I>, to: T|{
/// Move content in the positive direction of one or both axes. /// Move content in the positive direction of one or both axes.
/// ///
/// ``` /// ```
/// use tengri::Layout; /// # fn doctest_layout_push () -> Result<(), Box<dyn std::error::Error>> {
/// let _ = "".push_x(1); /// use tengri::{Layout, Draw, XYWH};
/// let _ = "".push_y(1); /// let area = XYWH(0u16, 0, 80, 25);
/// let _ = "".push_xy(1, 1); /// assert_eq!("1".layout(area)?, Some(XYWH(0u16, 0, 1, 1)));
/// assert_eq!("1".push_x(1).layout(area)?, Some(XYWH(1u16, 0, 1, 1)));
/// assert_eq!("1".push_y(1).layout(area)?, Some(XYWH(0u16, 1, 1, 1)));
/// assert_eq!("1".push_xy(1, 1).layout(area)?, Some(XYWH(1u16, 1, 1, 1)));
/// # Ok(()) }
/// ``` /// ```
pub enum Push<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> { pub enum Push<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
__(PhantomData<T>), __(PhantomData<T>),
@ -206,7 +215,6 @@ pub enum Push<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
Y(I, X), Y(I, X),
XY(I, X, X), XY(I, X, X),
} }
impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Push<T, I, X>, to: T|{ impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Push<T, I, X>, to: T|{
match self { match self {
Self::__(_) => unreachable!(), Self::__(_) => unreachable!(),
@ -232,10 +240,14 @@ impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Push<T, I, X
/// Move content in the negative direction of one or both axes. /// Move content in the negative direction of one or both axes.
/// ///
/// ``` /// ```
/// use tengri::Layout; /// # fn doctest_layout_pull () -> Result<(), Box<dyn std::error::Error>> {
/// let _ = "".pull_x(1); /// use tengri::{Layout, Draw, XYWH};
/// let _ = "".pull_y(1); /// let area = XYWH(1u16, 1, 80, 25);
/// let _ = "".pull_xy(1, 1); /// assert_eq!("1".layout(area)?, Some(XYWH(0u16, 0, 1, 1)));
/// assert_eq!("1".pull_x(1).layout(area)?, Some(XYWH(0u16, 1, 1, 1)));
/// assert_eq!("1".pull_y(1).layout(area)?, Some(XYWH(1u16, 0, 1, 1)));
/// assert_eq!("1".pull_xy(1, 1).layout(area)?, Some(XYWH(0u16, 0, 1, 1)));
/// # Ok(()) }
/// ``` /// ```
pub enum Pull<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> { pub enum Pull<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
__(PhantomData<T>), __(PhantomData<T>),
@ -243,7 +255,6 @@ pub enum Pull<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
Y(I, X), Y(I, X),
XY(I, X, X), XY(I, X, X),
} }
impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Pull<T, I, X>, _to: T|{ impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Pull<T, I, X>, _to: T|{
todo!() todo!()
}); });
@ -251,10 +262,14 @@ impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Pull<T, I, X
/// Only draw content if area is above a certain size. /// Only draw content if area is above a certain size.
/// ///
/// ``` /// ```
/// use tengri::Layout; /// # fn doctest_layout_min () -> Result<(), Box<dyn std::error::Error>> {
/// let _ = "".min_w(1); /// use tengri::{Layout, Draw, XYWH};
/// let _ = "".min_h(1); /// let area = XYWH(1u16, 1, 80, 25);
/// let _ = "".min_wh(1, 1); /// assert_eq!("1".min_w(5).layout(area)?, Some(XYWH(1u16, 1, 5, 1)));
/// assert_eq!("1".min_h(5).layout(area)?, Some(XYWH(1u16, 1, 1, 5)));
/// assert_eq!("1".min_wh(5, 5).layout(area)?, Some(XYWH(1u16, 1, 5, 5)));
/// assert_eq!("123456".min_w(5).layout(area)?, Some(XYWH(1u16, 1, 6, 1)));
/// # Ok(()) }
/// ``` /// ```
pub enum Min<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> { pub enum Min<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
__(PhantomData<T>), __(PhantomData<T>),
@ -262,7 +277,6 @@ pub enum Min<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
H(I, X), H(I, X),
WH(I, X, X), WH(I, X, X),
} }
impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Min<T, I, X>, _to: T|{ impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Min<T, I, X>, _to: T|{
todo!() todo!()
}); });
@ -270,10 +284,13 @@ impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Min<T, I, X>
/// Set maximum size of of drawing area. /// Set maximum size of of drawing area.
/// ///
/// ``` /// ```
/// use tengri::Layout; /// # fn doctest_layout_max () -> Result<(), Box<dyn std::error::Error>> {
/// let _ = "".max_w(1); /// use tengri::{Layout, Draw, XYWH};
/// let _ = "".max_h(1); /// let area = XYWH(1u16, 1, 80, 25);
/// let _ = "".max_wh(1, 1); /// assert_eq!("12345".max_w(1).layout(area)?, Some(XYWH(1u16, 1, 1, 1)));
/// assert_eq!("12345".max_h(1).layout(area)?, Some(XYWH(1u16, 1, 1, 1)));
/// assert_eq!("12345".max_wh(1, 1).layout(area)?, Some(XYWH(1u16, 1, 5, 1)));
/// # Ok(()) }
/// ``` /// ```
pub enum Max<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> { pub enum Max<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
__(PhantomData<T>), __(PhantomData<T>),
@ -281,7 +298,6 @@ pub enum Max<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
H(I, X), H(I, X),
WH(I, X, X), WH(I, X, X),
} }
impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Max<T, I, X>, to: T|{ impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Max<T, I, X>, to: T|{
let area: XYWH<T::Unit> = to.area(); let area: XYWH<T::Unit> = to.area();
let (item, area) = match self { let (item, area) = match self {
@ -316,7 +332,6 @@ pub enum Exact<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
H(I, X), H(I, X),
WH(I, X, X), WH(I, X, X),
} }
impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Exact<T, I, X>, to: T|{ impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Exact<T, I, X>, to: T|{
let area: XYWH<T::Unit> = to.area(); let area: XYWH<T::Unit> = to.area();
let (item, area) = match self { let (item, area) = match self {
@ -345,29 +360,27 @@ pub enum Pad<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
H(I, X), H(I, X),
WH(I, X, X), WH(I, X, X),
} }
impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Pad<T, I, X>, _to: T|{ impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Pad<T, I, X>, _to: T|{
todo!() todo!()
}); });
pub struct Align<T>(Option<Azimuth>, T); pub struct Align<T>(Option<Azimuth>, T);
impl_draw!(<S: Screen, T: Draw<S>,>|self: Align<T>, to: S|{ impl_draw!(<S: Screen, T: Draw<S>,>|self: Align<T>, to: S|{
use Azimuth::*; use Azimuth::*;
let XYWH(x0, y0, w0, h0) = to.area(); let XYWH(x0, y0, w0, h0) = to.area();
if let Some(XYWH(x, y, w, h)) = self.1.layout(to.area())? { if let Some(XYWH(x, y, w, h)) = self.1.layout(to.area())? {
to.clip(match self.0 { to.clip(match self.0 {
Some(NW) => XYWH(x0, y0, w, h), Some(NW) => XYWH(x0, y0, w, h),
Some(N) => XYWH(x0 + w0.sub(w) / 2.into(), y0, w, h), Some(N) => XYWH(x0 + w0.minus(w) / 2.into(), y0, w, h),
Some(NE) => XYWH((x0 + w0).sub(w), y0, w, h), Some(NE) => XYWH((x0 + w0).minus(w), y0, w, h),
Some(W) => XYWH(x0, y0 + h0.sub(h) / 2.into(), w, h), Some(W) => XYWH(x0, y0 + h0.minus(h) / 2.into(), w, h),
Some(C) => XYWH(x0 + w0.sub(w) / 2.into(), y0 + h0.sub(h) / 2.into(), w, h), Some(C) => XYWH(x0 + w0.minus(w) / 2.into(), y0 + h0.minus(h) / 2.into(), w, h),
Some(E) => XYWH((x0 + w0).sub(w), y0 + h0.sub(h) / 2.into(), w, h), Some(E) => XYWH((x0 + w0).minus(w), y0 + h0.minus(h) / 2.into(), w, h),
Some(SW) => XYWH(x0, (y0 + h0).sub(h), w, h), Some(SW) => XYWH(x0, (y0 + h0).minus(h), w, h),
Some(S) => XYWH(x0 + w0.sub(w) / 2.into(), (y0 + h0).sub(h), w, h), Some(S) => XYWH(x0 + w0.minus(w) / 2.into(), (y0 + h0).minus(h), w, h),
Some(SE) => XYWH((x0 + w0).sub(w), (y0 + h0).sub(h), w, h), Some(SE) => XYWH((x0 + w0).minus(w), (y0 + h0).minus(h), w, h),
Some(X) => XYWH(x0 + w0.sub(w) / 2.into(), y, w, h), Some(X) => XYWH(x0 + w0.minus(w) / 2.into(), y, w, h),
Some(Y) => XYWH(x, y0 + h0.sub(h) / 2.into(), w, h), Some(Y) => XYWH(x, y0 + h0.minus(h) / 2.into(), w, h),
None => to.area() None => to.area()
}, |to|self.1.draw(to)) }, |to|self.1.draw(to))
} else { } else {
@ -403,13 +416,11 @@ pub struct Area<S: Screen, T: Draw<S>>(
pub Option<XYWH<S::Unit>>, pub Option<XYWH<S::Unit>>,
pub T pub T
); );
impl_draw!(<S: Screen, T: Draw<S>,>|self: Area<S, T>, to: S|{ impl_draw!(<S: Screen, T: Draw<S>,>|self: Area<S, T>, to: S|{
to.clip(self.0, |to|self.1.draw(to)) to.clip(self.0, |to|self.1.draw(to))
}); });
pub struct Origin<T>(Option<Azimuth>, T); pub struct Origin<T>(Option<Azimuth>, T);
impl_draw!(<S: Screen, T: Draw<S>,>|self: Origin<T>, _to: S|{ impl_draw!(<S: Screen, T: Draw<S>,>|self: Origin<T>, _to: S|{
todo!() todo!()
}); });

View file

@ -8,7 +8,7 @@ pub trait Lrtb<N: Coord>: Xywh<N> {
// FIXME: factor origin // FIXME: factor origin
[self.x(), self.y(), self.x()+self.w(), self.y()+self.h()] [self.x(), self.y(), self.x()+self.w(), self.y()+self.h()]
} }
fn iter_x (&self) -> impl Iterator<Item = N> where Self: HasOrigin { fn iter_x (&self) -> std::ops::Range<N> where Self: HasOrigin {
self.x_west()..self.x_east() self.x_west()..self.x_east()
} }
fn x_west (&self) -> N where Self: HasOrigin { fn x_west (&self) -> N where Self: HasOrigin {
@ -26,7 +26,7 @@ pub trait Lrtb<N: Coord>: Xywh<N> {
fn x_center (&self) -> N where Self: HasOrigin { fn x_center (&self) -> N where Self: HasOrigin {
todo!() todo!()
} }
fn iter_y (&self) -> impl Iterator<Item = N> where Self: HasOrigin { fn iter_y (&self) -> std::ops::Range<N> where Self: HasOrigin {
self.y_north()..self.y_south() self.y_north()..self.y_south()
} }
fn y_north (&self) -> N where Self: HasOrigin { fn y_north (&self) -> N where Self: HasOrigin {

View file

@ -29,3 +29,17 @@ impl Sizer {
}) })
} }
} }
pub struct ShowSize;
impl Draw<Tui> for ShowSize {
fn layout (&self, area: XYWH<u16>) -> Perhaps<XYWH<u16>> {
let info = format!("{area:?}");
Ok(Some(XYWH(area.0, area.1, info.len() as u16, 1)))
}
fn draw (self, to: &mut Tui) -> Drawn<u16> {
let area = to.area();
let info = format!("{area:?}");
to.text(&info, area.0, area.1, info.len() as u16)
}
}

View file

@ -1,2 +0,0 @@
use crate::*;

View file

@ -190,18 +190,11 @@ pub fn eval_view <'a, O: Screen + 'a, S> (
/// use tengri::{*, lang::*, ratatui::prelude::Color}; /// use tengri::{*, lang::*, ratatui::prelude::Color};
/// ///
/// #[namespace(bool)] /// #[namespace(bool)]
/// #[namespace(u8 try_to_u8)] /// #[namespace(u8)]
/// #[namespace(u16 try_to_u16)] /// #[namespace(u16)]
/// #[namespace(Color try_to_color)] /// #[namespace(Color get_color)]
/// #[interpret(Tui -> Option<XYWH<u16>>: try_eval_tui)]
/// struct State; /// struct State;
/// tengri::lang::primitive!(u8: try_to_u8); ///
/// tengri::lang::primitive!(u16: try_to_u16);
/// tengri::lang::interpret!(|self: State, context: Tui, lang|->Option<XYWH<u16>>{
/// expression = {
/// "text" (...rest) => { todo!() }
/// }
/// });
/// impl Interpret<Tui, Option<XYWH<u16>>> for State { /// impl Interpret<Tui, Option<XYWH<u16>>> for State {
/// fn interpret_expr <'a> (&'a self, _: &mut Tui, lang: &'a impl Expression) /// fn interpret_expr <'a> (&'a self, _: &mut Tui, lang: &'a impl Expression)
/// -> Usually<Option<XYWH<u16>>> /// -> Usually<Option<XYWH<u16>>>
@ -210,6 +203,36 @@ pub fn eval_view <'a, O: Screen + 'a, S> (
/// } /// }
/// } /// }
/// ///
/// fn get_color (state: &State, src: impl Language) -> Perhaps<Color> {
/// if let Some(expr) = src.expr()? {
/// match (expr.head()?, expr.tail()?) {
/// (Some("g"), Some(tail)) => {
/// let n: u8 = state.namespace(tail.head().map_err(Into::into))?.ok_or(LanguageError::Domain("not gray"))?;
/// Ok(Some(Color::Rgb(n, n, n)))
/// },
/// (Some("rgb"), Some(tail)) => {
/// let r: u8 = state.namespace(tail.head().map_err(Into::into))?
/// .ok_or(LanguageError::Domain("not red"))?;
/// let g: u8 = state.namespace(tail.tail().head().map_err(Into::into))?
/// .ok_or(LanguageError::Domain("not green"))?;
/// let b: u8 = state.namespace(tail.tail().tail().head().map_err(Into::into))?
/// .ok_or(LanguageError::Domain("not blue"))?;
/// Ok(Some(Color::Rgb(r, g, b)))
/// },
/// (Some(_), _) => return Err(format!("not a color expression: {expr}").into()),
/// (None, _) => return Err(format!("not a color expression: {expr}").into()),
/// }
/// } else if let Ok(Some(sym)) = src.word() {
/// Ok(match sym {
/// ":color/bg" => Some(Color::Rgb(28, 32, 36)),
/// ":color/fg" => Some(Color::Rgb(98, 92, 96)),
/// _ => return Err(format!("not a color: {sym}").into())
/// })
/// } else {
/// return Err(format!("not a color: {:?}", src.src()?).into())
/// }
/// }
///
/// # fn main () -> tengri::Usually<()> { /// # fn main () -> tengri::Usually<()> {
/// let state = State; /// let state = State;
/// let mut out = Tui::new(80, 25); /// let mut out = Tui::new(80, 25);
@ -221,7 +244,7 @@ pub fn eval_view <'a, O: Screen + 'a, S> (
/// # Ok(()) } /// # Ok(()) }
/// ``` /// ```
pub fn eval_view_tui <'a, S> ( pub fn eval_view_tui <'a, S> (
state: &S, output: &mut Tui, expr: impl Expression + 'a state: &S, to: &mut Tui, expr: impl Expression + 'a
) -> Perhaps<XYWH<u16>> where ) -> Perhaps<XYWH<u16>> where
S: Interpret<Tui, Option<XYWH<u16>>> S: Interpret<Tui, Option<XYWH<u16>>>
+ for<'b>Namespace<'b, bool> + for<'b>Namespace<'b, bool>
@ -239,7 +262,7 @@ pub fn eval_view_tui <'a, S> (
match frags.next() { match frags.next() {
Some("text") => { Some("text") => {
if let Some(src) = args?.src()? { if let Some(src) = args?.src()? {
output.show(src) to.show(src)
} else { } else {
return Ok(None) return Ok(None)
} }
@ -248,11 +271,10 @@ pub fn eval_view_tui <'a, S> (
Some("fg") => { Some("fg") => {
let arg0 = arg0?.expect("fg: expected arg 0 (color)"); let arg0 = arg0?.expect("fg: expected arg 0 (color)");
if let Some(color) = Namespace::namespace(state, arg0)? { if let Some(color) = Namespace::namespace(state, arg0)? {
output.show(fg(color, thunk(move|output: &mut Tui|{ fg(color, thunk(move|to: &mut Tui|{
state.interpret(output, &arg1)?; state.interpret(to, &arg1)?;
// FIXME?: don't max out the used area? Ok(Some(to.area().into())) // FIXME?: don't max out the used area?
Ok(Some(output.area().into())) })).draw(to)
})))
} else { } else {
return Err(format!("fg: {arg0:?}: not a color").into()) return Err(format!("fg: {arg0:?}: not a color").into())
} }
@ -261,11 +283,10 @@ pub fn eval_view_tui <'a, S> (
Some("bg") => { Some("bg") => {
let arg0 = arg0?.expect("bg: expected arg 0 (color)"); let arg0 = arg0?.expect("bg: expected arg 0 (color)");
if let Some(color) = Namespace::namespace(state, arg0)? { if let Some(color) = Namespace::namespace(state, arg0)? {
output.show(bg(color, thunk(move|output: &mut Tui|{ bg(color, thunk(move|to: &mut Tui|{
state.interpret(output, &arg1)?; state.interpret(to, &arg1)?;
// FIXME?: don't max out the used area? Ok(Some(to.area().into())) // FIXME?: don't max out the used area?
Ok(Some(output.area().into())) })).draw(to)
})))
} else { } else {
return Err(format!("bg: {arg0:?}: not a color").into()) return Err(format!("bg: {arg0:?}: not a color").into())
} }

View file

@ -1,11 +1,11 @@
#![feature(anonymous_lifetime_in_impl_trait)] //#![feature(anonymous_lifetime_in_impl_trait)]
//#![feature(associated_type_defaults)] //#![feature(associated_type_defaults)]
//#![feature(const_default)] //#![feature(const_default)]
//#![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(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)]

View file

@ -803,7 +803,7 @@ pub trait AddMidiOut {
/// Port connection manager. /// Port connection manager.
/// ///
/// ``` /// ```
/// let connect = tek::Connect::default(); /// let connect = tengri::Connect::default();
/// ``` /// ```
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct Connect { pub struct Connect {
@ -814,7 +814,6 @@ pub struct Connect {
} }
impl Connect { impl Connect {
pub fn new <T: AsRef<str>> ( pub fn new <T: AsRef<str>> (
exact: Option<impl Iterator<Item = T>>, exact: Option<impl Iterator<Item = T>>,
re: Option<impl Iterator<Item = T>>, re: Option<impl Iterator<Item = T>>,
@ -827,62 +826,6 @@ impl Connect {
connections connections
} }
pub fn midi_ins <T: AsRef<str>> (
jack: &Jack<'static>,
name: &T,
midi_from: &[T],
midi_from_re: Option<&[T]>,
) -> Usually<Vec<MidiInput>> {
Ok(Connect::new(
Some(midi_from.into_iter()),
Some([].into_iter()),
midi_from_re.map(|x|x.into_iter())).iter().enumerate()
.map(|(index, connect)|jack.midi_in(&format!("{}/{index}", name.as_ref()), &[connect.clone()]))
.collect::<Result<_, _>>()?)
}
pub fn midi_outs <T: AsRef<str>> (
jack: &Jack<'static>,
name: &T,
midi_to: &[T],
midi_to_re: Option<&[T]>,
) -> Usually<Vec<MidiOutput>> {
Ok(Connect::new(
Some(midi_to.into_iter()),
Some([].into_iter()),
midi_to_re.map(|x|x.into_iter())).iter().enumerate()
.map(|(index, connect)|jack.midi_out(&format!("{index}/{}", name.as_ref()), &[connect.clone()]))
.collect::<Result<_, _>>()?)
}
pub fn audio_ins <T: AsRef<str>> (
jack: &Jack<'static>,
name: &T,
audio_from: &[T],
audio_from_re: Option<&[T]>,
) -> Usually<Vec<AudioInput>> {
Ok(Connect::new(
Some(audio_from.into_iter()),
Some([].into_iter()),
audio_from_re.map(|x|x.into_iter())).iter().enumerate()
.map(|(index, connect)|jack.audio_in(&format!("{}/{index}", name.as_ref()), &[connect.clone()]))
.collect::<Result<_, _>>()?)
}
pub fn audio_outs <T: AsRef<str>> (
jack: &Jack<'static>,
name: &T,
audio_to: &[T],
audio_to_re: Option<&[T]>,
) -> Usually<Vec<AudioOutput>> {
Ok(Connect::new(
Some(audio_to.into_iter()),
Some([].into_iter()),
audio_to_re.map(|x|x.into_iter())).iter().enumerate()
.map(|(index, connect)|jack.audio_out(&format!("{index}/{}", name.as_ref()), &[connect.clone()]))
.collect::<Result<_, _>>()?)
}
/// Connect to this exact port /// Connect to this exact port
pub fn exact (name: impl AsRef<str>) -> Self { pub fn exact (name: impl AsRef<str>) -> Self {
let info = format!("=:{}", name.as_ref()).into(); let info = format!("=:{}", name.as_ref()).into();
@ -923,3 +866,59 @@ impl Connect {
}).into() }).into()
} }
} }
pub fn connect_midi_ins <T: AsRef<str>> (
jack: &Jack<'static>,
name: &T,
midi_from: &[T],
midi_from_re: Option<&[T]>,
) -> Usually<Vec<MidiInput>> {
Ok(Connect::new(
Some(midi_from.into_iter()),
Some([].into_iter()),
midi_from_re.map(|x|x.into_iter())).iter().enumerate()
.map(|(index, connect)|jack.midi_in(&format!("{}/{index}", name.as_ref()), &[connect.clone()]))
.collect::<Result<_, _>>()?)
}
pub fn connect_midi_outs <T: AsRef<str>> (
jack: &Jack<'static>,
name: &T,
midi_to: &[T],
midi_to_re: Option<&[T]>,
) -> Usually<Vec<MidiOutput>> {
Ok(Connect::new(
Some(midi_to.into_iter()),
Some([].into_iter()),
midi_to_re.map(|x|x.into_iter())).iter().enumerate()
.map(|(index, connect)|jack.midi_out(&format!("{index}/{}", name.as_ref()), &[connect.clone()]))
.collect::<Result<_, _>>()?)
}
pub fn connect_audio_ins <T: AsRef<str>> (
jack: &Jack<'static>,
name: &T,
audio_from: &[T],
audio_from_re: Option<&[T]>,
) -> Usually<Vec<AudioInput>> {
Ok(Connect::new(
Some(audio_from.into_iter()),
Some([].into_iter()),
audio_from_re.map(|x|x.into_iter())).iter().enumerate()
.map(|(index, connect)|jack.audio_in(&format!("{}/{index}", name.as_ref()), &[connect.clone()]))
.collect::<Result<_, _>>()?)
}
pub fn connect_audio_outs <T: AsRef<str>> (
jack: &Jack<'static>,
name: &T,
audio_to: &[T],
audio_to_re: Option<&[T]>,
) -> Usually<Vec<AudioOutput>> {
Ok(Connect::new(
Some(audio_to.into_iter()),
Some([].into_iter()),
audio_to_re.map(|x|x.into_iter())).iter().enumerate()
.map(|(index, connect)|jack.audio_out(&format!("{index}/{}", name.as_ref()), &[connect.clone()]))
.collect::<Result<_, _>>()?)
}

View file

View file

View file

View file

@ -1,5 +1,4 @@
use crate::*; use crate::lang::*;
use crate::{*, lang::*, draw::*, task::*, exit::*};
use ::ratatui::buffer::Cell; use ::ratatui::buffer::Cell;
/// TUI buffer sized by `usize` instead of `u16`. /// TUI buffer sized by `usize` instead of `u16`.

View file

@ -1,6 +1,6 @@
use crate::*; use crate::*;
use ratatui::prelude::Color; use ratatui::prelude::Color;
use dizzle::{Ostensibly, Expression, LanguageError::*}; use dizzle::{Expression, LanguageError::*};
pub trait ColorDsl<T>: Sized { pub trait ColorDsl<T>: Sized {
fn new_g (expr: T, try_to_u8: impl Fn(Perhaps<&str>)->Perhaps<u8>) -> Usually<Self>; fn new_g (expr: T, try_to_u8: impl Fn(Perhaps<&str>)->Perhaps<u8>) -> Usually<Self>;

View file

@ -1,10 +1,5 @@
use crate::{task::Task, term::TuiEvent}; use ::dizzle::{Language, Symbol, Usually};
use ::std::sync::{Arc, RwLock, atomic::{AtomicBool, Ordering::*}}; use ::crossterm::event::{Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState};
use ::std::time::Duration;
use ::dizzle::{Language, Symbol, Usually, Apply};
use ::crossterm::event::{
read, Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState
};
/// TUI key spec. /// TUI key spec.
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd)] #[derive(Debug, Clone, Eq, PartialEq, PartialOrd)]

View file

@ -1,5 +1,3 @@
use crate::*;
/// Stackably padded. /// Stackably padded.
/// ///
/// ``` /// ```

View file

@ -1,5 +1,5 @@
use crate::*; use crate::*;
use ratatui::{prelude::{Style, Position, Backend, Color}}; use ratatui::{prelude::{Position}};
pub const fn x_repeat (c: &str) -> impl Draw<Tui> { pub const fn x_repeat (c: &str) -> impl Draw<Tui> {
thunk(move|to: &mut Tui|{ thunk(move|to: &mut Tui|{

View file

@ -1,5 +1,5 @@
use crate::*; use crate::*;
use ratatui::{prelude::{Style, Position, Backend, Color}}; use ratatui::{prelude::{Position}};
pub const ICON_DEC_V: &[char] = &['▲']; pub const ICON_DEC_V: &[char] = &['▲'];
pub const ICON_INC_V: &[char] = &['▼']; pub const ICON_INC_V: &[char] = &['▼'];
@ -7,7 +7,7 @@ pub const ICON_DEC_H: &[char] = &[' ', '🞀', ' '];
pub const ICON_INC_H: &[char] = &[' ', '🞂', ' ']; pub const ICON_INC_H: &[char] = &[' ', '🞂', ' '];
pub fn x_scroll () -> impl Draw<Tui> { pub fn x_scroll () -> impl Draw<Tui> {
thunk(|Tui(buf, XYWH(x1, y1, w, h)): &mut Tui|{ thunk(|Tui(buf, XYWH(x1, y1, w, _h)): &mut Tui|{
let x2 = *x1 + *w; let x2 = *x1 + *w;
for (i, x) in (*x1..=x2).enumerate() { for (i, x) in (*x1..=x2).enumerate() {
if let Some(cell) = buf.cell_mut(Position::from((x, *y1))) { if let Some(cell) = buf.cell_mut(Position::from((x, *y1))) {
@ -35,7 +35,7 @@ pub fn x_scroll () -> impl Draw<Tui> {
} }
pub fn y_scroll () -> impl Draw<Tui> { pub fn y_scroll () -> impl Draw<Tui> {
thunk(|Tui(buf, XYWH(x1, y1, w, h)): &mut Tui|{ thunk(|Tui(buf, XYWH(x1, y1, _w, h)): &mut Tui|{
let y2 = *y1 + *h; let y2 = *y1 + *h;
for (i, y) in (*y1..=y2).enumerate() { for (i, y) in (*y1..=y2).enumerate() {
if let Some(cell) = buf.cell_mut(Position::from((*x1, y))) { if let Some(cell) = buf.cell_mut(Position::from((*x1, y))) {

View file

@ -1,18 +1,36 @@
#![allow(unused)]
use crate::*; use crate::*;
pub(crate) use ::unicode_width::*; pub(crate) use ::unicode_width::*;
#[cfg(feature = "term")] mod impl_term { #[cfg(feature = "term")] mod impl_term {
use super::*; use super::*;
use crate::*;
use ratatui::prelude::Position; use ratatui::prelude::Position;
impl_draw!(|self: String, to: Tui|{self.as_str().draw(to)}); impl_draw!(|self: String, to: Tui|{self.as_str().draw(to)});
impl_draw!(|self: std::sync::Arc<str>, to: Tui|{self.as_ref().draw(to)}); impl_draw!(|self: std::sync::Arc<str>, to: Tui|{self.as_ref().draw(to)});
impl_draw!(|self: &std::sync::Arc<str>, to: Tui|{self.as_ref().draw(to)}); impl_draw!(|self: &std::sync::Arc<str>, to: Tui|{self.as_ref().draw(to)});
impl_draw!(|self: &str, to: Tui|{ impl Draw<Tui> for &str {
let XYWH(x, y, w, ..) = to.1.centered_xy([width_chars_max(to.w(), self), 1]); fn layout (&self, area: XYWH<u16>) -> Perhaps<XYWH<u16>> {
to.text(&self, x, y, w) let XYWH(x, y, ..) = area;
}); let mut max_w = 0u16;
let mut max_h = 0u16;
for line in self.split("\n") {
max_h += 1;
max_w = max_w.max(line.len() as u16);
}
Ok(Some(XYWH(x, y, max_w, max_h)))
}
fn draw (self, to: &mut Tui) -> Drawn<u16> {
let area = self.layout(to.area())?.unwrap();
////let info = format!("{area:?}");
//to.text(&self, area.0, area.1, self.len() as u16)
for (index, line) in self.split("\n").enumerate() {
let _ = to.text(&line, area.0, area.1 + index as u16, width_chars_max(area.2, line) as u16)?;
}
Ok(Some(area))
}
}
impl_draw!(<T: AsRef<str>,>|self: TrimString<T>, to: Tui|{self.as_ref().draw(to)}); impl_draw!(<T: AsRef<str>,>|self: TrimString<T>, to: Tui|{self.as_ref().draw(to)});
impl_draw!(<T: AsRef<str>,>|self: TrimStringRef<'_, T>, to: Tui|{ impl_draw!(<T: AsRef<str>,>|self: TrimStringRef<'_, T>, to: Tui|{
@ -80,7 +98,9 @@ pub fn trim_string (max_width: usize, input: impl AsRef<str>) -> String {
pub struct TrimString<T: AsRef<str>>(pub u16, pub T); pub struct TrimString<T: AsRef<str>>(pub u16, pub T);
impl<T: AsRef<str>> AsRef<str> for TrimString<T> { fn as_ref (&self) -> &str { self.1.as_ref() } } impl<T: AsRef<str>> AsRef<str> for TrimString<T> { fn as_ref (&self) -> &str { self.1.as_ref() } }
impl<'a, T: AsRef<str>> TrimString<T> { impl<'a, T: AsRef<str>> TrimString<T> {
fn to_ref (&self) -> TrimStringRef<'_, T> { TrimStringRef(self.0, &self.1) } fn to_ref (&self) -> TrimStringRef<'_, T> {
TrimStringRef(self.0, &self.1)
}
} }
/// Displays a borrowed [str]-like with fixed maximum width /// Displays a borrowed [str]-like with fixed maximum width