Skip to content

Commit

Permalink
Visitor: only box for list types
Browse files Browse the repository at this point in the history
  • Loading branch information
dhardy committed Jan 13, 2024
1 parent c851799 commit c792102
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 42 deletions.
4 changes: 2 additions & 2 deletions crates/kas-core/src/layout/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ pub use size_rules::SizeRules;
pub use size_types::*;
pub use sizer::{solve_size_rules, RulesSetter, RulesSolver, SolveCache};
pub use storage::*;
pub use visitor::{FrameStorage, PackStorage, Visitor};
pub use visitor::{FrameStorage, PackStorage, Visitable, Visitor};

/// Information on which axis is being resized
///
Expand Down Expand Up @@ -252,7 +252,7 @@ impl From<AxisInfo> for Directions {
/// [`layout`]: crate::widget#layout-1
pub trait LayoutVisitor {
/// Layout defined by a [`Visitor`]
fn layout_visitor(&mut self) -> Visitor<'_>;
fn layout_visitor(&mut self) -> Visitor<impl Visitable>;
}

#[cfg(test)]
Expand Down
117 changes: 81 additions & 36 deletions crates/kas-core/src/layout/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use std::iter::ExactSizeIterator;
///
/// Unlike when implementing a widget, all methods of this trait must be
/// implemented directly.
#[crate::autoimpl(for<T: trait + ?Sized> &'_ mut T, Box<T>)]
pub trait Visitable {
/// Get size rules for the given axis
///
Expand Down Expand Up @@ -56,101 +57,145 @@ pub trait Visitable {
/// This is an internal API and may be subject to unexpected breaking changes.
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
pub struct Visitor<'a>(Box<dyn Visitable + 'a>);
pub struct Visitor<V: Visitable>(V);

/// Visitor using a boxed dyn trait object
pub type BoxVisitor<'a> = Visitor<Box<dyn Visitable + 'a>>;

impl<'a, V: Visitable> Visitor<V>
where
V: 'a,
{
#[cfg(feature = "min_spec")]
#[inline]
pub default fn boxed(self) -> Visitor<Box<dyn Visitable + 'a>> {
Visitor(Box::new(self.0))
}
#[cfg(not(feature = "min_spec"))]
#[inline]
pub fn boxed(self) -> Visitor<Box<dyn Visitable + 'a>> {
Visitor(Box::new(self.0))
}
}

impl<'a> BoxVisitor<'a> {
#[cfg(feature = "min_spec")]
#[inline]
pub fn boxed(self) -> Visitor<Box<dyn Visitable + 'a>> {
self
}

impl<'a> Visitor<'a> {
/// Construct a single-item layout
pub fn single(widget: &'a mut dyn Layout) -> Self {
Visitor(Box::new(Single { widget }))
pub fn single(widget: &'a mut dyn Layout) -> Visitor<impl Visitable + 'a> {
Visitor(Single { widget })
}

/// Construct a single-item layout with alignment hints
pub fn align_single(widget: &'a mut dyn Layout, hints: AlignHints) -> Self {
Visitor(Box::new(AlignSingle { widget, hints }))
pub fn align_single(
widget: &'a mut dyn Layout,
hints: AlignHints,
) -> Visitor<impl Visitable + 'a> {
Visitor(AlignSingle { widget, hints })
}

/// Construct a sub-layout with alignment hints
pub fn align(child: Self, hints: AlignHints) -> Self {
Visitor(Box::new(Align { child, hints }))
pub fn align<C: Visitable + 'a>(child: C, hints: AlignHints) -> Visitor<impl Visitable + 'a> {
Visitor(Align { child, hints })
}

/// Construct a sub-layout which is squashed and aligned
pub fn pack(storage: &'a mut PackStorage, child: Self, hints: AlignHints) -> Self {
Visitor(Box::new(Pack {
pub fn pack<C: Visitable + 'a>(
storage: &'a mut PackStorage,
child: C,
hints: AlignHints,
) -> Visitor<impl Visitable + 'a> {
Visitor(Pack {
child,
storage,
hints,
}))
})
}

/// Replace the margins of a sub-layout
pub fn margins(child: Self, dirs: Directions, style: MarginStyle) -> Self {
Visitor(Box::new(Margins { child, dirs, style }))
pub fn margins<C: Visitable + 'a>(
child: C,
dirs: Directions,
style: MarginStyle,
) -> Visitor<impl Visitable + 'a> {
Visitor(Margins { child, dirs, style })
}

/// Construct a frame around a sub-layout
///
/// This frame has dimensions according to [`SizeCx::frame`].
pub fn frame(storage: &'a mut FrameStorage, child: Self, style: FrameStyle) -> Self {
Visitor(Box::new(Frame {
pub fn frame<C: Visitable + 'a>(
storage: &'a mut FrameStorage,
child: C,
style: FrameStyle,
) -> Visitor<impl Visitable + 'a> {
Visitor(Frame {
child,
storage,
style,
}))
})
}

/// Construct a button frame around a sub-layout
///
/// Generates a button frame containing the child node. Mouse/touch input
/// on the button reports input to `self`, not to the child node.
pub fn button(storage: &'a mut FrameStorage, child: Self, color: Option<Rgb>) -> Self {
Visitor(Box::new(Button {
pub fn button<C: Visitable + 'a>(
storage: &'a mut FrameStorage,
child: C,
color: Option<Rgb>,
) -> Visitor<impl Visitable + 'a> {
Visitor(Button {
child,
storage,
color,
}))
})
}

