Skip to content

Commit cad61d5

Browse files
authored
Tweak documentation for readability (#787)
Apply many small tweaks I found while going over the documentation. Rename some variables. Add asserts to `BoxConstraints::constrain_aspect_ratio`. Flesh out the WidgetState documentation. Introduce the concept of "zombie flags".
1 parent 5e3383a commit cad61d5

27 files changed

+170
-225
lines changed

masonry/src/action.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use std::any::Any;
55

66
use crate::event::PointerButton;
77

8-
// TODO - Refactor - See issue https://github.com/linebender/xilem/issues/335
8+
// TODO - Replace actions with an associated type on the Widget trait
9+
// See https://github.com/linebender/xilem/issues/664
910

1011
// TODO - TextCursor changed, ImeChanged, EnterKey, MouseEnter
1112
#[non_exhaustive]

masonry/src/box_constraints.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,17 @@ impl BoxConstraints {
184184
///
185185
/// Use this function when maintaining an aspect ratio is more important than minimizing the
186186
/// distance between input and output size width and height.
187+
///
188+
/// ## Panics
189+
///
190+
/// Panics if `aspect_ratio` or `width` are NaN, infinite or negative.
191+
#[track_caller]
187192
pub fn constrain_aspect_ratio(&self, aspect_ratio: f64, width: f64) -> Size {
193+
assert!(aspect_ratio.is_finite());
194+
assert!(width.is_finite());
195+
assert!(aspect_ratio >= 0.0);
196+
assert!(width >= 0.0);
197+
188198
// Minimizing/maximizing based on aspect ratio seems complicated, but in reality everything
189199
// is linear, so the amount of work to do is low.
190200
let ideal_size = Size {
@@ -201,8 +211,6 @@ impl BoxConstraints {
201211
return ideal_size;
202212
}
203213

204-
// Then we check if any `Size`s with our desired aspect ratio are inside the constraints.
205-
// TODO this currently outputs garbage when things are < 0 - See https://github.com/linebender/xilem/issues/377
206214
let min_w_min_h = self.min.height / self.min.width;
207215
let max_w_min_h = self.min.height / self.max.width;
208216
let min_w_max_h = self.max.height / self.min.width;

masonry/src/contexts.rs

+1-14
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33

44
//! The context types that are passed into various widget methods.
55
6-
use std::time::Duration;
7-
86
use accesskit::TreeUpdate;
97
use dpi::LogicalPosition;
108
use parley::{FontContext, LayoutContext};
@@ -574,7 +572,7 @@ impl_context_method!(MutateCtx<'_>, EventCtx<'_>, UpdateCtx<'_>, {
574572
pub fn children_changed(&mut self) {
575573
trace!("children_changed");
576574
self.widget_state.children_changed = true;
577-
self.widget_state.update_focus_chain = true;
575+
self.widget_state.needs_update_focus_chain = true;
578576
self.request_layout();
579577
}
580578

@@ -710,14 +708,6 @@ impl_context_method!(
710708
.push_back(RenderRootSignal::ShowWindowMenu(position));
711709
}
712710

713-
/// Request a timer event.
714-
///
715-
/// The return value is a token, which can be used to associate the
716-
/// request with the event.
717-
pub fn request_timer(&mut self, _deadline: Duration) -> TimerToken {
718-
todo!("request_timer");
719-
}
720-
721711
/// Mark child widget as stashed.
722712
///
723713
/// If `stashed` is true, the child will not be painted or listed in the accessibility tree.
@@ -738,9 +728,6 @@ impl_context_method!(
738728
}
739729
);
740730

741-
// FIXME - Remove
742-
pub struct TimerToken;
743-
744731
impl EventCtx<'_> {
745732
// TODO - clearly document all semantics of pointer capture when they've been decided on
746733
// TODO - Figure out cases where widget should be notified of pointer capture

masonry/src/debug_logger.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ impl DebugLogger {
204204
StateTree::new("has_focus", w_state.has_focus),
205205
StateTree::new("request_anim", w_state.request_anim),
206206
StateTree::new("children_changed", w_state.children_changed),
207-
StateTree::new("update_focus_chain", w_state.update_focus_chain),
207+
StateTree::new("update_focus_chain", w_state.needs_update_focus_chain),
208208
]
209209
.into();
210210
state

masonry/src/doc/01_creating_app.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,8 @@ When handling `ButtonPressed`:
118118
- `ctx.render_root()` returns a reference to the `RenderRoot`, which owns the widget tree and all the associated visual state.
119119
- `RenderRoot::edit_root_widget()` takes a closure; that closure takes a `WidgetMut<Box<dyn Widget>>` which we call `root`. Once the closure returns, `RenderRoot` runs some passes to update the app's internal states.
120120
- `root.downcast::<...>()` returns a `WidgetMut<RootWidget<...>>`.
121-
- `RootWidget::child_mut()` returns a `WidgetMut<Portal<...>>` for the `Portal`.
122-
- `Portal::child_mut()` returns a `WidgetMut<Flex>` for the `Flex`.
121+
- `RootWidget::child_mut()` returns a `WidgetMut<Portal<...>>`.
122+
- `Portal::child_mut()` returns a `WidgetMut<Flex>`.
123123

124124
A [`WidgetMut`] is a smart reference type which lets us modify the widget tree.
125125
It's set up to automatically propagate update flags and update internal state when dropped.

masonry/src/doc/03_implementing_container_widget.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -254,11 +254,11 @@ Doesn't `VerticalStack::paint` need to call `paint` on its children, for instanc
254254

255255
It doesn't.
256256

257-
In Masonry, most passes are automatically propagated to children, without container widgets having to implement code iterating over their children.
257+
In Masonry, most passes are automatically propagated to children, and so container widgets do not need to *and cannot* call the pass methods on their children.
258258

259259
So for instance, if `VerticalStack::children_ids()` returns a list of three children, the paint pass will automatically call `paint` on all three children after `VerticalStack::paint()`.
260260

261-
So various methods in container widgets should only implement the logic that is specific to the container itself.
261+
Pass methods in container widgets should only implement the logic that is specific to the container itself.
262262
For instance, a container widget with a background color should implement `paint` to draw the background.
263263

264264
[`Widget`]: crate::Widget

masonry/src/doc/05_pass_system.md

+16-12
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@
1212
1313
</div>
1414

15-
Masonry has a set of **passes**, which are computations run over a subset of the widget tree during every frame.
15+
Masonry's internal architecture is based on a set of **passes**, which are computations run over a subset of the widget tree during every frame.
1616

1717
Passes can be split into roughly three categories:
1818

19-
- **Event passes:** triggered by user interaction.
20-
- **Rewrite passes:** run after every event pass, may run multiple times until all invalidation flags are cleared.
21-
- **Render passes:** run just before rendering a new frame.
19+
- **Event passes** are triggered by user interaction.
20+
- **Rewrite passes** run after every event pass, may run multiple times until all invalidation flags are cleared.
21+
- **Render passes** run just before rendering a new frame.
2222

23-
Note: unless otherwise specified, all passes run over widgets in depth-first preorder.
23+
Note that unless otherwise specified, all passes run over widgets in depth-first preorder, where child order is determined by their position in the `children_ids()` array.
2424

2525

2626
## Event passes
@@ -68,7 +68,7 @@ To address these invalidations, Masonry runs a set of **rewrite passes** over th
6868
The layout and compose passes have methods with matching names in the Widget trait.
6969
The update_xxx passes call the widgets' update method.
7070

71-
By default, each of these passes completes immediately, unless pass-dependent invalidation flags are set.
71+
By default, each of these passes completes without doing any work, unless pass-dependent invalidation flags are set.
7272
Each pass can generally request work for later passes; for instance, the mutate pass can invalidate the layout of a widget, in which case the layout pass will run on that widget and its children and parents.
7373

7474
Passes may also request work for *previous* passes, in which case all rewrite passes are run again in sequence.
@@ -106,6 +106,13 @@ It is called when new widgets are added to the tree, or existing widgets are rem
106106

107107
It will call the `register_children()` widget method on container widgets whose children changed, then the `update()` method with the `WidgetAdded` event on new widgets.
108108

109+
<!-- TODO - document update disabled --- -->
110+
<!-- TODO - document update stashed --- -->
111+
<!-- TODO - document update focus chain --- -->
112+
<!-- TODO - document update focus --- (document iteration order) -->
113+
<!-- TODO - document update scroll --- -->
114+
<!-- TODO - document update pointer --- (document iteration order) -->
115+
109116
### Layout pass
110117

111118
The layout pass runs bidirectionally, passing constraints from the top down and getting back sizes and other layout info from the bottom up.
@@ -126,8 +133,6 @@ Because the compose pass is more limited than layout, it's easier to recompute i
126133

127134
For instance, if a widget in a list changes size, its siblings and parents must be re-laid out to account for the change; whereas changing a given widget's transform only affects its children.
128135

129-
Masonry automatically calls the `compose` methods of all widgets in the tree, in depth-first preorder, where child order is determined by their position in the `children_ids()` array.
130-
131136

132137
## Render passes
133138

@@ -144,17 +149,16 @@ These nodes together form the accessibility tree.
144149
Methods for these passes should be written under the assumption that they can be skipped or called multiple times for arbitrary reasons.
145150
Therefore, their ability to affect the widget tree is limited.
146151

147-
Masonry automatically calls these methods for all widgets in the tree in depth-first preorder.
148152

149153
## External mutation
150154

151155
Code with mutable access to the `RenderRoot`, like the Xilem app runner, can get mutable access to the root widget and all its children through the `edit_root_widget()` method, which takes a callback and passes it a `WidgetMut` to the root widget.
152156

153-
This is in effect a MUTATE pass which only processes one callback.
157+
This is in effect a "mutate" pass which only processes one callback.
154158

155159
External mutation is how Xilem applies any changes to the widget tree produced by its reactive step.
156160

157-
Calling the `edit_root_widget()` method, or any similar direct-mutation method, triggers the entire set of rewrite passes.
161+
`RenderRoot::edit_root_widget()` triggers the entire set of rewrite passes before returning.
158162

159163

160164
## Pass context types
@@ -164,7 +168,7 @@ Some notes about pass context types:
164168
- Render passes should be pure and can be skipped occasionally, therefore their context types ([`PaintCtx`] and [`AccessCtx`]) can't set invalidation flags or send signals.
165169
- The `layout` and `compose` passes lay out all widgets, which are transiently invalid during the passes, therefore [`LayoutCtx`]and [`ComposeCtx`] cannot access the size and position of the `self` widget.
166170
They can access the layout of children if they have already been laid out.
167-
- For the same reason, `LayoutCtx`and `ComposeCtx` cannot create a `WidgetRef` reference to a child.
171+
- For the same reason, [`LayoutCtx`]and [`ComposeCtx`] cannot create a `WidgetRef` reference to a child.
168172
- [`MutateCtx`], [`EventCtx`] and [`UpdateCtx`] can let you add and remove children.
169173
- [`RegisterCtx`] can't do anything except register children.
170174
- [`QueryCtx`] provides read-only information about the widget.

masonry/src/event.rs

+39-28
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,6 @@ impl From<PointerButton> for PointerButtons {
181181
// TODO - How can RenderRoot express "I started a drag-and-drop op"?
182182
// TODO - Touchpad, Touch, AxisMotion
183183
// TODO - How to handle CursorEntered?
184-
// Note to self: Events like "pointerenter", "pointerleave" are handled differently at the Widget level. But that's weird because WidgetPod can distribute them. Need to think about this again.
185184
#[derive(Debug, Clone)]
186185
pub enum PointerEvent {
187186
PointerDown(PointerButton, PointerState),
@@ -313,21 +312,7 @@ pub enum Update {
313312
}
314313

315314
impl PointerEvent {
316-
pub fn new_pointer_leave() -> Self {
317-
// TODO - The fact we're creating so many dummy values might be
318-
// a sign we should refactor that struct
319-
let pointer_state = PointerState {
320-
physical_position: Default::default(),
321-
position: Default::default(),
322-
buttons: Default::default(),
323-
mods: Default::default(),
324-
count: 0,
325-
focus: false,
326-
force: None,
327-
};
328-
Self::PointerLeave(pointer_state)
329-
}
330-
315+
/// Returns the [`PointerState`] of the event.
331316
pub fn pointer_state(&self) -> &PointerState {
332317
match self {
333318
Self::PointerDown(_, state)
@@ -343,13 +328,17 @@ impl PointerEvent {
343328
}
344329
}
345330

331+
/// Returns the position of the pointer event, except for [`PointerEvent::PointerLeave`] and [`PointerEvent::HoverFileCancel`].
346332
pub fn position(&self) -> Option<LogicalPosition<f64>> {
347333
match self {
348334
Self::PointerLeave(_) | Self::HoverFileCancel(_) => None,
349335
_ => Some(self.pointer_state().position),
350336
}
351337
}
352338

339+
/// Short name, for debug logging.
340+
///
341+
/// Returns the enum variant name.
353342
pub fn short_name(&self) -> &'static str {
354343
match self {
355344
Self::PointerDown(_, _) => "PointerDown",
@@ -365,6 +354,10 @@ impl PointerEvent {
365354
}
366355
}
367356

357+
/// Returns true if the event is likely to occur every frame.
358+
///
359+
/// Developers should avoid logging during high-density events to avoid
360+
/// cluttering the console.
368361
pub fn is_high_density(&self) -> bool {
369362
match self {
370363
Self::PointerDown(_, _) => false,
@@ -379,23 +372,47 @@ impl PointerEvent {
379372
Self::Pinch(_, _) => true,
380373
}
381374
}
375+
376+
/// Create a [`PointerEvent::PointerLeave`] event with dummy values.
377+
///
378+
/// This is used internally to create synthetic `PointerLeave` events when pointer
379+
/// capture is lost.
380+
pub fn new_pointer_leave() -> Self {
381+
// TODO - The fact we're creating so many dummy values might be
382+
// a sign we should refactor that struct
383+
let pointer_state = PointerState {
384+
physical_position: Default::default(),
385+
position: Default::default(),
386+
buttons: Default::default(),
387+
mods: Default::default(),
388+
count: 0,
389+
focus: false,
390+
force: None,
391+
};
392+
Self::PointerLeave(pointer_state)
393+
}
382394
}
383395

384396
impl TextEvent {
397+
/// Short name, for debug logging.
385398
pub fn short_name(&self) -> &'static str {
386399
match self {
387-
Self::KeyboardKey(KeyEvent { repeat: true, .. }, _) => "KeyboardKey (repeat)",
400+
Self::KeyboardKey(KeyEvent { repeat: true, .. }, _) => "KeyboardKey(repeat)",
388401
Self::KeyboardKey(_, _) => "KeyboardKey",
389402
Self::Ime(Ime::Disabled) => "Ime::Disabled",
390403
Self::Ime(Ime::Enabled) => "Ime::Enabled",
391404
Self::Ime(Ime::Commit(_)) => "Ime::Commit",
392405
Self::Ime(Ime::Preedit(s, _)) if s.is_empty() => "Ime::Preedit(\"\")",
393-
Self::Ime(Ime::Preedit(_, _)) => "Ime::Preedit",
406+
Self::Ime(Ime::Preedit(_, _)) => "Ime::Preedit(\"...\")",
394407
Self::ModifierChange(_) => "ModifierChange",
395408
Self::FocusChange(_) => "FocusChange",
396409
}
397410
}
398411

412+
/// Returns true if the event is likely to occur every frame.
413+
///
414+
/// Developers should avoid logging during high-density events to avoid
415+
/// cluttering the console.
399416
pub fn is_high_density(&self) -> bool {
400417
match self {
401418
Self::KeyboardKey(_, _) => false,
@@ -408,6 +425,9 @@ impl TextEvent {
408425
}
409426

410427
impl AccessEvent {
428+
/// Short name, for debug logging.
429+
///
430+
/// Returns the enum variant name.
411431
pub fn short_name(&self) -> &'static str {
412432
match self.action {
413433
accesskit::Action::Click => "Click",
@@ -442,15 +462,6 @@ impl AccessEvent {
442462

443463
impl PointerState {
444464
pub fn empty() -> Self {
445-
#[cfg(FALSE)]
446-
#[allow(unsafe_code)]
447-
// SAFETY: Uuuuh, unclear. Winit says the dummy id should only be used in
448-
// tests and should never be passed to winit. In principle, we're never
449-
// passing this id to winit, but it's still visible to custom widgets which
450-
// might do so if they tried really hard.
451-
// It would be a lot better if winit could just make this constructor safe.
452-
let device_id = unsafe { DeviceId::dummy() };
453-
454465
Self {
455466
physical_position: PhysicalPosition::new(0.0, 0.0),
456467
position: LogicalPosition::new(0.0, 0.0),
@@ -466,7 +477,7 @@ impl PointerState {
466477
impl Update {
467478
/// Short name, for debug logging.
468479
///
469-
/// Essentially returns the enum variant name.
480+
/// Returns the enum variant name.
470481
pub fn short_name(&self) -> &str {
471482
match self {
472483
Self::WidgetAdded => "WidgetAdded",

masonry/src/passes/accessibility.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ fn build_access_node(widget: &mut dyn Widget, ctx: &mut AccessCtx) -> Node {
102102
.collect::<Vec<NodeId>>(),
103103
);
104104

105-
// Note - These WidgetState flags can be modified by other passes.
105+
// Note - The values returned by these methods can be modified by other passes.
106106
// When that happens, the other pass should set flags to request an accessibility pass.
107107
if ctx.is_disabled() {
108108
node.set_disabled();

masonry/src/passes/compose.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ fn compose_widget(
7474
pub(crate) fn run_compose_pass(root: &mut RenderRoot) {
7575
let _span = info_span!("compose").entered();
7676

77-
// If widgets are moved, pointer-related info may be stale.
77+
// If widgets have moved, pointer-related info may be stale.
7878
// For instance, the "hovered" widget may have moved and no longer be under the pointer.
7979
if root.root_state().needs_compose {
8080
root.global_state.needs_pointer_pass = true;

masonry/src/passes/event.rs

+4-9
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::render_root::RenderRoot;
1111
use crate::{AccessEvent, EventCtx, Handled, PointerEvent, TextEvent, Widget, WidgetId};
1212

1313
// --- MARK: HELPERS ---
14-
fn get_target_widget(
14+
fn get_pointer_target(
1515
root: &RenderRoot,
1616
pointer_pos: Option<LogicalPosition<f64>>,
1717
) -> Option<WidgetId> {
@@ -89,8 +89,8 @@ pub(crate) fn run_on_pointer_event_pass(root: &mut RenderRoot, event: &PointerEv
8989

9090
if event.is_high_density() {
9191
// We still want to record that this pass occurred in the debug file log.
92-
// However, we choose not record any other tracing for this event,
93-
// as that would have a lot of noise.
92+
// However, we choose not to record any other tracing for this event,
93+
// as that would create a lot of noise.
9494
trace!("Running ON_POINTER_EVENT pass with {}", event.short_name());
9595
} else {
9696
debug!("Running ON_POINTER_EVENT pass with {}", event.short_name());
@@ -101,7 +101,7 @@ pub(crate) fn run_on_pointer_event_pass(root: &mut RenderRoot, event: &PointerEv
101101
root.last_mouse_pos = event.position();
102102
}
103103

104-
let target_widget_id = get_target_widget(root, event.position());
104+
let target_widget_id = get_pointer_target(root, event.position());
105105

106106
let handled = run_event_pass(
107107
root,
@@ -135,11 +135,6 @@ pub(crate) fn run_on_pointer_event_pass(root: &mut RenderRoot, event: &PointerEv
135135
handled
136136
}
137137

138-
// TODO https://github.com/linebender/xilem/issues/376 - Some implicit invariants:
139-
// - If a Widget gets a keyboard event or an ImeStateChange, then
140-
// focus is on it, its child or its parent.
141-
// - If a Widget has focus, then none of its parents is hidden
142-
143138
// --- MARK: TEXT EVENT ---
144139
pub(crate) fn run_on_text_event_pass(root: &mut RenderRoot, event: &TextEvent) -> Handled {
145140
if matches!(event, TextEvent::FocusChange(false)) {

0 commit comments

Comments
 (0)