Create WidgetArenaMut type and start refactor of pass internals. (#1197)

This is the first step of a refactor where widgets, widget states and
widget properties will be stored in the same object. So we would end up
with a type like this:

```rust
struct WidgetArenaNode {
    pub(crate) widget: Box<dyn Widget>,
    pub(crate) state: WidgetState,
    pub(crate) properties: AnyMap,
}
```

and WidgetArena might look like:

```rust
pub(crate) struct WidgetArena {
    pub(crate) nodes: TreeArena<WidgetArenaNode>,
}
```

Doing so without making an absolutely unreadable diff is tough, so I
split the work into several PRs. This one just moves code around to make
future diffs shorter.
This commit is contained in:
Olivier FAURE 2025-07-21 19:01:29 +02:00 committed by GitHub
parent 38e3d9a564
commit 69e5f2b808
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 638 additions and 542 deletions

View File

@ -16,8 +16,8 @@ use vello::kurbo::{Rect, Size};
use crate::core::{
AccessEvent, BrushIndex, DefaultProperties, ErasedAction, Handled, Ime, PointerEvent,
PropertiesRef, QueryCtx, ResizeDirection, TextEvent, Widget, WidgetArena, WidgetId, WidgetMut,
WidgetPod, WidgetRef, WidgetState, WindowEvent,
PropertiesRef, QueryCtx, ResizeDirection, TextEvent, Widget, WidgetArena, WidgetArenaMut,
WidgetArenaRef, WidgetId, WidgetMut, WidgetPod, WidgetRef, WidgetState, WindowEvent,
};
use crate::passes::accessibility::run_accessibility_pass;
use crate::passes::anim::run_update_anim_pass;
@ -459,31 +459,8 @@ impl RenderRoot {
// --- MARK: ACCESS WIDGETS
/// Get a [`WidgetRef`] to the root widget.
pub fn get_root_widget(&self) -> WidgetRef<'_, dyn Widget> {
let root_state_token = self.widget_arena.states.roots();
let root_widget_token = self.widget_arena.widgets.roots();
let root_properties_token = self.widget_arena.properties.roots();
let state_ref = root_state_token
.into_item(self.root.id())
.expect("root widget not in widget tree");
let widget_ref = root_widget_token
.into_item(self.root.id())
.expect("root widget not in widget tree");
let properties_ref = root_properties_token
.into_item(self.root.id())
.expect("root widget not in widget tree");
let widget = &**widget_ref.item;
let ctx = QueryCtx {
global_state: &self.global_state,
widget_state_children: state_ref.children,
widget_children: widget_ref.children,
widget_state: state_ref.item,
properties: PropertiesRef {
map: properties_ref.item,
default_map: self.default_properties.for_widget(widget.type_id()),
},
properties_children: properties_ref.children,
};
WidgetRef { ctx, widget }
self.get_widget(self.root.id())
.expect("root widget not in widget tree")
}
/// Get a [`WidgetRef`] to a specific widget.
@ -500,17 +477,23 @@ impl RenderRoot {
.find(id)
.expect("found state but not properties");
let children = WidgetArenaRef {
widget_children: widget_ref.children,
widget_state_children: state_ref.children,
properties_children: properties_ref.children,
};
let widget = &**widget_ref.item;
let state = state_ref.item;
let properties = properties_ref.item;
let ctx = QueryCtx {
global_state: &self.global_state,
widget_state_children: state_ref.children,
widget_children: widget_ref.children,
widget_state: state_ref.item,
widget_state: state,
properties: PropertiesRef {
map: properties_ref.item,
map: properties,
default_map: self.default_properties.for_widget(widget.type_id()),
},
properties_children: properties_ref.children,
children,
};
Some(WidgetRef { ctx, widget })
}
@ -628,25 +611,27 @@ impl RenderRoot {
pub(crate) fn request_render_all(&mut self) {
fn request_render_all_in(
mut widget: ArenaMut<'_, Box<dyn Widget>>,
widget: ArenaMut<'_, Box<dyn Widget>>,
state: ArenaMut<'_, WidgetState>,
properties: ArenaMut<'_, AnyMap>,
) {
state.item.needs_paint = true;
state.item.needs_accessibility = true;
state.item.request_paint = true;
state.item.request_accessibility = true;
let children = WidgetArenaMut {
widget_children: widget.children,
widget_state_children: state.children,
properties_children: properties.children,
};
let widget = &mut **widget.item;
let state = state.item;
let id = state.item.id;
recurse_on_children(
id,
widget.reborrow_mut(),
state.children,
properties.children,
|widget, state, properties| {
request_render_all_in(widget, state, properties);
},
);
state.needs_paint = true;
state.needs_accessibility = true;
state.request_paint = true;
state.request_accessibility = true;
let id = state.id;
recurse_on_children(id, widget, children, |widget, state, properties| {
request_render_all_in(widget, state, properties);
});
}
let (root_widget, root_state, root_properties) =

View File

@ -10,19 +10,18 @@ use anymore::AnyDebug;
use dpi::{LogicalPosition, PhysicalPosition};
use parley::{FontContext, LayoutContext};
use tracing::{trace, warn};
use tree_arena::{ArenaMutList, ArenaRefList};
use vello::kurbo::{Affine, Insets, Point, Rect, Size, Vec2};
use crate::app::{MutateCallback, RenderRootSignal, RenderRootState};
use crate::core::{
AllowRawMut, BoxConstraints, BrushIndex, CreateWidget, DefaultProperties, ErasedAction,
FromDynWidget, PropertiesMut, PropertiesRef, ResizeDirection, Widget, WidgetId, WidgetMut,
WidgetPod, WidgetRef, WidgetState,
FromDynWidget, PropertiesMut, PropertiesRef, ResizeDirection, Widget, WidgetArenaMut,
WidgetArenaRef, WidgetId, WidgetMut, WidgetPod, WidgetRef, WidgetState,
};
use crate::debug_panic;
use crate::passes::layout::run_layout_on;
use crate::peniko::Color;
use crate::util::{AnyMap, get_debug_color};
use crate::util::get_debug_color;
// Note - Most methods defined in this file revolve around `WidgetState` fields.
// Consider reading `WidgetState` documentation (especially the documented naming scheme)
@ -52,10 +51,8 @@ pub struct MutateCtx<'a> {
pub(crate) global_state: &'a mut RenderRootState,
pub(crate) parent_widget_state: Option<&'a mut WidgetState>,
pub(crate) widget_state: &'a mut WidgetState,
pub(crate) widget_state_children: ArenaMutList<'a, WidgetState>,
pub(crate) widget_children: ArenaMutList<'a, Box<dyn Widget>>,
pub(crate) properties: PropertiesMut<'a>,
pub(crate) properties_children: ArenaMutList<'a, AnyMap>,
pub(crate) children: WidgetArenaMut<'a>,
}
/// A context provided inside of [`WidgetRef`].
@ -65,19 +62,15 @@ pub struct MutateCtx<'a> {
pub struct QueryCtx<'a> {
pub(crate) global_state: &'a RenderRootState,
pub(crate) widget_state: &'a WidgetState,
pub(crate) widget_state_children: ArenaRefList<'a, WidgetState>,
pub(crate) widget_children: ArenaRefList<'a, Box<dyn Widget>>,
pub(crate) properties: PropertiesRef<'a>,
pub(crate) properties_children: ArenaRefList<'a, AnyMap>,
pub(crate) children: WidgetArenaRef<'a>,
}
/// A context provided to event-handling [`Widget`] methods.
pub struct EventCtx<'a> {
pub(crate) global_state: &'a mut RenderRootState,
pub(crate) widget_state: &'a mut WidgetState,
pub(crate) widget_state_children: ArenaMutList<'a, WidgetState>,
pub(crate) widget_children: ArenaMutList<'a, Box<dyn Widget>>,
pub(crate) properties_children: ArenaMutList<'a, AnyMap>,
pub(crate) children: WidgetArenaMut<'a>,
pub(crate) target: WidgetId,
pub(crate) allow_pointer_capture: bool,
pub(crate) is_handled: bool,
@ -85,9 +78,7 @@ pub struct EventCtx<'a> {
/// A context provided to the [`Widget::register_children`] method.
pub struct RegisterCtx<'a> {
pub(crate) widget_state_children: ArenaMutList<'a, WidgetState>,
pub(crate) widget_children: ArenaMutList<'a, Box<dyn Widget>>,
pub(crate) properties_children: ArenaMutList<'a, AnyMap>,
pub(crate) children: WidgetArenaMut<'a>,
#[cfg(debug_assertions)]
pub(crate) registered_ids: Vec<WidgetId>,
}
@ -96,9 +87,7 @@ pub struct RegisterCtx<'a> {
pub struct UpdateCtx<'a> {
pub(crate) global_state: &'a mut RenderRootState,
pub(crate) widget_state: &'a mut WidgetState,
pub(crate) widget_state_children: ArenaMutList<'a, WidgetState>,
pub(crate) widget_children: ArenaMutList<'a, Box<dyn Widget>>,
pub(crate) properties_children: ArenaMutList<'a, AnyMap>,
pub(crate) children: WidgetArenaMut<'a>,
}
// TODO - Change this once other layout methods are added.
@ -106,9 +95,7 @@ pub struct UpdateCtx<'a> {
pub struct LayoutCtx<'a> {
pub(crate) global_state: &'a mut RenderRootState,
pub(crate) widget_state: &'a mut WidgetState,
pub(crate) widget_state_children: ArenaMutList<'a, WidgetState>,
pub(crate) widget_children: ArenaMutList<'a, Box<dyn Widget>>,
pub(crate) properties_children: ArenaMutList<'a, AnyMap>,
pub(crate) children: WidgetArenaMut<'a>,
pub(crate) default_properties: &'a DefaultProperties,
}
@ -116,16 +103,14 @@ pub struct LayoutCtx<'a> {
pub struct ComposeCtx<'a> {
pub(crate) global_state: &'a mut RenderRootState,
pub(crate) widget_state: &'a mut WidgetState,
pub(crate) widget_state_children: ArenaMutList<'a, WidgetState>,
pub(crate) widget_children: ArenaMutList<'a, Box<dyn Widget>>,
pub(crate) children: WidgetArenaMut<'a>,
}
/// A context passed to [`Widget::paint`] method.
pub struct PaintCtx<'a> {
pub(crate) global_state: &'a mut RenderRootState,
pub(crate) widget_state: &'a WidgetState,
pub(crate) widget_state_children: ArenaMutList<'a, WidgetState>,
pub(crate) widget_children: ArenaMutList<'a, Box<dyn Widget>>,
pub(crate) children: WidgetArenaMut<'a>,
pub(crate) debug_paint: bool,
}
@ -133,9 +118,7 @@ pub struct PaintCtx<'a> {
pub struct AccessCtx<'a> {
pub(crate) global_state: &'a mut RenderRootState,
pub(crate) widget_state: &'a WidgetState,
pub(crate) widget_state_children: ArenaMutList<'a, WidgetState>,
pub(crate) widget_children: ArenaMutList<'a, Box<dyn Widget>>,
pub(crate) properties_children: ArenaMutList<'a, AnyMap>,
pub(crate) children: WidgetArenaMut<'a>,
pub(crate) tree_update: &'a mut TreeUpdate,
pub(crate) rebuild_all: bool,
}
@ -161,6 +144,7 @@ impl_context_method!(
/// 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
.children
.widget_children
.item(child.id())
.expect("get_child: child not found");
@ -173,6 +157,7 @@ impl_context_method!(
/// Helper method to get a direct reference to a child widget from its `WidgetPod`.
fn get_child_dyn(&self, child: &'_ WidgetPod<impl Widget + ?Sized>) -> &'_ dyn Widget {
let child_ref = self
.children
.widget_children
.item(child.id())
.expect("get_child: child not found");
@ -183,6 +168,7 @@ impl_context_method!(
/// Helper method to get a direct reference to a child widget's `WidgetState` from its `WidgetPod`.
fn get_child_state(&self, child: &'_ WidgetPod<impl Widget + ?Sized>) -> &'_ WidgetState {
let child_state_ref = self
.children
.widget_state_children
.item(child.id())
.expect("get_child_state: child not found");
@ -213,6 +199,7 @@ impl_context_method!(
child: &'_ mut WidgetPod<Child>,
) -> &'_ mut WidgetState {
let child_state_mut = self
.children
.widget_state_children
.item_mut(child.id())
.expect("get_child_state_mut: child not found");
@ -230,28 +217,34 @@ impl MutateCtx<'_> {
child: &'c mut WidgetPod<Child>,
) -> WidgetMut<'c, Child> {
let child_state_mut = self
.children
.widget_state_children
.item_mut(child.id())
.expect("get_mut: child not found");
let child_mut = self
.children
.widget_children
.item_mut(child.id())
.expect("get_mut: child not found");
let child_properties = self
.children
.properties_children
.item_mut(child.id())
.expect("get_mut: child not found");
let children = WidgetArenaMut {
widget_state_children: child_state_mut.children,
widget_children: child_mut.children,
properties_children: child_properties.children,
};
let child_ctx = MutateCtx {
global_state: self.global_state,
parent_widget_state: Some(&mut self.widget_state),
widget_state: child_state_mut.item,
widget_state_children: child_state_mut.children,
widget_children: child_mut.children,
properties: PropertiesMut {
map: child_properties.item,
default_map: self.properties.default_map,
},
properties_children: child_properties.children,
children,
};
WidgetMut {
ctx: child_ctx,
@ -267,10 +260,8 @@ impl MutateCtx<'_> {
// It will still be called when the original borrow is dropped.
parent_widget_state: None,
widget_state: self.widget_state,
widget_state_children: self.widget_state_children.reborrow_mut(),
widget_children: self.widget_children.reborrow_mut(),
properties: self.properties.reborrow_mut(),
properties_children: self.properties_children.reborrow_mut(),
children: self.children.reborrow_mut(),
}
}
@ -278,9 +269,7 @@ impl MutateCtx<'_> {
UpdateCtx {
global_state: self.global_state,
widget_state: self.widget_state,
widget_state_children: self.widget_state_children.reborrow_mut(),
widget_children: self.widget_children.reborrow_mut(),
properties_children: self.properties_children.reborrow_mut(),
children: self.children.reborrow_mut(),
}
}
@ -300,28 +289,34 @@ impl<'w> QueryCtx<'w> {
/// Return a [`WidgetRef`] to a child widget.
pub fn get(self, child: WidgetId) -> WidgetRef<'w, dyn Widget> {
let child_state = self
.children
.widget_state_children
.into_item(child)
.expect("get: child not found");
let child_widget = self
.children
.widget_children
.into_item(child)
.expect("get: child not found");
let child_properties = self
.children
.properties_children
.into_item(child)
.expect("get: child not found");
let children = WidgetArenaRef {
widget_state_children: child_state.children,
widget_children: child_widget.children,
properties_children: child_properties.children,
};
let ctx = QueryCtx {
global_state: self.global_state,
widget_state_children: child_state.children,
widget_children: child_widget.children,
widget_state: child_state.item,
properties: PropertiesRef {
map: child_properties.item,
default_map: self.properties.default_map,
},
properties_children: child_properties.children,
children,
};
WidgetRef {
ctx,
@ -1100,14 +1095,17 @@ impl_context_method!(MutateCtx<'_>, EventCtx<'_>, UpdateCtx<'_>, {
// TODO - Send recursive event to child
let id = child.id();
let _ = self
.children
.widget_state_children
.remove(id)
.expect("remove_child: child not found");
let _ = self
.children
.widget_children
.remove(id)
.expect("remove_child: child not found");
let _ = self
.children
.properties_children
.remove(id)
.expect("remove_child: child not found");
@ -1300,9 +1298,11 @@ impl RegisterCtx<'_> {
let id = child.id();
let state = WidgetState::new(child.id(), widget.short_type_name(), options);
self.widget_children.insert(id, widget.as_box_dyn());
self.widget_state_children.insert(id, state);
self.properties_children.insert(id, properties.map);
self.children
.widget_children
.insert(id, widget.as_box_dyn());
self.children.widget_state_children.insert(id, state);
self.children.properties_children.insert(id, properties.map);
}
}
@ -1347,27 +1347,33 @@ macro_rules! impl_get_raw {
's: 'r,
{
let child_state_mut = self
.children
.widget_state_children
.item_mut(child.id())
.expect("get_raw_ref: child not found");
let child_mut = self
.children
.widget_children
.item_mut(child.id())
.expect("get_raw_ref: child not found");
let child_properties = self
.children
.properties_children
.item_mut(child.id())
.expect("get_raw_ref: child not found");
let children = WidgetArenaMut {
widget_state_children: child_state_mut.children,
widget_children: child_mut.children,
properties_children: child_properties.children,
};
#[allow(
clippy::needless_update,
reason = "May be needless in some macro invocations"
)]
let child_ctx = $SomeCtx {
widget_state: child_state_mut.item,
widget_state_children: child_state_mut.children,
widget_children: child_mut.children,
properties_children: child_properties.children,
global_state: self.global_state,
children,
..*self
};
RawWrapper {
@ -1388,27 +1394,33 @@ macro_rules! impl_get_raw {
's: 'r,
{
let child_state_mut = self
.children
.widget_state_children
.item_mut(child.id())
.expect("get_raw_mut: child not found");
let child_mut = self
.children
.widget_children
.item_mut(child.id())
.expect("get_raw_mut: child not found");
let child_properties = self
.children
.properties_children
.item_mut(child.id())
.expect("get_raw_mut: child not found");
let children = WidgetArenaMut {
widget_state_children: child_state_mut.children,
widget_children: child_mut.children,
properties_children: child_properties.children,
};
#[allow(
clippy::needless_update,
reason = "May be needless in some macro invocations"
)]
let child_ctx = $SomeCtx {
widget_state: child_state_mut.item,
widget_state_children: child_state_mut.children,
widget_children: child_mut.children,
properties_children: child_properties.children,
global_state: self.global_state,
children,
..*self
};
RawWrapperMut {
@ -1436,22 +1448,28 @@ impl<'s> AccessCtx<'s> {
's: 'r,
{
let child_state_mut = self
.children
.widget_state_children
.item_mut(child.id())
.expect("get_raw_ref: child not found");
let child_mut = self
.children
.widget_children
.item_mut(child.id())
.expect("get_raw_ref: child not found");
let child_properties = self
.children
.properties_children
.item_mut(child.id())
.expect("get_raw_ref: child not found");
let child_ctx = AccessCtx {
widget_state: child_state_mut.item,
let children = WidgetArenaMut {
widget_state_children: child_state_mut.children,
widget_children: child_mut.children,
properties_children: child_properties.children,
};
let child_ctx = AccessCtx {
widget_state: child_state_mut.item,
children,
global_state: self.global_state,
tree_update: self.tree_update,
rebuild_all: self.rebuild_all,

View File

@ -41,7 +41,7 @@ pub use ui_events::pointer::{
};
pub use ui_events::{ScrollDelta, keyboard, pointer};
pub(crate) use widget_arena::WidgetArena;
pub(crate) use widget_arena::{WidgetArena, WidgetArenaMut, WidgetArenaRef};
pub(crate) use widget_pod::CreateWidget;
pub(crate) use widget_state::WidgetState;

View File

@ -1,7 +1,7 @@
// Copyright 2024 the Xilem Authors
// SPDX-License-Identifier: Apache-2.0
use tree_arena::{ArenaMut, ArenaRef, TreeArena};
use tree_arena::{ArenaMut, ArenaMutList, ArenaRef, ArenaRefList, TreeArena};
use crate::core::{Widget, WidgetId, WidgetState};
use crate::util::AnyMap;
@ -12,6 +12,19 @@ pub(crate) struct WidgetArena {
pub(crate) properties: TreeArena<AnyMap>,
}
#[derive(Clone, Copy)]
pub(crate) struct WidgetArenaRef<'a> {
pub(crate) widget_state_children: ArenaRefList<'a, WidgetState>,
pub(crate) widget_children: ArenaRefList<'a, Box<dyn Widget>>,
pub(crate) properties_children: ArenaRefList<'a, AnyMap>,
}
pub(crate) struct WidgetArenaMut<'a> {
pub(crate) widget_state_children: ArenaMutList<'a, WidgetState>,
pub(crate) widget_children: ArenaMutList<'a, Box<dyn Widget>>,
pub(crate) properties_children: ArenaMutList<'a, AnyMap>,
}
impl WidgetArena {
pub(crate) fn has(&self, widget_id: WidgetId) -> bool {
self.widgets.find(widget_id).is_some()
@ -106,3 +119,22 @@ impl WidgetArena {
.expect("get_state_mut: widget state not in widget tree")
}
}
impl<'a> WidgetArenaMut<'a> {
#[allow(unused, reason = "May be used later")]
pub(crate) fn reborrow(&self) -> WidgetArenaRef<'_> {
WidgetArenaRef {
widget_state_children: self.widget_state_children.reborrow(),
widget_children: self.widget_children.reborrow(),
properties_children: self.properties_children.reborrow(),
}
}
pub(crate) fn reborrow_mut(&mut self) -> WidgetArenaMut<'_> {
WidgetArenaMut {
widget_state_children: self.widget_state_children.reborrow_mut(),
widget_children: self.widget_children.reborrow_mut(),
properties_children: self.properties_children.reborrow_mut(),
}
}
}

View File

@ -7,7 +7,7 @@ use std::ops::Deref;
use smallvec::SmallVec;
use vello::kurbo::Point;
use crate::core::{PropertiesRef, Property, QueryCtx, Widget, WidgetId};
use crate::core::{PropertiesRef, Property, QueryCtx, Widget, WidgetArenaRef, WidgetId};
/// A rich reference to a [`Widget`].
///
@ -117,42 +117,43 @@ impl<'w, W: Widget + ?Sized> WidgetRef<'w, W> {
.children_ids()
.iter()
.map(|&id| {
let Some(state_ref) = self.ctx.widget_state_children.into_item(id) else {
let Some(state_ref) = self.ctx.children.widget_state_children.into_item(id) else {
panic!(
"Error in '{}' #{parent_id}: child #{id} has not been added to tree",
self.widget.short_type_name()
);
};
let Some(widget_ref) = self.ctx.widget_children.into_item(id) else {
let Some(widget_ref) = self.ctx.children.widget_children.into_item(id) else {
panic!(
"Error in '{}' #{parent_id}: child #{id} has not been added to tree",
self.widget.short_type_name()
);
};
let Some(properties_ref) = self.ctx.properties_children.into_item(id) else {
let Some(properties_ref) = self.ctx.children.properties_children.into_item(id)
else {
panic!(
"Error in '{}' #{parent_id}: child #{id} has not been added to tree",
self.widget.short_type_name()
);
};
// Box<dyn Widget> -> &dyn Widget
// Without this step, the type of `WidgetRef::widget` would be
// `&Box<dyn Widget> as &dyn Widget`, which would be an additional layer
// of indirection.
let widget = widget_ref.item;
let widget: &dyn Widget = &**widget;
let children = WidgetArenaRef {
widget_children: widget_ref.children,
widget_state_children: state_ref.children,
properties_children: properties_ref.children,
};
let widget = &**widget_ref.item;
let state = state_ref.item;
let properties = properties_ref.item;
let ctx = QueryCtx {
global_state: self.ctx.global_state,
widget_state_children: state_ref.children,
widget_children: widget_ref.children,
widget_state: state_ref.item,
widget_state: state,
properties: PropertiesRef {
map: properties_ref.item,
map: properties,
default_map: self.ctx.properties.default_map,
},
properties_children: properties_ref.children,
children,
};
WidgetRef { ctx, widget }

View File

@ -7,7 +7,9 @@ use tree_arena::ArenaMut;
use vello::kurbo::Rect;
use crate::app::{RenderRoot, RenderRootState};
use crate::core::{AccessCtx, DefaultProperties, PropertiesRef, Widget, WidgetId, WidgetState};
use crate::core::{
AccessCtx, DefaultProperties, PropertiesRef, Widget, WidgetArenaMut, WidgetId, WidgetState,
};
use crate::passes::{enter_span_if, recurse_on_children};
use crate::util::AnyMap;
@ -16,43 +18,49 @@ fn build_accessibility_tree(
global_state: &mut RenderRootState,
default_properties: &DefaultProperties,
tree_update: &mut TreeUpdate,
mut widget: ArenaMut<'_, Box<dyn Widget>>,
widget: ArenaMut<'_, Box<dyn Widget>>,
mut state: ArenaMut<'_, WidgetState>,
mut properties: ArenaMut<'_, AnyMap>,
properties: ArenaMut<'_, AnyMap>,
rebuild_all: bool,
scale_factor: Option<f64>,
) {
let _span = enter_span_if(global_state.trace.access, state.reborrow());
let id = state.item.id;
let mut children = WidgetArenaMut {
widget_children: widget.children,
widget_state_children: state.children,
properties_children: properties.children,
};
let widget = &mut **widget.item;
let state = state.item;
let properties = properties.item;
if !rebuild_all && !state.item.needs_accessibility {
if !rebuild_all && !state.needs_accessibility {
return;
}
if (rebuild_all || state.item.request_accessibility) && !state.item.is_stashed {
if (rebuild_all || state.request_accessibility) && !state.is_stashed {
if global_state.trace.access {
trace!(
"Building accessibility node for widget '{}' {}",
widget.item.short_type_name(),
widget.short_type_name(),
id,
);
}
let mut ctx = AccessCtx {
global_state,
widget_state: state.item,
widget_state_children: state.children.reborrow_mut(),
widget_children: widget.children.reborrow_mut(),
properties_children: properties.children.reborrow_mut(),
widget_state: state,
children: children.reborrow_mut(),
tree_update,
rebuild_all,
};
let mut node = build_access_node(&mut **widget.item, &mut ctx, scale_factor);
let mut node = build_access_node(widget, &mut ctx, scale_factor);
let props = PropertiesRef {
map: properties.item,
default_map: default_properties.for_widget(widget.item.type_id()),
map: properties,
default_map: default_properties.for_widget(widget.type_id()),
};
widget.item.accessibility(&mut ctx, &props, &mut node);
widget.accessibility(&mut ctx, &props, &mut node);
let id: NodeId = ctx.widget_state.id.into();
if ctx.global_state.trace.access {
@ -61,30 +69,23 @@ fn build_accessibility_tree(
ctx.tree_update.nodes.push((id, node));
}
state.item.request_accessibility = false;
state.item.needs_accessibility = false;
state.request_accessibility = false;
state.needs_accessibility = false;
let id = state.item.id;
let parent_state = state.item;
recurse_on_children(
id,
widget.reborrow_mut(),
state.children,
properties.children,
|widget, mut state, properties| {
build_accessibility_tree(
global_state,
default_properties,
tree_update,
widget,
state.reborrow_mut(),
properties,
rebuild_all,
None,
);
parent_state.merge_up(state.item);
},
);
let parent_state = state;
recurse_on_children(id, widget, children, |widget, mut state, properties| {
build_accessibility_tree(
global_state,
default_properties,
tree_update,
widget,
state.reborrow_mut(),
properties,
rebuild_all,
None,
);
parent_state.merge_up(state.item);
});
}
// --- MARK: BUILD NODE
@ -105,7 +106,8 @@ fn build_access_node(
node.set_transform(accesskit::Affine::new(local_transform.as_coeffs()));
fn is_child_stashed(ctx: &mut AccessCtx<'_>, id: WidgetId) -> bool {
ctx.widget_state_children
ctx.children
.widget_state_children
.find(id)
.expect("is_child_stashed: child not found")
.item

View File

@ -5,7 +5,9 @@ use tracing::info_span;
use tree_arena::ArenaMut;
use crate::app::{RenderRoot, RenderRootState};
use crate::core::{DefaultProperties, PropertiesMut, UpdateCtx, Widget, WidgetState};
use crate::core::{
DefaultProperties, PropertiesMut, UpdateCtx, Widget, WidgetArenaMut, WidgetState,
};
use crate::passes::{enter_span_if, recurse_on_children};
use crate::util::AnyMap;
@ -13,55 +15,56 @@ use crate::util::AnyMap;
fn update_anim_for_widget(
global_state: &mut RenderRootState,
default_properties: &DefaultProperties,
mut widget: ArenaMut<'_, Box<dyn Widget>>,
widget: ArenaMut<'_, Box<dyn Widget>>,
mut state: ArenaMut<'_, WidgetState>,
mut properties: ArenaMut<'_, AnyMap>,
properties: ArenaMut<'_, AnyMap>,
elapsed_ns: u64,
) {
let _span = enter_span_if(global_state.trace.anim, state.reborrow());
if !state.item.needs_anim {
let mut children = WidgetArenaMut {
widget_children: widget.children,
widget_state_children: state.children,
properties_children: properties.children,
};
let widget = &mut **widget.item;
let state = state.item;
let properties = properties.item;
if !state.needs_anim {
return;
}
state.item.needs_anim = false;
state.needs_anim = false;
// Most passes reset their `needs` and `request` flags after the call to
// the widget method, but it's valid and expected for `request_anim` to be
// set in response to `AnimFrame`.
if state.item.request_anim {
state.item.request_anim = false;
if state.request_anim {
state.request_anim = false;
let mut ctx = UpdateCtx {
global_state,
widget_state: state.item,
widget_state_children: state.children.reborrow_mut(),
widget_children: widget.children.reborrow_mut(),
properties_children: properties.children.reborrow_mut(),
widget_state: state,
children: children.reborrow_mut(),
};
let mut props = PropertiesMut {
map: properties.item,
default_map: default_properties.for_widget(widget.item.type_id()),
map: properties,
default_map: default_properties.for_widget(widget.type_id()),
};
widget.item.on_anim_frame(&mut ctx, &mut props, elapsed_ns);
widget.on_anim_frame(&mut ctx, &mut props, elapsed_ns);
}
let id = state.item.id;
let parent_state = state.item;
recurse_on_children(
id,
widget.reborrow_mut(),
state.children,
properties.children,
|widget, mut state, properties| {
update_anim_for_widget(
global_state,
default_properties,
widget,
state.reborrow_mut(),
properties,
elapsed_ns,
);
parent_state.merge_up(state.item);
},
);
let id = state.id;
let parent_state = state;
recurse_on_children(id, widget, children, |widget, mut state, properties| {
update_anim_for_widget(
global_state,
default_properties,
widget,
state.reborrow_mut(),
properties,
elapsed_ns,
);
parent_state.merge_up(state.item);
});
}
// TODO - switch anim frames to being about age / an absolute timestamp

View File

@ -6,81 +6,81 @@ use tree_arena::ArenaMut;
use vello::kurbo::Affine;
use crate::app::{RenderRoot, RenderRootState};
use crate::core::{ComposeCtx, Widget, WidgetState};
use crate::core::{ComposeCtx, Widget, WidgetArenaMut, WidgetState};
use crate::passes::{enter_span_if, recurse_on_children};
use crate::util::AnyMap;
// --- MARK: RECURSE
fn compose_widget(
global_state: &mut RenderRootState,
mut widget: ArenaMut<'_, Box<dyn Widget>>,
widget: ArenaMut<'_, Box<dyn Widget>>,
mut state: ArenaMut<'_, WidgetState>,
properties: ArenaMut<'_, AnyMap>,
parent_transformed: bool,
parent_window_transform: Affine,
) {
let _span = enter_span_if(global_state.trace.compose, state.reborrow());
let mut children = WidgetArenaMut {
widget_children: widget.children,
widget_state_children: state.children,
properties_children: properties.children,
};
let widget = &mut **widget.item;
let state = state.item;
let transformed = parent_transformed || state.item.transform_changed;
let transformed = parent_transformed || state.transform_changed;
if !transformed && !state.item.needs_compose {
if !transformed && !state.needs_compose {
return;
}
// the translation needs to be applied *after* applying the transform, as translation by scrolling should be within the transformed coordinate space. Same is true for the (layout) origin, to behave similar as in CSS.
let local_translation = state.item.scroll_translation + state.item.origin.to_vec2();
let local_translation = state.scroll_translation + state.origin.to_vec2();
state.item.window_transform =
parent_window_transform * state.item.transform.then_translate(local_translation);
state.window_transform =
parent_window_transform * state.transform.then_translate(local_translation);
let local_rect = state.item.size.to_rect() + state.item.paint_insets;
state.item.bounding_rect = state.item.window_transform.transform_rect_bbox(local_rect);
let local_rect = state.size.to_rect() + state.paint_insets;
state.bounding_rect = state.window_transform.transform_rect_bbox(local_rect);
let mut ctx = ComposeCtx {
global_state,
widget_state: state.item,
widget_state_children: state.children.reborrow_mut(),
widget_children: widget.children.reborrow_mut(),
widget_state: state,
children: children.reborrow_mut(),
};
if ctx.widget_state.request_compose {
widget.item.compose(&mut ctx);
widget.compose(&mut ctx);
}
// We need to update the accessibility node's coordinates and repaint it at the new position.
state.item.request_accessibility = true;
state.item.needs_accessibility = true;
state.item.needs_paint = true;
state.request_accessibility = true;
state.needs_accessibility = true;
state.needs_paint = true;
state.item.needs_compose = false;
state.item.request_compose = false;
state.item.transform_changed = false;
state.needs_compose = false;
state.request_compose = false;
state.transform_changed = false;
let id = state.item.id;
let parent_transform = state.item.window_transform;
let parent_state = state.item;
recurse_on_children(
id,
widget.reborrow_mut(),
state.children,
properties.children,
|widget, mut state, properties| {
compose_widget(
global_state,
widget,
state.reborrow_mut(),
properties,
transformed,
parent_transform,
);
let parent_bounding_rect = parent_state.bounding_rect;
let id = state.id;
let parent_transform = state.window_transform;
let parent_state = state;
recurse_on_children(id, widget, children, |widget, mut state, properties| {
compose_widget(
global_state,
widget,
state.reborrow_mut(),
properties,
transformed,
parent_transform,
);
let parent_bounding_rect = parent_state.bounding_rect;
if let Some(child_bounding_rect) = parent_state.clip_child(state.item.bounding_rect) {
parent_state.bounding_rect = parent_bounding_rect.union(child_bounding_rect);
}
if let Some(child_bounding_rect) = parent_state.clip_child(state.item.bounding_rect) {
parent_state.bounding_rect = parent_bounding_rect.union(child_bounding_rect);
}
parent_state.merge_up(state.item);
},
);
parent_state.merge_up(state.item);
});
}
// --- MARK: ROOT

View File

@ -8,7 +8,7 @@ use crate::app::{RenderRoot, RenderRootSignal};
use crate::core::keyboard::{Key, KeyState, NamedKey};
use crate::core::{
AccessEvent, EventCtx, Handled, PointerEvent, PointerInfo, PointerUpdate, PropertiesMut,
TextEvent, Widget, WidgetId,
TextEvent, Widget, WidgetArenaMut, WidgetId,
};
use crate::debug_panic;
use crate::dpi::{LogicalPosition, PhysicalPosition};
@ -94,12 +94,15 @@ fn run_event_pass<E>(
if !is_handled {
let _span = enter_span(state_mut.reborrow());
let children = WidgetArenaMut {
widget_children: widget_mut.children,
widget_state_children: state_mut.children,
properties_children: properties_mut.children.reborrow_mut(),
};
let mut ctx = EventCtx {
global_state: &mut root.global_state,
widget_state: state_mut.item,
widget_state_children: state_mut.children,
widget_children: widget_mut.children,
properties_children: properties_mut.children.reborrow_mut(),
children,
target: original_target.unwrap(),
allow_pointer_capture,
is_handled: false,

View File

@ -12,7 +12,9 @@ use tree_arena::ArenaMut;
use vello::kurbo::{Point, Rect, Size};
use crate::app::{RenderRoot, RenderRootSignal, WindowSizePolicy};
use crate::core::{BoxConstraints, LayoutCtx, PropertiesMut, Widget, WidgetPod, WidgetState};
use crate::core::{
BoxConstraints, LayoutCtx, PropertiesMut, Widget, WidgetArenaMut, WidgetPod, WidgetState,
};
use crate::debug_panic;
use crate::passes::{enter_span_if, recurse_on_children};
use crate::util::AnyMap;
@ -26,21 +28,42 @@ pub(crate) fn run_layout_on<W: Widget + ?Sized>(
bc: &BoxConstraints,
) -> Size {
let id = pod.id();
let mut widget = parent_ctx.widget_children.item_mut(id).unwrap();
let mut state = parent_ctx.widget_state_children.item_mut(id).unwrap();
let mut properties = parent_ctx.properties_children.item_mut(id).unwrap();
let widget = parent_ctx.children.widget_children.item_mut(id).unwrap();
let mut state = parent_ctx
.children
.widget_state_children
.item_mut(id)
.unwrap();
let properties = parent_ctx
.children
.properties_children
.item_mut(id)
.unwrap();
let trace = parent_ctx.global_state.trace.layout;
let _span = enter_span_if(trace, state.reborrow());
let mut children = WidgetArenaMut {
widget_children: widget.children,
widget_state_children: state.children,
properties_children: properties.children,
};
let widget = &mut **widget.item;
let state = state.item;
let properties = properties.item;
let mut children_ids = SmallVec::new();
if cfg!(debug_assertions) {
children_ids = widget.item.children_ids();
children_ids = widget.children_ids();
// We forcefully set request_layout to true for all children.
// This is used below to check that widget.layout(..) visited all of them.
for child_id in widget.item.children_ids() {
let child_state = state.children.item_mut(child_id).unwrap().item;
for child_id in widget.children_ids() {
let child_state = children
.widget_state_children
.item_mut(child_id)
.unwrap()
.item;
if !child_state.is_stashed {
child_state.request_layout = true;
}
@ -53,38 +76,37 @@ pub(crate) fn run_layout_on<W: Widget + ?Sized>(
// Note that, because this check exits before recursing, run_layout can only ever be
// reached for a widget whose parent is not stashed, which means is_explicitly_stashed
// being false is sufficient to know the widget is non-stashed.
if state.item.is_explicitly_stashed {
if state.is_explicitly_stashed {
debug_panic!(
"Error in '{}' {}: trying to compute layout of stashed widget.",
widget.item.short_type_name(),
widget.short_type_name(),
pod.id(),
);
state.item.size = Size::ZERO;
state.size = Size::ZERO;
return Size::ZERO;
}
// TODO - Not everything that has been re-laid out needs to be repainted.
state.item.needs_paint = true;
state.item.needs_compose = true;
state.item.needs_accessibility = true;
state.item.request_paint = true;
state.item.request_compose = true;
state.item.request_accessibility = true;
state.needs_paint = true;
state.needs_compose = true;
state.needs_accessibility = true;
state.request_paint = true;
state.request_compose = true;
state.request_accessibility = true;
bc.debug_check(widget.item.short_type_name());
bc.debug_check(widget.short_type_name());
if trace {
trace!("Computing layout with constraints {:?}", bc);
}
state.item.local_paint_rect = Rect::ZERO;
state.local_paint_rect = Rect::ZERO;
// If children are stashed, the layout pass will not recurse over them.
// We reset need_layout and request_layout to false directly instead.
recurse_on_children(
pod.id(),
widget.reborrow_mut(),
state.children.reborrow_mut(),
properties.children.reborrow_mut(),
widget,
children.reborrow_mut(),
|widget, state, properties| {
if state.item.is_stashed {
clear_layout_flags(widget, state, properties);
@ -94,10 +116,8 @@ pub(crate) fn run_layout_on<W: Widget + ?Sized>(
let new_size = {
let mut inner_ctx = LayoutCtx {
widget_state: state.item,
widget_state_children: state.children.reborrow_mut(),
widget_children: widget.children,
properties_children: properties.children.reborrow_mut(),
widget_state: state,
children: children.reborrow_mut(),
default_properties: parent_ctx.default_properties,
global_state: parent_ctx.global_state,
};
@ -106,40 +126,41 @@ pub(crate) fn run_layout_on<W: Widget + ?Sized>(
// skip calling layout
inner_ctx.widget_state.request_layout = false;
let mut props = PropertiesMut {
map: properties.item,
default_map: parent_ctx
.default_properties
.for_widget(widget.item.type_id()),
map: properties,
default_map: parent_ctx.default_properties.for_widget(widget.type_id()),
};
widget.item.layout(&mut inner_ctx, &mut props, bc)
widget.layout(&mut inner_ctx, &mut props, bc)
};
if state.item.request_layout {
if state.request_layout {
debug_panic!(
"Error in '{}' {}: layout request flag was set during layout pass",
widget.item.short_type_name(),
widget.short_type_name(),
pod.id(),
);
}
if trace {
trace!(
"Computed layout: size={}, baseline={}, insets={:?}",
new_size, state.item.baseline_offset, state.item.paint_insets,
new_size, state.baseline_offset, state.paint_insets,
);
}
state.item.needs_layout = false;
state.item.is_expecting_place_child_call = true;
state.needs_layout = false;
state.is_expecting_place_child_call = true;
state.item.local_paint_rect = state
.item
state.local_paint_rect = state
.local_paint_rect
.union(new_size.to_rect() + state.item.paint_insets);
.union(new_size.to_rect() + state.paint_insets);
#[cfg(debug_assertions)]
{
let name = widget.item.short_type_name();
for child_id in widget.item.children_ids() {
let child_state = state.children.item_mut(child_id).unwrap().item;
let name = widget.short_type_name();
for child_id in widget.children_ids() {
let child_state = children
.widget_state_children
.item_mut(child_id)
.unwrap()
.item;
if child_state.is_stashed {
continue;
@ -166,8 +187,8 @@ pub(crate) fn run_layout_on<W: Widget + ?Sized>(
}
}
let new_children_ids = widget.item.children_ids();
if children_ids != new_children_ids && !state.item.children_changed {
let new_children_ids = widget.children_ids();
if children_ids != new_children_ids && !state.children_changed {
debug_panic!(
"Error in '{}' {}: children changed during layout pass",
name,
@ -185,7 +206,11 @@ pub(crate) fn run_layout_on<W: Widget + ?Sized>(
}
}
let state_mut = parent_ctx.widget_state_children.item_mut(id).unwrap();
let state_mut = parent_ctx
.children
.widget_state_children
.item_mut(id)
.unwrap();
parent_ctx.widget_state.merge_up(state_mut.item);
state_mut.item.size = new_size;
new_size
@ -195,23 +220,26 @@ pub(crate) fn run_layout_on<W: Widget + ?Sized>(
// This function is called on stashed widgets and their children
// to set all layout flags to false.
fn clear_layout_flags(
mut widget: ArenaMut<'_, Box<dyn Widget>>,
widget: ArenaMut<'_, Box<dyn Widget>>,
state: ArenaMut<'_, WidgetState>,
properties: ArenaMut<'_, AnyMap>,
) {
state.item.needs_layout = false;
state.item.request_layout = false;
let children = WidgetArenaMut {
widget_children: widget.children,
widget_state_children: state.children,
properties_children: properties.children,
};
let id = state.item.id;
recurse_on_children(
id,
widget.reborrow_mut(),
state.children,
properties.children,
|widget, state, properties| {
clear_layout_flags(widget, state, properties);
},
);
let widget = &mut **widget.item;
let state = state.item;
state.needs_layout = false;
state.request_layout = false;
let id = state.id;
recurse_on_children(id, widget, children, |widget, state, properties| {
clear_layout_flags(widget, state, properties);
});
}
// --- MARK: ROOT
@ -238,9 +266,11 @@ pub(crate) fn run_layout_pass(root: &mut RenderRoot) {
let mut ctx = LayoutCtx {
global_state: &mut root.global_state,
widget_state: &mut dummy_state,
widget_state_children: root_state_token,
widget_children: root_widget_token,
properties_children: root_properties_token,
children: WidgetArenaMut {
widget_state_children: root_state_token,
widget_children: root_widget_token,
properties_children: root_properties_token,
},
default_properties: &root.default_properties,
};

View File

@ -8,9 +8,9 @@
//! This file includes utility functions used by multiple passes.
use tracing::span::EnteredSpan;
use tree_arena::{ArenaMut, ArenaMutList, ArenaRef};
use tree_arena::{ArenaMut, ArenaRef};
use crate::core::{Widget, WidgetArena, WidgetId, WidgetState};
use crate::core::{Widget, WidgetArena, WidgetArenaMut, WidgetId, WidgetState};
use crate::util::AnyMap;
pub(crate) mod accessibility;
@ -37,30 +37,29 @@ pub(crate) fn enter_span(state: ArenaRef<'_, WidgetState>) -> EnteredSpan {
pub(crate) fn recurse_on_children(
id: WidgetId,
mut widget: ArenaMut<'_, Box<dyn Widget>>,
mut state: ArenaMutList<'_, WidgetState>,
mut properties: ArenaMutList<'_, AnyMap>,
widget: &mut dyn Widget,
mut children: WidgetArenaMut<'_>,
mut callback: impl FnMut(
ArenaMut<'_, Box<dyn Widget>>,
ArenaMut<'_, WidgetState>,
ArenaMut<'_, AnyMap>,
),
) {
let parent_name = widget.item.short_type_name();
let parent_name = widget.short_type_name();
let parent_id = id;
for child_id in widget.item.children_ids() {
let widget = widget.children.item_mut(child_id).unwrap_or_else(|| {
for child_id in widget.children_ids() {
let widget = children.widget_children.item_mut(child_id).unwrap_or_else(|| {
panic!(
"Error in '{parent_name}' {parent_id}: cannot find child {child_id} returned by children_ids()"
)
});
let state = state.item_mut(child_id).unwrap_or_else(|| {
let state = children.widget_state_children.item_mut(child_id).unwrap_or_else(|| {
panic!(
"Error in '{parent_name}' {parent_id}: cannot find child {child_id} returned by children_ids()"
)
});
let properties = properties.item_mut(child_id).unwrap_or_else(|| {
let properties = children.properties_children.item_mut(child_id).unwrap_or_else(|| {
panic!(
"Error in '{parent_name}' {parent_id}: cannot find child {child_id} returned by children_ids()"
)

View File

@ -4,7 +4,7 @@
use tracing::info_span;
use crate::app::RenderRoot;
use crate::core::{MutateCtx, PropertiesMut, Widget, WidgetId, WidgetMut};
use crate::core::{MutateCtx, PropertiesMut, Widget, WidgetArenaMut, WidgetId, WidgetMut};
use crate::passes::merge_state_up;
pub(crate) fn mutate_widget<R>(
@ -13,26 +13,31 @@ pub(crate) fn mutate_widget<R>(
mutate_fn: impl FnOnce(WidgetMut<'_, dyn Widget>) -> R,
) -> R {
let (widget_mut, state_mut, properties_mut) = root.widget_arena.get_all_mut(id);
let children = WidgetArenaMut {
widget_children: widget_mut.children,
widget_state_children: state_mut.children,
properties_children: properties_mut.children,
};
let widget = &mut **widget_mut.item;
let state = state_mut.item;
let properties = properties_mut.item;
let _span = info_span!("mutate_widget", name = widget.short_type_name()).entered();
let _span = info_span!("mutate_widget", name = widget_mut.item.short_type_name()).entered();
// NOTE - we can set parent_widget_state to None here, because the loop below will merge the
// states up to the root.
let root_widget = WidgetMut {
ctx: MutateCtx {
global_state: &mut root.global_state,
parent_widget_state: None,
widget_state: state_mut.item,
widget_state_children: state_mut.children,
widget_children: widget_mut.children,
widget_state: state,
properties: PropertiesMut {
map: properties_mut.item,
default_map: root
.default_properties
.for_widget(widget_mut.item.type_id()),
map: properties,
default_map: root.default_properties.for_widget(widget.type_id()),
},
properties_children: properties_mut.children,
children,
},
widget: &mut **widget_mut.item,
widget,
};
let result = mutate_fn(root_widget);

View File

@ -10,7 +10,9 @@ use vello::kurbo::{Affine, Rect};
use vello::peniko::{Color, Fill, Mix};
use crate::app::{RenderRoot, RenderRootState};
use crate::core::{DefaultProperties, PaintCtx, PropertiesRef, Widget, WidgetId, WidgetState};
use crate::core::{
DefaultProperties, PaintCtx, PropertiesRef, Widget, WidgetArenaMut, WidgetId, WidgetState,
};
use crate::passes::{enter_span_if, recurse_on_children};
use crate::util::{AnyMap, get_debug_color, stroke};
@ -20,7 +22,7 @@ fn paint_widget(
default_properties: &DefaultProperties,
complete_scene: &mut Scene,
scenes: &mut HashMap<WidgetId, Scene>,
mut widget: ArenaMut<'_, Box<dyn Widget>>,
widget: ArenaMut<'_, Box<dyn Widget>>,
mut state: ArenaMut<'_, WidgetState>,
properties: ArenaMut<'_, AnyMap>,
debug_paint: bool,
@ -35,18 +37,26 @@ fn paint_widget(
// but we deliberately avoid doing that to avoid creating zombie flags.
// (See WidgetState doc.)
let mut children = WidgetArenaMut {
widget_children: widget.children,
widget_state_children: state.children,
properties_children: properties.children,
};
let widget = &mut **widget.item;
let state = state.item;
let properties = properties.item;
// TODO - Handle damage regions
// https://github.com/linebender/xilem/issues/789
let mut ctx = PaintCtx {
global_state,
widget_state: state.item,
widget_state_children: state.children.reborrow_mut(),
widget_children: widget.children.reborrow_mut(),
widget_state: state,
children: children.reborrow_mut(),
debug_paint,
};
if ctx.widget_state.request_paint && !is_stashed {
if trace {
trace!("Painting widget '{}' {}", widget.item.short_type_name(), id);
trace!("Painting widget '{}' {}", widget.short_type_name(), id);
}
// TODO - Reserve scene
@ -54,54 +64,48 @@ fn paint_widget(
let scene = scenes.entry(id).or_default();
scene.reset();
let props = PropertiesRef {
map: properties.item,
default_map: default_properties.for_widget(widget.item.type_id()),
map: properties,
default_map: default_properties.for_widget(widget.type_id()),
};
widget.item.paint(&mut ctx, &props, scene);
widget.paint(&mut ctx, &props, scene);
}
state.item.request_paint = false;
state.item.needs_paint = false;
state.request_paint = false;
state.needs_paint = false;
let has_clip = state.item.clip_path.is_some();
let has_clip = state.clip_path.is_some();
if !is_stashed {
let transform = state.item.window_transform;
let transform = state.window_transform;
let scene = scenes.get(&id).unwrap();
if let Some(clip) = state.item.clip_path {
if let Some(clip) = state.clip_path {
complete_scene.push_layer(Mix::Clip, 1., transform, &clip);
}
complete_scene.append(scene, Some(transform));
}
let id = state.item.id;
let bounding_rect = state.item.bounding_rect;
let parent_state = state.item;
recurse_on_children(
id,
widget.reborrow_mut(),
state.children,
properties.children,
|widget, mut state, properties| {
// TODO: We could skip painting children outside the parent clip path.
// There's a few things to consider if we do:
// - Some widgets can paint outside of their layout box.
// - Once we implement compositor layers, we may want to paint outside of the clip path anyway in anticipation of user scrolling.
// - We still want to reset needs_paint and request_paint flags.
paint_widget(
global_state,
default_properties,
complete_scene,
scenes,
widget,
state.reborrow_mut(),
properties,
debug_paint,
);
parent_state.merge_up(state.item);
},
);
let bounding_rect = state.bounding_rect;
let parent_state = state;
recurse_on_children(id, widget, children, |widget, mut state, properties| {
// TODO: We could skip painting children outside the parent clip path.
// There's a few things to consider if we do:
// - Some widgets can paint outside of their layout box.
// - Once we implement compositor layers, we may want to paint outside of the clip path anyway in anticipation of user scrolling.
// - We still want to reset needs_paint and request_paint flags.
paint_widget(
global_state,
default_properties,
complete_scene,
scenes,
widget,
state.reborrow_mut(),
properties,
debug_paint,
);
parent_state.merge_up(state.item);
});
if !is_stashed {
// draw the global axis aligned bounding rect of the widget

View File

@ -11,7 +11,8 @@ use ui_events::pointer::PointerType;
use crate::app::{RenderRoot, RenderRootSignal, RenderRootState};
use crate::core::{
DefaultProperties, Ime, PointerEvent, PointerInfo, PropertiesMut, PropertiesRef, QueryCtx,
RegisterCtx, TextEvent, Update, UpdateCtx, Widget, WidgetId, WidgetState,
RegisterCtx, TextEvent, Update, UpdateCtx, Widget, WidgetArenaMut, WidgetArenaRef, WidgetId,
WidgetState,
};
use crate::passes::event::{run_on_pointer_event_pass, run_on_text_event_pass};
use crate::passes::{enter_span, enter_span_if, merge_state_up, recurse_on_children};
@ -53,16 +54,22 @@ fn run_targeted_update_pass(
let parent_id = root.widget_arena.parent_of(widget_id);
let (widget_mut, state_mut, properties_mut) = root.widget_arena.get_all_mut(widget_id);
let widget = &mut **widget_mut.item;
let mut ctx = UpdateCtx {
global_state: &mut root.global_state,
widget_state: state_mut.item,
widget_state_children: state_mut.children,
let children = WidgetArenaMut {
widget_children: widget_mut.children,
widget_state_children: state_mut.children,
properties_children: properties_mut.children,
};
let widget = &mut **widget_mut.item;
let state = state_mut.item;
let properties = properties_mut.item;
let mut ctx = UpdateCtx {
global_state: &mut root.global_state,
widget_state: state,
children,
};
let mut props = PropertiesMut {
map: properties_mut.item,
map: properties,
default_map: root.default_properties.for_widget(widget.type_id()),
};
pass_fn(widget, &mut ctx, &mut props);
@ -86,20 +93,25 @@ fn run_single_update_pass(
let (widget_mut, state_mut, properties_mut) = root.widget_arena.get_all_mut(target);
let mut ctx = UpdateCtx {
global_state: &mut root.global_state,
widget_state: state_mut.item,
widget_state_children: state_mut.children,
let children = WidgetArenaMut {
widget_children: widget_mut.children,
widget_state_children: state_mut.children,
properties_children: properties_mut.children,
};
let mut props = PropertiesMut {
map: properties_mut.item,
default_map: root
.default_properties
.for_widget(widget_mut.item.type_id()),
let widget = &mut **widget_mut.item;
let state = state_mut.item;
let properties = properties_mut.item;
let mut ctx = UpdateCtx {
global_state: &mut root.global_state,
widget_state: state,
children,
};
pass_fn(&mut **widget_mut.item, &mut ctx, &mut props);
let mut props = PropertiesMut {
map: properties,
default_map: root.default_properties.for_widget(widget.type_id()),
};
pass_fn(widget, &mut ctx, &mut props);
let mut current_id = Some(target);
while let Some(widget_id) = current_id {
@ -112,41 +124,48 @@ fn run_single_update_pass(
fn update_widget_tree(
global_state: &mut RenderRootState,
default_properties: &DefaultProperties,
mut widget: ArenaMut<'_, Box<dyn Widget>>,
widget: ArenaMut<'_, Box<dyn Widget>>,
mut state: ArenaMut<'_, WidgetState>,
mut properties: ArenaMut<'_, AnyMap>,
properties: ArenaMut<'_, AnyMap>,
) {
let trace = global_state.trace.update_tree;
let _span = enter_span_if(trace, state.reborrow());
let id = state.item.id;
if !state.item.children_changed {
let mut children = WidgetArenaMut {
widget_children: widget.children,
widget_state_children: state.children,
properties_children: properties.children,
};
let widget = &mut **widget.item;
let state = state.item;
let properties = properties.item;
if !state.children_changed {
return;
}
state.item.children_changed = false;
state.children_changed = false;
{
let mut ctx = RegisterCtx {
widget_state_children: state.children.reborrow_mut(),
widget_children: widget.children.reborrow_mut(),
properties_children: properties.children.reborrow_mut(),
children: children.reborrow_mut(),
#[cfg(debug_assertions)]
registered_ids: Vec::new(),
};
// The widget will call `RegisterCtx::register_child` on all its children,
// which will add the new widgets to the arena.
widget.item.register_children(&mut ctx);
widget.register_children(&mut ctx);
#[cfg(debug_assertions)]
{
let children_ids = widget.item.children_ids();
let children_ids = widget.children_ids();
for child_id in ctx.registered_ids {
if !children_ids.contains(&child_id) {
panic!(
"Error in '{}' {}: method register_children() called \
RegisterCtx::register_child() on child {}, which isn't \
in the list returned by children_ids()",
widget.item.short_type_name(),
widget.short_type_name(),
id,
child_id
);
@ -155,12 +174,12 @@ fn update_widget_tree(
}
#[cfg(debug_assertions)]
for child_id in widget.item.children_ids() {
if widget.children.item(child_id).is_none() {
for child_id in widget.children_ids() {
if !children.widget_children.has(child_id) {
panic!(
"Error in '{}' {}: method register_children() did not call \
RegisterCtx::register_child() on child {} returned by children_ids()",
widget.item.short_type_name(),
widget.short_type_name(),
id,
child_id
);
@ -168,53 +187,40 @@ fn update_widget_tree(
}
}
if state.item.is_new {
if state.is_new {
let mut ctx = UpdateCtx {
global_state,
widget_state: state.item,
widget_state_children: state.children.reborrow_mut(),
widget_children: widget.children.reborrow_mut(),
properties_children: properties.children.reborrow_mut(),
widget_state: state,
children: children.reborrow_mut(),
};
let mut props = PropertiesMut {
map: properties.item,
default_map: default_properties.for_widget(widget.item.type_id()),
map: properties,
default_map: default_properties.for_widget(widget.type_id()),
};
widget
.item
.update(&mut ctx, &mut props, &Update::WidgetAdded);
widget.update(&mut ctx, &mut props, &Update::WidgetAdded);
if trace {
trace!(
"{} received Update::WidgetAdded",
widget.item.short_type_name()
);
trace!("{} received Update::WidgetAdded", widget.short_type_name());
}
state.item.accepts_pointer_interaction = widget.item.accepts_pointer_interaction();
state.item.accepts_focus = widget.item.accepts_focus();
state.item.accepts_text_input = widget.item.accepts_text_input();
state.item.trace_span = widget.item.make_trace_span(state.item.id);
state.item.is_new = false;
state.accepts_pointer_interaction = widget.accepts_pointer_interaction();
state.accepts_focus = widget.accepts_focus();
state.accepts_text_input = widget.accepts_text_input();
state.trace_span = widget.make_trace_span(state.id);
state.is_new = false;
}
// We can recurse on this widget's children, because they have already been added
// to the arena above.
let parent_state = state.item;
recurse_on_children(
id,
widget.reborrow_mut(),
state.children,
properties.children,
|widget, mut state, properties| {
update_widget_tree(
global_state,
default_properties,
widget,
state.reborrow_mut(),
properties,
);
parent_state.merge_up(state.item);
},
);
let parent_state = state;
recurse_on_children(id, widget, children, |widget, mut state, properties| {
update_widget_tree(
global_state,
default_properties,
widget,
state.reborrow_mut(),
properties,
);
parent_state.merge_up(state.item);
});
}
/// See the [passes documentation](../doc/05_pass_system.md#update-tree-pass).
@ -223,9 +229,11 @@ pub(crate) fn run_update_widget_tree_pass(root: &mut RenderRoot) {
if root.root.incomplete() {
let mut ctx = RegisterCtx {
widget_state_children: root.widget_arena.states.roots_mut(),
widget_children: root.widget_arena.widgets.roots_mut(),
properties_children: root.widget_arena.properties.roots_mut(),
children: WidgetArenaMut {
widget_state_children: root.widget_arena.states.roots_mut(),
widget_children: root.widget_arena.widgets.roots_mut(),
properties_children: root.widget_arena.properties.roots_mut(),
},
#[cfg(debug_assertions)]
registered_ids: Vec::new(),
};
@ -251,60 +259,59 @@ pub(crate) fn run_update_widget_tree_pass(root: &mut RenderRoot) {
fn update_disabled_for_widget(
global_state: &mut RenderRootState,
default_properties: &DefaultProperties,
mut widget: ArenaMut<'_, Box<dyn Widget>>,
widget: ArenaMut<'_, Box<dyn Widget>>,
mut state: ArenaMut<'_, WidgetState>,
mut properties: ArenaMut<'_, AnyMap>,
properties: ArenaMut<'_, AnyMap>,
parent_disabled: bool,
) {
let _span = enter_span(state.reborrow());
let id = state.item.id;
let disabled = state.item.is_explicitly_disabled || parent_disabled;
if !state.item.needs_update_disabled && disabled == state.item.is_disabled {
let mut children = WidgetArenaMut {
widget_children: widget.children,
widget_state_children: state.children,
properties_children: properties.children,
};
let widget = &mut **widget.item;
let state = state.item;
let properties = properties.item;
let disabled = state.is_explicitly_disabled || parent_disabled;
if !state.needs_update_disabled && disabled == state.is_disabled {
return;
}
if disabled != state.item.is_disabled {
if disabled != state.is_disabled {
let mut ctx = UpdateCtx {
global_state,
widget_state: state.item,
widget_state_children: state.children.reborrow_mut(),
widget_children: widget.children.reborrow_mut(),
properties_children: properties.children.reborrow_mut(),
widget_state: state,
children: children.reborrow_mut(),
};
let mut props = PropertiesMut {
map: properties.item,
default_map: default_properties.for_widget(widget.item.type_id()),
map: properties,
default_map: default_properties.for_widget(widget.type_id()),
};
widget
.item
.update(&mut ctx, &mut props, &Update::DisabledChanged(disabled));
state.item.is_disabled = disabled;
state.item.needs_update_focus_chain = true;
state.item.request_accessibility = true;
state.item.needs_accessibility = true;
widget.update(&mut ctx, &mut props, &Update::DisabledChanged(disabled));
state.is_disabled = disabled;
state.needs_update_focus_chain = true;
state.request_accessibility = true;
state.needs_accessibility = true;
}
state.item.needs_update_disabled = false;
state.needs_update_disabled = false;
let parent_state = state.item;
recurse_on_children(
id,
widget.reborrow_mut(),
state.children,
properties.children,
|widget, mut state, properties| {
update_disabled_for_widget(
global_state,
default_properties,
widget,
state.reborrow_mut(),
properties,
disabled,
);
parent_state.merge_up(state.item);
},
);
let parent_state = state;
recurse_on_children(id, widget, children, |widget, mut state, properties| {
update_disabled_for_widget(
global_state,
default_properties,
widget,
state.reborrow_mut(),
properties,
disabled,
);
parent_state.merge_up(state.item);
});
}
pub(crate) fn run_update_disabled_pass(root: &mut RenderRoot) {
@ -337,69 +344,68 @@ pub(crate) fn run_update_disabled_pass(root: &mut RenderRoot) {
fn update_stashed_for_widget(
global_state: &mut RenderRootState,
default_properties: &DefaultProperties,
mut widget: ArenaMut<'_, Box<dyn Widget>>,
widget: ArenaMut<'_, Box<dyn Widget>>,
mut state: ArenaMut<'_, WidgetState>,
mut properties: ArenaMut<'_, AnyMap>,
properties: ArenaMut<'_, AnyMap>,
parent_stashed: bool,
) {
let _span = enter_span(state.reborrow());
let id = state.item.id;
let stashed = state.item.is_explicitly_stashed || parent_stashed;
if !state.item.needs_update_stashed && stashed == state.item.is_stashed {
let mut children = WidgetArenaMut {
widget_children: widget.children,
widget_state_children: state.children,
properties_children: properties.children,
};
let widget = &mut **widget.item;
let state = state.item;
let properties = properties.item;
let stashed = state.is_explicitly_stashed || parent_stashed;
if !state.needs_update_stashed && stashed == state.is_stashed {
return;
}
if stashed != state.item.is_stashed {
if stashed != state.is_stashed {
let mut ctx = UpdateCtx {
global_state,
widget_state: state.item,
widget_state_children: state.children.reborrow_mut(),
widget_children: widget.children.reborrow_mut(),
properties_children: properties.children.reborrow_mut(),
widget_state: state,
children: children.reborrow_mut(),
};
let mut props = PropertiesMut {
map: properties.item,
default_map: default_properties.for_widget(widget.item.type_id()),
map: properties,
default_map: default_properties.for_widget(widget.type_id()),
};
widget
.item
.update(&mut ctx, &mut props, &Update::StashedChanged(stashed));
state.item.is_stashed = stashed;
state.item.needs_update_focus_chain = true;
widget.update(&mut ctx, &mut props, &Update::StashedChanged(stashed));
state.is_stashed = stashed;
state.needs_update_focus_chain = true;
// Items may have been changed while they were stashed in ways that require a
// relayout and a re-render.
if !stashed {
state.item.needs_layout = true;
state.item.request_layout = true;
state.item.request_paint = true;
state.item.needs_paint = true;
state.item.needs_accessibility = true;
state.item.request_accessibility = true;
state.needs_layout = true;
state.request_layout = true;
state.request_paint = true;
state.needs_paint = true;
state.needs_accessibility = true;
state.request_accessibility = true;
}
}
state.item.needs_update_stashed = false;
state.needs_update_stashed = false;
let parent_state = state.item;
recurse_on_children(
id,
widget.reborrow_mut(),
state.children,
properties.children,
|widget, mut state, properties| {
update_stashed_for_widget(
global_state,
default_properties,
widget,
state.reborrow_mut(),
properties,
stashed,
);
parent_state.merge_up(state.item);
},
);
let parent_state = state;
recurse_on_children(id, widget, children, |widget, mut state, properties| {
update_stashed_for_widget(
global_state,
default_properties,
widget,
state.reborrow_mut(),
properties,
stashed,
);
parent_state.merge_up(state.item);
});
}
pub(crate) fn run_update_stashed_pass(root: &mut RenderRoot) {
@ -429,7 +435,7 @@ pub(crate) fn run_update_stashed_pass(root: &mut RenderRoot) {
/// See the [passes documentation](../doc/05_pass_system.md#update-passes).
fn update_focus_chain_for_widget(
global_state: &mut RenderRootState,
mut widget: ArenaMut<'_, Box<dyn Widget>>,
widget: ArenaMut<'_, Box<dyn Widget>>,
mut state: ArenaMut<'_, WidgetState>,
properties: ArenaMut<'_, AnyMap>,
parent_focus_chain: &mut Vec<WidgetId>,
@ -437,48 +443,50 @@ fn update_focus_chain_for_widget(
let _span = enter_span(state.reborrow());
let id = state.item.id;
let children = WidgetArenaMut {
widget_children: widget.children,
widget_state_children: state.children,
properties_children: properties.children,
};
let widget = &mut **widget.item;
let state = state.item;
// Replace has_focused to check if the value changed in the meantime
state.item.has_focus_target = global_state.focused_widget == Some(id);
let had_focus = state.item.has_focus_target;
state.has_focus_target = global_state.focused_widget == Some(id);
let had_focus = state.has_focus_target;
if state.item.needs_update_focus_chain {
state.item.focus_chain.clear();
if state.item.accepts_focus {
state.item.focus_chain.push(id);
if state.needs_update_focus_chain {
state.focus_chain.clear();
if state.accepts_focus {
state.focus_chain.push(id);
}
state.item.needs_update_focus_chain = false;
state.needs_update_focus_chain = false;
let parent_state = &mut *state.item;
recurse_on_children(
id,
widget.reborrow_mut(),
state.children,
properties.children,
|widget, mut state, properties| {
update_focus_chain_for_widget(
global_state,
widget,
state.reborrow_mut(),
properties,
&mut parent_state.focus_chain,
);
parent_state.merge_up(state.item);
},
);
let parent_state = &mut *state;
recurse_on_children(id, widget, children, |widget, mut state, properties| {
update_focus_chain_for_widget(
global_state,
widget,
state.reborrow_mut(),
properties,
&mut parent_state.focus_chain,
);
parent_state.merge_up(state.item);
});
}
if !state.item.is_disabled && !state.item.is_stashed {
parent_focus_chain.extend(&state.item.focus_chain);
if !state.is_disabled && !state.is_stashed {
parent_focus_chain.extend(&state.focus_chain);
}
// had_focus is the old focus value. state.has_focused was replaced with parent_ctx.is_focused().
// Therefore if had_focus is true but state.has_focused is false then the widget which is
// currently focused is not part of the functional tree anymore and should resign the focus.
if had_focus && !state.item.has_focus_target {
if had_focus && !state.has_focus_target {
// Not sure about this logic, might remove
global_state.next_focused_widget = None;
}
state.item.has_focus_target = had_focus;
state.has_focus_target = had_focus;
}
pub(crate) fn run_update_focus_chain_pass(root: &mut RenderRoot) {
@ -888,23 +896,29 @@ pub(crate) fn run_update_pointer_pass(root: &mut RenderRoot) {
let new_icon = if let (Some(icon_source), Some(pos)) = (icon_source, pointer_pos) {
let (widget, state, properties) = root.widget_arena.get_all(icon_source);
let children = WidgetArenaRef {
widget_children: widget.children,
widget_state_children: state.children,
properties_children: properties.children,
};
let widget = widget.item;
let state = state.item;
let properties = properties.item;
let ctx = QueryCtx {
global_state: &root.global_state,
widget_state_children: state.children,
widget_children: widget.children,
widget_state: state.item,
widget_state: state,
properties: PropertiesRef {
map: properties.item,
default_map: root.default_properties.for_widget(widget.item.type_id()),
map: properties,
default_map: root.default_properties.for_widget(widget.type_id()),
},
properties_children: properties.children,
children,
};
if state.item.is_disabled {
if state.is_disabled {
CursorIcon::Default
} else {
widget.item.get_cursor(&ctx, pos)
widget.get_cursor(&ctx, pos)
}
} else {
CursorIcon::Default

View File

@ -377,7 +377,7 @@ impl<'arena, T> ArenaRefList<'arena, T> {
impl<'arena, T> ArenaMutList<'arena, T> {
/// Returns `true` if the list has an element with the given id.
pub fn has(self, id: impl Into<NodeId>) -> bool {
pub fn has(&self, id: impl Into<NodeId>) -> bool {
let id = id.into();
self.children.contains_key(&id)
}