Skip to content

Commit

Permalink
Add pack layout specifier
Browse files Browse the repository at this point in the history
  • Loading branch information
dhardy committed Aug 17, 2022
1 parent 5b90d9c commit 422845d
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 14 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 @@ -60,7 +60,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, Visitor};
pub use visitor::{FrameStorage, PackStorage, Visitor};

/// Information on which axis is being resized
///
Expand Down Expand Up @@ -90,7 +90,7 @@ impl AxisInfo {
/// Construct a copy using the given alignment hints
#[inline]
pub fn with_align_hints(mut self, hints: AlignHints) -> Self {
self.align = hints.extract(self);
self.align = hints.extract(self).or(self.align);
self
}

Expand Down
54 changes: 45 additions & 9 deletions crates/kas-core/src/layout/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// Methods have to take `&mut self`
#![allow(clippy::wrong_self_convention)]

use super::{Align, AlignHints, AxisInfo, SizeRules};
use super::{Align, AlignHints, AlignPair, AxisInfo, SizeRules};
use super::{DynRowStorage, RowPositionSolver, RowSetter, RowSolver, RowStorage};
use super::{GridChildInfo, GridDimensions, GridSetter, GridSolver, GridStorage};
use super::{RulesSetter, RulesSolver};
Expand Down Expand Up @@ -45,7 +45,9 @@ enum LayoutType<'a> {
/// A single child widget with alignment
AlignSingle(&'a mut dyn Widget, AlignHints),
/// Apply alignment hints to some sub-layout
AlignLayout(Box<Visitor<'a>>, AlignHints),
Align(Box<Visitor<'a>>, AlignHints),
/// Apply alignment and pack some sub-layout
Pack(Box<Visitor<'a>>, &'a mut PackStorage, AlignHints),
/// Replace (some) margins
Margins(Box<Visitor<'a>>, Directions, MarginStyle),
/// Frame around content
Expand All @@ -64,7 +66,7 @@ impl<'a> LayoutType<'a> {
LayoutType::BoxComponent(_) => None,
LayoutType::Single(w) => Some(w.id()),
LayoutType::AlignSingle(w, _) => Some(w.id()),
LayoutType::AlignLayout(l, _) => l.layout.id(),
LayoutType::Align(l, _) => l.layout.id(),
LayoutType::Margins(l, _, _) => l.layout.id(),
LayoutType::Frame(l, _, _) => l.layout.id(),
LayoutType::Button(l, _, _) => l.layout.id(),
Expand Down Expand Up @@ -97,9 +99,15 @@ impl<'a> Visitor<'a> {
Visitor { layout }
}

/// Align a sub-layout
/// Construct a sub-layout with alignment hints
pub fn align(layout: Self, hints: AlignHints) -> Self {
let layout = LayoutType::AlignLayout(Box::new(layout), hints);
let layout = LayoutType::Align(Box::new(layout), hints);
Visitor { layout }
}

/// Construct a sub-layout which is squashed and aligned
pub fn pack(stor: &'a mut PackStorage, layout: Self, hints: AlignHints) -> Self {
let layout = LayoutType::Pack(Box::new(layout), stor, hints);
Visitor { layout }
}

Expand Down Expand Up @@ -206,9 +214,14 @@ impl<'a> Visitor<'a> {
LayoutType::AlignSingle(child, hints) => {
child.size_rules(mgr, axis.with_align_hints(*hints))
}
LayoutType::AlignLayout(layout, hints) => {
LayoutType::Align(layout, hints) => {
layout.size_rules_(mgr, axis.with_align_hints(*hints))
}
LayoutType::Pack(layout, stor, hints) => {
let rules = layout.size_rules_(mgr, stor.apply_align(axis, *hints));
stor.size.set_component(axis, rules.ideal_size());
rules
}
LayoutType::Margins(child, dirs, margins) => {
let mut child_rules = child.size_rules_(mgr.re(), axis);
if dirs.intersects(Directions::from(axis)) {
Expand Down Expand Up @@ -246,8 +259,9 @@ impl<'a> Visitor<'a> {
LayoutType::Component(component) => component.set_rect(mgr, rect),
LayoutType::BoxComponent(layout) => layout.set_rect(mgr, rect),
LayoutType::Single(child) => child.set_rect(mgr, rect),
LayoutType::Align(layout, _) => layout.set_rect_(mgr, rect),
LayoutType::AlignSingle(child, _) => child.set_rect(mgr, rect),
LayoutType::AlignLayout(layout, _) => layout.set_rect_(mgr, rect),
LayoutType::Pack(layout, stor, _) => layout.set_rect_(mgr, stor.aligned_rect(rect)),
LayoutType::Margins(child, _, _) => child.set_rect_(mgr, rect),
LayoutType::Frame(child, storage, _) | LayoutType::Button(child, storage, _) => {
storage.rect = rect;
Expand All @@ -274,7 +288,8 @@ impl<'a> Visitor<'a> {
LayoutType::Component(component) => component.find_id(coord),
LayoutType::BoxComponent(layout) => layout.find_id(coord),
LayoutType::Single(child) | LayoutType::AlignSingle(child, _) => child.find_id(coord),
LayoutType::AlignLayout(layout, _) => layout.find_id_(coord),
LayoutType::Align(layout, _) => layout.find_id_(coord),
LayoutType::Pack(layout, _, _) => layout.find_id_(coord),
LayoutType::Margins(layout, _, _) => layout.find_id_(coord),
LayoutType::Frame(child, _, _) => child.find_id_(coord),
// Buttons steal clicks, hence Button never returns ID of content
Expand All @@ -293,7 +308,8 @@ impl<'a> Visitor<'a> {
LayoutType::Component(component) => component.draw(draw),
LayoutType::BoxComponent(layout) => layout.draw(draw),
LayoutType::Single(child) | LayoutType::AlignSingle(child, _) => draw.recurse(*child),
LayoutType::AlignLayout(layout, _) => layout.draw_(draw),
LayoutType::Align(layout, _) => layout.draw_(draw),
LayoutType::Pack(layout, _, _) => layout.draw_(draw),
LayoutType::Margins(layout, _, _) => layout.draw_(draw),
LayoutType::Frame(child, storage, style) => {
draw.frame(storage.rect, *style, Background::Default);
Expand Down Expand Up @@ -470,6 +486,26 @@ where
}
}

/// Layout storage for alignment
#[derive(Clone, Default, Debug)]
pub struct PackStorage {
align: AlignPair,
size: Size,
}
impl PackStorage {
/// Set alignment
fn apply_align(&mut self, axis: AxisInfo, hints: AlignHints) -> AxisInfo {
let axis = axis.with_align_hints(hints);
self.align.set_component(axis, axis.align_or_default());
axis
}

/// Align rect
fn aligned_rect(&self, rect: Rect) -> Rect {
self.align.aligned_rect(self.size, rect)
}
}

/// Layout storage for frame layout
#[derive(Clone, Default, Debug)]
pub struct FrameStorage {
Expand Down
6 changes: 5 additions & 1 deletion crates/kas-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,11 @@ pub fn impl_scope(input: TokenStream) -> TokenStream {
///
/// > _Align_ :\
/// > &nbsp;&nbsp; `align` `(` _AlignType_ ( `,` _AlignType_ )? `)` `:` _Layout_\
/// > &nbsp;&nbsp; Applies some alignment to a sub-layout, e.g. `align(top): self.foo`. Two-dimensional alignment is possible but must be horizontal first, e.g. `align(left, top): ..`
/// > &nbsp;&nbsp; Applies some alignment to a sub-layout, e.g. `align(top): self.foo`. Two-dimensional alignment is possible but must be horizontal first, e.g. `align(left, top): ..`. Note: this does not constrain the size of the widget but merely adjusts content alignment; see also _Pack_.
/// >
/// > _Pack_ :\
/// > &nbsp;&nbsp; `pack` `(` _AlignType_ ( `,` _AlignType_ )? `)` _Storage_? `:` _Layout_\
/// > &nbsp;&nbsp; As `align`, this applies some alignment to content, but also restricts the size of that content to its ideal size (i.e. no stretching).
/// >
/// > _Frame_ :\
/// > &nbsp;&nbsp; `frame` ( `(` _Expr_ `)` )? _Storage_? `:` _Layout_\
Expand Down
20 changes: 20 additions & 0 deletions crates/kas-macros/src/make_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ mod kw {
use syn::custom_keyword;

custom_keyword!(align);
custom_keyword!(pack);
custom_keyword!(column);
custom_keyword!(row);
custom_keyword!(right);
Expand Down Expand Up @@ -104,6 +105,7 @@ impl ToTokens for StorIdent {
enum Layout {
Align(Box<Layout>, AlignHints),
AlignSingle(ExprMember, AlignHints),
Pack(StorIdent, Box<Layout>, AlignHints),
Margins(Box<Layout>, Directions, Toks),
Single(ExprMember),
Widget(StorIdent, Expr),
Expand Down Expand Up @@ -272,6 +274,14 @@ impl Layout {
let layout = Layout::parse(input, gen)?;
Ok(Layout::Align(Box::new(layout), align))
}
} else if lookahead.peek(kw::pack) {
let _: kw::pack = input.parse()?;
let align = parse_align(input)?;
let stor = gen.parse_or_next(input)?;
let _: Token![:] = input.parse()?;

let layout = Layout::parse(input, gen)?;
Ok(Layout::Pack(stor, Box::new(layout), align))
} else if lookahead.peek(kw::margins) {
let _ = input.parse::<kw::margins>()?;
let inner;
Expand Down Expand Up @@ -693,6 +703,11 @@ impl Layout {
layout.append_fields(ty_toks, def_toks, children);
}
Layout::AlignSingle(..) | Layout::Margins(..) | Layout::Single(_) => (),
Layout::Pack(stor, layout, _) => {
ty_toks.append_all(quote! { #stor: ::kas::layout::PackStorage, });
def_toks.append_all(quote! { #stor: Default::default(), });
layout.append_fields(ty_toks, def_toks, children);
}
Layout::Widget(stor, expr) => {
children.push(stor.to_token_stream());
ty_toks.append_all(quote! { #stor: Box<dyn ::kas::Widget>, });
Expand Down Expand Up @@ -756,6 +771,10 @@ impl Layout {
Layout::AlignSingle(expr, align) => {
quote! { layout::Visitor::align_single(&mut (#expr), #align) }
}
Layout::Pack(stor, layout, align) => {
let inner = layout.generate(core)?;
quote! { layout::Visitor::pack(&mut self.#core.#stor, #inner, #align) }
}
Layout::Margins(layout, dirs, selector) => {
let inner = layout.generate(core)?;
quote! { layout::Visitor::margins(
Expand Down Expand Up @@ -850,6 +869,7 @@ impl Layout {
) -> std::result::Result<(), &'static str> {
match self {
Layout::Align(layout, _)
| Layout::Pack(_, layout, _)
| Layout::Margins(layout, _, _)
| Layout::Frame(_, layout, _) => layout.nav_next(children, output, index),
Layout::Button(_, layout, _) | Layout::NonNavigable(layout) => {
Expand Down
4 changes: 2 additions & 2 deletions examples/gallery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ fn widgets() -> Box<dyn SetDisabled> {
row: ["ScrollLabel", self.sl],
row: ["EditBox", self.eb],
row: ["TextButton", self.tb],
row: ["Button<Image>", self.bi],
row: ["Button<Image>", pack(center): self.bi],
row: ["CheckButton", self.cb],
row: ["RadioButton", self.rb],
row: ["RadioButton", self.rb2],
Expand Down Expand Up @@ -239,7 +239,7 @@ Demonstration of *as-you-type* formatting from **Markdown**.
Box::new(impl_singleton! {
#[widget{
layout = float: [
align(right, top): TextButton::new_msg("↻", MsgDirection),
pack(right, top): TextButton::new_msg("↻", MsgDirection),
list(self.dir): [self.editor, non_navigable: self.label],
];
}]
Expand Down

0 comments on commit 422845d

Please sign in to comment.