Skip to content

Commit

Permalink
Merge pull request #440 from kas-gui/work1
Browse files Browse the repository at this point in the history
Move layout macros to kas-widgets; do not recurse internally
  • Loading branch information
dhardy authored Feb 20, 2024
2 parents 1333b6f + 75ea167 commit 2b2d506
Show file tree
Hide file tree
Showing 20 changed files with 153 additions and 73 deletions.
17 changes: 16 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,22 @@ wayland = ["kas-core/wayland"]
# Support X11
x11 = ["kas-core/x11"]

# Optimise Node using unsafe code
# Optimize generated layout widgets
#
# Recursive layout macros allow the generation of complex layout widgets; for
# example `row!["a", column!["b", "c"]]` yields a single layout widget (over
# three `StrLabel` widgets) instead of two separate layout widgets.
# (Note that this happens anyway in custom widget layout syntax where it is
# requried to support reference to widget fields.)
#
# A limited number of method calls such as `.align(AlignHints::LEFT)` and
# `.map_any()` are supported but are not linked correctly via rust-analyzer.
#
# Often results in unused import warnings, hence you may want to use
# RUSTFLAGS="-A unused_imports".
recursive-layout-widgets = ["kas-core/recursive-layout-widgets"]

# Optimize Node using unsafe code
unsafe_node = ["kas-core/unsafe_node"]

[dependencies]
Expand Down
5 changes: 4 additions & 1 deletion crates/kas-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ dark-light = ["dep:dark-light"]
# Support spawning async tasks
spawn = ["dep:async-global-executor"]

# Optimise Node using unsafe code
# Optimize generated layout widgets
recursive-layout-widgets = ["kas-macros/recursive-layout-widgets"]

# Optimize Node using unsafe code
unsafe_node = []

[build-dependencies]
Expand Down
6 changes: 3 additions & 3 deletions crates/kas-core/src/geom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -636,19 +636,19 @@ impl std::ops::SubAssign<Offset> for Rect {
#[cfg_attr(doc_cfg, doc(cfg(feature = "winit")))]
mod winit_impls {
use super::{Coord, Size};
use crate::cast::{Cast, CastApprox, Conv, ConvApprox};
use crate::cast::{Cast, CastApprox, Conv, ConvApprox, Result};
use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};

impl<X: CastApprox<i32>> ConvApprox<PhysicalPosition<X>> for Coord {
#[inline]
fn try_conv_approx(pos: PhysicalPosition<X>) -> cast::Result<Self> {
fn try_conv_approx(pos: PhysicalPosition<X>) -> Result<Self> {
Ok(Coord(pos.x.cast_approx(), pos.y.cast_approx()))
}
}

impl<X: Cast<i32>> Conv<PhysicalSize<X>> for Size {
#[inline]
fn try_conv(size: PhysicalSize<X>) -> cast::Result<Self> {
fn try_conv(size: PhysicalSize<X>) -> Result<Self> {
Ok(Size(size.width.cast(), size.height.cast()))
}
}
Expand Down
3 changes: 2 additions & 1 deletion crates/kas-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ mod root;
pub use crate::core::*;
pub use action::Action;
pub use decorations::Decorations;
pub use kas_macros::*;
pub use kas_macros::{autoimpl, extends, impl_default, widget};
pub use kas_macros::{collection, impl_anon, impl_scope, widget_index};
#[doc(inline)] pub use popup::Popup;
#[doc(inline)] pub(crate) use popup::PopupDescriptor;
#[doc(inline)]
Expand Down
3 changes: 3 additions & 0 deletions crates/kas-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ proc-macro = true
# Requires that all crates using these macros depend on the log crate.
log = []

# Optimize generated layout widgets
recursive-layout-widgets = []

[dependencies]
quote = "1.0"
proc-macro2 = { version = "1.0" }
Expand Down
6 changes: 4 additions & 2 deletions crates/kas-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -617,9 +617,11 @@ pub fn aligned_row(input: TokenStream) -> TokenStream {
/// # Example
///
/// ```ignore
/// let list = kas::widgets::List::right(kas::collection![
/// use kas::collection;
/// use kas::widgets::{CheckBox, List};
/// let list = List::right(collection![
/// "A checkbox",
/// kas::widgets::CheckBox::new(|_, _| false),
/// CheckBox::new(|_, _| false),
/// ]);
/// ```
///
Expand Down
100 changes: 74 additions & 26 deletions crates/kas-macros/src/make_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,15 +266,15 @@ impl Tree {
pub fn column(inner: ParseStream) -> Result<Self> {
let mut gen = NameGenerator::default();
let stor = gen.next();
let list = parse_layout_items(inner, &mut gen)?;
let list = parse_layout_items(inner, &mut gen, false)?;
Ok(Tree(Layout::List(stor.into(), Direction::Down, list)))
}

/// Parse a row (contents only)
pub fn row(inner: ParseStream) -> Result<Self> {
let mut gen = NameGenerator::default();
let stor = gen.next();
let list = parse_layout_items(inner, &mut gen)?;
let list = parse_layout_items(inner, &mut gen, false)?;
Ok(Tree(Layout::List(stor.into(), Direction::Right, list)))
}

Expand All @@ -287,6 +287,7 @@ impl Tree {
inner,
&mut gen,
true,
false,
)?))
}

Expand All @@ -299,6 +300,7 @@ impl Tree {
inner,
&mut gen,
false,
false,
)?))
}

