From c88599f937d759c1f71e09594f587beb3bcb9e54 Mon Sep 17 00:00:00 2001 From: ejjonny Date: Sat, 1 Feb 2025 17:51:20 -0700 Subject: [PATCH 01/16] no ctx --- src/lib.rs | 1 - src/nodes.rs | 33 +++++- src/scoper.rs | 230 ++++++++++++++++++--------------------- src/tests/scope_tests.rs | 31 ++---- 4 files changed, 138 insertions(+), 157 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 395960f..1957ade 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,6 @@ mod node; pub use node::Node; mod node_cache; mod scoper; -pub use scoper::{ScopeCtx, ScopeCtxResult}; mod subtree; mod tests; diff --git a/src/nodes.rs b/src/nodes.rs index 87b2a08..aef4983 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -3,7 +3,7 @@ use crate::{ layout::NodeValue, models::*, node_cache::NodeCache, - scoper::{ScopeCtx, ScopeCtxResult, Scoper}, + scoper::{OptionScoper, OwnedScoper, Scoper}, traits::Drawable, Node, }; @@ -220,15 +220,36 @@ pub fn dynamic<'nodes, State>( /// }); ///``` pub fn scope<'nodes, State, Scoped: 'nodes>( - scope: impl Fn(ScopeCtx<'_, '_, Scoped>, &mut State) -> ScopeCtxResult + 'nodes, + scope: impl Fn(&mut State) -> &mut Scoped + 'nodes, node: Node<'nodes, Scoped>, ) -> Node<'nodes, State> { Node { inner: NodeValue::NodeTrait { - node: Box::new(Scoper { - scope_fn: scope, - node, - }), + node: Box::new(Scoper { scope, node }), + }, + } +} +/// Scopes state to some derived *optional* subset which is unwrapped for all children of this node +/// See `nodes::scope` +pub fn scope_unwrap<'nodes, State, Scoped: 'nodes>( + scope: impl Fn(&mut State) -> &mut Option + 'nodes, + node: Node<'nodes, Scoped>, +) -> Node<'nodes, State> { + Node { + inner: NodeValue::NodeTrait { + node: Box::new(OptionScoper { scope, node }), + }, + } +} +/// Scopes state to some owned derivative for all children of this node +/// See `nodes::scope` +pub fn scope_owned<'nodes, State, Scoped: 'nodes>( + scope: impl Fn(&mut State) -> Scoped + 'nodes, + node: Node<'nodes, Scoped>, +) -> Node<'nodes, State> { + Node { + inner: NodeValue::NodeTrait { + node: Box::new(OwnedScoper { scope, node }), }, } } diff --git a/src/scoper.rs b/src/scoper.rs index 1f16d99..96d0eeb 100644 --- a/src/scoper.rs +++ b/src/scoper.rs @@ -1,109 +1,127 @@ -use std::fmt::Debug; - use crate::{ constraints::SizeConstraints, models::{Area, XAlign, YAlign}, traits::NodeTrait, Node, }; +use std::fmt::Debug; -pub(crate) struct Scoper<'n, SubState, ScopeStateFn> { - pub(crate) scope_fn: ScopeStateFn, - pub(crate) node: Node<'n, SubState>, +pub(crate) struct Scoper<'nodes, ScopedState, Scope> { + pub(crate) scope: Scope, + pub(crate) node: Node<'nodes, ScopedState>, +} + +impl Debug for Scoper<'_, ScopedState, Scope> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Scoper") + .field("scope", &"") + .field("node", &self.node) + .finish() + } } -/// Anonymous result to return from the closure passed to `nodes::scope` -/// See `nodes::scope` -pub struct ScopeCtxResult { - value: ResultValue, +impl NodeTrait for Scoper<'_, ScopedState, Scope> +where + Scope: Fn(&mut State) -> &mut ScopedState, +{ + fn constraints(&mut self, available_area: Area, state: &mut State) -> Option { + let substate = (self.scope)(state); + self.node.inner.constraints(available_area, substate) + } + + fn layout( + &mut self, + available_area: Area, + contextual_x_align: Option, + contextual_y_align: Option, + state: &mut State, + ) { + let substate = (self.scope)(state); + self.node.inner.layout( + available_area, + contextual_x_align, + contextual_y_align, + substate, + ); + } + + fn draw(&mut self, state: &mut State, contextual_visibility: bool) { + let substate = (self.scope)(state); + self.node.inner.draw(substate, contextual_visibility); + } } -enum ResultValue { - Void, - Constraints(Option), +pub(crate) struct OptionScoper<'nodes, ScopedState, Scope> { + pub(crate) scope: Scope, + pub(crate) node: Node<'nodes, ScopedState>, } -impl Debug for Scoper<'_, SubState, ScopeStateFn> { +impl Debug for OptionScoper<'_, ScopedState, Scope> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Scoper") - .field("scope_fn", &"") + f.debug_struct("OptionScoper") + .field("scope", &"") .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 NodeTrait for OptionScoper<'_, ScopedState, Scope> +where + Scope: Fn(&mut State) -> &mut Option, +{ + fn constraints(&mut self, available_area: Area, state: &mut State) -> Option { + if let Some(substate) = (self.scope)(state) { + self.node.inner.constraints(available_area, substate) + } else { + None + } + } -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, - ), + fn layout( + &mut self, + available_area: Area, + contextual_x_align: Option, + contextual_y_align: Option, + state: &mut State, + ) { + if let Some(substate) = (self.scope)(state) { + self.node.inner.layout( + available_area, + contextual_x_align, + contextual_y_align, + substate, + ) } } - /// 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, + + fn draw(&mut self, state: &mut State, contextual_visibility: bool) { + if let Some(substate) = (self.scope)(state) { + self.node.inner.draw(substate, contextual_visibility) } } } -impl<'nodes, State, SubState, ScopeStateFn> NodeTrait - for Scoper<'nodes, SubState, ScopeStateFn> +pub(crate) struct OwnedScoper<'nodes, ScopedState, Scope> { + pub(crate) scope: Scope, + pub(crate) node: Node<'nodes, ScopedState>, +} + +impl Debug for OwnedScoper<'_, ScopedState, Scope> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("OwnedScoper") + .field("scope", &"") + .field("node", &self.node) + .finish() + } +} + +impl NodeTrait for OwnedScoper<'_, ScopedState, Scope> where - ScopeStateFn: Fn(ScopeCtx<'_, 'nodes, SubState>, &mut State) -> ScopeCtxResult, + Scope: Fn(&mut State) -> ScopedState, { 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 + let mut substate = (self.scope)(state); + self.node.inner.constraints(available_area, &mut substate) } fn layout( @@ -113,57 +131,17 @@ where 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; - }; + let mut substate = (self.scope)(state); + self.node.inner.layout( + available_area, + contextual_x_align, + contextual_y_align, + &mut substate, + ); } 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; - }; + let mut substate = (self.scope)(state); + self.node.inner.draw(&mut substate, contextual_visibility); } } diff --git a/src/tests/scope_tests.rs b/src/tests/scope_tests.rs index 4d428ef..6c137af 100644 --- a/src/tests/scope_tests.rs +++ b/src/tests/scope_tests.rs @@ -1,10 +1,8 @@ #[cfg(test)] mod tests { - use crate::layout::*; use crate::models::*; use crate::nodes::*; - use crate::scoper::ScopeCtx; #[test] fn test_scope() { @@ -37,7 +35,7 @@ mod tests { } }, scope( - |ctx: ScopeCtx, a: &mut A| ctx.with_scoped(&mut a.b), + |a: &mut A| &mut a.b, dynamic(|b: &mut B| { if b.test { draw(|area, b: &mut B| { @@ -73,19 +71,13 @@ mod tests { } Layout::new(stack(vec![ scope( - |ctx: ScopeCtx, a: &mut A| { - //> - ctx.with_scoped(&mut a.b) - }, + |a: &mut A| &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) - }, + |a: &mut A| &mut a.c, draw(|area, _state: &mut C| { assert_eq!(area, Area::new(0., 0., 100., 100.)); }), @@ -105,16 +97,7 @@ mod tests { 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), - ), + scope_unwrap(|a: &mut A| &mut a.b, draw(|_, b: &mut B| b.test = !b.test)), ]) }); let mut state = A { @@ -141,10 +124,10 @@ mod tests { // let mut oneone = A { b: B }; // Layout::new(stack(vec![ // //> - // scope( - // |ctx: ScopeCtx, a: &mut One| { + // scope_owned( + // |a: &mut One| { // //> - // ctx.with_scoped(&mut (&mut a.0.b, a.1)) + // (&mut a.0.b, a.1.borrow_mut()) // }, // space(), // ), From 1351c6617bce699004d25e0ecdd0751d5c00e5ba Mon Sep 17 00:00:00 2001 From: ejjonny Date: Sat, 1 Feb 2025 17:53:14 -0700 Subject: [PATCH 02/16] tests --- src/nodes.rs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/nodes.rs b/src/nodes.rs index aef4983..61cb37b 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -197,22 +197,13 @@ pub fn dynamic<'nodes, State>( /// use backer::nodes::*; /// /// struct A { -/// b: Option, +/// b: bool, /// } /// 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) -/// }, +/// // This closure selects which state to scope to +/// |a: &mut A| &mut a.b, /// // These nodes now have direct access to only the boolean /// draw(|_, b: &mut bool| *b = !*b), /// ), From 35606ec6589e025cb11d7c926104cc099353395b Mon Sep 17 00:00:00 2001 From: ejjonny Date: Sat, 1 Feb 2025 17:54:04 -0700 Subject: [PATCH 03/16] snap --- ...ker__tests__public_api_test__api_full.snap | 59 ++----------------- 1 file changed, 6 insertions(+), 53 deletions(-) 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 index 72c1658..40dfafd 100644 --- a/src/tests/snapshots/backer__tests__public_api_test__api_full.snap +++ b/src/tests/snapshots/backer__tests__public_api_test__api_full.snap @@ -103,17 +103,19 @@ pub fn backer::nodes::area_reader<'nodes, State>(func: impl core::ops::function: 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<'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::draw_object<'nodes, State>(drawable: impl backer::traits::Drawable + 'nodes) -> backer::Node<'nodes, State> +pub fn backer::nodes::dynamic<'nodes, State>(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::scope<'nodes, State, Scoped: 'nodes>(scope: impl core::ops::function::Fn(&mut State) -> &mut Scoped + 'nodes, node: backer::Node<'nodes, Scoped>) -> backer::Node<'nodes, State> +pub fn backer::nodes::scope_owned<'nodes, State, Scoped: 'nodes>(scope: impl core::ops::function::Fn(&mut State) -> Scoped + 'nodes, node: backer::Node<'nodes, Scoped>) -> backer::Node<'nodes, State> +pub fn backer::nodes::scope_unwrap<'nodes, State, Scoped: 'nodes>(scope: impl core::ops::function::Fn(&mut State) -> &mut core::option::Option + '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 trait backer::traits::Drawable<'nodes, State> +pub trait backer::traits::Drawable pub fn backer::traits::Drawable::draw(&mut self, area: backer::models::Area, state: &mut State, visible: bool) pub struct backer::Layout<'nodes, State> impl<'nodes, State> backer::Layout<'nodes, State> @@ -194,52 +196,3 @@ impl core::borrow::BorrowMut for backer::Node<'nodes, State> where T: ?cor 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 From b6524670cc287b51375e07919c95ef9863269458 Mon Sep 17 00:00:00 2001 From: ejjonny Date: Sat, 1 Feb 2025 17:56:31 -0700 Subject: [PATCH 04/16] fix examples up --- Cargo.toml | 3 ++- examples/egui-example/src/main.rs | 32 ++++++++++++++------------ examples/macroquad-example/src/main.rs | 5 +--- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 54392b4..15c3449 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,9 @@ authors = ["cyypherus"] crate-type = ["lib"] [features] -default = [] +default = ["examples"] test-api = [] +examples = ["macroquad-examples", "egui-examples"] macroquad-examples = ["macroquad"] egui-examples = ["egui", "eframe", "egui_extras"] diff --git a/examples/egui-example/src/main.rs b/examples/egui-example/src/main.rs index 19c680e..5e8b038 100644 --- a/examples/egui-example/src/main.rs +++ b/examples/egui-example/src/main.rs @@ -22,21 +22,23 @@ fn main() -> eframe::Result { } fn my_layout_fn<'n>() -> Node<'n, Ui> { - column_spaced( - 10., - vec![ - draw_a(ui), - row_spaced( - 10., - vec![ - draw_b(ui).width_range(200.0..), - column_spaced(10., vec![draw_a(ui), draw_b(ui), draw_c(ui)]), - ], - ), - draw_c(ui), - ], - ) - .pad(10.) + dynamic(|ui| { + column_spaced( + 10., + vec![ + draw_a(ui), + row_spaced( + 10., + vec![ + draw_b(ui).width_range(200.0..), + column_spaced(10., vec![draw_a(ui), draw_b(ui), draw_c(ui)]), + ], + ), + draw_c(ui), + ], + ) + .pad(10.) + }) } fn draw_a<'n>(ui: &mut Ui) -> Node<'n, Ui> { diff --git a/examples/macroquad-example/src/main.rs b/examples/macroquad-example/src/main.rs index c45f348..fc5a1a1 100644 --- a/examples/macroquad-example/src/main.rs +++ b/examples/macroquad-example/src/main.rs @@ -2,7 +2,6 @@ use backer::models::*; use backer::nodes::*; use backer::Layout; use backer::Node; -use backer::ScopeCtx; use macroquad::prelude::*; use macroquad::ui::root_ui; use macroquad::ui::widgets; @@ -50,9 +49,7 @@ fn layout_for_highlight<'n>() -> Node<'n, State> { || highlight == HighlightedCase::None { scope( - |ctx: ScopeCtx, state: &mut State| { - ctx.with_scoped(&mut state.highlight) - }, + |state: &mut State| &mut state.highlight, rel_abs_seq(highlight), ) } else { From 07beb07aa03bc57f6d19bdbc7d90e3806948f208 Mon Sep 17 00:00:00 2001 From: ejjonny Date: Sat, 1 Feb 2025 17:56:57 -0700 Subject: [PATCH 05/16] toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 15c3449..0331224 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ authors = ["cyypherus"] crate-type = ["lib"] [features] -default = ["examples"] +default = [] test-api = [] examples = ["macroquad-examples", "egui-examples"] macroquad-examples = ["macroquad"] From 1f0ab206cf3a0386c409d27927f56ef2039cd29f Mon Sep 17 00:00:00 2001 From: ejjonny Date: Sat, 1 Feb 2025 18:00:18 -0700 Subject: [PATCH 06/16] snap --- ...r__tests__public_api_test__api_simplified.snap | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) 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 index 23eb8a3..7b918ee 100644 --- a/src/tests/snapshots/backer__tests__public_api_test__api_simplified.snap +++ b/src/tests/snapshots/backer__tests__public_api_test__api_simplified.snap @@ -32,16 +32,20 @@ pub fn backer::nodes::area_reader<'nodes, State>(func: impl core::ops::function: 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::draw_object<'nodes, State>(drawable: impl backer::traits::Drawable + 'nodes) -> backer::Node<'nodes, State> +pub fn backer::nodes::dynamic<'nodes, State>(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::scope<'nodes, State, Scoped: 'nodes>(scope: impl core::ops::function::Fn(&mut State) -> &mut Scoped + 'nodes, node: backer::Node<'nodes, Scoped>) -> backer::Node<'nodes, State> +pub fn backer::nodes::scope_owned<'nodes, State, Scoped: 'nodes>(scope: impl core::ops::function::Fn(&mut State) -> Scoped + 'nodes, node: backer::Node<'nodes, Scoped>) -> backer::Node<'nodes, State> +pub fn backer::nodes::scope_unwrap<'nodes, State, Scoped: 'nodes>(scope: impl core::ops::function::Fn(&mut State) -> &mut core::option::Option + '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 trait backer::traits::Drawable +pub fn backer::traits::Drawable::draw(&mut self, area: backer::models::Area, state: &mut State, visible: bool) 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 @@ -77,8 +81,3 @@ pub fn backer::Node<'_, State>::dynamic_height(self, f: impl core::ops::function 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 From 27fdcca87bd154c9bc2d3d7773eff8e5d7919490 Mon Sep 17 00:00:00 2001 From: ejjonny Date: Sat, 1 Feb 2025 18:07:29 -0700 Subject: [PATCH 07/16] embed owned --- src/nodes.rs | 6 +++++- src/scoper.rs | 15 +++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/nodes.rs b/src/nodes.rs index 61cb37b..8744092 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -233,14 +233,18 @@ pub fn scope_unwrap<'nodes, State, Scoped: 'nodes>( } } /// Scopes state to some owned derivative for all children of this node +/// once the child nodes have operated on state, embed is then called. +/// +/// The scope & embed functions are generally called multiple times in a single `draw` call, use them sparingly /// See `nodes::scope` pub fn scope_owned<'nodes, State, Scoped: 'nodes>( scope: impl Fn(&mut State) -> Scoped + 'nodes, + embed: impl Fn(&mut State, Scoped) + 'nodes, node: Node<'nodes, Scoped>, ) -> Node<'nodes, State> { Node { inner: NodeValue::NodeTrait { - node: Box::new(OwnedScoper { scope, node }), + node: Box::new(OwnedScoper { scope, embed, node }), }, } } diff --git a/src/scoper.rs b/src/scoper.rs index 96d0eeb..b3c52c8 100644 --- a/src/scoper.rs +++ b/src/scoper.rs @@ -101,12 +101,13 @@ where } } -pub(crate) struct OwnedScoper<'nodes, ScopedState, Scope> { +pub(crate) struct OwnedScoper<'nodes, ScopedState, Scope, Embed> { pub(crate) scope: Scope, + pub(crate) embed: Embed, pub(crate) node: Node<'nodes, ScopedState>, } -impl Debug for OwnedScoper<'_, ScopedState, Scope> { +impl Debug for OwnedScoper<'_, ScopedState, Scope, Embed> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("OwnedScoper") .field("scope", &"") @@ -115,13 +116,17 @@ impl Debug for OwnedScoper<'_, ScopedState, Scope> { } } -impl NodeTrait for OwnedScoper<'_, ScopedState, Scope> +impl NodeTrait + for OwnedScoper<'_, ScopedState, Scope, Embed> where Scope: Fn(&mut State) -> ScopedState, + Embed: Fn(&mut State, ScopedState), { fn constraints(&mut self, available_area: Area, state: &mut State) -> Option { let mut substate = (self.scope)(state); - self.node.inner.constraints(available_area, &mut substate) + let result = self.node.inner.constraints(available_area, &mut substate); + (self.embed)(state, substate); + result } fn layout( @@ -138,10 +143,12 @@ where contextual_y_align, &mut substate, ); + (self.embed)(state, substate); } fn draw(&mut self, state: &mut State, contextual_visibility: bool) { let mut substate = (self.scope)(state); self.node.inner.draw(&mut substate, contextual_visibility); + (self.embed)(state, substate); } } From 735379cdf1465af60a26d653fe8f89770792b812 Mon Sep 17 00:00:00 2001 From: ejjonny Date: Sat, 1 Feb 2025 18:16:21 -0700 Subject: [PATCH 08/16] snap --- .../snapshots/backer__tests__public_api_test__api_full.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 40dfafd..239e59b 100644 --- a/src/tests/snapshots/backer__tests__public_api_test__api_full.snap +++ b/src/tests/snapshots/backer__tests__public_api_test__api_full.snap @@ -110,7 +110,7 @@ pub fn backer::nodes::group(elements: alloc::vec::Vec(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(&mut State) -> &mut Scoped + 'nodes, node: backer::Node<'nodes, Scoped>) -> backer::Node<'nodes, State> -pub fn backer::nodes::scope_owned<'nodes, State, Scoped: 'nodes>(scope: impl core::ops::function::Fn(&mut State) -> Scoped + 'nodes, node: backer::Node<'nodes, Scoped>) -> backer::Node<'nodes, State> +pub fn backer::nodes::scope_owned<'nodes, State, Scoped: 'nodes>(scope: impl core::ops::function::Fn(&mut State) -> Scoped + 'nodes, embed: impl core::ops::function::Fn(&mut State, Scoped) + 'nodes, node: backer::Node<'nodes, Scoped>) -> backer::Node<'nodes, State> pub fn backer::nodes::scope_unwrap<'nodes, State, Scoped: 'nodes>(scope: impl core::ops::function::Fn(&mut State) -> &mut core::option::Option + '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> From 4fc7f64814f3dfbaf4035ad29b29fa14c084125f Mon Sep 17 00:00:00 2001 From: ejjonny Date: Sat, 1 Feb 2025 18:18:40 -0700 Subject: [PATCH 09/16] snap --- .../backer__tests__public_api_test__api_simplified.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 7b918ee..8bafaf1 100644 --- a/src/tests/snapshots/backer__tests__public_api_test__api_simplified.snap +++ b/src/tests/snapshots/backer__tests__public_api_test__api_simplified.snap @@ -39,7 +39,7 @@ pub fn backer::nodes::group(elements: alloc::vec::Vec(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(&mut State) -> &mut Scoped + 'nodes, node: backer::Node<'nodes, Scoped>) -> backer::Node<'nodes, State> -pub fn backer::nodes::scope_owned<'nodes, State, Scoped: 'nodes>(scope: impl core::ops::function::Fn(&mut State) -> Scoped + 'nodes, node: backer::Node<'nodes, Scoped>) -> backer::Node<'nodes, State> +pub fn backer::nodes::scope_owned<'nodes, State, Scoped: 'nodes>(scope: impl core::ops::function::Fn(&mut State) -> Scoped + 'nodes, embed: impl core::ops::function::Fn(&mut State, Scoped) + 'nodes, node: backer::Node<'nodes, Scoped>) -> backer::Node<'nodes, State> pub fn backer::nodes::scope_unwrap<'nodes, State, Scoped: 'nodes>(scope: impl core::ops::function::Fn(&mut State) -> &mut core::option::Option + '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> From 066461f30d9481ac2653921c457114c4193e0179 Mon Sep 17 00:00:00 2001 From: cyypherus Date: Mon, 3 Feb 2025 12:38:31 -0700 Subject: [PATCH 10/16] into into into --- src/nodes.rs | 63 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/src/nodes.rs b/src/nodes.rs index 8744092..ab328a8 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -25,10 +25,10 @@ or pushing against other unconstrained nodes with equal force. /// Creates a vertical sequence of elements /// #[doc = container_doc!()] -pub fn column(elements: Vec>) -> Node<'_, State> { +pub fn column<'n, State>(elements: Vec>>) -> Node<'n, State> { Node { inner: NodeValue::Column { - elements: filter_empty(ungroup(elements)), + elements: filter_empty(ungroup(convert_into(elements))), spacing: 0., align: None, off_axis_align: None, @@ -53,18 +53,21 @@ pub fn column(elements: Vec>) -> Node<'_, State> { /// ), /// ]); /// ``` -pub fn group(elements: Vec>) -> Node<'_, State> { +pub fn group<'n, State>(elements: Vec>>) -> Node<'n, State> { Node { - inner: NodeValue::Group(filter_empty(ungroup(elements))), + inner: NodeValue::Group(filter_empty(ungroup(convert_into(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>) -> Node<'_, State> { +pub fn column_spaced<'n, State>( + spacing: f32, + elements: Vec>>, +) -> Node<'n, State> { Node { inner: NodeValue::Column { - elements: filter_empty(ungroup(elements)), + elements: filter_empty(ungroup(convert_into(elements))), spacing, align: None, off_axis_align: None, @@ -74,10 +77,10 @@ pub fn column_spaced(spacing: f32, elements: Vec>) -> Node<'_ /// Creates a horizontal sequence of elements /// #[doc = container_doc!()] -pub fn row(elements: Vec>) -> Node<'_, State> { +pub fn row<'n, State>(elements: Vec>>) -> Node<'n, State> { Node { inner: NodeValue::Row { - elements: filter_empty(ungroup(elements)), + elements: filter_empty(ungroup(convert_into(elements))), spacing: 0., align: None, off_axis_align: None, @@ -87,10 +90,13 @@ pub fn row(elements: Vec>) -> Node<'_, State> { /// Creates a horizontal sequence of elements with the specified spacing between each element. /// #[doc = container_doc!()] -pub fn row_spaced(spacing: f32, elements: Vec>) -> Node<'_, State> { +pub fn row_spaced<'n, State>( + spacing: f32, + elements: Vec>>, +) -> Node<'n, State> { Node { inner: NodeValue::Row { - elements: filter_empty(ungroup(elements)), + elements: filter_empty(ungroup(convert_into(elements))), spacing, align: None, off_axis_align: None, @@ -100,10 +106,10 @@ pub fn row_spaced(spacing: f32, elements: Vec>) -> Node<'_, S /// Creates a sequence of elements to be laid out on top of each other. /// #[doc = container_doc!()] -pub fn stack(elements: Vec>) -> Node<'_, State> { +pub fn stack<'n, State>(elements: Vec>>) -> Node<'n, State> { Node { inner: NodeValue::Stack { - elements: filter_empty(ungroup(elements)), + elements: filter_empty(ungroup(convert_into(elements))), x_align: None, y_align: None, }, @@ -140,11 +146,14 @@ pub fn draw<'nodes, State>( /// (or the `TransitionDrawable` trait) /// /// See [`draw`] -pub fn draw_object<'nodes, State>(drawable: impl Drawable + 'nodes) -> Node<'nodes, State> { +pub fn draw_object<'nodes, State, D>(drawable: impl Into) -> Node<'nodes, State> +where + D: Drawable + 'nodes, +{ Node { inner: NodeValue::Draw(DrawableNode { area: Area::default(), - drawable: SomeDrawable::Object(Box::new(drawable)), + drawable: SomeDrawable::Object(Box::new(drawable.into())), }), } } @@ -212,11 +221,14 @@ pub fn dynamic<'nodes, State>( ///``` pub fn scope<'nodes, State, Scoped: 'nodes>( scope: impl Fn(&mut State) -> &mut Scoped + 'nodes, - node: Node<'nodes, Scoped>, + node: impl Into>, ) -> Node<'nodes, State> { Node { inner: NodeValue::NodeTrait { - node: Box::new(Scoper { scope, node }), + node: Box::new(Scoper { + scope, + node: node.into(), + }), }, } } @@ -224,11 +236,14 @@ pub fn scope<'nodes, State, Scoped: 'nodes>( /// See `nodes::scope` pub fn scope_unwrap<'nodes, State, Scoped: 'nodes>( scope: impl Fn(&mut State) -> &mut Option + 'nodes, - node: Node<'nodes, Scoped>, + node: impl Into>, ) -> Node<'nodes, State> { Node { inner: NodeValue::NodeTrait { - node: Box::new(OptionScoper { scope, node }), + node: Box::new(OptionScoper { + scope, + node: node.into(), + }), }, } } @@ -240,15 +255,23 @@ pub fn scope_unwrap<'nodes, State, Scoped: 'nodes>( pub fn scope_owned<'nodes, State, Scoped: 'nodes>( scope: impl Fn(&mut State) -> Scoped + 'nodes, embed: impl Fn(&mut State, Scoped) + 'nodes, - node: Node<'nodes, Scoped>, + node: impl Into>, ) -> Node<'nodes, State> { Node { inner: NodeValue::NodeTrait { - node: Box::new(OwnedScoper { scope, embed, node }), + node: Box::new(OwnedScoper { + scope, + embed, + node: node.into(), + }), }, } } +fn convert_into<'n, State>(elements: Vec>>) -> Vec> { + elements.into_iter().map(|e| e.into()).collect() +} + fn ungroup(elements: Vec>) -> Vec> { elements .into_iter() From 1680537f1a9914a47c4710471bd6dc023f33fc85 Mon Sep 17 00:00:00 2001 From: cyypherus Date: Mon, 3 Feb 2025 12:40:56 -0700 Subject: [PATCH 11/16] snap --- ...ker__tests__public_api_test__api_full.snap | 20 +++++++++---------- ...ests__public_api_test__api_simplified.snap | 20 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) 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 index 239e59b..6493a2b 100644 --- a/src/tests/snapshots/backer__tests__public_api_test__api_full.snap +++ b/src/tests/snapshots/backer__tests__public_api_test__api_full.snap @@ -100,20 +100,20 @@ 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::column<'n, State>(elements: alloc::vec::Vec>>) -> backer::Node<'n, State> +pub fn backer::nodes::column_spaced<'n, State>(spacing: f32, elements: alloc::vec::Vec>>) -> backer::Node<'n, 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 + 'nodes) -> backer::Node<'nodes, State> +pub fn backer::nodes::draw_object<'nodes, State, D>(drawable: impl core::convert::Into) -> backer::Node<'nodes, State> where D: backer::traits::Drawable + 'nodes pub fn backer::nodes::dynamic<'nodes, State>(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(&mut State) -> &mut Scoped + 'nodes, node: backer::Node<'nodes, Scoped>) -> backer::Node<'nodes, State> -pub fn backer::nodes::scope_owned<'nodes, State, Scoped: 'nodes>(scope: impl core::ops::function::Fn(&mut State) -> Scoped + 'nodes, embed: impl core::ops::function::Fn(&mut State, Scoped) + 'nodes, node: backer::Node<'nodes, Scoped>) -> backer::Node<'nodes, State> -pub fn backer::nodes::scope_unwrap<'nodes, State, Scoped: 'nodes>(scope: impl core::ops::function::Fn(&mut State) -> &mut core::option::Option + 'nodes, node: backer::Node<'nodes, Scoped>) -> backer::Node<'nodes, State> +pub fn backer::nodes::group<'n, State>(elements: alloc::vec::Vec>>) -> backer::Node<'n, State> +pub fn backer::nodes::row<'n, State>(elements: alloc::vec::Vec>>) -> backer::Node<'n, State> +pub fn backer::nodes::row_spaced<'n, State>(spacing: f32, elements: alloc::vec::Vec>>) -> backer::Node<'n, State> +pub fn backer::nodes::scope<'nodes, State, Scoped: 'nodes>(scope: impl core::ops::function::Fn(&mut State) -> &mut Scoped + 'nodes, node: impl core::convert::Into>) -> backer::Node<'nodes, State> +pub fn backer::nodes::scope_owned<'nodes, State, Scoped: 'nodes>(scope: impl core::ops::function::Fn(&mut State) -> Scoped + 'nodes, embed: impl core::ops::function::Fn(&mut State, Scoped) + 'nodes, node: impl core::convert::Into>) -> backer::Node<'nodes, State> +pub fn backer::nodes::scope_unwrap<'nodes, State, Scoped: 'nodes>(scope: impl core::ops::function::Fn(&mut State) -> &mut core::option::Option + 'nodes, node: impl core::convert::Into>) -> 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 fn backer::nodes::stack<'n, State>(elements: alloc::vec::Vec>>) -> backer::Node<'n, State> pub mod backer::traits pub trait backer::traits::Drawable pub fn backer::traits::Drawable::draw(&mut self, area: backer::models::Area, state: &mut State, visible: bool) 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 index 8bafaf1..b169754 100644 --- a/src/tests/snapshots/backer__tests__public_api_test__api_simplified.snap +++ b/src/tests/snapshots/backer__tests__public_api_test__api_simplified.snap @@ -29,20 +29,20 @@ 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::column<'n, State>(elements: alloc::vec::Vec>>) -> backer::Node<'n, State> +pub fn backer::nodes::column_spaced<'n, State>(spacing: f32, elements: alloc::vec::Vec>>) -> backer::Node<'n, 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 + 'nodes) -> backer::Node<'nodes, State> +pub fn backer::nodes::draw_object<'nodes, State, D>(drawable: impl core::convert::Into) -> backer::Node<'nodes, State> where D: backer::traits::Drawable + 'nodes pub fn backer::nodes::dynamic<'nodes, State>(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(&mut State) -> &mut Scoped + 'nodes, node: backer::Node<'nodes, Scoped>) -> backer::Node<'nodes, State> -pub fn backer::nodes::scope_owned<'nodes, State, Scoped: 'nodes>(scope: impl core::ops::function::Fn(&mut State) -> Scoped + 'nodes, embed: impl core::ops::function::Fn(&mut State, Scoped) + 'nodes, node: backer::Node<'nodes, Scoped>) -> backer::Node<'nodes, State> -pub fn backer::nodes::scope_unwrap<'nodes, State, Scoped: 'nodes>(scope: impl core::ops::function::Fn(&mut State) -> &mut core::option::Option + 'nodes, node: backer::Node<'nodes, Scoped>) -> backer::Node<'nodes, State> +pub fn backer::nodes::group<'n, State>(elements: alloc::vec::Vec>>) -> backer::Node<'n, State> +pub fn backer::nodes::row<'n, State>(elements: alloc::vec::Vec>>) -> backer::Node<'n, State> +pub fn backer::nodes::row_spaced<'n, State>(spacing: f32, elements: alloc::vec::Vec>>) -> backer::Node<'n, State> +pub fn backer::nodes::scope<'nodes, State, Scoped: 'nodes>(scope: impl core::ops::function::Fn(&mut State) -> &mut Scoped + 'nodes, node: impl core::convert::Into>) -> backer::Node<'nodes, State> +pub fn backer::nodes::scope_owned<'nodes, State, Scoped: 'nodes>(scope: impl core::ops::function::Fn(&mut State) -> Scoped + 'nodes, embed: impl core::ops::function::Fn(&mut State, Scoped) + 'nodes, node: impl core::convert::Into>) -> backer::Node<'nodes, State> +pub fn backer::nodes::scope_unwrap<'nodes, State, Scoped: 'nodes>(scope: impl core::ops::function::Fn(&mut State) -> &mut core::option::Option + 'nodes, node: impl core::convert::Into>) -> 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 fn backer::nodes::stack<'n, State>(elements: alloc::vec::Vec>>) -> backer::Node<'n, State> pub mod backer::traits pub trait backer::traits::Drawable pub fn backer::traits::Drawable::draw(&mut self, area: backer::models::Area, state: &mut State, visible: bool) From 3f95c115c61ef989202332451ca55f872a003239 Mon Sep 17 00:00:00 2001 From: cyypherus Date: Mon, 3 Feb 2025 12:42:53 -0700 Subject: [PATCH 12/16] fix insta recommendation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 72c491b..50fef1f 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,6 @@ The crate is currently usable but new! Breaking changes may be relatively freque > If your PR changes the public API, one of the checks will fail by default. > If the changes to the public API were intentional you can update the snapshot by running: > -> `INSTA_UPDATE=always && cargo test --features test-api` +> `INSTA_UPDATE=always cargo test --features test-api` Contributions are always welcome 🤗 From 149ba534f03f2da2c9181d41973a6281b10fb9eb Mon Sep 17 00:00:00 2001 From: cyypherus Date: Mon, 3 Feb 2025 12:48:22 -0700 Subject: [PATCH 13/16] undo drawable into --- src/nodes.rs | 7 ++----- .../backer__tests__public_api_test__api_full.snap | 2 +- .../backer__tests__public_api_test__api_simplified.snap | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/nodes.rs b/src/nodes.rs index ab328a8..666ce74 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -146,14 +146,11 @@ pub fn draw<'nodes, State>( /// (or the `TransitionDrawable` trait) /// /// See [`draw`] -pub fn draw_object<'nodes, State, D>(drawable: impl Into) -> Node<'nodes, State> -where - D: Drawable + 'nodes, -{ +pub fn draw_object<'nodes, State>(drawable: impl Drawable + 'nodes) -> Node<'nodes, State> { Node { inner: NodeValue::Draw(DrawableNode { area: Area::default(), - drawable: SomeDrawable::Object(Box::new(drawable.into())), + drawable: SomeDrawable::Object(Box::new(drawable)), }), } } 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 index 6493a2b..0db1135 100644 --- a/src/tests/snapshots/backer__tests__public_api_test__api_full.snap +++ b/src/tests/snapshots/backer__tests__public_api_test__api_full.snap @@ -103,7 +103,7 @@ pub fn backer::nodes::area_reader<'nodes, State>(func: impl core::ops::function: pub fn backer::nodes::column<'n, State>(elements: alloc::vec::Vec>>) -> backer::Node<'n, State> pub fn backer::nodes::column_spaced<'n, State>(spacing: f32, elements: alloc::vec::Vec>>) -> backer::Node<'n, 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, D>(drawable: impl core::convert::Into) -> backer::Node<'nodes, State> where D: backer::traits::Drawable + 'nodes +pub fn backer::nodes::draw_object<'nodes, State>(drawable: impl backer::traits::Drawable + 'nodes) -> backer::Node<'nodes, State> pub fn backer::nodes::dynamic<'nodes, State>(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<'n, State>(elements: alloc::vec::Vec>>) -> backer::Node<'n, State> 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 index b169754..420cefa 100644 --- a/src/tests/snapshots/backer__tests__public_api_test__api_simplified.snap +++ b/src/tests/snapshots/backer__tests__public_api_test__api_simplified.snap @@ -32,7 +32,7 @@ pub fn backer::nodes::area_reader<'nodes, State>(func: impl core::ops::function: pub fn backer::nodes::column<'n, State>(elements: alloc::vec::Vec>>) -> backer::Node<'n, State> pub fn backer::nodes::column_spaced<'n, State>(spacing: f32, elements: alloc::vec::Vec>>) -> backer::Node<'n, 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, D>(drawable: impl core::convert::Into) -> backer::Node<'nodes, State> where D: backer::traits::Drawable + 'nodes +pub fn backer::nodes::draw_object<'nodes, State>(drawable: impl backer::traits::Drawable + 'nodes) -> backer::Node<'nodes, State> pub fn backer::nodes::dynamic<'nodes, State>(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<'n, State>(elements: alloc::vec::Vec>>) -> backer::Node<'n, State> From 37008c5b6750e8a18425129b7b4ad2de9b89251d Mon Sep 17 00:00:00 2001 From: cyypherus Date: Tue, 4 Feb 2025 15:30:13 -0700 Subject: [PATCH 14/16] dynamic attach test --- src/tests/layout_tests.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/tests/layout_tests.rs b/src/tests/layout_tests.rs index a26e322..555a81a 100644 --- a/src/tests/layout_tests.rs +++ b/src/tests/layout_tests.rs @@ -669,4 +669,22 @@ mod tests { }) .draw(Area::new(0., 0., 100., 100.), &mut ()); } + #[test] + fn test_dynamic_attached() { + Layout::new({ + row(vec![ + space(), + draw(|a, _: &mut ()| { + assert_eq!(a, Area::new(25., 25., 25., 50.)); + }) + .attach_under(draw(|a, _: &mut ()| { + assert_eq!(a, Area::new(25., 25., 25., 50.)); + })) + .dynamic_height(|h, _| h * 2.), + space(), + space(), + ]) + }) + .draw(Area::new(0., 0., 100., 100.), &mut ()); + } } From 65be5c1562391c22ef2aac534d1eddb75894c6ab Mon Sep 17 00:00:00 2001 From: cyypherus Date: Tue, 4 Feb 2025 15:54:19 -0700 Subject: [PATCH 15/16] coupled node refer to leader element for allocation --- src/layout.rs | 7 ++++++- src/tests/layout_tests.rs | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/layout.rs b/src/layout.rs index b7098e5..00c4aa3 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -275,10 +275,15 @@ impl NodeValue<'_, State> { NodeValue::Visibility { .. } => { vec![available_area] } + NodeValue::Coupled { element, .. } => element.kind.allocate_area( + available_area, + contextual_x_align, + contextual_y_align, + state, + ), NodeValue::Draw(_) | NodeValue::Space | NodeValue::AreaReader { .. } - | NodeValue::Coupled { .. } | NodeValue::NodeTrait { .. } | NodeValue::Dynamic { .. } => { vec![available_area] diff --git a/src/tests/layout_tests.rs b/src/tests/layout_tests.rs index 555a81a..c2fce13 100644 --- a/src/tests/layout_tests.rs +++ b/src/tests/layout_tests.rs @@ -677,10 +677,10 @@ mod tests { draw(|a, _: &mut ()| { assert_eq!(a, Area::new(25., 25., 25., 50.)); }) + .dynamic_height(|h, _| h * 2.) .attach_under(draw(|a, _: &mut ()| { assert_eq!(a, Area::new(25., 25., 25., 50.)); - })) - .dynamic_height(|h, _| h * 2.), + })), space(), space(), ]) From d0a3cee8d5eee550985526025b00304e789c582a Mon Sep 17 00:00:00 2001 From: cyypherus Date: Tue, 4 Feb 2025 16:38:35 -0700 Subject: [PATCH 16/16] nvm, constrain space with leader node --- src/layout.rs | 20 +++++++++++++------- src/tests/layout_tests.rs | 5 ++++- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/layout.rs b/src/layout.rs index 00c4aa3..b040951 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -275,16 +275,11 @@ impl NodeValue<'_, State> { NodeValue::Visibility { .. } => { vec![available_area] } - NodeValue::Coupled { element, .. } => element.kind.allocate_area( - available_area, - contextual_x_align, - contextual_y_align, - state, - ), NodeValue::Draw(_) | NodeValue::Space | NodeValue::AreaReader { .. } | NodeValue::NodeTrait { .. } + | NodeValue::Coupled { .. } | NodeValue::Dynamic { .. } => { vec![available_area] } @@ -351,7 +346,18 @@ impl NodeValue<'_, State> { element, coupled, .. } => { element.layout(allocated[0], None, None, state); - coupled.layout(allocated[0], None, None, state); + coupled.layout( + available_area.constrained( + &element + .constraints(available_area, state) + .unwrap_or_default(), + contextual_x_align.unwrap_or(XAlign::Center), + contextual_y_align.unwrap_or(YAlign::Center), + ), + None, + None, + state, + ); } NodeValue::Visibility { element, .. } => { element.layout(allocated[0], None, None, state); diff --git a/src/tests/layout_tests.rs b/src/tests/layout_tests.rs index c2fce13..ef0f122 100644 --- a/src/tests/layout_tests.rs +++ b/src/tests/layout_tests.rs @@ -615,9 +615,12 @@ mod tests { .width_range(20.0..) .pad(0.) .attach_under(draw(|a, _: &mut ()| { - assert_eq!(a, Area::new(45., 0., 10., 100.)); + assert_eq!(a, Area::new(40., 0., 20., 100.)); })) .width_range(..10.) + .attach_under(draw(|a, _: &mut ()| { + assert_eq!(a, Area::new(45., 0., 10., 100.)); + })) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); }