From de591addb34215ae2aad3bb3d914925ac517fa8d Mon Sep 17 00:00:00 2001 From: facile pop culture reference Date: Sat, 8 Aug 2026 21:46:05 +0300 Subject: [PATCH 1/2] fix x/y/xy ops; remove button_2/3 --- src/layout.rs | 72 ++++++++++++++++++++++++++++++++++++++++++- src/layout/axis.rs | 23 +++++++++----- src/layout/azimuth.rs | 16 +++++----- src/layout/iter.rs | 72 ++++++++++++++++++++++++++----------------- src/lib.rs | 70 ----------------------------------------- src/term.rs | 1 - src/term/button.rs | 28 ----------------- 7 files changed, 138 insertions(+), 144 deletions(-) delete mode 100644 src/term/button.rs diff --git a/src/layout.rs b/src/layout.rs index 1bd7906..e2586fe 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -96,6 +96,76 @@ impl Sizer { } } +/// Some layout operations exist in multiple variants that take a single argument. +/// Their handling in [eval_view] is uniform and goes like this: +macro_rules! eval_enum (( + $name:literal, $output:ident, $state:ident, $value:expr, $Enum:ident { + $($v:literal => $V:ident),* $(,)? + } +) => {{ + match $value { + $(Some($v) => $Enum::$V,)* + frag => return Err(format!("invalid variant: {}/{frag:?}", $name).into()) + } +}}); + +/// Some layout operations exist in XY, X, and Y variants that take 3 or 2 arguments. +/// Their handling in [eval_view] is uniform and goes like this: +macro_rules! eval_xy ( + // Valueless variant: + // (fill/x ...) + // (fill/y ...) + // (fill/xy ...) + ( + $name:expr => $expr:expr, $head:expr, $output:ident, $state:ident, + $variant:expr, $xy:ident, $x:ident, $y:ident, $arg:expr, + ) => {{ + // frags.next(): 2nd slash-delimited fragment: /x, /y, /xy + let variant = $variant; + let thunk = draw(move|screen|$state.interpret(screen, &$arg)); + match variant { + // X variant + Some("x") => thunk.$x().draw($output), + // Y variant + Some("y") => thunk.$y().draw($output), + // XY variant (can be omitted) + Some("xy") | None => thunk.$xy().draw($output), + // Other namespace members are invalid + frag => invalid_variant($name, frag, $expr, $head) + } + }}; + + // Variadic variant: + // (push/x n ...) + // (push/y n ...) + // (push/xy n m ...) + ( + $name:expr => $expr:expr, $head:expr, $output:ident, $state:ident, + $variant:expr, $xy:ident, $x:ident, $y:ident, $arg0:expr, $arg1:expr, $arg2:expr, + ) => {{ + // frags.next(): 2nd slash-delimited fragment: /x, /y, /xy + let variant = $variant; + let thunk = draw(move|screen|$state.interpret(screen, &match variant { + Some("x") | Some("y") => $arg1, + Some("xy") | None => $arg2, + _ => panic!("{}: unsupported axis {variant:?}; try /x, /y, /xy", $name) + })); + match variant { + // X variant + Some("x") => thunk.$x($state.namespace($arg0?)?) + .draw($output), + // Y variant + Some("y") => thunk.$y($state.namespace($arg0?)?) + .draw($output), + // XY variant (can be omitted) + Some("xy") | None => thunk.$xy($state.namespace($arg0?)?, $state.namespace($arg1?)?) + .draw($output), + // Other namespace members are invalid + frag => invalid_variant($name, frag, $expr, $head) + } + }}; +); + macro_rules! def_layout_modifier { ( $Trait:ident ($fn_x:ident $fn_y:ident $fn_xy:ident), @@ -126,7 +196,7 @@ macro_rules! def_layout_modifier { fn_kw_layout!($kw_name |state, output, expr| { let head = expr.head()?; let mut frags = head.src()?.unwrap_or_default().split("/"); - Ok(matches!(expr.head()?, Some("exact")).then(||{ + Ok(matches!(frags.next(), Some($name)).then(||{ let args = expr.tail(); let arg0 = args.head(); let tail0 = args.tail(); diff --git a/src/layout/axis.rs b/src/layout/axis.rs index 2625744..bf9169f 100644 --- a/src/layout/axis.rs +++ b/src/layout/axis.rs @@ -35,14 +35,23 @@ impl_draw!(,>|self: Full, to: T|{ def_layout_modifier!( LayoutExact (exact_w exact_h exact_wh), Exact { W H WH } kw_exact "exact" |self, to| { - let XYWH(x, y, w0, h0) = to.area(); - let (item, w, h) = match self { - Self::W(item, w) => (item, (*w).into(), None), - Self::H(item, h) => (item, None, (*h).into()), - Self::WH(item, w, h) => (item, (*w).into(), (*h).into()), - _ => return Ok(None) + let XYWH(x0, y0, w0, h0) = to.area(); + let (item, w1, h1) = match self { + Self::W(item, w1) => (item, (*w1).into(), None), + Self::H(item, h1) => (item, None, (*h1).into()), + Self::WH(item, w1, h1) => (item, (*w1).into(), (*h1).into()), + _ => unreachable!() }; - to.draw(XYWH(x, y, w.unwrap_or(w0), h.unwrap_or(h0)), item) + let w1 = w1.unwrap_or(w0); + let h1 = h1.unwrap_or(h0); + let area = XYWH(x0, y0, w1, h1); + let area = to.draw(area, item)?; + Ok(area.map(|XYWH(x2, y2, w2, h2)|match self { + Self::W(..) => XYWH(x0, y2, w1, h2), + Self::H(..) => XYWH(x2, y0, w2, h1), + Self::WH(..) => XYWH(x0, y0, w1, h1), + _ => unreachable!() + })) } ); diff --git a/src/layout/azimuth.rs b/src/layout/azimuth.rs index eb8d038..bfb4d80 100644 --- a/src/layout/azimuth.rs +++ b/src/layout/azimuth.rs @@ -192,15 +192,13 @@ fn draw_stacks ( area_b: impl Into>>, origin_b: impl Into>, ) -> Usually<(Option>, Option>)> { - let draw_a = |to: &mut S|Ok::<_, Box>(if let Some(origin_a) = origin_a.into() { - to.draw(area_a.into(), a.align(origin_a))? - } else { - to.draw(area_a.into(), a)? + let draw_a = |to: &mut S|Ok::<_, Box>(match origin_a.into() { + Some(origin_a) => to.draw(area_a.into(), a.align(origin_a))?, + None => to.draw(area_a.into(), a)? }); - let draw_b = |to: &mut S|Ok::<_, Box>(if let Some(origin_b) = origin_b.into() { - to.draw(area_b.into(), b.align(origin_b))? - } else { - to.draw(area_b.into(), b)? + let draw_b = |to: &mut S|Ok::<_, Box>(match origin_b.into() { + Some(origin_b) => to.draw(area_b.into(), b.align(origin_b))?, + None => to.draw(area_b.into(), b)? }); Ok(if matches!(split, Split::Below) { let drawn_b = draw_b(to)?; @@ -217,7 +215,7 @@ pub fn stack_areas ( a: impl Draw, b: impl Draw, ) -> Usually<(Option>, Option>)> { - let area_a = to.draw(None, a)?; + let area_a = to.size(None, a)?; Ok(match split { Split::South => ( area_a, diff --git a/src/layout/iter.rs b/src/layout/iter.rs index b43a9d0..1662c0c 100644 --- a/src/layout/iter.rs +++ b/src/layout/iter.rs @@ -1,5 +1,49 @@ use super::*; +pub fn iter_south <'a, S: Screen, D: Draw, I: Iterator> ( + iter: impl Fn()->I +) -> impl Draw { + draw(move|to: &mut S|{ + let XYWH(x, y, w, h) = to.area(); + let mut y_next = y; + let mut h_used = S::Unit::zero(); + for item in iter() { + let area = XYWH(x, y_next, w, h.minus(h_used)); + if let Some(used) = to.draw(area, item)? { + let used_h = used.h(); + y_next = y_next.plus(used_h); + h_used = h_used.plus(used_h); + if h_used >= h { + break; + } + } + } + Ok(Some(XYWH(x, y, w, h_used))) + }) +} + +pub fn iter_east <'a, S: Screen, D: Draw, I: Iterator> ( + iter: impl Fn()->I +) -> impl Draw { + draw(move|to: &mut S|{ + let XYWH(x, y, w, h) = to.area(); + let mut x_next = x; + let mut w_used = S::Unit::zero(); + for item in iter() { + let area = XYWH(x_next, y, w.minus(w_used), h); + if let Some(used) = to.draw(area, item)? { + let used_w = used.w(); + x_next = x_next.plus(used_w); + w_used = w_used.plus(used_w); + if w_used >= w { + break; + } + } + } + Ok(Some(XYWH(x, y, w_used, h))) + }) +} + /// Iterate over a collection of the same kind of [Draw]able: pub fn iter <'a, S: Screen, D: 'a, I: Iterator, U: Draw> ( _iter: impl Fn()->I, _draw: impl Fn(D, usize)->U, @@ -31,40 +75,12 @@ pub fn iter_north <'a, S: Screen, D: 'a, I: Iterator, U: Draw> ( draw(move|_to: &mut S|{ todo!() }) } -pub fn iter_east <'a, S: Screen, D: 'a, I: Iterator, U: Draw> ( - _iter: impl Fn()->I, _draw: impl Fn(D, usize)->U, -) -> impl Draw { - draw(move|_to: &mut S|{ todo!() }) -} - pub fn iter_east_fixed <'a, S: Screen, D: 'a, I: Iterator, U: Draw> ( _height: S::Unit, _iter: impl Fn()->I, _draw: impl Fn(D, usize)->U, ) -> impl Draw { draw(move|_to: &mut S|{ todo!() }) } -pub fn iter_south <'a, S: Screen, D: Draw, I: Iterator> ( - iter: impl Fn()->I -) -> impl Draw { - draw(move|to: &mut S|{ - let XYWH(x, y, w, h) = to.area(); - let mut y_next = y; - let mut h_used = S::Unit::zero(); - for item in iter() { - let area = XYWH(x, y_next, w, h.minus(h_used)); - if let Some(used) = to.draw(area, item)? { - let used_h = used.h(); - y_next = y_next.plus(used_h); - h_used = h_used.plus(used_h); - if h_used >= h { - break; - } - } - } - Ok(Some(XYWH(x, y, w, h_used))) - }) -} - pub fn iter_south_fixed <'a, S: Screen, D: 'a, I: Iterator, U: Draw> ( _height: S::Unit, _iter: impl Fn()->I, _draw: impl Fn(D, usize)->U, ) -> impl Draw { diff --git a/src/lib.rs b/src/lib.rs index 892f0a4..814adac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -179,76 +179,6 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } } ); -/// Some layout operations exist in multiple variants that take a single argument. -/// Their handling in [eval_view] is uniform and goes like this: -macro_rules! eval_enum (( - $name:literal, $output:ident, $state:ident, $value:expr, $Enum:ident { - $($v:literal => $V:ident),* $(,)? - } -) => {{ - match $value { - $(Some($v) => $Enum::$V,)* - frag => unimplemented!("{}/{frag:?}", $name) - } -}}); - -/// Some layout operations exist in XY, X, and Y variants that take 3 or 2 arguments. -/// Their handling in [eval_view] is uniform and goes like this: -macro_rules! eval_xy ( - // Valueless variant: - // (fill/x ...) - // (fill/y ...) - // (fill/xy ...) - ( - $name:expr => $expr:expr, $head:expr, $output:ident, $state:ident, - $variant:expr, $xy:ident, $x:ident, $y:ident, $arg:expr, - ) => {{ - // frags.next(): 2nd slash-delimited fragment: /x, /y, /xy - let variant = $variant; - let thunk = draw(move|screen|$state.interpret(screen, &$arg)); - match variant { - // X variant - Some("x") => thunk.$x().draw($output), - // Y variant - Some("y") => thunk.$y().draw($output), - // XY variant (can be omitted) - Some("xy") | None => thunk.$xy().draw($output), - // Other namespace members are invalid - frag => invalid_variant($name, frag, $expr, $head) - } - }}; - - // Variadic variant: - // (push/x n ...) - // (push/y n ...) - // (push/xy n m ...) - ( - $name:expr => $expr:expr, $head:expr, $output:ident, $state:ident, - $variant:expr, $xy:ident, $x:ident, $y:ident, $arg0:expr, $arg1:expr, $arg2:expr, - ) => {{ - // frags.next(): 2nd slash-delimited fragment: /x, /y, /xy - let variant = $variant; - let thunk = draw(move|screen|$state.interpret(screen, &match variant { - Some("x") | Some("y") => $arg1, - Some("xy") | None => $arg2, - _ => panic!("{}: unsupported axis {variant:?}; try /x, /y, /xy", $name) - })); - match variant { - // X variant - Some("x") => thunk.$x($state.namespace($arg0?)?) - .draw($output), - // Y variant - Some("y") => thunk.$y($state.namespace($arg0?)?) - .draw($output), - // XY variant (can be omitted) - Some("xy") | None => thunk.$xy($state.namespace($arg0?)?, $state.namespace($arg1?)?) - .draw($output), - // Other namespace members are invalid - frag => invalid_variant($name, frag, $expr, $head) - } - }}; -); - #[cfg(feature = "exit")] pub use self::exit::*; #[cfg(feature = "exit")] mod exit { use crate::*; diff --git a/src/term.rs b/src/term.rs index bfbe0e0..e9060b1 100644 --- a/src/term.rs +++ b/src/term.rs @@ -24,7 +24,6 @@ pub(crate) use ::{ mod border; pub use self::border::*; mod buffer; pub use self::buffer::*; -mod button; pub use self::button::*; mod event; pub use self::event::*; mod keys; pub use self::keys::*; mod modify; pub use self::modify::*; diff --git a/src/term/button.rs b/src/term/button.rs deleted file mode 100644 index 85c7887..0000000 --- a/src/term/button.rs +++ /dev/null @@ -1,28 +0,0 @@ -use crate::{*, Color::*}; - -/// ``` -/// let _ = tengri::button_2("", "", true); -/// let _ = tengri::button_2("", "", false); -/// ``` -pub const fn button_2 <'a> (key: impl Draw, label: impl Draw, hide: bool) -> impl Draw { - let c1 = tui_orange(); - let c2 = tui_g(0); - let c3 = tui_g(96); - let c4 = tui_g(255); - bold(true, fg_bg(c1, c2, east(fg(c2, east(key, fg(c3, "▐"))), when(!hide, fg_bg(c4, c3, label))))) -} - -/// ``` -/// let _ = tengri::button_3("", "", "", true); -/// let _ = tengri::button_3("", "", "", false); -/// ``` -pub const fn button_3 <'a> ( - key: impl Draw, label: impl Draw, value: impl Draw, editing: bool, -) -> impl Draw { - bold(true, east( - fg_bg(tui_orange(), tui_g(0), - east(fg(tui_g(0), "▐"), east(key, fg(if editing { tui_g(128) } else { tui_g(96) }, "▐")))), - east( - when(!editing, east(fg_bg(tui_g(255), tui_g(96), label), fg_bg(tui_g(128), tui_g(96), "▐"),)), - east(fg_bg(tui_g(224), tui_g(128), value), fg_bg(tui_g(128), Reset, "▌"), )))) -} From 8a6eb19e279afefc126008c27b38cc66645e1c96 Mon Sep 17 00:00:00 2001 From: facile pop culture reference Date: Sat, 8 Aug 2026 23:37:06 +0300 Subject: [PATCH 2/2] reenable full/xy mod --- src/layout.rs | 86 +++++++++++++++++++++++++++++++++++++--------- src/layout/axis.rs | 41 ++++++++++------------ src/lib.rs | 31 ----------------- 3 files changed, 88 insertions(+), 70 deletions(-) diff --git a/src/layout.rs b/src/layout.rs index e2586fe..108f013 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -3,16 +3,6 @@ use crate::*; impl> Layout for T {} pub trait Layout: Draw + Sized { - fn full_w (self) -> impl Draw { - Full::W(self) - } - fn full_h (self) -> impl Draw { - Full::H(self) - } - fn full_wh (self) -> impl Draw { - Full::WH(self) - } - fn origin (self, azimuth: impl Into>) -> impl Draw { Origin(azimuth.into(), self) } @@ -98,7 +88,7 @@ impl Sizer { /// Some layout operations exist in multiple variants that take a single argument. /// Their handling in [eval_view] is uniform and goes like this: -macro_rules! eval_enum (( +#[macro_export] macro_rules! eval_enum (( $name:literal, $output:ident, $state:ident, $value:expr, $Enum:ident { $($v:literal => $V:ident),* $(,)? } @@ -111,7 +101,7 @@ macro_rules! eval_enum (( /// Some layout operations exist in XY, X, and Y variants that take 3 or 2 arguments. /// Their handling in [eval_view] is uniform and goes like this: -macro_rules! eval_xy ( +#[macro_export] macro_rules! eval_xy ( // Valueless variant: // (fill/x ...) // (fill/y ...) @@ -166,7 +156,36 @@ macro_rules! eval_xy ( }}; ); -macro_rules! def_layout_modifier { +#[macro_export] macro_rules! fn_kw_layout { + ($name:ident |$state:ident, $output: ident, $expr:ident| $body:block) => { + #[cfg(all(feature = "draw", feature = "eval"))] + pub fn $name ( + $state: &S, $output: &mut O, $expr: &str + ) -> Perhaps> where + S: Interpret>> + + for<'b> Namespace<'b, bool> + + for<'b> Namespace<'b, O::Unit> + + for<'b> Namespace<'b, Option> + $body + } +} + +#[macro_export] macro_rules! fn_kw_layout_tui { + ($name:ident |$state:ident, $output: ident, $expr:ident| $body:block) => { + #[cfg(all(feature = "draw", feature = "eval", feature = "term"))] + pub fn $name ( + $state: &S, $output: &mut Tui, $expr: &str + ) -> Perhaps> where + S: Interpret>> + + for<'b> Namespace<'b, bool> + + for<'b> Namespace<'b, u16> + + for<'b> Namespace<'b, Option> + + for<'b> Namespace<'b, Color> + $body + } +} + +#[macro_export] macro_rules! def_layout_modifier { ( $Trait:ident ($fn_x:ident $fn_y:ident $fn_xy:ident), $Struct:ident { $X:ident $Y:ident $XY:ident } $kw_name:ident $name:literal @@ -188,9 +207,7 @@ macro_rules! def_layout_modifier { } impl , X: Into> + Copy> Draw for $Struct { - fn draw (&$self, $to: &mut S) -> Perhaps> { - $body - } + fn draw (&$self, $to: &mut S) -> Perhaps> { $body } } fn_kw_layout!($kw_name |state, output, expr| { @@ -213,6 +230,43 @@ macro_rules! def_layout_modifier { } } +#[macro_export] macro_rules! def_layout_modifier_flat { + ( + $Trait:ident ($fn_x:ident $fn_y:ident $fn_xy:ident), + $Struct:ident { $X:ident $Y:ident $XY:ident } $kw_name:ident $name:literal + |$self:ident, $to:ident| $body:block + ) => { + impl> $Trait for T {} + + pub trait $Trait: Draw + Sized { + fn $fn_x (self) -> $Struct { $Struct::$X(self) } + fn $fn_y (self) -> $Struct { $Struct::$Y(self) } + fn $fn_xy (self) -> $Struct { $Struct::$XY(self) } + } + + pub enum $Struct> { + __(PhantomData), $X(I), $Y(I), $XY(I), + } + + impl > Draw for $Struct { + fn draw (&$self, $to: &mut S) -> Perhaps> { $body } + } + + fn_kw_layout!($kw_name |state, output, expr| { + let head = expr.head()?; + let mut frags = head.src()?.unwrap_or_default().split("/"); + Ok(matches!(frags.next(), Some($name)).then(||{ + let args = expr.tail(); + let arg0 = args.head(); + eval_xy!( + $name => expr, head, output, state, frags.next(), + $fn_xy, $fn_x, $fn_y, arg0, + ) + }).transpose()?.flatten()) + }); + } +} + mod area; pub use self::area::*; mod axis; pub use self::axis::*; mod azimuth; pub use self::azimuth::*; diff --git a/src/layout/axis.rs b/src/layout/axis.rs index bf9169f..28002d5 100644 --- a/src/layout/axis.rs +++ b/src/layout/axis.rs @@ -1,28 +1,23 @@ use crate::*; -/// Use whole drawing area along one or both axes. -pub enum Full> { - __(PhantomData), - W(I), - H(I), - WH(I), -} - -impl_draw!(,>|self: Full, to: T|{ - let XYWH(x0, y0, w0, h0) = to.area(); - let item = match self { - Self::W(i) => i, Self::H(i) => i, Self::WH(i) => i, _ => unreachable!() - }; - let (x1, y1, w1, h1) = match self { - Self::W(..) => (Some(x0), None, Some(w0), None), - Self::H(..) => (None, Some(y0), None, Some(h0)), - Self::WH(..) => (Some(x0), Some(y0), Some(w0), Some(h0)), - _ => unreachable!() - }; - Ok(to.draw(to.area(), item)?.map(|XYWH(x2, y2, w2, h2)|XYWH( - x1.unwrap_or(x2), y1.unwrap_or(y2), w1.unwrap_or(w2), h1.unwrap_or(h2), - ))) -}); +def_layout_modifier_flat!( + LayoutFull (full_w full_h full_wh), + Full { W H WH } kw_full "full" |self, to| { + let XYWH(x0, y0, w0, h0) = to.area(); + let item = match self { + Self::W(i) => i, Self::H(i) => i, Self::WH(i) => i, _ => unreachable!() + }; + let (x1, y1, w1, h1) = match self { + Self::W(..) => (Some(x0), None, Some(w0), None), + Self::H(..) => (None, Some(y0), None, Some(h0)), + Self::WH(..) => (Some(x0), Some(y0), Some(w0), Some(h0)), + _ => unreachable!() + }; + Ok(to.draw(to.area(), item)?.map(|XYWH(x2, y2, w2, h2)|XYWH( + x1.unwrap_or(x2), y1.unwrap_or(y2), w1.unwrap_or(w2), h1.unwrap_or(h2), + ))) + } +); #[cfg(test)] #[test] fn test_layout_full () -> Usually<()> { assert_eq!(check_layout("1")?, Some(XYWH(1u16, 1, 1, 1))); diff --git a/src/lib.rs b/src/lib.rs index 814adac..494a73b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,37 +29,6 @@ pub(crate) use ::{ std::marker::PhantomData }; -#[cfg(all(feature = "draw", feature = "eval"))] -macro_rules! fn_kw_layout { - ($name:ident |$state:ident, $output: ident, $expr:ident| $body:block) => { - #[cfg(all(feature = "draw", feature = "eval"))] - pub fn $name ( - $state: &S, $output: &mut O, $expr: &str - ) -> Perhaps> where - S: Interpret>> - + for<'b> Namespace<'b, bool> - + for<'b> Namespace<'b, O::Unit> - + for<'b> Namespace<'b, Option> - $body - } -} - -#[cfg(all(feature = "draw", feature = "eval", feature = "term"))] -macro_rules! fn_kw_layout_tui { - ($name:ident |$state:ident, $output: ident, $expr:ident| $body:block) => { - #[cfg(all(feature = "draw", feature = "eval", feature = "term"))] - pub fn $name ( - $state: &S, $output: &mut Tui, $expr: &str - ) -> Perhaps> where - S: Interpret>> - + for<'b> Namespace<'b, bool> - + for<'b> Namespace<'b, u16> - + for<'b> Namespace<'b, Option> - + for<'b> Namespace<'b, Color> - $body - } -} - #[cfg(feature = "lang")] pub use ::dizzle::{Usually, Perhaps}; #[cfg(feature = "lang")] use ::dizzle::*;