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, "▌"), )))) -}