diff --git a/src/draw/layout.rs b/src/draw/layout.rs index 0c892aa..366eadf 100644 --- a/src/draw/layout.rs +++ b/src/draw/layout.rs @@ -167,8 +167,26 @@ pub enum Full> { WH(I), } -impl_draw!(,>|self: Full, _to: T|{ - todo!() +impl_draw!(,>|self: Full, to: T|{ + let XYWH(x0, y0, w0, h0) = to.area(); + match self { + Self::W(item) => if let Some(XYWH(_, y, _, h)) = item.layout(to.area())? { + to.clip(XYWH(x0, y, w0, h), |to|item.draw(to)) + } else { + Ok(None) + }, + Self::H(item) => if let Some(XYWH(x, _, w, _)) = item.layout(to.area())? { + to.clip(XYWH(x, y0, w, h0), |to|item.draw(to)) + } else { + Ok(None) + }, + Self::WH(item) => if let Some(XYWH(..)) = item.layout(to.area())? { + to.clip(XYWH(x0, y0, w0, h0), |to|item.draw(to)) + } else { + Ok(None) + }, + _ => unreachable!(), + } }); /// Move content in the positive direction of one or both axes. @@ -186,8 +204,26 @@ pub enum Push, X: Into>> { XY(I, X, X), } -impl_draw!(, X: Into>,>|self: Push, _to: T|{ - todo!() +impl_draw!(, X: Into>,>|self: Push, to: T|{ + match self { + Self::__(_) => unreachable!(), + Self::X(item, x1) if let Some(XYWH(x, y, w, h)) = item.layout(to.area())? => { + to.clip(XYWH( + x + x1.into().unwrap_or_default(), y, w, h + ), |to|item.draw(to)) + }, + Self::Y(item, y1) if let Some(XYWH(x, y, w, h)) = item.layout(to.area())? => { + to.clip(XYWH( + x, y + y1.into().unwrap_or_default(), w, h + ), |to|item.draw(to)) + }, + Self::XY(item, x1, y1) if let Some(XYWH(x, y, w, h)) = item.layout(to.area())? => { + to.clip(XYWH( + x + x1.into().unwrap_or_default(), y + y1.into().unwrap_or_default(), w, h + ), |to|item.draw(to)) + }, + _ => Ok(None) + } }); /// Move content in the negative direction of one or both axes. @@ -262,8 +298,20 @@ pub enum Exact, X: Into>> { WH(I, X, X), } -impl_draw!(, X: Into>,>|self: Exact, _to: T|{ - todo!() +impl_draw!(, X: Into>,>|self: Exact, to: T|{ + match self { + Self::__(_) => unreachable!(), + Self::W(item, w1) if let Some(XYWH(x, y, w, h)) = item.layout(to.area())? => { + to.clip(XYWH(x, y, w1.into().unwrap_or(w), h), |to|item.draw(to)) + }, + Self::H(item, h1) if let Some(XYWH(x, y, w, h)) = item.layout(to.area())? => { + to.clip(XYWH(x, y, w, h1.into().unwrap_or(h)), |to|item.draw(to)) + }, + Self::WH(item, w1, h1) if let Some(XYWH(x, y, w, h)) = item.layout(to.area())? => { + to.clip(XYWH(x, y, w1.into().unwrap_or(w), h1.into().unwrap_or(h)), |to|item.draw(to)) + }, + _ => Ok(None) + } }); /// Define inner drawing area. diff --git a/src/eval.rs b/src/eval.rs index d103a0a..67f565e 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -1,52 +1,6 @@ use crate::{*, lang::*}; use ratatui::style::Color; -/// 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 (( - $name:expr, $expr:expr, $head:expr, $output:ident, $state:ident, - $variant:expr, $arg0: ident, $arg1: ident, $arg2: ident, - $xy:ident, $x:ident, $y:ident -) => {{ - let variant = $variant; - let cb = thunk(move|output: &mut O|$state.interpret(output, &match variant { - Some("x") | Some("y") => $arg1, - Some("xy") | None => $arg2, - _ => panic!("{}: unsupported axis {variant:?}; try /x, /y, /xy", $name) - })); - - match variant { - - // XY variant (can be omitted) - Some("xy") | None => cb.push_xy( - $state.namespace($arg0?)?, - $state.namespace($arg1?)?, - ).draw($output), - - // X variant - Some("x") => cb.push_x( - $state.namespace($arg0?)?, - ).draw($output), - - // Y variant - Some("y") => cb.push_y( - $state.namespace($arg0?)?, - ).draw($output), - - // Other namespace members are invalid - frag => { - let name = $name; - let expr = $expr; - let head = $head; - unimplemented!( - "{name}/{frag:?} ({expr:?}) ({head:?}) ({:?})", - head.src()?.unwrap_or_default().split("/").next() - ) - } - - } -}}); - /// 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 (( @@ -60,6 +14,75 @@ 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 ( + // 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: ident, + ) => {{ + // frags.next(): 2nd slash-delimited fragment: /x, /y, /xy + let variant = $variant; + let thunk = thunk(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: ident, $arg1: ident, $arg2: ident, + ) => {{ + // frags.next(): 2nd slash-delimited fragment: /x, /y, /xy + let variant = $variant; + let thunk = thunk(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) + } + }}; +); + +fn invalid_variant ( + name: &str, + frag: impl Language, + expr: impl Language, + head: impl Language, +) -> Usually { + unimplemented!( + "{name}/{frag:?} ({expr:?}) ({head:?}) ({:?})", + head.src()?.unwrap_or_default().split("/").next() + ) +} + /// Interpret layout operation. /// /// ``` @@ -103,67 +126,46 @@ pub fn eval_view <'a, O: Screen + 'a, S> ( let arg1 = tail0.head(); let tail1 = tail0.tail(); let arg2 = tail1.head(); - // First `frags.next()` calls returns the namespace. match frags.next() { - Some("when") => when( state.namespace(arg0?)?.unwrap(), thunk(move|output: &mut O|{state.interpret(output, &arg1)}) ).draw(output), - Some("either") => either( state.namespace(arg0?)?.unwrap(), thunk(move|output: &mut O|{state.interpret(output, &arg1)}), thunk(move|output: &mut O|{state.interpret(output, &arg2)}), ).draw(output), - - // Second `frags.next()` calls returns the namespace member. Some("bsp") => eval_enum!("bsp", output, state, frags.next(), arg0, Split { - "n" => North, - "s" => South, - "e" => East, - "w" => West, - "a" => Above, - "b" => Below + "n" => North, "s" => South, "e" => East, "w" => West, "a" => Above, "b" => Below }).half( thunk(move|output: &mut O|{state.interpret(output, &arg0)}), thunk(move|output: &mut O|{state.interpret(output, &arg1)}), ).draw(output), - - Some("align") => { - let content = thunk(move|output: &mut O|{state.interpret(output, &arg0)}); - content.align(eval_enum!("align", output, state, frags.next(), arg0, Azimuth { - "c" => C, - "n" => N, - "s" => S, - "e" => E, - "w" => W, - "x" => X, - "y" => Y - })).draw(output) - }, - + Some("align") => thunk(move|output: &mut O|{ + state.interpret(output, &arg0) + }).align(eval_enum!("align", output, state, frags.next(), arg0, Azimuth { + "c" => C, "n" => N, "s" => S, "e" => E, "w" => W, "x" => X, "y" => Y + })).draw(output), Some("exact") => eval_xy!( - "exact", expr, head, output, state, frags.next(), - arg0, arg1, arg2, wh_exact, w_exact, h_exact + "exact" => expr, head, output, state, frags.next(), exact_wh, exact_w, exact_h, arg0, arg1, arg2, + ), + Some("fixed") => eval_xy!( + "fixed" => expr, head, output, state, frags.next(), exact_wh, exact_w, exact_h, arg0, arg1, arg2, ), - Some("min") => eval_xy!( - "min", expr, head, output, state, frags.next(), - arg0, arg1, arg2, wh_min, w_min, h_min + "min" => expr, head, output, state, frags.next(), min_wh, min_w, min_h, arg0, arg1, arg2, ), - Some("max") => eval_xy!( - "max", expr, head, output, state, frags.next(), - arg0, arg1, arg2, wh_max, w_max, h_max + "max" => expr, head, output, state, frags.next(), max_wh, max_w, max_h, arg0, arg1, arg2, ), - Some("push") => eval_xy!( - "push", expr, head, output, state, frags.next(), - arg0, arg1, arg2, xy_push, x_push, y_push + "push" => expr, head, output, state, frags.next(), push_xy, push_x, push_y, arg0, arg1, arg2, + ), + Some("fill") => eval_xy!( + "fill" => expr, head, output, state, frags.next(), full_wh, full_w, full_h, arg0, ), - _ => return Ok(None) }