Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scoping Feature #55

Merged
merged 45 commits into from
Oct 16, 2024
Merged
Changes from 1 commit
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
6c5071e
scope improvements
cyypherus Oct 1, 2024
da780e8
scope test
cyypherus Oct 3, 2024
c8cdb11
scope test
cyypherus Oct 3, 2024
e627490
yapping
cyypherus Oct 4, 2024
ff22cd2
yapping
cyypherus Oct 4, 2024
03d1e30
wip multiple generics
cyypherus Oct 8, 2024
ea41412
working?
cyypherus Oct 8, 2024
d8cc455
fixes
cyypherus Oct 8, 2024
22b3a83
doc
cyypherus Oct 8, 2024
56db748
test
cyypherus Oct 8, 2024
a290598
public api workaround
cyypherus Oct 14, 2024
9a1460d
working?
cyypherus Oct 14, 2024
95c77bb
polish, working!!!!!!!
cyypherus Oct 14, 2024
6c6a1c0
polish, working!!!!!!!
cyypherus Oct 14, 2024
f549374
swap &mut T for T: Copy for max flexibility
cyypherus Oct 14, 2024
87d8667
swap &mut T for T: Copy for max flexibility
cyypherus Oct 14, 2024
db4c8ff
checkpoint
cyypherus Oct 14, 2024
5ee5b84
copy > &mut
cyypherus Oct 15, 2024
7ba7be2
associated type
cyypherus Oct 15, 2024
7a1307e
cleanup
cyypherus Oct 15, 2024
cba54a8
merge & cleanup
cyypherus Oct 15, 2024
05c1d57
rename generics
cyypherus Oct 15, 2024
c35c241
doc examples
cyypherus Oct 15, 2024
6c3555e
scope test
cyypherus Oct 15, 2024
1f5fc04
warnings
cyypherus Oct 15, 2024
6349436
remove anynode, nice
cyypherus Oct 15, 2024
eb55807
cleanup
cyypherus Oct 15, 2024
0437e83
cleanup
cyypherus Oct 15, 2024
d7424e2
fixup macroquad example
cyypherus Oct 15, 2024
a156412
fixup egui example
cyypherus Oct 15, 2024
b58b129
fixup egui case study example
cyypherus Oct 15, 2024
3e069df
fixup demo site
cyypherus Oct 15, 2024
cacbceb
associated type -> generic for multiple scope paths
cyypherus Oct 15, 2024
7277b14
associated type -> generic for multiple scope paths
cyypherus Oct 15, 2024
97d607f
if let scope test wip
cyypherus Oct 15, 2024
7811fdc
if let scoping, cleanup
cyypherus Oct 15, 2024
3368a08
rename generics
cyypherus Oct 15, 2024
cc3194c
organizing
cyypherus Oct 16, 2024
0b2a9a6
organizing
cyypherus Oct 16, 2024
f015679
remove public-api workaround
cyypherus Oct 16, 2024
440853e
make scopable independent of state bc orphan rules
cyypherus Oct 16, 2024
52044b1
nicer method signature
cyypherus Oct 16, 2024
ec42776
update examples
cyypherus Oct 16, 2024
3c8f478
docs
cyypherus Oct 16, 2024
470ee86
docs
cyypherus Oct 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fixes
cyypherus committed Oct 8, 2024
commit d8cc455e0c5ae14fe2091c8020f4775ac8aa119e
10 changes: 5 additions & 5 deletions src/layout.rs
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ use crate::{
constraints::{Constraint, SizeConstraints},
drawable::Drawable,
models::*,
Node,
NodeWith,
};
use core::f32;
use std::{any::Any, rc::Rc};
@@ -48,11 +48,11 @@ pub struct Layout<A, B> {
tree: LayoutFn<A, B>,
}

pub type LayoutFn<A, B> = Box<dyn Fn(&mut A, &mut B) -> Node<A, B>>;
pub type LayoutFn<A, B> = Box<dyn Fn(&mut A, &mut B) -> NodeWith<A, B>>;

