Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace some layout proc-macros with by-example macros over named widgets #476

Merged
merged 21 commits into from
Feb 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
5c8b218
Remove support for named storage fields
dhardy Feb 12, 2025
38774e1
Adapt: remove parameter S and doc state type restriction
dhardy Feb 10, 2025
14a0cb1
Add fn AdaptWidget::with_state(..) -> Adapt<..>
dhardy Feb 10, 2025
c94d31e
Replace column![..] with a shim over Column::new(kas::collection![..])
dhardy Feb 10, 2025
dc188b5
Replace row![..] with a shim over Row::new(kas::collection![..])
dhardy Feb 10, 2025
d10cc5a
Replace list![..] with a shim over List::new(kas::collection![..])
dhardy Feb 10, 2025
3b7b37a
Add frame! macro and fn Frame::with_style method
dhardy Feb 10, 2025
c32e26c
Fix parsing of unmatched method call error case
dhardy Feb 12, 2025
bfb66ad
Add fn Frame::with_background; remove button!
dhardy Feb 12, 2025
f9c0a07
Remove non_navigable! macro
dhardy Feb 12, 2025
2fedfcc
Fix AlignHints::TOP_LEFT / BOTTOM_RIGHT
dhardy Feb 12, 2025
9c66256
Move Align, Pack widgets to kas_core::hidden
dhardy Feb 13, 2025
da8ee46
Support align and pack method calls on string literals in collection!
dhardy Feb 13, 2025
710d4cb
Add Float widget
dhardy Feb 12, 2025
de011b8
Generate fn Tile::rect() only if widget_set_rect! is used
dhardy Feb 12, 2025
f9ae74b
Improve doc for new macros
dhardy Feb 13, 2025
4036449
Move grid! macro to kas_widgets
dhardy Feb 13, 2025
2307b46
Update aligned_column! / aligned_row! docs
dhardy Feb 13, 2025
9175888
Move aligned_column, aligned_row macros to kas_widgets
dhardy Feb 13, 2025
c86816b
Remove feature recursive-layout-widgets
dhardy Feb 13, 2025
875c5e8
Clippy
dhardy Feb 13, 2025
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
17 changes: 1 addition & 16 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ stable = ["default", "x11", "serde", "toml", "yaml", "json", "ron", "macros_log"
# Enables all "recommended" features for nightly rustc
nightly = ["stable", "nightly-diagnostics", "kas-core/nightly"]
# Additional, less recommendation-worthy features
experimental = ["dark-light", "recursive-layout-widgets", "unsafe_node"]
experimental = ["dark-light", "unsafe_node"]

# Enable dynamic linking (faster linking via an extra run-time dependency):
dynamic = ["dep:kas-dylib"]
Expand Down Expand Up @@ -114,21 +114,6 @@ wayland = ["kas-core/wayland"]
# Support X11
x11 = ["kas-core/x11"]

# 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"]

Expand Down
5 changes: 1 addition & 4 deletions crates/kas-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ stable = ["minimal", "clipboard", "markdown", "shaping", "spawn", "x11", "serde"
# Enables all "recommended" features for nightly rustc
nightly = ["stable", "nightly-diagnostics"]
# Additional, less recommendation-worthy features
experimental = ["dark-light", "recursive-layout-widgets", "unsafe_node"]
experimental = ["dark-light", "unsafe_node"]

# Enables better proc-macro diagnostics (including warnings); nightly only.
nightly-diagnostics = ["kas-macros/nightly"]
Expand Down Expand Up @@ -85,9 +85,6 @@ dark-light = ["dep:dark-light"]
# Support spawning async tasks
spawn = ["dep:async-global-executor"]

# Optimize generated layout widgets
recursive-layout-widgets = ["kas-macros/recursive-layout-widgets"]

# Optimize Node using unsafe code
unsafe_node = []

Expand Down
30 changes: 18 additions & 12 deletions crates/kas-core/src/core/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,10 +264,9 @@ pub trait Tile: Layout {

/// Get the widget's region, relative to its parent.
///
/// This method is implemented by the `#[widget]` macro.
fn rect(&self) -> Rect {
unimplemented!() // make rustdoc show that this is a provided method
}
/// This method is usually implemented by the `#[widget]` macro.
/// See also [`kas::widget_set_rect`].
fn rect(&self) -> Rect;

/// Get the name of the widget struct
///
Expand All @@ -291,6 +290,8 @@ pub trait Tile: Layout {

/// Access a child as a `dyn Tile`
///
/// This method returns `None` exactly when `index >= self.num_children()`.
///
/// This method is usually implemented automatically by the `#[widget]`
/// macro.
fn get_child(&self, index: usize) -> Option<&dyn Tile> {
Expand All @@ -316,16 +317,21 @@ pub trait Tile: Layout {
/// Controls <kbd>Tab</kbd> navigation order of children.
/// This method should:
///
/// - Return `None` if there is no next child
/// - Determine the next child after `from` (if provided) or the whole
/// range, optionally in `reverse` order
/// - Ensure that the selected widget is addressable through
/// [`Tile::get_child`]
/// - Return `None` if there is no (next) navigable child
/// - In the case there are navigable children and `from == None`, return
/// the index of the first (or last if `reverse`) navigable child
/// - In the case there are navigable children and `from == Some(index)`,
/// it may be expected that `from` is the output of a previous call to
/// this method; the method should return the next (or previous if
/// `reverse`) navigable child (if any)
///
/// The return value mut be `None` or `Some(index)` where
/// `self.get_child(index).is_some()` (see [`Tile::get_child`]).
///
/// Both `from` and the return value use the widget index, as used by
/// [`Tile::get_child`].
/// It is not required that all children (all indices `i` for
/// `i < self.num_children()`) are returnable from this method.
///
/// Default implementation:
/// Default (macro generated) implementation:
///
/// - Generated from `#[widget]`'s layout property, if used (not always possible!)
/// - Otherwise, iterate through children in order of definition
Expand Down
2 changes: 1 addition & 1 deletion crates/kas-core/src/draw/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ impl From<[u8; 4]> for Rgba8Srgb {
#[derive(Copy, Clone, Debug, Error)]
pub enum ParseError {
/// Incorrect input length
#[error("invalid length (expected 6 or 8 bytes")]
#[error("invalid length (expected 6 or 8 bytes)")]
Length,
/// Invalid hex byte
#[error("invalid hex byte (expected 0-9, a-f or A-F)")]
Expand Down
80 changes: 77 additions & 3 deletions crates/kas-core/src/hidden.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@
//! not supported (i.e. **changes are not considered breaking**).

use crate::event::ConfigCx;
use crate::geom::Rect;
use crate::layout::AlignHints;
use crate::theme::{Text, TextClass};
use crate::geom::{Rect, Size};
use crate::layout::{AlignHints, AxisInfo, SizeRules};
use crate::theme::{SizeCx, Text, TextClass};
#[allow(unused)] use crate::Action;
use crate::{Events, Layout, Widget};
use kas_macros::{autoimpl, impl_scope, widget_set_rect};

Expand Down Expand Up @@ -94,3 +95,76 @@ impl_scope! {
}
}
}

impl_scope! {
/// Apply an alignment hint
///
/// The inner widget chooses how to apply (or ignore) this hint.
///
/// Usually, this type will be constructed through one of the methods on
/// [`AdaptWidget`](https://docs.rs/kas/latest/kas/widgets/trait.AdaptWidget.html).
#[widget{ derive = self.inner; }]
pub struct Align<W: Widget> {
pub inner: W,
/// Hints may be modified directly.
///
/// Use [`Action::RESIZE`] to apply changes.
pub hints: AlignHints,
}

impl Self {
/// Construct
#[inline]
pub fn new(inner: W, hints: AlignHints) -> Self {
Align { inner, hints }
}
}

impl Layout for Self {
fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) {
self.inner.set_rect(cx, rect, self.hints.combine(hints));
}
}
}

impl_scope! {
/// Apply an alignment hint, squash and align the result
///
/// The inner widget chooses how to apply (or ignore) this hint.
/// The widget is then prevented from stretching beyond its ideal size,
/// aligning within the available rect.
///
/// Usually, this type will be constructed through one of the methods on
/// [`AdaptWidget`](https://docs.rs/kas/latest/kas/widgets/trait.AdaptWidget.html).
#[widget{ derive = self.inner; }]
pub struct Pack<W: Widget> {
pub inner: W,
/// Hints may be modified directly.
///
/// Use [`Action::RESIZE`] to apply changes.
pub hints: AlignHints,
size: Size,
}

impl Self {
/// Construct
#[inline]
pub fn new(inner: W, hints: AlignHints) -> Self {
Pack { inner, hints, size: Size::ZERO }
}
}

impl Layout for Self {
fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
let rules = self.inner.size_rules(sizer, axis);
self.size.set_component(axis, rules.ideal_size());
rules
}

fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) {
let align = self.hints.combine(hints).complete_default();
let rect = align.aligned_rect(self.size, rect);
self.inner.set_rect(cx, rect, hints);
}
}
}
4 changes: 2 additions & 2 deletions crates/kas-core/src/layout/align.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ impl AlignHints {
/// Top, left
pub const TOP_LEFT: AlignHints = AlignHints::new(Some(Align::TL), Some(Align::TL));
/// Top, right
pub const TOP_RIGHT: AlignHints = AlignHints::new(Some(Align::TL), Some(Align::BR));
pub const TOP_RIGHT: AlignHints = AlignHints::new(Some(Align::BR), Some(Align::TL));
/// Bottom, left
pub const BOTTOM_LEFT: AlignHints = AlignHints::new(Some(Align::BR), Some(Align::TL));
pub const BOTTOM_LEFT: AlignHints = AlignHints::new(Some(Align::TL), Some(Align::BR));
/// Bottom, right
pub const BOTTOM_RIGHT: AlignHints = AlignHints::new(Some(Align::BR), Some(Align::BR));

Expand Down
5 changes: 4 additions & 1 deletion crates/kas-core/src/layout/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,13 @@ impl<'a> Visitor<Box<dyn Layout + 'a>> {
storage: &'a mut FrameStorage,
child: C,
style: FrameStyle,
bg: Background,
) -> Visitor<impl Layout + 'a> {
Visitor(Frame {
child,
storage,
style,
bg,
})
}

Expand Down Expand Up @@ -340,6 +342,7 @@ struct Frame<'a, C: Layout> {
child: C,
storage: &'a mut FrameStorage,
style: FrameStyle,
bg: Background,
}

impl<'a, C: Layout> Layout for Frame<'a, C> {
Expand All @@ -365,7 +368,7 @@ impl<'a, C: Layout> Layout for Frame<'a, C> {
}

fn draw(&mut self, mut draw: DrawCx) {
draw.frame(self.storage.rect, self.style, Background::Default);
draw.frame(self.storage.rect, self.style, self.bg);
self.child.draw(draw);
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/kas-core/src/popup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ impl_scope! {
///
/// A popup receives input data from its parent like any other widget.
#[widget {
layout = frame!(self.inner, style = kas::theme::FrameStyle::Popup);
layout = frame!(self.inner).with_style(kas::theme::FrameStyle::Popup);
}]
pub struct Popup<W: Widget> {
core: widget_core!(),
Expand Down
35 changes: 34 additions & 1 deletion crates/kas-core/src/theme/draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

use super::{FrameStyle, MarkStyle, SelectionStyle, SizeCx, Text, TextClass, ThemeSize};
use crate::dir::Direction;
use crate::draw::color::Rgb;
use crate::draw::color::{ParseError, Rgb};
use crate::draw::{Draw, DrawIface, DrawShared, DrawSharedImpl, ImageId, PassType};
use crate::event::{ConfigCx, EventState};
use crate::geom::{Offset, Rect};
Expand All @@ -29,6 +29,39 @@ pub enum Background {
Rgb(Rgb),
}

impl From<Rgb> for Background {
#[inline]
fn from(color: Rgb) -> Self {
Background::Rgb(color)
}
}

#[derive(Copy, Clone, Debug, thiserror::Error)]
pub enum BackgroundParseError {
/// No `#` prefix
///
/// NOTE: this exists to allow the possibility of supporting new exprs like
/// "Default" or "Error".
#[error("Unknown: no `#` prefix")]
Unknown,
/// Invalid hex
#[error("invalid hex sequence")]
InvalidRgb(#[from] ParseError),
}

impl std::str::FromStr for Background {
type Err = BackgroundParseError;

#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.starts_with("#") {
Rgb::from_str(s).map(|c| c.into()).map_err(|e| e.into())
} else {
Err(BackgroundParseError::Unknown)
}
}
}

/// Draw interface
///
/// This interface is provided to widgets in [`crate::Layout::draw`].
Expand Down
2 changes: 1 addition & 1 deletion crates/kas-core/src/theme/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ impl<T: FormattableText> Layout for Text<T> {
}

fn draw(&mut self, mut draw: DrawCx) {
draw.text(self.rect, &self);
draw.text(self.rect, self);
}
}

Expand Down
3 changes: 0 additions & 3 deletions crates/kas-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ proc-macro = true
# Requires that all crates using these macros depend on the log crate.
log = []

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

# Enable reporting of warnings from proc-macros
nightly = ["proc-macro-error2/nightly"]

Expand Down
Loading