Document more items (#899)

Make missing_docs annotations more granular.
This commit is contained in:
Olivier FAURE 2025-03-21 15:50:16 +01:00 committed by GitHub
parent ab341367b0
commit 125dab7949
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 269 additions and 147 deletions

View File

@ -1,8 +1,7 @@
// Copyright 2024 the Xilem Authors // Copyright 2024 the Xilem Authors
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// We use allow because expect(missing_docs) is noisy with rust-analyzer. #![expect(missing_docs, reason = "TODO - Document these items")]
#![allow(missing_docs, reason = "We have many as-yet undocumented items")]
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
use std::sync::Arc; use std::sync::Arc;

View File

@ -10,12 +10,9 @@ mod event;
mod object_fit; mod object_fit;
mod properties; mod properties;
mod text; mod text;
#[allow(missing_docs, reason = "TODO")]
mod widget; mod widget;
mod widget_arena; mod widget_arena;
#[allow(missing_docs, reason = "TODO")]
mod widget_mut; mod widget_mut;
#[allow(missing_docs, reason = "TODO")]
mod widget_pod; mod widget_pod;
mod widget_ref; mod widget_ref;
mod widget_state; mod widget_state;

View File

@ -45,6 +45,7 @@ use crate::kurbo::{Point, Size};
pub struct WidgetId(pub(crate) NonZeroU64); pub struct WidgetId(pub(crate) NonZeroU64);
impl WidgetId { impl WidgetId {
/// A serialized representation of the `WidgetId` for debugging purposes.
pub fn trace(self) -> DisplayValue<Self> { pub fn trace(self) -> DisplayValue<Self> {
tracing::field::display(self) tracing::field::display(self)
} }
@ -242,6 +243,7 @@ pub trait Widget: AsAny + AsDynWidget {
bc: &BoxConstraints, bc: &BoxConstraints,
) -> Size; ) -> Size;
/// Runs after the widget's final transform has been computed.
fn compose(&mut self, ctx: &mut ComposeCtx) {} fn compose(&mut self, ctx: &mut ComposeCtx) {}
/// Paint the widget appearance. /// Paint the widget appearance.
@ -495,6 +497,7 @@ impl WidgetId {
} }
} }
/// Returns the integer value of the `WidgetId`.
pub fn to_raw(self) -> u64 { pub fn to_raw(self) -> u64 {
self.0.into() self.0.into()
} }

View File

