diff --git a/.gitignore b/.gitignore index 6f7f1d4..97fd6f0 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ pkg/ dist/ traces/ *.DS_Store +.cargo/ diff --git a/Cargo.toml b/Cargo.toml index 45ccb89..d7f42ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,8 @@ test-api = [] macroquad-examples = ["macroquad"] egui-examples = ["egui", "eframe", "egui_extras"] +required-features = ["macroquad-examples"] + [[example]] name = "macroquad-example" path = "examples/macroquad-example/src/main.rs" diff --git a/examples/demo-site/src/app.rs b/examples/demo-site/src/app.rs index c4ec902..daaf18b 100644 --- a/examples/demo-site/src/app.rs +++ b/examples/demo-site/src/app.rs @@ -36,7 +36,7 @@ impl eframe::App for TemplateApp { ctx.set_zoom_factor(zoom_factor); } egui::CentralPanel::default().show(ctx, |ui| { - let layout = Layout::new(my_layout_fn); + let mut layout = Layout::new(my_layout_fn()); let viewport = ctx.input(|i| i.screen_rect()); let available_area = area_from(viewport); let mut state = State { @@ -56,29 +56,31 @@ const DEMO_HINT: Color32 = Color32::from_rgb(35, 35, 38); const DEMO_FG: Color32 = Color32::from_rgb(250, 250, 255); const DEMO_FG_SECONDARY: Color32 = Color32::from_rgb(180, 180, 183); -fn my_layout_fn<'a>(state: &mut State<'_>) -> Node> { - stack(vec![ - rect(Color32::TRANSPARENT, DEMO_BG, 0.), - row(vec![ - row_divider(DEMO_GRAY).width(1.), - column(vec![ - header(state), - col_divider(DEMO_GRAY).height(1.), - main_view(state), - col_divider(DEMO_GRAY).height(1.), - footer(state), - ]), +fn my_layout_fn<'a>() -> Node<'a, State<'a>> { + dynamic(|state| { + stack(vec![ + rect(Color32::TRANSPARENT, DEMO_BG, 0.), + row(vec![ + row_divider(DEMO_GRAY).width(1.), + column(vec![ + header(state), + col_divider(DEMO_GRAY).height(1.), + main_view(state), + col_divider(DEMO_GRAY).height(1.), + footer(state), + ]), + ]) + .align(Align::Top), + if *state.sidebar { + side_bar(state) + } else { + empty() + }, ]) - .align(Align::Top), - if *state.sidebar { - side_bar(state) - } else { - empty() - }, - ]) + }) } -fn footer<'a>(state: &mut State<'_>) -> Node> { +fn footer<'a>(state: &mut State<'_>) -> Node<'a, State<'a>> { row_spaced( 10., vec![ @@ -105,7 +107,7 @@ fn footer<'a>(state: &mut State<'_>) -> Node> { .height(40.) } -fn main_view<'a>(state: &mut State<'_>) -> Node> { +fn main_view<'a>(state: &mut State<'_>) -> Node<'a, State<'a>> { let profile_blurb = "Your public profile URL can be shared with anyone and allows them to immediately see your bases and activity in Backer."; let pic_blurb = "Upload a profile picture of yourself or the character you always wanted to be. Your avatar will be displayed all over the Backer world."; let info_blurb = "Tell the world about yourself. Information you add will be visible only in your profile, not for all users."; @@ -298,7 +300,7 @@ fn main_view<'a>(state: &mut State<'_>) -> Node> { .pad(20.)]) } -fn side_bar<'a>(state: &mut State<'_>) -> Node> { +fn side_bar<'a>(state: &mut State<'_>) -> Node<'a, State<'a>> { stack(vec![ rect(Color32::TRANSPARENT, DEMO_BG, 0.), column_spaced( @@ -331,7 +333,7 @@ fn side_bar<'a>(state: &mut State<'_>) -> Node> { .width(200.) } -fn header<'a>(state: &mut State<'_>) -> Node> { +fn header<'a>(state: &mut State<'_>) -> Node<'a, State<'a>> { row_spaced( 10., vec![ @@ -366,7 +368,7 @@ fn header<'a>(state: &mut State<'_>) -> Node> { .height(80.) } -fn menu_button<'a>(_ui: &mut State<'_>) -> Node> { +fn menu_button<'a>(_ui: &mut State<'_>) -> Node<'a, State<'a>> { draw(move |area, ui: &mut State<'_>| { if ui .ui @@ -385,7 +387,7 @@ fn menu_button<'a>(_ui: &mut State<'_>) -> Node> { .height(30.) } -fn icon<'a>(image: impl Into> + 'static) -> Node> { +fn icon<'a>(image: impl Into> + 'static) -> Node<'a, State<'a>> { let image = Image::new(image).tint(Color32::WHITE); draw(move |area, ui: &mut State<'_>| { ui.ui.put(rect_from(area), image.clone()); @@ -396,7 +398,7 @@ fn label<'a, S: AsRef + 'static + Clone + Copy>( state: &mut State<'_>, text: S, size: f32, -) -> Node> { +) -> Node<'a, State<'a>> { label_common(state, text, size, false, Color32::WHITE) } @@ -404,7 +406,7 @@ fn fit_label<'a, S: AsRef + 'static + Clone + Copy>( state: &mut State<'_>, text: S, size: f32, -) -> Node> { +) -> Node<'a, State<'a>> { label_common(state, text, size, true, Color32::WHITE) } @@ -413,7 +415,7 @@ fn fit_label_color<'a, S: AsRef + 'static + Clone + Copy>( text: S, size: f32, color: Color32, -) -> Node> { +) -> Node<'a, State<'a>> { label_common(state, text, size, true, color) } @@ -422,7 +424,7 @@ fn label_color<'a, S: AsRef + 'static + Clone + Copy>( text: S, size: f32, color: Color32, -) -> Node> { +) -> Node<'a, State<'a>> { label_common(state, text, size, false, color) } @@ -432,7 +434,7 @@ fn label_common<'a, S: AsRef + 'static + Clone + Copy>( size: f32, fit_width: bool, color: Color32, -) -> Node> { +) -> Node<'a, State<'a>> { fn layout_job( font_size: f32, width: f32, @@ -502,7 +504,7 @@ fn label_common<'a, S: AsRef + 'static + Clone + Copy>( } } -fn col_divider<'a>(color: Color32) -> Node> { +fn col_divider<'a>(color: Color32) -> Node<'a, State<'a>> { draw(move |area, ui: &mut State<'_>| { ui.ui.painter().line_segment( [ @@ -514,7 +516,7 @@ fn col_divider<'a>(color: Color32) -> Node> { }) } -fn row_divider<'a>(color: Color32) -> Node> { +fn row_divider<'a>(color: Color32) -> Node<'a, State<'a>> { draw(move |area, ui: &mut State<'_>| { ui.ui.painter().line_segment( [ @@ -526,7 +528,7 @@ fn row_divider<'a>(color: Color32) -> Node> { }) } -fn rect<'a>(stroke: Color32, fill: Color32, rounding: f32) -> Node> { +fn rect<'a>(stroke: Color32, fill: Color32, rounding: f32) -> Node<'a, State<'a>> { draw(move |area, ui: &mut State<'_>| { ui.ui .painter() @@ -535,7 +537,7 @@ fn rect<'a>(stroke: Color32, fill: Color32, rounding: f32) -> Node> { }) } -fn rect_stroke<'a>(color: Color32) -> Node> { +fn rect_stroke<'a>(color: Color32) -> Node<'a, State<'a>> { draw(move |area, ui: &mut State<'_>| { ui.ui .painter() diff --git a/examples/egui-example/src/main.rs b/examples/egui-example/src/main.rs index 6af231b..d3d72db 100644 --- a/examples/egui-example/src/main.rs +++ b/examples/egui-example/src/main.rs @@ -36,6 +36,7 @@ fn my_layout_fn(ui: &mut Ui) -> Node { draw_c(ui), ], ) + .pad(10.) } fn draw_a(ui: &mut Ui) -> Node { diff --git a/examples/macroquad-example/src/main.rs b/examples/macroquad-example/src/main.rs index 5f37cbb..dd1ef0f 100644 --- a/examples/macroquad-example/src/main.rs +++ b/examples/macroquad-example/src/main.rs @@ -1,6 +1,5 @@ use backer::models::*; use backer::nodes::*; -use backer::traits::Scopable; use backer::Layout; use backer::Node; use macroquad::prelude::*; @@ -41,24 +40,17 @@ async fn main() { const BTN_SIZE: f32 = 50.; fn layout_for_highlight(ctx: &mut State) -> Node { - struct HighlightScoper; - impl Scopable for HighlightScoper { - fn scope( - scoping: &mut State, - f: impl FnOnce(&mut HighlightedCase) -> Result, - ) -> Result { - f(&mut scoping.highlight) - } - } let highlight = ctx.highlight; row_spaced( 10., vec![ - if highlight == HighlightedCase::RelAbsSequence || highlight == HighlightedCase::None { - scope::<_, _, HighlightScoper>(|highlight| rel_abs_seq(*highlight)) - } else { - empty() - }, + draw(|area, state: &mut State| { + Layout::new(|state: &mut HighlightedCase| rel_abs_seq(*state)) + .draw(area, &mut state.highlight); + }) + .visible( + highlight == HighlightedCase::RelAbsSequence || highlight == HighlightedCase::None, + ), if highlight == HighlightedCase::AlignmentOffset || highlight == HighlightedCase::None { column_spaced( 10., @@ -134,7 +126,7 @@ fn rel_abs_seq(_highlight: HighlightedCase) -> Node { fn text(string: &'static str, font_size: f32, color: Color) -> Node { let dimensions = measure_text(string, None, font_size as u16, 1.0); - draw(move |area: Area, _| { + draw(move |area: Area, _: &mut U| { draw_text( string, area.x + ((area.width - dimensions.width) * 0.5), @@ -148,7 +140,7 @@ fn text(string: &'static str, font_size: f32, color: Color) -> Node { } fn rect(color: Color) -> Node { - draw(move |area: Area, _| { + draw(move |area: Area, _: &mut U| { draw_rectangle(area.x, area.y, area.width, area.height, color); }) } @@ -157,7 +149,7 @@ fn button(label: &'static str, action: Action) -> Node where Action: Fn(&mut U) + 'static, { - draw(move |area: Area, ctx| { + draw(move |area: Area, ctx: &mut U| { if widgets::Button::new(label) .size(vec2(area.width, area.height)) .position(vec2(area.x, area.y)) diff --git a/src/constraints.rs b/src/constraints.rs index 4040445..abc0927 100644 --- a/src/constraints.rs +++ b/src/constraints.rs @@ -1,7 +1,7 @@ use crate::{ layout::NodeValue, models::{Area, Size, XAlign, YAlign}, - traits::NodeTrait, + node_cache::NodeCache, }; #[derive(Debug, Clone, Copy, PartialEq)] @@ -71,48 +71,45 @@ impl Constraint { } } -impl NodeValue { +impl NodeValue<'_, State> { pub(crate) fn constraints( &mut self, available_area: Area, state: &mut State, - ctx: &mut Ctx, - ) -> SizeConstraints { + ) -> Option { let contextual_aligns = self.contextual_aligns(); let allocations = self.allocate_area( available_area, contextual_aligns.0, contextual_aligns.1, state, - ctx, ); match self { - NodeValue::Padding { amounts, element } => { - let child = element.constraints(allocations[0], state, ctx); - SizeConstraints { + NodeValue::Padding { amounts, element } => element + .constraints(allocations[0], state) + .map(|constraints| SizeConstraints { width: Constraint::new( - child + constraints .width .get_lower() .map(|lower| lower + amounts.leading + amounts.trailing), - child + constraints .width .get_upper() .map(|upper| upper + amounts.leading + amounts.trailing), ), height: Constraint::new( - child + constraints .height .get_lower() .map(|lower| lower + amounts.top + amounts.bottom), - child + constraints .height .get_upper() .map(|upper| upper + amounts.top + amounts.bottom), ), ..Default::default() - } - } + }), NodeValue::Column { ref mut elements, spacing, @@ -120,26 +117,18 @@ impl NodeValue { } => elements .iter_mut() .zip(allocations.iter()) - .fold( - Option::::None, - |current, (element, allocated)| { - if let Some(current) = current { - Some(SizeConstraints { - width: current.width.combine_adjacent_priority( - element.constraints(*allocated, state, ctx).width, - ), - height: current.height.combine_sum( - element.constraints(*allocated, state, ctx).height, - *spacing, - ), - ..Default::default() - }) - } else { - Some(element.constraints(*allocated, state, ctx)) - } - }, - ) - .unwrap_or_default(), + .filter_map(|(element, &allocated)| element.constraints(allocated, state)) + .fold(Option::::None, |current, constraints| { + if let Some(current) = current { + Some(SizeConstraints { + width: current.width.combine_adjacent_priority(constraints.width), + height: current.height.combine_sum(constraints.height, *spacing), + ..Default::default() + }) + } else { + Some(constraints) + } + }), NodeValue::Row { ref mut elements, spacing, @@ -147,50 +136,50 @@ impl NodeValue { } => elements .iter_mut() .zip(allocations.iter()) - .fold( - Option::::None, - |current, (element, allocated)| { - if let Some(current) = current { - Some(SizeConstraints { - width: current.width.combine_sum( - element.constraints(*allocated, state, ctx).width, - *spacing, - ), - height: current.height.combine_adjacent_priority( - element.constraints(*allocated, state, ctx).height, - ), - ..Default::default() - }) - } else { - Some(element.constraints(*allocated, state, ctx)) - } - }, - ) - .unwrap_or_default(), + .filter_map(|(element, &allocated)| element.constraints(allocated, state)) + .fold(Option::::None, |current, constraints| { + if let Some(current) = current { + Some(SizeConstraints { + width: current.width.combine_sum(constraints.width, *spacing), + height: current.height.combine_adjacent_priority(constraints.height), + ..Default::default() + }) + } else { + Some(constraints) + } + }), NodeValue::Stack { elements, .. } => elements .iter_mut() - .fold(Option::::None, |current, element| { + .filter_map(|element| element.constraints(allocations[0], state)) + .fold(Option::::None, |current, constraints| { if let Some(current) = current { - Some(current.combine_adjacent_priority(element.constraints( - allocations[0], - state, - ctx, - ))) + Some(current.combine_adjacent_priority(constraints)) } else { - Some(element.constraints(allocations[0], state, ctx)) + Some(constraints) } - }) - .unwrap_or_default(), - NodeValue::Explicit { options, element } => { - SizeConstraints::from_size(options.clone(), allocations[0], state, ctx) - .combine_explicit_with_child(element.constraints(allocations[0], state, ctx)) - } - NodeValue::Offset { element, .. } => element.constraints(allocations[0], state, ctx), - NodeValue::Scope { scoped } => scoped.constraints(allocations[0], state, ctx), - NodeValue::Draw(_) | NodeValue::Space | NodeValue::AreaReader { .. } => { - SizeConstraints::default() + }), + NodeValue::Explicit { options, element } => element + .constraints(allocations[0], state) + .map(|child_constraints| { + SizeConstraints::from_size(options.clone(), allocations[0], state) + .combine_explicit_with_child(child_constraints) + }), + NodeValue::Offset { element, .. } => element.constraints(allocations[0], state), + NodeValue::Draw(_) => Some(SizeConstraints::default()), + NodeValue::Space | NodeValue::AreaReader { .. } => Some(SizeConstraints::default()), + NodeValue::Coupled { element, .. } => element.constraints(allocations[0], state), + NodeValue::Visibility { visible, element } => { + if *visible { + element.constraints(allocations[0], state) + } else { + None + } } - NodeValue::Coupled { element, .. } => element.constraints(allocations[0], state, ctx), + NodeValue::NodeTrait { node } => node.constraints(available_area, state), + NodeValue::Dynamic { node, computed } => computed + .get_or_insert(Box::new(NodeCache::new(node(state).inner))) + .constraints(available_area, state), + NodeValue::Empty | NodeValue::Group(_) => unreachable!(), } } @@ -300,7 +289,7 @@ impl Constraint { } impl SizeConstraints { - pub(crate) fn from_size(value: Size, area: Area, a: &mut A, b: &mut B) -> Self { + pub(crate) fn from_size(value: Size, area: Area, state: &mut State) -> Self { let mut initial = SizeConstraints { width: if value.width_min.is_some() || value.width_max.is_some() { Constraint::new(value.width_min, value.width_max) @@ -319,12 +308,12 @@ impl SizeConstraints { y_align: value.y_align, }; if let Some(dynamic) = value.dynamic_height { - let result = Some(initial.height.clamp(dynamic(area.width, a, b))); + let result = Some(initial.height.clamp(dynamic(area.width, state))); initial.height.set_lower(result); initial.height.set_upper(result); } if let Some(dynamic) = value.dynamic_width { - let result = Some(initial.width.clamp(dynamic(area.height, a, b))); + let result = Some(initial.width.clamp(dynamic(area.height, state))); initial.width.set_lower(result); initial.width.set_upper(result); } diff --git a/src/debug.rs b/src/debug.rs index a533c49..07e2bf2 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -1,7 +1,7 @@ use crate::layout::NodeValue; use std::fmt; -impl fmt::Debug for NodeValue { +impl fmt::Debug for NodeValue<'_, State> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { NodeValue::Padding { amounts, element } => f @@ -63,9 +63,6 @@ impl fmt::Debug for NodeValue { NodeValue::Space => write!(f, "Space"), NodeValue::Empty => write!(f, "Empty"), NodeValue::AreaReader { .. } => write!(f, "WidthReader"), - NodeValue::Scope { scoped } => { - f.debug_struct("Scope").field("scoped", &scoped).finish() - } NodeValue::Coupled { element, coupled, @@ -76,6 +73,16 @@ impl fmt::Debug for NodeValue { .field("coupled", coupled) .field("over", over) .finish(), + NodeValue::NodeTrait { .. } => f.debug_struct("NodeTrait").finish(), + NodeValue::Visibility { visible, element } => f + .debug_struct("Visibility") + .field("element", &element) + .field("visible", visible) + .finish(), + NodeValue::Dynamic { computed, .. } => f + .debug_struct("Dynamic") + .field("computed", computed) + .finish(), } } } diff --git a/src/drawable.rs b/src/drawable.rs index 6255d73..43137f9 100644 --- a/src/drawable.rs +++ b/src/drawable.rs @@ -1,23 +1,36 @@ -use crate::models::Area; -use std::{fmt, rc::Rc}; +use crate::{models::Area, traits::Drawable}; +use std::fmt; -type DrawFn = Rc; +type DrawFn<'nodes, State> = Box; -#[derive(Clone)] -pub(crate) struct Drawable { +pub(crate) enum SomeDrawable<'nodes, State> { + Fn(DrawFn<'nodes, State>), + Object(Box + 'nodes>), +} + +impl SomeDrawable<'_, State> { + fn draw(&mut self, area: Area, state: &mut State, visible: bool) { + match self { + SomeDrawable::Fn(closure) => closure(area, state), + SomeDrawable::Object(object) => object.draw(area, state, visible), + } + } +} + +pub(crate) struct DrawableNode<'nodes, State> { pub(crate) area: Area, - pub(crate) draw: DrawFn, + pub(crate) drawable: SomeDrawable<'nodes, State>, } -impl Drawable { - pub(crate) fn draw(&self, area: Area, a: &mut State, b: &mut Ctx) { - if area.width > 0. && area.height > 0. { - (self.draw)(area, a, b); +impl DrawableNode<'_, State> { + pub(crate) fn draw(&mut self, area: Area, state: &mut State, contextual_visibility: bool) { + if area.width >= 0. && area.height >= 0. { + self.drawable.draw(area, state, contextual_visibility); } } } -impl fmt::Debug for Drawable { +impl fmt::Debug for DrawableNode<'_, State> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Drawable") .field("area", &self.area) diff --git a/src/layout.rs b/src/layout.rs index bc42b96..b7098e5 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -1,9 +1,9 @@ use crate::{ - constraints::SizeConstraints, drawable::Drawable, models::*, node_cache::NodeCache, - traits::NodeTrait, Node, NodeWith, + constraints::SizeConstraints, drawable::DrawableNode, models::*, node_cache::NodeCache, + traits::NodeTrait, Node, }; use core::f32; -use std::{fmt::Debug, rc::Rc}; +use std::fmt::Debug; /** The root object used to store & calculate a layout @@ -16,168 +16,164 @@ use backer::*; use backer::models::*; use backer::nodes::*; -let layout = Layout::new(my_layout_fn); - // UI libraries generally will expose methods to get the available screen size // In a real implementation this should use the real screen size! let available_area = Area { - x: 0., - y: 0., - width: 100., - height: 100., - }; + x: 0., + y: 0., + width: 100., + height: 100., +}; + let mut my_state = MyState {}; -let layout = Layout::new(my_layout_fn); +let mut layout = Layout::new( + dynamic(|state: &mut MyState| { + // Your layout here + row(vec![ + space(), + ]) + }) +); + // Perform layout & draw all of your drawable nodes. layout.draw(available_area, &mut my_state); -fn my_layout_fn(state: &mut MyState) -> Node { - // Your layout here - row(vec![ - space(), - ]) -} struct MyState {} ``` */ -pub struct Layout { - tree: LayoutFn, -} - -pub type LayoutFn = Box NodeWith>; - -impl Layout { - /// Creates a new [`Layout`]. - pub fn new_with(tree: impl Fn(&mut State, &mut Ctx) -> NodeWith + 'static) -> Self { - Self { - tree: Box::new(tree), - } - } -} - -impl Layout { - /// Creates a new [`Layout`]. - pub fn new(tree: impl Fn(&mut State) -> Node + 'static) -> Self { - Self { - tree: Box::new(move |state, _| tree(state)), - } - } +pub struct Layout<'nodes, State> { + tree: Node<'nodes, State>, } -impl Layout { - /// Calculates layout and draws all draw nodes in the tree - pub fn draw(&self, area: Area, state: &mut State) { - let ctx = &mut (); - let mut layout = (self.tree)(state, ctx); - let constraints = layout.inner.constraints(area, state, ctx); - layout.inner.layout( - area.constrained(&constraints, XAlign::Center, YAlign::Center), - None, - None, - state, - ctx, - ); - layout.inner.draw(state, ctx); +impl<'nodes, State> Layout<'nodes, State> { + /// Creates a new [`Layout`]. + pub fn new(tree: Node<'nodes, State>) -> Self { + Self { tree } } } -impl Layout { +impl Layout<'_, State> { /// Calculates layout and draws all draw nodes in the tree - pub fn draw_with(&self, area: Area, state: &mut State, ctx: &mut Ctx) { - let mut layout = (self.tree)(state, ctx); - let constraints = layout.inner.constraints(area, state, ctx); - layout.inner.layout( - area.constrained(&constraints, XAlign::Center, YAlign::Center), + pub fn draw(&mut self, area: Area, state: &mut State) { + let constraints = self.tree.inner.constraints(area, state); + self.tree.inner.layout( + area.constrained( + &constraints.unwrap_or_default(), + XAlign::Center, + YAlign::Center, + ), None, None, state, - ctx, ); - layout.inner.draw(state, ctx); + self.tree.inner.draw(state, true); } } -type AreaReaderFn = Rc NodeWith>; +type AreaReaderFn<'nodes, State> = Box Node<'nodes, State> + 'nodes>; +type DynamicNodeFn<'nodes, State> = Box Node<'nodes, State> + 'nodes>; -pub(crate) enum NodeValue { +pub(crate) enum NodeValue<'nodes, State> { Padding { amounts: Padding, - element: Box>, + element: Box>, }, Column { - elements: Vec>, + elements: Vec>, spacing: f32, align: Option, off_axis_align: Option, }, Row { - elements: Vec>, + elements: Vec>, spacing: f32, align: Option, off_axis_align: Option, }, Stack { - elements: Vec>, + elements: Vec>, x_align: Option, y_align: Option, }, - Group(Vec>), + Group(Vec>), Offset { offset_x: f32, offset_y: f32, - element: Box>, + element: Box>, }, - Draw(Drawable), + Draw(DrawableNode<'nodes, State>), Explicit { - options: Size, - element: Box>, + options: Size, + element: Box>, }, Empty, Space, - Scope { - scoped: Box>, - }, AreaReader { - read: AreaReaderFn, + read: AreaReaderFn<'nodes, State>, }, Coupled { over: bool, - element: Box>, - coupled: Box>, + element: Box>, + coupled: Box>, + }, + Visibility { + visible: bool, + element: Box>, + }, + NodeTrait { + node: Box + 'nodes>, + }, + Dynamic { + node: DynamicNodeFn<'nodes, State>, + computed: Option>>, }, } -impl NodeValue { - pub(crate) fn draw(&mut self, state: &mut State, ctx: &mut Ctx) { +impl NodeValue<'_, State> { + pub(crate) fn draw(&mut self, state: &mut State, contextual_visibility: bool) { match self { - NodeValue::Draw(drawable) => drawable.draw(drawable.area, state, ctx), + NodeValue::Draw(drawable) => drawable.draw(drawable.area, state, contextual_visibility), NodeValue::Padding { element, .. } | NodeValue::Explicit { element, .. } | NodeValue::Offset { element, .. } => { - element.draw(state, ctx); + element.draw(state, contextual_visibility); } NodeValue::Stack { elements, .. } => { - elements.iter_mut().for_each(|el| el.draw(state, ctx)); + elements + .iter_mut() + .for_each(|el| el.draw(state, contextual_visibility)); } NodeValue::Column { elements, .. } | NodeValue::Row { elements, .. } => { - elements.iter_mut().rev().for_each(|el| el.draw(state, ctx)); + elements + .iter_mut() + .rev() + .for_each(|el| el.draw(state, contextual_visibility)); } NodeValue::Space => (), - NodeValue::Scope { scoped } => scoped.draw(state, ctx), NodeValue::Coupled { element, coupled, over, } => { if *over { - element.draw(state, ctx); - coupled.draw(state, ctx); + element.draw(state, contextual_visibility); + coupled.draw(state, contextual_visibility); } else { - coupled.draw(state, ctx); - element.draw(state, ctx); + coupled.draw(state, contextual_visibility); + element.draw(state, contextual_visibility); } } + Self::Visibility { element, visible } => { + element.draw(state, *visible && contextual_visibility) + } + Self::NodeTrait { node } => { + node.draw(state, contextual_visibility); + } + NodeValue::Dynamic { computed, .. } => computed + .as_mut() + .unwrap() + .draw(state, contextual_visibility), NodeValue::Group(_) | NodeValue::Empty | NodeValue::AreaReader { .. } => { unreachable!() } @@ -208,7 +204,6 @@ impl NodeValue { contextual_x_align: Option, contextual_y_align: Option, state: &mut State, - ctx: &mut Ctx, ) -> Vec { match self { NodeValue::Padding { amounts, .. } => vec![Area { @@ -230,7 +225,6 @@ impl NodeValue { off_axis_align.unwrap_or(XAlign::Center), align.unwrap_or(YAlign::Center), state, - ctx, true, ), NodeValue::Row { @@ -246,7 +240,6 @@ impl NodeValue { align.unwrap_or(XAlign::Center), off_axis_align.unwrap_or(YAlign::Center), state, - ctx, true, ), NodeValue::Stack { @@ -255,9 +248,10 @@ impl NodeValue { y_align, } => elements .iter_mut() - .map(|child| { + .filter_map(|element| element.constraints(available_area, state)) + .map(|constraints| { available_area.constrained( - &child.constraints(available_area, state, ctx), + &constraints, x_align.unwrap_or(XAlign::Center), y_align.unwrap_or(YAlign::Center), ) @@ -265,7 +259,7 @@ impl NodeValue { .collect(), NodeValue::Explicit { options, .. } => { vec![available_area.constrained( - &SizeConstraints::from_size(options.clone(), available_area, state, ctx), + &SizeConstraints::from_size(options.clone(), available_area, state), contextual_x_align.unwrap_or(XAlign::Center), contextual_y_align.unwrap_or(YAlign::Center), )] @@ -278,11 +272,15 @@ impl NodeValue { width: available_area.width, height: available_area.height, }], + NodeValue::Visibility { .. } => { + vec![available_area] + } NodeValue::Draw(_) | NodeValue::Space - | NodeValue::Scope { .. } | NodeValue::AreaReader { .. } - | NodeValue::Coupled { .. } => { + | NodeValue::Coupled { .. } + | NodeValue::NodeTrait { .. } + | NodeValue::Dynamic { .. } => { vec![available_area] } NodeValue::Group(_) | NodeValue::Empty => unreachable!(), @@ -295,7 +293,6 @@ impl NodeValue { contextual_x_align: Option, contextual_y_align: Option, state: &mut State, - ctx: &mut Ctx, ) { let contextual_aligns = self.contextual_aligns(); @@ -304,7 +301,6 @@ impl NodeValue { contextual_aligns.0.or(contextual_x_align), contextual_aligns.1.or(contextual_y_align), state, - ctx, ); match self { @@ -323,20 +319,18 @@ impl NodeValue { elements .iter_mut() .zip(allocated) - .for_each(|(el, allocation)| { - el.layout(allocation, *x_align, *y_align, state, ctx) - }); + .for_each(|(el, allocation)| el.layout(allocation, *x_align, *y_align, state)); } NodeValue::Stack { elements, .. } => { elements .iter_mut() .zip(allocated) - .for_each(|(el, allocation)| el.layout(allocation, None, None, state, ctx)); + .for_each(|(el, allocation)| el.layout(allocation, None, None, state)); } NodeValue::Padding { element, .. } | NodeValue::Explicit { element, .. } | NodeValue::Offset { element, .. } => { - element.layout(allocated[0], None, None, state, ctx); + element.layout(allocated[0], None, None, state); } NodeValue::Draw(drawable) => { drawable.area = allocated[0]; @@ -344,18 +338,36 @@ impl NodeValue { drawable.area.height = drawable.area.height.max(0.); } NodeValue::Space => (), - NodeValue::Scope { scoped } => { - scoped.layout(available_area, None, None, state, ctx); - } NodeValue::AreaReader { read } => { - *self = read(allocated[0], state, ctx).inner; - self.layout(allocated[0], None, None, state, ctx); + *self = read(allocated[0], state).inner; + self.layout(allocated[0], None, None, state); } NodeValue::Coupled { element, coupled, .. } => { - element.layout(allocated[0], None, None, state, ctx); - coupled.layout(allocated[0], None, None, state, ctx); + element.layout(allocated[0], None, None, state); + coupled.layout(allocated[0], None, None, state); + } + NodeValue::Visibility { element, .. } => { + element.layout(allocated[0], None, None, state); + } + NodeValue::NodeTrait { node } => { + node.layout( + available_area, + contextual_x_align, + contextual_y_align, + state, + ); + } + NodeValue::Dynamic { node, computed } => { + let mut node = NodeCache::new(node(state).inner); + node.layout( + available_area, + contextual_x_align, + contextual_y_align, + state, + ); + *computed = Some(Box::new(node)) } NodeValue::Group(_) | NodeValue::Empty => unreachable!(), } @@ -363,7 +375,7 @@ impl NodeValue { } impl Area { - fn constrained( + pub(crate) fn constrained( self, constraints: &SizeConstraints, contextual_x_align: XAlign, @@ -425,30 +437,30 @@ pub(crate) enum Orientation { } #[allow(clippy::too_many_arguments)] -pub(crate) fn layout_axis( - elements: &mut [NodeCache], +pub(crate) fn layout_axis( + elements: &mut [NodeCache<'_, State>], spacing: &f32, available_area: Area, orientation: Orientation, x_align: XAlign, y_align: YAlign, state: &mut State, - ctx: &mut Ctx, check: bool, ) -> Vec { - let sizes: Vec = elements + let element_count = elements.len(); + let sizes: Vec> = elements .iter_mut() - .map(|element| element.constraints(available_area, state, ctx)) + .map(|element| element.constraints(available_area, state)) .collect(); - let element_count = elements.len(); + let filtered_element_count = sizes.iter().filter_map(|&el| el).count(); - let total_spacing = *spacing * (element_count as i32 - 1).max(0) as f32; + let total_spacing = *spacing * (filtered_element_count as i32 - 1).max(0) as f32; let available_size = match orientation { Orientation::Horizontal => available_area.width, Orientation::Vertical => available_area.height, } - total_spacing; - let default_size = available_size / element_count as f32; + let default_size = available_size / filtered_element_count as f32; let mut pool = 0.; let mut final_sizes = vec![None; element_count]; @@ -456,61 +468,63 @@ pub(crate) fn layout_axis( let mut room_to_shrink = vec![0.0; element_count]; for (i, size_constraint) in sizes.iter().enumerate() { - let constraint = match orientation { - Orientation::Horizontal => size_constraint.width, - Orientation::Vertical => size_constraint.height, - }; - let mut final_size = Option::::None; - let mut lower = constraint.get_lower(); - let mut upper = constraint.get_upper(); - - if let Some(aspect) = size_constraint.aspect { - match orientation { - Orientation::Horizontal => { - let value = sizes[i].height.clamping(available_area.height) * aspect; - lower = Some(value); - upper = Some(value); - } - Orientation::Vertical => { - let value = sizes[i].width.clamping(available_area.width) / aspect; - lower = Some(value); - upper = Some(value); + if let Some(size_constraint) = size_constraint { + let constraint = match orientation { + Orientation::Horizontal => size_constraint.width, + Orientation::Vertical => size_constraint.height, + }; + let mut final_size = Option::::None; + let mut lower = constraint.get_lower(); + let mut upper = constraint.get_upper(); + + if let Some(aspect) = size_constraint.aspect { + match orientation { + Orientation::Horizontal => { + let value = size_constraint.height.clamping(available_area.height) * aspect; + lower = Some(value); + upper = Some(value); + } + Orientation::Vertical => { + let value = size_constraint.width.clamping(available_area.width) / aspect; + lower = Some(value); + upper = Some(value); + } } } - } - if let Some(lower) = lower { - if default_size < lower { - pool += default_size - lower; - final_size = Some(lower); + if let Some(lower) = lower { + if default_size < lower { + pool += default_size - lower; + final_size = Some(lower); + } } - } - if let Some(upper) = upper { - if default_size > upper { - pool += default_size - upper; - final_size = Some(upper); + if let Some(upper) = upper { + if default_size > upper { + pool += default_size - upper; + final_size = Some(upper); + } } - } - if let Some(lower) = lower { - if default_size >= lower { - room_to_shrink[i] = -(final_size.unwrap_or(default_size) - lower); + if let Some(lower) = lower { + if default_size >= lower { + room_to_shrink[i] = -(final_size.unwrap_or(default_size) - lower); + } + } else { + // Effectively, this means the element can shrink to 0 + room_to_shrink[i] = -default_size; } - } else { - // Effectively, this means the element can shrink to 0 - room_to_shrink[i] = -default_size; - } - if let Some(upper) = upper { - if default_size <= upper { - room_to_grow[i] = -(final_size.unwrap_or(default_size) - upper); + if let Some(upper) = upper { + if default_size <= upper { + room_to_grow[i] = -(final_size.unwrap_or(default_size) - upper); + } + } else { + // Effectively, this means the element can expand any amount + room_to_grow[i] = default_size * 10.; } - } else { - // Effectively, this means the element can expand any amount - room_to_grow[i] = default_size * 10.; - } - final_sizes[i] = final_size.unwrap_or(default_size).into(); + final_sizes[i] = final_size.unwrap_or(default_size).into(); + } } fn can_accommodate(room: &[f32]) -> bool { @@ -593,7 +607,14 @@ pub(crate) fn layout_axis( let mut areas = Vec::::new(); for (i, child) in elements.iter_mut().enumerate() { - let child_size = final_sizes[i].unwrap(); + let child_size = final_sizes[i].unwrap_or(if filtered_element_count > 1 { + 0. + } else { + match orientation { + Orientation::Horizontal => available_area.width, + Orientation::Vertical => available_area.height, + } + }); let area = match orientation { Orientation::Horizontal => Area { @@ -609,15 +630,17 @@ pub(crate) fn layout_axis( height: child_size, }, } - .constrained(&sizes[i], x_align, y_align); + .constrained(&sizes[i].unwrap_or_default(), x_align, y_align); if !check { - child.layout(area, Some(x_align), Some(y_align), state, ctx); + child.layout(area, Some(x_align), Some(y_align), state); } else { areas.push(area); } - current_pos += child_size + *spacing; + if sizes[i].is_some() { + current_pos += child_size + *spacing; + } } areas } diff --git a/src/lib.rs b/src/lib.rs index e59d7ae..cfc95c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,12 +20,13 @@ mod layout; mod modifiers; mod node; mod node_cache; +mod scoper; mod subtree; mod tests; pub use layout::Layout; pub use node::Node; -pub use node::NodeWith; +pub use scoper::{ScopeCtx, ScopeCtxResult}; /// Traits for layout definitions pub mod traits; diff --git a/src/models.rs b/src/models.rs index 2abe4a5..a1ca940 100644 --- a/src/models.rs +++ b/src/models.rs @@ -116,9 +116,9 @@ pub(crate) struct Padding { pub(crate) bottom: f32, } -type DimensionFn = Option f32>>; +type DimensionFn = Option f32>>; -pub(crate) struct Size { +pub(crate) struct Size { pub(crate) width_min: Option, pub(crate) width_max: Option, pub(crate) height_min: Option, @@ -126,13 +126,13 @@ pub(crate) struct Size { pub(crate) x_align: Option, pub(crate) y_align: Option, pub(crate) aspect: Option, - pub(crate) dynamic_height: DimensionFn, - pub(crate) dynamic_width: DimensionFn, + pub(crate) dynamic_height: DimensionFn, + pub(crate) dynamic_width: DimensionFn, pub(crate) expand_x: bool, pub(crate) expand_y: bool, } -impl Clone for Size { +impl Clone for Size { fn clone(&self) -> Self { Self { width_min: self.width_min, @@ -150,7 +150,7 @@ impl Clone for Size { } } -impl std::fmt::Debug for Size { +impl std::fmt::Debug for Size { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Size") .field("width_min", &self.width_min) @@ -168,13 +168,13 @@ impl std::fmt::Debug for Size { } } -impl Default for Size { +impl Default for Size { fn default() -> Self { Self::new() } } -impl Size { +impl Size { /// Creates a default size object to add constraints to pub(crate) fn new() -> Self { Size { diff --git a/src/modifiers.rs b/src/modifiers.rs index f1ff40a..47fc708 100644 --- a/src/modifiers.rs +++ b/src/modifiers.rs @@ -1,7 +1,7 @@ -use crate::{layout::NodeValue, models::*, node_cache::NodeCache, NodeWith}; +use crate::{layout::NodeValue, models::*, node_cache::NodeCache, Node}; use std::{ops::RangeBounds, rc::Rc}; -impl NodeWith { +impl Node<'_, State> { /// Constrains the node's height as a function of available width. /// /// Generally you should prefer size constraints, aspect ratio constraints or area readers over dynamic height. @@ -10,7 +10,7 @@ impl NodeWith { /// not a simple option. pub fn dynamic_height(self, f: impl Fn(f32, &mut State) -> f32 + 'static) -> Self { self.wrap_or_update_explicit(Size { - dynamic_height: Some(Rc::new(move |h, a, _| f(h, a))), + dynamic_height: Some(Rc::new(f)), ..Default::default() }) } @@ -22,46 +22,16 @@ impl NodeWith { /// not a simple option. pub fn dynamic_width(self, f: impl Fn(f32, &mut State) -> f32 + 'static) -> Self { self.wrap_or_update_explicit(Size { - dynamic_width: Some(Rc::new(move |h, a, _| f(h, a))), + dynamic_width: Some(Rc::new(f)), ..Default::default() }) } } -impl NodeWith { - /// Constrains the node's height as a function of available width. - /// - /// Generally you should prefer size constraints, aspect ratio constraints or area readers over dynamic height. - /// - /// **This is primarily for UI elements such as text** where node height must depend on available width & scaling is - /// not a simple option. - pub fn dynamic_height_with( - self, - f: impl Fn(f32, &mut State, &mut Ctx) -> f32 + 'static, - ) -> Self { - self.wrap_or_update_explicit(Size { - dynamic_height: Some(Rc::new(f)), - ..Default::default() - }) - } - /// Constrains the node's width as a function of available height. - /// - /// Generally you should prefer size constraints, aspect ratio constraints or area readers over dynamic height. - /// - /// **This is primarily for UI elements such as text** where node width must depend on available height & scaling is - /// not a simple option. - pub fn dynamic_width_with( - self, - f: impl Fn(f32, &mut State, &mut Ctx) -> f32 + 'static, - ) -> Self { - self.wrap_or_update_explicit(Size { - dynamic_width: Some(Rc::new(f)), - ..Default::default() - }) - } +impl<'nodes, State> Node<'nodes, State> { /// Adds padding to the node along the leading edge - pub fn pad_leading(self, amount: f32) -> NodeWith { - NodeWith { + pub fn pad_leading(self, amount: f32) -> Node<'nodes, State> { + Node { inner: NodeValue::Padding { amounts: Padding { leading: amount, @@ -74,8 +44,8 @@ impl NodeWith { } } /// Adds horizontal padding to the node (leading & trailing) - pub fn pad_x(self, amount: f32) -> NodeWith { - NodeWith { + pub fn pad_x(self, amount: f32) -> Node<'nodes, State> { + Node { inner: NodeValue::Padding { amounts: Padding { leading: amount, @@ -88,8 +58,8 @@ impl NodeWith { } } /// Adds padding to the node along the trailing edge - pub fn pad_trailing(self, amount: f32) -> NodeWith { - NodeWith { + pub fn pad_trailing(self, amount: f32) -> Node<'nodes, State> { + Node { inner: NodeValue::Padding { amounts: Padding { leading: 0., @@ -102,8 +72,8 @@ impl NodeWith { } } /// Adds padding to the node along the top edge - pub fn pad_top(self, amount: f32) -> NodeWith { - NodeWith { + pub fn pad_top(self, amount: f32) -> Node<'nodes, State> { + Node { inner: NodeValue::Padding { amounts: Padding { leading: 0., @@ -117,8 +87,8 @@ impl NodeWith { } /// Adds vertical padding to the node (top & bottom) - pub fn pad_y(self, amount: f32) -> NodeWith { - NodeWith { + pub fn pad_y(self, amount: f32) -> Node<'nodes, State> { + Node { inner: NodeValue::Padding { amounts: Padding { leading: 0., @@ -131,8 +101,8 @@ impl NodeWith { } } /// Adds padding to the node along the bottom edge - pub fn pad_bottom(self, amount: f32) -> NodeWith { - NodeWith { + pub fn pad_bottom(self, amount: f32) -> Node<'nodes, State> { + Node { inner: NodeValue::Padding { amounts: Padding { leading: 0., @@ -145,8 +115,8 @@ impl NodeWith { } } /// Adds padding to the node on all sides - pub fn pad(self, amount: f32) -> NodeWith { - NodeWith { + pub fn pad(self, amount: f32) -> Node<'nodes, State> { + Node { inner: NodeValue::Padding { amounts: Padding { leading: amount, @@ -161,8 +131,8 @@ impl NodeWith { /// Offsets the node along the x axis. /// This is an absolute offset that simply shifts nodes away from their calculated position /// This won't impact layout besides child nodes also being offset - pub fn offset_x(self, amount: f32) -> NodeWith { - NodeWith { + pub fn offset_x(self, amount: f32) -> Node<'nodes, State> { + Node { inner: NodeValue::Offset { offset_x: amount, offset_y: 0., @@ -173,8 +143,8 @@ impl NodeWith { /// Offsets the node along the y axis. /// This is an absolute offset that simply shifts nodes away from their calculated position /// This won't impact layout besides child nodes also being offset - pub fn offset_y(self, amount: f32) -> NodeWith { - NodeWith { + pub fn offset_y(self, amount: f32) -> Node<'nodes, State> { + Node { inner: NodeValue::Offset { offset_x: 0., offset_y: amount, @@ -185,8 +155,8 @@ impl NodeWith { /// Offsets the node along the x & y axis. /// This is an absolute offset that simply shifts nodes away from their calculated position /// This won't impact layout besides child nodes also being offset - pub fn offset(self, offset_x: f32, offset_y: f32) -> NodeWith { - NodeWith { + pub fn offset(self, offset_x: f32, offset_y: f32) -> Node<'nodes, State> { + Node { inner: NodeValue::Offset { offset_x, offset_y, @@ -261,7 +231,7 @@ impl NodeWith { /// the container must be constrained to be larger than the child - /// otherwise there is no wiggle room for alignment to take effect. /// - /// If you don't want a container to hug / shrink-wrap it's contents use [`NodeWith::expand()`] + /// If you don't want a container to hug / shrink-wrap it's contents use [`Node::expand()`] pub fn align(self, align: Align) -> Self { let (x, y) = align.axis_aligns(); match (x, y) { @@ -279,7 +249,7 @@ impl NodeWith { /// use backer::models::*; /// use backer::nodes::*; /// - /// row::<(), ()>(vec![ + /// row::<()>(vec![ /// draw(|a, _| { /// assert_eq!(a, Area::new(60., 0., 10., 100.)); /// }) @@ -390,7 +360,7 @@ impl NodeWith { /// The area available to the attached node is the size of the node it's attached to. /// Useful for adding an unconstrained node as an ornament, background, or overlay to a constrained node. pub fn attach_over(self, node: Self) -> Self { - NodeWith { + Node { inner: NodeValue::Coupled { over: true, element: Box::new(NodeCache::new(self.inner)), @@ -403,7 +373,7 @@ impl NodeWith { /// The area available to the attached node is the size of the node it's attached to. /// Useful for adding an unconstrained node as an ornament, background, or overlay to a constrained node. pub fn attach_under(self, node: Self) -> Self { - NodeWith { + Node { inner: NodeValue::Coupled { over: false, element: Box::new(NodeCache::new(self.inner)), @@ -411,7 +381,19 @@ impl NodeWith { }, } } - fn wrap_or_update_explicit(mut self, size: Size) -> Self { + /// Controls contextual visibility for this node & it's children. + /// + /// Can be used alongside `traits::drawable::Drawable::draw(...visible: bool)` + /// to customize visibility-based behavior + pub fn visible(self, visibility: bool) -> Self { + Node { + inner: NodeValue::Visibility { + visible: visibility, + element: Box::new(NodeCache::new(self.inner)), + }, + } + } + fn wrap_or_update_explicit(mut self, size: Size) -> Self { match self.inner { NodeValue::Explicit { ref mut options, @@ -450,7 +432,7 @@ impl NodeWith { }; } _ => { - return NodeWith { + return Node { inner: NodeValue::Explicit { options: size, element: Box::new(NodeCache::new(self.inner)), @@ -461,20 +443,3 @@ impl NodeWith { self } } - -#[cfg(test)] -mod tests { - use crate::models::*; - use crate::nodes::*; - - #[test] - fn test_explicit_wrap_valid() { - let c = space::<(), ()>() - .width(10.) - .width_range(5.0..) - .inner - .constraints(Area::zero(), &mut (), &mut ()); - assert!(c.width.get_upper().is_none()); - assert_eq!(c.width.get_lower().unwrap(), 5.); - } -} diff --git a/src/node.rs b/src/node.rs index b9103f8..26e2f41 100644 --- a/src/node.rs +++ b/src/node.rs @@ -4,14 +4,11 @@ use std::fmt::{Debug, Formatter}; use crate::layout::NodeValue; /// A layout tree node. Use methods in [`crate::nodes`] to create nodes. -pub type Node = NodeWith; - -/// A layout tree node. Use methods in [`crate::nodes`] to create nodes. -pub struct NodeWith { - pub(crate) inner: NodeValue, +pub struct Node<'nodes, State> { + pub(crate) inner: NodeValue<'nodes, State>, } -impl Debug for NodeWith { +impl Debug for Node<'_, State> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("NodeWith") .field("inner", &self.inner) diff --git a/src/node_cache.rs b/src/node_cache.rs index 6a8b6a3..3833113 100644 --- a/src/node_cache.rs +++ b/src/node_cache.rs @@ -4,17 +4,16 @@ use crate::{ constraints::SizeConstraints, layout::NodeValue, models::{Area, XAlign, YAlign}, - traits::NodeTrait, }; -pub(crate) struct NodeCache { - pub(crate) kind: NodeValue, - cache_area: Option, - cached_constraints: Option, +pub(crate) struct NodeCache<'nodes, State> { + pub(crate) kind: NodeValue<'nodes, State>, + pub(crate) cache_area: Option, + pub(crate) cached_constraints: Option, } -impl NodeCache { - pub(crate) fn new(kind: NodeValue) -> Self { +impl<'nodes, State> NodeCache<'nodes, State> { + pub(crate) fn new(kind: NodeValue<'nodes, State>) -> Self { Self { kind, cache_area: None, @@ -23,7 +22,7 @@ impl NodeCache { } } -impl Debug for NodeCache { +impl Debug for NodeCache<'_, State> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("NodeCache") .field("kind", &self.kind) @@ -33,40 +32,37 @@ impl Debug for NodeCache { } } -impl NodeTrait for NodeCache { - fn constraints( +impl NodeCache<'_, State> { + pub(crate) fn constraints( &mut self, available_area: Area, state: &mut State, - ctx: &mut Ctx, - ) -> SizeConstraints { + ) -> Option { if let (Some(cache), Some(constraints)) = (self.cache_area, self.cached_constraints) { if cache == available_area { - return constraints; + return Some(constraints); } } - let constraints = self.kind.constraints(available_area, state, ctx); + let constraints = self.kind.constraints(available_area, state); self.cache_area = Some(available_area); - self.cached_constraints = Some(constraints); + self.cached_constraints = constraints; constraints } - fn layout( + pub(crate) fn layout( &mut self, available_area: Area, contextual_x_align: Option, contextual_y_align: Option, state: &mut State, - ctx: &mut Ctx, ) { self.kind.layout( available_area, contextual_x_align, contextual_y_align, state, - ctx, - ) + ); } - fn draw(&mut self, state: &mut State, ctx: &mut Ctx) { - self.kind.draw(state, ctx) + pub(crate) fn draw(&mut self, state: &mut State, contextual_visibility: bool) { + self.kind.draw(state, contextual_visibility) } } diff --git a/src/nodes.rs b/src/nodes.rs index d01b5e5..09a6ecd 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -1,13 +1,12 @@ use crate::{ - drawable::Drawable, + drawable::{DrawableNode, SomeDrawable}, layout::NodeValue, models::*, node_cache::NodeCache, - subtree::Subtree, - traits::{ScopableOption, VoidScoper}, - Node, NodeWith, + scoper::{ScopeCtx, ScopeCtxResult, Scoper}, + traits::Drawable, + Node, }; -use std::{marker::PhantomData, rc::Rc}; macro_rules! container_doc { () => { @@ -26,8 +25,8 @@ or pushing against other unconstrained nodes with equal force. /// Creates a vertical sequence of elements /// #[doc = container_doc!()] -pub fn column(elements: Vec>) -> NodeWith { - NodeWith { +pub fn column(elements: Vec>) -> Node<'_, State> { + Node { inner: NodeValue::Column { elements: filter_empty(ungroup(elements)), spacing: 0., @@ -43,9 +42,8 @@ pub fn column(elements: Vec>) -> NodeWith(vec![ +/// column::<()>(vec![ /// empty(), /// group( /// (0..5) @@ -55,19 +53,16 @@ pub fn column(elements: Vec>) -> NodeWith(elements: Vec>) -> NodeWith { - NodeWith { +pub fn group(elements: Vec>) -> Node<'_, State> { + Node { inner: NodeValue::Group(filter_empty(ungroup(elements))), } } /// Creates a vertical sequence of elements with the specified spacing between each element. /// #[doc = container_doc!()] -pub fn column_spaced( - spacing: f32, - elements: Vec>, -) -> NodeWith { - NodeWith { +pub fn column_spaced(spacing: f32, elements: Vec>) -> Node<'_, State> { + Node { inner: NodeValue::Column { elements: filter_empty(ungroup(elements)), spacing, @@ -79,8 +74,8 @@ pub fn column_spaced( /// Creates a horizontal sequence of elements /// #[doc = container_doc!()] -pub fn row(elements: Vec>) -> NodeWith { - NodeWith { +pub fn row(elements: Vec>) -> Node<'_, State> { + Node { inner: NodeValue::Row { elements: filter_empty(ungroup(elements)), spacing: 0., @@ -92,11 +87,8 @@ pub fn row(elements: Vec>) -> NodeWith( - spacing: f32, - elements: Vec>, -) -> NodeWith { - NodeWith { +pub fn row_spaced(spacing: f32, elements: Vec>) -> Node<'_, State> { + Node { inner: NodeValue::Row { elements: filter_empty(ungroup(elements)), spacing, @@ -108,8 +100,8 @@ pub fn row_spaced( /// Creates a sequence of elements to be laid out on top of each other. /// #[doc = container_doc!()] -pub fn stack(elements: Vec>) -> NodeWith { - NodeWith { +pub fn stack(elements: Vec>) -> Node<'_, State> { + Node { inner: NodeValue::Stack { elements: filter_empty(ungroup(elements)), x_align: None, @@ -117,13 +109,13 @@ pub fn stack(elements: Vec>) -> NodeWith Node { @@ -134,128 +126,116 @@ pub fn stack(elements: Vec>) -> NodeWith(drawable: impl Fn(Area, &mut State) + 'static) -> Node { - NodeWith { - inner: NodeValue::Draw(Drawable { +pub fn draw<'nodes, State>( + drawable_fn: impl Fn(Area, &mut State) + 'static, +) -> Node<'nodes, State> { + Node { + inner: NodeValue::Draw(DrawableNode { area: Area::default(), - draw: Rc::new(move |area, a, _| drawable(area, a)), + drawable: SomeDrawable::Fn(Box::new(drawable_fn)), }), } } -/// Creates a node that can be drawn (see [`draw`]) -pub fn draw_with( - drawable: impl Fn(Area, &mut State, &mut Ctx) + 'static, -) -> NodeWith { - NodeWith { - inner: NodeValue::Draw(Drawable { +/// Creates a node that can be drawn using an object which implements the `Drawable` trait +/// (or the `TransitionDrawable` trait) +/// +/// See [`draw`] +pub fn draw_object<'nodes, State>( + drawable: impl Drawable<'nodes, State> + 'nodes, +) -> Node<'nodes, State> { + Node { + inner: NodeValue::Draw(DrawableNode { area: Area::default(), - draw: Rc::new(move |area, a, b| drawable(area, a, b)), + drawable: SomeDrawable::Object(Box::new(drawable)), }), } } + /// Creates an empty space which is laid out the same as any other node. /// /// To add spacing between each item in a row or column you can also use /// [`row_spaced`] & [`column_spaced`] -pub fn space() -> NodeWith { - NodeWith { +pub fn space<'nodes, State>() -> Node<'nodes, State> { + Node { inner: NodeValue::Space, } } /// Nothing! This will not have any impact on layout - useful for conditionally /// adding elements to a layout in the case where nothing should be added. -pub fn empty() -> NodeWith { - NodeWith { +pub fn empty<'nodes, State>() -> Node<'nodes, State> { + Node { inner: NodeValue::Empty, } } -/// Return nodes based on available area -/// -/// This node comes with caveats! Constraints within an area reader **cannot** expand the area reader itself. -/// If it could - it would create cyclical dependency which may be impossible to resolve. -pub fn area_reader( - func: impl Fn(Area, &mut State, &mut ()) -> Node + 'static, -) -> Node { - NodeWith { - inner: NodeValue::AreaReader { - read: Rc::new(func), - }, - } -} -/// Return nodes based on available area +/// Returns nodes based on available area /// /// This node comes with caveats! Constraints within an area reader **cannot** expand the area reader itself. /// If it could - it would create cyclical dependency which may be impossible to resolve. -pub fn area_reader_with( - func: impl Fn(Area, &mut State, &mut Ctx) -> NodeWith + 'static, -) -> NodeWith { - NodeWith { +pub fn area_reader<'nodes, State>( + func: impl Fn(Area, &mut State) -> Node<'nodes, State> + 'static, +) -> Node<'nodes, State> { + Node { inner: NodeValue::AreaReader { - read: Rc::new(func), + read: Box::new(func), }, } } -/// Narrows or scopes the mutable state available to the children of this node -/// The `StateScoper` generic must implement [`Scopable`] or [`ScopableOption`]. -/// -/// This function usually needs explicit generic arguments `scope::<_, _, Scoper>(my_scoped_layout)` -/// See [`Scopable`] for more documentation. -/// -/// The children of this node will only have access to the scoped state and context. -pub fn scope( - node: impl Fn(&mut ScopedState) -> Node + 'static, -) -> Node -where - ScopedState: 'static, - State: 'static, - StateScoper: ScopableOption + 'static, -{ - NodeWith { - inner: NodeValue::Scope { - scoped: Box::new(Subtree { - subtree_fn: Box::new(move |state, _| node(state)), - stored_tree: None, - _p: PhantomData, - _c: PhantomData, - _cs: PhantomData::, - _ss: PhantomData::, - }), +/// Returns a dynamic set of nodes based on state +pub fn dynamic<'nodes, State: 'nodes>( + func: impl Fn(&'_ mut State) -> Node<'nodes, State> + 'nodes, +) -> Node<'nodes, State> { + Node { + inner: NodeValue::Dynamic { + node: Box::new(func), + computed: None, }, } } -/// Narrows or scopes the mutable state available to the children of this node -/// The `StateScoper` & CtxScoper generics both must implement [`Scopable`] or [`ScopableOption`]. +/// Scopes state to some derived subset for all children of this node /// -/// This function usually needs explicit generic arguments `scope::<_, _, _, _, MyStateScoper, MyCtxScoper>(my_scoped_layout)` -/// See [`Scopable`] for more documentation. +///```rust +/// use backer::*; +/// use backer::models::*; +/// use backer::nodes::*; /// -/// The children of this node will only have access to the scoped state and context. -pub fn scope_with( - node: impl Fn(&mut ScopedState, &mut ScopedCtx) -> NodeWith + 'static, -) -> NodeWith -where - ScopedState: 'static, - State: 'static, - ScopedCtx: 'static, - Ctx: 'static, - StateScoper: ScopableOption + 'static, - CtxScoper: ScopableOption + 'static, -{ - NodeWith { - inner: NodeValue::Scope { - scoped: Box::new(Subtree { - subtree_fn: Box::new(node), - stored_tree: None, - _p: PhantomData, - _c: PhantomData, - _cs: PhantomData::, - _ss: PhantomData::, +/// struct A { +/// b: Option, +/// } +/// let layout = dynamic(|_: &mut A| { +/// stack(vec![ +/// scope( +/// // Explicit types are often necessary. +/// // bool is the type of the subset in this case +/// |ctx: ScopeCtx, a: &mut A| { +/// // This closure transforms state into the desired subset. +/// // The desired subset is passed to ctx.with_scoped(...) +/// // or the entire hierarchy can be skipped with ctx.empty() +/// let Some(ref mut b) = a.b else { +/// return ctx.empty(); +/// }; +/// ctx.with_scoped(b) +/// }, +/// // These nodes now have direct access to only the boolean +/// draw(|_, b: &mut bool| *b = !*b), +/// ), +/// ]) +/// }); +///``` +pub fn scope<'nodes, State, Scoped: 'nodes>( + scope: impl Fn(ScopeCtx<'_, '_, Scoped>, &mut State) -> ScopeCtxResult + 'nodes, + node: Node<'nodes, Scoped>, +) -> Node<'nodes, State> { + Node { + inner: NodeValue::NodeTrait { + node: Box::new(Scoper { + scope_fn: scope, + node, }), }, } } -fn ungroup(elements: Vec>) -> Vec> { +fn ungroup(elements: Vec>) -> Vec> { elements .into_iter() .flat_map(|el| { @@ -271,7 +251,7 @@ fn ungroup(elements: Vec>) -> Vec(elements: Vec>) -> Vec> { +fn filter_empty(elements: Vec>) -> Vec> { elements .into_iter() .filter(|el| { diff --git a/src/scoper.rs b/src/scoper.rs new file mode 100644 index 0000000..1f16d99 --- /dev/null +++ b/src/scoper.rs @@ -0,0 +1,169 @@ +use std::fmt::Debug; + +use crate::{ + constraints::SizeConstraints, + models::{Area, XAlign, YAlign}, + traits::NodeTrait, + Node, +}; + +pub(crate) struct Scoper<'n, SubState, ScopeStateFn> { + pub(crate) scope_fn: ScopeStateFn, + pub(crate) node: Node<'n, SubState>, +} + +/// Anonymous result to return from the closure passed to `nodes::scope` +/// See `nodes::scope` +pub struct ScopeCtxResult { + value: ResultValue, +} + +enum ResultValue { + Void, + Constraints(Option), +} + +impl Debug for Scoper<'_, SubState, ScopeStateFn> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Scoper") + .field("scope_fn", &"") + .field("node", &self.node) + .finish() + } +} + +type WithScopedFnPointer = fn( + area: Area, + contextual_x_align: Option, + contextual_y_align: Option, + contextual_visibility: bool, + &mut Node, + &mut SubState, +) -> ResultValue; + +/// Contextual state for scoping, see `nodes::scope` +pub struct ScopeCtx<'a, 'nodes, SubState> { + node: &'a mut Node<'nodes, SubState>, + area: Area, + contextual_x_align: Option, + contextual_y_align: Option, + contextual_visibility: bool, + with_scoped: WithScopedFnPointer, +} + +impl ScopeCtx<'_, '_, SubState> { + /// Takes the subset of state being scoped to, returns an anonymous result to be returned from the closure passed into `nodes::scope` + pub fn with_scoped(self, scoped: &mut SubState) -> ScopeCtxResult { + ScopeCtxResult { + value: (self.with_scoped)( + self.area, + self.contextual_x_align, + self.contextual_y_align, + self.contextual_visibility, + self.node, + scoped, + ), + } + } + /// Used when scoping to a state that is potentially "invalid", such as when scoping to an `Option` in a way that will unwrap the state for the child nodes + /// Returns an anonymous result to be returned from the closure passed into `nodes::scope` + pub fn empty(self) -> ScopeCtxResult { + ScopeCtxResult { + value: ResultValue::Void, + } + } +} + +impl<'nodes, State, SubState, ScopeStateFn> NodeTrait + for Scoper<'nodes, SubState, ScopeStateFn> +where + ScopeStateFn: Fn(ScopeCtx<'_, 'nodes, SubState>, &mut State) -> ScopeCtxResult, +{ + fn constraints(&mut self, available_area: Area, state: &mut State) -> Option { + let ScopeCtxResult { + value: ResultValue::Constraints(constraints), + } = (self.scope_fn)( + ScopeCtx { + node: &mut self.node, + area: available_area, + contextual_x_align: None, + contextual_y_align: None, + contextual_visibility: false, + with_scoped: |area: Area, + _contextual_x_align: Option, + _contextual_y_align: Option, + _contextual_visibility: bool, + node: &mut Node, + sc: &mut SubState| { + ResultValue::Constraints(node.inner.constraints(area, sc)) + }, + }, + state, + ) + else { + return None; + }; + constraints + } + + fn layout( + &mut self, + available_area: Area, + contextual_x_align: Option, + contextual_y_align: Option, + state: &mut State, + ) { + let ScopeCtxResult { + value: ResultValue::Void, + } = (self.scope_fn)( + ScopeCtx { + node: &mut self.node, + area: available_area, + contextual_x_align, + contextual_y_align, + contextual_visibility: false, + with_scoped: |available_area: Area, + contextual_x_align: Option, + contextual_y_align: Option, + _contextual_visibility: bool, + node: &mut Node, + sc: &mut SubState| { + node.inner + .layout(available_area, contextual_x_align, contextual_y_align, sc); + ResultValue::Void + }, + }, + state, + ) + else { + return; + }; + } + + fn draw(&mut self, state: &mut State, contextual_visibility: bool) { + let ScopeCtxResult { + value: ResultValue::Void, + } = (self.scope_fn)( + ScopeCtx { + node: &mut self.node, + area: Area::zero(), + contextual_x_align: None, + contextual_y_align: None, + contextual_visibility, + with_scoped: |_available_area: Area, + _contextual_x_align: Option, + _contextual_y_align: Option, + contextual_visibility: bool, + node: &mut Node, + sc: &mut SubState| { + node.inner.draw(sc, contextual_visibility); + ResultValue::Void + }, + }, + state, + ) + else { + return; + }; + } +} diff --git a/src/subtree.rs b/src/subtree.rs index d916a88..8b13789 100644 --- a/src/subtree.rs +++ b/src/subtree.rs @@ -1,122 +1 @@ -use core::fmt; -use std::{ - fmt::{Debug, Formatter}, - marker::PhantomData, -}; -use crate::{ - models::{Area, XAlign, YAlign}, - traits::{NodeTrait, ScopableOption}, - NodeWith, -}; - -type SubtreeFn = - Box NodeWith>; - -pub(crate) struct Subtree< - SubState, - SubCtx, - State, - Ctx, - StateScoper: ScopableOption, - CtxScoper: ScopableOption, -> { - pub(crate) subtree_fn: SubtreeFn, - pub(crate) stored_tree: Option>, - pub(crate) _p: PhantomData, - pub(crate) _c: PhantomData, - pub(crate) _ss: PhantomData, - pub(crate) _cs: PhantomData, -} - -impl< - SubState, - SubCtx, - State, - Ctx, - StateScoper: ScopableOption, - CtxScoper: ScopableOption, - > Debug for Subtree -{ - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("Subtree") - .field("subtree_fn", &"") - .field("stored_tree", &self.stored_tree) - .finish() - } -} - -impl NodeTrait - for Subtree -where - StateScoper: ScopableOption, - CtxScoper: ScopableOption, -{ - fn draw(&mut self, state: &mut State, ctx: &mut Ctx) { - StateScoper::scope_option(state, |state| { - CtxScoper::scope_option(ctx, |ctx| { - let (Some(state), Some(ctx)) = (state, ctx) else { - return None::<()>; - }; - let mut subtree = self - .stored_tree - .take() - .unwrap_or((self.subtree_fn)(state, ctx)); - subtree.inner.draw(state, ctx); - self.stored_tree = Some(subtree); - None::<()> - }) - }); - } - fn layout( - &mut self, - available_area: Area, - contextual_x_align: Option, - contextual_y_align: Option, - state: &mut State, - ctx: &mut Ctx, - ) { - StateScoper::scope_option(state, |state| { - CtxScoper::scope_option(ctx, |ctx| { - let (Some(state), Some(ctx)) = (state, ctx) else { - return None::<()>; - }; - let mut subtree = self - .stored_tree - .take() - .unwrap_or((self.subtree_fn)(state, ctx)); - subtree.inner.layout( - available_area, - contextual_x_align, - contextual_y_align, - state, - ctx, - ); - self.stored_tree = Some(subtree); - None::<()> - }) - }); - } - fn constraints( - &mut self, - area: Area, - state: &mut State, - ctx: &mut Ctx, - ) -> crate::constraints::SizeConstraints { - StateScoper::scope_option(state, |state| { - CtxScoper::scope_option(ctx, |ctx| { - let (Some(state), Some(ctx)) = (state, ctx) else { - return None; - }; - let mut subtree = self - .stored_tree - .take() - .unwrap_or((self.subtree_fn)(state, ctx)); - let result = subtree.inner.constraints(area, state, ctx); - self.stored_tree = Some(subtree); - Some(result) - }) - }) - .unwrap_or_default() - } -} diff --git a/src/tests/attach_tests.rs b/src/tests/attach_tests.rs index 8bb022e..0ce0d9a 100644 --- a/src/tests/attach_tests.rs +++ b/src/tests/attach_tests.rs @@ -5,29 +5,29 @@ mod tests { use crate::nodes::*; #[test] fn test_attach() { - Layout::new(|()| { + Layout::new({ stack(vec![ //> - draw(|a, _| { + draw(move |a, _: &mut ()| { assert_eq!(a, Area::new(40., 40., 20., 20.)); }) .width(20.) .height(20.) .pad(10.) - .attach_under(draw(|a, _| { + .attach_under(draw(|a, _: &mut ()| { assert_eq!(a, Area::new(30., 30., 40., 40.)); })), ]) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { - stack(vec![draw(|a, _| { + Layout::new({ + stack(vec![draw(|a, _: &mut ()| { assert_eq!(a, Area::new(40., 40., 20., 20.)); }) .width(20.) .height(20.) .pad(10.) - .attach_over(draw(|a, _| { + .attach_over(draw(|a, _: &mut ()| { assert_eq!(a, Area::new(30., 30., 40., 40.)); }))]) }) diff --git a/src/tests/dynamic_tests.rs b/src/tests/dynamic_tests.rs index 8467d37..49e649d 100644 --- a/src/tests/dynamic_tests.rs +++ b/src/tests/dynamic_tests.rs @@ -5,13 +5,13 @@ mod tests { use crate::nodes::*; #[test] fn test_simple() { - Layout::new(|()| { + Layout::new({ column(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 100., 50.)); }) .dynamic_height(|w, _| w * 0.5), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 50., 100., 50.)); }), ]) @@ -20,26 +20,26 @@ mod tests { } #[test] fn test_nested() { - Layout::new(|()| { + Layout::new({ column(vec![ - row(vec![draw(|a, _| { + row(vec![draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 20., 100., 50.)); }) .dynamic_height(|w, _| w * 0.5)]), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 70., 100., 10.)); }) .height(10.), ]) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ column(vec![ - row(vec![draw(|a, _| { + row(vec![draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., -5., 100., 50.)); }) .dynamic_height(|w, _| w * 0.5)]), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 45., 100., 60.)); }) .height(60.), diff --git a/src/tests/layout_tests.rs b/src/tests/layout_tests.rs index 147fd1c..0d5f10c 100644 --- a/src/tests/layout_tests.rs +++ b/src/tests/layout_tests.rs @@ -6,13 +6,13 @@ mod tests { #[test] fn test_seq_align_on_axis() { - Layout::new(|()| { + Layout::new({ row(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 10., 100.)); }) .width(10.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(10., 0., 30., 100.)); }) .width(30.), @@ -21,13 +21,13 @@ mod tests { .expand() }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ row(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(30., 0., 10., 100.)); }) .width(10.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(40., 0., 30., 100.)); }) .width(30.), @@ -35,13 +35,13 @@ mod tests { .align(Align::CenterX) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ row(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(60., 0., 10., 100.)); }) .width(10.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(70., 0., 30., 100.)); }) .width(30.), @@ -50,13 +50,13 @@ mod tests { .expand() }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ column(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 100., 10.)); }) .height(10.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 10., 100., 30.)); }) .height(30.), @@ -65,13 +65,13 @@ mod tests { .expand() }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ column(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 30., 100., 10.)); }) .height(10.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 40., 100., 30.)); }) .height(30.), @@ -79,13 +79,13 @@ mod tests { .align(Align::CenterY) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ column(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 60., 100., 10.)); }) .height(10.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 70., 100., 30.)); }) .height(30.), @@ -97,13 +97,13 @@ mod tests { } #[test] fn test_seq_align_off_axis() { - Layout::new(|()| { + Layout::new({ column(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 10., 50.)); }) .width(10.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 50., 30., 50.)); }) .width(30.), @@ -112,13 +112,13 @@ mod tests { .expand() }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ column(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(45., 0., 10., 50.)); }) .width(10.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(35., 50., 30., 50.)); }) .width(30.), @@ -126,13 +126,13 @@ mod tests { .align(Align::CenterX) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ column(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(90., 0., 10., 50.)); }) .width(10.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(70., 50., 30., 50.)); }) .width(30.), @@ -141,13 +141,13 @@ mod tests { .expand() }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ row(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 50., 10.)); }) .height(10.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(50., 0., 50., 30.)); }) .height(30.), @@ -156,13 +156,13 @@ mod tests { .expand() }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ row(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 45., 50., 10.)); }) .height(10.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(50., 35., 50., 30.)); }) .height(30.), @@ -170,13 +170,13 @@ mod tests { .align(Align::CenterY) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ row(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 90., 50., 10.)); }) .height(10.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(50., 70., 50., 30.)); }) .height(30.), @@ -188,13 +188,13 @@ mod tests { } #[test] fn test_seq_align_on_axis_nested_seq() { - Layout::new(|()| { + Layout::new({ row(vec![ - row(vec![draw(|a, _| { + row(vec![draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 10., 100.)); }) .width(10.)]), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(10., 0., 30., 100.)); }) .width(30.), @@ -203,13 +203,13 @@ mod tests { .expand() }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ row(vec![ - row(vec![draw(|a, _| { + row(vec![draw(|a, _: &mut ()| { assert_eq!(a, Area::new(30., 0., 10., 100.)); }) .width(10.)]), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(40., 0., 30., 100.)); }) .width(30.), @@ -217,13 +217,13 @@ mod tests { .align(Align::CenterX) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ row(vec![ - row(vec![draw(|a, _| { + row(vec![draw(|a, _: &mut ()| { assert_eq!(a, Area::new(60., 0., 10., 100.)); }) .width(10.)]), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(70., 0., 30., 100.)); }) .width(30.), @@ -232,13 +232,13 @@ mod tests { .expand() }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ column(vec![ - row(vec![draw(|a, _| { + row(vec![draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 100., 10.)); }) .height(10.)]), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 10., 100., 30.)); }) .height(30.), @@ -247,13 +247,13 @@ mod tests { .expand() }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ column(vec![ - row(vec![draw(|a, _| { + row(vec![draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 30., 100., 10.)); }) .height(10.)]), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 40., 100., 30.)); }) .height(30.), @@ -261,13 +261,13 @@ mod tests { .align(Align::CenterY) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ column(vec![ - row(vec![draw(|a, _| { + row(vec![draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 60., 100., 10.)); }) .height(10.)]), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 70., 100., 30.)); }) .height(30.), @@ -279,13 +279,13 @@ mod tests { } #[test] fn test_seq_align_off_axis_nested_seq() { - Layout::new(|()| { + Layout::new({ column(vec![ - row(vec![draw(|a, _| { + row(vec![draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 10., 50.)); }) .width(10.)]), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 50., 30., 50.)); }) .width(30.), @@ -294,13 +294,13 @@ mod tests { .expand() }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ column(vec![ - row(vec![draw(|a, _| { + row(vec![draw(|a, _: &mut ()| { assert_eq!(a, Area::new(45., 0., 10., 50.)); }) .width(10.)]), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(35., 50., 30., 50.)); }) .width(30.), @@ -308,13 +308,13 @@ mod tests { .align(Align::CenterX) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ column(vec![ - row(vec![draw(|a, _| { + row(vec![draw(|a, _: &mut ()| { assert_eq!(a, Area::new(90., 0., 10., 50.)); }) .width(10.)]), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(70., 50., 30., 50.)); }) .width(30.), @@ -323,13 +323,13 @@ mod tests { .expand() }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ row(vec![ - row(vec![draw(|a, _| { + row(vec![draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 50., 10.)); }) .height(10.)]), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(50., 0., 50., 30.)); }) .height(30.), @@ -338,13 +338,13 @@ mod tests { .expand() }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ row(vec![ - row(vec![draw(|a, _| { + row(vec![draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 45., 50., 10.)); }) .height(10.)]), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(50., 35., 50., 30.)); }) .height(30.), @@ -352,13 +352,13 @@ mod tests { .align(Align::CenterY) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ row(vec![ - row(vec![draw(|a, _| { + row(vec![draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 90., 50., 10.)); }) .height(10.)]), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(50., 70., 50., 30.)); }) .height(30.), @@ -370,30 +370,30 @@ mod tests { } #[test] fn test_aspect_ratio() { - Layout::new(|()| { - draw(|a, _| { + Layout::new({ + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 100., 100.)); }) .aspect(1.) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { - draw(|a, _| { + Layout::new({ + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(25., 0., 50., 100.)); }) .aspect(0.5) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { - draw(|a, _| { + Layout::new({ + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 50., 100.)); }) .aspect(0.5) .align(Align::Leading) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { - draw(|a, _| { + Layout::new({ + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(50., 0., 50., 100.)); }) .aspect(0.5) @@ -401,23 +401,23 @@ mod tests { }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { - draw(|a, _| { + Layout::new({ + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 25., 100., 50.)); }) .aspect(2.) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { - draw(|a, _| { + Layout::new({ + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 100., 50.)); }) .aspect(2.) .align(Align::Top) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { - draw(|a, _| { + Layout::new({ + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 50., 100., 50.)); }) .aspect(2.) @@ -427,22 +427,22 @@ mod tests { } #[test] fn test_aspect_ratio_in_seq() { - Layout::new(|()| { - row(vec![draw(|a, _| { + Layout::new({ + row(vec![draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 100., 100.)); }) .aspect(1.)]) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { - stack(vec![draw(|a, _| { + Layout::new({ + stack(vec![draw(|a, _: &mut ()| { assert_eq!(a, Area::new(25., 0., 50., 100.)); }) .aspect(0.5)]) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { - column(vec![draw(|a, _| { + Layout::new({ + column(vec![draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., -50., 100., 200.)); }) .aspect(0.5) @@ -450,8 +450,8 @@ mod tests { .expand() }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { - stack(vec![draw(|a, _| { + Layout::new({ + stack(vec![draw(|a, _: &mut ()| { assert_eq!(a, Area::new(50., 0., 50., 100.)); }) .aspect(0.5) @@ -462,50 +462,50 @@ mod tests { } #[test] fn test_pad() { - Layout::new(|()| { - draw(|a, _| { + Layout::new({ + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(10., 10., 80., 80.)); }) .pad(10.) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { - draw(|a, _| { + Layout::new({ + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(10., 0., 80., 100.)); }) .pad_x(10.) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { - draw(|a, _| { + Layout::new({ + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 10., 100., 80.)); }) .pad_y(10.) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { - draw(|a, _| { + Layout::new({ + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(10., 0., 90., 100.)); }) .pad_leading(10.) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { - draw(|a, _| { + Layout::new({ + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 90., 100.)); }) .pad_trailing(10.) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { - draw(|a, _| { + Layout::new({ + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 10., 100., 90.)); }) .pad_top(10.) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { - draw(|a, _| { + Layout::new({ + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 100., 90.)); }) .pad_bottom(10.) @@ -514,15 +514,15 @@ mod tests { } #[test] fn test_aspect_ratio_in_pad() { - Layout::new(|()| { - draw(|a, _| { + Layout::new({ + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(25., 0., 50., 100.)); }) .aspect(0.5) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { - stack(vec![draw(|a, _| { + Layout::new({ + stack(vec![draw(|a, _: &mut ()| { // 0.5 aspect ratio // padded size // 10., 10., 80., 80. @@ -535,8 +535,8 @@ mod tests { .pad(10.)]) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { - stack(vec![draw(|a, _| { + Layout::new({ + stack(vec![draw(|a, _: &mut ()| { // 0.5 aspect ratio // aspect constrained size // 25., 0., 50., 100. @@ -552,18 +552,18 @@ mod tests { #[test] fn test_space_expansion() { // The unconstrained space node should expand an unlimited amount - Layout::new(|()| { + Layout::new({ row(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 1., 100.)); }) .width(1.), space(), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(998., 0., 1., 100.)); }) .width(1.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(999., 0., 1., 100.)); }) .width(1.), @@ -573,16 +573,16 @@ mod tests { } #[test] fn test_explicit_aspect() { - Layout::new(|()| { + Layout::new({ column_spaced( 10., vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(45., 0., 10., 20.)); }) .width(10.) .aspect(0.5), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 30., 100., 70.)); }), ], @@ -592,14 +592,14 @@ mod tests { } #[test] fn test_explicit_with_padding() { - Layout::new(|()| { + Layout::new({ column(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(10., 10., 80., 20.)); }) .height(20.) .pad(10.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 40., 100., 60.)); }), ]) @@ -608,13 +608,13 @@ mod tests { } #[test] fn test_explicit_in_explicit() { - Layout::new(|()| { - draw(|a, _| { + Layout::new({ + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(40., 0., 20., 100.)); }) .width_range(20.0..) .pad(0.) - .attach_under(draw(|a, _| { + .attach_under(draw(|a, _: &mut ()| { assert_eq!(a, Area::new(45., 0., 10., 100.)); })) .width_range(..10.) @@ -623,26 +623,26 @@ mod tests { } #[test] fn test_compressed_expanded_respects_lower_bound() { - Layout::new(|()| { + Layout::new({ stack(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., -50., 100., 200.)); }) .height(200.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., -50., 100., 200.)); }), ]) .expand() }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ column(vec![stack(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., -50., 100., 200.)); }) .height(200.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., -50., 100., 200.)); }), ]) diff --git a/src/tests/public_api_test.rs b/src/tests/public_api_test.rs index 38678d5..f8f2e00 100644 --- a/src/tests/public_api_test.rs +++ b/src/tests/public_api_test.rs @@ -11,10 +11,18 @@ fn public_api() { .unwrap(); // Derive the public API from the rustdoc JSON - let public_api = public_api::Builder::from_rustdoc_json(rustdoc_json) + let public_api = public_api::Builder::from_rustdoc_json(rustdoc_json.clone()) + .build() + .unwrap(); + + let public_api_simplified = public_api::Builder::from_rustdoc_json(rustdoc_json) + .omit_blanket_impls(true) + .omit_auto_trait_impls(true) + .omit_auto_derived_impls(true) .build() .unwrap(); // Assert that the public API looks correct - insta::assert_snapshot!(public_api); + insta::assert_snapshot!("api_full", public_api); + insta::assert_snapshot!("api_simplified", public_api_simplified); } diff --git a/src/tests/scope_tests.rs b/src/tests/scope_tests.rs index 92c81a5..4d428ef 100644 --- a/src/tests/scope_tests.rs +++ b/src/tests/scope_tests.rs @@ -1,19 +1,19 @@ #[cfg(test)] mod tests { + use crate::layout::*; use crate::models::*; use crate::nodes::*; - use crate::traits::NoOpScoper; - use crate::traits::Scopable; - use crate::traits::ScopableOption; - use crate::Node; - use crate::NodeWith; + use crate::scoper::ScopeCtx; + #[test] fn test_scope() { + #[derive(Debug)] struct A { test: bool, b: B, } + #[derive(Debug)] struct B { test: bool, } @@ -21,75 +21,48 @@ mod tests { test: true, b: B { test: true }, }; - struct AToBScoper; - impl Scopable for AToBScoper { - fn scope(scoping: &mut A, f: impl FnOnce(&mut B) -> Result) -> Result { - f(&mut scoping.b) - } - } - fn layout(a: &mut A) -> NodeWith { + let layout = dynamic(|a: &mut A| { stack(vec![ - if a.test { - draw(|area, a: &mut A| { - assert_eq!(area, Area::new(0., 0., 100., 100.)); - a.test = false; - }) - } else { - draw(|area, a: &mut A| { - assert_eq!(area, Area::new(0., 0., 100., 100.)); - a.test = true; - }) - }, - scope::<_, _, AToBScoper>(|b: &mut B| { - if b.test { - draw(|area, b: &mut B| { + { + if a.test { + draw(|area, a: &mut A| { assert_eq!(area, Area::new(0., 0., 100., 100.)); - b.test = false; + a.test = false; }) } else { - draw(|area, b: &mut B| { + draw(|area, a: &mut A| { assert_eq!(area, Area::new(0., 0., 100., 100.)); - b.test = true; + a.test = true; }) } - }), + }, + scope( + |ctx: ScopeCtx, a: &mut A| ctx.with_scoped(&mut a.b), + dynamic(|b: &mut B| { + if b.test { + draw(|area, b: &mut B| { + assert_eq!(area, Area::new(0., 0., 100., 100.)); + b.test = false; + }) + } else { + draw(|area, b: &mut B| { + assert_eq!(area, Area::new(0., 0., 100., 100.)); + b.test = true; + }) + } + }), + ), ]) - } - Layout::new(layout).draw(Area::new(0., 0., 100., 100.), &mut a); + }); + let mut layout = Layout::new(layout); + layout.draw(Area::new(0., 0., 100., 100.), &mut a); assert!(!a.test); assert!(!a.b.test); - Layout::new(layout).draw(Area::new(0., 0., 100., 100.), &mut a); + layout.draw(Area::new(0., 0., 100., 100.), &mut a); assert!(a.test); assert!(a.b.test); } - #[test] - fn test_partial_scope_variadic() { - struct A; - struct C; - struct B { - c: C, - } - struct BToCScoper; - impl Scopable for BToCScoper { - fn scope(scoping: &mut B, f: impl FnOnce(&mut C) -> Result) -> Result { - f(&mut scoping.c) - } - } - fn layout(_: &mut A, _: &mut B) -> NodeWith { - stack(vec![ - draw_with(|area, _, _| { - assert_eq!(area, Area::new(0., 0., 100., 100.)); - }), - scope_with::<_, _, _, _, NoOpScoper, BToCScoper>(|_, _| { - draw_with(|area, _a: &mut A, _c: &mut C| { - assert_eq!(area, Area::new(0., 0., 100., 100.)); - }) - }), - ]) - } - Layout::new_with(layout).draw_with(Area::new(0., 0., 100., 100.), &mut A, &mut B { c: C }); - } #[test] fn test_multiple_scope_paths() { struct C; @@ -98,79 +71,84 @@ mod tests { b: B, c: C, } - fn layout(a: &mut A) -> Node { - stack(vec![path_b(a), path_c(a)]) - } - struct AToBScoper; - impl Scopable for AToBScoper { - fn scope(scoping: &mut A, f: impl FnOnce(&mut B) -> Result) -> Result { - f(&mut scoping.b) - } - } - struct AToCScoper; - impl Scopable for AToCScoper { - fn scope(scoping: &mut A, f: impl FnOnce(&mut C) -> Result) -> Result { - f(&mut scoping.c) - } - } - fn path_b(_: &mut A) -> Node { - stack(vec![scope::<_, _, AToBScoper>(|_b: &mut B| space())]) - } - fn path_c(_: &mut A) -> Node { - stack(vec![scope::<_, _, AToCScoper>(|_c: &mut C| space())]) - } - Layout::new(layout).draw(Area::new(0., 0., 100., 100.), &mut A { b: B, c: C }); + Layout::new(stack(vec![ + scope( + |ctx: ScopeCtx, a: &mut A| { + //> + ctx.with_scoped(&mut a.b) + }, + draw(|area, _state: &mut B| { + assert_eq!(area, Area::new(0., 0., 100., 100.)); + }), + ), + scope( + |ctx: ScopeCtx, a: &mut A| { + //> + ctx.with_scoped(&mut a.c) + }, + draw(|area, _state: &mut C| { + assert_eq!(area, Area::new(0., 0., 100., 100.)); + }), + ), + ])) + .draw(Area::new(0., 0., 100., 100.), &mut A { b: B, c: C }); } + #[test] fn test_scope_unwrap() { - struct B; - struct A { - b: Option, - } - struct AScoper; - impl ScopableOption for AScoper { - fn scope_option( - scoping: &mut A, - f: impl FnOnce(Option<&mut B>) -> Result, - ) -> Result { - f(scoping.b.as_mut()) - } - } - fn layout(_a: &mut A) -> Node { - stack(vec![scope::<_, _, AScoper>(|_b: &mut B| space())]) + struct B { + test: bool, } - Layout::new(layout).draw(Area::new(0., 0., 100., 100.), &mut A { b: Some(B) }); - } - #[test] - fn test_scope_unwrap_ctx() { - struct B; struct A { b: Option, } - struct AScoper; - impl ScopableOption for AScoper { - fn scope_option( - scoping: &mut A, - f: impl FnOnce(Option<&mut B>) -> Result, - ) -> Result { - f(scoping.b.as_mut()) - } - } - struct BScoper; - impl Scopable for BScoper { - fn scope(scoping: &mut B, f: impl FnOnce(&mut B) -> Result) -> Result { - f(scoping) - } - } - fn layout(_b: &mut B, _a: &mut A) -> NodeWith { - stack(vec![scope_with::<_, _, _, _, BScoper, AScoper>( - |_b: &mut B, _b_1: &mut B| space(), - )]) - } - Layout::new_with(layout).draw_with( - Area::new(0., 0., 100., 100.), - &mut B, - &mut A { b: Some(B) }, - ); + let layout = dynamic(|_: &mut A| { + stack(vec![ + //> + scope( + |ctx: ScopeCtx, a: &mut A| { + //> + let Some(ref mut b) = a.b else { + return ctx.empty(); + }; + ctx.with_scoped(b) + }, + draw(|_, b: &mut B| b.test = !b.test), + ), + ]) + }); + let mut state = A { + b: Some(B { test: false }), + }; + let mut layout = Layout::new(layout); + layout.draw(Area::new(0., 0., 100., 100.), &mut state); + assert!(state.b.as_ref().unwrap().test); + layout.draw(Area::new(0., 0., 100., 100.), &mut state); + assert!(!state.b.as_ref().unwrap().test); } + + // No can do buddy + // #[test] + // fn test_scope_inv() { + // struct B; + // struct A { + // b: B, + // } + // type One<'a> = (&'a mut A, &'a mut A); + // type Two<'a> = (&'a mut B, &'a mut A); + + // let mut one = A { b: B }; + // let mut oneone = A { b: B }; + // Layout::new(stack(vec![ + // //> + // scope( + // |ctx: ScopeCtx, a: &mut One| { + // //> + // ctx.with_scoped(&mut (&mut a.0.b, a.1)) + // }, + // space(), + // ), + // ])) + // .draw(Area::new(0., 0., 100., 100.), &mut (&mut one, &mut oneone)); + // } } diff --git a/src/tests/sequence_tests.rs b/src/tests/sequence_tests.rs index c423f4f..b820aa4 100644 --- a/src/tests/sequence_tests.rs +++ b/src/tests/sequence_tests.rs @@ -7,12 +7,12 @@ mod tests { use crate::nodes::*; #[test] fn test_column_basic() { - Layout::new(|()| { + Layout::new({ column(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 100., 50.)); }), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 50., 100., 50.)); }), ]) @@ -21,25 +21,25 @@ mod tests { } #[test] fn test_column_constrained_1() { - Layout::new(|()| { + Layout::new({ column(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 100., 10.)); }) .height(10.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 10., 100., 90.)); }), ]) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ column(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 100., 10.)); }) .height(10.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 10., 100., 90.)); }), ]) @@ -48,24 +48,24 @@ mod tests { } #[test] fn test_column_constrained_2() { - Layout::new(|()| { + Layout::new({ column(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 100., 90.)); }), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 90., 100., 10.)); }) .height(10.), ]) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ column(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 100., 90.)); }), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 90., 100., 10.)); }) .height(10.), @@ -75,12 +75,12 @@ mod tests { } #[test] fn test_row_basic() { - Layout::new(|()| { + Layout::new({ row(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 50., 100.)); }), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(50., 0., 50., 100.)); }), ]) @@ -89,39 +89,39 @@ mod tests { } #[test] fn test_row_constrained_1() { - Layout::new(|()| { + Layout::new({ row(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 25., 10., 50.)); }) .width(10.) .height(50.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(10., 0., 90., 100.)); }), ]) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ row(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 10., 20.)); }) .width(10.) .height(20.) .align(Align::Top), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(10., 40., 10., 20.)); }) .width(10.) .height(20.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(20., 80., 10., 20.)); }) .width(10.) .height(20.) .align(Align::Bottom), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(30., 0., 70., 100.)); }), ]) @@ -130,23 +130,23 @@ mod tests { } #[test] fn test_row_constrained_2() { - Layout::new(|()| { + Layout::new({ row(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 70., 100.)); }), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(70., 0., 10., 20.)); }) .width(10.) .height(20.) .align(Align::Top), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(80., 40., 10., 20.)); }) .width(10.) .height(20.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(90., 80., 10., 20.)); }) .width(10.) @@ -155,23 +155,23 @@ mod tests { ]) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ row(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 70., 100.)); }), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(70., 0., 10., 20.)); }) .width(10.) .height(20.) .align(Align::Top), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(80., 40., 10., 20.)); }) .width(10.) .height(20.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(90., 80., 10., 20.)); }) .width(10.) @@ -183,12 +183,12 @@ mod tests { } #[test] fn test_stack_basic() { - Layout::new(|()| { + Layout::new({ stack(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 100., 100.)); }), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 100., 100.)); }), ]) @@ -198,57 +198,57 @@ mod tests { #[test] fn test_stack_alignment() { - Layout::new(|()| { + Layout::new({ stack(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 10., 20.)); }) .width(10.) .height(20.) .align(Align::TopLeading), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(45., 0., 10., 20.)); }) .width(10.) .height(20.) .align(Align::TopCenter), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(90., 0., 10., 20.)); }) .width(10.) .height(20.) .align(Align::TopTrailing), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(90., 40., 10., 20.)); }) .width(10.) .height(20.) .align(Align::CenterTrailing), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(90., 80., 10., 20.)); }) .width(10.) .height(20.) .align(Align::BottomTrailing), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(45., 80., 10., 20.)); }) .width(10.) .height(20.) .align(Align::BottomCenter), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 80., 10., 20.)); }) .width(10.) .height(20.) .align(Align::BottomLeading), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 40., 10., 20.)); }) .width(10.) .height(20.) .align(Align::CenterLeading), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(45., 40., 10., 20.)); }) .width(10.) @@ -261,46 +261,46 @@ mod tests { } #[test] fn test_sequence_spacing() { - Layout::new(|()| { + Layout::new({ row_spaced( 10., vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 40., 10., 20.)); }) .width(10.) .height(20.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(20., 0., 25., 100.)); }), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(55., 40., 10., 20.)); }) .width(10.) .height(20.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(75., 0., 25., 100.)); }), ], ) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); - Layout::new(|()| { + Layout::new({ column_spaced( 10., vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 100., 15.)); }), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(45., 25., 10., 20.)); }) .width(10.) .height(20.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 55., 100., 15.)); }), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(45., 80., 10., 20.)); }) .width(10.) @@ -312,13 +312,13 @@ mod tests { } #[test] fn test_row_with_constrained_item() { - Layout::new(|()| { + Layout::new({ row(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 30., 100.)); }) .width(30.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(30., 0., 70., 100.)); }), ]) @@ -328,19 +328,19 @@ mod tests { #[test] fn test_nested_row_with_constrained_item() { - Layout::new(|()| { + Layout::new({ row(vec![ row(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 20., 100.)); }) .width(20.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(20., 0., 30., 100.)); }), ]) .width(50.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(50., 0., 50., 100.)); }), ]) @@ -350,12 +350,12 @@ mod tests { #[test] fn test_stack_with_constrained_item() { - Layout::new(|()| { + Layout::new({ stack(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 100., 100.)); }), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(25., 25., 50., 50.)); }) .width(50.) @@ -367,21 +367,21 @@ mod tests { #[test] fn test_row_with_multiple_constrained_items() { - Layout::new(|()| { + Layout::new({ row(vec![ - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(0., 0., 20., 100.)); }) .width(20.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(20., 25., 30., 50.)); }) .width(30.) .height(50.), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(50., 0., 25., 100.)); }), - draw(|a, _| { + draw(|a, _: &mut ()| { assert_eq!(a, Area::new(75., 0., 25., 100.)); }), ]) @@ -392,100 +392,109 @@ mod tests { #[test] fn test_constraint_combination() { assert_eq!( - row::<(), ()>(vec![space(), space().height(30.)]) + row::<()>(vec![space(), space().height(30.)]) .inner - .constraints(Area::zero(), &mut (), &mut ()), + .constraints(Area::zero(), &mut ()), SizeConstraints { width: Constraint::none(), height: Constraint::new(Some(30.), None), ..Default::default() } + .into() ); assert_eq!( - row::<(), ()>(vec![space().height(40.), space().height(30.)]) + row::<()>(vec![space().height(40.), space().height(30.)]) .inner - .constraints(Area::zero(), &mut (), &mut ()), + .constraints(Area::zero(), &mut ()), SizeConstraints { width: Constraint::none(), height: Constraint::new(Some(40.), Some(40.)), ..Default::default() } + .into() ); assert_eq!( - column::<(), ()>(vec![space(), space().width(10.)]) + column::<()>(vec![space(), space().width(10.)]) .inner - .constraints(Area::zero(), &mut (), &mut ()), + .constraints(Area::zero(), &mut ()), SizeConstraints { width: Constraint::new(Some(10.), None), height: Constraint::none(), ..Default::default() } + .into() ); assert_eq!( - column::<(), ()>(vec![space().width(20.), space().width(10.)]) + column::<()>(vec![space().width(20.), space().width(10.)]) .inner - .constraints(Area::zero(), &mut (), &mut ()), + .constraints(Area::zero(), &mut ()), SizeConstraints { width: Constraint::new(Some(20.), Some(20.)), height: Constraint::none(), ..Default::default() } + .into() ); assert_eq!( - stack::<(), ()>(vec![space(), space().height(10.)]) + stack::<()>(vec![space(), space().height(10.)]) .inner - .constraints(Area::zero(), &mut (), &mut ()), + .constraints(Area::zero(), &mut ()), SizeConstraints { width: Constraint::none(), height: Constraint::new(Some(10.), None), ..Default::default() } + .into() ); assert_eq!( - stack::<(), ()>(vec![space().height(20.), space().width(10.)]) + stack::<()>(vec![space().height(20.), space().width(10.)]) .inner - .constraints(Area::zero(), &mut (), &mut ()), + .constraints(Area::zero(), &mut ()), SizeConstraints { width: Constraint::new(Some(10.), None), height: Constraint::new(Some(20.), None), ..Default::default() } + .into() ); assert_eq!( - stack::<(), ()>(vec![space().height(20.), space().height(10.)]) + stack::<()>(vec![space().height(20.), space().height(10.)]) .inner - .constraints(Area::zero(), &mut (), &mut ()), + .constraints(Area::zero(), &mut ()), SizeConstraints { width: Constraint::none(), height: Constraint::new(Some(20.), Some(20.)), ..Default::default() } + .into() ); assert_eq!( - stack::<(), ()>(vec![space().width(20.), space().width(10.)]) + stack::<()>(vec![space().width(20.), space().width(10.)]) .inner - .constraints(Area::zero(), &mut (), &mut ()), + .constraints(Area::zero(), &mut ()), SizeConstraints { width: Constraint::new(Some(20.), Some(20.)), height: Constraint::none(), ..Default::default() } + .into() ); } #[test] fn test_explicit_in_explicit_conflict_parent_priority() { assert_eq!( - space::<(), ()>() + space::<()>() .width_range(10.0..) .pad(0.) .width_range(..5.) .inner - .constraints(Area::zero(), &mut (), &mut ()), + .constraints(Area::zero(), &mut ()), SizeConstraints { width: Constraint::new(Some(5.), Some(5.)), height: Constraint::none(), ..Default::default() } + .into() ); } } diff --git a/src/tests/snapshots/backer__tests__public_api_test__api_full.snap b/src/tests/snapshots/backer__tests__public_api_test__api_full.snap new file mode 100644 index 0000000..c914753 --- /dev/null +++ b/src/tests/snapshots/backer__tests__public_api_test__api_full.snap @@ -0,0 +1,243 @@ +--- +source: src/tests/public_api_test.rs +expression: public_api +--- +pub mod backer +pub mod backer::models +pub enum backer::models::Align +pub backer::models::Align::Bottom +pub backer::models::Align::BottomCenter +pub backer::models::Align::BottomLeading +pub backer::models::Align::BottomTrailing +pub backer::models::Align::CenterCenter +pub backer::models::Align::CenterLeading +pub backer::models::Align::CenterTrailing +pub backer::models::Align::CenterX +pub backer::models::Align::CenterY +pub backer::models::Align::Leading +pub backer::models::Align::Top +pub backer::models::Align::TopCenter +pub backer::models::Align::TopLeading +pub backer::models::Align::TopTrailing +pub backer::models::Align::Trailing +impl core::clone::Clone for backer::models::Align +pub fn backer::models::Align::clone(&self) -> backer::models::Align +impl core::fmt::Debug for backer::models::Align +pub fn backer::models::Align::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Copy for backer::models::Align +impl core::marker::Freeze for backer::models::Align +impl core::marker::Send for backer::models::Align +impl core::marker::Sync for backer::models::Align +impl core::marker::Unpin for backer::models::Align +impl core::panic::unwind_safe::RefUnwindSafe for backer::models::Align +impl core::panic::unwind_safe::UnwindSafe for backer::models::Align +impl core::convert::Into for backer::models::Align where U: core::convert::From +pub fn backer::models::Align::into(self) -> U +impl core::convert::TryFrom for backer::models::Align where U: core::convert::Into +pub type backer::models::Align::Error = core::convert::Infallible +pub fn backer::models::Align::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for backer::models::Align where U: core::convert::TryFrom +pub type backer::models::Align::Error = >::Error +pub fn backer::models::Align::try_into(self) -> core::result::Result>::Error> +impl alloc::borrow::ToOwned for backer::models::Align where T: core::clone::Clone +pub type backer::models::Align::Owned = T +pub fn backer::models::Align::clone_into(&self, target: &mut T) +pub fn backer::models::Align::to_owned(&self) -> T +impl core::any::Any for backer::models::Align where T: 'static + ?core::marker::Sized +pub fn backer::models::Align::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for backer::models::Align where T: ?core::marker::Sized +pub fn backer::models::Align::borrow(&self) -> &T +impl core::borrow::BorrowMut for backer::models::Align where T: ?core::marker::Sized +pub fn backer::models::Align::borrow_mut(&mut self) -> &mut T +impl core::clone::CloneToUninit for backer::models::Align where T: core::clone::Clone +pub unsafe fn backer::models::Align::clone_to_uninit(&self, dst: *mut u8) +impl core::convert::From for backer::models::Align +pub fn backer::models::Align::from(t: T) -> T +pub struct backer::models::Area +pub backer::models::Area::height: f32 +pub backer::models::Area::width: f32 +pub backer::models::Area::x: f32 +pub backer::models::Area::y: f32 +impl backer::models::Area +pub fn backer::models::Area::new(x: f32, y: f32, width: f32, height: f32) -> Self +impl core::clone::Clone for backer::models::Area +pub fn backer::models::Area::clone(&self) -> backer::models::Area +impl core::cmp::PartialEq for backer::models::Area +pub fn backer::models::Area::eq(&self, other: &backer::models::Area) -> bool +impl core::default::Default for backer::models::Area +pub fn backer::models::Area::default() -> backer::models::Area +impl core::fmt::Debug for backer::models::Area +pub fn backer::models::Area::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Copy for backer::models::Area +impl core::marker::StructuralPartialEq for backer::models::Area +impl core::marker::Freeze for backer::models::Area +impl core::marker::Send for backer::models::Area +impl core::marker::Sync for backer::models::Area +impl core::marker::Unpin for backer::models::Area +impl core::panic::unwind_safe::RefUnwindSafe for backer::models::Area +impl core::panic::unwind_safe::UnwindSafe for backer::models::Area +impl core::convert::Into for backer::models::Area where U: core::convert::From +pub fn backer::models::Area::into(self) -> U +impl core::convert::TryFrom for backer::models::Area where U: core::convert::Into +pub type backer::models::Area::Error = core::convert::Infallible +pub fn backer::models::Area::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for backer::models::Area where U: core::convert::TryFrom +pub type backer::models::Area::Error = >::Error +pub fn backer::models::Area::try_into(self) -> core::result::Result>::Error> +impl alloc::borrow::ToOwned for backer::models::Area where T: core::clone::Clone +pub type backer::models::Area::Owned = T +pub fn backer::models::Area::clone_into(&self, target: &mut T) +pub fn backer::models::Area::to_owned(&self) -> T +impl core::any::Any for backer::models::Area where T: 'static + ?core::marker::Sized +pub fn backer::models::Area::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for backer::models::Area where T: ?core::marker::Sized +pub fn backer::models::Area::borrow(&self) -> &T +impl core::borrow::BorrowMut for backer::models::Area where T: ?core::marker::Sized +pub fn backer::models::Area::borrow_mut(&mut self) -> &mut T +impl core::clone::CloneToUninit for backer::models::Area where T: core::clone::Clone +pub unsafe fn backer::models::Area::clone_to_uninit(&self, dst: *mut u8) +impl core::convert::From for backer::models::Area +pub fn backer::models::Area::from(t: T) -> T +pub mod backer::nodes +pub fn backer::nodes::area_reader<'nodes, State>(func: impl core::ops::function::Fn(backer::models::Area, &mut State) -> backer::Node<'nodes, State> + 'static) -> backer::Node<'nodes, State> +pub fn backer::nodes::column(elements: alloc::vec::Vec>) -> backer::Node<'_, State> +pub fn backer::nodes::column_spaced(spacing: f32, elements: alloc::vec::Vec>) -> backer::Node<'_, State> +pub fn backer::nodes::draw<'nodes, State>(drawable_fn: impl core::ops::function::Fn(backer::models::Area, &mut State) + 'static) -> backer::Node<'nodes, State> +pub fn backer::nodes::draw_object<'nodes, State>(drawable: impl backer::traits::drawable::Drawable<'nodes, State> + 'nodes) -> backer::Node<'nodes, State> +pub fn backer::nodes::dynamic<'nodes, State: 'nodes>(func: impl core::ops::function::Fn(&mut State) -> backer::Node<'nodes, State> + 'nodes) -> backer::Node<'nodes, State> +pub fn backer::nodes::empty<'nodes, State>() -> backer::Node<'nodes, State> +pub fn backer::nodes::group(elements: alloc::vec::Vec>) -> backer::Node<'_, State> +pub fn backer::nodes::row(elements: alloc::vec::Vec>) -> backer::Node<'_, State> +pub fn backer::nodes::row_spaced(spacing: f32, elements: alloc::vec::Vec>) -> backer::Node<'_, State> +pub fn backer::nodes::scope<'nodes, State, Scoped: 'nodes>(scope: impl core::ops::function::Fn(backer::ScopeCtx<'_, '_, Scoped>, &mut State) -> backer::ScopeCtxResult + 'nodes, node: backer::Node<'nodes, Scoped>) -> backer::Node<'nodes, State> +pub fn backer::nodes::space<'nodes, State>() -> backer::Node<'nodes, State> +pub fn backer::nodes::stack(elements: alloc::vec::Vec>) -> backer::Node<'_, State> +pub mod backer::traits +pub struct backer::Layout<'nodes, State> +impl<'nodes, State> backer::Layout<'nodes, State> +pub fn backer::Layout<'nodes, State>::new(tree: backer::Node<'nodes, State>) -> Self +impl backer::Layout<'_, State> +pub fn backer::Layout<'_, State>::draw(&mut self, area: backer::models::Area, state: &mut State) +impl<'nodes, State> core::marker::Freeze for backer::Layout<'nodes, State> +impl<'nodes, State> !core::marker::Send for backer::Layout<'nodes, State> +impl<'nodes, State> !core::marker::Sync for backer::Layout<'nodes, State> +impl<'nodes, State> core::marker::Unpin for backer::Layout<'nodes, State> +impl<'nodes, State> !core::panic::unwind_safe::RefUnwindSafe for backer::Layout<'nodes, State> +impl<'nodes, State> !core::panic::unwind_safe::UnwindSafe for backer::Layout<'nodes, State> +impl core::convert::Into for backer::Layout<'nodes, State> where U: core::convert::From +pub fn backer::Layout<'nodes, State>::into(self) -> U +impl core::convert::TryFrom for backer::Layout<'nodes, State> where U: core::convert::Into +pub type backer::Layout<'nodes, State>::Error = core::convert::Infallible +pub fn backer::Layout<'nodes, State>::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for backer::Layout<'nodes, State> where U: core::convert::TryFrom +pub type backer::Layout<'nodes, State>::Error = >::Error +pub fn backer::Layout<'nodes, State>::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for backer::Layout<'nodes, State> where T: 'static + ?core::marker::Sized +pub fn backer::Layout<'nodes, State>::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for backer::Layout<'nodes, State> where T: ?core::marker::Sized +pub fn backer::Layout<'nodes, State>::borrow(&self) -> &T +impl core::borrow::BorrowMut for backer::Layout<'nodes, State> where T: ?core::marker::Sized +pub fn backer::Layout<'nodes, State>::borrow_mut(&mut self) -> &mut T +impl core::convert::From for backer::Layout<'nodes, State> +pub fn backer::Layout<'nodes, State>::from(t: T) -> T +pub struct backer::Node<'nodes, State> +impl<'nodes, State> backer::Node<'nodes, State> +pub fn backer::Node<'nodes, State>::align(self, align: backer::models::Align) -> Self +pub fn backer::Node<'nodes, State>::align_contents(self, align: backer::models::Align) -> Self +pub fn backer::Node<'nodes, State>::aspect(self, ratio: f32) -> Self +pub fn backer::Node<'nodes, State>::attach_over(self, node: Self) -> Self +pub fn backer::Node<'nodes, State>::attach_under(self, node: Self) -> Self +pub fn backer::Node<'nodes, State>::expand(self) -> Self +pub fn backer::Node<'nodes, State>::expand_x(self) -> Self +pub fn backer::Node<'nodes, State>::expand_y(self) -> Self +pub fn backer::Node<'nodes, State>::height(self, height: f32) -> Self +pub fn backer::Node<'nodes, State>::height_range(self, range: R) -> Self where R: core::ops::range::RangeBounds +pub fn backer::Node<'nodes, State>::offset(self, offset_x: f32, offset_y: f32) -> backer::Node<'nodes, State> +pub fn backer::Node<'nodes, State>::offset_x(self, amount: f32) -> backer::Node<'nodes, State> +pub fn backer::Node<'nodes, State>::offset_y(self, amount: f32) -> backer::Node<'nodes, State> +pub fn backer::Node<'nodes, State>::pad(self, amount: f32) -> backer::Node<'nodes, State> +pub fn backer::Node<'nodes, State>::pad_bottom(self, amount: f32) -> backer::Node<'nodes, State> +pub fn backer::Node<'nodes, State>::pad_leading(self, amount: f32) -> backer::Node<'nodes, State> +pub fn backer::Node<'nodes, State>::pad_top(self, amount: f32) -> backer::Node<'nodes, State> +pub fn backer::Node<'nodes, State>::pad_trailing(self, amount: f32) -> backer::Node<'nodes, State> +pub fn backer::Node<'nodes, State>::pad_x(self, amount: f32) -> backer::Node<'nodes, State> +pub fn backer::Node<'nodes, State>::pad_y(self, amount: f32) -> backer::Node<'nodes, State> +pub fn backer::Node<'nodes, State>::visible(self, visibility: bool) -> Self +pub fn backer::Node<'nodes, State>::width(self, width: f32) -> Self +pub fn backer::Node<'nodes, State>::width_range(self, range: R) -> Self where R: core::ops::range::RangeBounds +impl backer::Node<'_, State> +pub fn backer::Node<'_, State>::dynamic_height(self, f: impl core::ops::function::Fn(f32, &mut State) -> f32 + 'static) -> Self +pub fn backer::Node<'_, State>::dynamic_width(self, f: impl core::ops::function::Fn(f32, &mut State) -> f32 + 'static) -> Self +impl core::fmt::Debug for backer::Node<'_, State> +pub fn backer::Node<'_, State>::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl<'nodes, State> core::marker::Freeze for backer::Node<'nodes, State> +impl<'nodes, State> !core::marker::Send for backer::Node<'nodes, State> +impl<'nodes, State> !core::marker::Sync for backer::Node<'nodes, State> +impl<'nodes, State> core::marker::Unpin for backer::Node<'nodes, State> +impl<'nodes, State> !core::panic::unwind_safe::RefUnwindSafe for backer::Node<'nodes, State> +impl<'nodes, State> !core::panic::unwind_safe::UnwindSafe for backer::Node<'nodes, State> +impl core::convert::Into for backer::Node<'nodes, State> where U: core::convert::From +pub fn backer::Node<'nodes, State>::into(self) -> U +impl core::convert::TryFrom for backer::Node<'nodes, State> where U: core::convert::Into +pub type backer::Node<'nodes, State>::Error = core::convert::Infallible +pub fn backer::Node<'nodes, State>::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for backer::Node<'nodes, State> where U: core::convert::TryFrom +pub type backer::Node<'nodes, State>::Error = >::Error +pub fn backer::Node<'nodes, State>::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for backer::Node<'nodes, State> where T: 'static + ?core::marker::Sized +pub fn backer::Node<'nodes, State>::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for backer::Node<'nodes, State> where T: ?core::marker::Sized +pub fn backer::Node<'nodes, State>::borrow(&self) -> &T +impl core::borrow::BorrowMut for backer::Node<'nodes, State> where T: ?core::marker::Sized +pub fn backer::Node<'nodes, State>::borrow_mut(&mut self) -> &mut T +impl core::convert::From for backer::Node<'nodes, State> +pub fn backer::Node<'nodes, State>::from(t: T) -> T +pub struct backer::ScopeCtx<'a, 'nodes, SubState> +impl backer::ScopeCtx<'_, '_, SubState> +pub fn backer::ScopeCtx<'_, '_, SubState>::empty(self) -> backer::ScopeCtxResult +pub fn backer::ScopeCtx<'_, '_, SubState>::with_scoped(self, scoped: &mut SubState) -> backer::ScopeCtxResult +impl<'a, 'nodes, SubState> core::marker::Freeze for backer::ScopeCtx<'a, 'nodes, SubState> +impl<'a, 'nodes, SubState> !core::marker::Send for backer::ScopeCtx<'a, 'nodes, SubState> +impl<'a, 'nodes, SubState> !core::marker::Sync for backer::ScopeCtx<'a, 'nodes, SubState> +impl<'a, 'nodes, SubState> core::marker::Unpin for backer::ScopeCtx<'a, 'nodes, SubState> +impl<'a, 'nodes, SubState> !core::panic::unwind_safe::RefUnwindSafe for backer::ScopeCtx<'a, 'nodes, SubState> +impl<'a, 'nodes, SubState> !core::panic::unwind_safe::UnwindSafe for backer::ScopeCtx<'a, 'nodes, SubState> +impl core::convert::Into for backer::ScopeCtx<'a, 'nodes, SubState> where U: core::convert::From +pub fn backer::ScopeCtx<'a, 'nodes, SubState>::into(self) -> U +impl core::convert::TryFrom for backer::ScopeCtx<'a, 'nodes, SubState> where U: core::convert::Into +pub type backer::ScopeCtx<'a, 'nodes, SubState>::Error = core::convert::Infallible +pub fn backer::ScopeCtx<'a, 'nodes, SubState>::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for backer::ScopeCtx<'a, 'nodes, SubState> where U: core::convert::TryFrom +pub type backer::ScopeCtx<'a, 'nodes, SubState>::Error = >::Error +pub fn backer::ScopeCtx<'a, 'nodes, SubState>::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for backer::ScopeCtx<'a, 'nodes, SubState> where T: 'static + ?core::marker::Sized +pub fn backer::ScopeCtx<'a, 'nodes, SubState>::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for backer::ScopeCtx<'a, 'nodes, SubState> where T: ?core::marker::Sized +pub fn backer::ScopeCtx<'a, 'nodes, SubState>::borrow(&self) -> &T +impl core::borrow::BorrowMut for backer::ScopeCtx<'a, 'nodes, SubState> where T: ?core::marker::Sized +pub fn backer::ScopeCtx<'a, 'nodes, SubState>::borrow_mut(&mut self) -> &mut T +impl core::convert::From for backer::ScopeCtx<'a, 'nodes, SubState> +pub fn backer::ScopeCtx<'a, 'nodes, SubState>::from(t: T) -> T +pub struct backer::ScopeCtxResult +impl core::marker::Freeze for backer::ScopeCtxResult +impl core::marker::Send for backer::ScopeCtxResult +impl core::marker::Sync for backer::ScopeCtxResult +impl core::marker::Unpin for backer::ScopeCtxResult +impl core::panic::unwind_safe::RefUnwindSafe for backer::ScopeCtxResult +impl core::panic::unwind_safe::UnwindSafe for backer::ScopeCtxResult +impl core::convert::Into for backer::ScopeCtxResult where U: core::convert::From +pub fn backer::ScopeCtxResult::into(self) -> U +impl core::convert::TryFrom for backer::ScopeCtxResult where U: core::convert::Into +pub type backer::ScopeCtxResult::Error = core::convert::Infallible +pub fn backer::ScopeCtxResult::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for backer::ScopeCtxResult where U: core::convert::TryFrom +pub type backer::ScopeCtxResult::Error = >::Error +pub fn backer::ScopeCtxResult::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for backer::ScopeCtxResult where T: 'static + ?core::marker::Sized +pub fn backer::ScopeCtxResult::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for backer::ScopeCtxResult where T: ?core::marker::Sized +pub fn backer::ScopeCtxResult::borrow(&self) -> &T +impl core::borrow::BorrowMut for backer::ScopeCtxResult where T: ?core::marker::Sized +pub fn backer::ScopeCtxResult::borrow_mut(&mut self) -> &mut T +impl core::convert::From for backer::ScopeCtxResult +pub fn backer::ScopeCtxResult::from(t: T) -> T diff --git a/src/tests/snapshots/backer__tests__public_api_test__api_simplified.snap b/src/tests/snapshots/backer__tests__public_api_test__api_simplified.snap new file mode 100644 index 0000000..23eb8a3 --- /dev/null +++ b/src/tests/snapshots/backer__tests__public_api_test__api_simplified.snap @@ -0,0 +1,84 @@ +--- +source: src/tests/public_api_test.rs +expression: public_api_simplified +--- +pub mod backer +pub mod backer::models +pub enum backer::models::Align +pub backer::models::Align::Bottom +pub backer::models::Align::BottomCenter +pub backer::models::Align::BottomLeading +pub backer::models::Align::BottomTrailing +pub backer::models::Align::CenterCenter +pub backer::models::Align::CenterLeading +pub backer::models::Align::CenterTrailing +pub backer::models::Align::CenterX +pub backer::models::Align::CenterY +pub backer::models::Align::Leading +pub backer::models::Align::Top +pub backer::models::Align::TopCenter +pub backer::models::Align::TopLeading +pub backer::models::Align::TopTrailing +pub backer::models::Align::Trailing +pub struct backer::models::Area +pub backer::models::Area::height: f32 +pub backer::models::Area::width: f32 +pub backer::models::Area::x: f32 +pub backer::models::Area::y: f32 +impl backer::models::Area +pub fn backer::models::Area::new(x: f32, y: f32, width: f32, height: f32) -> Self +pub mod backer::nodes +pub fn backer::nodes::area_reader<'nodes, State>(func: impl core::ops::function::Fn(backer::models::Area, &mut State) -> backer::Node<'nodes, State> + 'static) -> backer::Node<'nodes, State> +pub fn backer::nodes::column(elements: alloc::vec::Vec>) -> backer::Node<'_, State> +pub fn backer::nodes::column_spaced(spacing: f32, elements: alloc::vec::Vec>) -> backer::Node<'_, State> +pub fn backer::nodes::draw<'nodes, State>(drawable_fn: impl core::ops::function::Fn(backer::models::Area, &mut State) + 'static) -> backer::Node<'nodes, State> +pub fn backer::nodes::draw_object<'nodes, State>(drawable: impl backer::traits::drawable::Drawable<'nodes, State> + 'nodes) -> backer::Node<'nodes, State> +pub fn backer::nodes::dynamic<'nodes, State: 'nodes>(func: impl core::ops::function::Fn(&mut State) -> backer::Node<'nodes, State> + 'nodes) -> backer::Node<'nodes, State> +pub fn backer::nodes::empty<'nodes, State>() -> backer::Node<'nodes, State> +pub fn backer::nodes::group(elements: alloc::vec::Vec>) -> backer::Node<'_, State> +pub fn backer::nodes::row(elements: alloc::vec::Vec>) -> backer::Node<'_, State> +pub fn backer::nodes::row_spaced(spacing: f32, elements: alloc::vec::Vec>) -> backer::Node<'_, State> +pub fn backer::nodes::scope<'nodes, State, Scoped: 'nodes>(scope: impl core::ops::function::Fn(backer::ScopeCtx<'_, '_, Scoped>, &mut State) -> backer::ScopeCtxResult + 'nodes, node: backer::Node<'nodes, Scoped>) -> backer::Node<'nodes, State> +pub fn backer::nodes::space<'nodes, State>() -> backer::Node<'nodes, State> +pub fn backer::nodes::stack(elements: alloc::vec::Vec>) -> backer::Node<'_, State> +pub mod backer::traits +pub struct backer::Layout<'nodes, State> +impl<'nodes, State> backer::Layout<'nodes, State> +pub fn backer::Layout<'nodes, State>::new(tree: backer::Node<'nodes, State>) -> Self +impl backer::Layout<'_, State> +pub fn backer::Layout<'_, State>::draw(&mut self, area: backer::models::Area, state: &mut State) +pub struct backer::Node<'nodes, State> +impl<'nodes, State> backer::Node<'nodes, State> +pub fn backer::Node<'nodes, State>::align(self, align: backer::models::Align) -> Self +pub fn backer::Node<'nodes, State>::align_contents(self, align: backer::models::Align) -> Self +pub fn backer::Node<'nodes, State>::aspect(self, ratio: f32) -> Self +pub fn backer::Node<'nodes, State>::attach_over(self, node: Self) -> Self +pub fn backer::Node<'nodes, State>::attach_under(self, node: Self) -> Self +pub fn backer::Node<'nodes, State>::expand(self) -> Self +pub fn backer::Node<'nodes, State>::expand_x(self) -> Self +pub fn backer::Node<'nodes, State>::expand_y(self) -> Self +pub fn backer::Node<'nodes, State>::height(self, height: f32) -> Self +pub fn backer::Node<'nodes, State>::height_range(self, range: R) -> Self where R: core::ops::range::RangeBounds +pub fn backer::Node<'nodes, State>::offset(self, offset_x: f32, offset_y: f32) -> backer::Node<'nodes, State> +pub fn backer::Node<'nodes, State>::offset_x(self, amount: f32) -> backer::Node<'nodes, State> +pub fn backer::Node<'nodes, State>::offset_y(self, amount: f32) -> backer::Node<'nodes, State> +pub fn backer::Node<'nodes, State>::pad(self, amount: f32) -> backer::Node<'nodes, State> +pub fn backer::Node<'nodes, State>::pad_bottom(self, amount: f32) -> backer::Node<'nodes, State> +pub fn backer::Node<'nodes, State>::pad_leading(self, amount: f32) -> backer::Node<'nodes, State> +pub fn backer::Node<'nodes, State>::pad_top(self, amount: f32) -> backer::Node<'nodes, State> +pub fn backer::Node<'nodes, State>::pad_trailing(self, amount: f32) -> backer::Node<'nodes, State> +pub fn backer::Node<'nodes, State>::pad_x(self, amount: f32) -> backer::Node<'nodes, State> +pub fn backer::Node<'nodes, State>::pad_y(self, amount: f32) -> backer::Node<'nodes, State> +pub fn backer::Node<'nodes, State>::visible(self, visibility: bool) -> Self +pub fn backer::Node<'nodes, State>::width(self, width: f32) -> Self +pub fn backer::Node<'nodes, State>::width_range(self, range: R) -> Self where R: core::ops::range::RangeBounds +impl backer::Node<'_, State> +pub fn backer::Node<'_, State>::dynamic_height(self, f: impl core::ops::function::Fn(f32, &mut State) -> f32 + 'static) -> Self +pub fn backer::Node<'_, State>::dynamic_width(self, f: impl core::ops::function::Fn(f32, &mut State) -> f32 + 'static) -> Self +impl core::fmt::Debug for backer::Node<'_, State> +pub fn backer::Node<'_, State>::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub struct backer::ScopeCtx<'a, 'nodes, SubState> +impl backer::ScopeCtx<'_, '_, SubState> +pub fn backer::ScopeCtx<'_, '_, SubState>::empty(self) -> backer::ScopeCtxResult +pub fn backer::ScopeCtx<'_, '_, SubState>::with_scoped(self, scoped: &mut SubState) -> backer::ScopeCtxResult +pub struct backer::ScopeCtxResult diff --git a/src/tests/snapshots/backer__tests__public_api_test__public_api.snap b/src/tests/snapshots/backer__tests__public_api_test__public_api.snap deleted file mode 100644 index 4bbb536..0000000 --- a/src/tests/snapshots/backer__tests__public_api_test__public_api.snap +++ /dev/null @@ -1,236 +0,0 @@ ---- -source: src/tests/public_api_test.rs -expression: public_api ---- -pub mod backer -pub mod backer::models -pub enum backer::models::Align -pub backer::models::Align::Bottom -pub backer::models::Align::BottomCenter -pub backer::models::Align::BottomLeading -pub backer::models::Align::BottomTrailing -pub backer::models::Align::CenterCenter -pub backer::models::Align::CenterLeading -pub backer::models::Align::CenterTrailing -pub backer::models::Align::CenterX -pub backer::models::Align::CenterY -pub backer::models::Align::Leading -pub backer::models::Align::Top -pub backer::models::Align::TopCenter -pub backer::models::Align::TopLeading -pub backer::models::Align::TopTrailing -pub backer::models::Align::Trailing -impl core::clone::Clone for backer::models::Align -pub fn backer::models::Align::clone(&self) -> backer::models::Align -impl core::fmt::Debug for backer::models::Align -pub fn backer::models::Align::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::marker::Copy for backer::models::Align -impl core::marker::Freeze for backer::models::Align -impl core::marker::Send for backer::models::Align -impl core::marker::Sync for backer::models::Align -impl core::marker::Unpin for backer::models::Align -impl core::panic::unwind_safe::RefUnwindSafe for backer::models::Align -impl core::panic::unwind_safe::UnwindSafe for backer::models::Align -impl core::convert::Into for backer::models::Align where U: core::convert::From -pub fn backer::models::Align::into(self) -> U -impl core::convert::TryFrom for backer::models::Align where U: core::convert::Into -pub type backer::models::Align::Error = core::convert::Infallible -pub fn backer::models::Align::try_from(value: U) -> core::result::Result>::Error> -impl core::convert::TryInto for backer::models::Align where U: core::convert::TryFrom -pub type backer::models::Align::Error = >::Error -pub fn backer::models::Align::try_into(self) -> core::result::Result>::Error> -impl alloc::borrow::ToOwned for backer::models::Align where T: core::clone::Clone -pub type backer::models::Align::Owned = T -pub fn backer::models::Align::clone_into(&self, target: &mut T) -pub fn backer::models::Align::to_owned(&self) -> T -impl core::any::Any for backer::models::Align where T: 'static + ?core::marker::Sized -pub fn backer::models::Align::type_id(&self) -> core::any::TypeId -impl core::borrow::Borrow for backer::models::Align where T: ?core::marker::Sized -pub fn backer::models::Align::borrow(&self) -> &T -impl core::borrow::BorrowMut for backer::models::Align where T: ?core::marker::Sized -pub fn backer::models::Align::borrow_mut(&mut self) -> &mut T -impl core::clone::CloneToUninit for backer::models::Align where T: core::clone::Clone -pub unsafe fn backer::models::Align::clone_to_uninit(&self, dst: *mut u8) -impl core::convert::From for backer::models::Align -pub fn backer::models::Align::from(t: T) -> T -pub struct backer::models::Area -pub backer::models::Area::height: f32 -pub backer::models::Area::width: f32 -pub backer::models::Area::x: f32 -pub backer::models::Area::y: f32 -impl backer::models::Area -pub fn backer::models::Area::new(x: f32, y: f32, width: f32, height: f32) -> Self -impl core::clone::Clone for backer::models::Area -pub fn backer::models::Area::clone(&self) -> backer::models::Area -impl core::cmp::PartialEq for backer::models::Area -pub fn backer::models::Area::eq(&self, other: &backer::models::Area) -> bool -impl core::default::Default for backer::models::Area -pub fn backer::models::Area::default() -> backer::models::Area -impl core::fmt::Debug for backer::models::Area -pub fn backer::models::Area::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::marker::Copy for backer::models::Area -impl core::marker::StructuralPartialEq for backer::models::Area -impl core::marker::Freeze for backer::models::Area -impl core::marker::Send for backer::models::Area -impl core::marker::Sync for backer::models::Area -impl core::marker::Unpin for backer::models::Area -impl core::panic::unwind_safe::RefUnwindSafe for backer::models::Area -impl core::panic::unwind_safe::UnwindSafe for backer::models::Area -impl core::convert::Into for backer::models::Area where U: core::convert::From -pub fn backer::models::Area::into(self) -> U -impl core::convert::TryFrom for backer::models::Area where U: core::convert::Into -pub type backer::models::Area::Error = core::convert::Infallible -pub fn backer::models::Area::try_from(value: U) -> core::result::Result>::Error> -impl core::convert::TryInto for backer::models::Area where U: core::convert::TryFrom -pub type backer::models::Area::Error = >::Error -pub fn backer::models::Area::try_into(self) -> core::result::Result>::Error> -impl alloc::borrow::ToOwned for backer::models::Area where T: core::clone::Clone -pub type backer::models::Area::Owned = T -pub fn backer::models::Area::clone_into(&self, target: &mut T) -pub fn backer::models::Area::to_owned(&self) -> T -impl core::any::Any for backer::models::Area where T: 'static + ?core::marker::Sized -pub fn backer::models::Area::type_id(&self) -> core::any::TypeId -impl core::borrow::Borrow for backer::models::Area where T: ?core::marker::Sized -pub fn backer::models::Area::borrow(&self) -> &T -impl core::borrow::BorrowMut for backer::models::Area where T: ?core::marker::Sized -pub fn backer::models::Area::borrow_mut(&mut self) -> &mut T -impl core::clone::CloneToUninit for backer::models::Area where T: core::clone::Clone -pub unsafe fn backer::models::Area::clone_to_uninit(&self, dst: *mut u8) -impl core::convert::From for backer::models::Area -pub fn backer::models::Area::from(t: T) -> T -pub mod backer::nodes -pub fn backer::nodes::area_reader(func: impl core::ops::function::Fn(backer::models::Area, &mut State, &mut ()) -> backer::Node + 'static) -> backer::Node -pub fn backer::nodes::area_reader_with(func: impl core::ops::function::Fn(backer::models::Area, &mut State, &mut Ctx) -> backer::NodeWith + 'static) -> backer::NodeWith -pub fn backer::nodes::column(elements: alloc::vec::Vec>) -> backer::NodeWith -pub fn backer::nodes::column_spaced(spacing: f32, elements: alloc::vec::Vec>) -> backer::NodeWith -pub fn backer::nodes::draw(drawable: impl core::ops::function::Fn(backer::models::Area, &mut State) + 'static) -> backer::Node -pub fn backer::nodes::draw_with(drawable: impl core::ops::function::Fn(backer::models::Area, &mut State, &mut Ctx) + 'static) -> backer::NodeWith -pub fn backer::nodes::empty() -> backer::NodeWith -pub fn backer::nodes::group(elements: alloc::vec::Vec>) -> backer::NodeWith -pub fn backer::nodes::row(elements: alloc::vec::Vec>) -> backer::NodeWith -pub fn backer::nodes::row_spaced(spacing: f32, elements: alloc::vec::Vec>) -> backer::NodeWith -pub fn backer::nodes::scope(node: impl core::ops::function::Fn(&mut ScopedState) -> backer::Node + 'static) -> backer::Node where ScopedState: 'static, State: 'static, StateScoper: backer::traits::ScopableOption + 'static -pub fn backer::nodes::scope_with(node: impl core::ops::function::Fn(&mut ScopedState, &mut ScopedCtx) -> backer::NodeWith + 'static) -> backer::NodeWith where ScopedState: 'static, State: 'static, ScopedCtx: 'static, Ctx: 'static, StateScoper: backer::traits::ScopableOption + 'static, CtxScoper: backer::traits::ScopableOption + 'static -pub fn backer::nodes::space() -> backer::NodeWith -pub fn backer::nodes::stack(elements: alloc::vec::Vec>) -> backer::NodeWith -pub mod backer::traits -pub struct backer::traits::NoOpScoper -impl backer::traits::Scopable for backer::traits::NoOpScoper -pub fn backer::traits::NoOpScoper::scope(scoping: &mut Scoping, f: impl core::ops::function::FnOnce(&mut Scoping) -> Result) -> Result -impl core::marker::Freeze for backer::traits::NoOpScoper -impl core::marker::Send for backer::traits::NoOpScoper where Scoping: core::marker::Send -impl core::marker::Sync for backer::traits::NoOpScoper where Scoping: core::marker::Sync -impl core::marker::Unpin for backer::traits::NoOpScoper where Scoping: core::marker::Unpin -impl core::panic::unwind_safe::RefUnwindSafe for backer::traits::NoOpScoper where Scoping: core::panic::unwind_safe::RefUnwindSafe -impl core::panic::unwind_safe::UnwindSafe for backer::traits::NoOpScoper where Scoping: core::panic::unwind_safe::UnwindSafe -impl backer::traits::ScopableOption for backer::traits::NoOpScoper where T: backer::traits::Scopable -pub fn backer::traits::NoOpScoper::scope_option(scoping: &mut Scoping, f: impl core::ops::function::FnOnce(core::option::Option<&mut Scoped>) -> Result) -> Result -impl core::convert::Into for backer::traits::NoOpScoper where U: core::convert::From -pub fn backer::traits::NoOpScoper::into(self) -> U -impl core::convert::TryFrom for backer::traits::NoOpScoper where U: core::convert::Into -pub type backer::traits::NoOpScoper::Error = core::convert::Infallible -pub fn backer::traits::NoOpScoper::try_from(value: U) -> core::result::Result>::Error> -impl core::convert::TryInto for backer::traits::NoOpScoper where U: core::convert::TryFrom -pub type backer::traits::NoOpScoper::Error = >::Error -pub fn backer::traits::NoOpScoper::try_into(self) -> core::result::Result>::Error> -impl core::any::Any for backer::traits::NoOpScoper where T: 'static + ?core::marker::Sized -pub fn backer::traits::NoOpScoper::type_id(&self) -> core::any::TypeId -impl core::borrow::Borrow for backer::traits::NoOpScoper where T: ?core::marker::Sized -pub fn backer::traits::NoOpScoper::borrow(&self) -> &T -impl core::borrow::BorrowMut for backer::traits::NoOpScoper where T: ?core::marker::Sized -pub fn backer::traits::NoOpScoper::borrow_mut(&mut self) -> &mut T -impl core::convert::From for backer::traits::NoOpScoper -pub fn backer::traits::NoOpScoper::from(t: T) -> T -pub trait backer::traits::Scopable -pub fn backer::traits::Scopable::scope(scoping: &mut Scoping, f: impl core::ops::function::FnOnce(&mut Scoped) -> Result) -> Result -impl backer::traits::Scopable for backer::traits::NoOpScoper -pub fn backer::traits::NoOpScoper::scope(scoping: &mut Scoping, f: impl core::ops::function::FnOnce(&mut Scoping) -> Result) -> Result -pub trait backer::traits::ScopableOption -pub fn backer::traits::ScopableOption::scope_option(scoping: &mut Scoping, f: impl core::ops::function::FnOnce(core::option::Option<&mut Scoped>) -> Result) -> Result -impl backer::traits::ScopableOption for T where T: backer::traits::Scopable -pub fn T::scope_option(scoping: &mut Scoping, f: impl core::ops::function::FnOnce(core::option::Option<&mut Scoped>) -> Result) -> Result -pub struct backer::Layout -impl backer::Layout -pub fn backer::Layout::draw_with(&self, area: backer::models::Area, state: &mut State, ctx: &mut Ctx) -impl backer::Layout -pub fn backer::Layout::new_with(tree: impl core::ops::function::Fn(&mut State, &mut Ctx) -> backer::NodeWith + 'static) -> Self -impl backer::Layout -pub fn backer::Layout::draw(&self, area: backer::models::Area, state: &mut State) -impl backer::Layout -pub fn backer::Layout::new(tree: impl core::ops::function::Fn(&mut State) -> backer::Node + 'static) -> Self -impl core::marker::Freeze for backer::Layout -impl !core::marker::Send for backer::Layout -impl !core::marker::Sync for backer::Layout -impl core::marker::Unpin for backer::Layout -impl !core::panic::unwind_safe::RefUnwindSafe for backer::Layout -impl !core::panic::unwind_safe::UnwindSafe for backer::Layout -impl core::convert::Into for backer::Layout where U: core::convert::From -pub fn backer::Layout::into(self) -> U -impl core::convert::TryFrom for backer::Layout where U: core::convert::Into -pub type backer::Layout::Error = core::convert::Infallible -pub fn backer::Layout::try_from(value: U) -> core::result::Result>::Error> -impl core::convert::TryInto for backer::Layout where U: core::convert::TryFrom -pub type backer::Layout::Error = >::Error -pub fn backer::Layout::try_into(self) -> core::result::Result>::Error> -impl core::any::Any for backer::Layout where T: 'static + ?core::marker::Sized -pub fn backer::Layout::type_id(&self) -> core::any::TypeId -impl core::borrow::Borrow for backer::Layout where T: ?core::marker::Sized -pub fn backer::Layout::borrow(&self) -> &T -impl core::borrow::BorrowMut for backer::Layout where T: ?core::marker::Sized -pub fn backer::Layout::borrow_mut(&mut self) -> &mut T -impl core::convert::From for backer::Layout -pub fn backer::Layout::from(t: T) -> T -pub struct backer::NodeWith -impl backer::NodeWith -pub fn backer::NodeWith::align(self, align: backer::models::Align) -> Self -pub fn backer::NodeWith::align_contents(self, align: backer::models::Align) -> Self -pub fn backer::NodeWith::aspect(self, ratio: f32) -> Self -pub fn backer::NodeWith::attach_over(self, node: Self) -> Self -pub fn backer::NodeWith::attach_under(self, node: Self) -> Self -pub fn backer::NodeWith::dynamic_height_with(self, f: impl core::ops::function::Fn(f32, &mut State, &mut Ctx) -> f32 + 'static) -> Self -pub fn backer::NodeWith::dynamic_width_with(self, f: impl core::ops::function::Fn(f32, &mut State, &mut Ctx) -> f32 + 'static) -> Self -pub fn backer::NodeWith::expand(self) -> Self -pub fn backer::NodeWith::expand_x(self) -> Self -pub fn backer::NodeWith::expand_y(self) -> Self -pub fn backer::NodeWith::height(self, height: f32) -> Self -pub fn backer::NodeWith::height_range(self, range: R) -> Self where R: core::ops::range::RangeBounds -pub fn backer::NodeWith::offset(self, offset_x: f32, offset_y: f32) -> backer::NodeWith -pub fn backer::NodeWith::offset_x(self, amount: f32) -> backer::NodeWith -pub fn backer::NodeWith::offset_y(self, amount: f32) -> backer::NodeWith -pub fn backer::NodeWith::pad(self, amount: f32) -> backer::NodeWith -pub fn backer::NodeWith::pad_bottom(self, amount: f32) -> backer::NodeWith -pub fn backer::NodeWith::pad_leading(self, amount: f32) -> backer::NodeWith -pub fn backer::NodeWith::pad_top(self, amount: f32) -> backer::NodeWith -pub fn backer::NodeWith::pad_trailing(self, amount: f32) -> backer::NodeWith -pub fn backer::NodeWith::pad_x(self, amount: f32) -> backer::NodeWith -pub fn backer::NodeWith::pad_y(self, amount: f32) -> backer::NodeWith -pub fn backer::NodeWith::width(self, width: f32) -> Self -pub fn backer::NodeWith::width_range(self, range: R) -> Self where R: core::ops::range::RangeBounds -impl backer::NodeWith -pub fn backer::NodeWith::dynamic_height(self, f: impl core::ops::function::Fn(f32, &mut State) -> f32 + 'static) -> Self -pub fn backer::NodeWith::dynamic_width(self, f: impl core::ops::function::Fn(f32, &mut State) -> f32 + 'static) -> Self -impl core::fmt::Debug for backer::NodeWith -pub fn backer::NodeWith::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::marker::Freeze for backer::NodeWith -impl !core::marker::Send for backer::NodeWith -impl !core::marker::Sync for backer::NodeWith -impl core::marker::Unpin for backer::NodeWith -impl !core::panic::unwind_safe::RefUnwindSafe for backer::NodeWith -impl !core::panic::unwind_safe::UnwindSafe for backer::NodeWith -impl core::convert::Into for backer::NodeWith where U: core::convert::From -pub fn backer::NodeWith::into(self) -> U -impl core::convert::TryFrom for backer::NodeWith where U: core::convert::Into -pub type backer::NodeWith::Error = core::convert::Infallible -pub fn backer::NodeWith::try_from(value: U) -> core::result::Result>::Error> -impl core::convert::TryInto for backer::NodeWith where U: core::convert::TryFrom -pub type backer::NodeWith::Error = >::Error -pub fn backer::NodeWith::try_into(self) -> core::result::Result>::Error> -impl core::any::Any for backer::NodeWith where T: 'static + ?core::marker::Sized -pub fn backer::NodeWith::type_id(&self) -> core::any::TypeId -impl core::borrow::Borrow for backer::NodeWith where T: ?core::marker::Sized -pub fn backer::NodeWith::borrow(&self) -> &T -impl core::borrow::BorrowMut for backer::NodeWith where T: ?core::marker::Sized -pub fn backer::NodeWith::borrow_mut(&mut self) -> &mut T -impl core::convert::From for backer::NodeWith -pub fn backer::NodeWith::from(t: T) -> T -pub type backer::Node = backer::NodeWith diff --git a/src/traits/drawable.rs b/src/traits/drawable.rs new file mode 100644 index 0000000..b9808a6 --- /dev/null +++ b/src/traits/drawable.rs @@ -0,0 +1,5 @@ +use crate::models::Area; + +pub trait Drawable<'nodes, State> { + fn draw(&mut self, area: Area, state: &mut State, visible: bool); +} diff --git a/src/traits/mod.rs b/src/traits/mod.rs index ab36843..7db4945 100644 --- a/src/traits/mod.rs +++ b/src/traits/mod.rs @@ -1,9 +1,5 @@ +mod drawable; mod nodetrait; -mod scopable; -mod scopable_option; +pub(crate) use drawable::Drawable; pub(crate) use nodetrait::NodeTrait; -pub use scopable::NoOpScoper; -pub use scopable::Scopable; -pub(crate) use scopable::VoidScoper; -pub use scopable_option::ScopableOption; diff --git a/src/traits/nodetrait.rs b/src/traits/nodetrait.rs index ab7fd84..78686b9 100644 --- a/src/traits/nodetrait.rs +++ b/src/traits/nodetrait.rs @@ -4,20 +4,14 @@ use crate::{ }; use std::fmt::Debug; -pub(crate) trait NodeTrait: Debug { - fn constraints( - &mut self, - available_area: Area, - state: &mut State, - ctx: &mut Ctx, - ) -> SizeConstraints; +pub(crate) trait NodeTrait: Debug { + fn constraints(&mut self, available_area: Area, state: &mut State) -> Option; fn layout( &mut self, available_area: Area, contextual_x_align: Option, contextual_y_align: Option, state: &mut State, - ctx: &mut Ctx, ); - fn draw(&mut self, state: &mut State, ctx: &mut Ctx); + fn draw(&mut self, state: &mut State, contextual_visibility: bool); } diff --git a/src/traits/scopable.rs b/src/traits/scopable.rs deleted file mode 100644 index 65b3c2d..0000000 --- a/src/traits/scopable.rs +++ /dev/null @@ -1,61 +0,0 @@ -use std::marker::PhantomData; - -/// Implement `Scopable` to enable usage with [`Node::scope`] -/// -/// ```rust -/// use backer::traits::*; -/// use backer::nodes::*; -/// use backer::models::*; -/// use backer::{Node, Layout}; -/// -/// struct A { -/// b: B, -/// } -/// struct B; -/// -/// struct Scoper; -/// impl Scopable for Scoper { -/// fn scope(scoping: &mut A, f: impl FnOnce(&mut B) -> Result) -> Result { -/// f(&mut scoping.b) -/// } -/// } -/// -/// let layout = Layout::new(my_layout); -/// layout.draw(Area::new(0., 0., 100., 100.), &mut A { b: B }); -/// -/// fn my_layout(_state: &mut A) -> Node { -/// scope::<_, _, Scoper>(my_scoped_layout) -/// } -/// -/// // This tree & it's children now only have access to a subset of our state -/// fn my_scoped_layout(_state: &mut B) -> Node { -/// draw(|_area, _state: &mut B| {}) -/// } -/// ``` -pub trait Scopable { - /// Provide a scoped mutable reference to a subset of your state. - /// - /// This method is called by backer for various purposes, - /// passing different closures for `f` & using the result returned by `Scopable::scope`. - fn scope(scoping: &mut Scoping, f: impl FnOnce(&mut Scoped) -> Result) -> Result; -} - -pub(crate) struct VoidScoper; - -impl Scopable<(), ()> for VoidScoper { - fn scope(scoping: &mut (), f: impl FnOnce(&mut ()) -> Result) -> Result { - f(scoping) - } -} - -/// Used to implement scoping which has no effect. -/// Useful for scoping one of State / Ctx without scoping the other. -pub struct NoOpScoper { - _s: PhantomData, -} - -impl Scopable for NoOpScoper { - fn scope(scoping: &mut Scoping, f: impl FnOnce(&mut Scoping) -> Result) -> Result { - f(scoping) - } -} diff --git a/src/traits/scopable_option.rs b/src/traits/scopable_option.rs deleted file mode 100644 index 954e092..0000000 --- a/src/traits/scopable_option.rs +++ /dev/null @@ -1,42 +0,0 @@ -use super::Scopable; - -/// Implement `ScopableOption` to enable usage with [`scope`] for an optional subset of your state. -/// For non-optional state, implement [`Scopable`]. -/// -/// See [`Scopable`] for more example code. -/// -/// ```rust -/// use backer::traits::ScopableOption; -/// -/// struct A { -/// b: Option, -/// } -/// -/// struct B; -/// -/// struct Scoper; -/// impl ScopableOption for Scoper { -/// fn scope_option(scoping: &mut A, f: impl FnOnce(Option<&mut B>) -> Result) -> Result { -/// f(scoping.b.as_mut()) -/// } -/// } -/// ``` -pub trait ScopableOption { - /// Provide a scoped mutable reference to an optional subset of your state. - fn scope_option( - scoping: &mut Scoping, - f: impl FnOnce(Option<&mut Scoped>) -> Result, - ) -> Result; -} - -impl ScopableOption for T -where - T: Scopable, -{ - fn scope_option( - scoping: &mut Scoping, - f: impl FnOnce(Option<&mut Scoped>) -> Result, - ) -> Result { - Self::scope(scoping, |s| f(Some(s))) - } -}