impl<A, B> Layout<A, B> {
/// Creates a new [`Layout<A, B>`].
pub fn new_with(tree: impl Fn(&mut A, &mut B) -> Node<A, B> + 'static) -> Self {
pub fn new_with(tree: impl Fn(&mut A, &mut B) -> NodeWith<A, B> + 'static) -> Self {
Self {
tree: Box::new(tree),
}
@@ -61,7 +61,7 @@ impl<A, B> Layout<A, B> {

impl<A> Layout<A, ()> {
/// Creates a new [`Layout<A, B>`].
pub fn new(tree: impl Fn(&mut A) -> Node<A, ()> + 'static) -> Self {
pub fn new(tree: impl Fn(&mut A) -> NodeWith<A, ()> + 'static) -> Self {
Self {
tree: Box::new(move |a, _| tree(a)),
}
@@ -87,7 +87,7 @@ impl<A, B> Layout<A, B> {
}
}

type AreaReaderFn<A, B> = Rc<dyn Fn(Area, &mut A, &mut B) -> Node<A, B>>;
type AreaReaderFn<A, B> = Rc<dyn Fn(Area, &mut A, &mut B) -> NodeWith<A, B>>;
type ScopeAStateFn<A> = Rc<dyn Fn(&mut A) -> &mut dyn Any>;
type ScopeBStateFn<A> = Rc<dyn Fn(&mut A) -> &mut dyn Any>;
type ScopeNodeFn<A, B> = Rc<dyn Fn(&mut dyn Any, &mut dyn Any) -> AnyNode<A, B>>;
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -25,6 +25,7 @@ mod tests;

pub use layout::Layout;
pub use node::Node;
pub use node::NodeWith;

/// Structs involved in layout definitions
pub mod models;
52 changes: 26 additions & 26 deletions src/modifiers.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{layout::NodeValue, models::*, Node};
use crate::{layout::NodeValue, models::*, NodeWith};
use std::{ops::RangeBounds, rc::Rc};

impl<A> Node<A, ()> {
impl<A> NodeWith<A, ()> {
/// 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.
@@ -28,10 +28,10 @@ impl<A> Node<A, ()> {
}
}

impl<A, B> Node<A, B> {
impl<A, B> NodeWith<A, B> {
/// Adds padding to the node along the leading edge
pub fn pad_leading(self, amount: f32) -> Node<A, B> {
Node {
pub fn pad_leading(self, amount: f32) -> NodeWith<A, B> {
NodeWith {
inner: NodeValue::Padding {
amounts: Padding {
leading: amount,
@@ -44,8 +44,8 @@ impl<A, B> Node<A, B> {
}
}
/// Adds horizontal padding to the node (leading & trailing)
pub fn pad_x(self, amount: f32) -> Node<A, B> {
Node {
pub fn pad_x(self, amount: f32) -> NodeWith<A, B> {
NodeWith {
inner: NodeValue::Padding {
amounts: Padding {
leading: amount,
@@ -58,8 +58,8 @@ impl<A, B> Node<A, B> {
}
}
/// Adds padding to the node along the trailing edge
pub fn pad_trailing(self, amount: f32) -> Node<A, B> {
Node {
pub fn pad_trailing(self, amount: f32) -> NodeWith<A, B> {
NodeWith {
inner: NodeValue::Padding {
amounts: Padding {
leading: 0.,
@@ -72,8 +72,8 @@ impl<A, B> Node<A, B> {
}
}
/// Adds padding to the node along the top edge
pub fn pad_top(self, amount: f32) -> Node<A, B> {
Node {
pub fn pad_top(self, amount: f32) -> NodeWith<A, B> {
NodeWith {
inner: NodeValue::Padding {
amounts: Padding {
leading: 0.,
@@ -87,8 +87,8 @@ impl<A, B> Node<A, B> {
}

/// Adds vertical padding to the node (top & bottom)
pub fn pad_y(self, amount: f32) -> Node<A, B> {
Node {
pub fn pad_y(self, amount: f32) -> NodeWith<A, B> {
NodeWith {
inner: NodeValue::Padding {
amounts: Padding {
leading: 0.,
@@ -101,8 +101,8 @@ impl<A, B> Node<A, B> {
}
}
/// Adds padding to the node along the bottom edge
pub fn pad_bottom(self, amount: f32) -> Node<A, B> {
Node {
pub fn pad_bottom(self, amount: f32) -> NodeWith<A, B> {
NodeWith {
inner: NodeValue::Padding {
amounts: Padding {
leading: 0.,
@@ -115,8 +115,8 @@ impl<A, B> Node<A, B> {
}
}
/// Adds padding to the node on all sides
pub fn pad(self, amount: f32) -> Node<A, B> {
Node {
pub fn pad(self, amount: f32) -> NodeWith<A, B> {
NodeWith {
inner: NodeValue::Padding {
amounts: Padding {
leading: amount,
@@ -131,8 +131,8 @@ impl<A, B> Node<A, B> {
/// 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) -> Node<A, B> {
Node {
pub fn offset_x(self, amount: f32) -> NodeWith<A, B> {
NodeWith {
inner: NodeValue::Offset {
offset_x: amount,
offset_y: 0.,
@@ -143,8 +143,8 @@ impl<A, B> Node<A, B> {
/// 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) -> Node<A, B> {
Node {
pub fn offset_y(self, amount: f32) -> NodeWith<A, B> {
NodeWith {
inner: NodeValue::Offset {
offset_x: 0.,
offset_y: amount,
@@ -155,8 +155,8 @@ impl<A, B> Node<A, B> {
/// 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) -> Node<A, B> {
Node {
pub fn offset(self, offset_x: f32, offset_y: f32) -> NodeWith<A, B> {
NodeWith {
inner: NodeValue::Offset {
offset_x,
offset_y,
@@ -281,7 +281,7 @@ impl<A, B> Node<A, B> {
/// 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 {
Node {
NodeWith {
inner: NodeValue::Coupled {
over: true,
element: Box::new(self.inner),
@@ -294,7 +294,7 @@ impl<A, B> Node<A, B> {
/// 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 {
Node {
NodeWith {
inner: NodeValue::Coupled {
over: false,
element: Box::new(self.inner),
@@ -339,7 +339,7 @@ impl<A, B> Node<A, B> {
};
}
_ => {
return Node {
return NodeWith {
inner: NodeValue::Explicit {
options: size,
element: Box::new(self.inner),
8 changes: 5 additions & 3 deletions src/node.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
use crate::layout::NodeValue;

pub type Node<A> = NodeWith<A, ()>;

/// A layout tree node. Use methods in [`crate::nodes`] to create nodes.
#[derive(Debug)]
pub struct Node<A, B> {
pub struct NodeWith<A, B> {
pub(crate) inner: NodeValue<A, B>,
}

impl<A, B> Clone for Node<A, B> {
impl<A, B> Clone for NodeWith<A, B> {
fn clone(&self) -> Self {
Node {
NodeWith {
inner: self.inner.clone(),
}
}
70 changes: 35 additions & 35 deletions src/nodes.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use crate::{
anynode::AnyNode, constraints::SizeConstraints, drawable::Drawable, layout::NodeValue,
models::*, Node,
models::*, Node, NodeWith,
};
use std::{any::Any, rc::Rc};

/// Defines a vertical sequence of elements
pub fn column<A, B>(elements: Vec<Node<A, B>>) -> Node<A, B> {
Node {
pub fn column<A, B>(elements: Vec<NodeWith<A, B>>) -> NodeWith<A, B> {
NodeWith {
inner: NodeValue::Column {
elements: filter_empty(ungroup(elements)),
spacing: 0.,
@@ -23,7 +23,7 @@ pub fn column<A, B>(elements: Vec<Node<A, B>>) -> Node<A, B> {
/// use backer::models::*;
/// use backer::nodes::*;
///
/// column::<()>(vec![
/// column::<(), ()>(vec![
/// empty(),
/// group(
/// (0..5)
@@ -33,14 +33,14 @@ pub fn column<A, B>(elements: Vec<Node<A, B>>) -> Node<A, B> {
/// ),
/// ]);
/// ```
pub fn group<A, B>(elements: Vec<Node<A, B>>) -> Node<A, B> {
Node {
pub fn group<A, B>(elements: Vec<NodeWith<A, B>>) -> NodeWith<A, B> {
NodeWith {
inner: NodeValue::Group(filter_empty(ungroup(elements))),
}
}
/// Defines a vertical sequence of elements with the specified spacing between each element.
pub fn column_spaced<A, B>(spacing: f32, elements: Vec<Node<A, B>>) -> Node<A, B> {
Node {
pub fn column_spaced<A, B>(spacing: f32, elements: Vec<NodeWith<A, B>>) -> NodeWith<A, B> {
NodeWith {
inner: NodeValue::Column {
elements: filter_empty(ungroup(elements)),
spacing,
@@ -50,8 +50,8 @@ pub fn column_spaced<A, B>(spacing: f32, elements: Vec<Node<A, B>>) -> Node<A, B
}
}
/// Defines a horizontal sequence of elements
pub fn row<A, B>(elements: Vec<Node<A, B>>) -> Node<A, B> {
Node {
pub fn row<A, B>(elements: Vec<NodeWith<A, B>>) -> NodeWith<A, B> {
NodeWith {
inner: NodeValue::Row {
elements: filter_empty(ungroup(elements)),
spacing: 0.,
@@ -61,8 +61,8 @@ pub fn row<A, B>(elements: Vec<Node<A, B>>) -> Node<A, B> {
}
}
/// Defines a horizontal sequence of elements with the specified spacing between each element.
pub fn row_spaced<A, B>(spacing: f32, elements: Vec<Node<A, B>>) -> Node<A, B> {
Node {
pub fn row_spaced<A, B>(spacing: f32, elements: Vec<NodeWith<A, B>>) -> NodeWith<A, B> {
NodeWith {
inner: NodeValue::Row {
elements: filter_empty(ungroup(elements)),
spacing,
@@ -72,8 +72,8 @@ pub fn row_spaced<A, B>(spacing: f32, elements: Vec<Node<A, B>>) -> Node<A, B> {
}
}
/// Defines a sequence of elements to be laid out on top of each other.
pub fn stack<A, B>(elements: Vec<Node<A, B>>) -> Node<A, B> {
Node {
pub fn stack<A, B>(elements: Vec<NodeWith<A, B>>) -> NodeWith<A, B> {
NodeWith {
inner: NodeValue::Stack(filter_empty(ungroup(elements))),
}
}
@@ -93,24 +93,24 @@ pub fn stack<A, B>(elements: Vec<Node<A, B>>) -> Node<A, B> {
/// })
///}
/// ```
pub fn draw<A>(drawable: impl Fn(Area, &mut A) + 'static) -> Node<A, ()> {
Node {
pub fn draw<A>(drawable: impl Fn(Area, &mut A) + 'static) -> NodeWith<A, ()> {
NodeWith {
inner: NodeValue::Draw(Drawable {
area: Area::default(),
draw: Rc::new(move |area, a, _| drawable(area, a)),
}),
}
}
/// Defines an empty space which is laid out the same as any other node.
pub fn space<A, B>() -> Node<A, B> {
Node {
pub fn space<A, B>() -> NodeWith<A, B> {
NodeWith {
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<A, B>() -> Node<A, B> {
Node {
pub fn empty<A, B>() -> NodeWith<A, B> {
NodeWith {
inner: NodeValue::Empty,
}
}
@@ -119,9 +119,9 @@ pub fn empty<A, B>() -> Node<A, B> {
/// 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<A, B>(
func: impl Fn(Area, &mut A, &mut B) -> Node<A, B> + 'static,
) -> Node<A, B> {
Node {
func: impl Fn(Area, &mut A, &mut B) -> NodeWith<A, B> + 'static,
) -> NodeWith<A, B> {
NodeWith {
inner: NodeValue::AreaReader {
read: Rc::new(func),
},
@@ -131,41 +131,41 @@ pub fn area_reader<A, B>(
pub fn scope_with<T, U, A: 'static, B: 'static>(
scope_a: impl Fn(&mut T) -> &mut A + 'static + Copy,
scope_b: impl Fn(&mut U) -> &mut B + 'static + Copy,
node: impl Fn(&mut A, &mut B) -> Node<A, B> + 'static + Copy,
) -> Node<T, U> {
Node {
node: impl Fn(&mut A, &mut B) -> NodeWith<A, B> + 'static + Copy,
) -> NodeWith<T, U> {
NodeWith {
inner: NodeValue::Scope {
node: None,
scope_a: Rc::new(move |a| scope_a(a)),
scope_b: Rc::new(move |b| scope_b(b)),
scoped: Rc::new(move |any_a, any_b| {
let downcast_a = any_a.downcast_mut::<&mut A>().expect("Invalid downcast");
let downcast_b = any_b.downcast_mut::<&mut B>().expect("Invalid downcast");
let downcast_a = any_a.downcast_mut::<A>().expect("Invalid downcast");
let downcast_b = any_b.downcast_mut::<B>().expect("Invalid downcast");
let anynode = node(downcast_a, downcast_b);
AnyNode {
inner: Box::new(anynode),
clone: move |any| {
Box::new(
any.downcast_ref::<Node<A, B>>()
any.downcast_ref::<NodeWith<A, B>>()
.expect("Invalid downcast")
.clone(),
) as Box<dyn Any>
},
layout: Rc::new(move |any, area, a, b| {
any.downcast_mut::<Node<A, B>>()
any.downcast_mut::<NodeWith<A, B>>()
.expect("Invalid downcast")
.inner
.layout(area, None, None, scope_a(a), scope_b(b))
}),
draw: Rc::new(move |any, a, b| {
any.downcast_ref::<Node<A, B>>()
any.downcast_ref::<NodeWith<A, B>>()
.expect("Invalid downcast")
.inner
.draw(scope_a(a), scope_b(b))
}),
constraints: Rc::new(move |any, area, a, b| {
let scoped = any
.downcast_mut::<Node<A, B>>()
.downcast_mut::<NodeWith<A, B>>()
.expect("Invalid downcast")
.inner
.constraints(area, scope_a(a), scope_b(b));
@@ -184,12 +184,12 @@ pub fn scope_with<T, U, A: 'static, B: 'static>(
/// Narrows or scopes the mutable state available to the children of this node
pub fn scope<T, A: 'static>(
scope_a: impl Fn(&mut T) -> &mut A + 'static + Copy,
node: impl Fn(&mut A) -> Node<A, ()> + 'static + Copy,
) -> Node<T, ()> {
node: impl Fn(&mut A) -> NodeWith<A, ()> + 'static + Copy,
) -> Node<T> {
scope_with(scope_a, |b| b, move |a, _| node(a))
}

fn ungroup<A, B>(elements: Vec<Node<A, B>>) -> Vec<NodeValue<A, B>> {
fn ungroup<A, B>(elements: Vec<NodeWith<A, B>>) -> Vec<NodeValue<A, B>> {
elements
.into_iter()
.flat_map(|el| {
8 changes: 4 additions & 4 deletions src/tests/layout_tests.rs
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ mod tests {
use crate::layout::*;
use crate::models::*;
use crate::nodes::*;
use crate::Node;
use crate::NodeWith;
#[test]
fn test_seq_align_on_axis() {
Layout::new(|()| {
@@ -601,7 +601,7 @@ mod tests {
test: true,
b: B { test: true },
};
fn layout(a: &mut A) -> Node<A, ()> {
fn layout(a: &mut A) -> NodeWith<A, ()> {
stack(vec![
if a.test {
draw(|area, a: &mut A| {
@@ -645,7 +645,7 @@ mod tests {
struct B;
let b = &mut B;
type Tuple<'a> = (&'a mut A, (&'a mut B, ()));
fn layout<'a>(_: &mut Tuple<'_>) -> Node<Tuple<'a>, ()> {
fn layout<'a>(_: &mut Tuple<'_>) -> NodeWith<Tuple<'a>, ()> {
stack(vec![
draw(|area, _: &mut Tuple| {
assert_eq!(area, Area::new(0., 0., 100., 100.));
@@ -676,7 +676,7 @@ mod tests {
type TupleA<'a> = (&'a mut A, &'a mut B);
// type TupleB<'a> = (&'a mut A, &'a mut C);

fn layout<'a>(_: &mut TupleA<'_>) -> Node<TupleA<'a>, ()> {
fn layout<'a>(_: &mut TupleA<'_>) -> NodeWith<TupleA<'a>, ()> {
stack(vec![
draw(|area, _: &mut TupleA| {
assert_eq!(area, Area::new(0., 0., 100., 100.));