@ -8,8 +8,6 @@ use anymap3::Entry;
use crate::core::{FromDynWidget, MutateCtx, Widget}; use crate::core::{FromDynWidget, MutateCtx, Widget};
use crate::kurbo::Affine; use crate::kurbo::Affine;
// TODO - Document extension trait workaround.
// See https://xi.zulipchat.com/#narrow/stream/317477-masonry/topic/Thoughts.20on.20simplifying.20WidgetMut/near/436478885
/// A rich mutable reference to a [`Widget`]. /// A rich mutable reference to a [`Widget`].
/// ///
/// In Masonry, widgets can't be mutated directly. All mutations go through a `WidgetMut` /// In Masonry, widgets can't be mutated directly. All mutations go through a `WidgetMut`
@ -29,8 +27,10 @@ use crate::kurbo::Affine;
/// widgets in downstream crates can use `WidgetMut` as the receiver for inherent methods. /// widgets in downstream crates can use `WidgetMut` as the receiver for inherent methods.
#[non_exhaustive] #[non_exhaustive]
pub struct WidgetMut<'a, W: Widget + ?Sized> { pub struct WidgetMut<'a, W: Widget + ?Sized> {
pub ctx: MutateCtx<'a>, /// The widget we're mutating.
pub widget: &'a mut W, pub widget: &'a mut W,
/// A context handle that points to the widget state and other relevant data.
pub ctx: MutateCtx<'a>,
} }
impl<W: Widget + ?Sized> Drop for WidgetMut<'_, W> { impl<W: Widget + ?Sized> Drop for WidgetMut<'_, W> {
@ -48,8 +48,8 @@ impl<W: Widget + ?Sized> WidgetMut<'_, W> {
pub fn reborrow_mut(&mut self) -> WidgetMut<'_, W> { pub fn reborrow_mut(&mut self) -> WidgetMut<'_, W> {
let widget = &mut self.widget; let widget = &mut self.widget;
WidgetMut { WidgetMut {
ctx: self.ctx.reborrow_mut(),
widget, widget,
ctx: self.ctx.reborrow_mut(),
} }
} }
@ -118,8 +118,8 @@ impl<W: Widget + ?Sized> WidgetMut<'_, W> {
let w1_name = self.widget.type_name(); let w1_name = self.widget.type_name();
match W2::from_dyn_mut(self.widget.as_mut_dyn()) { match W2::from_dyn_mut(self.widget.as_mut_dyn()) {
Some(widget) => WidgetMut { Some(widget) => WidgetMut {
ctx: self.ctx.reborrow_mut(),
widget, widget,
ctx: self.ctx.reborrow_mut(),
}, },
None => { None => {
panic!( panic!(

View File

@ -55,6 +55,7 @@ impl<W: Widget + ?Sized> WidgetPod<W> {
Self::new_with_id_and_transform(inner, WidgetId::next(), transform) Self::new_with_id_and_transform(inner, WidgetId::next(), transform)
} }
/// Create a new widget pod with a custom transform and a pre-set [`WidgetId`].
pub fn new_with_id_and_transform(inner: Box<W>, id: WidgetId, transform: Affine) -> Self { pub fn new_with_id_and_transform(inner: Box<W>, id: WidgetId, transform: Affine) -> Self {
Self { Self {
id, id,
@ -67,6 +68,7 @@ impl<W: Widget + ?Sized> WidgetPod<W> {
} }
// TODO - Remove transform, have it as a special-case property instead. // TODO - Remove transform, have it as a special-case property instead.
/// Create a new widget pod with a custom transform and custom [`Properties`].
pub fn new_with(inner: Box<W>, id: WidgetId, transform: Affine, props: Properties) -> Self { pub fn new_with(inner: Box<W>, id: WidgetId, transform: Affine, props: Properties) -> Self {
Self { Self {
id, id,

View File

@ -1,9 +1,9 @@
// Copyright 2019 the Xilem Authors and the Druid Authors // Copyright 2019 the Xilem Authors and the Druid Authors
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
//! Theme keys and initial values. //! Default values used by various widgets in their paint methods.
#![allow(missing_docs)] #![allow(missing_docs, reason = "Names are self-explanatory.")]
use crate::kurbo::Insets; use crate::kurbo::Insets;
use crate::peniko::Color; use crate::peniko::Color;

View File

@ -86,7 +86,9 @@ impl<T: Any> AsAny for T {
// --- MARK: PAINT HELPERS --- // --- MARK: PAINT HELPERS ---
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
#[allow(missing_docs)] /// A point with coordinates in the range [0.0, 1.0].
///
/// This is useful for specifying points in a normalized space, such as a gradient.
pub struct UnitPoint { pub struct UnitPoint {
u: f64, u: f64,
v: f64, v: f64,
@ -96,7 +98,7 @@ pub struct UnitPoint {
single_use_lifetimes, single_use_lifetimes,
reason = "Anonymous lifetimes in `impl Trait` are unstable, see https://github.com/rust-lang/rust/issues/129255" reason = "Anonymous lifetimes in `impl Trait` are unstable, see https://github.com/rust-lang/rust/issues/129255"
)] )]
#[allow(missing_docs)] /// Helper function for [`Scene::stroke`].
pub fn stroke<'b>( pub fn stroke<'b>(
scene: &mut Scene, scene: &mut Scene,
path: &impl Shape, path: &impl Shape,
@ -112,8 +114,6 @@ pub fn stroke<'b>(
scene.stroke(&style, Affine::IDENTITY, brush, None, path); scene.stroke(&style, Affine::IDENTITY, brush, None, path);
} }
#[allow(unused)]
#[allow(missing_docs)]
impl UnitPoint { impl UnitPoint {
/// `(0.0, 0.0)` /// `(0.0, 0.0)`
pub const TOP_LEFT: Self = Self::new(0.0, 0.0); pub const TOP_LEFT: Self = Self::new(0.0, 0.0);
@ -151,7 +151,7 @@ impl UnitPoint {
} }
} }
#[allow(missing_docs)] /// Helper function for [`Scene::fill`] with a linear gradient as the brush.
pub fn fill_lin_gradient( pub fn fill_lin_gradient(
scene: &mut Scene, scene: &mut Scene,
path: &impl Shape, path: &impl Shape,
@ -164,7 +164,7 @@ pub fn fill_lin_gradient(
scene.fill(Fill::NonZero, Affine::IDENTITY, &brush, None, path); scene.fill(Fill::NonZero, Affine::IDENTITY, &brush, None, path);
} }
#[allow(missing_docs)] /// Helper function for [`Scene::fill`] with a uniform color as the brush.
pub fn fill_color(scene: &mut Scene, path: &impl Shape, color: Color) { pub fn fill_color(scene: &mut Scene, path: &impl Shape, color: Color) {
scene.fill(Fill::NonZero, Affine::IDENTITY, color, None, path); scene.fill(Fill::NonZero, Affine::IDENTITY, color, None, path);
} }

View File

@ -79,7 +79,7 @@ impl Button {
Label::set_text(&mut Self::label_mut(this), new_text); Label::set_text(&mut Self::label_mut(this), new_text);
} }
#[expect(missing_docs, reason = "TODO")] /// Get a mutable reference to the label.
pub fn label_mut<'t>(this: &'t mut WidgetMut<'_, Self>) -> WidgetMut<'t, Label> { pub fn label_mut<'t>(this: &'t mut WidgetMut<'_, Self>) -> WidgetMut<'t, Label> {
this.ctx.get_mut(&mut this.widget.label) this.ctx.get_mut(&mut this.widget.label)
} }

View File

@ -46,7 +46,7 @@ impl Checkbox {
// --- MARK: WIDGETMUT --- // --- MARK: WIDGETMUT ---
impl Checkbox { impl Checkbox {
#[expect(missing_docs, reason = "TODO")] /// Check or uncheck the box.
pub fn set_checked(this: &mut WidgetMut<'_, Self>, checked: bool) { pub fn set_checked(this: &mut WidgetMut<'_, Self>, checked: bool) {
this.widget.checked = checked; this.widget.checked = checked;
// Checked state impacts appearance and accessibility node // Checked state impacts appearance and accessibility node
@ -60,7 +60,7 @@ impl Checkbox {
Label::set_text(&mut Self::label_mut(this), new_text); Label::set_text(&mut Self::label_mut(this), new_text);
} }
#[expect(missing_docs, reason = "TODO")] /// Get a mutable reference to the label.
pub fn label_mut<'t>(this: &'t mut WidgetMut<'_, Self>) -> WidgetMut<'t, Label> { pub fn label_mut<'t>(this: &'t mut WidgetMut<'_, Self>) -> WidgetMut<'t, Label> {
this.ctx.get_mut(&mut this.widget.label) this.ctx.get_mut(&mut this.widget.label)
} }

View File

@ -294,12 +294,12 @@ impl Flex {
self self
} }
#[expect(missing_docs, reason = "TODO")] /// Returns the number of children (widgets and spacers) this flex container has.
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.children.len() self.children.len()
} }
#[expect(missing_docs, reason = "TODO")] /// Returns `true` if this flex container has no children (widgets or spacers).
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.len() == 0 self.len() == 0
} }
@ -390,7 +390,11 @@ impl Flex {
this.ctx.children_changed(); this.ctx.children_changed();
} }
#[expect(missing_docs, reason = "TODO")] /// Add a non-flex child widget with a pre-assigned id.
///
/// See also [`with_child_id`].
///
/// [`with_child_id`]: Flex::with_child_id
pub fn add_child_id(this: &mut WidgetMut<'_, Self>, child: impl Widget, id: WidgetId) { pub fn add_child_id(this: &mut WidgetMut<'_, Self>, child: impl Widget, id: WidgetId) {
let child = Child::Fixed { let child = Child::Fixed {
widget: WidgetPod::new_with_id(child, id).erased(), widget: WidgetPod::new_with_id(child, id).erased(),
@ -453,16 +457,20 @@ impl Flex {
this.ctx.request_layout(); this.ctx.request_layout();
} }
/// Add a non-flex child widget. /// Insert a non-flex child widget at the given index.
/// ///
/// See also [`with_child`]. /// # Panics
/// ///
/// [`with_child`]: Flex::with_child /// Panics if the index is larger than the number of children.
pub fn insert_child(this: &mut WidgetMut<'_, Self>, idx: usize, child: impl Widget) { pub fn insert_child(this: &mut WidgetMut<'_, Self>, idx: usize, child: impl Widget) {
Self::insert_child_pod(this, idx, WidgetPod::new(child).erased()); Self::insert_child_pod(this, idx, WidgetPod::new(child).erased());
} }
/// Add a non-flex child widget. /// Insert a non-flex child widget wrapped in a [`WidgetPod`] at the given index.
///
/// # Panics
///
/// Panics if the index is larger than the number of children.
pub fn insert_child_pod( pub fn insert_child_pod(
this: &mut WidgetMut<'_, Self>, this: &mut WidgetMut<'_, Self>,
idx: usize, idx: usize,
@ -476,7 +484,11 @@ impl Flex {
this.ctx.children_changed(); this.ctx.children_changed();
} }
#[expect(missing_docs, reason = "TODO")] /// Insert a flex child widget at the given index.
///
/// # Panics
///
/// Panics if the index is larger than the number of children.
pub fn insert_flex_child( pub fn insert_flex_child(
this: &mut WidgetMut<'_, Self>, this: &mut WidgetMut<'_, Self>,
idx: usize, idx: usize,
@ -486,7 +498,11 @@ impl Flex {
Self::insert_flex_child_pod(this, idx, WidgetPod::new(child).erased(), params); Self::insert_flex_child_pod(this, idx, WidgetPod::new(child).erased(), params);
} }
#[expect(missing_docs, reason = "TODO")] /// Insert a flex child widget wrapped in a [`WidgetPod`] at the given index.
///
/// # Panics
///
/// Panics if the index is larger than the number of children.
pub fn insert_flex_child_pod( pub fn insert_flex_child_pod(
this: &mut WidgetMut<'_, Self>, this: &mut WidgetMut<'_, Self>,
idx: usize, idx: usize,
@ -499,22 +515,30 @@ impl Flex {
} }
// TODO - remove // TODO - remove
/// Add a spacer widget with a standard size. /// Insert a spacer widget with a standard size at the given index.
/// ///
/// The actual value of this spacer depends on whether this container is /// The actual value of this spacer depends on whether this container is
/// a row or column, as well as theme settings. /// a row or column, as well as theme settings.
///
/// # Panics
///
/// Panics if the index is larger than the number of children.
pub fn insert_default_spacer(this: &mut WidgetMut<'_, Self>, idx: usize) { pub fn insert_default_spacer(this: &mut WidgetMut<'_, Self>, idx: usize) {
let key = axis_default_spacer(this.widget.direction); let key = axis_default_spacer(this.widget.direction);
Self::insert_spacer(this, idx, key); Self::insert_spacer(this, idx, key);
this.ctx.request_layout(); this.ctx.request_layout();
} }
/// Add an empty spacer widget with the given size. /// Insert an empty spacer widget with the given size at the given index.
/// ///
/// If you are laying out standard controls in this container, you should /// If you are laying out standard controls in this container, you should
/// generally prefer to use [`add_default_spacer`]. /// generally prefer to use [`add_default_spacer`].
/// ///
/// [`add_default_spacer`]: Flex::add_default_spacer /// [`add_default_spacer`]: Flex::add_default_spacer
///
/// # Panics
///
/// Panics if the index is larger than the number of children.
pub fn insert_spacer(this: &mut WidgetMut<'_, Self>, idx: usize, mut len: f64) { pub fn insert_spacer(this: &mut WidgetMut<'_, Self>, idx: usize, mut len: f64) {
if len < 0.0 { if len < 0.0 {
tracing::warn!("add_spacer called with negative length: {}", len); tracing::warn!("add_spacer called with negative length: {}", len);
@ -527,6 +551,10 @@ impl Flex {
} }
/// Add an empty spacer widget with a specific `flex` factor. /// Add an empty spacer widget with a specific `flex` factor.
///
/// # Panics
///
/// Panics if the index is larger than the number of children.
pub fn insert_flex_spacer(this: &mut WidgetMut<'_, Self>, idx: usize, flex: f64) { pub fn insert_flex_spacer(this: &mut WidgetMut<'_, Self>, idx: usize, flex: f64) {
let flex = if flex >= 0.0 { let flex = if flex >= 0.0 {
flex flex
@ -539,7 +567,13 @@ impl Flex {
this.ctx.request_layout(); this.ctx.request_layout();
} }
#[expect(missing_docs, reason = "TODO")] /// Remove the child at `idx`.
///
/// This child can be a widget or a spacer.
///
/// # Panics
///
/// Panics if the index is larger than the number of children.
pub fn remove_child(this: &mut WidgetMut<'_, Self>, idx: usize) { pub fn remove_child(this: &mut WidgetMut<'_, Self>, idx: usize) {
let child = this.widget.children.remove(idx); let child = this.widget.children.remove(idx);
if let Child::Fixed { widget, .. } | Child::Flex { widget, .. } = child { if let Child::Fixed { widget, .. } | Child::Flex { widget, .. } = child {
@ -548,7 +582,13 @@ impl Flex {
this.ctx.request_layout(); this.ctx.request_layout();
} }
#[expect(missing_docs, reason = "TODO")] /// Returns a mutable reference to the child widget at `idx`.
///
/// Returns `None` if the child at `idx` is a spacer.
///
/// # Panics
///
/// Panics if the index is larger than the number of children.
pub fn child_mut<'t>( pub fn child_mut<'t>(
this: &'t mut WidgetMut<'_, Self>, this: &'t mut WidgetMut<'_, Self>,
idx: usize, idx: usize,
@ -623,7 +663,7 @@ impl Flex {
this.ctx.children_changed(); this.ctx.children_changed();
} }
#[expect(missing_docs, reason = "TODO")] /// Remove all children from the container.
pub fn clear(this: &mut WidgetMut<'_, Self>) { pub fn clear(this: &mut WidgetMut<'_, Self>) {
if !this.widget.children.is_empty() { if !this.widget.children.is_empty() {
this.ctx.request_layout(); this.ctx.request_layout();

View File

@ -32,17 +32,21 @@ struct Child {
} }
#[derive(Default, Debug, Copy, Clone, PartialEq)] #[derive(Default, Debug, Copy, Clone, PartialEq)]
#[expect(missing_docs, reason = "TODO")] /// Parameters required when adding an item to a [`Grid`] container.
pub struct GridParams { pub struct GridParams {
/// Index of the column this item is starting from.
pub x: i32, pub x: i32,
/// Index of the row this item is starting from.
pub y: i32, pub y: i32,
/// Number of columns this item spans.
pub width: i32, pub width: i32,
/// Number of rows this item spans.
pub height: i32, pub height: i32,
} }
// --- MARK: IMPL GRID --- // --- MARK: IMPL GRID ---
impl Grid { impl Grid {
#[expect(missing_docs, reason = "TODO")] /// Create a new grid with the given number of columns and rows.
pub fn with_dimensions(width: i32, height: i32) -> Self { pub fn with_dimensions(width: i32, height: i32) -> Self {
Self { Self {
children: Vec::new(), children: Vec::new(),
@ -52,25 +56,23 @@ impl Grid {
} }
} }
#[expect(missing_docs, reason = "TODO")] /// Builder-style method for setting the spacing between grid items.
pub fn with_spacing(mut self, spacing: f64) -> Self { pub fn with_spacing(mut self, spacing: f64) -> Self {
self.grid_spacing = spacing; self.grid_spacing = spacing;
self self
} }
/// Builder-style variant of [`Grid::add_child`]. /// Builder-style method to add a child widget.
///
/// Convenient for assembling a group of widgets in a single expression.
pub fn with_child(self, child: impl Widget, params: GridParams) -> Self { pub fn with_child(self, child: impl Widget, params: GridParams) -> Self {
self.with_child_pod(WidgetPod::new(child).erased(), params) self.with_child_pod(WidgetPod::new(child).erased(), params)
} }
#[expect(missing_docs, reason = "TODO")] /// Builder-style method to add a child widget with a pre-assigned id.
pub fn with_child_id(self, child: impl Widget, id: WidgetId, params: GridParams) -> Self { pub fn with_child_id(self, child: impl Widget, id: WidgetId, params: GridParams) -> Self {
self.with_child_pod(WidgetPod::new_with_id(child, id).erased(), params) self.with_child_pod(WidgetPod::new_with_id(child, id).erased(), params)
} }
#[expect(missing_docs, reason = "TODO")] /// Builder-style method to add a child widget already wrapped in a [`WidgetPod`].
pub fn with_child_pod(mut self, widget: WidgetPod<dyn Widget>, params: GridParams) -> Self { pub fn with_child_pod(mut self, widget: WidgetPod<dyn Widget>, params: GridParams) -> Self {
let child = Child { let child = Child {
widget, widget,
@ -86,13 +88,6 @@ impl Grid {
// --- MARK: IMPL CHILD --- // --- MARK: IMPL CHILD ---
impl Child { impl Child {
fn widget_mut(&mut self) -> Option<&mut WidgetPod<dyn Widget>> {
Some(&mut self.widget)
}
fn widget(&self) -> Option<&WidgetPod<dyn Widget>> {
Some(&self.widget)
}
fn update_params(&mut self, params: GridParams) { fn update_params(&mut self, params: GridParams) {
self.x = params.x; self.x = params.x;
self.y = params.y; self.y = params.y;
@ -113,8 +108,13 @@ fn new_grid_child(params: GridParams, widget: WidgetPod<dyn Widget>) -> Child {
// --- MARK: IMPL GRIDPARAMS --- // --- MARK: IMPL GRIDPARAMS ---
impl GridParams { impl GridParams {
#[expect(missing_docs, reason = "TODO")] /// Create grid parameters with the given values.
///
/// # Panics
///
/// When debug assertions are on, panics if the width or height is less than or equal to zero or if x or y is negative.
pub fn new(mut x: i32, mut y: i32, mut width: i32, mut height: i32) -> Self { pub fn new(mut x: i32, mut y: i32, mut width: i32, mut height: i32) -> Self {
// TODO - Use u32 params instead?
if x < 0 { if x < 0 {
debug_panic!("Grid x value should be a non-negative number; got {}", x); debug_panic!("Grid x value should be a non-negative number; got {}", x);
x = 0; x = 0;
@ -150,15 +150,15 @@ impl GridParams {
impl Grid { impl Grid {
/// Add a child widget. /// Add a child widget.
/// ///
/// See also [`with_child`]. /// See also [`with_child`](Grid::with_child).
///
/// [`with_child`]: Grid::with_child
pub fn add_child(this: &mut WidgetMut<'_, Self>, child: impl Widget, params: GridParams) { pub fn add_child(this: &mut WidgetMut<'_, Self>, child: impl Widget, params: GridParams) {
let child_pod: WidgetPod<dyn Widget> = WidgetPod::new(child).erased(); let child_pod: WidgetPod<dyn Widget> = WidgetPod::new(child).erased();
Self::insert_child_pod(this, child_pod, params); Self::add_child_pod(this, child_pod, params);
} }
#[expect(missing_docs, reason = "TODO")] /// Add a child widget with a pre-assigned id.
///
/// See also [`with_child_id`](Grid::with_child_id).
pub fn add_child_id( pub fn add_child_id(
this: &mut WidgetMut<'_, Self>, this: &mut WidgetMut<'_, Self>,
child: impl Widget, child: impl Widget,
@ -166,11 +166,13 @@ impl Grid {
params: GridParams, params: GridParams,
) { ) {
let child_pod: WidgetPod<dyn Widget> = WidgetPod::new_with_id(child, id).erased(); let child_pod: WidgetPod<dyn Widget> = WidgetPod::new_with_id(child, id).erased();
Self::insert_child_pod(this, child_pod, params); Self::add_child_pod(this, child_pod, params);
} }
/// Add a child widget. /// Add a child widget already wrapped in a [`WidgetPod`].
pub fn insert_child_pod( ///
/// See also [`with_child_pod`](Grid::with_child_pod).
pub fn add_child_pod(
this: &mut WidgetMut<'_, Self>, this: &mut WidgetMut<'_, Self>,
widget: WidgetPod<dyn Widget>, widget: WidgetPod<dyn Widget>,
params: GridParams, params: GridParams,
@ -181,7 +183,14 @@ impl Grid {
this.ctx.request_layout(); this.ctx.request_layout();
} }
#[expect(missing_docs, reason = "TODO")] /// Insert a child widget at the given index.
///
/// This lets you control the order in which the children are drawn. Children are
/// drawn in index order (i.e. each child is drawn on top of the children with lower indices).
///
/// # Panics
///
/// Panics if the index is larger than the number of children.
pub fn insert_grid_child_at( pub fn insert_grid_child_at(
this: &mut WidgetMut<'_, Self>, this: &mut WidgetMut<'_, Self>,
idx: usize, idx: usize,
@ -191,7 +200,14 @@ impl Grid {
Self::insert_grid_child_pod(this, idx, WidgetPod::new(child).erased(), params); Self::insert_grid_child_pod(this, idx, WidgetPod::new(child).erased(), params);
} }
#[expect(missing_docs, reason = "TODO")] /// Insert a child widget already wrapped in a [`WidgetPod`] at the given index.
///
/// This lets you control the order in which the children are drawn. Children are
/// drawn in index order (i.e. each child is drawn on top of the children with lower indices).
///
/// # Panics
///
/// Panics if the index is larger than the number of children.
pub fn insert_grid_child_pod( pub fn insert_grid_child_pod(
this: &mut WidgetMut<'_, Self>, this: &mut WidgetMut<'_, Self>,
idx: usize, idx: usize,
@ -204,31 +220,37 @@ impl Grid {
this.ctx.request_layout(); this.ctx.request_layout();
} }
#[expect(missing_docs, reason = "TODO")] /// Set the spacing between grid items.
pub fn set_spacing(this: &mut WidgetMut<'_, Self>, spacing: f64) { pub fn set_spacing(this: &mut WidgetMut<'_, Self>, spacing: f64) {
this.widget.grid_spacing = spacing; this.widget.grid_spacing = spacing;
this.ctx.request_layout(); this.ctx.request_layout();
} }
#[expect(missing_docs, reason = "TODO")] // TODO - Some of these method names should maybe be changed.
// "height" and "width" are misleading, since they suggest a pixel size.
/// Set the number of columns of the grid.
pub fn set_width(this: &mut WidgetMut<'_, Self>, width: i32) { pub fn set_width(this: &mut WidgetMut<'_, Self>, width: i32) {
this.widget.grid_width = width; this.widget.grid_width = width;
this.ctx.request_layout(); this.ctx.request_layout();
} }
#[expect(missing_docs, reason = "TODO")] /// Set the number of rows of the grid.
pub fn set_height(this: &mut WidgetMut<'_, Self>, height: i32) { pub fn set_height(this: &mut WidgetMut<'_, Self>, height: i32) {
this.widget.grid_height = height; this.widget.grid_height = height;
this.ctx.request_layout(); this.ctx.request_layout();
} }
#[expect(missing_docs, reason = "TODO")] /// Get a mutable reference to the child at `idx`.
///
/// # Panics
///
/// Panics if `idx` is out of bounds.
pub fn child_mut<'t>( pub fn child_mut<'t>(
this: &'t mut WidgetMut<'_, Self>, this: &'t mut WidgetMut<'_, Self>,
idx: usize, idx: usize,
) -> Option<WidgetMut<'t, dyn Widget>> { ) -> WidgetMut<'t, dyn Widget> {
let child = this.widget.children[idx].widget_mut()?; let child = &mut this.widget.children[idx].widget;
Some(this.ctx.get_mut(child)) this.ctx.get_mut(child)
} }
/// Updates the grid parameters for the child at `idx`, /// Updates the grid parameters for the child at `idx`,
@ -246,7 +268,11 @@ impl Grid {
this.ctx.request_layout(); this.ctx.request_layout();
} }
#[expect(missing_docs, reason = "TODO")] /// Removes a child widget at the given index.
///
/// # Panics
///
/// Panics if the index is out of bounds.
pub fn remove_child(this: &mut WidgetMut<'_, Self>, idx: usize) { pub fn remove_child(this: &mut WidgetMut<'_, Self>, idx: usize) {
let child = this.widget.children.remove(idx); let child = this.widget.children.remove(idx);
this.ctx.remove_child(child.widget); this.ctx.remove_child(child.widget);
@ -281,8 +307,8 @@ impl Widget for Grid {
} }
fn register_children(&mut self, ctx: &mut crate::core::RegisterCtx) { fn register_children(&mut self, ctx: &mut crate::core::RegisterCtx) {
for child in self.children.iter_mut().filter_map(|x| x.widget_mut()) { for child in self.children.iter_mut() {
ctx.register_child(child); ctx.register_child(&mut child.widget);
} }
} }
@ -343,8 +369,7 @@ impl Widget for Grid {
fn children_ids(&self) -> SmallVec<[WidgetId; 16]> { fn children_ids(&self) -> SmallVec<[WidgetId; 16]> {
self.children self.children
.iter() .iter()
.filter_map(|child| child.widget()) .map(|child| child.widget.id())
.map(|widget_pod| widget_pod.id())
.collect() .collect()
} }

View File

@ -18,6 +18,8 @@ use crate::theme;
use crate::util::{UnitPoint, fill_lin_gradient, stroke}; use crate::util::{UnitPoint, fill_lin_gradient, stroke};
use crate::widgets::{Label, LineBreaking}; use crate::widgets::{Label, LineBreaking};
// TODO - NaN probably shouldn't be a meaningful value in our API.
/// A progress bar. /// A progress bar.
/// ///
#[doc = crate::include_screenshot!("widget/screenshots/masonry__widget__progress_bar__tests__25_percent_progressbar.png", "25% progress bar.")] #[doc = crate::include_screenshot!("widget/screenshots/masonry__widget__progress_bar__tests__25_percent_progressbar.png", "25% progress bar.")]
@ -33,11 +35,11 @@ pub struct ProgressBar {
impl ProgressBar { impl ProgressBar {
/// Create a new `ProgressBar`. /// Create a new `ProgressBar`.
/// ///
/// `progress` is a number between 0 and 1 inclusive. If it is `NaN`, then an /// The progress value will be clamped to [0, 1].
/// indefinite progress bar will be shown. ///
/// Otherwise, the input will be clamped to [0, 1]. /// A `None` value (or NaN) will show an indeterminate progress bar.
pub fn new(mut progress: Option<f64>) -> Self { pub fn new(progress: Option<f64>) -> Self {
clamp_progress(&mut progress); let progress = clamp_progress(progress);
let label = WidgetPod::new( let label = WidgetPod::new(
Label::new(Self::value(progress)).with_line_break_mode(LineBreaking::Overflow), Label::new(Self::value(progress)).with_line_break_mode(LineBreaking::Overflow),
); );
@ -63,9 +65,13 @@ impl ProgressBar {
// --- MARK: WIDGETMUT --- // --- MARK: WIDGETMUT ---
impl ProgressBar { impl ProgressBar {
#[expect(missing_docs, reason = "TODO")] /// Set the progress displayed by the bar.
pub fn set_progress(this: &mut WidgetMut<'_, Self>, mut progress: Option<f64>) { ///
clamp_progress(&mut progress); /// The progress value will be clamped to [0, 1].
///
/// A `None` value (or NaN) will show an indeterminate progress bar.
pub fn set_progress(this: &mut WidgetMut<'_, Self>, progress: Option<f64>) {
let progress = clamp_progress(progress);
let progress_changed = this.widget.progress != progress; let progress_changed = this.widget.progress != progress;
if progress_changed { if progress_changed {
this.widget.progress = progress; this.widget.progress = progress;
@ -80,13 +86,12 @@ impl ProgressBar {
/// Helper to ensure progress is either a number between [0, 1] inclusive, or `None`. /// Helper to ensure progress is either a number between [0, 1] inclusive, or `None`.
/// ///
/// NaNs are converted to `None`. /// NaNs are converted to `None`.
fn clamp_progress(progress: &mut Option<f64>) { fn clamp_progress(progress: Option<f64>) -> Option<f64> {
if let Some(value) = progress { let progress = progress?;
if value.is_nan() { if progress.is_nan() {
*progress = None; None
} else { } else {
*progress = Some(value.clamp(0., 1.)); Some(progress.clamp(0., 1.))
}
} }
} }

View File

@ -14,15 +14,15 @@ use crate::core::{
}; };
use crate::kurbo::Size; use crate::kurbo::Size;
// TODO: This is a hack to provide an accessibility node with a Window type. // TODO: This should eventually be removed once accesskit does that for us.
// This should eventually be removed. // See https://github.com/AccessKit/accesskit/issues/531
#[expect(missing_docs, reason = "TODO")] /// A widget wrapper that reports a [`Role::Window`] to the accessibility API.
pub struct RootWidget<W: ?Sized> { pub struct RootWidget<W: ?Sized> {
pub(crate) pod: WidgetPod<W>, pub(crate) pod: WidgetPod<W>,
} }
impl<W: Widget> RootWidget<W> { impl<W: Widget> RootWidget<W> {
#[expect(missing_docs, reason = "TODO")] /// Create a new root widget.
pub fn new(widget: W) -> Self { pub fn new(widget: W) -> Self {
Self { Self {
pod: WidgetPod::new(widget), pod: WidgetPod::new(widget),
@ -31,14 +31,14 @@ impl<W: Widget> RootWidget<W> {
} }
impl<W: Widget + FromDynWidget + ?Sized> RootWidget<W> { impl<W: Widget + FromDynWidget + ?Sized> RootWidget<W> {
#[expect(missing_docs, reason = "TODO")] /// Create a new root widget from a [`WidgetPod`].
pub fn from_pod(pod: WidgetPod<W>) -> Self { pub fn from_pod(pod: WidgetPod<W>) -> Self {
Self { pod } Self { pod }
} }
} }
impl<W: Widget + FromDynWidget + ?Sized> RootWidget<W> { impl<W: Widget + FromDynWidget + ?Sized> RootWidget<W> {
#[expect(missing_docs, reason = "TODO")] /// Get a mutable reference to the child widget.
pub fn child_mut<'t>(this: &'t mut WidgetMut<'_, Self>) -> WidgetMut<'t, W> { pub fn child_mut<'t>(this: &'t mut WidgetMut<'_, Self>) -> WidgetMut<'t, W> {
this.ctx.get_mut(&mut this.widget.pod) this.ctx.get_mut(&mut this.widget.pod)
} }

View File

@ -327,7 +327,9 @@ impl SizedBox {
// --- MARK: WIDGETMUT --- // --- MARK: WIDGETMUT ---
impl SizedBox { impl SizedBox {
#[expect(missing_docs, reason = "TODO")] /// Give this container a child widget.
///
/// If this container already has a child, it will be overwritten.
pub fn set_child(this: &mut WidgetMut<'_, Self>, child: impl Widget) { pub fn set_child(this: &mut WidgetMut<'_, Self>, child: impl Widget) {
if let Some(child) = this.widget.child.take() { if let Some(child) = this.widget.child.take() {
this.ctx.remove_child(child); this.ctx.remove_child(child);
@ -337,7 +339,9 @@ impl SizedBox {
this.ctx.request_layout(); this.ctx.request_layout();
} }
#[expect(missing_docs, reason = "TODO")] /// Remove the child widget.
///
/// (If this widget has no child, this method does nothing.)
pub fn remove_child(this: &mut WidgetMut<'_, Self>) { pub fn remove_child(this: &mut WidgetMut<'_, Self>) {
if let Some(child) = this.widget.child.take() { if let Some(child) = this.widget.child.take() {
this.ctx.remove_child(child); this.ctx.remove_child(child);
@ -422,7 +426,7 @@ impl SizedBox {
this.ctx.request_layout(); this.ctx.request_layout();
} }
#[expect(missing_docs, reason = "TODO")] /// Get mutable reference to the child widget, if any.
pub fn child_mut<'t>(this: &'t mut WidgetMut<'_, Self>) -> Option<WidgetMut<'t, dyn Widget>> { pub fn child_mut<'t>(this: &'t mut WidgetMut<'_, Self>) -> Option<WidgetMut<'t, dyn Widget>> {
let child = this.widget.child.as_mut()?; let child = this.widget.child.as_mut()?;
Some(this.ctx.get_mut(child)) Some(this.ctx.get_mut(child))

View File

@ -53,7 +53,7 @@ impl<ChildA: Widget, ChildB: Widget> Split<ChildA, ChildB> {
} }
impl<ChildA: Widget + ?Sized, ChildB: Widget + ?Sized> Split<ChildA, ChildB> { impl<ChildA: Widget + ?Sized, ChildB: Widget + ?Sized> Split<ChildA, ChildB> {
#[expect(missing_docs, reason = "TODO")] /// Build split panel from two children already wrapped in [`WidgetPod`]s.
pub fn new_pod(child1: WidgetPod<ChildA>, child2: WidgetPod<ChildB>) -> Self { pub fn new_pod(child1: WidgetPod<ChildA>, child2: WidgetPod<ChildB>) -> Self {
Self { Self {
split_axis: Axis::Horizontal, split_axis: Axis::Horizontal,
@ -304,12 +304,12 @@ where
ChildA: Widget + FromDynWidget + ?Sized, ChildA: Widget + FromDynWidget + ?Sized,
ChildB: Widget + FromDynWidget + ?Sized, ChildB: Widget + FromDynWidget + ?Sized,
{ {
#[expect(missing_docs, reason = "TODO")] /// Get a mutable reference to the first child widget.
pub fn child1_mut<'t>(this: &'t mut WidgetMut<'_, Self>) -> WidgetMut<'t, ChildA> { pub fn child1_mut<'t>(this: &'t mut WidgetMut<'_, Self>) -> WidgetMut<'t, ChildA> {
this.ctx.get_mut(&mut this.widget.child1) this.ctx.get_mut(&mut this.widget.child1)
} }
#[expect(missing_docs, reason = "TODO")] /// Get a mutable reference to the second child widget.
pub fn child2_mut<'t>(this: &'t mut WidgetMut<'_, Self>) -> WidgetMut<'t, ChildB> { pub fn child2_mut<'t>(this: &'t mut WidgetMut<'_, Self>) -> WidgetMut<'t, ChildB> {
this.ctx.get_mut(&mut this.widget.child2) this.ctx.get_mut(&mut this.widget.child2)
} }

View File

@ -133,12 +133,16 @@ impl VariableLabel {
Self::from_label_pod(WidgetPod::new(Label::new(text))) Self::from_label_pod(WidgetPod::new(Label::new(text)))
} }
#[expect(missing_docs, reason = "TODO")] /// Create a new variable label from the given label.
///
/// Uses the label's text and style values.
pub fn from_label(label: Label) -> Self { pub fn from_label(label: Label) -> Self {
Self::from_label_pod(WidgetPod::new(label)) Self::from_label_pod(WidgetPod::new(label))
} }
#[expect(missing_docs, reason = "TODO")] /// Create a new variable label from the given label wrapped in a [`WidgetPod`].
///
/// Uses the label's text and style values.
pub fn from_label_pod(label: WidgetPod<Label>) -> Self { pub fn from_label_pod(label: WidgetPod<Label>) -> Self {
Self { Self {
label, label,

View File

@ -1,6 +1,8 @@
// Copyright 2024 the Xilem Authors // Copyright 2024 the Xilem Authors
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#![expect(missing_docs, reason = "TODO - Document these items")]
use std::sync::Arc; use std::sync::Arc;
use masonry::app::{AppDriver, EventLoopProxy, MasonryState, MasonryUserEvent}; use masonry::app::{AppDriver, EventLoopProxy, MasonryState, MasonryUserEvent};

View File

@ -128,7 +128,6 @@
#![expect(clippy::missing_assert_message, reason = "Deferred: Noisy")] #![expect(clippy::missing_assert_message, reason = "Deferred: Noisy")]
#![expect(elided_lifetimes_in_paths, reason = "Deferred: Noisy")] #![expect(elided_lifetimes_in_paths, reason = "Deferred: Noisy")]
// https://github.com/rust-lang/rust/pull/130025 // https://github.com/rust-lang/rust/pull/130025
#![allow(missing_docs, reason = "We have many as-yet undocumented items")]
#![expect(clippy::allow_attributes_without_reason, reason = "Deferred: Noisy")] #![expect(clippy::allow_attributes_without_reason, reason = "Deferred: Noisy")]
use std::collections::HashMap; use std::collections::HashMap;
@ -178,11 +177,13 @@ pub struct Xilem<State, Logic> {
fonts: Vec<Vec<u8>>, fonts: Vec<Vec<u8>>,
} }
#[expect(missing_docs, reason = "TODO - Document these items")]
impl<State, Logic, View> Xilem<State, Logic> impl<State, Logic, View> Xilem<State, Logic>
where where
Logic: FnMut(&mut State) -> View, Logic: FnMut(&mut State) -> View,
View: WidgetView<State>, View: WidgetView<State>,
{ {
/// Initialize the builder state for your app.
pub fn new(state: State, logic: Logic) -> Self { pub fn new(state: State, logic: Logic) -> Self {
let runtime = tokio::runtime::Runtime::new().unwrap(); let runtime = tokio::runtime::Runtime::new().unwrap();
Self { Self {
@ -287,6 +288,7 @@ where
/// and so might not actually own the underlying widget value. /// and so might not actually own the underlying widget value.
/// When creating widgets in Xilem, layered views all want access to the - using /// When creating widgets in Xilem, layered views all want access to the - using
/// `WidgetPod` for this purpose would require fallible unwrapping. /// `WidgetPod` for this purpose would require fallible unwrapping.
#[expect(missing_docs, reason = "TODO - Document these items")]
pub struct Pod<W: Widget + FromDynWidget + ?Sized> { pub struct Pod<W: Widget + FromDynWidget + ?Sized> {
pub widget: Box<W>, pub widget: Box<W>,
pub id: WidgetId, pub id: WidgetId,
@ -366,6 +368,7 @@ impl<W: Widget + FromDynWidget + ?Sized> SuperElement<Pod<W>, ViewCtx> for Pod<d
} }
} }
#[expect(missing_docs, reason = "TODO - Document these items")]
pub trait WidgetView<State, Action = ()>: pub trait WidgetView<State, Action = ()>:
View<State, Action, ViewCtx, Element = Pod<Self::Widget>> + Send + Sync View<State, Action, ViewCtx, Element = Pod<Self::Widget>> + Send + Sync
{ {
@ -438,6 +441,7 @@ impl<Seq, State, Action> WidgetViewSequence<State, Action> for Seq where
type WidgetMap = HashMap<WidgetId, Vec<ViewId>>; type WidgetMap = HashMap<WidgetId, Vec<ViewId>>;
/// A context type passed to various methods of Xilem traits.
pub struct ViewCtx { pub struct ViewCtx {
/// The map from a widgets id to its position in the View tree. /// The map from a widgets id to its position in the View tree.
/// ///
@ -462,6 +466,7 @@ impl ViewPathTracker for ViewCtx {
} }
} }
#[expect(missing_docs, reason = "TODO - Document these items")]
impl ViewCtx { impl ViewCtx {
pub fn new_pod<W: Widget + FromDynWidget>(&mut self, widget: W) -> Pod<W> { pub fn new_pod<W: Widget + FromDynWidget>(&mut self, widget: W) -> Pod<W> {
Pod::new(widget) Pod::new(widget)

View File

@ -84,20 +84,23 @@ pub struct Flex<Seq, State, Action = ()> {
} }
impl<Seq, State, Action> Flex<Seq, State, Action> { impl<Seq, State, Action> Flex<Seq, State, Action> {
/// Set the flex direction (see [`Axis`]).
pub fn direction(mut self, axis: Axis) -> Self { pub fn direction(mut self, axis: Axis) -> Self {
self.axis = axis; self.axis = axis;
self self
} }
/// Set the childrens' [`CrossAxisAlignment`].
pub fn cross_axis_alignment(mut self, axis: CrossAxisAlignment) -> Self { pub fn cross_axis_alignment(mut self, axis: CrossAxisAlignment) -> Self {
self.cross_axis_alignment = axis; self.cross_axis_alignment = axis;
self self
} }
/// Set the childrens' [`MainAxisAlignment`].
pub fn main_axis_alignment(mut self, axis: MainAxisAlignment) -> Self { pub fn main_axis_alignment(mut self, axis: MainAxisAlignment) -> Self {
self.main_axis_alignment = axis; self.main_axis_alignment = axis;
self self
} }
/// Set whether the container must expand to fill the available space on
/// its main axis.
pub fn must_fill_major_axis(mut self, fill_major_axis: bool) -> Self { pub fn must_fill_major_axis(mut self, fill_major_axis: bool) -> Self {
self.fill_major_axis = fill_major_axis; self.fill_major_axis = fill_major_axis;
self self
@ -214,12 +217,17 @@ where
} }
} }
/// A child element of a [`Flex`] view.
pub enum FlexElement { pub enum FlexElement {
/// Child widget.
Child(Pod<dyn Widget>, FlexParams), Child(Pod<dyn Widget>, FlexParams),
/// Child spacer with fixed size.
FixedSpacer(f64), FixedSpacer(f64),
/// Child spacer with flex size.
FlexSpacer(f64), FlexSpacer(f64),
} }
/// A mutable reference to a [`FlexElement`], used internally by Xilem traits.
pub struct FlexElementMut<'w> { pub struct FlexElementMut<'w> {
parent: WidgetMut<'w, widgets::Flex>, parent: WidgetMut<'w, widgets::Flex>,
idx: usize, idx: usize,
@ -549,6 +557,7 @@ where
/// A spacer that can be used within a [`Flex`] [`View`] /// A spacer that can be used within a [`Flex`] [`View`]
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
#[expect(missing_docs, reason = "TODO - Need to document units used.")]
pub enum FlexSpacer { pub enum FlexSpacer {
Fixed(f64), Fixed(f64),
Flex(f64), Flex(f64),
@ -610,7 +619,9 @@ impl<State, Action> View<State, Action, ViewCtx> for FlexSpacer {
/// A widget-type-erased flex child [`View`], can be used within a [`Flex`] [`View`] /// A widget-type-erased flex child [`View`], can be used within a [`Flex`] [`View`]
pub enum AnyFlexChild<State, Action = ()> { pub enum AnyFlexChild<State, Action = ()> {
/// A child widget.
Item(FlexItem<Box<AnyWidgetView<State, Action>>, State, Action>), Item(FlexItem<Box<AnyWidgetView<State, Action>>, State, Action>),
/// A spacer.
Spacer(FlexSpacer), Spacer(FlexSpacer),
} }

View File

@ -76,6 +76,7 @@ pub struct Grid<Seq, State, Action = ()> {
} }
impl<Seq, State, Action> Grid<Seq, State, Action> { impl<Seq, State, Action> Grid<Seq, State, Action> {
/// Set the spacing (both vertical and horizontal) between grid items.
#[track_caller] #[track_caller]
pub fn spacing(mut self, spacing: f64) -> Self { pub fn spacing(mut self, spacing: f64) -> Self {
if spacing.is_finite() && spacing >= 0.0 { if spacing.is_finite() && spacing >= 0.0 {
@ -104,12 +105,8 @@ where
let mut widget = widgets::Grid::with_dimensions(self.width, self.height); let mut widget = widgets::Grid::with_dimensions(self.width, self.height);
widget = widget.with_spacing(self.spacing); widget = widget.with_spacing(self.spacing);
let seq_state = self.sequence.seq_build(ctx, &mut elements); let seq_state = self.sequence.seq_build(ctx, &mut elements);
for child in elements.into_inner() { for element in elements.into_inner() {
widget = match child { widget = widget.with_child_pod(element.child.erased_widget_pod(), element.params);
GridElement::Child(child, params) => {
widget.with_child_pod(child.erased_widget_pod(), params)
}
}
} }
let pod = ctx.new_pod(widget); let pod = ctx.new_pod(widget);
(pod, seq_state) (pod, seq_state)
@ -195,7 +192,11 @@ impl<W: Widget + FromDynWidget + ?Sized> SuperElement<Pod<W>, ViewCtx> for GridE
// There is not much else, beyond purposefully failing, that can be done here, // There is not much else, beyond purposefully failing, that can be done here,
// because there isn't enough information to determine an appropriate spot // because there isn't enough information to determine an appropriate spot
// for the widget. // for the widget.
Self::Child(child.erased(), GridParams::new(1, 1, 1, 1)) Self {
child: child.erased(),
// TODO - Should be 0, 0?
params: GridParams::new(1, 1, 1, 1),
}
} }
fn with_downcast_val<R>( fn with_downcast_val<R>(
@ -203,8 +204,7 @@ impl<W: Widget + FromDynWidget + ?Sized> SuperElement<Pod<W>, ViewCtx> for GridE
f: impl FnOnce(Mut<Pod<W>>) -> R, f: impl FnOnce(Mut<Pod<W>>) -> R,
) -> (Mut<Self>, R) { ) -> (Mut<Self>, R) {
let ret = { let ret = {
let mut child = widgets::Grid::child_mut(&mut this.parent, this.idx) let mut child = widgets::Grid::child_mut(&mut this.parent, this.idx);
.expect("This is supposed to be a widget");
let downcast = child.downcast(); let downcast = child.downcast();
f(downcast) f(downcast)
}; };
@ -218,32 +218,24 @@ impl ElementSplice<GridElement> for GridSplice<'_> {
fn with_scratch<R>(&mut self, f: impl FnOnce(&mut AppendVec<GridElement>) -> R) -> R { fn with_scratch<R>(&mut self, f: impl FnOnce(&mut AppendVec<GridElement>) -> R) -> R {
let ret = f(&mut self.scratch); let ret = f(&mut self.scratch);
for element in self.scratch.drain() { for element in self.scratch.drain() {
match element { widgets::Grid::insert_grid_child_pod(
GridElement::Child(child, params) => { &mut self.element,
widgets::Grid::insert_grid_child_pod( self.idx,
&mut self.element, element.child.erased_widget_pod(),
self.idx, element.params,
child.erased_widget_pod(), );
params,
);
}
};
self.idx += 1; self.idx += 1;
} }
ret ret
} }
fn insert(&mut self, element: GridElement) { fn insert(&mut self, element: GridElement) {
match element { widgets::Grid::insert_grid_child_pod(
GridElement::Child(child, params) => { &mut self.element,
widgets::Grid::insert_grid_child_pod( self.idx,
&mut self.element, element.child.erased_widget_pod(),
self.idx, element.params,
child.erased_widget_pod(), );
params,
);
}
};
self.idx += 1; self.idx += 1;
} }
@ -342,17 +334,22 @@ pub trait GridExt<State, Action>: WidgetView<State, Action> {
impl<State, Action, V: WidgetView<State, Action>> GridExt<State, Action> for V {} impl<State, Action, V: WidgetView<State, Action>> GridExt<State, Action> for V {}
pub enum GridElement { /// A child widget within a [`Grid`] view.
Child(Pod<dyn Widget>, GridParams), pub struct GridElement {
/// The child widget.
child: Pod<dyn Widget>,
/// The grid parameters of the child widget.
params: GridParams,
} }
/// A mutable reference to a [`GridElement`], used internally by Xilem traits.
pub struct GridElementMut<'w> { pub struct GridElementMut<'w> {
parent: WidgetMut<'w, widgets::Grid>, parent: WidgetMut<'w, widgets::Grid>,
idx: usize, idx: usize,
} }
// Used for manipulating the ViewSequence. // Used for manipulating the ViewSequence.
pub struct GridSplice<'w> { struct GridSplice<'w> {
idx: usize, idx: usize,
element: WidgetMut<'w, widgets::Grid>, element: WidgetMut<'w, widgets::Grid>,
scratch: AppendVec<GridElement>, scratch: AppendVec<GridElement>,
@ -375,6 +372,7 @@ pub struct GridItem<V, State, Action> {
phantom: PhantomData<fn() -> (State, Action)>, phantom: PhantomData<fn() -> (State, Action)>,
} }
/// Creates a [`GridItem`] from a view and [`GridParams`].
pub fn grid_item<V, State, Action>( pub fn grid_item<V, State, Action>(
view: V, view: V,
params: impl Into<GridParams>, params: impl Into<GridParams>,
@ -405,7 +403,13 @@ where
fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) { fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
let (pod, state) = self.view.build(ctx); let (pod, state) = self.view.build(ctx);
(GridElement::Child(pod.erased(), self.params), state) (
GridElement {
child: pod.erased(),
params: self.params,
},
state,
)
} }
fn rebuild( fn rebuild(
@ -423,8 +427,7 @@ where
self.params, self.params,
); );
} }
let mut child = widgets::Grid::child_mut(&mut element.parent, element.idx) let mut child = widgets::Grid::child_mut(&mut element.parent, element.idx);
.expect("GridWrapper always has a widget child");
self.view self.view
.rebuild(&prev.view, view_state, ctx, child.downcast()); .rebuild(&prev.view, view_state, ctx, child.downcast());
} }
@ -436,8 +439,7 @@ where
ctx: &mut ViewCtx, ctx: &mut ViewCtx,
mut element: Mut<Self::Element>, mut element: Mut<Self::Element>,
) { ) {
let mut child = widgets::Grid::child_mut(&mut element.parent, element.idx) let mut child = widgets::Grid::child_mut(&mut element.parent, element.idx);
.expect("GridWrapper always has a widget child");
self.view.teardown(view_state, ctx, child.downcast()); self.view.teardown(view_state, ctx, child.downcast());
} }

View File

@ -21,6 +21,7 @@ where
} }
} }
/// The [`View`] created by [`portal`].
#[must_use = "View values do nothing unless provided to Xilem."] #[must_use = "View values do nothing unless provided to Xilem."]
pub struct Portal<V, State, Action> { pub struct Portal<V, State, Action> {
child: V, child: V,

View File

@ -6,10 +6,15 @@ use masonry::widgets;
use crate::core::{DynMessage, Mut, ViewMarker}; use crate::core::{DynMessage, Mut, ViewMarker};
use crate::{MessageResult, Pod, View, ViewCtx, ViewId}; use crate::{MessageResult, Pod, View, ViewCtx, ViewId};
/// A view which displays a progress bar.
///
/// This can be for showing progress of a task or a download.
pub fn progress_bar(progress: Option<f64>) -> ProgressBar { pub fn progress_bar(progress: Option<f64>) -> ProgressBar {
ProgressBar { progress } ProgressBar { progress }
} }
/// The [`View`] created by [`progress_bar`].
#[must_use = "View values do nothing unless provided to Xilem."]
pub struct ProgressBar { pub struct ProgressBar {
progress: Option<f64>, progress: Option<f64>,
} }

View File

@ -10,6 +10,7 @@ use vello::peniko::Brush;
use crate::core::{DynMessage, Mut, ViewMarker}; use crate::core::{DynMessage, Mut, ViewMarker};
use crate::{Color, MessageResult, Pod, TextAlignment, View, ViewCtx, ViewId}; use crate::{Color, MessageResult, Pod, TextAlignment, View, ViewCtx, ViewId};
/// A view which displays selectable text.
pub fn prose(content: impl Into<ArcStr>) -> Prose { pub fn prose(content: impl Into<ArcStr>) -> Prose {
Prose { Prose {
content: content.into(), content: content.into(),
@ -30,6 +31,7 @@ pub fn inline_prose(content: impl Into<ArcStr>) -> Prose {
prose(content).line_break_mode(LineBreaking::Overflow) prose(content).line_break_mode(LineBreaking::Overflow)
} }
/// The [`View`] created by [`prose`] or [`inline_prose`].
#[must_use = "View values do nothing unless provided to Xilem."] #[must_use = "View values do nothing unless provided to Xilem."]
pub struct Prose { pub struct Prose {
content: ArcStr, content: ArcStr,
@ -43,22 +45,27 @@ pub struct Prose {
} }
impl Prose { impl Prose {
/// Set the brush used to paint the text.
#[doc(alias = "color")] #[doc(alias = "color")]
pub fn brush(mut self, brush: impl Into<Brush>) -> Self { pub fn brush(mut self, brush: impl Into<Brush>) -> Self {
self.text_brush = brush.into(); self.text_brush = brush.into();
self self
} }
/// Set the [alignment](https://en.wikipedia.org/wiki/Typographic_alignment) of the text.
pub fn alignment(mut self, alignment: TextAlignment) -> Self { pub fn alignment(mut self, alignment: TextAlignment) -> Self {
self.alignment = alignment; self.alignment = alignment;
self self
} }
/// Set the font size of the text.
#[doc(alias = "font_size")] #[doc(alias = "font_size")]
pub fn text_size(mut self, text_size: f32) -> Self { pub fn text_size(mut self, text_size: f32) -> Self {
self.text_size = text_size; self.text_size = text_size;
self self
} }
/// Set how the text is broken when the content is too wide for its container.
pub fn line_break_mode(mut self, line_break_mode: LineBreaking) -> Self { pub fn line_break_mode(mut self, line_break_mode: LineBreaking) -> Self {
self.line_break_mode = line_break_mode; self.line_break_mode = line_break_mode;
self self

View File

@ -1,6 +1,8 @@
// Copyright 2024 the Xilem Authors // Copyright 2024 the Xilem Authors
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#![expect(missing_docs, reason = "TODO - Document these items")]
use std::future::Future; use std::future::Future;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::sync::Arc; use std::sync::Arc;

View File

@ -13,6 +13,7 @@ use crate::{Color, MessageResult, Pod, TextAlignment, ViewCtx, ViewId};
type Callback<State, Action> = Box<dyn Fn(&mut State, String) -> Action + Send + Sync + 'static>; type Callback<State, Action> = Box<dyn Fn(&mut State, String) -> Action + Send + Sync + 'static>;
/// A view which displays editable text.
pub fn textbox<F, State, Action>(contents: String, on_changed: F) -> Textbox<State, Action> pub fn textbox<F, State, Action>(contents: String, on_changed: F) -> Textbox<State, Action>
where where
F: Fn(&mut State, String) -> Action + Send + Sync + 'static, F: Fn(&mut State, String) -> Action + Send + Sync + 'static,
@ -28,6 +29,7 @@ where
} }
} }
/// The [`View`] created by [`textbox`].
#[must_use = "View values do nothing unless provided to Xilem."] #[must_use = "View values do nothing unless provided to Xilem."]
pub struct Textbox<State, Action> { pub struct Textbox<State, Action> {
contents: String, contents: String,
@ -39,17 +41,20 @@ pub struct Textbox<State, Action> {
} }
impl<State, Action> Textbox<State, Action> { impl<State, Action> Textbox<State, Action> {
/// Set the brush used to paint the text.
#[doc(alias = "color")] #[doc(alias = "color")]
pub fn brush(mut self, color: impl Into<Brush>) -> Self { pub fn brush(mut self, color: impl Into<Brush>) -> Self {
self.text_brush = color.into(); self.text_brush = color.into();
self self
} }
/// Set the [alignment](https://en.wikipedia.org/wiki/Typographic_alignment) of the text.
pub fn alignment(mut self, alignment: TextAlignment) -> Self { pub fn alignment(mut self, alignment: TextAlignment) -> Self {
self.alignment = alignment; self.alignment = alignment;
self self
} }
/// Set a callback that will be run when the user presses the `Enter` key to submit their input.
pub fn on_enter<F>(mut self, on_enter: F) -> Self pub fn on_enter<F>(mut self, on_enter: F) -> Self
where where
F: Fn(&mut State, String) -> Action + Send + Sync + 'static, F: Fn(&mut State, String) -> Action + Send + Sync + 'static,

View File

@ -21,6 +21,7 @@ pub fn variable_label(text: impl Into<ArcStr>) -> VariableLabel {
} }
} }
/// The [`View`] created by [`variable_label`].
#[must_use = "View values do nothing unless provided to Xilem."] #[must_use = "View values do nothing unless provided to Xilem."]
pub struct VariableLabel { pub struct VariableLabel {
label: Label, label: Label,
@ -63,17 +64,20 @@ impl VariableLabel {
self self
} }
/// Set the brush used to paint the text.
#[doc(alias = "color")] #[doc(alias = "color")]
pub fn brush(mut self, brush: impl Into<Brush>) -> Self { pub fn brush(mut self, brush: impl Into<Brush>) -> Self {
self.label = self.label.brush(brush); self.label = self.label.brush(brush);
self self
} }
/// Set the [alignment](https://en.wikipedia.org/wiki/Typographic_alignment) of the text.
pub fn alignment(mut self, alignment: TextAlignment) -> Self { pub fn alignment(mut self, alignment: TextAlignment) -> Self {
self.label = self.label.alignment(alignment); self.label = self.label.alignment(alignment);
self self
} }
/// Set the font size of the text.
#[doc(alias = "font_size")] #[doc(alias = "font_size")]
pub fn text_size(mut self, text_size: f32) -> Self { pub fn text_size(mut self, text_size: f32) -> Self {
self.label = self.label.text_size(text_size); self.label = self.label.text_size(text_size);

View File

@ -1,6 +1,8 @@
// Copyright 2024 the Xilem Authors // Copyright 2024 the Xilem Authors
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#![expect(missing_docs, reason = "TODO - Document these items")]
use std::future::Future; use std::future::Future;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::sync::Arc; use std::sync::Arc;

View File

@ -1,8 +1,6 @@
// Copyright 2024 the Xilem Authors // Copyright 2024 the Xilem Authors
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#![warn(missing_docs)]
use std::marker::PhantomData; use std::marker::PhantomData;
use masonry::core::{FromDynWidget, Widget, WidgetMut}; use masonry::core::{FromDynWidget, Widget, WidgetMut};

View File

@ -21,9 +21,9 @@ pub trait PhantomElementCtx: ViewPathTracker {
} }
/// A [`View`] which can be one of nine inner view types. /// A [`View`] which can be one of nine inner view types.
#[allow(missing_docs)] // On variants
#[derive(Debug)] #[derive(Debug)]
#[must_use = "View values do nothing unless provided to Xilem."] #[must_use = "View values do nothing unless provided to Xilem."]
#[expect(missing_docs, reason = "No need to document all variants")]
pub enum OneOf<A = (), B = (), C = (), D = (), E = (), F = (), G = (), H = (), I = ()> { pub enum OneOf<A = (), B = (), C = (), D = (), E = (), F = (), G = (), H = (), I = ()> {
A(A), A(A),
B(B), B(B),

View File

@ -27,8 +27,7 @@
#![expect(clippy::cast_possible_truncation, reason = "Deferred: Noisy")] #![expect(clippy::cast_possible_truncation, reason = "Deferred: Noisy")]
#![expect(clippy::missing_assert_message, reason = "Deferred: Noisy")] #![expect(clippy::missing_assert_message, reason = "Deferred: Noisy")]
#![expect(elided_lifetimes_in_paths, reason = "Deferred: Noisy")] #![expect(elided_lifetimes_in_paths, reason = "Deferred: Noisy")]
// expect doesn't work here: https://github.com/rust-lang/rust/pull/130025 #![expect(missing_docs, reason = "We have many as-yet undocumented items")]
#![allow(missing_docs, reason = "We have many as-yet undocumented items")]
#![expect(unreachable_pub, reason = "Potentially controversial code style")] #![expect(unreachable_pub, reason = "Potentially controversial code style")]
#![expect( #![expect(
unnameable_types, unnameable_types,