Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ crate-type = ["lib"]
[features]
default = []
test-api = []
examples = ["macroquad-examples", "egui-examples"]
macroquad-examples = ["macroquad"]
egui-examples = ["egui", "eframe", "egui_extras"]

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 🤗
32 changes: 17 additions & 15 deletions examples/egui-example/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down
5 changes: 1 addition & 4 deletions examples/macroquad-example/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -50,9 +49,7 @@ fn layout_for_highlight<'n>() -> Node<'n, State> {
|| highlight == HighlightedCase::None
{
scope(
|ctx: ScopeCtx<HighlightedCase>, state: &mut State| {
ctx.with_scoped(&mut state.highlight)
},
|state: &mut State| &mut state.highlight,
rel_abs_seq(highlight),
)
} else {
Expand Down
15 changes: 13 additions & 2 deletions src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,8 @@ impl<State> NodeValue<'_, State> {
NodeValue::Draw(_)
| NodeValue::Space
| NodeValue::AreaReader { .. }
| NodeValue::Coupled { .. }
| NodeValue::NodeTrait { .. }
| NodeValue::Coupled { .. }
| NodeValue::Dynamic { .. } => {
vec![available_area]
}
Expand Down Expand Up @@ -346,7 +346,18 @@ impl<State> 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);
Expand Down
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ mod node;
pub use node::Node;
mod node_cache;
mod scoper;
pub use scoper::{ScopeCtx, ScopeCtxResult};
mod subtree;
mod tests;

Expand Down
94 changes: 65 additions & 29 deletions src/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{
layout::NodeValue,
models::*,
node_cache::NodeCache,
scoper::{ScopeCtx, ScopeCtxResult, Scoper},
scoper::{OptionScoper, OwnedScoper, Scoper},
traits::Drawable,
Node,
};
Expand All @@ -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<State>(elements: Vec<Node<'_, State>>) -> Node<'_, State> {
pub fn column<'n, State>(elements: Vec<impl Into<Node<'n, State>>>) -> 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,
Expand All @@ -53,18 +53,21 @@ pub fn column<State>(elements: Vec<Node<'_, State>>) -> Node<'_, State> {
/// ),
/// ]);
/// ```
pub fn group<State>(elements: Vec<Node<State>>) -> Node<'_, State> {
pub fn group<'n, State>(elements: Vec<impl Into<Node<'n, State>>>) -> 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<State>(spacing: f32, elements: Vec<Node<State>>) -> Node<'_, State> {
pub fn column_spaced<'n, State>(
spacing: f32,
elements: Vec<impl Into<Node<'n, State>>>,
) -> 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,
Expand All @@ -74,10 +77,10 @@ pub fn column_spaced<State>(spacing: f32, elements: Vec<Node<State>>) -> Node<'_
/// Creates a horizontal sequence of elements
///
#[doc = container_doc!()]
pub fn row<State>(elements: Vec<Node<State>>) -> Node<'_, State> {
pub fn row<'n, State>(elements: Vec<impl Into<Node<'n, State>>>) -> 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,
Expand All @@ -87,10 +90,13 @@ pub fn row<State>(elements: Vec<Node<State>>) -> Node<'_, State> {
/// Creates a horizontal sequence of elements with the specified spacing between each element.
///
#[doc = container_doc!()]
pub fn row_spaced<State>(spacing: f32, elements: Vec<Node<State>>) -> Node<'_, State> {
pub fn row_spaced<'n, State>(
spacing: f32,
elements: Vec<impl Into<Node<'n, State>>>,
) -> 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,
Expand All @@ -100,10 +106,10 @@ pub fn row_spaced<State>(spacing: f32, elements: Vec<Node<State>>) -> Node<'_, S
/// Creates a sequence of elements to be laid out on top of each other.
///
#[doc = container_doc!()]
pub fn stack<State>(elements: Vec<Node<State>>) -> Node<'_, State> {
pub fn stack<'n, State>(elements: Vec<impl Into<Node<'n, State>>>) -> 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,
},
Expand Down Expand Up @@ -197,41 +203,71 @@ pub fn dynamic<'nodes, State>(
/// use backer::nodes::*;
///
/// struct A {
/// b: Option<bool>,
/// 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<bool>, 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),
/// ),
/// ])
/// });
///```
pub fn scope<'nodes, State, Scoped: 'nodes>(
scope: impl Fn(ScopeCtx<'_, '_, Scoped>, &mut State) -> ScopeCtxResult + 'nodes,
node: Node<'nodes, Scoped>,
scope: impl Fn(&mut State) -> &mut Scoped + 'nodes,
node: impl Into<Node<'nodes, Scoped>>,
) -> Node<'nodes, State> {
Node {
inner: NodeValue::NodeTrait {
node: Box::new(Scoper {
scope_fn: scope,
node,
scope,
node: node.into(),
}),
},
}
}
/// 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<Scoped> + 'nodes,
node: impl Into<Node<'nodes, Scoped>>,
) -> Node<'nodes, State> {
Node {
inner: NodeValue::NodeTrait {
node: Box::new(OptionScoper {
scope,
node: node.into(),
}),
},
}
}
/// 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: impl Into<Node<'nodes, Scoped>>,
) -> Node<'nodes, State> {
Node {
inner: NodeValue::NodeTrait {
node: Box::new(OwnedScoper {
scope,
embed,
node: node.into(),
}),
},
}
}

fn convert_into<'n, State>(elements: Vec<impl Into<Node<'n, State>>>) -> Vec<Node<'n, State>> {
elements.into_iter().map(|e| e.into()).collect()
}

fn ungroup<State>(elements: Vec<Node<State>>) -> Vec<NodeCache<State>> {
elements
Expand Down
Loading