From 8dfe20a58ce36c09e8aa233370e61ac62eb4184c Mon Sep 17 00:00:00 2001 From: unspeaker Date: Mon, 8 Sep 2025 17:48:56 +0300 Subject: [PATCH 1/3] tui: update example --- tui/examples/edn/edn01.edn | 1 - tui/examples/edn/edn02.edn | 1 - tui/examples/edn/edn03.edn | 1 - tui/examples/edn/edn04.edn | 1 - tui/examples/edn/edn05.edn | 1 - tui/examples/edn/edn06.edn | 1 - tui/examples/edn/edn07.edn | 1 - tui/examples/edn/edn08.edn | 1 - tui/examples/edn/edn09.edn | 1 - tui/examples/edn/edn10.edn | 1 - tui/examples/edn/edn11.edn | 11 --- tui/examples/edn/edn12.edn | 11 --- tui/examples/edn/edn13.edn | 11 --- tui/examples/edn/edn14.edn | 1 - tui/examples/edn/edn15.edn | 1 - tui/examples/edn/edn16.edn | 1 - tui/examples/edn/edn17.edn | 1 - tui/examples/edn/edn99.edn | 73 -------------- tui/examples/tui.rs | 195 ++++++++++++++++++++++++++++++------- 19 files changed, 160 insertions(+), 155 deletions(-) delete mode 100644 tui/examples/edn/edn01.edn delete mode 100644 tui/examples/edn/edn02.edn delete mode 100644 tui/examples/edn/edn03.edn delete mode 100644 tui/examples/edn/edn04.edn delete mode 100644 tui/examples/edn/edn05.edn delete mode 100644 tui/examples/edn/edn06.edn delete mode 100644 tui/examples/edn/edn07.edn delete mode 100644 tui/examples/edn/edn08.edn delete mode 100644 tui/examples/edn/edn09.edn delete mode 100644 tui/examples/edn/edn10.edn delete mode 100644 tui/examples/edn/edn11.edn delete mode 100644 tui/examples/edn/edn12.edn delete mode 100644 tui/examples/edn/edn13.edn delete mode 100644 tui/examples/edn/edn14.edn delete mode 100644 tui/examples/edn/edn15.edn delete mode 100644 tui/examples/edn/edn16.edn delete mode 100644 tui/examples/edn/edn17.edn delete mode 100644 tui/examples/edn/edn99.edn diff --git a/tui/examples/edn/edn01.edn b/tui/examples/edn/edn01.edn deleted file mode 100644 index 7ff93d1..0000000 --- a/tui/examples/edn/edn01.edn +++ /dev/null @@ -1 +0,0 @@ -:hello-world diff --git a/tui/examples/edn/edn02.edn b/tui/examples/edn/edn02.edn deleted file mode 100644 index 4f352a6..0000000 --- a/tui/examples/edn/edn02.edn +++ /dev/null @@ -1 +0,0 @@ -(bsp/s :hello :world) diff --git a/tui/examples/edn/edn03.edn b/tui/examples/edn/edn03.edn deleted file mode 100644 index 1622275..0000000 --- a/tui/examples/edn/edn03.edn +++ /dev/null @@ -1 +0,0 @@ -(fill/xy :hello-world) diff --git a/tui/examples/edn/edn04.edn b/tui/examples/edn/edn04.edn deleted file mode 100644 index 9393669..0000000 --- a/tui/examples/edn/edn04.edn +++ /dev/null @@ -1 +0,0 @@ -(fixed/xy 20 10 :hello-world) diff --git a/tui/examples/edn/edn05.edn b/tui/examples/edn/edn05.edn deleted file mode 100644 index 90313ad..0000000 --- a/tui/examples/edn/edn05.edn +++ /dev/null @@ -1 +0,0 @@ -(bsp/s (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) diff --git a/tui/examples/edn/edn06.edn b/tui/examples/edn/edn06.edn deleted file mode 100644 index e35abf0..0000000 --- a/tui/examples/edn/edn06.edn +++ /dev/null @@ -1 +0,0 @@ -(bsp/e (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) diff --git a/tui/examples/edn/edn07.edn b/tui/examples/edn/edn07.edn deleted file mode 100644 index 7370353..0000000 --- a/tui/examples/edn/edn07.edn +++ /dev/null @@ -1 +0,0 @@ -(bsp/n (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) diff --git a/tui/examples/edn/edn08.edn b/tui/examples/edn/edn08.edn deleted file mode 100644 index afcde46..0000000 --- a/tui/examples/edn/edn08.edn +++ /dev/null @@ -1 +0,0 @@ -(bsp/w (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) diff --git a/tui/examples/edn/edn09.edn b/tui/examples/edn/edn09.edn deleted file mode 100644 index 3ced769..0000000 --- a/tui/examples/edn/edn09.edn +++ /dev/null @@ -1 +0,0 @@ -(bsp/a (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) diff --git a/tui/examples/edn/edn10.edn b/tui/examples/edn/edn10.edn deleted file mode 100644 index 07af31e..0000000 --- a/tui/examples/edn/edn10.edn +++ /dev/null @@ -1 +0,0 @@ -(bsp/b (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) diff --git a/tui/examples/edn/edn11.edn b/tui/examples/edn/edn11.edn deleted file mode 100644 index 1c47cb5..0000000 --- a/tui/examples/edn/edn11.edn +++ /dev/null @@ -1,11 +0,0 @@ -(bsp/s - (bsp/e (align/nw (fixed/xy 5 3 :hello)) - (bsp/e (align/n (fixed/xy 5 3 :hello)) - (align/ne (fixed/xy 5 3 :hello)))) - (bsp/s - (bsp/e (align/w (fixed/xy 5 3 :hello)) - (bsp/e (align/c (fixed/xy 5 3 :hello)) - (align/e (fixed/xy 5 3 :hello)))) - (bsp/e (align/sw (fixed/xy 5 3 :hello)) - (bsp/e (align/s (fixed/xy 5 3 :hello)) - (align/se (fixed/xy 5 3 :hello)))))) diff --git a/tui/examples/edn/edn12.edn b/tui/examples/edn/edn12.edn deleted file mode 100644 index 26e3f5f..0000000 --- a/tui/examples/edn/edn12.edn +++ /dev/null @@ -1,11 +0,0 @@ -(bsp/s - (bsp/e (fixed/xy 8 5 (align/nw :hello)) - (bsp/e (fixed/xy 8 5 (align/n :hello)) - (fixed/xy 8 5 (align/ne :hello)))) - (bsp/s - (bsp/e (fixed/xy 8 5 (align/w :hello)) - (bsp/e (fixed/xy 8 5 (align/c :hello)) - (fixed/xy 8 5 (align/e :hello)))) - (bsp/e (fixed/xy 8 5 (align/sw :hello)) - (bsp/e (fixed/xy 8 5 (align/s :hello)) - (fixed/xy 8 5 (align/se :hello)))))) diff --git a/tui/examples/edn/edn13.edn b/tui/examples/edn/edn13.edn deleted file mode 100644 index 8fe81c3..0000000 --- a/tui/examples/edn/edn13.edn +++ /dev/null @@ -1,11 +0,0 @@ -(bsp/s - (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/nw :hello))) - (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/n :hello))) - (grow/xy 1 1 (fixed/xy 8 5 (align/ne :hello))))) - (bsp/s - (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/w :hello))) - (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/c :hello))) - (grow/xy 1 1 (fixed/xy 8 5 (align/e :hello))))) - (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/sw :hello))) - (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/s :hello))) - (grow/xy 1 1 (fixed/xy 8 5 (align/se :hello))))))) diff --git a/tui/examples/edn/edn14.edn b/tui/examples/edn/edn14.edn deleted file mode 100644 index 7aab387..0000000 --- a/tui/examples/edn/edn14.edn +++ /dev/null @@ -1 +0,0 @@ -:map-e diff --git a/tui/examples/edn/edn15.edn b/tui/examples/edn/edn15.edn deleted file mode 100644 index ddd4d58..0000000 --- a/tui/examples/edn/edn15.edn +++ /dev/null @@ -1 +0,0 @@ -(align/c :map-e) diff --git a/tui/examples/edn/edn16.edn b/tui/examples/edn/edn16.edn deleted file mode 100644 index 9c6b9ad..0000000 --- a/tui/examples/edn/edn16.edn +++ /dev/null @@ -1 +0,0 @@ -:map-s diff --git a/tui/examples/edn/edn17.edn b/tui/examples/edn/edn17.edn deleted file mode 100644 index efd092f..0000000 --- a/tui/examples/edn/edn17.edn +++ /dev/null @@ -1 +0,0 @@ -(align/c :map-s) diff --git a/tui/examples/edn/edn99.edn b/tui/examples/edn/edn99.edn deleted file mode 100644 index 933db8c..0000000 --- a/tui/examples/edn/edn99.edn +++ /dev/null @@ -1,73 +0,0 @@ -(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))))))) - - fn content (&self) -> dyn Draw { - 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(()) - //}))))) - //})) - } diff --git a/tui/examples/tui.rs b/tui/examples/tui.rs index 323dc9b..2cc35b5 100644 --- a/tui/examples/tui.rs +++ b/tui/examples/tui.rs @@ -1,36 +1,102 @@ use tengri::{self, Usually, Perhaps, input::*, output::*, tui::*, dsl::*}; use std::sync::{Arc, RwLock}; -//use crossterm::event::{*, KeyCode::*}; use crate::ratatui::style::Color; +//use crossterm::event::{*, KeyCode::*}; fn main () -> Usually<()> { - let state = Arc::new(RwLock::new(Example(10, Measure::new()))); + let state = Example::new(); Tui::new().unwrap().run(&state)?; Ok(()) } -#[derive(Debug)] pub struct Example(usize, Measure); +#[derive(Debug)] pub struct Example( + usize, + Measure +); -const KEYMAP: &str = "(@left prev) (@right next)"; -const EXAMPLES: &'static [&'static str] = &[ - include_str!("edn/edn01.edn"), - include_str!("edn/edn02.edn"), - include_str!("edn/edn03.edn"), - include_str!("edn/edn04.edn"), - include_str!("edn/edn05.edn"), - include_str!("edn/edn06.edn"), - include_str!("edn/edn07.edn"), - include_str!("edn/edn08.edn"), - include_str!("edn/edn09.edn"), - include_str!("edn/edn10.edn"), - include_str!("edn/edn11.edn"), - include_str!("edn/edn12.edn"), - //include_str!("edn/edn13.edn"), - include_str!("edn/edn14.edn"), - include_str!("edn/edn15.edn"), - include_str!("edn/edn16.edn"), - include_str!("edn/edn17.edn"), -]; +impl Example { + fn new () -> Arc> { + Arc::new(RwLock::new(Example(10, Measure::new()))) + } + const BINDS: &'static str = stringify! { + (@left prev) + (@right next) + }; + const VIEWS: &'static [&'static str] = &[ + stringify! { :hello-world }, + stringify! { (fill/xy :hello-world) }, + stringify! { (bsp/s :hello :world) }, + stringify! { (fixed/xy 20 10 :hello-world) }, + stringify! { (bsp/s (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, + stringify! { (bsp/e (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, + stringify! { (bsp/n (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, + stringify! { (bsp/w (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, + stringify! { (bsp/a (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, + stringify! { (bsp/b (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, + stringify! { + (bsp/s + (bsp/e (align/nw (fixed/xy 5 3 :hello)) + (bsp/e (align/n (fixed/xy 5 3 :hello)) + (align/ne (fixed/xy 5 3 :hello)))) + (bsp/s + (bsp/e (align/w (fixed/xy 5 3 :hello)) + (bsp/e (align/c (fixed/xy 5 3 :hello)) + (align/e (fixed/xy 5 3 :hello)))) + (bsp/e (align/sw (fixed/xy 5 3 :hello)) + (bsp/e (align/s (fixed/xy 5 3 :hello)) + (align/se (fixed/xy 5 3 :hello)))))) + }, + stringify! { + (bsp/s + (bsp/e (fixed/xy 8 5 (align/nw :hello)) + (bsp/e (fixed/xy 8 5 (align/n :hello)) + (fixed/xy 8 5 (align/ne :hello)))) + (bsp/s + (bsp/e (fixed/xy 8 5 (align/w :hello)) + (bsp/e (fixed/xy 8 5 (align/c :hello)) + (fixed/xy 8 5 (align/e :hello)))) + (bsp/e (fixed/xy 8 5 (align/sw :hello)) + (bsp/e (fixed/xy 8 5 (align/s :hello)) + (fixed/xy 8 5 (align/se :hello)))))) + }, + stringify! { + (bsp/s + (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/nw :hello))) + (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/n :hello))) + (grow/xy 1 1 (fixed/xy 8 5 (align/ne :hello))))) + (bsp/s + (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/w :hello))) + (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/c :hello))) + (grow/xy 1 1 (fixed/xy 8 5 (align/e :hello))))) + (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/sw :hello))) + (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/s :hello))) + (grow/xy 1 1 (fixed/xy 8 5 (align/se :hello))))))) + }, + 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))))))) + }, + ]; +} + +tui_draw!(|self: Example, to|to.place(&self.content())); + +content!(TuiOut: |self: Example|{ + let index = self.0 + 1; + let wh = self.1.wh(); + let src = EXAMPLES.get(self.0).unwrap_or(&""); + let heading = format!("Example {}/{} in {:?}", index, EXAMPLES.len(), &wh); + let title = Tui::bg(Color::Rgb(60, 10, 10), Push::y(1, Align::n(heading))); + let code = Tui::bg(Color::Rgb(10, 60, 10), Push::y(2, Align::n(format!("{}", src)))); + let content = ();//Tui::bg(Color::Rgb(10, 10, 60), View(self, CstIter::new(src))); + self.1.of(Bsp::s(title, Bsp::n(""/*code*/, content))) +}); handle!(TuiIn: |self: Example, input|{ Ok(None)/*if let Some(command) = CstIter::new(KEYMAP).command::<_, ExampleCommand, _>(self, input) { @@ -63,18 +129,6 @@ impl ExampleCommand { } } -tui_draw!(|self: Example, to|to.place(&self.content())); -content!(TuiOut: |self: Example|{ - let index = self.0 + 1; - let wh = self.1.wh(); - let src = EXAMPLES.get(self.0).unwrap_or(&""); - let heading = format!("Example {}/{} in {:?}", index, EXAMPLES.len(), &wh); - let title = Tui::bg(Color::Rgb(60, 10, 10), Push::y(1, Align::n(heading))); - let code = Tui::bg(Color::Rgb(10, 60, 10), Push::y(2, Align::n(format!("{}", src)))); - let content = ();//Tui::bg(Color::Rgb(10, 10, 60), View(self, CstIter::new(src))); - self.1.of(Bsp::s(title, Bsp::n(""/*code*/, content))) -}); - //#[tengri_proc::view(TuiOut)] //impl Example { //pub fn title (&self) -> impl Draw + use<'_> { @@ -99,3 +153,74 @@ content!(TuiOut: |self: Example|{ //Map::south(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed() //} //} +/* + + fn content (&self) -> dyn Draw { + 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(()) + //}))))) + //})) + } +*/ From ca862b9802524ee1713c0088c7a525ad07f370a0 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Mon, 8 Sep 2025 18:44:42 +0300 Subject: [PATCH 2/3] dsl, output, tui: add tests, examples, root dispatchers --- Justfile | 9 +- bacon.toml | 44 +++++ dsl/src/dsl_test.rs | 90 +++++----- output/src/layout/layout_cond.rs | 20 +-- output/src/layout/layout_map.rs | 10 +- output/src/output.rs | 51 +++--- output/src/{test.rs => output_test.rs} | 15 +- output/src/view.rs | 137 +++++++++++++++ tui/Cargo.toml | 3 + tui/examples/tui.rs | 226 ------------------------- tui/examples/tui_00.rs | 124 ++++++++++++++ tui/examples/tui_01.rs | 105 ++++++++++++ tui/src/lib.rs | 59 ------- tui/src/tui.rs | 78 +++++++++ tui/src/tui_content.rs | 8 + tui/src/tui_test.rs | 35 ++++ 16 files changed, 637 insertions(+), 377 deletions(-) create mode 100644 bacon.toml rename output/src/{test.rs => output_test.rs} (92%) create mode 100644 output/src/view.rs delete mode 100644 tui/examples/tui.rs create mode 100644 tui/examples/tui_00.rs create mode 100644 tui/examples/tui_01.rs delete mode 100644 tui/src/lib.rs create mode 100644 tui/src/tui.rs create mode 100644 tui/src/tui_test.rs diff --git a/Justfile b/Justfile index 3dbff77..1d5f794 100644 --- a/Justfile +++ b/Justfile @@ -2,6 +2,9 @@ export LLVM_PROFILE_FILE := "cov/cargo-test-%p-%m.profraw" grcov-binary := "--binary-path ./target/coverage/deps/" grcov-ignore := "--ignore-not-existing --ignore '../*' --ignore \"/*\" --ignore 'target/*'" +default: + just -l + bacon: bacon -s @@ -25,5 +28,7 @@ doc: CARGO_INCREMENTAL=0 RUSTFLAGS='-Cinstrument-coverage' RUSTDOCFLAGS='-Cinstrument-coverage' \ cargo doc -example-tui: - cargo run -p tengri_tui --example tui +example-tui-00: + cargo run -p tengri_tui --example tui_00 +example-tui-01: + cargo run -p tengri_tui --example tui_01 diff --git a/bacon.toml b/bacon.toml new file mode 100644 index 0000000..8a5516f --- /dev/null +++ b/bacon.toml @@ -0,0 +1,44 @@ +default_job = "check-all" + +env.CARGO_TERM_COLOR = "always" + +[keybindings] +c = "job:check" +d = "job:doc-open" +t = "job:test" +n = "job:nextest" +l = "job:clippy" + +[jobs] + +[jobs.check] +command = ["cargo", "check"] +need_stdout = false +watch = ["core","dsl","editor","input","output","proc","tengri","tui"] + +[jobs.clippy-all] +command = ["cargo", "clippy"] +need_stdout = false +watch = ["tek", "deps"] + +[jobs.test] +command = ["cargo", "test"] +need_stdout = true +watch = ["tek", "deps"] + +[jobs.doc] +command = ["cargo", "doc", "--no-deps"] +need_stdout = false + +[jobs.doc-open] +command = ["cargo", "doc", "--no-deps", "--open"] +need_stdout = false +on_success = "back" # so that we don't open the browser at each change + +[skin] +status_fg = 251 +status_bg = 200 +key_fg = 11 +status_key_fg = 11 +project_name_badge_fg = 11 +project_name_badge_bg = 69 diff --git a/dsl/src/dsl_test.rs b/dsl/src/dsl_test.rs index 253b4c9..fb6d6f7 100644 --- a/dsl/src/dsl_test.rs +++ b/dsl/src/dsl_test.rs @@ -1,16 +1,16 @@ use crate::*; -macro_rules!is_some(($exp:expr, $val:expr)=>{assert_eq!($exp, Ok(Some($val)))};); -macro_rules!is_none(($exp:expr)=>{assert_eq!($exp, Ok(None))};); -macro_rules!is_err(($exp:expr)=>{assert!($exp.is_err())}; - ($exp:expr, $err:expr)=>{assert_eq!($exp, Err($err))};); -#[test] fn test_exp () -> Result<(), DslError> { - let e0 = DslError::Unexpected('a'); - let e1 = DslError::Unexpected('('); - let e2 = DslError::Unexpected('b'); - let e3 = DslError::Unexpected('d'); - let check = |src: &str, key, exp, head, tail|{ - assert_eq!(src.key(), key, "{src}"); - assert_eq!(src.exp(), exp, "{src}"); +macro_rules!is_some(($expr:expr, $val:expr)=>{assert_eq!($expr, Ok(Some($val)))};); +macro_rules!is_none(($expr:expr)=>{assert_eq!($expr, Ok(None))};); +macro_rules!is_err(($expr:expr)=>{assert!($expr.is_err())}; + ($expr:expr, $err:expr)=>{assert_eq!($expr, Err($err))};); +#[test] fn test_expr () -> Result<(), DslError> { + let e0 = DslError::Unexpected('a', None, None); + let e1 = DslError::Unexpected('(', None, None); + let e2 = DslError::Unexpected('b', None, None); + let e3 = DslError::Unexpected('d', None, None); + let check = |src: &str, word, expr, head, tail|{ + assert_eq!(src.word(), word, "{src}"); + assert_eq!(src.expr(), expr, "{src}"); assert_eq!(src.head(), head, "{src}"); assert_eq!(src.tail(), tail, "{src}"); }; @@ -21,43 +21,43 @@ macro_rules!is_err(($exp:expr)=>{assert!($exp.is_err())}; check("(a b c) d e f", Err(e1), Err(e3), Ok(Some("(a b c)")), Ok(Some("d e f"))); check("a (b c d) e f", Err(e1), Err(e0), Ok(Some("a")), Ok(Some("(b c d) e f"))); - is_some!(sym_peek("\n :view/transport"), ":view/transport"); + is_some!(word_peek("\n :view/transport"), ":view/transport"); - assert!(is_whitespace(' ')); - assert!(!is_key_start(' ')); - assert!(is_key_start('f')); + assert!(is_space(' ')); + assert!(!is_word_char(' ')); + assert!(is_word_char('f')); - is_some!(key_seek_start("foo"), 0); - is_some!(key_seek_start("foo "), 0); - is_some!(key_seek_start(" foo "), 1); - is_some!(key_seek_length(&" foo "[1..]), 3); - is_some!(key_seek("foo"), (0, 3)); - is_some!(key_peek("foo"), "foo"); - is_some!(key_seek("foo "), (0, 3)); - is_some!(key_peek("foo "), "foo"); - is_some!(key_seek(" foo "), (1, 3)); - is_some!(key_peek(" foo "), "foo"); + is_some!(word_seek_start("foo"), 0); + is_some!(word_seek_start("foo "), 0); + is_some!(word_seek_start(" foo "), 1); + is_some!(word_seek_length(&" foo "[1..]), 3); + is_some!(word_seek("foo"), (0, 3)); + is_some!(word_peek("foo"), "foo"); + is_some!(word_seek("foo "), (0, 3)); + is_some!(word_peek("foo "), "foo"); + is_some!(word_seek(" foo "), (1, 3)); + is_some!(word_peek(" foo "), "foo"); - is_err!("(foo)".key()); - is_err!("foo".exp()); + is_err!("(foo)".word()); + is_err!("foo".expr()); - is_some!("(foo)".exp(), "foo"); + is_some!("(foo)".expr(), "foo"); is_some!("(foo)".head(), "(foo)"); is_none!("(foo)".tail()); - is_some!("(foo bar baz)".exp(), "foo bar baz"); + is_some!("(foo bar baz)".expr(), "foo bar baz"); is_some!("(foo bar baz)".head(), "(foo bar baz)"); is_none!("(foo bar baz)".tail()); - is_some!("(foo bar baz)".exp().head(), "foo"); - is_some!("(foo bar baz)".exp().tail(), "bar baz"); - is_some!("(foo bar baz)".exp().tail().head(), "bar"); - is_some!("(foo bar baz)".exp().tail().tail(), "baz"); + is_some!("(foo bar baz)".expr().head(), "foo"); + is_some!("(foo bar baz)".expr().tail(), "bar baz"); + is_some!("(foo bar baz)".expr().tail().head(), "bar"); + is_some!("(foo bar baz)".expr().tail().tail(), "baz"); - is_err!("foo".exp()); - is_some!("foo".key(), "foo"); - is_some!(" foo".key(), "foo"); - is_some!(" foo ".key(), "foo"); + is_err!("foo".expr()); + is_some!("foo".word(), "foo"); + is_some!(" foo".word(), "foo"); + is_some!(" foo ".word(), "foo"); is_some!(" foo ".head(), "foo"); //assert_eq!(" foo ".head().head(), Ok(None)); @@ -84,16 +84,16 @@ macro_rules!is_err(($exp:expr)=>{assert!($exp.is_err())}; is_some!(" (foo) (bar) ".tail(), " (bar) "); is_some!(" (foo) (bar) ".tail().head(), "(bar)"); is_some!(" (foo) (bar) ".tail().head().head(), "(bar)"); - is_some!(" (foo) (bar) ".tail().head().exp(), "bar"); - is_some!(" (foo) (bar) ".tail().head().exp().head(), "bar"); + is_some!(" (foo) (bar) ".tail().head().expr(), "bar"); + is_some!(" (foo) (bar) ".tail().head().expr().head(), "bar"); is_some!(" (foo bar baz) ".head(), "(foo bar baz)"); is_some!(" (foo bar baz) ".head().head(), "(foo bar baz)"); - is_some!(" (foo bar baz) ".exp(), "foo bar baz"); - is_some!(" (foo bar baz) ".exp().head(), "foo"); - is_some!(" (foo bar baz) ".exp().tail(), "bar baz"); - is_some!(" (foo bar baz) ".exp().tail().head(), "bar"); - is_some!(" (foo bar baz) ".exp().tail().tail(), "baz"); + is_some!(" (foo bar baz) ".expr(), "foo bar baz"); + is_some!(" (foo bar baz) ".expr().head(), "foo"); + is_some!(" (foo bar baz) ".expr().tail(), "bar baz"); + is_some!(" (foo bar baz) ".expr().tail().head(), "bar"); + is_some!(" (foo bar baz) ".expr().tail().tail(), "baz"); is_none!(" (foo bar baz) ".tail()); Ok(()) } diff --git a/output/src/layout/layout_cond.rs b/output/src/layout/layout_cond.rs index 103166f..aceb535 100644 --- a/output/src/layout/layout_cond.rs +++ b/output/src/layout/layout_cond.rs @@ -1,15 +1,15 @@ use crate::*; /// Show an item only when a condition is true. -pub struct When(pub bool, pub A); -impl When { +pub struct When(bool, T, PhantomData); +impl When { /// Create a binary condition. - pub const fn new (c: bool, a: A) -> Self { Self(c, a) } + pub const fn new (c: bool, a: T) -> Self { Self(c, a, PhantomData) } } -impl> Layout for When { - fn layout (&self, to: E::Area) -> E::Area { - let Self(cond, item) = self; - let mut area = E::Area::zero(); +impl> Layout for When { + fn layout (&self, to: O::Area) -> O::Area { + let Self(cond, item, ..) = self; + let mut area = O::Area::zero(); if *cond { let item_area = item.layout(to); area[0] = item_area.x(); @@ -20,9 +20,9 @@ impl> Layout for When { area.into() } } -impl> Draw for When { - fn draw (&self, to: &mut E) { - let Self(cond, item) = self; +impl> Draw for When { + fn draw (&self, to: &mut O) { + let Self(cond, item, ..) = self; if *cond { item.draw(to) } } } diff --git a/output/src/layout/layout_map.rs b/output/src/layout/layout_map.rs index cb230e2..f509dcc 100644 --- a/output/src/layout/layout_map.rs +++ b/output/src/layout/layout_map.rs @@ -80,16 +80,16 @@ impl<'a, O, A, B, I, F, G> Layout for Map where let [mut max_x, mut max_y] = area.center(); for item in get_iter() { let [x,y,w,h] = get_item(item, index).layout(area).xywh(); - min_x = min_x.min(x.into()); - min_y = min_y.min(y.into()); - max_x = max_x.max((x + w).into()); - max_y = max_y.max((y + h).into()); + min_x = min_x.min(x); + min_y = min_y.min(y); + max_x = max_x.max(x + w); + max_y = max_y.max(y + h); index += 1; } let w = max_x - min_x; let h = max_y - min_y; //[min_x.into(), min_y.into(), w.into(), h.into()].into() - area.center_xy([w.into(), h.into()].into()).into() + area.center_xy([w.into(), h.into()]).into() } } impl<'a, O, A, B, I, F, G> Draw for Map where diff --git a/output/src/output.rs b/output/src/output.rs index 29ff6b2..00c7bb9 100644 --- a/output/src/output.rs +++ b/output/src/output.rs @@ -3,15 +3,9 @@ #![feature(impl_trait_in_assoc_type)] #![feature(const_precise_live_drops)] #![feature(type_changing_struct_update)] +#![feature(anonymous_lifetime_in_impl_trait)] //#![feature(non_lifetime_binders)] -mod content; pub use self::content::*; -mod draw; pub use self::draw::*; -mod group; pub use self::group::*; -mod layout; pub use self::layout::*; -mod space; pub use self::space::*; -mod thunk; pub use self::thunk::*; -mod widget; pub use self::widget::*; pub(crate) use self::Direction::*; pub(crate) use std::fmt::{Debug, Display}; pub(crate) use std::marker::PhantomData; @@ -19,38 +13,49 @@ pub(crate) use std::ops::{Add, Sub, Mul, Div}; pub(crate) use std::sync::{Arc, RwLock, atomic::{AtomicUsize, Ordering::Relaxed}}; pub(crate) use tengri_core::*; -/// Draw target. +/// Drawing target. pub trait Out: Send + Sync + Sized { /// Unit of length type Unit: Coordinate; + /// Rectangle without offset type Size: Size; + /// Rectangle with offset type Area: Area; - /// Current output area - fn area (&self) -> Self::Area; - /// Mutable pointer to area - fn area_mut (&mut self) -> &mut Self::Area; - /// Draw widget in area - fn place_at <'t, T: Draw + ?Sized> (&mut self, area: Self::Area, content: &'t T); - fn place <'t, T: Draw + Layout + ?Sized> (&mut self, content: &'t T) { + /// Render drawable in area specified by `T::layout(self.area())` + #[inline] fn place <'t, T: Draw + Layout + ?Sized> ( + &mut self, content: &'t T + ) { self.place_at(content.layout(self.area()), content) } + + /// Render drawable in area specified by `area` + fn place_at <'t, T: Draw + ?Sized> (&mut self, area: Self::Area, content: &'t T); + + /// Current output area + fn area (&self) -> Self::Area; #[inline] fn x (&self) -> Self::Unit { self.area().x() } #[inline] fn y (&self) -> Self::Unit { self.area().y() } #[inline] fn w (&self) -> Self::Unit { self.area().w() } #[inline] fn h (&self) -> Self::Unit { self.area().h() } #[inline] fn wh (&self) -> Self::Size { self.area().wh().into() } + + /// Mutable pointer to area. + fn area_mut (&mut self) -> &mut Self::Area; } -#[cfg(test)] mod test; +#[cfg(test)] mod output_test; #[cfg(test)] pub(crate) use proptest_derive::Arbitrary; -//impl + Layout> Draw for C { // if only - //fn draw (&self, to: &mut E) { - //if let Some(content) = self.content() { - //to.place_at(self.layout(to.area()), &content); - //} - //} -//} +mod content; pub use self::content::*; +mod draw; pub use self::draw::*; +mod group; pub use self::group::*; +mod layout; pub use self::layout::*; +mod space; pub use self::space::*; +mod thunk; pub use self::thunk::*; +mod widget; pub use self::widget::*; + +#[cfg(feature = "dsl")] mod view; +#[cfg(feature = "dsl")] pub use self::view::*; diff --git a/output/src/test.rs b/output/src/output_test.rs similarity index 92% rename from output/src/test.rs rename to output/src/output_test.rs index ce859ed..958f485 100644 --- a/output/src/test.rs +++ b/output/src/output_test.rs @@ -89,8 +89,8 @@ macro_rules! test_op_transform { (None, Some(y)) => Some($Op::y(y, content)), _ => None } { - assert_eq!(Content::layout(&op, [x, y, w, h]), - Draw::layout(&op, [x, y, w, h])); + //assert_eq!(Content::layout(&op, [x, y, w, h]), + //Draw::layout(&op, [x, y, w, h])); } } } @@ -122,10 +122,10 @@ proptest! { h in u16::MIN..u16::MAX, ) { let bsp = Bsp(d, a, b); - assert_eq!( - Content::layout(&bsp, [x, y, w, h]), - Draw::layout(&bsp, [x, y, w, h]), - ); + //assert_eq!( + //Content::layout(&bsp, [x, y, w, h]), + //Draw::layout(&bsp, [x, y, w, h]), + //); } } @@ -142,7 +142,8 @@ proptest! { fn area_mut (&mut self) -> &mut [u16;4] { &mut self.0 } - fn place + ?Sized> (&mut self, _: [u16;4], _: &T) { + fn place_at + ?Sized> (&mut self, area: [u16;4], _: &T) { + println!("place_at: {area:?}"); () } } diff --git a/output/src/view.rs b/output/src/view.rs new file mode 100644 index 0000000..bd1cc51 --- /dev/null +++ b/output/src/view.rs @@ -0,0 +1,137 @@ +use crate::*; +use ::tengri_dsl::{Dsl, DslExpr, DslWord, DslNs}; + +pub trait View { + fn view_expr <'a> (&'a self, output: &mut O, expr: &'a impl DslExpr) -> Usually { + Err(format!("View::view_expr: no exprs defined: {expr:?}").into()) + } + fn view_word <'a> (&'a self, output: &mut O, word: &'a impl DslWord) -> Usually { + Err(format!("View::view_word: no words defined: {word:?}").into()) + } + fn view <'a> (&'a self, output: &mut O, dsl: &'a impl Dsl) -> Usually { + if let Ok(Some(expr)) = dsl.expr() { + self.view_expr(output, &expr) + } else if let Ok(Some(word)) = dsl.word() { + self.view_word(output, &word) + } else { + panic!("{dsl:?}: invalid") + } + } +} + +pub fn evaluate_output_expression <'a, O: Out + 'a, S> ( + state: &S, output: &mut O, expr: &'a impl DslExpr +) -> Usually where + S: View + + for<'b>DslNs<'b, bool> + + for<'b>DslNs<'b, O::Unit> +{ + // First element of expression is used for dispatch. + // Dispatch is proto-namespaced using separator character + let head = expr.head()?; + let mut frags = head.src()?.unwrap_or_default().split("/"); + // The rest of the tokens in the expr are arguments. + // Their meanings depend on the dispatched operation + let args = expr.tail(); + let arg0 = args.head(); + let tail0 = args.tail(); + let arg1 = tail0.head(); + let tail1 = tail0.tail(); + let arg2 = tail1.head(); + // And we also have to do the above binding dance + // so that the Perhapss remain in scope. + match frags.next() { + + Some("when") => output.place(&When::new( + state.from(arg0?)?.unwrap(), + Thunk::new(move|output: &mut O|state.view(output, &arg1).unwrap()) + )), + + Some("either") => output.place(&Either::new( + state.from(arg0?)?.unwrap(), + Thunk::new(move|output: &mut O|state.view(output, &arg1).unwrap()), + Thunk::new(move|output: &mut O|state.view(output, &arg2).unwrap()) + )), + + Some("bsp") => output.place(&{ + let a = Thunk::new(move|output: &mut O|state.view(output, &arg0).unwrap()); + let b = Thunk::new(move|output: &mut O|state.view(output, &arg1).unwrap()); + match frags.next() { + Some("n") => Bsp::n(a, b), + Some("s") => Bsp::s(a, b), + Some("e") => Bsp::e(a, b), + Some("w") => Bsp::w(a, b), + Some("a") => Bsp::a(a, b), + Some("b") => Bsp::b(a, b), + frag => unimplemented!("bsp/{frag:?}") + } + }), + + Some("align") => output.place(&{ + let a = Thunk::new(move|output: &mut O|state.view(output, &arg0).unwrap()); + match frags.next() { + Some("n") => Align::n(a), + Some("s") => Align::s(a), + Some("e") => Align::e(a), + Some("w") => Align::w(a), + Some("x") => Align::x(a), + Some("y") => Align::y(a), + Some("c") => Align::c(a), + frag => unimplemented!("align/{frag:?}") + } + }), + + Some("fill") => output.place(&{ + let a = Thunk::new(move|output: &mut O|state.view(output, &arg0).unwrap()); + match frags.next() { + Some("x") => Fill::X(a), + Some("y") => Fill::Y(a), + Some("xy") => Fill::XY(a), + frag => unimplemented!("fill/{frag:?}") + } + }), + + Some("fixed") => output.place(&{ + let axis = frags.next(); + let arg = match axis { Some("x") | Some("y") => arg1, Some("xy") => arg2, _ => panic!() }; + let cb = Thunk::new(move|output: &mut O|state.view(output, &arg).unwrap()); + match axis { + Some("x") => Fixed::X(state.from(arg0?)?.unwrap(), cb), + Some("y") => Fixed::Y(state.from(arg0?)?.unwrap(), cb), + Some("xy") => Fixed::XY(state.from(arg0?)?.unwrap(), state.from(arg1?)?.unwrap(), cb), + frag => unimplemented!("fixed/{frag:?} ({expr:?}) ({head:?}) ({:?})", + head.src()?.unwrap_or_default().split("/").next()) + } + }), + + Some("min") => output.place(&{ + let c = match frags.next() { + Some("x") | Some("y") => arg1, Some("xy") => arg2, _ => panic!() + }; + let cb = Thunk::new(move|output: &mut O|state.view(output, &c).unwrap()); + match frags.next() { + Some("x") => Min::X(state.from(arg0?)?.unwrap(), cb), + Some("y") => Min::Y(state.from(arg0?)?.unwrap(), cb), + Some("xy") => Min::XY(state.from(arg0?)?.unwrap(), state.from(arg1?)?.unwrap(), cb), + frag => unimplemented!("min/{frag:?}") + } + }), + + Some("max") => output.place(&{ + let c = match frags.next() { + Some("x") | Some("y") => arg1, Some("xy") => arg2, _ => panic!() + }; + let cb = Thunk::new(move|output: &mut O|state.view(output, &c).unwrap()); + match frags.next() { + Some("x") => Max::X(state.from(arg0?)?.unwrap(), cb), + Some("y") => Max::Y(state.from(arg0?)?.unwrap(), cb), + Some("xy") => Max::XY(state.from(arg0?)?.unwrap(), state.from(arg1?)?.unwrap(), cb), + frag => unimplemented!("max/{frag:?}") + } + }), + + _ => return Ok(false) + + }; + Ok(true) +} diff --git a/tui/Cargo.toml b/tui/Cargo.toml index 9dfa085..3e9ab61 100644 --- a/tui/Cargo.toml +++ b/tui/Cargo.toml @@ -4,6 +4,9 @@ description = "UI metaframework, Ratatui backend." version = { workspace = true } edition = { workspace = true } +[lib] +path = "src/tui.rs" + [features] dsl = [ "dep:tengri_dsl", "tengri_output/dsl" ] bumpalo = [ "dep:bumpalo" ] diff --git a/tui/examples/tui.rs b/tui/examples/tui.rs deleted file mode 100644 index 2cc35b5..0000000 --- a/tui/examples/tui.rs +++ /dev/null @@ -1,226 +0,0 @@ -use tengri::{self, Usually, Perhaps, input::*, output::*, tui::*, dsl::*}; -use std::sync::{Arc, RwLock}; -use crate::ratatui::style::Color; -//use crossterm::event::{*, KeyCode::*}; - -fn main () -> Usually<()> { - let state = Example::new(); - Tui::new().unwrap().run(&state)?; - Ok(()) -} - -#[derive(Debug)] pub struct Example( - usize, - Measure -); - -impl Example { - fn new () -> Arc> { - Arc::new(RwLock::new(Example(10, Measure::new()))) - } - const BINDS: &'static str = stringify! { - (@left prev) - (@right next) - }; - const VIEWS: &'static [&'static str] = &[ - stringify! { :hello-world }, - stringify! { (fill/xy :hello-world) }, - stringify! { (bsp/s :hello :world) }, - stringify! { (fixed/xy 20 10 :hello-world) }, - stringify! { (bsp/s (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, - stringify! { (bsp/e (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, - stringify! { (bsp/n (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, - stringify! { (bsp/w (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, - stringify! { (bsp/a (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, - stringify! { (bsp/b (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, - stringify! { - (bsp/s - (bsp/e (align/nw (fixed/xy 5 3 :hello)) - (bsp/e (align/n (fixed/xy 5 3 :hello)) - (align/ne (fixed/xy 5 3 :hello)))) - (bsp/s - (bsp/e (align/w (fixed/xy 5 3 :hello)) - (bsp/e (align/c (fixed/xy 5 3 :hello)) - (align/e (fixed/xy 5 3 :hello)))) - (bsp/e (align/sw (fixed/xy 5 3 :hello)) - (bsp/e (align/s (fixed/xy 5 3 :hello)) - (align/se (fixed/xy 5 3 :hello)))))) - }, - stringify! { - (bsp/s - (bsp/e (fixed/xy 8 5 (align/nw :hello)) - (bsp/e (fixed/xy 8 5 (align/n :hello)) - (fixed/xy 8 5 (align/ne :hello)))) - (bsp/s - (bsp/e (fixed/xy 8 5 (align/w :hello)) - (bsp/e (fixed/xy 8 5 (align/c :hello)) - (fixed/xy 8 5 (align/e :hello)))) - (bsp/e (fixed/xy 8 5 (align/sw :hello)) - (bsp/e (fixed/xy 8 5 (align/s :hello)) - (fixed/xy 8 5 (align/se :hello)))))) - }, - stringify! { - (bsp/s - (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/nw :hello))) - (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/n :hello))) - (grow/xy 1 1 (fixed/xy 8 5 (align/ne :hello))))) - (bsp/s - (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/w :hello))) - (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/c :hello))) - (grow/xy 1 1 (fixed/xy 8 5 (align/e :hello))))) - (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/sw :hello))) - (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/s :hello))) - (grow/xy 1 1 (fixed/xy 8 5 (align/se :hello))))))) - }, - 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))))))) - }, - ]; -} - -tui_draw!(|self: Example, to|to.place(&self.content())); - -content!(TuiOut: |self: Example|{ - let index = self.0 + 1; - let wh = self.1.wh(); - let src = EXAMPLES.get(self.0).unwrap_or(&""); - let heading = format!("Example {}/{} in {:?}", index, EXAMPLES.len(), &wh); - let title = Tui::bg(Color::Rgb(60, 10, 10), Push::y(1, Align::n(heading))); - let code = Tui::bg(Color::Rgb(10, 60, 10), Push::y(2, Align::n(format!("{}", src)))); - let content = ();//Tui::bg(Color::Rgb(10, 10, 60), View(self, CstIter::new(src))); - self.1.of(Bsp::s(title, Bsp::n(""/*code*/, content))) -}); - -handle!(TuiIn: |self: Example, input|{ - Ok(None)/*if let Some(command) = CstIter::new(KEYMAP).command::<_, ExampleCommand, _>(self, input) { - command.execute(self)?; - Some(true) - } else { - None - })*/ -}); - -//#[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::command(Example)] -impl ExampleCommand { - fn next (state: &mut Example) -> Perhaps { - state.0 = (state.0 + 1) % EXAMPLES.len(); - Ok(None) - } - fn prev (state: &mut Example) -> Perhaps { - state.0 = if state.0 > 0 { state.0 - 1 } else { EXAMPLES.len() - 1 }; - Ok(None) - } -} - -//#[tengri_proc::view(TuiOut)] -//impl Example { - //pub fn title (&self) -> impl Draw + use<'_> { - //Tui::bg(Color::Rgb(60, 10, 10), Push::y(1, Align::n(format!("Example {}/{}:", self.0 + 1, EXAMPLES.len())))).boxed() - //} - //pub fn code (&self) -> impl Draw + use<'_> { - //Tui::bg(Color::Rgb(10, 60, 10), Push::y(2, Align::n(format!("{}", EXAMPLES[self.0])))).boxed() - //} - //pub fn hello (&self) -> impl Draw + use<'_> { - //Tui::bg(Color::Rgb(10, 100, 10), "Hello").boxed() - //} - //pub fn world (&self) -> impl Draw + use<'_> { - //Tui::bg(Color::Rgb(100, 10, 10), "world").boxed() - //} - //pub fn hello_world (&self) -> impl Draw + use<'_> { - //"Hello world!".boxed() - //} - //pub fn map_e (&self) -> impl Draw + use<'_> { - //Map::east(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed() - //} - //pub fn map_s (&self) -> impl Draw + use<'_> { - //Map::south(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed() - //} -//} -/* - - fn content (&self) -> dyn Draw { - 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(()) - //}))))) - //})) - } -*/ diff --git a/tui/examples/tui_00.rs b/tui/examples/tui_00.rs new file mode 100644 index 0000000..a4aa475 --- /dev/null +++ b/tui/examples/tui_00.rs @@ -0,0 +1,124 @@ +use tengri::{self, Usually, Perhaps, input::*, output::*, tui::*, dsl::*}; +use std::sync::{Arc, RwLock}; +use crate::ratatui::style::Color; +//use crossterm::event::{*, KeyCode::*}; + +fn main () -> Usually<()> { + let state = Example::new(); + Tui::new().unwrap().run(&state)?; + Ok(()) +} + +#[derive(Debug)] struct Example(usize, Measure); + +handle!(TuiIn: |self: Example, input|Ok(None)); +enum ExampleCommand { Next, Prev } +impl ExampleCommand { + fn eval (&self, state: &mut Example) -> Perhaps { + match self { + Self::Next => { + state.0 = (state.0 + 1) % Example::VIEWS.len(); + Ok(Some(Self::Prev)) + }, + Self::Prev => { + state.0 = if state.0 > 0 { state.0 - 1 } else { Example::VIEWS.len() - 1 }; + Ok(Some(Self::Next)) + } + } + } +} + +tui_draw!(|self: Example, to|{ + to.place(&self.content()); +}); +content!(TuiOut: |self: Example|{ + let index = self.0 + 1; + let wh = self.1.wh(); + let src = Self::VIEWS.get(self.0).unwrap_or(&""); + let heading = format!("Example {}/{} in {:?}", index, Self::VIEWS.len(), &wh); + let title = Tui::bg(Color::Rgb(60, 10, 10), Push::y(1, Align::n(heading))); + let code = Tui::bg(Color::Rgb(10, 60, 10), Push::y(2, Align::n(format!("{}", src)))); + let content = ();//Tui::bg(Color::Rgb(10, 10, 60), View(self, CstIter::new(src))); + self.1.of(Bsp::s(title, Bsp::n(""/*code*/, content))) +}); +impl View for Example { + fn view_expr <'a> (&'a self, to: &mut TuiOut, expr: &'a impl DslExpr) -> Usually<()> { + if evaluate_output_expression(self, to, expr)? + || evaluate_output_expression_tui(self, to, expr)? { + Ok(()) + } else { + Err(format!("Example::view_expr: unexpected: {expr:?}").into()) + } + } +} + +impl Example { + fn new () -> Arc> { + Arc::new(RwLock::new(Example(10, Measure::new()))) + } + const BINDS: &'static str = stringify! { + (@left prev) + (@right next) + }; + const VIEWS: &'static [&'static str] = &[ + stringify! { :hello-world }, + stringify! { (fill/xy :hello-world) }, + stringify! { (bsp/s :hello :world) }, + stringify! { (fixed/xy 20 10 :hello-world) }, + stringify! { (bsp/s (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, + stringify! { (bsp/e (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, + stringify! { (bsp/n (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, + stringify! { (bsp/w (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, + stringify! { (bsp/a (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, + stringify! { (bsp/b (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, + stringify! { + (bsp/s + (bsp/e (align/nw (fixed/xy 5 3 :hello)) + (bsp/e (align/n (fixed/xy 5 3 :hello)) + (align/ne (fixed/xy 5 3 :hello)))) + (bsp/s + (bsp/e (align/w (fixed/xy 5 3 :hello)) + (bsp/e (align/c (fixed/xy 5 3 :hello)) + (align/e (fixed/xy 5 3 :hello)))) + (bsp/e (align/sw (fixed/xy 5 3 :hello)) + (bsp/e (align/s (fixed/xy 5 3 :hello)) + (align/se (fixed/xy 5 3 :hello)))))) + }, + stringify! { + (bsp/s + (bsp/e (fixed/xy 8 5 (align/nw :hello)) + (bsp/e (fixed/xy 8 5 (align/n :hello)) + (fixed/xy 8 5 (align/ne :hello)))) + (bsp/s + (bsp/e (fixed/xy 8 5 (align/w :hello)) + (bsp/e (fixed/xy 8 5 (align/c :hello)) + (fixed/xy 8 5 (align/e :hello)))) + (bsp/e (fixed/xy 8 5 (align/sw :hello)) + (bsp/e (fixed/xy 8 5 (align/s :hello)) + (fixed/xy 8 5 (align/se :hello)))))) + }, + stringify! { + (bsp/s + (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/nw :hello))) + (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/n :hello))) + (grow/xy 1 1 (fixed/xy 8 5 (align/ne :hello))))) + (bsp/s + (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/w :hello))) + (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/c :hello))) + (grow/xy 1 1 (fixed/xy 8 5 (align/e :hello))))) + (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/sw :hello))) + (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/s :hello))) + (grow/xy 1 1 (fixed/xy 8 5 (align/se :hello))))))) + }, + 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))))))) + }, + ]; +} diff --git a/tui/examples/tui_01.rs b/tui/examples/tui_01.rs new file mode 100644 index 0000000..58d505e --- /dev/null +++ b/tui/examples/tui_01.rs @@ -0,0 +1,105 @@ +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 Draw + 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 Draw + use<'_> { + //Tui::bg(Color::Rgb(10, 60, 10), Push::y(2, Align::n(format!("{}", VIEWS[self.0])))).boxed() + //} + //pub fn hello (&self) -> impl Draw + use<'_> { + //Tui::bg(Color::Rgb(10, 100, 10), "Hello").boxed() + //} + //pub fn world (&self) -> impl Draw + use<'_> { + //Tui::bg(Color::Rgb(100, 10, 10), "world").boxed() + //} + //pub fn hello_world (&self) -> impl Draw + use<'_> { + //"Hello world!".boxed() + //} + //pub fn map_e (&self) -> impl Draw + use<'_> { + //Map::east(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed() + //} + //pub fn map_s (&self) -> impl Draw + use<'_> { + //Map::south(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed() + //} +//} + + //fn content (&self) -> dyn Draw { + //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(()) + ////}))))) + ////})) + //} diff --git a/tui/src/lib.rs b/tui/src/lib.rs deleted file mode 100644 index a2ca42b..0000000 --- a/tui/src/lib.rs +++ /dev/null @@ -1,59 +0,0 @@ -#![feature(type_changing_struct_update)] -mod tui_engine; pub use self::tui_engine::*; -mod tui_content; pub use self::tui_content::*; -pub(crate) use ::tengri_core::*; -#[cfg(feature = "dsl")] pub use ::tengri_dsl::*; -pub use ::tengri_input as input; pub(crate) use ::tengri_input::*; -pub use ::tengri_output as output; pub(crate) use ::tengri_output::*; -pub(crate) use atomic_float::AtomicF64; -pub use ::better_panic; pub(crate) use ::better_panic::{Settings, Verbosity}; -pub use ::palette; pub(crate) use ::palette::{*, convert::*, okhsl::*}; -pub use ::crossterm; pub(crate) use ::crossterm::{ - ExecutableCommand, - terminal::{EnterAlternateScreen, LeaveAlternateScreen, enable_raw_mode, disable_raw_mode}, - event::{Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState}, -}; -pub use ::ratatui; pub(crate) use ratatui::{ - prelude::{Color, Style, Buffer}, - style::Modifier, - backend::{Backend, CrosstermBackend, ClearType}, - layout::{Size, Rect}, - buffer::Cell -}; -pub(crate) use std::sync::{Arc, RwLock, atomic::{AtomicBool, Ordering::*}}; -pub(crate) use std::io::{stdout, Stdout}; -#[cfg(test)] #[test] fn test_tui_engine () -> Usually<()> { - use crate::*; - //use std::sync::{Arc, RwLock}; - struct TestComponent(String); - impl Content for TestComponent { - fn content (&self) -> impl Draw { - Some(self.0.as_str()) - } - } - impl Handle for TestComponent { - fn handle (&mut self, _from: &TuiIn) -> Perhaps { - Ok(None) - } - } - let engine = Tui::new()?; - engine.read().unwrap().exited.store(true, std::sync::atomic::Ordering::Relaxed); - let state = TestComponent("hello world".into()); - let _state = std::sync::Arc::new(std::sync::RwLock::new(state)); - //engine.run(&state)?; - Ok(()) -} -#[cfg(test)] #[test] fn test_parse_key () { - //use KeyModifiers as Mods; - let _test = |x: &str, y|assert_eq!(KeyMatcher::new(x).build(), Some(Event::Key(y))); - //test(":x", - //KeyEvent::new(KeyCode::Char('x'), Mods::NONE)); - //test(":ctrl-x", - //KeyEvent::new(KeyCode::Char('x'), Mods::CONTROL)); - //test(":alt-x", - //KeyEvent::new(KeyCode::Char('x'), Mods::ALT)); - //test(":shift-x", - //KeyEvent::new(KeyCode::Char('x'), Mods::SHIFT)); - //test(":ctrl-alt-shift-x", - //KeyEvent::new(KeyCode::Char('x'), Mods::CONTROL | Mods::ALT | Mods::SHIFT )); -} diff --git a/tui/src/tui.rs b/tui/src/tui.rs new file mode 100644 index 0000000..c54a44e --- /dev/null +++ b/tui/src/tui.rs @@ -0,0 +1,78 @@ +#![feature(type_changing_struct_update)] +#[cfg(test)] mod tui_test; +mod tui_engine; pub use self::tui_engine::*; +mod tui_content; pub use self::tui_content::*; +pub use ::{ + tengri_input, + tengri_output, + ratatui, + crossterm, + palette, + better_panic +}; +pub(crate) use ::{ + tengri_core::*, + tengri_input::*, + tengri_output::*, + atomic_float::AtomicF64, + std::{io::{stdout, Stdout}, sync::{Arc, RwLock, atomic::{AtomicBool, Ordering::*}}}, + better_panic::{Settings, Verbosity}, + palette::{*, convert::*, okhsl::*}, + ratatui::{ + prelude::{Color, Style, Buffer}, + style::Modifier, + backend::{Backend, CrosstermBackend, ClearType}, + layout::{Size, Rect}, + buffer::Cell + }, + crossterm::{ + ExecutableCommand, + terminal::{EnterAlternateScreen, LeaveAlternateScreen, enable_raw_mode, disable_raw_mode}, + event::{Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState}, + } +}; + +#[cfg(feature = "dsl")] use tengri_dsl::*; +#[cfg(feature = "dsl")] +pub fn evaluate_output_expression_tui <'a, S> ( + state: &S, mut output: &mut TuiOut, expr: impl DslExpr + 'a +) -> Usually where + S: View + + for<'b>DslNs<'b, bool> + + for<'b>DslNs<'b, u16> + + for<'b>DslNs<'b, Color> +{ + // See `tengri_output::evaluate_output_expression` + let head = expr.head()?; + let mut frags = head.src()?.unwrap_or_default().split("/"); + let args = expr.tail(); + let arg0 = args.head(); + let tail0 = args.tail(); + let arg1 = tail0.head(); + let tail1 = tail0.tail(); + let arg2 = tail1.head(); + match frags.next() { + + Some("text") => if let Some(src) = args?.src()? { output.place(&src) }, + + Some("fg") => { + let arg0 = arg0?.expect("fg: expected arg 0 (color)"); + output.place(&Tui::fg( + DslNs::::from(state, arg0)?.unwrap_or_else(||panic!("fg: {arg0:?}: not a color")), + Thunk::new(move|output: &mut TuiOut|state.view(output, &arg1).unwrap()), + )) + }, + + Some("bg") => { + let arg0 = arg0?.expect("bg: expected arg 0 (color)"); + output.place(&Tui::bg( + DslNs::::from(state, arg0)?.unwrap_or_else(||panic!("bg: {arg0:?}: not a color")), + Thunk::new(move|output: &mut TuiOut|state.view(output, &arg1).unwrap()), + )) + }, + + _ => return Ok(false) + + }; + Ok(true) +} diff --git a/tui/src/tui_content.rs b/tui/src/tui_content.rs index 184d7ea..90e6f3a 100644 --- a/tui/src/tui_content.rs +++ b/tui/src/tui_content.rs @@ -37,6 +37,14 @@ impl Tui { } }); +#[macro_export] macro_rules! tui_content ((|$self:ident:$Self:ty|$sexpr:expr)=>{ + impl Content for $Self { + fn content (&$self) -> impl Draw + Layout + '_ { + $expr + } + } +}); + mod tui_border; pub use self::tui_border::*; mod tui_button; pub use self::tui_button::*; mod tui_color; pub use self::tui_color::*; diff --git a/tui/src/tui_test.rs b/tui/src/tui_test.rs new file mode 100644 index 0000000..9db2a6d --- /dev/null +++ b/tui/src/tui_test.rs @@ -0,0 +1,35 @@ +use crate::*; +#[test] fn test_tui_engine () -> Usually<()> { + //use std::sync::{Arc, RwLock}; + struct TestComponent(String); + impl Content for TestComponent { + fn content (&self) -> impl Draw + Layout { + Some(self.0.as_str()) + } + } + impl Handle for TestComponent { + fn handle (&mut self, _from: &TuiIn) -> Perhaps { + Ok(None) + } + } + let engine = Tui::new()?; + engine.read().unwrap().exited.store(true, std::sync::atomic::Ordering::Relaxed); + let state = TestComponent("hello world".into()); + let _state = std::sync::Arc::new(std::sync::RwLock::new(state)); + //engine.run(&state)?; + Ok(()) +} +//#[test] fn test_parse_key () { + ////use KeyModifiers as Mods; + //let _test = |x: &str, y|assert_eq!(KeyMatcher::new(x).build(), Some(Event::Key(y))); + ////test(":x", + ////KeyEvent::new(KeyCode::Char('x'), Mods::NONE)); + ////test(":ctrl-x", + ////KeyEvent::new(KeyCode::Char('x'), Mods::CONTROL)); + ////test(":alt-x", + ////KeyEvent::new(KeyCode::Char('x'), Mods::ALT)); + ////test(":shift-x", + ////KeyEvent::new(KeyCode::Char('x'), Mods::SHIFT)); + ////test(":ctrl-alt-shift-x", + ////KeyEvent::new(KeyCode::Char('x'), Mods::CONTROL | Mods::ALT | Mods::SHIFT )); +//} From 3ebdf9e71f4ac50b680c3b2164a6a9361b2d3e4d Mon Sep 17 00:00:00 2001 From: unspeaker Date: Tue, 9 Sep 2025 01:07:19 +0300 Subject: [PATCH 3/3] uuugh --- core/src/core_macros.rs | 32 ++++--- output/src/content.rs | 140 +++++++++++++++++++++------- output/src/draw.rs | 107 ++++----------------- output/src/layout.rs | 104 ++------------------- output/src/layout/layout_align.rs | 60 +++++------- output/src/layout/layout_bsp.rs | 81 ++++++++++++---- output/src/layout/layout_cond.rs | 33 +++---- output/src/layout/layout_expand.rs | 39 -------- output/src/layout/layout_fill.rs | 55 ----------- output/src/layout/layout_fixed.rs | 47 ---------- output/src/layout/layout_map.rs | 32 +++---- output/src/layout/layout_margin.rs | 50 ---------- output/src/layout/layout_max.rs | 47 ---------- output/src/layout/layout_min.rs | 47 ---------- output/src/layout/layout_move.rs | 33 +++++++ output/src/layout/layout_pad.rs | 27 ++++++ output/src/layout/layout_padding.rs | 50 ---------- output/src/layout/layout_pull.rs | 48 ---------- output/src/layout/layout_push.rs | 43 --------- output/src/layout/layout_shrink.rs | 43 --------- output/src/layout/layout_size.rs | 121 ++++++++++++++++++++++++ output/src/output.rs | 5 +- output/src/output_test.rs | 7 +- output/src/space/space_measure.rs | 2 +- output/src/thunk.rs | 50 +++------- output/src/view.rs | 66 +++++++------ output/src/widget/widget_field.rs | 22 +---- tui/examples/tui_00.rs | 25 +++-- tui/src/tui_content.rs | 90 +++--------------- tui/src/tui_content/tui_border.rs | 59 ++++++------ tui/src/tui_content/tui_button.rs | 24 ++--- tui/src/tui_content/tui_field.rs | 36 +------ tui/src/tui_content/tui_phat.rs | 14 +-- tui/src/tui_content/tui_repeat.rs | 15 --- tui/src/tui_content/tui_string.rs | 34 ++----- tui/src/tui_content/tui_tryptich.rs | 17 ++-- tui/src/tui_engine/tui_output.rs | 3 - tui/src/tui_test.rs | 2 +- 38 files changed, 602 insertions(+), 1108 deletions(-) delete mode 100644 output/src/layout/layout_expand.rs delete mode 100644 output/src/layout/layout_fill.rs delete mode 100644 output/src/layout/layout_fixed.rs delete mode 100644 output/src/layout/layout_margin.rs delete mode 100644 output/src/layout/layout_max.rs delete mode 100644 output/src/layout/layout_min.rs create mode 100644 output/src/layout/layout_move.rs create mode 100644 output/src/layout/layout_pad.rs delete mode 100644 output/src/layout/layout_padding.rs delete mode 100644 output/src/layout/layout_pull.rs delete mode 100644 output/src/layout/layout_push.rs delete mode 100644 output/src/layout/layout_shrink.rs create mode 100644 output/src/layout/layout_size.rs diff --git a/core/src/core_macros.rs b/core/src/core_macros.rs index a8fdda4..ef56421 100644 --- a/core/src/core_macros.rs +++ b/core/src/core_macros.rs @@ -10,22 +10,25 @@ )* }; ); -/// Define a trait an implement it for read-only wrapper types. */ +/// Define a trait and implement it for read-only wrapper types. #[macro_export] macro_rules! flex_trait ( ($Trait:ident $(<$($A:ident:$T:ident),+>)? $(:$dep:ident $(<$dtt:tt>)? $(+$dep2:ident $(<$dtt2:tt>)?)*)? { - $(fn $fn:ident (&$self:ident $(, $arg:ident:$ty:ty)*) -> $ret:ty $body:block)* + $(fn $fn:ident $(<$($fl:lifetime),*>)? (& $($fl2:lifetime)* $self:ident $(, $arg:ident:$ty:ty)*) $(-> $ret:ty)? $body:block)* }) => { pub trait $Trait $(<$($A: $T),+>)? $(:$dep $(<$dtt>)? $(+$dep2 $(<$dtt2>)?)*)? { - $(fn $fn (&$self $(,$arg:$ty)*) -> $ret $body)* + $(fn $fn $(<$($fl),*>)? (& $($fl2)* $self $(,$arg:$ty)*) $(-> $ret)? $body)* } impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for &_T_ { - $(fn $fn (&$self $(,$arg:$ty)*) -> $ret { (*$self).$fn($($arg),*) })* + $(fn $fn $(<$($fl),*>)? (& $($fl2)* $self $(,$arg:$ty)*) $(-> $ret)? { (*$self).$fn($($arg),*) })* } impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for &mut _T_ { - $(fn $fn (&$self $(,$arg:$ty)*) -> $ret { (**$self).$fn($($arg),*) })* + $(fn $fn $(<$($fl),*>)? (& $($fl2)* $self $(,$arg:$ty)*) $(-> $ret)? { (**$self).$fn($($arg),*) })* } impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Arc<_T_> { - $(fn $fn (&$self $(,$arg:$ty)*) -> $ret { (*$self).$fn($($arg),*) })* + $(fn $fn $(<$($fl),*>)? (& $($fl2)* $self $(,$arg:$ty)*) $(-> $ret)? { (*$self).$fn($($arg),*) })* + } + impl<$($($A: $T,)+)?> $Trait $(<$($A),+>)? for Box)?> { + $(fn $fn $(<$($fl),*>)? (& $($fl2)* $self $(,$arg:$ty)*) $(-> $ret)? { (**$self).$fn($($arg),*) })* } //impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for Option<_T_> { //$(fn $fn (&$self $(,$arg:$ty)*) -> $ret { @@ -40,21 +43,24 @@ //} }); /// Define a trait an implement it for read-only wrapper types. */ -#[macro_export] macro_rules! flex_trait_unsized ( +#[macro_export] macro_rules! flex_trait_sized ( ($Trait:ident $(<$($A:ident:$T:ident),+>)? $(:$dep:ident $(<$dtt:tt>)? $(+$dep2:ident $(<$dtt2:tt>)?)*)? { - $(fn $fn:ident (&$self:ident $(, $arg:ident:$ty:ty)*) -> $ret:ty $body:block)* + $(fn $fn:ident (&$self:ident $(, $arg:ident:$ty:ty)*) $(-> $ret:ty)? $body:block)* }) => { - pub trait $Trait $(<$($A: $T),+>)? : $($dep $(<$dtt>+)? $($dep2 $(<$dtt2>)?)*+)? ?Sized { - $(fn $fn (&$self $(,$arg:$ty)*) -> $ret $body)* + pub trait $Trait $(<$($A: $T),+>)? : $($dep $(<$dtt>+)? $($dep2 $(<$dtt2>)?)*+)? Sized { + $(fn $fn (&$self $(,$arg:$ty)*) $(-> $ret)? $body)* } impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for &_T_ { - $(fn $fn (&$self $(,$arg:$ty)*) -> $ret { (*$self).$fn($($arg),*) })* + $(fn $fn (&$self $(,$arg:$ty)*) $(-> $ret)? { (*$self).$fn($($arg),*) })* } impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for &mut _T_ { - $(fn $fn (&$self $(,$arg:$ty)*) -> $ret { (**$self).$fn($($arg),*) })* + $(fn $fn (&$self $(,$arg:$ty)*) $(-> $ret)? { (**$self).$fn($($arg),*) })* } impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Arc<_T_> { - $(fn $fn (&$self $(,$arg:$ty)*) -> $ret { (*$self).$fn($($arg),*) })* + $(fn $fn (&$self $(,$arg:$ty)*) $(-> $ret)? { (*$self).$fn($($arg),*) })* + } + impl<$($($A: $T,)+)?> $Trait $(<$($A),+>)? for Box)?> { + $(fn $fn (&$self $(,$arg:$ty)*) $(-> $ret)? { (**$self).$fn($($arg),*) })* } //impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for Option<_T_> { //$(fn $fn (&$self $(,$arg:$ty)*) -> $ret { diff --git a/output/src/content.rs b/output/src/content.rs index f797f98..4407023 100644 --- a/output/src/content.rs +++ b/output/src/content.rs @@ -1,40 +1,110 @@ use crate::*; - -/// Composable renderable with static dispatch. -pub trait Content: Sized { - /// Return opaque [Draw]able. - fn content (&self) -> impl Draw + Layout + '_ { () } -} - -/// The platonic ideal unit of [Content]: -/// total emptiness at dead center (e=1vg^sqrt(-1)) -impl Content for () {} - -impl + Layout> Content for fn()->T { - fn content (&self) -> impl Draw + Layout + '_ { - self() +pub trait HasContent { fn content (&self) -> impl Content; } +pub trait Content: Draw + Layout {} +impl + Layout> Content for T {} +impl<'a, O: Out> AsRef + 'a> for dyn Content + 'a { fn as_ref (&self) -> &(dyn Draw + 'a) { self } } +impl<'a, O: Out> AsRef + 'a> for dyn Content + 'a { fn as_ref (&self) -> &(dyn Layout + 'a) { self } } +/// Drawable with dynamic dispatch. +pub trait Draw { fn draw (&self, to: &mut O); } +impl Draw for () { fn draw (&self, to: &mut O) {} } +impl Draw for fn(&mut O) { fn draw (&self, to: &mut O) { (*self)(to) } } +impl Draw for Box> { fn draw (&self, to: &mut O) { (**self).draw(to) } } +impl> Draw for &D { fn draw (&self, to: &mut O) { (*self).draw(to) } } +impl> Draw for &mut D { fn draw (&self, to: &mut O) { (**self).draw(to) } } +/// Drawable area of display. +pub trait Layout { + fn x (&self, to: O::Area) -> O::Unit { to.x() } + fn y (&self, to: O::Area) -> O::Unit { to.y() } + fn min_w (&self, to: O::Area) -> O::Unit { 0.into() } + fn max_w (&self, to: O::Area) -> O::Unit { to.w() } + fn w (&self, to: O::Area) -> O::Unit { + to.w().max(self.min_w(to)).min(self.max_w(to)) + } + fn min_h (&self, to: O::Area) -> O::Unit { 0.into() } + fn max_h (&self, to: O::Area) -> O::Unit { to.h() } + fn h (&self, to: O::Area) -> O::Unit { + to.h().max(self.min_h(to)).min(self.max_h(to)) + } + fn layout (&self, to: O::Area) -> O::Area { + [self.x(to), self.y(to), self.w(to), self.h(to)].into() } } -/// Implement composable content for a struct. -#[macro_export] macro_rules! content { - - // Implement for all [Out]s. - (|$self:ident:$Struct:ty| $content:expr) => { - impl Content for $Struct { - fn content (&$self) -> impl Draw + Layout + '_ { Some($content) } - } - }; - - // Implement for specific [Out]. - ($Out:ty:| - $self:ident: - $Struct:ident$(<$($($L:lifetime)? $($T:ident)? $(:$Trait:path)?),+>)? - |$content:expr) => { - impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Content<$Out> - for $Struct $(<$($($L)? $($T)?),+>)? { - fn content (&$self) -> impl Draw<$Out> + Layout<$Out> + '_ { $content } - } - }; - +impl Layout for () { + fn x (&self, a: O::Area) -> O::Unit { a.x() } + fn y (&self, a: O::Area) -> O::Unit { a.y() } + fn w (&self, _: O::Area) -> O::Unit { 0.into() } + fn min_w (&self, _: O::Area) -> O::Unit { 0.into() } + fn max_w (&self, _: O::Area) -> O::Unit { 0.into() } + fn h (&self, _: O::Area) -> O::Unit { 0.into() } + fn min_h (&self, _: O::Area) -> O::Unit { 0.into() } + fn max_h (&self, _: O::Area) -> O::Unit { 0.into() } + fn layout (&self, a: O::Area) -> O::Area { [a.x(), a.y(), 0.into(), 0.into()].into() } +} + +impl> Layout for &L { + fn x (&self, a: O::Area) -> O::Unit { (*self).x(a) } + fn y (&self, a: O::Area) -> O::Unit { (*self).y(a) } + fn w (&self, a: O::Area) -> O::Unit { (*self).w(a) } + fn min_w (&self, a: O::Area) -> O::Unit { (*self).min_w(a) } + fn max_w (&self, a: O::Area) -> O::Unit { (*self).max_w(a) } + fn h (&self, a: O::Area) -> O::Unit { (*self).h(a) } + fn min_h (&self, a: O::Area) -> O::Unit { (*self).min_h(a) } + fn max_h (&self, a: O::Area) -> O::Unit { (*self).max_h(a) } + fn layout (&self, a: O::Area) -> O::Area { (*self).layout(a) } +} + +impl> Layout for &mut L { + fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) } + fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) } + fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) } + fn min_w (&self, a: O::Area) -> O::Unit { (**self).min_w(a) } + fn max_w (&self, a: O::Area) -> O::Unit { (**self).max_w(a) } + fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) } + fn min_h (&self, a: O::Area) -> O::Unit { (**self).min_h(a) } + fn max_h (&self, a: O::Area) -> O::Unit { (**self).max_h(a) } + fn layout (&self, a: O::Area) -> O::Area { (**self).layout(a) } +} + +impl Layout for Box> { + fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) } + fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) } + fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) } + fn min_w (&self, a: O::Area) -> O::Unit { (**self).min_w(a) } + fn max_w (&self, a: O::Area) -> O::Unit { (**self).max_w(a) } + fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) } + fn min_h (&self, a: O::Area) -> O::Unit { (**self).min_h(a) } + fn max_h (&self, a: O::Area) -> O::Unit { (**self).max_h(a) } + fn layout (&self, a: O::Area) -> O::Area { (**self).layout(a) } +} + +impl> Layout for Option { + fn x (&self, to: O::Area) -> O::Unit { + self.as_ref().map(|c|c.x(to)).unwrap_or(to.x()) + } + fn y (&self, to: O::Area) -> O::Unit { + self.as_ref().map(|c|c.y(to)).unwrap_or(to.y()) + } + fn min_w (&self, to: O::Area) -> O::Unit { + self.as_ref().map(|c|c.min_w(to)).unwrap_or(0.into()) + } + fn max_w (&self, to: O::Area) -> O::Unit { + self.as_ref().map(|c|c.max_w(to)).unwrap_or(0.into()) + } + fn w (&self, to: O::Area) -> O::Unit { + self.as_ref().map(|c|c.w(to)).unwrap_or(0.into()) + } + fn min_h (&self, to: O::Area) -> O::Unit { + self.as_ref().map(|c|c.min_h(to)).unwrap_or(0.into()) + } + fn max_h (&self, to: O::Area) -> O::Unit { + self.as_ref().map(|c|c.max_h(to)).unwrap_or(0.into()) + } + fn h (&self, to: O::Area) -> O::Unit { + self.as_ref().map(|c|c.h(to)).unwrap_or(0.into()) + } + fn layout (&self, to: O::Area) -> O::Area { + self.as_ref().map(|c|c.layout([self.x(to), self.y(to), self.w(to), self.h(to)].into())) + .unwrap_or([to.x(), to.y(), 0.into(), 0.into()].into()) + } } diff --git a/output/src/draw.rs b/output/src/draw.rs index f78574d..9826267 100644 --- a/output/src/draw.rs +++ b/output/src/draw.rs @@ -1,94 +1,27 @@ use crate::*; -/// Drawable with dynamic dispatch. -pub trait Draw { - /// Write data to display. - fn draw (&self, to: &mut O); +pub struct Bound(pub O::Area, pub D); + +impl> HasContent for Bound { + fn content (&self) -> &impl Content { &self.1 } } -impl Draw for fn(&mut O) { +impl Layout for Bound { + fn x (&self, _: O::Area) -> O::Unit { self.0.x() } + fn y (&self, _: O::Area) -> O::Unit { self.0.y() } + fn w (&self, _: O::Area) -> O::Unit { self.0.w() } + fn min_w (&self, _: O::Area) -> O::Unit { self.0.w() } + fn max_w (&self, _: O::Area) -> O::Unit { self.0.w() } + fn h (&self, _: O::Area) -> O::Unit { self.0.h() } + fn min_h (&self, _: O::Area) -> O::Unit { self.0.w() } + fn max_h (&self, _: O::Area) -> O::Unit { self.0.w() } +} + +impl> Draw for Bound { fn draw (&self, to: &mut O) { - self(to) - } -} - -impl Draw for () { fn draw (&self, _: &mut O) {} } - -impl> Draw for &T { - fn draw (&self, to: &mut O) { (*self).draw(to) } -} - -impl<'x, O: Out> Draw for &(dyn Draw + 'x) { - fn draw (&self, to: &mut O) { - (*self).draw(to) - } -} - -impl<'x, O: Out> Draw for &mut (dyn Draw + 'x) { - fn draw (&self, to: &mut O) { - (**self).draw(to) - } -} - -impl> Draw for RwLock { - fn draw (&self, to: &mut O) { self.read().unwrap().draw(to) } -} - -impl> Draw for [T] { - fn draw (&self, to: &mut O) { - for draw in self.iter() { - draw.draw(to) - } - } -} - -//impl> Draw for &mut T { - //fn draw (&self, to: &mut O) { - //(**self).draw(to) - //} -//} - -//impl, U: Draw> Draw for &mut T { - //fn draw (&self, to: &mut O) { - //for draw in *self { - //draw.draw(to) - //} - //} -//} - -/// Implement custom drawing for a struct. -#[macro_export] macro_rules! draw { - - // Implement for all [Out] backends. - (|$self:ident:$Struct:ident $(< - $($L:lifetime),* $($T:ident $(:$Trait:path)?),* - >)?, $to:ident | $draw:expr) => { - impl <$($($L),*)? O: Out, $($($T$(:$Trait)?),*)?> Draw - for $Struct $(<$($L),* $($T),*>)? { - fn draw (&$self, $to: &mut O) { $draw } - } - }; - - // Implement for a specific [Out] backend. - ($O:ty:| - $self:ident: - $Struct:ident $(<$($($L:lifetime)? $($T:ident)? $(:$Trait:path)?),+>)?, $to:ident - |$draw:expr) => { - impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Draw<$O> - for $Struct $(<$($($L)? $($T)?),+>)? { - fn draw (&$self, $to: &mut $O) { $draw } - } - }; -} - -draw!(|self: Arc>, to|(**self).draw(to)); -draw!(|self: Box>, to|(**self).draw(to)); -//draw!(|self: Option>, to|if let Some(draw) = self { draw.draw(to) }); - -impl> Draw for Option { - fn draw (&self, to: &mut O) { - if let Some(draw) = self { - draw.draw(to) - } + let area = to.area(); + *to.area_mut() = self.0; + self.1.draw(to); + *to.area_mut() = area; } } diff --git a/output/src/layout.rs b/output/src/layout.rs index ec42644..85ca20a 100644 --- a/output/src/layout.rs +++ b/output/src/layout.rs @@ -1,98 +1,10 @@ use crate::*; -pub trait Layout { - fn x (&self, area: O::Area) -> O::Unit { - area.x() - } - fn y (&self, area: O::Area) -> O::Unit { - area.y() - } - - fn min_w (&self, _area: O::Area) -> O::Unit { - 0.into() - } - fn max_w (&self, area: O::Area) -> O::Unit { - area.w() - } - - fn min_h (&self, _area: O::Area) -> O::Unit { - 0.into() - } - fn max_h (&self, area: O::Area) -> O::Unit { - area.h() - } - - fn layout (&self, area: O::Area) -> O::Area { - O::Area::from([ - self.x(area), - self.y(area), - area.w().max(self.min_w(area)).min(self.max_w(area)), - area.h().max(self.min_h(area)).min(self.max_h(area)), - ]) - } -} - -#[macro_export] macro_rules! layout { - // Implement for all [Out] backends. - (|$self:ident:$Struct:ident $(< - $($L:lifetime),* $($T:ident $(:$Trait:path)?),* - >)?, $to:ident|$($method:ident = |$area:ident|$body:expr;)*) => { - impl <$($($L),*)? O: Out, $($($T$(:$Trait)?),*)?> Layout - for $Struct $(<$($L),* $($T),*>)? { - $(fn $method (&$self, $area: O::Area) -> O::Area { - $body - })* - } - }; - // Implement for a specific [Out] backend. - ($O:ty:| - $self:ident: - $Struct:ident $(<$($($L:lifetime)? $($T:ident)? $(:$Trait:path)?),+>)?, - $to:ident - |$($method:ident = |$area:ident|$body:expr;)*) => { - impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Layout<$O> - for $Struct $(<$($($L)? $($T)?),+>)? { - $(fn $method (&$self, $area: <$O as Out>::Area) -> <$O as Out>::Area { - $body - })* - } - }; -} - -impl Layout for () {} - -impl> Layout for &L { /*FIXME*/ } - -impl> Layout for RwLock { /*FIXME*/ } - -impl> Layout for Option { /*FIXME*/ } - -//impl Layout for fn(&mut O) {} - -impl> Layout for Arc { - fn layout (&self, to: O::Area) -> O::Area { - (**self).layout(to) - } -} - -impl<'x, O: Out> Layout for &(dyn Draw + 'x) { - fn layout (&self, to: O::Area) -> O::Area { - Fill::xy(self).layout(to) - } -} - -mod layout_align; pub use self::layout_align::*; -mod layout_bsp; pub use self::layout_bsp::*; -mod layout_cond; pub use self::layout_cond::*; -mod layout_expand; pub use self::layout_expand::*; -mod layout_fill; pub use self::layout_fill::*; -mod layout_fixed; pub use self::layout_fixed::*; -mod layout_map; pub use self::layout_map::*; -mod layout_margin; pub use self::layout_margin::*; -mod layout_max; pub use self::layout_max::*; -mod layout_min; pub use self::layout_min::*; -mod layout_padding; pub use self::layout_padding::*; -mod layout_pull; pub use self::layout_pull::*; -mod layout_push; pub use self::layout_push::*; -mod layout_shrink; pub use self::layout_shrink::*; -mod layout_stack; //pub use self::layout_stack::*; +mod layout_align; pub use self::layout_align::*; +mod layout_bsp; pub use self::layout_bsp::*; +mod layout_cond; pub use self::layout_cond::*; +mod layout_map; pub use self::layout_map::*; +mod layout_pad; pub use self::layout_pad::*; +mod layout_move; pub use self::layout_move::*; +mod layout_size; pub use self::layout_size::*; +mod layout_stack; //pub use self::layout_stack::*; diff --git a/output/src/layout/layout_align.rs b/output/src/layout/layout_align.rs index c8f0922..6fab1b7 100644 --- a/output/src/layout/layout_align.rs +++ b/output/src/layout/layout_align.rs @@ -6,7 +6,7 @@ //! assert_eq!(Draw::layout(item, area), expected); //! }; //! -//! let four = ||Fixed::xy(4, 4, ""); +//! 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]); @@ -16,7 +16,7 @@ //! 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, ""); +//! 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]); @@ -27,13 +27,12 @@ //! test(area, &Align::w(two_by_four()), [10, 19, 4, 2]); //! ``` use crate::*; +use Alignment::*; /// 9th of area to place. #[derive(Debug, Copy, Clone, Default)] pub enum Alignment { #[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W } - pub struct Align(Alignment, T); - impl Align { #[inline] pub const fn c (a: T) -> Self { Self(Alignment::Center, a) } #[inline] pub const fn x (a: T) -> Self { Self(Alignment::X, a) } @@ -47,39 +46,24 @@ impl Align { #[inline] pub const fn ne (a: T) -> Self { Self(Alignment::NE, a) } #[inline] pub const fn se (a: T) -> Self { Self(Alignment::SE, a) } } - -impl> Layout for Align { - fn layout (&self, on: E::Area) -> E::Area { - self.0.align(on, &self.1) - } -} -impl + Layout> Draw for Align { - fn draw (&self, to: &mut E) { - to.place_at(self.layout(to.area()), &self.1) - } -} - -impl Alignment { - fn align (&self, on: E::Area, content: &impl Layout) -> E::Area { - use Alignment::*; - let it = content.layout(on).xywh(); - let cx = on.x()+(on.w().minus(it.w())/2.into()); - let cy = on.y()+(on.h().minus(it.h())/2.into()); - let fx = (on.x()+on.w()).minus(it.w()); - let fy = (on.y()+on.h()).minus(it.h()); - let [x, y] = match self { - Center => [cx, cy], - X => [cx, it.y()], - Y => [it.x(), cy], - NW => [on.x(), on.y()], - N => [cx, on.y()], - NE => [fx, on.y()], - W => [on.x(), cy], - E => [fx, cy], - SW => [on.x(), fy], - S => [cx, fy], - SE => [fx, fy], - }; - [x, y, it.w(), it.h()].into() +impl> Draw for Align { + fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), &self.1).draw(to) } +} +impl> Layout for Align { + fn x (&self, to: O::Area) -> O::Unit { + match self.0 { + NW | W | SW => to.x(), + N | Center | S => to.x().plus(to.w() / 2.into()).minus(self.1.w(to) / 2.into()), + NE | E | SE => to.x().plus(to.w()).minus(self.1.w(to)), + _ => todo!(), + } + } + fn y (&self, to: O::Area) -> O::Unit { + match self.0 { + NW | N | NE => to.y(), + W | Center | E => to.y().plus(to.h() / 2.into()).minus(self.1.h(to) / 2.into()), + SW | S | SE => to.y().plus(to.h()).minus(self.1.h(to)), + _ => todo!(), + } } } diff --git a/output/src/layout/layout_bsp.rs b/output/src/layout/layout_bsp.rs index ff3e4aa..3de4cfc 100644 --- a/output/src/layout/layout_bsp.rs +++ b/output/src/layout/layout_bsp.rs @@ -1,7 +1,6 @@ use crate::*; -use Direction::*; -/// A split or layer. +/// A binary split or layer. pub struct Bsp( pub(crate) Direction, /// First element. @@ -19,31 +18,79 @@ impl Bsp { #[inline] pub const fn b (a: Head, b: Tail) -> Self { Self(Below, a, b) } } -impl< - O: Out, - Head: Draw + Layout, - Tail: Draw + Layout -> Draw for Bsp { +impl, Tail: Content> Draw for Bsp { fn draw (&self, to: &mut O) { - let [a, b, _] = bsp_areas(to.area(), self.0, &self.1, &self.2); - if self.0 == Below { - to.place_at(a, &self.1); - to.place_at(b, &self.2); - } else { - to.place_at(b, &self.2); - to.place_at(a, &self.1); + match self.0 { + South => { + panic!("{}", self.1.h(to.area())); + let area_1 = self.1.layout(to.area()); + let area_2 = self.2.layout([ + to.area().x(), + to.area().y().plus(area_1.h()), + to.area().w(), + to.area().h().minus(area_1.h()) + ].into()); + panic!("{area_1:?} {area_2:?}"); + 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:?}"); + //if self.0 == Below { + //to.place_at(a, &self.1); + //to.place_at(b, &self.2); + //} else { + //to.place_at(b, &self.2); + //to.place_at(a, &self.1); + //} } } - impl, Tail: Layout> Layout for Bsp { + fn w (&self, area: O::Area) -> O::Unit { + match self.0 { + North | South | Above | Below => self.1.w(area).max(self.2.w(area)), + East | West => self.1.min_w(area).plus(self.2.w(area)), + } + } + fn min_w (&self, area: O::Area) -> O::Unit { + match self.0 { + North | South | Above | Below => self.1.min_w(area).max(self.2.min_w(area)), + East | West => self.1.min_w(area).plus(self.2.min_w(area)), + } + } + fn max_w (&self, area: O::Area) -> O::Unit { + match self.0 { + North | South | Above | Below => self.1.max_w(area).max(self.2.max_w(area)), + East | West => self.1.max_w(area).plus(self.2.max_w(area)), + } + } + fn h (&self, area: O::Area) -> O::Unit { + match self.0 { + East | West | Above | Below => self.1.h(area).max(self.2.h(area)), + North | South => self.1.h(area).plus(self.2.h(area)), + } + } + fn min_h (&self, area: O::Area) -> O::Unit { + match self.0 { + East | West | Above | Below => self.1.min_h(area).max(self.2.min_h(area)), + North | South => self.1.min_h(area).plus(self.2.min_h(area)), + } + } + fn max_h (&self, area: O::Area) -> O::Unit { + match self.0 { + North | South | Above | Below => self.1.max_h(area).max(self.2.max_h(area)), + East | West => self.1.max_h(area).plus(self.2.max_h(area)), + } + } fn layout (&self, area: O::Area) -> O::Area { bsp_areas(area, self.0, &self.1, &self.2)[2] } } -fn bsp_areas ( - area: O::Area, direction: Direction, a: &impl Layout, b: &impl Layout, +fn bsp_areas , B: Layout> ( + area: O::Area, direction: Direction, a: &A, b: &B, ) -> [O::Area;3] { let [x, y, w, h] = area.xywh(); let [aw, ah] = a.layout(area).wh(); diff --git a/output/src/layout/layout_cond.rs b/output/src/layout/layout_cond.rs index aceb535..65ebc5b 100644 --- a/output/src/layout/layout_cond.rs +++ b/output/src/layout/layout_cond.rs @@ -2,54 +2,45 @@ use crate::*; /// Show an item only when a condition is true. pub struct When(bool, T, PhantomData); -impl When { +impl> When { /// Create a binary condition. pub const fn new (c: bool, a: T) -> Self { Self(c, a, PhantomData) } } impl> Layout for When { fn layout (&self, to: O::Area) -> O::Area { let Self(cond, item, ..) = self; - let mut area = O::Area::zero(); - if *cond { - let item_area = item.layout(to); - area[0] = item_area.x(); - area[1] = item_area.y(); - area[2] = item_area.w(); - area[3] = item_area.h(); - } - area.into() + if *cond { item.layout(to) } else { O::Area::zero().into() } } } -impl> Draw for When { +impl> Draw for When { fn draw (&self, to: &mut O) { let Self(cond, item, ..) = self; - if *cond { item.draw(to) } + if *cond { Bound(self.layout(to.area()), item).draw(to) } } } /// Show one item if a condition is true and another if the condition is false -pub struct Either + Layout, B: Draw + Layout>(pub bool, pub A, pub B, pub PhantomData); -impl + Layout, B: Draw + Layout> Either { +pub struct Either(pub bool, pub A, pub B, pub PhantomData); +impl, B: Content> Either { /// Create a ternary view condition. pub const fn new (c: bool, a: A, b: B) -> Self { Self(c, a, b, PhantomData) } } -impl + Layout, B: Draw + Layout> Layout for Either { +impl, B: Layout> Layout for Either { fn layout (&self, to: E::Area) -> E::Area { let Self(cond, a, b, ..) = self; if *cond { a.layout(to) } else { b.layout(to) } } } -impl + Layout, B: Draw + Layout> Draw for Either { +impl, B: Content> Draw for Either { fn draw (&self, to: &mut E) { let Self(cond, a, b, ..) = self; - if *cond { a.draw(to) } else { b.draw(to) } + let area = self.layout(to.area()); + if *cond { Bound(area, a).draw(to) } else { Bound(area, b).draw(to) } } } - - /////////////////////////////////////////////////////////////////////////////// @@ -116,7 +107,7 @@ impl + Layout, B: Draw + Layout> Draw for Either< //return Ok(Some(match words.next() { //Some(Token{value: Key($x),..}) => Self::x(content), //Some(Token{value: Key($y),..}) => Self::y(content), - //Some(Token{value: Key($xy),..}) => Self::xy(content), + //Some(Token{value: Key($xy),..}) => Self::XY(content), //_ => unreachable!() //})) //} else { @@ -134,7 +125,7 @@ impl + Layout, B: Draw + Layout> Draw for Either< //state.give_or_fail(words, ||"y: no unit")?, //state.give_or_fail(words, ||"y: no content")?, //), - //Some(Token { value: Key($x), .. }) => Self::xy( + //Some(Token { value: Key($x), .. }) => Self::XY( //state.give_or_fail(words, ||"xy: no unit x")?, //state.give_or_fail(words, ||"xy: no unit y")?, //state.give_or_fail(words, ||"xy: no content")? diff --git a/output/src/layout/layout_expand.rs b/output/src/layout/layout_expand.rs deleted file mode 100644 index 8194fe2..0000000 --- a/output/src/layout/layout_expand.rs +++ /dev/null @@ -1,39 +0,0 @@ -use crate::*; - -pub enum Expand { X(U, A), Y(U, A), XY(U, U, A), } - -impl Expand { - #[inline] pub const fn x (x: U, item: A) -> Self { Self::X(x, item) } - #[inline] pub const fn y (y: U, item: A) -> Self { Self::Y(y, item) } - #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { Self::XY(x, y, item) } -} - -impl + Layout> Content for Expand { - fn content (&self) -> impl Draw + Layout + '_ { - use Expand::*; - match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } - } -} - -impl + Layout> Draw for Expand { - fn draw (&self, to: &mut O) { - to.place_at(self.layout(to.area()), &self.content()) - } -} - -impl Expand { - #[inline] pub fn dx (&self) -> U { - use Expand::*; - match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } - } - #[inline] pub fn dy (&self) -> U { - use Expand::*; - match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } - } -} - -impl> Layout for Expand { - fn layout (&self, to: O::Area) -> O::Area { - [to.x(), to.y(), to.w().plus(self.dx()), to.h().plus(self.dy())].into() - } -} diff --git a/output/src/layout/layout_fill.rs b/output/src/layout/layout_fill.rs deleted file mode 100644 index 6287714..0000000 --- a/output/src/layout/layout_fill.rs +++ /dev/null @@ -1,55 +0,0 @@ -use crate::*; - -pub enum Fill { - /// Use maximum width of area. - X(A), - /// Use maximum height of area. - Y(A), - /// Use maximum width and height of area. - XY(A) -} - -impl Fill { - #[inline] pub const fn x (item: T) -> Self { - Self::X(item) - } - #[inline] pub const fn y (item: T) -> Self { - Self::Y(item) - } - #[inline] pub const fn xy (item: T) -> Self { - Self::XY(item) - } - #[inline] pub const fn has_x (&self) -> bool { - matches!(self, Self::X(_) | Self::XY(_)) - } - #[inline] pub const fn has_y (&self) -> bool { - matches!(self, Self::Y(_) | Self::XY(_)) - } - #[inline] pub const fn content (&self) -> &T { - use Fill::*; - match self { X(item) | Y(item) | XY(item) => item } - } -} - -impl> Draw for Fill { - fn draw (&self, to: &mut O) { - to.place_at(self.layout(to.area()), &self.content()) - } -} - -impl> Layout for Fill { - fn min_w (&self, area: O::Area) -> O::Unit { - if self.has_x() { - area.w() - } else { - 0.into() - } - } - fn min_h (&self, area: O::Area) -> O::Unit { - if self.has_y() { - area.h() - } else { - 0.into() - } - } -} diff --git a/output/src/layout/layout_fixed.rs b/output/src/layout/layout_fixed.rs deleted file mode 100644 index 1d6eb1d..0000000 --- a/output/src/layout/layout_fixed.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::*; - -pub enum Fixed { X(U, A), Y(U, A), XY(U, U, A), } - -impl Fixed { - #[inline] pub const fn x (x: U, item: A) -> Self { - Self::X(x, item) - } - #[inline] pub const fn y (y: U, item: A) -> Self { - Self::Y(y, item) - } - #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { - Self::XY(x, y, item) - } - #[inline] pub const fn content (&self) -> &A { - use Fixed::*; - match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } - } -} - -impl Fixed { - #[inline] pub fn dx (&self) -> U { - use Fixed::*; - match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } - } - #[inline] pub fn dy (&self) -> U { - use Fixed::*; - match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } - } -} - -impl> Draw for Fixed { - fn draw (&self, to: &mut O) { - let area = Layout::::layout(&self, to.area()); - to.place_at(area, &self.content()) - } -} - -impl Layout for Fixed { - fn layout (&self, area: O::Area) -> O::Area { - [area.x(), area.y(), match self { - Fixed::X(w, _) | Fixed::XY(w, _, _) => *w, _ => area.w() - }, match self { - Fixed::Y(h, _) | Fixed::XY(_, h, _) => *h, _ => area.h() - }].into() - } -} diff --git a/output/src/layout/layout_map.rs b/output/src/layout/layout_map.rs index f509dcc..ffd7005 100644 --- a/output/src/layout/layout_map.rs +++ b/output/src/layout/layout_map.rs @@ -31,7 +31,7 @@ macro_rules! impl_map_direction (($name:ident, $axis:ident, $align:ident)=>{ O, A, Push>>>, I, F, fn(A, usize)->B > where O: Out, - B: Draw + Layout, + B: Draw, I: Iterator + Send + Sync + 'a, F: Fn() -> I + Send + Sync + 'a { @@ -61,14 +61,14 @@ macro_rules! impl_map_direction (($name:ident, $axis:ident, $align:ident)=>{ } }); -impl_map_direction!(east, x, w); -impl_map_direction!(south, y, n); -impl_map_direction!(west, x, e); -impl_map_direction!(north, y, s); +impl_map_direction!(east, X, w); +impl_map_direction!(south, Y, n); +impl_map_direction!(west, X, e); +impl_map_direction!(north, Y, s); impl<'a, O, A, B, I, F, G> Layout for Map where O: Out, - B: Draw + Layout, + B: Layout, I: Iterator + Send + Sync + 'a, F: Fn() -> I + Send + Sync + 'a, G: Fn(A, usize)->B + Send + Sync @@ -94,7 +94,7 @@ impl<'a, O, A, B, I, F, G> Layout for Map where } impl<'a, O, A, B, I, F, G> Draw for Map where O: Out, - B: Draw + Layout, + B: Content, I: Iterator + Send + Sync + 'a, F: Fn() -> I + Send + Sync + 'a, G: Fn(A, usize)->B + Send + Sync @@ -115,25 +115,25 @@ impl<'a, O, A, B, I, F, G> Draw for Map where #[inline] pub fn map_south( item_offset: O::Unit, item_height: O::Unit, - item: impl Draw + Layout -) -> impl Draw + Layout { - Push::y(item_offset, Fixed::y(item_height, Fill::x(item))) + item: impl Content +) -> impl Content { + Push::Y(item_offset, Fixed::Y(item_height, Fill::X(item))) } #[inline] pub fn map_south_west( item_offset: O::Unit, item_height: O::Unit, - item: impl Draw + Layout -) -> impl Draw + Layout { - Push::y(item_offset, Align::nw(Fixed::y(item_height, Fill::x(item)))) + item: impl Content +) -> impl Content { + Push::Y(item_offset, Align::nw(Fixed::Y(item_height, Fill::X(item)))) } #[inline] pub fn map_east( item_offset: O::Unit, item_width: O::Unit, - item: impl Draw + Layout -) -> impl Draw + Layout { - Push::x(item_offset, Align::w(Fixed::x(item_width, Fill::y(item)))) + item: impl Content +) -> impl Content { + Push::X(item_offset, Align::w(Fixed::X(item_width, Fill::Y(item)))) } diff --git a/output/src/layout/layout_margin.rs b/output/src/layout/layout_margin.rs deleted file mode 100644 index cee3800..0000000 --- a/output/src/layout/layout_margin.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::*; - -pub enum Margin { X(U, A), Y(U, A), XY(U, U, A), } - -impl Margin { - #[inline] pub const fn x (x: U, item: A) -> Self { - Self::X(x, item) - } - #[inline] pub const fn y (y: U, item: A) -> Self { - Self::Y(y, item) - } - #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { - Self::XY(x, y, item) - } - #[inline] pub const fn content (&self) -> &A { - use Margin::*; - match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } - } -} - -impl + Layout> Draw for Margin { - fn draw (&self, to: &mut E) { - to.place_at(self.layout(to.area()), &self.content()) - } -} - -impl Margin { - #[inline] pub fn dx (&self) -> U { - use Margin::*; - match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } - } - #[inline] pub fn dy (&self) -> U { - use Margin::*; - match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } - } -} - -impl> Layout for Margin { - fn layout (&self, area: E::Area) -> E::Area { - let area = self.content().layout(area); - let dx = self.dx(); - let dy = self.dy(); - [ - area.x().minus(dx), - area.y().minus(dy), - area.w().plus(dy.plus(dy)), - area.h().plus(dy.plus(dy)), - ].into() - } -} diff --git a/output/src/layout/layout_max.rs b/output/src/layout/layout_max.rs deleted file mode 100644 index 81467ae..0000000 --- a/output/src/layout/layout_max.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::*; - -pub enum Max { X(U, A), Y(U, A), XY(U, U, A), } - -impl Max { - #[inline] pub const fn x (x: U, item: A) -> Self { - Self::X(x, item) - } - #[inline] pub const fn y (y: U, item: A) -> Self { - Self::Y(y, item) - } - #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { - Self::XY(x, y, item) - } - #[inline] pub const fn content (&self) -> &A { - use Max::*; - match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } - } -} - -impl + Layout> Draw for Max { - fn draw (&self, to: &mut E) { - to.place_at(self.layout(to.area()), &self.content()) - } -} - -impl Max { - #[inline] pub fn dx (&self) -> U { - use Max::*; - match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } - } - #[inline] pub fn dy (&self) -> U { - use Max::*; - match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } - } -} - -impl> Layout for Max { - fn layout (&self, area: E::Area) -> E::Area { - let [x, y, w, h] = self.content().layout(area).xywh(); - match self { - Self::X(mw, _) => [x, y, w.min(*mw), h], - Self::Y(mh, _) => [x, y, w, h.min(*mh)], - Self::XY(mw, mh, _) => [x, y, w.min(*mw), h.min(*mh)], - }.into() - } -} diff --git a/output/src/layout/layout_min.rs b/output/src/layout/layout_min.rs deleted file mode 100644 index 73f8209..0000000 --- a/output/src/layout/layout_min.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::*; - -pub enum Min { X(U, A), Y(U, A), XY(U, U, A), } - -impl Min { - #[inline] pub const fn x (x: U, item: A) -> Self { - Self::X(x, item) - } - #[inline] pub const fn y (y: U, item: A) -> Self { - Self::Y(y, item) - } - #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { - Self::XY(x, y, item) - } - #[inline] pub const fn content (&self) -> &A { - use Min::*; - match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } - } -} - -impl + Layout> Draw for Min { - fn draw (&self, to: &mut E) { - to.place_at(self.layout(to.area()), &self.content()) - } -} - -impl Min { - #[inline] pub fn dx (&self) -> U { - use Min::*; - match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } - } - #[inline] pub fn dy (&self) -> U { - use Min::*; - match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } - } -} - -impl> Layout for Min { - fn layout (&self, area: E::Area) -> E::Area { - let [x, y, w, h] = self.content().layout(area).xywh(); - match self { - Self::X(mw, _) => [x, y, w.max(*mw), h], - Self::Y(mh, _) => [x, y, w, h.max(*mh)], - Self::XY(mw, mh, _) => [x, y, w.max(*mw), h.max(*mh)], - }.into() - } -} diff --git a/output/src/layout/layout_move.rs b/output/src/layout/layout_move.rs new file mode 100644 index 0000000..163d2e3 --- /dev/null +++ b/output/src/layout/layout_move.rs @@ -0,0 +1,33 @@ +use crate::*; +/// Increment X and/or Y coordinate. +pub enum Push { X(U, A), Y(U, A), XY(U, U, A), } +impl Push { + #[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c, } } +} +impl Push { + #[inline] pub fn dx (&self) -> U { match self { Self::X(x, ..) | Self::XY(x, _, _) => *x, Self::Y(_, _) => 0.into() } } + #[inline] pub fn dy (&self) -> U { match self { Self::Y(y, ..) | Self::XY(_, y, _) => *y, Self::X(_, _) => 0.into() } } +} +impl> Draw for Push { + fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) } +} +impl> Layout for Push { + fn x (&self, area: O::Area) -> O::Unit { area.x().plus(self.dx()) } + fn y (&self, area: O::Area) -> O::Unit { area.y().plus(self.dy()) } +} +/// Decrement X and/or Y coordinate. +pub enum Pull { X(U, A), Y(U, A), XY(U, U, A), } +impl Pull { + #[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c, } } +} +impl Pull { + #[inline] pub fn dx (&self) -> U { match self { Self::X(x, ..) | Self::XY(x, _, _) => *x, Self::Y(_, _) => 0.into() } } + #[inline] pub fn dy (&self) -> U { match self { Self::Y(y, ..) | Self::XY(_, y, _) => *y, Self::X(_, _) => 0.into() } } +} +impl> Draw for Pull { + fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) } +} +impl> Layout for Pull { + fn x (&self, area: O::Area) -> O::Unit { area.x().minus(self.dx()) } + fn y (&self, area: O::Area) -> O::Unit { area.y().minus(self.dy()) } +} diff --git a/output/src/layout/layout_pad.rs b/output/src/layout/layout_pad.rs new file mode 100644 index 0000000..0570c23 --- /dev/null +++ b/output/src/layout/layout_pad.rs @@ -0,0 +1,27 @@ +use crate::*; +use Pad::*; +pub enum Pad { X(U, A), Y(U, A), XY(U, U, A), } +impl Pad { + #[inline] pub const fn inner (&self) -> &A { match self { X(_, c) | Y(_, c) | XY(_, _, c) => c, } } +} +impl Pad { + #[inline] pub fn dx (&self) -> U { match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } } + #[inline] pub fn dy (&self) -> U { match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } } +} +impl> Draw for Pad { + fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) } +} +impl> Layout for Pad { + fn x (&self, area: O::Area) -> O::Unit { + area.x().plus(self.dx()) + } + fn y (&self, area: O::Area) -> O::Unit { + area.x().plus(self.dx()) + } + fn w (&self, area: O::Area) -> O::Unit { + area.w().minus(self.dx() * 2.into()) + } + fn h (&self, area: O::Area) -> O::Unit { + area.h().minus(self.dy() * 2.into()) + } +} diff --git a/output/src/layout/layout_padding.rs b/output/src/layout/layout_padding.rs deleted file mode 100644 index 5295fdf..0000000 --- a/output/src/layout/layout_padding.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::*; - -pub enum Padding { X(U, A), Y(U, A), XY(U, U, A), } - -impl Padding { - #[inline] pub const fn x (x: U, item: A) -> Self { - Self::X(x, item) - } - #[inline] pub const fn y (y: U, item: A) -> Self { - Self::Y(y, item) - } - #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { - Self::XY(x, y, item) - } - #[inline] pub const fn content (&self) -> &A { - use Padding::*; - match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } - } -} - -impl + Layout> Draw for Padding { - fn draw (&self, to: &mut E) { - to.place_at(self.layout(to.area()), &self.content()) - } -} - -impl Padding { - #[inline] pub fn dx (&self) -> U { - use Padding::*; - match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } - } - #[inline] pub fn dy (&self) -> U { - use Padding::*; - match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } - } -} - -impl> Layout for Padding { - fn layout (&self, area: E::Area) -> E::Area { - let area = self.content().layout(area); - let dx = self.dx(); - let dy = self.dy(); - [ - area.x().plus(dx), - area.y().plus(dy), - area.w().minus(dy.plus(dy)), - area.h().minus(dy.plus(dy)) - ].into() - } -} diff --git a/output/src/layout/layout_pull.rs b/output/src/layout/layout_pull.rs deleted file mode 100644 index 9fdda7f..0000000 --- a/output/src/layout/layout_pull.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::*; - -pub enum Pull { X(U, A), Y(U, A), XY(U, U, A), } - -impl Pull { - #[inline] pub const fn x (x: U, item: A) -> Self { - Self::X(x, item) - } - #[inline] pub const fn y (y: U, item: A) -> Self { - Self::Y(y, item) - } - #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { - Self::XY(x, y, item) - } - #[inline] pub const fn content (&self) -> &A { - use Pull::*; - match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } - } -} - -impl + Layout> Draw for Pull { - fn draw (&self, to: &mut E) { - to.place_at(self.layout(to.area()), &self.content()) - } -} - -impl Pull { - #[inline] pub fn dx (&self) -> U { - use Pull::*; - match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } - } - #[inline] pub fn dy (&self) -> U { - use Pull::*; - match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } - } -} - -impl> Layout for Pull { - fn layout (&self, to: E::Area) -> E::Area { - let area = self.content().layout(to); - [ - area.x().minus(self.dx()), - area.y().minus(self.dy()), - area.w(), - area.h() - ].into() - } -} diff --git a/output/src/layout/layout_push.rs b/output/src/layout/layout_push.rs deleted file mode 100644 index 4c507b3..0000000 --- a/output/src/layout/layout_push.rs +++ /dev/null @@ -1,43 +0,0 @@ -use crate::*; - -pub enum Push { X(U, A), Y(U, A), XY(U, U, A), } - -impl Push { - #[inline] pub const fn x (x: U, item: A) -> Self { - Self::X(x, item) - } - #[inline] pub const fn y (y: U, item: A) -> Self { - Self::Y(y, item) - } - #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { - Self::XY(x, y, item) - } - #[inline] pub const fn content (&self) -> &A { - use Push::*; - match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } - } -} - -impl + Layout> Draw for Push { - fn draw (&self, to: &mut E) { - to.place_at(self.layout(to.area()), &self.content()) - } -} - -impl Push { - #[inline] pub fn dx (&self) -> U { - use Push::*; - match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } - } - #[inline] pub fn dy (&self) -> U { - use Push::*; - match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } - } -} - -impl> Layout for Push { - fn layout (&self, area: E::Area) -> E::Area { - let area = self.content().layout(area); - [area.x().plus(self.dx()), area.y().plus(self.dy()), area.w(), area.h()].into() - } -} diff --git a/output/src/layout/layout_shrink.rs b/output/src/layout/layout_shrink.rs deleted file mode 100644 index 6a64425..0000000 --- a/output/src/layout/layout_shrink.rs +++ /dev/null @@ -1,43 +0,0 @@ -use crate::*; - -pub enum Shrink { X(U, A), Y(U, A), XY(U, U, A), } - -impl Shrink { - #[inline] pub const fn x (x: U, item: A) -> Self { - Self::X(x, item) - } - #[inline] pub const fn y (y: U, item: A) -> Self { - Self::Y(y, item) - } - #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { - Self::XY(x, y, item) - } - #[inline] pub const fn content (&self) -> &A { - use Shrink::*; - match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } - } -} - -impl + Layout> Draw for Shrink { - fn draw (&self, to: &mut E) { - to.place_at(self.layout(to.area()), &self.content()) - } -} - -impl Shrink { - #[inline] pub fn dx (&self) -> U { - use Shrink::*; - match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } - } - #[inline] pub fn dy (&self) -> U { - use Shrink::*; - match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } - } -} - -impl> Layout for Shrink { - fn layout (&self, to: E::Area) -> E::Area { - let area = self.content().layout(to); - [area.x(), area.y(), area.w().minus(self.dx()), area.h().minus(self.dy())].into() - } -} diff --git a/output/src/layout/layout_size.rs b/output/src/layout/layout_size.rs new file mode 100644 index 0000000..a9d4932 --- /dev/null +++ b/output/src/layout/layout_size.rs @@ -0,0 +1,121 @@ +use crate::*; + +pub enum Fill { X(A), Y(A), XY(A) } +impl Fill { + #[inline] pub const fn inner (&self) -> &A { match self { Self::X(c) | Self::Y(c) | Self::XY(c) => c } } + #[inline] pub const fn dx (&self) -> bool { match self { Self::X(_) | Self::XY(_) => true, _ => false } } + #[inline] pub const fn dy (&self) -> bool { match self { Self::Y(_) | Self::XY(_) => true, _ => false } } +} +impl> Draw for Fill { + fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) } +} +impl> Layout for Fill { + fn x (&self, area: O::Area) -> O::Unit { if self.dx() { area.x() } else { self.inner().x(area) } } + fn y (&self, area: O::Area) -> O::Unit { if self.dy() { area.y() } else { self.inner().y(area) } } + fn w (&self, area: O::Area) -> O::Unit { if self.dx() { area.w() } else { self.inner().w(area) } } + fn min_w (&self, area: O::Area) -> O::Unit { if self.dx() { area.w() } else { self.inner().min_w(area) } } + fn max_w (&self, area: O::Area) -> O::Unit { if self.dx() { area.w() } else { self.inner().max_w(area) } } + fn h (&self, area: O::Area) -> O::Unit { if self.dy() { area.h() } else { self.inner().h(area) } } + fn min_h (&self, area: O::Area) -> O::Unit { if self.dy() { area.h() } else { self.inner().min_h(area) } } + fn max_h (&self, area: O::Area) -> O::Unit { if self.dy() { area.h() } else { self.inner().max_h(area) } } +} +/// Set fixed size for content. +pub enum Fixed { X(U, A), Y(U, A), XY(U, U, A), } +impl Fixed { + #[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c } } +} +impl Fixed { + #[inline] pub const fn dx (&self) -> Option { match self { Self::X(x, _) | Self::XY(x, ..) => Some(*x), _ => None } } + #[inline] pub const fn dy (&self) -> Option { match self { Self::Y(y, _) | Self::XY(y, ..) => Some(*y), _ => None } } +} +impl> Draw for Fixed { + fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) } +} +impl> Layout for Fixed { + fn w (&self, area: O::Area) -> O::Unit { self.dx().unwrap_or(self.inner().w(area)) } + fn min_w (&self, area: O::Area) -> O::Unit { self.dx().unwrap_or(self.inner().min_w(area)) } + fn max_w (&self, area: O::Area) -> O::Unit { self.dx().unwrap_or(self.inner().max_w(area)) } + fn h (&self, area: O::Area) -> O::Unit { self.dy().unwrap_or(self.inner().h(area)) } + fn min_h (&self, area: O::Area) -> O::Unit { self.dy().unwrap_or(self.inner().min_h(area)) } + fn max_h (&self, area: O::Area) -> O::Unit { self.dy().unwrap_or(self.inner().max_h(area)) } +} + +pub enum Max { X(U, A), Y(U, A), XY(U, U, A), } +impl Max { + #[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c } } +} +impl Max { + #[inline] pub const fn dx (&self) -> Option { match self { Self::X(x, _) | Self::XY(x, ..) => Some(*x), _ => None } } + #[inline] pub const fn dy (&self) -> Option { match self { Self::Y(y, _) | Self::XY(y, ..) => Some(*y), _ => None } } +} +impl> Draw for Max { + fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) } +} +impl> Layout for Max { + fn layout (&self, area: E::Area) -> E::Area { + let [x, y, w, h] = self.inner().layout(area).xywh(); + match self { + Self::X(mw, _) => [x, y, w.min(*mw), h], + Self::Y(mh, _) => [x, y, w, h.min(*mh)], + Self::XY(mw, mh, _) => [x, y, w.min(*mw), h.min(*mh)], + }.into() + } +} + +pub enum Min { X(U, A), Y(U, A), XY(U, U, A), } +impl Min { + #[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c } } +} +impl Min { + #[inline] pub const fn dx (&self) -> Option { match self { Self::X(x, _) | Self::XY(x, ..) => Some(*x), _ => None } } + #[inline] pub const fn dy (&self) -> Option { match self { Self::Y(y, _) | Self::XY(y, ..) => Some(*y), _ => None } } +} +impl> Draw for Min { + fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) } +} +impl> Layout for Min { + fn layout (&self, area: E::Area) -> E::Area { + let [x, y, w, h] = self.inner().layout(area).xywh(); + match self { + Self::X(mw, _) => [x, y, w.max(*mw), h], + Self::Y(mh, _) => [x, y, w, h.max(*mh)], + Self::XY(mw, mh, _) => [x, y, w.max(*mw), h.max(*mh)], + }.into() + } +} + +pub enum Expand { X(U, A), Y(U, A), XY(U, U, A), } +impl Expand { + #[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c } } +} +impl Expand { + #[inline] pub const fn dx (&self) -> Option { match self { Self::X(x, _) | Self::XY(x, ..) => Some(*x), _ => None } } + #[inline] pub const fn dy (&self) -> Option { match self { Self::Y(y, _) | Self::XY(y, ..) => Some(*y), _ => None } } +} +impl> Draw for Expand { + fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) } +} +impl> Layout for Expand { + fn w (&self, to: O::Area) -> O::Unit { self.inner().w(to).plus(self.dx().unwrap_or_default()) } + fn h (&self, to: O::Area) -> O::Unit { self.inner().w(to).plus(self.dy().unwrap_or_default()) } +} + +pub enum Shrink { X(U, A), Y(U, A), XY(U, U, A), } +impl Shrink { + #[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c } } +} +impl Shrink { + #[inline] pub const fn dx (&self) -> Option { match self { Self::X(x, _) | Self::XY(x, ..) => Some(*x), _ => None } } + #[inline] pub const fn dy (&self) -> Option { match self { Self::Y(y, _) | Self::XY(y, ..) => Some(*y), _ => None } } +} +impl> Draw for Shrink { + fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) } +} +impl> Layout for Shrink { + fn layout (&self, to: E::Area) -> E::Area { + let area = self.inner().layout(to); + let dx = self.dx().unwrap_or_default(); + let dy = self.dy().unwrap_or_default(); + [area.x(), area.y(), area.w().minus(dx), area.h().minus(dy)].into() + } +} diff --git a/output/src/output.rs b/output/src/output.rs index 00c7bb9..6a32109 100644 --- a/output/src/output.rs +++ b/output/src/output.rs @@ -4,6 +4,9 @@ #![feature(const_precise_live_drops)] #![feature(type_changing_struct_update)] #![feature(anonymous_lifetime_in_impl_trait)] +#![feature(const_option_ops)] +#![feature(const_trait_impl)] +#![feature(const_default)] //#![feature(non_lifetime_binders)] pub(crate) use self::Direction::*; @@ -25,7 +28,7 @@ pub trait Out: Send + Sync + Sized { type Area: Area; /// Render drawable in area specified by `T::layout(self.area())` - #[inline] fn place <'t, T: Draw + Layout + ?Sized> ( + #[inline] fn place <'t, T: Content + ?Sized> ( &mut self, content: &'t T ) { self.place_at(content.layout(self.area()), content) diff --git a/output/src/output_test.rs b/output/src/output_test.rs index 958f485..932e87a 100644 --- a/output/src/output_test.rs +++ b/output/src/output_test.rs @@ -84,8 +84,8 @@ macro_rules! test_op_transform { h in u16::MIN..u16::MAX, ) { if let Some(op) = match (op_x, op_y) { - (Some(x), Some(y)) => Some($Op::xy(x, y, content)), - (Some(x), None) => Some($Op::x(x, content)), + (Some(x), Some(y)) => Some($Op::XY(x, y, content)), + (Some(x), None) => Some($Op::X(x, content)), (None, Some(y)) => Some($Op::y(y, content)), _ => None } { @@ -104,8 +104,7 @@ test_op_transform!(proptest_op_push, Push); test_op_transform!(proptest_op_pull, Pull); test_op_transform!(proptest_op_shrink, Shrink); test_op_transform!(proptest_op_expand, Expand); -test_op_transform!(proptest_op_margin, Margin); -test_op_transform!(proptest_op_padding, Padding); +test_op_transform!(proptest_op_padding, Pad); proptest! { #[test] fn proptest_op_bsp ( diff --git a/output/src/space/space_measure.rs b/output/src/space/space_measure.rs index d1bdb30..f508554 100644 --- a/output/src/space/space_measure.rs +++ b/output/src/space/space_measure.rs @@ -76,6 +76,6 @@ impl Measure { format!("{}x{}", self.w(), self.h()).into() } pub fn of > (&self, item: T) -> Bsp, T> { - Bsp::b(Fill::xy(self), item) + Bsp::b(Fill::XY(self), item) } } diff --git a/output/src/thunk.rs b/output/src/thunk.rs index da2d84e..29b7321 100644 --- a/output/src/thunk.rs +++ b/output/src/thunk.rs @@ -1,43 +1,19 @@ use crate::*; -/// Lazily-evaluated [Draw]able. -pub struct Lazy(PhantomData<(E, T)>, F); -impl + Layout, F: Fn()->T> Lazy { - pub const fn new (thunk: F) -> Self { - Self(PhantomData, thunk) - } -} -impl + Layout, F: Fn()->T> Content for Lazy { - fn content (&self) -> impl Draw + Layout + '_ { - (self.1)() - } -} - -pub struct Thunk(PhantomData, F); -impl Thunk { - pub const fn new (draw: F) -> Self { Self(PhantomData, draw) } -} -impl Draw for Thunk { - fn draw (&self, to: &mut E) { (self.1)(to) } -} -impl Layout for Thunk { - fn layout (&self, to: E::Area) -> E::Area { to } -} - -#[derive(Debug, Default)] pub struct Memo { - pub value: T, - pub view: Arc> +pub struct Lazy(F, PhantomData<(O, T)>); +impl, F: Fn()->T> Lazy { pub const fn new (thunk: F) -> Self { Self(thunk, PhantomData) } } + +pub struct Thunk(PhantomData, F); +impl Thunk { pub const fn new (draw: F) -> Self { Self(PhantomData, draw) } } +impl Layout for Thunk {} +impl Draw for Thunk { + fn draw (&self, to: &mut O) { (self.1)(to) } } +#[derive(Debug, Default)] pub struct Memo { pub value: T, pub view: Arc> } impl Memo { - pub fn new (value: T, view: U) -> Self { - Self { value, view: Arc::new(view.into()) } - } - pub fn update ( - &mut self, - newval: T, - draw: impl Fn(&mut U, &T, &T)->R - ) -> Option { + pub fn new (value: T, view: U) -> Self { Self { value, view: Arc::new(view.into()) } } + pub fn update (&mut self, newval: T, draw: impl Fn(&mut U, &T, &T)->R) -> Option { if newval != self.value { let result = draw(&mut*self.view.write().unwrap(), &newval, &self.value); self.value = newval; @@ -48,6 +24,4 @@ impl Memo { } /// Clear a pre-allocated buffer, then write into it. -#[macro_export] macro_rules! rewrite { - ($buf:ident, $($rest:tt)*) => { |$buf,_,_|{ $buf.clear(); write!($buf, $($rest)*) } } -} +#[macro_export] macro_rules! rewrite { ($buf:ident, $($rest:tt)*) => { |$buf,_,_|{ $buf.clear(); write!($buf, $($rest)*) } } } diff --git a/output/src/view.rs b/output/src/view.rs index bd1cc51..f8a182f 100644 --- a/output/src/view.rs +++ b/output/src/view.rs @@ -67,7 +67,7 @@ pub fn evaluate_output_expression <'a, O: Out + 'a, S> ( } }), - Some("align") => output.place(&{ + Some("align") => output.place(&{ let a = Thunk::new(move|output: &mut O|state.view(output, &arg0).unwrap()); match frags.next() { Some("n") => Align::n(a), @@ -81,55 +81,65 @@ pub fn evaluate_output_expression <'a, O: Out + 'a, S> ( } }), - Some("fill") => output.place(&{ + Some("fill") => output.place(&{ let a = Thunk::new(move|output: &mut O|state.view(output, &arg0).unwrap()); match frags.next() { - Some("x") => Fill::X(a), - Some("y") => Fill::Y(a), - Some("xy") => Fill::XY(a), + Some("xy") | None => Fill::XY(a), + Some("x") => Fill::X(a), + Some("y") => Fill::Y(a), frag => unimplemented!("fill/{frag:?}") } }), - Some("fixed") => output.place(&{ + Some("fixed") => output.place(&{ let axis = frags.next(); - let arg = match axis { Some("x") | Some("y") => arg1, Some("xy") => arg2, _ => panic!() }; + let arg = match axis { Some("x") | Some("y") => arg1, Some("xy") | None => arg2, _ => panic!("fixed: unsupported axis {axis:?}") }; let cb = Thunk::new(move|output: &mut O|state.view(output, &arg).unwrap()); match axis { - Some("x") => Fixed::X(state.from(arg0?)?.unwrap(), cb), - Some("y") => Fixed::Y(state.from(arg0?)?.unwrap(), cb), - Some("xy") => Fixed::XY(state.from(arg0?)?.unwrap(), state.from(arg1?)?.unwrap(), cb), + Some("xy") | None => Fixed::XY(state.from(arg0?)?.unwrap(), state.from(arg1?)?.unwrap(), cb), + Some("x") => Fixed::X(state.from(arg0?)?.unwrap(), cb), + Some("y") => Fixed::Y(state.from(arg0?)?.unwrap(), cb), frag => unimplemented!("fixed/{frag:?} ({expr:?}) ({head:?}) ({:?})", head.src()?.unwrap_or_default().split("/").next()) } }), - Some("min") => output.place(&{ - let c = match frags.next() { - Some("x") | Some("y") => arg1, Some("xy") => arg2, _ => panic!() - }; - let cb = Thunk::new(move|output: &mut O|state.view(output, &c).unwrap()); - match frags.next() { - Some("x") => Min::X(state.from(arg0?)?.unwrap(), cb), - Some("y") => Min::Y(state.from(arg0?)?.unwrap(), cb), - Some("xy") => Min::XY(state.from(arg0?)?.unwrap(), state.from(arg1?)?.unwrap(), cb), + Some("min") => output.place(&{ + let axis = frags.next(); + let arg = match axis { Some("x") | Some("y") => arg1, Some("xy") | None => arg2, _ => panic!("fixed: unsupported axis {axis:?}") }; + let cb = Thunk::new(move|output: &mut O|state.view(output, &arg).unwrap()); + match axis { + Some("xy") | None => Min::XY(state.from(arg0?)?.unwrap(), state.from(arg1?)?.unwrap(), cb), + Some("x") => Min::X(state.from(arg0?)?.unwrap(), cb), + Some("y") => Min::Y(state.from(arg0?)?.unwrap(), cb), frag => unimplemented!("min/{frag:?}") } }), - Some("max") => output.place(&{ - let c = match frags.next() { - Some("x") | Some("y") => arg1, Some("xy") => arg2, _ => panic!() - }; - let cb = Thunk::new(move|output: &mut O|state.view(output, &c).unwrap()); - match frags.next() { - Some("x") => Max::X(state.from(arg0?)?.unwrap(), cb), - Some("y") => Max::Y(state.from(arg0?)?.unwrap(), cb), - Some("xy") => Max::XY(state.from(arg0?)?.unwrap(), state.from(arg1?)?.unwrap(), cb), + Some("max") => output.place(&{ + let axis = frags.next(); + let arg = match axis { Some("x") | Some("y") => arg1, Some("xy") | None => arg2, _ => panic!("fixed: unsupported axis {axis:?}") }; + let cb = Thunk::new(move|output: &mut O|state.view(output, &arg).unwrap()); + match axis { + Some("xy") | None => Max::XY(state.from(arg0?)?.unwrap(), state.from(arg1?)?.unwrap(), cb), + Some("x") => Max::X(state.from(arg0?)?.unwrap(), cb), + Some("y") => Max::Y(state.from(arg0?)?.unwrap(), cb), frag => unimplemented!("max/{frag:?}") } }), + Some("push") => output.place(&{ + let axis = frags.next(); + let arg = match axis { Some("x") | Some("y") => arg1, Some("xy") | None => arg2, _ => panic!("fixed: unsupported axis {axis:?}") }; + let cb = Thunk::new(move|output: &mut O|state.view(output, &arg).unwrap()); + match axis { + Some("xy") | None => Push::XY(state.from(arg0?)?.unwrap(), state.from(arg1?)?.unwrap(), cb), + Some("x") => Push::X(state.from(arg0?)?.unwrap(), cb), + Some("y") => Push::Y(state.from(arg0?)?.unwrap(), cb), + frag => unimplemented!("push/{frag:?}") + } + }), + _ => return Ok(false) }; diff --git a/output/src/widget/widget_field.rs b/output/src/widget/widget_field.rs index 8de2515..6bedc02 100644 --- a/output/src/widget/widget_field.rs +++ b/output/src/widget/widget_field.rs @@ -1,25 +1,11 @@ use crate::*; pub struct FieldH(pub Theme, pub Label, pub Value); -impl, V: Draw> Layout for FieldH where Self: Content { - fn layout (&self, to: O::Area) -> O::Area { - self.content().layout(to) - } -} -impl, V: Draw> Draw for FieldH where Self: Content { - fn draw (&self, to: &mut O) { - self.content().draw(to) - } +impl, V: Content> HasContent for FieldH { + fn content (&self) -> impl Content { Bsp::e(&self.1, &self.2) } } pub struct FieldV(pub Theme, pub Label, pub Value); -impl, V: Draw> Layout for FieldV where Self: Content { - fn layout (&self, to: O::Area) -> O::Area { - self.content().layout(to) - } -} -impl, V: Draw> Draw for FieldV where Self: Content { - fn draw (&self, to: &mut O) { - self.content().draw(to) - } +impl, V: Content> HasContent for FieldV { + fn content (&self) -> impl Content { Bsp::s(&self.1, &self.2) } } diff --git a/tui/examples/tui_00.rs b/tui/examples/tui_00.rs index a4aa475..f13049e 100644 --- a/tui/examples/tui_00.rs +++ b/tui/examples/tui_00.rs @@ -28,19 +28,18 @@ impl ExampleCommand { } } -tui_draw!(|self: Example, to|{ - to.place(&self.content()); -}); -content!(TuiOut: |self: Example|{ - let index = self.0 + 1; - let wh = self.1.wh(); - let src = Self::VIEWS.get(self.0).unwrap_or(&""); - let heading = format!("Example {}/{} in {:?}", index, Self::VIEWS.len(), &wh); - let title = Tui::bg(Color::Rgb(60, 10, 10), Push::y(1, Align::n(heading))); - let code = Tui::bg(Color::Rgb(10, 60, 10), Push::y(2, Align::n(format!("{}", src)))); - let content = ();//Tui::bg(Color::Rgb(10, 10, 60), View(self, CstIter::new(src))); - self.1.of(Bsp::s(title, Bsp::n(""/*code*/, content))) -}); +impl Draw for Example { + fn content (&self) -> impl Draw { + let index = self.0 + 1; + let wh = self.1.wh(); + let src = Self::VIEWS.get(self.0).unwrap_or(&""); + let heading = format!("Example {}/{} in {:?}", index, Self::VIEWS.len(), &wh); + let title = Tui::bg(Color::Rgb(60, 10, 10), Push::y(1, Align::n(heading))); + let code = Tui::bg(Color::Rgb(10, 60, 10), Push::y(2, Align::n(format!("{}", src)))); + let content = ();//Tui::bg(Color::Rgb(10, 10, 60), View(self, CstIter::new(src))); + self.1.of(Bsp::s(title, Bsp::n(""/*code*/, content))) + } +} impl View for Example { fn view_expr <'a> (&'a self, to: &mut TuiOut, expr: &'a impl DslExpr) -> Usually<()> { if evaluate_output_expression(self, to, expr)? diff --git a/tui/src/tui_content.rs b/tui/src/tui_content.rs index 90e6f3a..38cf5ba 100644 --- a/tui/src/tui_content.rs +++ b/tui/src/tui_content.rs @@ -1,50 +1,12 @@ #[allow(unused)] use crate::*; - impl Tui { - pub const fn fg (color: Color, w: T) -> TuiForeground { - TuiForeground(Foreground(color, w)) - } - pub const fn bg (color: Color, w: T) -> TuiBackground { - TuiBackground(Background(color, w)) - } - pub const fn fg_bg (fg: Color, bg: Color, w: T) -> TuiBackground> { - TuiBackground(Background(bg, TuiForeground(Foreground(fg, w)))) - } - pub const fn modify (enable: bool, modifier: Modifier, w: T) -> Modify { - Modify(enable, modifier, w) - } - pub const fn bold (enable: bool, w: T) -> Modify { - Self::modify(enable, Modifier::BOLD, w) - } - pub const fn border (enable: bool, style: S, w: T) -> Bordered { - Bordered(enable, style, w) - } + pub const fn fg (color: Color, w: T) -> Foreground { Foreground(color, w) } + pub const fn bg (color: Color, w: T) -> Background { Background(color, w) } + pub const fn fg_bg (fg: Color, bg: Color, w: T) -> Background> { Background(bg, Foreground(fg, w)) } + pub const fn modify (enable: bool, modifier: Modifier, w: T) -> Modify { Modify(enable, modifier, w) } + pub const fn bold (enable: bool, w: T) -> Modify { Self::modify(enable, Modifier::BOLD, w) } + pub const fn border (enable: bool, style: S, w: T) -> Bordered { Bordered(enable, style, w) } } - -#[macro_export] macro_rules! tui_layout ((|$self:ident:$Self:ty, $to:ident|$expr:expr)=>{ - impl Layout for $Self { - fn layout (&$self, $to: [u16;4]) -> [u16;4] { - $expr - } - } -}); - -#[macro_export] macro_rules! tui_draw ((|$self:ident:$Self:ty, $to:ident|$expr:expr)=>{ - impl Draw for $Self { - fn draw (&$self, $to: &mut TuiOut) { - $expr - } - } -}); - -#[macro_export] macro_rules! tui_content ((|$self:ident:$Self:ty|$sexpr:expr)=>{ - impl Content for $Self { - fn content (&$self) -> impl Draw + Layout + '_ { - $expr - } - } -}); - mod tui_border; pub use self::tui_border::*; mod tui_button; pub use self::tui_button::*; mod tui_color; pub use self::tui_color::*; @@ -54,67 +16,41 @@ mod tui_phat; pub use self::tui_phat::*; mod tui_repeat; pub use self::tui_repeat::*; mod tui_scroll; pub use self::tui_scroll::*; mod tui_string; pub use self::tui_string::*; - mod tui_number; //pub use self::tui_number::*; mod tui_tryptich; //pub use self::tui_tryptich::*; - -pub struct TuiForeground(pub(crate) Foreground); -pub struct TuiBackground(pub(crate) Background); pub struct Modify(pub bool, pub Modifier, pub T); pub struct Styled(pub Option