diff --git a/Cargo.toml b/Cargo.toml index 5ffeb879b..294a53123 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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] diff --git a/crates/kas-core/Cargo.toml b/crates/kas-core/Cargo.toml index facb402cc..44f8e41e6 100644 --- a/crates/kas-core/Cargo.toml +++ b/crates/kas-core/Cargo.toml @@ -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] diff --git a/crates/kas-core/src/geom.rs b/crates/kas-core/src/geom.rs index f96790973..3940a81e6 100644 --- a/crates/kas-core/src/geom.rs +++ b/crates/kas-core/src/geom.rs @@ -636,19 +636,19 @@ impl std::ops::SubAssign 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> ConvApprox> for Coord { #[inline] - fn try_conv_approx(pos: PhysicalPosition) -> cast::Result { + fn try_conv_approx(pos: PhysicalPosition) -> Result { Ok(Coord(pos.x.cast_approx(), pos.y.cast_approx())) } } impl> Conv> for Size { #[inline] - fn try_conv(size: PhysicalSize) -> cast::Result { + fn try_conv(size: PhysicalSize) -> Result { Ok(Size(size.width.cast(), size.height.cast())) } } diff --git a/crates/kas-core/src/lib.rs b/crates/kas-core/src/lib.rs index 9fe162e9f..9bea034e4 100644 --- a/crates/kas-core/src/lib.rs +++ b/crates/kas-core/src/lib.rs @@ -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)] diff --git a/crates/kas-macros/Cargo.toml b/crates/kas-macros/Cargo.toml index 4acb7e0b6..f7548117c 100644 --- a/crates/kas-macros/Cargo.toml +++ b/crates/kas-macros/Cargo.toml @@ -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" } diff --git a/crates/kas-macros/src/lib.rs b/crates/kas-macros/src/lib.rs index 311bd70c4..5f2fb0393 100644 --- a/crates/kas-macros/src/lib.rs +++ b/crates/kas-macros/src/lib.rs @@ -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), /// ]); /// ``` /// diff --git a/crates/kas-macros/src/make_layout.rs b/crates/kas-macros/src/make_layout.rs index 78ea6feaa..ae067fa49 100644 --- a/crates/kas-macros/src/make_layout.rs +++ b/crates/kas-macros/src/make_layout.rs @@ -266,7 +266,7 @@ impl Tree { pub fn column(inner: ParseStream) -> Result { 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))) } @@ -274,7 +274,7 @@ impl Tree { pub fn row(inner: ParseStream) -> Result { 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))) } @@ -287,6 +287,7 @@ impl Tree { inner, &mut gen, true, + false, )?)) } @@ -299,6 +300,7 @@ impl Tree { inner, &mut gen, false, + false, )?)) } @@ -308,14 +310,14 @@ 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 { 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))) } @@ -323,7 +325,7 @@ impl Tree { pub fn grid(inner: ParseStream) -> Result { 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)?)) } } @@ -504,13 +506,45 @@ impl GridDimensions { impl Parse for Tree { fn parse(input: ParseStream) -> Result { 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 { - let mut layout = if input.peek2(Token![!]) { + fn parse(input: ParseStream, gen: &mut NameGenerator, _recurse: bool) -> Result { + #[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::() { + 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::() { + 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()?) @@ -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()?; @@ -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()?; @@ -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()?; @@ -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()?; @@ -638,7 +672,7 @@ impl Layout { let inner; let _ = bracketed!(inner in input); Ok(parse_grid_as_list_of_lists::( - stor, &inner, gen, true, + stor, &inner, gen, true, true, )?) } else if lookahead.peek(kw::aligned_row) { let _: kw::aligned_row = input.parse()?; @@ -648,7 +682,7 @@ impl Layout { let inner; let _ = bracketed!(inner in input); Ok(parse_grid_as_list_of_lists::( - stor, &inner, gen, false, + stor, &inner, gen, false, true, )?) } else if lookahead.peek(kw::grid) { let _: kw::grid = input.parse()?; @@ -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(); @@ -674,20 +708,28 @@ impl Layout { } } -fn parse_layout_list(input: ParseStream, gen: &mut NameGenerator) -> Result> { +fn parse_layout_list( + input: ParseStream, + gen: &mut NameGenerator, + recurse: bool, +) -> Result> { 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> { +fn parse_layout_items( + inner: ParseStream, + gen: &mut NameGenerator, + recurse: bool, +) -> Result> { 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() { @@ -705,6 +747,7 @@ fn parse_grid_as_list_of_lists( inner: ParseStream, gen: &mut NameGenerator, row_major: bool, + recurse: bool, ) -> Result { let (mut col, mut row) = (0, 0); let mut dim = GridDimensions::default(); @@ -721,7 +764,7 @@ fn parse_grid_as_list_of_lists( 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(), @@ -757,7 +800,12 @@ fn parse_grid_as_list_of_lists( Ok(Layout::Grid(stor, dim, VisitableList(cells))) } -fn parse_grid(stor: StorIdent, inner: ParseStream, gen: &mut NameGenerator) -> Result { +fn parse_grid( + stor: StorIdent, + inner: ParseStream, + gen: &mut NameGenerator, + recurse: bool, +) -> Result { let mut dim = GridDimensions::default(); let mut gen2 = NameGenerator::default(); let mut cells = vec![]; @@ -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 { diff --git a/crates/kas-widgets/src/lib.rs b/crates/kas-widgets/src/lib.rs index 3f74a3ac0..7b309fcc5 100644 --- a/crates/kas-widgets/src/lib.rs +++ b/crates/kas-widgets/src/lib.rs @@ -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; diff --git a/crates/kas-widgets/src/list.rs b/crates/kas-widgets/src/list.rs index 88c14954b..47d891449 100644 --- a/crates/kas-widgets/src/list.rs +++ b/crates/kas-widgets/src/list.rs @@ -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). @@ -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] diff --git a/examples/calculator.rs b/examples/calculator.rs index 560674dd4..fd20790a9 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -10,7 +10,7 @@ use std::str::FromStr; use kas::event::NamedKey; use kas::prelude::*; -use kas::widgets::{AccessLabel, Adapt, Button, EditBox}; +use kas::widgets::{column, grid, AccessLabel, Adapt, Button, EditBox}; type Key = kas::event::Key; @@ -31,7 +31,7 @@ fn calc_ui() -> Window<()> { .with_width_em(5.0, 10.0); // We use map_any to avoid passing input data (not wanted by buttons): - let buttons = kas::grid! { + let buttons = grid! { // Key bindings: C, Del (0, 0) => Button::label_msg("&clear", Key::Named(NamedKey::Clear)) .with_access_key(NamedKey::Delete.into()), @@ -57,7 +57,7 @@ fn calc_ui() -> Window<()> { } .map_any(); - let ui = Adapt::new(kas::column![display, buttons], Calculator::new()) + let ui = Adapt::new(column![display, buttons], Calculator::new()) .on_message(|_, calc, key| calc.handle(key)) .on_configure(|cx, _| { cx.disable_nav_focus(true); diff --git a/examples/counter.rs b/examples/counter.rs index 3be7e602c..9633fcc6b 100644 --- a/examples/counter.rs +++ b/examples/counter.rs @@ -6,13 +6,13 @@ //! Counter example (simple button) use kas::prelude::*; -use kas::widgets::{format_value, Adapt, Button}; +use kas::widgets::{column, format_value, row, Adapt, Button}; #[derive(Clone, Debug)] struct Increment(i32); fn counter() -> impl Widget { - let tree = kas::column![ + let tree = column![ format_value!("{}").align(AlignHints::CENTER), row![ Button::label_msg("−", Increment(-1)), diff --git a/examples/data-list-view.rs b/examples/data-list-view.rs index 4bad96385..7295f5949 100644 --- a/examples/data-list-view.rs +++ b/examples/data-list-view.rs @@ -11,9 +11,8 @@ //! to calculate the maximum scroll offset). use kas::prelude::*; -use kas::row; use kas::view::{Driver, ListData, ListView, SharedData}; -use kas::widgets::*; +use kas::widgets::{column, *}; use std::collections::HashMap; #[derive(Debug)] @@ -206,7 +205,7 @@ fn main() -> kas::app::Result<()> { let act = list.set_direction(data.dir); cx.action(list, act); }); - let tree = kas::column![ + let tree = column![ "Demonstration of dynamic widget creation / deletion", controls.map(|data: &Data| &data.len), "Contents of selected entry:", diff --git a/examples/data-list.rs b/examples/data-list.rs index 6ebe362e2..54ebf4deb 100644 --- a/examples/data-list.rs +++ b/examples/data-list.rs @@ -20,8 +20,8 @@ //! is still fast. use kas::prelude::*; -use kas::row; use kas::widgets::edit::{EditBox, EditField, EditGuard}; +use kas::widgets::{column, row}; use kas::widgets::{Adapt, Button, Label, List, RadioButton, ScrollBarRegion, Separator, Text}; #[derive(Debug)] @@ -170,7 +170,7 @@ fn main() -> kas::app::Result<()> { } cx.action(list, act); }); - let tree = kas::column![ + let tree = column![ "Demonstration of dynamic widget creation / deletion", controls.map(|data: &Data| &data.len), "Contents of selected entry:", diff --git a/examples/gallery.rs b/examples/gallery.rs index 07a6831fe..2a498cf32 100644 --- a/examples/gallery.rs +++ b/examples/gallery.rs @@ -8,12 +8,13 @@ //! This is a test-bed to demonstrate most toolkit functionality //! (excepting custom graphics). +use kas::collection; use kas::dir::Right; use kas::event::Key; use kas::prelude::*; use kas::resvg::Svg; use kas::theme::{MarginStyle, ThemeControl}; -use kas::widgets::*; +use kas::widgets::{column, *}; #[derive(Debug, Default)] struct AppData { @@ -54,6 +55,7 @@ fn widgets() -> Box> { ("Th&ree", Entry::Three), ]; + #[allow(unused)] #[derive(Clone, Debug)] enum Item { Button, @@ -146,7 +148,7 @@ fn widgets() -> Box> { Пример текста на нескольких языках. טקסט לדוגמא במספר שפות."; - let widgets = kas::aligned_column![ + let widgets = aligned_column![ row!["ScrollLabel", ScrollLabel::new(text).map_any()], row![ "EditBox", @@ -306,11 +308,11 @@ Demonstration of *as-you-type* formatting from **Markdown**. ``` "; - let ui = kas::float![ + let ui = float![ Button::label_msg("↻", MsgDirection) .map_any() .pack(AlignHints::TOP_RIGHT), - Splitter::new(kas::collection![ + Splitter::new(collection![ EditBox::new(Guard) .with_multi_line(true) .with_lines(4, 12) @@ -391,13 +393,13 @@ fn filter_list() -> Box> { cx.action(list, act); }); - let sel_buttons = kas::row![ + let sel_buttons = row![ "Selection:", RadioButton::new_value("&n&one", SelectionMode::None), RadioButton::new_value("s&ingle", SelectionMode::Single), RadioButton::new_value("&multiple", SelectionMode::Multiple), ]; - let ui = kas::column![ + let ui = column![ sel_buttons.map(|data: &Data| &data.mode), ScrollBars::new(list_view), ]; @@ -486,7 +488,7 @@ fn canvas() -> Box> { } } - let ui = kas::column![ + let ui = column![ Label::new( "Animated canvas demo (CPU-rendered, async). Note: scheduling is broken on X11." ), @@ -512,7 +514,7 @@ KAS_CONFIG_MODE=readwrite ) .unwrap(); - let ui = kas::column![ScrollLabel::new(desc), Separator::new(), EventConfig::new(),] + let ui = column![ScrollLabel::new(desc), Separator::new(), EventConfig::new(),] .map_any() .on_update(|cx, _, data: &AppData| cx.set_disabled(data.disabled)); Box::new(ui) @@ -582,7 +584,7 @@ fn main() -> kas::app::Result<()> { }) .build(); - let ui = kas::column![ + let ui = column![ menubar, Separator::new(), TabStack::from([ diff --git a/examples/layout.rs b/examples/layout.rs index ab1f445de..a7ea31eec 100644 --- a/examples/layout.rs +++ b/examples/layout.rs @@ -6,7 +6,7 @@ //! Demonstration of widget and text layouts use kas::layout::AlignHints; -use kas::widgets::{AdaptWidget, CheckBox, EditBox, ScrollLabel}; +use kas::widgets::{grid, AdaptWidget, CheckBox, EditBox, ScrollLabel}; use kas::Window; const LIPSUM: &str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi nunc mi, consequat eget urna ut, auctor luctus mi. Sed molestie mi est. Sed non ligula ante. Curabitur ac molestie ante, nec sodales eros. In non arcu at turpis euismod bibendum ut tincidunt eros. Suspendisse blandit maximus nisi, viverra hendrerit elit efficitur et. Morbi ut facilisis eros. Vivamus dignissim, sapien sed mattis consectetur, libero leo imperdiet turpis, ac pulvinar libero purus eu lorem. Etiam quis sollicitudin urna. Integer vitae erat vel neque gravida blandit ac non quam."; @@ -15,7 +15,7 @@ const CRASIT: &str = "Cras sit amet justo ipsum. Aliquam in nunc posuere leo ege fn main() -> kas::app::Result<()> { env_logger::init(); - let ui = kas::grid! { + let ui = grid! { (1, 0) => "Layout demo", (2, 0) => CheckBox::new(|_, _| true), (0..3, 1) => ScrollLabel::new(LIPSUM), diff --git a/examples/splitter.rs b/examples/splitter.rs index 3c2e8bcc5..de2b928b0 100644 --- a/examples/splitter.rs +++ b/examples/splitter.rs @@ -6,7 +6,7 @@ //! Counter example (simple button) use kas::prelude::*; -use kas::widgets::{Adapt, Button, EditField, Splitter}; +use kas::widgets::{column, row, Adapt, Button, EditField, Splitter}; #[derive(Clone, Debug)] enum Message { @@ -17,7 +17,7 @@ enum Message { fn main() -> kas::app::Result<()> { env_logger::init(); - let ui = kas::column![ + let ui = column![ row![ Button::label_msg("−", Message::Decr), Button::label_msg("+", Message::Incr), diff --git a/examples/stopwatch.rs b/examples/stopwatch.rs index 92e3a009e..7617eafa5 100644 --- a/examples/stopwatch.rs +++ b/examples/stopwatch.rs @@ -8,7 +8,7 @@ use std::time::{Duration, Instant}; use kas::prelude::*; -use kas::widgets::{format_data, Adapt, Button}; +use kas::widgets::{format_data, row, Adapt, Button}; use kas::Decorations; #[derive(Clone, Debug)] @@ -23,7 +23,7 @@ struct Timer { } fn make_window() -> impl Widget { - let ui = kas::row![ + let ui = row![ format_data!(timer: &Timer, "{}.{:03}", timer.elapsed.as_secs(), timer.elapsed.subsec_millis()), Button::label_msg("&reset", MsgReset).map_any(), Button::label_msg("&start / &stop", MsgStart).map_any(), diff --git a/examples/sync-counter.rs b/examples/sync-counter.rs index 9be5276f2..5aac3e222 100644 --- a/examples/sync-counter.rs +++ b/examples/sync-counter.rs @@ -7,7 +7,7 @@ //! //! Each window shares the counter, but has its own increment step. -use kas::widgets::{format_data, label_any, Adapt, Button, Slider}; +use kas::widgets::{column, format_data, label_any, row, Adapt, Button, Slider}; use kas::{messages::MessageStack, Window}; #[derive(Clone, Debug)] @@ -37,7 +37,7 @@ fn counter(title: &str) -> Window { struct SetValue(i32); let slider = Slider::right(1..=10, |_, data: &Data| data.1).with_msg(SetValue); - let ui = kas::column![ + let ui = column![ format_data!(data: &Data, "Count: {}", data.0.0), row![slider, format_data!(data: &Data, "{}", data.1)], row![ diff --git a/examples/times-tables.rs b/examples/times-tables.rs index 0ea18b3f3..fae5cfd05 100644 --- a/examples/times-tables.rs +++ b/examples/times-tables.rs @@ -2,7 +2,7 @@ use kas::prelude::*; use kas::view::{driver, MatrixData, MatrixView, SelectionMode, SelectionMsg, SharedData}; -use kas::widgets::{Adapt, EditBox, ScrollBars}; +use kas::widgets::{column, row, Adapt, EditBox, ScrollBars}; #[derive(Debug)] struct TableSize(usize); @@ -54,7 +54,7 @@ fn main() -> kas::app::Result<()> { #[derive(Debug)] struct SetLen(usize); - let ui = kas::column![ + let ui = column![ row![ "From 1 to", EditBox::parser(|data: &TableSize| data.0, SetLen) diff --git a/tests/layout_macros.rs b/tests/layout_macros.rs index 677a608c9..7378c69df 100644 --- a/tests/layout_macros.rs +++ b/tests/layout_macros.rs @@ -1,26 +1,27 @@ use kas::layout::AlignHints; +use kas::widgets::{aligned_column, aligned_row, column, float, grid, list, row}; use kas::Widget; fn use_widget>(_: W) {} #[test] fn column() { - use_widget(kas::column!["one", "two",]) + use_widget(column!["one", "two",]) } #[test] fn row() { - use_widget(kas::row!["one", "two"]); + use_widget(row!["one", "two"]); } #[test] fn list() { - use_widget(kas::list!(left, ["one", "two"])); + use_widget(list!(left, ["one", "two"])); } #[test] fn float() { - use_widget(kas::float![ + use_widget(float![ "one".pack(AlignHints::TOP_LEFT), "two".pack(AlignHints::BOTTOM_RIGHT), "some text\nin the\nbackground", @@ -29,7 +30,7 @@ fn float() { #[test] fn grid() { - use_widget(kas::grid! { + use_widget(grid! { (0, 0) => "top left", (1, 0) => "top right", (0..2, 1) => "bottom row (merged)", @@ -38,14 +39,16 @@ fn grid() { #[test] fn aligned_column() { - use_widget(kas::aligned_column![row!["one", "two"], row![ - "three", "four" - ],]); + #[rustfmt::skip] + use_widget(aligned_column![ + row!["one", "two"], + row!["three", "four"], + ]); } #[test] fn aligned_row() { - use_widget(kas::aligned_row![column!["one", "two"], column![ + use_widget(aligned_row![column!["one", "two"], column![ "three", "four" ],]); }