Document most items in Masonry (#815)

Move `#[allow(missing_docs)]` to two modules. Missing docs now warn
everywhere else by default.

Remove `#[expect(rustdoc::broken_intra_doc_links)]` and fix all broken
links.
This commit is contained in:
Olivier FAURE 2025-01-13 17:36:31 +00:00 committed by GitHub
parent 522dfd4433
commit 51a355615f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 327 additions and 162 deletions

View File

@ -21,7 +21,6 @@ Full documentation at https://github.com/orium/cargo-rdme -->
<!-- Intra-doc links used in lib.rs should be evaluated here.
See https://linebender.org/blog/doc-include/ for related discussion. -->
[tracing_tracy]: https://crates.io/crates/tracing-tracy
<!-- cargo-rdme start -->
@ -112,6 +111,7 @@ The following feature flags are available:
[winit]: https://crates.io/crates/winit
[Druid]: https://crates.io/crates/druid
[Xilem]: https://crates.io/crates/xilem
[tracing_tracy]: https://crates.io/crates/tracing-tracy
<!-- cargo-rdme end -->

View File

@ -14,11 +14,16 @@ use crate::event::PointerButton;
///
/// Note: Actions are still a WIP feature.
pub enum Action {
/// A button was pressed.
ButtonPressed(PointerButton),
/// Text changed.
TextChanged(String),
/// Text entered.
TextEntered(String),
CheckboxChecked(bool),
/// A checkbox was toggled.
CheckboxToggled(bool),
// FIXME - This is a huge hack
/// Other.
Other(Box<dyn Any + Send>),
}
@ -28,7 +33,7 @@ impl PartialEq for Action {
(Self::ButtonPressed(l_button), Self::ButtonPressed(r_button)) => l_button == r_button,
(Self::TextChanged(l0), Self::TextChanged(r0)) => l0 == r0,
(Self::TextEntered(l0), Self::TextEntered(r0)) => l0 == r0,
(Self::CheckboxChecked(l0), Self::CheckboxChecked(r0)) => l0 == r0,
(Self::CheckboxToggled(l0), Self::CheckboxToggled(r0)) => l0 == r0,
// FIXME
// (Self::Other(val_l), Self::Other(val_r)) => false,
_ => false,
@ -42,7 +47,7 @@ impl std::fmt::Debug for Action {
Self::ButtonPressed(button) => f.debug_tuple("ButtonPressed").field(button).finish(),
Self::TextChanged(text) => f.debug_tuple("TextChanged").field(text).finish(),
Self::TextEntered(text) => f.debug_tuple("TextEntered").field(text).finish(),
Self::CheckboxChecked(b) => f.debug_tuple("CheckboxChecked").field(b).finish(),
Self::CheckboxToggled(b) => f.debug_tuple("CheckboxChecked").field(b).finish(),
Self::Other(_) => write!(f, "Other(...)"),
}
}

View File

@ -4,11 +4,19 @@
use crate::event_loop_runner::MasonryState;
use crate::{Action, RenderRoot, WidgetId};
/// Context for the [`AppDriver`] trait.
///
/// Currently holds a reference to the [`RenderRoot`].
pub struct DriverCtx<'a> {
pub(crate) render_root: &'a mut RenderRoot,
}
/// A trait for defining how your app interacts with the Masonry widget tree.
///
/// When launching your app with [`crate::event_loop_runner::run`], you need to provide
/// a type that implements this trait.
pub trait AppDriver {
/// A hook which will be executed when a widget emits an [`Action`].
fn on_action(&mut self, ctx: &mut DriverCtx<'_>, widget_id: WidgetId, action: Action);
#[allow(unused_variables)]
@ -27,6 +35,7 @@ impl DriverCtx<'_> {
self.render_root
}
/// Returns true if something happened that requires a rewrite pass or a re-render.
pub fn content_changed(&self) -> bool {
self.render_root.needs_rewrite_passes()
}

View File

@ -18,7 +18,7 @@ use vello::kurbo::Size;
/// The constraints are always [rounded away from zero] to integers
/// to enable pixel perfect layout.
///
/// [`layout`]: crate::widget::Widget::layout
/// [`layout`]: crate::Widget::layout
/// [Flutter BoxConstraints]: https://api.flutter.dev/flutter/rendering/BoxConstraints-class.html
/// [rounded away from zero]: Size::expand
#[derive(Clone, Copy, Debug, PartialEq)]

View File

@ -44,9 +44,7 @@ macro_rules! impl_context_method {
/// When you declare a mutable reference type for your widget, methods of this type
/// will have access to a `MutateCtx`. If that method mutates the widget in a way that
/// requires a later pass (for instance, if your widget has a `set_color` method),
/// you will need to signal that change in the pass (eg `request_paint`).
///
// TODO add tutorial - See https://github.com/linebender/xilem/issues/376
/// you will need to signal that change in the pass (eg [`request_render`](MutateCtx::request_render)).
pub struct MutateCtx<'a> {
pub(crate) global_state: &'a mut RenderRootState,
pub(crate) parent_widget_state: Option<&'a mut WidgetState>,
@ -55,7 +53,9 @@ pub struct MutateCtx<'a> {
pub(crate) widget_children: ArenaMutChildren<'a, Box<dyn Widget>>,
}
/// A context provided to methods of widgets requiring shared, read-only access.
/// A context provided inside of [`WidgetRef`].
///
/// This context is passed to methods of widgets requiring shared, read-only access.
#[derive(Clone, Copy)]
pub struct QueryCtx<'a> {
pub(crate) global_state: &'a RenderRootState,
@ -64,10 +64,7 @@ pub struct QueryCtx<'a> {
pub(crate) widget_children: ArenaRefChildren<'a, Box<dyn Widget>>,
}
/// A context provided to event handling methods of widgets.
///
/// Widgets should call [`request_paint`](Self::request_paint) whenever an event causes a change
/// in the widget's appearance, to schedule a repaint.
/// A context provided to Widget event-handling methods.
pub struct EventCtx<'a> {
pub(crate) global_state: &'a mut RenderRootState,
pub(crate) widget_state: &'a mut WidgetState,
@ -78,7 +75,7 @@ pub struct EventCtx<'a> {
pub(crate) is_handled: bool,
}
/// A context provided to the [`Widget::register_children`] method on widgets.
/// A context provided to the [`Widget::register_children`] method.
pub struct RegisterCtx<'a> {
pub(crate) widget_state_children: ArenaMutChildren<'a, WidgetState>,
pub(crate) widget_children: ArenaMutChildren<'a, Box<dyn Widget>>,
@ -86,9 +83,7 @@ pub struct RegisterCtx<'a> {
pub(crate) registered_ids: Vec<WidgetId>,
}
/// A context provided to the [`update`] method on widgets.
///
/// [`update`]: Widget::update
/// A context provided to the [`Widget::update`] method.
pub struct UpdateCtx<'a> {
pub(crate) global_state: &'a mut RenderRootState,
pub(crate) widget_state: &'a mut WidgetState,
@ -96,11 +91,8 @@ pub struct UpdateCtx<'a> {
pub(crate) widget_children: ArenaMutChildren<'a, Box<dyn Widget>>,
}
/// A context provided to layout handling methods of widgets.
///
/// As of now, the main service provided is access to a factory for
/// creating text layout objects, which are likely to be useful
/// during widget layout.
// TODO - Change this once other layout methods are added.
/// A context provided to [`Widget::layout`] methods.
pub struct LayoutCtx<'a> {
pub(crate) global_state: &'a mut RenderRootState,
pub(crate) widget_state: &'a mut WidgetState,
@ -108,6 +100,7 @@ pub struct LayoutCtx<'a> {
pub(crate) widget_children: ArenaMutChildren<'a, Box<dyn Widget>>,
}
/// A context provided to the [`Widget::compose`] method.
pub struct ComposeCtx<'a> {
pub(crate) global_state: &'a mut RenderRootState,
pub(crate) widget_state: &'a mut WidgetState,
@ -115,7 +108,7 @@ pub struct ComposeCtx<'a> {
pub(crate) widget_children: ArenaMutChildren<'a, Box<dyn Widget>>,
}
/// A context passed to paint methods of widgets.
/// A context passed to [`Widget::paint`] method.
pub struct PaintCtx<'a> {
pub(crate) global_state: &'a mut RenderRootState,
pub(crate) widget_state: &'a WidgetState,
@ -124,6 +117,7 @@ pub struct PaintCtx<'a> {
pub(crate) debug_paint: bool,
}
/// A context passed to [`Widget::accessibility`] method.
pub struct AccessCtx<'a> {
pub(crate) global_state: &'a mut RenderRootState,
pub(crate) widget_state: &'a WidgetState,
@ -145,12 +139,12 @@ impl_context_method!(
PaintCtx<'_>,
AccessCtx<'_>,
{
/// get the `WidgetId` of the current widget.
/// The `WidgetId` of the current widget.
pub fn widget_id(&self) -> WidgetId {
self.widget_state.id
}
#[allow(dead_code)]
#[allow(dead_code, reason = "Copy-pasted for some types that don't need it")]
/// Helper method to get a direct reference to a child widget from its `WidgetPod`.
fn get_child<Child: Widget>(&self, child: &'_ WidgetPod<Child>) -> &'_ Child {
let child_ref = self
@ -160,7 +154,7 @@ impl_context_method!(
child_ref.item.as_dyn_any().downcast_ref::<Child>().unwrap()
}
#[allow(dead_code)]
#[allow(dead_code, reason = "Copy-pasted for some types that don't need it")]
/// Helper method to get a direct reference to a child widget's `WidgetState` from its `WidgetPod`.
fn get_child_state<Child: Widget>(&self, child: &'_ WidgetPod<Child>) -> &'_ WidgetState {
let child_state_ref = self
@ -293,12 +287,9 @@ impl_context_method!(
// --- MARK: EVENT HANDLING ---
impl EventCtx<'_> {
// TODO - clearly document all semantics of pointer capture when they've been decided on
// TODO - Figure out cases where widget should be notified of pointer capture
// loss
/// Capture the pointer in the current widget.
///
/// Pointer capture is only allowed during a [`PointerDown`] event. It is a logic error to
/// [Pointer capture] is only allowed during a [`PointerDown`] event. It is a logic error to
/// capture the pointer during any other event.
///
/// A widget normally only receives pointer events when the pointer is inside the widget's
@ -318,6 +309,10 @@ impl EventCtx<'_> {
/// released after handling of a [`PointerUp`] or [`PointerLeave`] event completes. A widget
/// holding the pointer capture will be the target of these events.
///
/// If pointer capture is lost for external reasons (the widget is disabled, the window
/// lost focus, etc), the widget will still get a [`PointerLeave`] event.
///
/// [Pointer capture]: crate::doc::doc_06_masonry_concepts#pointer-capture
/// [`PointerDown`]: crate::PointerEvent::PointerDown
/// [`PointerUp`]: crate::PointerEvent::PointerUp
/// [`PointerLeave`]: crate::PointerEvent::PointerLeave
@ -333,8 +328,9 @@ impl EventCtx<'_> {
self.global_state.pointer_capture_target = Some(self.widget_state.id);
}
/// Release the pointer previously captured through [`capture_pointer`].
/// Release the pointer previously [captured] through [`capture_pointer`].
///
/// [captured]: crate::doc::doc_06_masonry_concepts#pointer-capture
/// [`capture_pointer`]: EventCtx::capture_pointer
pub fn release_pointer(&mut self) {
self.global_state.pointer_capture_target = None;
@ -357,14 +353,14 @@ impl EventCtx<'_> {
.push((self.widget_state.id, rect));
}
/// Set the event as "handled", which stops its propagation to other
/// Set the event as "handled", which stops its propagation to parent
/// widgets.
pub fn set_handled(&mut self) {
trace!("set_handled");
self.is_handled = true;
}
/// Determine whether the event has been handled by some other widget.
/// Determine whether the event has been handled.
pub fn is_handled(&self) -> bool {
self.is_handled
}
@ -376,13 +372,13 @@ impl EventCtx<'_> {
self.target
}
/// Request keyboard focus.
/// Request [text focus].
///
/// Because only one widget can be focused at a time, multiple focus requests
/// from different widgets during a single event cycle means that the last
/// widget that requests focus will override the previous requests.
///
/// See [`is_focused`](Self::is_focused) for more information about focus.
/// [text focus]: crate::doc::doc_06_masonry_concepts#text-focus
pub fn request_focus(&mut self) {
trace!("request_focus");
// We need to send the request even if we're currently focused,
@ -393,19 +389,19 @@ impl EventCtx<'_> {
self.global_state.next_focused_widget = Some(id);
}
/// Transfer focus to the widget with the given `WidgetId`.
/// Transfer [text focus] to the widget with the given `WidgetId`.
///
/// See [`is_focused`](Self::is_focused) for more information about focus.
/// [text focus]: crate::doc::doc_06_masonry_concepts#text-focus
pub fn set_focus(&mut self, target: WidgetId) {
trace!("set_focus target={:?}", target);
self.global_state.next_focused_widget = Some(target);
}
/// Give up focus.
/// Give up [text focus].
///
/// This should only be called by a widget that currently has focus.
///
/// See [`is_focused`](Self::is_focused) for more information about focus.
/// [text focus]: crate::doc::doc_06_masonry_concepts#text-focus
pub fn resign_focus(&mut self) {
trace!("resign_focus");
if self.has_focus() {
@ -448,7 +444,7 @@ impl LayoutCtx<'_> {
}
}
/// Compute layout of a child widget.
/// Compute the layout of a child widget.
///
/// Container widgets must call this on every child as part of
/// their [`layout`] method.
@ -503,10 +499,6 @@ impl LayoutCtx<'_> {
/// to paint outside of your layout bounds. In this case, the argument
/// should be an [`Insets`] struct that indicates where your widget
/// needs to overpaint, relative to its bounds.
///
/// For more information, see [`WidgetPod::paint_insets`].
///
/// [`WidgetPod::paint_insets`]: crate::widget::WidgetPod::paint_insets
pub fn set_paint_insets(&mut self, insets: impl Into<Insets>) {
let insets = insets.into();
self.widget_state.paint_insets = insets.nonnegative();
@ -522,7 +514,7 @@ impl LayoutCtx<'_> {
///
/// ## Panics
///
/// This method will panic if the child's [`layout()`](WidgetPod::layout) method has not been called yet
/// This method will panic if the child's [`layout()`](LayoutCtx::run_layout) method has not been called yet
/// and if [`LayoutCtx::place_child()`] has not been called for the child.
#[track_caller]
pub fn compute_insets_from_child(
@ -553,12 +545,12 @@ impl LayoutCtx<'_> {
self.widget_state.baseline_offset = baseline;
}
/// Returns whether this widget needs to call [`WidgetPod::layout`]
/// Returns whether this widget needs to call [`LayoutCtx::run_layout`].
pub fn needs_layout(&self) -> bool {
self.widget_state.needs_layout
}
/// Returns whether a child of this widget needs to call [`WidgetPod::layout`]
/// Returns whether a child of this widget needs to call [`LayoutCtx::run_layout`].
pub fn child_needs_layout(&self, child: &WidgetPod<impl Widget>) -> bool {
self.get_child_state(child).needs_layout
}
@ -567,7 +559,7 @@ impl LayoutCtx<'_> {
///
/// ## Panics
///
/// This method will panic if [`WidgetPod::layout`] has not been called yet for
/// This method will panic if [`LayoutCtx::run_layout`] has not been called yet for
/// the child.
#[track_caller]
pub fn child_baseline_offset(&self, child: &WidgetPod<impl Widget>) -> f64 {
@ -579,7 +571,7 @@ impl LayoutCtx<'_> {
///
/// ## Panics
///
/// This method will panic if [`WidgetPod::layout`] and [`LayoutCtx::place_child`]
/// This method will panic if [`LayoutCtx::run_layout`] and [`LayoutCtx::place_child`]
/// have not been called yet for the child.
#[track_caller]
pub fn child_layout_rect(&self, child: &WidgetPod<impl Widget>) -> Rect {
@ -592,7 +584,7 @@ impl LayoutCtx<'_> {
///
/// ## Panics
///
/// This method will panic if [`WidgetPod::layout`] and [`LayoutCtx::place_child`]
/// This method will panic if [`LayoutCtx::run_layout`] and [`LayoutCtx::place_child`]
/// have not been called yet for the child.
#[track_caller]
pub fn child_paint_rect(&self, child: &WidgetPod<impl Widget>) -> Rect {
@ -605,7 +597,7 @@ impl LayoutCtx<'_> {
///
/// ## Panics
///
/// This method will panic if [`WidgetPod::layout`] has not been called yet for
/// This method will panic if [`LayoutCtx::run_layout`] has not been called yet for
/// the child.
#[track_caller]
pub fn child_size(&self, child: &WidgetPod<impl Widget>) -> Size {
@ -613,7 +605,7 @@ impl LayoutCtx<'_> {
self.get_child_state(child).layout_rect().size()
}
/// Skips running the layout pass and calling `place_child` on the child.
/// Skips running the layout pass and calling [`LayoutCtx::place_child`] on the child.
///
/// This may be removed in the future. Currently it's useful for
/// stashed children and children whose layout is cached.
@ -654,6 +646,8 @@ impl LayoutCtx<'_> {
}
impl ComposeCtx<'_> {
// TODO - Remove?
/// Returns whether [`Widget::compose`] will be called on this widget.
pub fn needs_compose(&self) -> bool {
self.widget_state.needs_compose
}
@ -701,17 +695,19 @@ impl_context_method!(
{
/// The layout size.
///
/// This is the layout size as ultimately determined by the parent
/// container, on the previous layout pass.
///
/// Generally it will be the same as the size returned by the child widget's
/// [`layout`] method.
/// This is the layout size returned by the [`layout`] method on the previous
/// layout pass.
///
/// [`layout`]: Widget::layout
pub fn size(&self) -> Size {
self.widget_state.size
}
// TODO - Remove? A widget doesn't really have a concept of its own "origin",
// it's more useful for the parent widget.
/// The layout rect of the widget.
///
/// This is the layout [size](Self::size) and origin (in the parent's coordinate space) combined.
pub fn layout_rect(&self) -> Rect {
self.widget_state.layout_rect()
}
@ -727,10 +723,17 @@ impl_context_method!(
self.widget_state.window_origin()
}
/// The layout rect of the widget in window coordinates.
///
/// Combines the [size](Self::size) and [window origin](Self::window_origin).
pub fn window_layout_rect(&self) -> Rect {
self.widget_state.window_layout_rect()
}
// TODO - Remove? See above.
/// The paint rect of the widget.
///
/// Covers the area we expect to be invalidated when the widget is painted.
pub fn paint_rect(&self) -> Rect {
self.widget_state.paint_rect()
}
@ -764,59 +767,51 @@ impl_context_method!(
PaintCtx<'_>,
AccessCtx<'_>,
{
// TODO - Update once we introduce the is_hovered/has_hovered distinction.
/// The "hovered" status of a widget.
///
/// A widget is "hovered" when the mouse is hovered over it. Widgets will
/// A widget is "hovered" when a pointer is hovering over it. Widgets will
/// often change their appearance as a visual indication that they
/// will respond to mouse interaction.
/// will respond to pointer (usually mouse) interaction.
///
/// The hovered status is computed from the widget's layout rect. In a
/// container hierarchy, all widgets with layout rects containing the
/// mouse position have hovered status.
/// pointer position have hovered status.
///
/// Discussion: there is currently some confusion about whether a
/// widget can be considered hovered when some other widget has captured the
/// pointer (for example, when clicking one widget and dragging to the
/// next). The documentation should clearly state the resolution.
/// If the pointer is [captured], then only that widget and its parents
/// can have hovered status. If the pointer is captured but not hovering
/// over the captured widget, then no widget has the hovered status.
///
/// [captured]: crate::doc::doc_06_masonry_concepts#pointer-capture
pub fn is_hovered(&self) -> bool {
self.widget_state.is_hovered
}
/// Whether the pointer is captured by this widget.
/// Whether a pointer is [captured] by this widget.
///
/// See [`capture_pointer`] for more information about pointer capture.
/// The pointer will usually be the mouse. In future versions, this
/// function will take a pointer id as input to test a specific pointer.
///
/// [`capture_pointer`]: EventCtx::capture_pointer
/// [captured]: crate::doc::doc_06_masonry_concepts#pointer-capture
pub fn has_pointer_capture(&self) -> bool {
self.global_state.pointer_capture_target == Some(self.widget_state.id)
}
/// The focus status of a widget.
/// The [text focus] status of a widget.
///
/// The focused widget is the one that receives keyboard events.
///
/// Returns `true` if this specific widget is focused.
/// To check if any descendants are focused use [`has_focus`].
///
/// Focus means that the widget receives keyboard events.
///
/// A widget can request focus using the [`request_focus`] method.
/// It's also possible to register for automatic focus via [`register_for_focus`].
///
/// If a widget gains or loses focus it will get a [`Update::FocusChanged`] event.
///
/// Only one widget at a time is focused. However due to the way events are routed,
/// all ancestors of that widget will also receive keyboard events.
///
/// [`request_focus`]: EventCtx::request_focus
/// [`register_for_focus`]: UpdateCtx::register_for_focus
/// [`Update::FocusChanged`]: crate::Update::FocusChanged
/// [text focus]: crate::doc::doc_06_masonry_concepts#text-focus
/// [`has_focus`]: Self::has_focus
pub fn is_focused(&self) -> bool {
self.global_state.focused_widget == Some(self.widget_id())
}
/// The (tree) focus status of a widget.
/// Whether this widget or any of its descendants are focused.
///
/// Returns `true` if either this specific widget or any one of its descendants is focused.
/// To check if only this specific widget is focused use [`is_focused`](Self::is_focused).
pub fn has_focus(&self) -> bool {
self.widget_state.has_focus
@ -843,7 +838,7 @@ impl_context_method!(
/// To make this widget explicitly disabled use [`set_disabled`].
///
/// Disabled means that this widget should not change the state of the application. What
/// that means is not entirely clear but in any it should not change its data. Therefore
/// that means is not entirely clear but in any case it should not change its data. Therefore
/// others can use this as a safety mechanism to prevent the application from entering an
/// illegal state.
/// For an example the decrease button of a counter of type `usize` should be disabled if the
@ -854,9 +849,9 @@ impl_context_method!(
self.widget_state.is_disabled
}
/// Check is widget is stashed.
/// Check is widget is [stashed]().
///
/// **Note:** Stashed widgets are a WIP feature.
/// [stashed]: crate::doc::doc_06_masonry_concepts#stashed
pub fn is_stashed(&self) -> bool {
self.widget_state.is_stashed
}
@ -895,14 +890,9 @@ impl_context_method!(MutateCtx<'_>, EventCtx<'_>, UpdateCtx<'_>, {
self.widget_state.request_accessibility = true;
}
/// Request a layout pass.
/// Request a [`layout`] pass.
///
/// A Widget's [`layout`] method is always called when the widget tree
/// changes, or the window is resized.
///
/// If your widget would like to have layout called at any other time,
/// (such as if it would like to change the layout of children in
/// response to some event) it must call this method.
/// Call this method if the widget has changed in a way that requires a layout pass.
///
/// [`layout`]: crate::Widget::layout
pub fn request_layout(&mut self) {
@ -915,6 +905,7 @@ impl_context_method!(MutateCtx<'_>, EventCtx<'_>, UpdateCtx<'_>, {
/// Request a [`compose`] pass.
///
/// The compose pass is often cheaper than the layout pass, because it can only transform individual widgets' position.
///
/// [`compose`]: crate::Widget::compose
pub fn request_compose(&mut self) {
trace!("request_compose");
@ -1051,6 +1042,8 @@ impl_context_method!(
/// to the platform. The area can be used by the platform to, for example, place a
/// candidate box near that area, while ensuring the area is not obscured.
///
/// If no IME area is set, the platform will use the widget's layout rect.
///
/// [focused]: EventCtx::request_focus
/// [accepts text input]: Widget::accepts_text_input
pub fn set_ime_area(&mut self, ime_area: Rect) {
@ -1244,6 +1237,7 @@ impl_get_raw!(EventCtx);
impl_get_raw!(UpdateCtx);
impl_get_raw!(LayoutCtx);
#[allow(missing_docs, reason = "RawWrapper is likely to be reworked")]
impl<'s> AccessCtx<'s> {
pub fn get_raw_ref<'a, 'r, Child: Widget>(
&'a mut self,
@ -1276,17 +1270,20 @@ impl<'s> AccessCtx<'s> {
}
}
#[allow(missing_docs, reason = "RawWrapper is likely to be reworked")]
pub struct RawWrapper<'a, Ctx, W> {
ctx: Ctx,
widget: &'a W,
}
#[allow(missing_docs, reason = "RawWrapper is likely to be reworked")]
pub struct RawWrapperMut<'a, Ctx: IsContext, W> {
parent_widget_state: &'a mut WidgetState,
ctx: Ctx,
widget: &'a mut W,
}
#[allow(missing_docs, reason = "RawWrapper is likely to be reworked")]
impl<Ctx, W> RawWrapper<'_, Ctx, W> {
pub fn widget(&self) -> &W {
self.widget
@ -1297,6 +1294,7 @@ impl<Ctx, W> RawWrapper<'_, Ctx, W> {
}
}
#[allow(missing_docs, reason = "RawWrapper is likely to be reworked")]
impl<Ctx: IsContext, W> RawWrapperMut<'_, Ctx, W> {
pub fn widget(&mut self) -> &mut W {
self.widget
@ -1315,7 +1313,10 @@ impl<Ctx: IsContext, W> Drop for RawWrapperMut<'_, Ctx, W> {
}
mod private {
#[allow(unnameable_types)] // reason: see https://predr.ag/blog/definitive-guide-to-sealed-traits-in-rust/
#[allow(
unnameable_types,
reason = "see https://predr.ag/blog/definitive-guide-to-sealed-traits-in-rust/"
)]
pub trait Sealed {}
}
@ -1323,7 +1324,11 @@ mod private {
// We're exporting a trait with a method that returns a private type.
// It's mostly fine because the trait is sealed anyway, but it's not great for documentation.
#[allow(private_interfaces)] // reason: see https://predr.ag/blog/definitive-guide-to-sealed-traits-in-rust/
#[allow(
private_interfaces,
reason = "see https://predr.ag/blog/definitive-guide-to-sealed-traits-in-rust/"
)]
#[allow(missing_docs, reason = "RawWrapper is likely to be reworked")]
pub trait IsContext: private::Sealed {
fn get_widget_state(&mut self) -> &mut WidgetState;
}
@ -1332,7 +1337,10 @@ macro_rules! impl_context_trait {
($SomeCtx:tt) => {
impl private::Sealed for $SomeCtx<'_> {}
#[allow(private_interfaces)] // reason: see https://predr.ag/blog/definitive-guide-to-sealed-traits-in-rust/
#[allow(
private_interfaces,
reason = "see https://predr.ag/blog/definitive-guide-to-sealed-traits-in-rust/"
)]
impl IsContext for $SomeCtx<'_> {
fn get_widget_state(&mut self) -> &mut WidgetState {
self.widget_state

View File

@ -58,7 +58,10 @@ Focus marks whether a widget receives text events.
To give a simple example, when you click a textbox, the textbox gets focus: anything you type on your keyboard will be sent to that textbox.
Focus can be changed with the tab key, or by clicking on a widget, both which Masonry automatically handles.
Widgets can also set custom focus behavior.
Masonry will only give focus to widgets that accept focus (see [`Widget::accepts_focus`]).
Widgets can also use context types to request focus.
If a widget gains or loses focus it will get a [`FocusChanged`] event.
Note that widgets without text-edition capabilities such as buttons and checkboxes can also get focus.
For instance, pressing space when a button is focused will trigger that button.
@ -103,3 +106,5 @@ Safety rails aren't guaranteed to run and may be disabled even in debug mode for
They should not be relied upon to check code correctness, but are meant to help you catch implementation errors early on during development.
[`PointerLeave`]: crate::PointerEvent::PointerLeave
[`FocusChanged`]: crate::Update::FocusChanged
[`Widget::accepts_focus`]: crate::Widget::accepts_focus

View File

@ -19,11 +19,17 @@ use crate::kurbo::Rect;
// TODO - switch anim frames to being about age / an absolute timestamp
// instead of time elapsed.
// (this will help in cases where we want to skip anim frames)
/// A global event.
#[derive(Debug, Clone)]
pub enum WindowEvent {
/// The window's DPI factor changed.
Rescale(f64),
/// The window was resized.
Resize(PhysicalSize<u32>),
/// The animation frame requested by this window must run.
AnimFrame,
/// The accessibility tree must be rebuilt.
RebuildAccessTree,
}
@ -178,68 +184,119 @@ impl From<PointerButton> for PointerButtons {
}
}
// TODO - Document units for MouseWheel and Pinch deltas.
// TODO - How can RenderRoot express "I started a drag-and-drop op"?
// TODO - Touchpad, Touch, AxisMotion
// TODO - How to handle CursorEntered?
/// A pointer-related event.
///
/// A pointer in this context can be a mouse, a pen, a touch screen, etc. Though
/// Masonry currently doesn't really support multiple pointers.
#[derive(Debug, Clone)]
pub enum PointerEvent {
/// A pointer was pressed.
PointerDown(PointerButton, PointerState),
/// A pointer was released.
PointerUp(PointerButton, PointerState),
/// A pointer was moved.
PointerMove(PointerState),
/// A pointer entered the window.
PointerEnter(PointerState),
/// A pointer left the window.
///
/// A synthetic `PointerLeave` event may also be sent when a widget
/// loses [pointer capture](crate::doc::doc_06_masonry_concepts#pointer-capture).
PointerLeave(PointerState),
/// A mouse wheel event.
///
/// The first tuple value is the scrolled distances. In most cases with a
/// standard mouse wheel, x will be 0 and y will be the number of ticks
/// scrolled. Trackballs and touchpads may produce non-zero values for x.
MouseWheel(LogicalPosition<f64>, PointerState),
/// During a file drag-and-drop operation, a file was kept over the window.
HoverFile(PathBuf, PointerState),
/// During a file drag-and-drop operation, a file was dropped on the window.
DropFile(PathBuf, PointerState),
/// A file drag-and-drop operation was cancelled.
HoverFileCancel(PointerState),
/// A pinch gesture was detected.
///
/// The first tuple value is the delta. Positive values indicate magnification
/// (zooming in) and negative values indicate shrinking (zooming out).
Pinch(f64, PointerState),
}
// TODO - Clipboard Paste?
// TODO skip is_synthetic=true events
/// A text-related event.
#[derive(Debug, Clone)]
pub enum TextEvent {
/// A keyboard event.
KeyboardKey(KeyEvent, ModifiersState),
/// An IME event.
Ime(Ime),
/// Modifier keys (e.g. Shift, Ctrl, Alt) were pressed or released.
ModifierChange(ModifiersState),
/// The window took or lost focus.
// TODO - Document difference with Update focus change
FocusChange(bool),
}
// TODO - Go into more detail.
/// An accessibility event.
#[derive(Debug, Clone)]
pub struct AccessEvent {
/// The action that was performed.
pub action: accesskit::Action,
/// The data associated with the action.
pub data: Option<accesskit::ActionData>,
}
/// The persistent state of a pointer.
#[derive(Debug, Clone)]
pub struct PointerState {
// TODO
// pub device_id: DeviceId,
/// The position of the pointer in physical coordinates.
/// This is the number of pixels from the top and left of the window.
pub physical_position: PhysicalPosition<f64>,
/// The position of the pointer in logical coordinates.
/// This is different from physical coordinates for high-DPI displays.
pub position: LogicalPosition<f64>,
/// The buttons that are currently pressed (mostly useful for the mouse).
pub buttons: PointerButtons,
/// The modifier keys (e.g. Shift, Ctrl, Alt) that are currently pressed.
pub mods: Modifiers,
/// The number of successive clicks registered. This is used to detect e.g. double-clicks.
pub count: u8,
// TODO - Find out why this was added, maybe remove it.
/// Currently unused.
pub focus: bool,
/// The force of a touch event.
pub force: Option<Force>,
}
/// The light/dark mode of the window.
#[derive(Debug, Clone)]
pub enum WindowTheme {
/// Light mode.
Light,
/// Dark mode.
Dark,
}
// TODO - Rewrite that doc.
/// Application life cycle events.
/// Changes to widget state.
///
/// Unlike [`Event`]s, [`Update`] events are generated by Masonry, and
/// may occur at different times during a given pass of the event loop. The
/// [`Update::WidgetAdded`] event, for instance, may occur when the app
/// first launches (during the handling of [`Event::WindowConnected`]) or it
/// may occur during an [`on_event`](crate::Widget::on_event) pass, if some
/// widget has been added then.
/// Unlike [`PointerEvent`]s, [`TextEvent`]s and [`AccessEvent`]s, [`Update`] events
/// are generated by Masonry, and may occur at different times during a given pass of
/// the event loop.
#[non_exhaustive]
#[derive(Debug, Clone)]
#[allow(variant_size_differences)]
@ -283,7 +340,7 @@ pub enum Update {
StashedChanged(bool),
/// Called when a child widgets uses
/// [`EventCtx::request_pan_to_this`](crate::EventCtx::request_pan_to_this).
/// [`EventCtx::request_scroll_to_this`](crate::EventCtx::request_scroll_to_this).
RequestPanToChild(Rect),
/// Called when the "hovered" status changes.
@ -461,6 +518,9 @@ impl AccessEvent {
}
impl PointerState {
/// Create a new [`PointerState`] with dummy values.
///
/// Mostly used for testing.
pub fn empty() -> Self {
Self {
physical_position: PhysicalPosition::new(0.0, 0.0),

View File

@ -1,6 +1,9 @@
// Copyright 2024 the Xilem Authors
// SPDX-License-Identifier: Apache-2.0
// We use allow because expect(missing_docs) is noisy with rust-analyzer.
#![allow(missing_docs, reason = "We have many as-yet undocumented items")]
use std::num::NonZeroUsize;
use std::sync::Arc;
@ -547,6 +550,8 @@ impl MasonryState<'_> {
}
},
WinitWindowEvent::MouseWheel { delta, .. } => {
// TODO - This delta value doesn't quite make sense.
// Figure out and document a better standard.
let delta = match delta {
winit::event::MouseScrollDelta::LineDelta(x, y) => {
LogicalPosition::new(x as f64, y as f64)

View File

@ -89,6 +89,7 @@
//! [winit]: https://crates.io/crates/winit
//! [Druid]: https://crates.io/crates/druid
//! [Xilem]: https://crates.io/crates/xilem
//! [tracing_tracy]: https://crates.io/crates/tracing-tracy
// LINEBENDER LINT SET - lib.rs - v1
// See https://linebender.org/wiki/canonical-lints/
@ -109,10 +110,6 @@
#![expect(clippy::allow_attributes, reason = "Deferred: Noisy")]
#![expect(clippy::allow_attributes_without_reason, reason = "Deferred: Noisy")]
// TODO: Remove any items listed as "Deferred"
#![expect(
rustdoc::broken_intra_doc_links,
reason = "Deferred: Noisy. Tracked in https://github.com/linebender/xilem/issues/449"
)]
#![expect(clippy::needless_doctest_main, reason = "Deferred: Noisy")]
#![expect(clippy::should_implement_trait, reason = "Deferred: Noisy")]
#![cfg_attr(not(debug_assertions), expect(unused, reason = "Deferred: Noisy"))]
@ -126,8 +123,6 @@
#![expect(clippy::missing_assert_message, reason = "Deferred: Noisy")]
#![expect(clippy::return_self_not_must_use, reason = "Deferred: Noisy")]
#![expect(elided_lifetimes_in_paths, reason = "Deferred: Noisy")]
// https://github.com/rust-lang/rust/pull/130025
#![allow(missing_docs, reason = "We have many as-yet undocumented items")]
#![expect(unreachable_pub, reason = "Potentially controversial code style")]
#![expect(
unnameable_types,

View File

@ -44,6 +44,12 @@ const INVALID_IME_AREA: Rect = Rect::new(f64::NAN, f64::NAN, f64::NAN, f64::NAN)
// --- MARK: STRUCTS ---
/// The composition root of Masonry.
///
/// This is the entry point for all user events, and the source of all signals to be sent to
/// winit or similar event loop runners, as well as 2D scenes and accessibility information.
///
/// This is also the type that owns the widget tree.
pub struct RenderRoot {
pub(crate) root: WidgetPod<Box<dyn Widget>>,
pub(crate) size_policy: WindowSizePolicy,
@ -104,9 +110,20 @@ pub enum WindowSizePolicy {
User,
}
/// Options for creating a [`RenderRoot`].
pub struct RenderRootOptions {
/// If true, `fontique` will provide access to system fonts
/// using platform-specific APIs.
pub use_system_fonts: bool,
/// Defines how the window size should be determined.
pub size_policy: WindowSizePolicy,
/// The scale factor to use for rendering.
///
/// Useful for high-DPI displays.
///
/// `1.0` is a sensible default.
pub scale_factor: f64,
/// Add a font from its raw data for use in tests.
@ -119,35 +136,55 @@ pub struct RenderRootOptions {
pub test_font: Option<Vec<u8>>,
}
/// Objects emitted by the [`RenderRoot`] to signal that something has changed or require external actions.
pub enum RenderRootSignal {
/// A widget has emitted an action.
Action(Action, WidgetId),
/// An IME session has been started.
StartIme,
/// The IME session has ended.
EndIme,
/// The IME area has been moved.
ImeMoved(LogicalPosition<f64>, LogicalSize<f64>),
/// The window needs to be redrawn.
RequestRedraw,
/// The window should be redrawn for an animation frame. Currently this isn't really different from `RequestRedraw`.
RequestAnimFrame,
/// The window should take focus.
TakeFocus,
/// The mouse icon has changed.
SetCursor(CursorIcon),
/// The window size has changed.
SetSize(PhysicalSize<u32>),
/// The window title has changed.
SetTitle(String),
/// The window is being dragged.
DragWindow,
/// The window is being resized.
DragResizeWindow(ResizeDirection),
/// The window is being maximized.
ToggleMaximized,
/// The window is being minimized.
Minimize,
/// The window is being closed.
Exit,
/// The window menu is being shown.
ShowWindowMenu(LogicalPosition<f64>),
}
impl RenderRoot {
pub fn new(
root_widget: impl Widget,
RenderRootOptions {
/// Create a new `RenderRoot` with the given options.
///
/// Note that this doesn't create a window or start the event loop.
///
/// See [`crate::event_loop_runner::run`] for that.
pub fn new(root_widget: impl Widget, options: RenderRootOptions) -> Self {
let RenderRootOptions {
use_system_fonts,
size_policy,
scale_factor,
test_font,
}: RenderRootOptions,
) -> Self {
} = options;
let mut root = Self {
root: WidgetPod::new(root_widget).boxed(),
size_policy,
@ -222,6 +259,7 @@ impl RenderRoot {
}
// --- MARK: WINDOW_EVENT ---
/// Handle a window event.
pub fn handle_window_event(&mut self, event: WindowEvent) -> Handled {
match event {
WindowEvent::Rescale(scale_factor) => {
@ -265,6 +303,7 @@ impl RenderRoot {
}
// --- MARK: PUB FUNCTIONS ---
/// Handle a pointer event.
pub fn handle_pointer_event(&mut self, event: PointerEvent) -> Handled {
let _span = info_span!("pointer_event");
let handled = run_on_pointer_event_pass(self, &event);
@ -274,6 +313,7 @@ impl RenderRoot {
handled
}
/// Handle a text event.
pub fn handle_text_event(&mut self, event: TextEvent) -> Handled {
let _span = info_span!("text_event");
let handled = run_on_text_event_pass(self, &event);
@ -289,6 +329,7 @@ impl RenderRoot {
handled
}
/// Handle an accesskit event.
pub fn handle_access_event(&mut self, event: ActionRequest) {
let _span = info_span!("access_event");
let Ok(id) = event.target.0.try_into() else {
@ -318,6 +359,10 @@ impl RenderRoot {
.register_fonts(data)
}
/// Redraw the window.
///
/// Returns an update to the accessibility tree and a Vello scene representing
/// the widget tree's current state.
pub fn redraw(&mut self) -> (Scene, TreeUpdate) {
if self.root_state().needs_layout {
// TODO - Rewrite more clearly after run_rewrite_passes is rewritten
@ -330,17 +375,23 @@ impl RenderRoot {
}
// TODO - Handle invalidation regions
// TODO - Improve caching of scenes.
(
run_paint_pass(self),
run_accessibility_pass(self, self.scale_factor),
)
}
/// Pop the oldest signal from the queue.
pub fn pop_signal(&mut self) -> Option<RenderRootSignal> {
self.global_state.signal_queue.pop_front()
}
/// Pop the oldest signal from the queue that matches the predicate.
///
/// Doesn't affect other signals.
///
/// Note that you should still use [`Self::pop_signal`] to avoid letting the queue
/// grow indefinitely.
pub fn pop_signal_matching(
&mut self,
predicate: impl Fn(&RenderRootSignal) -> bool,
@ -349,6 +400,7 @@ impl RenderRoot {
self.global_state.signal_queue.remove(idx)
}
/// Get the current icon that the mouse should display.
pub fn cursor_icon(&self) -> CursorIcon {
self.cursor_icon
}

View File

@ -101,6 +101,9 @@ use crate::{Color, Handled, Point, Size, Vec2, Widget, WidgetId};
///
/// # simple_button();
/// ```
///
/// [`assert_render_snapshot`]: crate::assert_render_snapshot
/// [`insta`]: https://docs.rs/insta/latest/insta/
pub struct TestHarness {
render_root: RenderRoot,
mouse_state: PointerState,
@ -171,8 +174,8 @@ impl Default for TestHarnessParams {
impl TestHarness {
/// Builds harness with given root widget.
///
/// Window size will be [`Self::DEFAULT_SIZE`].
/// Background color will be [`Self::DEFAULT_BACKGROUND_COLOR`].
/// Window size will be [`TestHarnessParams::DEFAULT_SIZE`].
/// Background color will be [`TestHarnessParams::DEFAULT_BACKGROUND_COLOR`].
pub fn create(root_widget: impl Widget) -> Self {
Self::create_with(root_widget, TestHarnessParams::default())
}

View File

@ -106,15 +106,25 @@ pub struct Recording(Rc<RefCell<VecDeque<Record>>>);
/// Each member of the enum corresponds to one of the methods on `Widget`.
#[derive(Debug, Clone)]
pub enum Record {
/// Pointer event.
PE(PointerEvent),
/// Text event.
TE(TextEvent),
/// Access event.
AE(AccessEvent),
/// Animation frame.
AF(u64),
/// Register children
RC,
/// Update
U(Update),
/// Layout. Records the size returned by the layout method.
Layout(Size),
/// Compose.
Compose,
/// Paint.
Paint,
/// Accessibility.
Access,
}
@ -122,6 +132,7 @@ pub enum Record {
///
/// Implements helper methods useful for unit testing.
pub trait TestWidgetExt: Widget + Sized + 'static {
/// Wrap this widget in a [`Recorder`] that records all method calls.
fn record(self, recording: &Recording) -> Recorder<Self> {
Recorder {
child: self,
@ -129,6 +140,7 @@ pub trait TestWidgetExt: Widget + Sized + 'static {
}
}
/// Wrap this widget in a [`SizedBox`] with the given id.
fn with_id(self, id: WidgetId) -> SizedBox {
SizedBox::new_with_id(self, id)
}

View File

@ -25,7 +25,7 @@ pub type ArcStr = std::sync::Arc<str>;
/// The Parley [`parley::Brush`] used within Masonry.
///
/// This enables updating of brush details without performing relayouts;
/// the inner values are indexes into the `brushes` argument to [`render_text`].
/// the inner values are indexes into the `brushes` argument to [`render_text()`].
#[derive(Clone, PartialEq, Default, Debug)]
pub struct BrushIndex(pub usize);

View File

@ -77,7 +77,7 @@ impl Widget for Checkbox {
PointerEvent::PointerUp(_, _) => {
if ctx.has_pointer_capture() && ctx.is_hovered() && !ctx.is_disabled() {
self.checked = !self.checked;
ctx.submit_action(Action::CheckboxChecked(self.checked));
ctx.submit_action(Action::CheckboxToggled(self.checked));
trace!("Checkbox {:?} released", ctx.widget_id());
}
// Checked state impacts appearance and accessibility node
@ -94,7 +94,7 @@ impl Widget for Checkbox {
match event.action {
accesskit::Action::Click => {
self.checked = !self.checked;
ctx.submit_action(Action::CheckboxChecked(self.checked));
ctx.submit_action(Action::CheckboxToggled(self.checked));
// Checked state impacts appearance and accessibility node
ctx.request_render();
}
@ -251,7 +251,7 @@ mod tests {
harness.mouse_click_on(checkbox_id);
assert_eq!(
harness.pop_action(),
Some((Action::CheckboxChecked(true), checkbox_id))
Some((Action::CheckboxToggled(true), checkbox_id))
);
assert_debug_snapshot!(harness.root_widget());
@ -260,7 +260,7 @@ mod tests {
harness.mouse_click_on(checkbox_id);
assert_eq!(
harness.pop_action(),
Some((Action::CheckboxChecked(false), checkbox_id))
Some((Action::CheckboxToggled(false), checkbox_id))
);
}

View File

@ -210,14 +210,14 @@ impl Flex {
self
}
/// Builder-style variant of [`WidgetMut::add_child`].
/// Builder-style variant of [`Flex::add_child`].
///
/// Convenient for assembling a group of widgets in a single expression.
pub fn with_child(self, child: impl Widget) -> Self {
self.with_child_pod(WidgetPod::new(Box::new(child)))
}
/// Builder-style variant of [`WidgetMut::add_child`], that takes the id that the child will have.
/// Builder-style variant of [`Flex::add_child`], that takes the id that the child will have.
///
/// Useful for unit tests.
pub fn with_child_id(self, child: impl Widget, id: WidgetId) -> Self {
@ -266,7 +266,7 @@ impl Flex {
/// If you are laying out standard controls in this container, you should
/// generally prefer to use [`add_default_spacer`].
///
/// [`add_default_spacer`]: WidgetMut::add_default_spacer
/// [`add_default_spacer`]: Flex::add_default_spacer
pub fn with_spacer(mut self, mut len: f64) -> Self {
if len < 0.0 {
tracing::warn!("add_spacer called with negative length: {}", len);
@ -422,7 +422,7 @@ impl Flex {
/// If you are laying out standard controls in this container, you should
/// generally prefer to use [`add_default_spacer`].
///
/// [`add_default_spacer`]: WidgetMut::add_default_spacer
/// [`add_default_spacer`]: Flex::add_default_spacer
pub fn add_spacer(this: &mut WidgetMut<'_, Self>, mut len: f64) {
if len < 0.0 {
tracing::warn!("add_spacer called with negative length: {}", len);
@ -506,7 +506,7 @@ impl Flex {
/// If you are laying out standard controls in this container, you should
/// generally prefer to use [`add_default_spacer`].
///
/// [`add_default_spacer`]: WidgetMut::add_default_spacer
/// [`add_default_spacer`]: Flex::add_default_spacer
pub fn insert_spacer(this: &mut WidgetMut<'_, Self>, idx: usize, mut len: f64) {
if len < 0.0 {
tracing::warn!("add_spacer called with negative length: {}", len);

View File

@ -52,7 +52,7 @@ impl Grid {
self
}
/// Builder-style variant of [`WidgetMut::add_child`].
/// Builder-style variant of [`Grid::add_child`].
///
/// Convenient for assembling a group of widgets in a single expression.
pub fn with_child(self, child: impl Widget, params: GridParams) -> Self {

View File

@ -3,6 +3,9 @@
//! Common widgets.
// We use allow because expect(missing_docs) is noisy with rust-analyzer.
#![allow(missing_docs, reason = "We have many as-yet undocumented items")]
#[allow(clippy::module_inception)]
pub(crate) mod widget;
mod widget_mut;

View File

@ -69,7 +69,7 @@ impl<W: Widget> Portal<W> {
///
/// The default is `false`.
///
/// This setting affects how a `ClipBox` lays out its child.
/// This setting affects how a `Portal` lays out its child.
///
/// - When it is `false` (the default), the child does not receive any upper
/// bound on its height: the idea is that the child can be as tall as it
@ -94,7 +94,7 @@ impl<W: Widget> Portal<W> {
///
/// If `false` (the default) there is no minimum constraint on the child's
/// size. If `true`, the child is passed the same minimum constraints as
/// the `ClipBox`.
/// the `Portal`.
pub fn content_must_fill(mut self, must_fill: bool) -> Self {
self.must_fill = must_fill;
self
@ -198,11 +198,11 @@ impl<W: Widget> Portal<W> {
}
/// Set whether the child's size must be greater than or equal the size of
/// the `ClipBox`.
/// the `Portal`.
///
/// See [`content_must_fill`] for more details.
///
/// [`content_must_fill`]: ClipBox::content_must_fill
/// [`content_must_fill`]: Portal::content_must_fill
pub fn set_content_must_fill(this: &mut WidgetMut<'_, Self>, must_fill: bool) {
this.widget.must_fill = must_fill;
this.ctx.request_layout();

View File

@ -287,6 +287,7 @@ impl SizedBox {
/// notably, it can be any [`Color`], any gradient, or an [`Image`].
///
/// [`Image`]: vello::peniko::Image
/// [`Color`]: crate::Color
pub fn background(mut self, brush: impl Into<Brush>) -> Self {
self.background = Some(brush.into());
self
@ -373,6 +374,7 @@ impl SizedBox {
/// notably, it can be any [`Color`], any gradient, or an [`Image`].
///
/// [`Image`]: vello::peniko::Image
/// [`Color`]: crate::Color
pub fn set_background(this: &mut WidgetMut<'_, Self>, brush: impl Into<Brush>) {
this.widget.background = Some(brush.into());
this.ctx.request_paint_only();

View File

@ -27,9 +27,8 @@ use crate::{
/// `WidgetId`s are generated automatically for all widgets in the widget tree.
/// More specifically, each [`WidgetPod`](crate::WidgetPod) has a unique `WidgetId`.
///
/// These ids are used internally to route events, and can be used to communicate
/// between widgets, by submitting a command (as with [`EventCtx::submit_command`])
/// and passing a `WidgetId` as the [`Target`](crate::Target).
/// These ids are used internally to route events, and can be used to fetch a specific
/// widget for testing or event handling.
///
/// A widget can retrieve its id via methods on the various contexts, such as
/// [`UpdateCtx::widget_id`].
@ -52,38 +51,48 @@ impl WidgetId {
}
}
// TODO - Add tutorial: implementing a widget - See https://github.com/linebender/xilem/issues/376
/// The trait implemented by all widgets.
///
/// For details on how to implement this trait, see tutorial **(TODO)**
/// For details on how to implement this trait, see the [tutorials](crate::doc).
///
/// Whenever external events affect the given widget, methods [`on_event`],
/// [`on_status_change`](Self::on_status_change) and [`update`](Self::update)
/// are called. Later on, when the widget is laid out and displayed, methods
/// [`layout`](Self::layout) and [`paint`](Self::paint) are called.
/// Whenever external events affect the given widget, methods
/// [`on_pointer_event`](Self::on_pointer_event),
/// [`on_text_event`](Self::on_text_event),
/// [`on_access_event`](Self::on_access_event),
/// [`on_anim_frame`](Self::on_anim_frame) and [`update`](Self::update) are called.
///
/// Later on, when the widget is laid out and displayed, methods
/// [`layout`](Self::layout), [`compose`](Self::compose), [`paint`](Self::paint) and
/// [`accessibility`](Self::accessibility) are called.
///
/// These trait methods are provided with a corresponding context. The widget can
/// request things and cause actions by calling methods on that context.
///
/// Widgets also have a [`children`](Self::children) method. Leaf widgets return an empty array,
/// whereas container widgets return an array of [`WidgetRef`]. Container widgets
/// have some validity invariants to maintain regarding their children.
/// Widgets also have a [`children_ids`](Self::children_ids) method. Leaf widgets return an empty array,
/// whereas container widgets return an array of [`WidgetId`].
/// Container widgets have some validity invariants to maintain regarding their children.
///
/// Generally speaking, widgets aren't used directly. They are stored in
/// [`WidgetPod`](crate::WidgetPod)s. Widget methods are called by `WidgetPod`s, and the
/// widget is mutated either during a method call (eg `on_event` or `update`) or
/// through a [`WidgetMut`](crate::widget::WidgetMut).
/// Generally speaking, widgets aren't used directly. They are stored by Masonry and accessed
/// through [`WidgetPod`](crate::WidgetPod)s. Widget methods are called by Masonry, and a
/// widget should only be mutated either during a method call or through a [`WidgetMut`](crate::widget::WidgetMut).
#[allow(unused_variables)]
pub trait Widget: AsAny {
/// Handle an event - usually user interaction.
/// Handle a pointer event.
///
/// A number of different events (in the [`Event`] enum) are handled in this
/// method call. A widget can handle these events in a number of ways, such as
/// requesting things from the [`EventCtx`] or mutating the data.
/// Pointer events will target the widget under the pointer, and then the
/// event will bubble to each of its parents.
fn on_pointer_event(&mut self, ctx: &mut EventCtx, event: &PointerEvent) {}
/// Handle a text event.
///
/// Text events will target the [focused widget], then bubble to each parent.
///
/// [focused widget]: crate::doc::doc_06_masonry_concepts#text-focus
fn on_text_event(&mut self, ctx: &mut EventCtx, event: &TextEvent) {}
/// Handle an event from the platform's accessibility API.
///
/// Accessibility events target a specific widget id, then bubble to each parent.
fn on_access_event(&mut self, ctx: &mut EventCtx, event: &AccessEvent) {}
/// Called at the beginning of a new animation frame.
@ -236,7 +245,7 @@ pub trait Widget: AsAny {
/// Return the cursor icon for this widget.
///
/// This will be called when the mouse moves or [`request_cursor_icon_change`](MutateCtx::request_cursor_icon_change) is called.
/// This will be called when the mouse moves or [`request_cursor_icon_change`](crate::MutateCtx::request_cursor_icon_change) is called.
///
/// **pos** - the mouse position in global coordinates (e.g. `(0,0)` is the top-left corner of the
/// window).

View File

@ -257,20 +257,17 @@ impl WidgetState {
}
/// The paint region for this widget.
///
/// For more information, see [`WidgetPod::paint_rect`](crate::WidgetPod::paint_rect).
pub fn paint_rect(&self) -> Rect {
self.local_paint_rect + self.origin.to_vec2()
}
/// The rectangle used when calculating layout with other widgets
///
/// For more information, see [`WidgetPod::layout_rect`](crate::WidgetPod::layout_rect).
// TODO - Remove
/// The rectangle used when calculating layout with other widgets.
pub fn layout_rect(&self) -> Rect {
Rect::from_origin_size(self.origin, self.size)
}
/// The [`layout_rect`](crate::WidgetPod::layout_rect) in window coordinates.
/// The [`layout_rect`](Self::layout_rect) in window coordinates.
///
/// This might not map to a visible area of the screen, eg if the widget is scrolled
/// away.

View File

@ -75,7 +75,7 @@ where
);
match message.downcast::<masonry::Action>() {
Ok(action) => {
if let masonry::Action::CheckboxChecked(checked) = *action {
if let masonry::Action::CheckboxToggled(checked) = *action {
MessageResult::Action((self.callback)(app_state, checked))
} else {
tracing::error!("Wrong action type in Checkbox::message: {action:?}");