Expand All @@ -308,22 +310,22 @@ impl Tree {
let stor = gen.next();
let dir: Direction = inner.parse()?;
let _: Token![,] = inner.parse()?;
let list = parse_layout_list(inner, &mut gen)?;
let list = parse_layout_list(inner, &mut gen, false)?;
Ok(Tree(Layout::List(stor.into(), dir, list)))
}

/// Parse a float (contents only)
pub fn float(inner: ParseStream) -> Result<Self> {
let mut gen = NameGenerator::default();
let list = parse_layout_items(inner, &mut gen)?;
let list = parse_layout_items(inner, &mut gen, false)?;
Ok(Tree(Layout::Float(list)))
}

/// Parse a grid (contents only)
pub fn grid(inner: ParseStream) -> Result<Self> {
let mut gen = NameGenerator::default();
let stor = gen.next();
Ok(Tree(parse_grid(stor.into(), inner, &mut gen)?))
Ok(Tree(parse_grid(stor.into(), inner, &mut gen, false)?))
}
}

Expand Down Expand Up @@ -504,13 +506,45 @@ impl GridDimensions {
impl Parse for Tree {
fn parse(input: ParseStream) -> Result<Self> {
let mut gen = NameGenerator::default();
Ok(Tree(Layout::parse(input, &mut gen)?))
Ok(Tree(Layout::parse(input, &mut gen, true)?))
}
}

impl Layout {
fn parse(input: ParseStream, gen: &mut NameGenerator) -> Result<Self> {
let mut layout = if input.peek2(Token![!]) {
fn parse(input: ParseStream, gen: &mut NameGenerator, _recurse: bool) -> Result<Self> {
#[cfg(feature = "recursive-layout-widgets")]
let _recurse = true;

#[cfg(not(feature = "recursive-layout-widgets"))]
if input.peek2(Token![!]) {
let input2 = input.fork();
let mut gen2 = NameGenerator::default();
if Self::parse_macro_like(&input2, &mut gen2).is_ok() {
loop {
if let Ok(dot_token) = input2.parse::<Token![.]>() {
if input2.peek(kw::map_any) {
let _ = MapAny::parse(dot_token, &input2)?;
continue;
} else if input2.peek(kw::align) {
let _ = Align::parse(dot_token, &input2)?;
continue;
} else if input2.peek(kw::pack) {
let _ = Pack::parse(dot_token, &input2, &mut gen2)?;
continue;
} else if let Ok(ident) = input2.parse::<Ident>() {
proc_macro_error::emit_warning!(
ident, "this method call is incompatible with feature `recursive-layout-widgets`";
note = "extract operand from layout expression or wrap with braces",
);
}
}

break;
}
}
}

let mut layout = if _recurse && input.peek2(Token![!]) {
Self::parse_macro_like(input, gen)?
} else if input.peek(Token![self]) {
Layout::Single(input.parse()?)
Expand Down Expand Up @@ -572,7 +606,7 @@ impl Layout {

let inner;
let _ = parenthesized!(inner in input);
let layout = Layout::parse(&inner, gen)?;
let layout = Layout::parse(&inner, gen, true)?;

let style: Expr = if !inner.is_empty() {
let _: Token![,] = inner.parse()?;
Expand All @@ -591,7 +625,7 @@ impl Layout {

let inner;
let _ = parenthesized!(inner in input);
let layout = Layout::parse(&inner, gen)?;
let layout = Layout::parse(&inner, gen, true)?;

let color: Expr = if !inner.is_empty() {
let _: Token![,] = inner.parse()?;
Expand All @@ -607,13 +641,13 @@ impl Layout {
let _: kw::column = input.parse()?;
let _: Token![!] = input.parse()?;
let stor = gen.parse_or_next(input)?;
let list = parse_layout_list(input, gen)?;
let list = parse_layout_list(input, gen, true)?;
Ok(Layout::List(stor, Direction::Down, list))
} else if lookahead.peek(kw::row) {
let _: kw::row = input.parse()?;
let _: Token![!] = input.parse()?;
let stor = gen.parse_or_next(input)?;
let list = parse_layout_list(input, gen)?;
let list = parse_layout_list(input, gen, true)?;
Ok(Layout::List(stor, Direction::Right, list))
} else if lookahead.peek(kw::list) {
let _: kw::list = input.parse()?;
Expand All @@ -623,12 +657,12 @@ impl Layout {
let _ = parenthesized!(inner in input);
let dir: Direction = inner.parse()?;
let _: Token![,] = inner.parse()?;
let list = parse_layout_list(&inner, gen)?;
let list = parse_layout_list(&inner, gen, true)?;
Ok(Layout::List(stor, dir, list))
} else if lookahead.peek(kw::float) {
let _: kw::float = input.parse()?;
let _: Token![!] = input.parse()?;
let list = parse_layout_list(input, gen)?;
let list = parse_layout_list(input, gen, true)?;
Ok(Layout::Float(list))
} else if lookahead.peek(kw::aligned_column) {
let _: kw::aligned_column = input.parse()?;
Expand All @@ -638,7 +672,7 @@ impl Layout {
let inner;
let _ = bracketed!(inner in input);
Ok(parse_grid_as_list_of_lists::<kw::row>(
stor, &inner, gen, true,
stor, &inner, gen, true, true,
)?)
} else if lookahead.peek(kw::aligned_row) {
let _: kw::aligned_row = input.parse()?;
Expand All @@ -648,7 +682,7 @@ impl Layout {
let inner;
let _ = bracketed!(inner in input);
Ok(parse_grid_as_list_of_lists::<kw::column>(
stor, &inner, gen, false,
stor, &inner, gen, false, true,
)?)
} else if lookahead.peek(kw::grid) {
let _: kw::grid = input.parse()?;
Expand All @@ -657,14 +691,14 @@ impl Layout {

let inner;
let _ = braced!(inner in input);
Ok(parse_grid(stor, &inner, gen)?)
Ok(parse_grid(stor, &inner, gen, true)?)
} else if lookahead.peek(kw::non_navigable) {
let _: kw::non_navigable = input.parse()?;
let _: Token![!] = input.parse()?;

let inner;
let _ = parenthesized!(inner in input);
let layout = Layout::parse(&inner, gen)?;
let layout = Layout::parse(&inner, gen, true)?;
Ok(Layout::NonNavigable(Box::new(layout)))
} else {
let ident = gen.next();
Expand All @@ -674,20 +708,28 @@ impl Layout {
}
}

fn parse_layout_list(input: ParseStream, gen: &mut NameGenerator) -> Result<VisitableList<()>> {
fn parse_layout_list(
input: ParseStream,
gen: &mut NameGenerator,
recurse: bool,
) -> Result<VisitableList<()>> {
let inner;
let _ = bracketed!(inner in input);
parse_layout_items(&inner, gen)
parse_layout_items(&inner, gen, recurse)
}

fn parse_layout_items(inner: ParseStream, gen: &mut NameGenerator) -> Result<VisitableList<()>> {
fn parse_layout_items(
inner: ParseStream,
gen: &mut NameGenerator,
recurse: bool,
) -> Result<VisitableList<()>> {
let mut list = vec![];
let mut gen2 = NameGenerator::default();
while !inner.is_empty() {
list.push(ListItem {
cell: (),
stor: gen2.next(),
layout: Layout::parse(inner, gen)?,
layout: Layout::parse(inner, gen, recurse)?,
});

if inner.is_empty() {
Expand All @@ -705,6 +747,7 @@ fn parse_grid_as_list_of_lists<KW: Parse>(
inner: ParseStream,
gen: &mut NameGenerator,
row_major: bool,
recurse: bool,
) -> Result<Layout> {
let (mut col, mut row) = (0, 0);
let mut dim = GridDimensions::default();
Expand All @@ -721,7 +764,7 @@ fn parse_grid_as_list_of_lists<KW: Parse>(
while !inner2.is_empty() {
let cell = CellInfo::new(col, row);
dim.update(&cell);
let layout = Layout::parse(&inner2, gen)?;
let layout = Layout::parse(&inner2, gen, recurse)?;
cells.push(ListItem {
cell,
stor: gen2.next(),
Expand Down Expand Up @@ -757,7 +800,12 @@ fn parse_grid_as_list_of_lists<KW: Parse>(
Ok(Layout::Grid(stor, dim, VisitableList(cells)))
}

fn parse_grid(stor: StorIdent, inner: ParseStream, gen: &mut NameGenerator) -> Result<Layout> {
fn parse_grid(
stor: StorIdent,
inner: ParseStream,
gen: &mut NameGenerator,
recurse: bool,
) -> Result<Layout> {
let mut dim = GridDimensions::default();
let mut gen2 = NameGenerator::default();
let mut cells = vec![];
Expand All @@ -771,10 +819,10 @@ fn parse_grid(stor: StorIdent, inner: ParseStream, gen: &mut NameGenerator) -> R
if inner.peek(syn::token::Brace) {
let inner2;
let _ = braced!(inner2 in inner);
layout = Layout::parse(&inner2, gen)?;
layout = Layout::parse(&inner2, gen, recurse)?;
require_comma = false;
} else {
layout = Layout::parse(inner, gen)?;
layout = Layout::parse(inner, gen, recurse)?;
require_comma = true;
}
cells.push(ListItem {
Expand Down
2 changes: 2 additions & 0 deletions crates/kas-widgets/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ mod stack;
mod tab_stack;
mod text;

pub use kas_macros::{aligned_column, aligned_row, column, float, grid, list, row};

pub use crate::image::Image;
#[cfg(feature = "image")] pub use crate::image::ImageError;
pub use button::Button;
Expand Down
4 changes: 3 additions & 1 deletion crates/kas-widgets/src/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ impl_scope! {
///
/// [`Row`] and [`Column`] are type-defs to `List` which fix the direction `D`.
///
/// The macros [`kas::row`] and [`kas::column`] also create row/column
/// The macros [`row!`] and [`column!`] also create row/column
/// layouts, but are not fully equivalent:
///
/// - `row!` and `column!` generate anonymous layout widgets (or objects).
Expand All @@ -55,6 +55,8 @@ impl_scope! {
/// Drawing and event handling is O(log n) in the number of children (assuming
/// only a small number are visible at any one time).
///
/// [`row!`]: crate::row
/// [`column!`]: crate::column
/// [`set_direction`]: List::set_direction
#[autoimpl(Default where C: Default, D: Default)]
#[widget]
Expand Down
Loading

0 comments on commit 2b2d506

Please sign in to comment.