/// Construct a row/column layout over an iterator of layouts
pub fn list<I, D, S>(list: I, direction: D, data: &'a mut S) -> Self
pub fn list<I, D, S>(list: I, direction: D, data: &'a mut S) -> Visitor<impl Visitable + 'a>
where
I: ExactSizeIterator<Item = Visitor<'a>> + 'a,
I: ExactSizeIterator<Item = BoxVisitor<'a>> + 'a,
D: Directional,
S: RowStorage,
{
Visitor(Box::new(List {
Visitor(List {
children: list,
direction,
data,
}))
})
}

/// Construct a float of layouts
///
/// This is a stack, but showing all items simultaneously.
/// The first item is drawn on top and has first input priority.
pub fn float<I>(list: I) -> Self
pub fn float<I>(list: I) -> Visitor<impl Visitable + 'a>
where
I: DoubleEndedIterator<Item = Visitor<'a>> + 'a,
I: DoubleEndedIterator<Item = BoxVisitor<'a>> + 'a,
{
Visitor(Box::new(Float { children: list }))
Visitor(Float { children: list })
}

/// Construct a grid layout over an iterator of `(cell, layout)` items
pub fn grid<I, S>(iter: I, dim: GridDimensions, data: &'a mut S) -> Self
pub fn grid<I, S>(iter: I, dim: GridDimensions, data: &'a mut S) -> Visitor<impl Visitable + 'a>
where
I: DoubleEndedIterator<Item = (GridChildInfo, Visitor<'a>)> + 'a,
I: DoubleEndedIterator<Item = (GridChildInfo, BoxVisitor<'a>)> + 'a,
S: GridStorage,
{
Visitor(Box::new(Grid {
Visitor(Grid {
data,
dim,
children: iter,
}))
})
}
}

impl<'a> Visitor<'a> {
impl<V: Visitable> Visitor<V> {
/// Get size rules for the given axis
#[inline]
pub fn size_rules(mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
Expand Down Expand Up @@ -191,7 +236,7 @@ impl<'a> Visitor<'a> {
}
}

impl<'a> Visitable for Visitor<'a> {
impl<V: Visitable> Visitable for Visitor<V> {
fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
self.size_rules_(sizer, axis)
}
Expand Down Expand Up @@ -425,7 +470,7 @@ struct List<'a, I, D, S> {

impl<'a, I, D: Directional, S: RowStorage> Visitable for List<'a, I, D, S>
where
I: ExactSizeIterator<Item = Visitor<'a>>,
I: ExactSizeIterator<Item = BoxVisitor<'a>>,
{
fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
let dim = (self.direction, self.children.len());
Expand Down Expand Up @@ -460,14 +505,14 @@ where
/// Float layout
struct Float<'a, I>
where
I: DoubleEndedIterator<Item = Visitor<'a>>,
I: DoubleEndedIterator<Item = BoxVisitor<'a>>,
{
children: I,
}

impl<'a, I> Visitable for Float<'a, I>
where
I: DoubleEndedIterator<Item = Visitor<'a>>,
I: DoubleEndedIterator<Item = BoxVisitor<'a>>,
{
fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
let mut rules = SizeRules::EMPTY;
Expand Down Expand Up @@ -507,7 +552,7 @@ struct Grid<'a, S, I> {

impl<'a, S: GridStorage, I> Visitable for Grid<'a, S, I>
where
I: DoubleEndedIterator<Item = (GridChildInfo, Visitor<'a>)>,
I: DoubleEndedIterator<Item = (GridChildInfo, BoxVisitor<'a>)>,
{
fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
let mut solver = GridSolver::<Vec<_>, Vec<_>, _>::new(axis, self.dim, self.data);
Expand Down
6 changes: 3 additions & 3 deletions crates/kas-macros/src/make_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1126,7 +1126,7 @@ impl Layout {
let mut items = Toks::new();
for item in list {
let item = item.generate(core_path)?;
items.append_all(quote! {{ #item },});
items.append_all(quote! { ::kas::layout::Visitor::boxed(#item), });
}
let iter = quote! { { let arr = [#items]; arr.into_iter() } };
quote! {{
Expand All @@ -1148,7 +1148,7 @@ impl Layout {
row: #row,
row_end: #row_end,
},
#layout,
::kas::layout::Visitor::boxed(#layout),
),
});
}
Expand All @@ -1160,7 +1160,7 @@ impl Layout {
let mut items = Toks::new();
for item in list {
let item = item.generate(core_path)?;
items.append_all(quote! {{ #item },});
items.append_all(quote! { ::kas::layout::Visitor::boxed(#item), });
}
let iter = quote! { { let arr = [#items]; arr.into_iter() } };
quote! { layout::Visitor::float(#iter) }
Expand Down
2 changes: 1 addition & 1 deletion crates/kas-macros/src/widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,7 @@ pub fn widget(attr_span: Span, mut args: WidgetArgs, scope: &mut Scope) -> Resul
let layout_visitor = layout.layout_visitor(&quote! { self.#core })?;
scope.generated.push(quote! {
impl #impl_generics ::kas::layout::LayoutVisitor for #impl_target {
fn layout_visitor(&mut self) -> ::kas::layout::Visitor<'_> {
fn layout_visitor(&mut self) -> ::kas::layout::Visitor<impl ::kas::layout::Visitable> {
use ::kas::layout;
#layout_visitor
}
Expand Down

0 comments on commit c792102

Please sign in to comment.