Implement update_anim pass (#539)

This is part of the Pass Specification RFC:
https://github.com/linebender/rfcs/pull/7

---------

Co-authored-by: Daniel McNab <36049421+DJMcNab@users.noreply.github.com>
This commit is contained in:
Olivier FAURE 2024-09-12 11:06:33 +00:00 committed by GitHub
parent 3726e91a48
commit 2fa8a055bd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 79 additions and 16 deletions

View File

@ -450,6 +450,7 @@ impl_context_method!(MutateCtx<'_>, EventCtx<'_>, LifeCycleCtx<'_>, {
pub fn request_anim_frame(&mut self) {
trace!("request_anim_frame");
self.widget_state.request_anim = true;
self.widget_state.needs_anim = true;
}
/// Indicate that your children have changed.

View File

@ -230,3 +230,60 @@ pub(crate) fn run_update_scroll_pass(root: &mut RenderRoot) {
});
}
}
// ----------------
fn update_anim_for_widget(
global_state: &mut RenderRootState,
mut widget: ArenaMut<'_, Box<dyn Widget>>,
mut state: ArenaMut<'_, WidgetState>,
elapsed_ns: u64,
) {
let _span = widget.item.make_trace_span().entered();
if !state.item.needs_anim {
return;
}
state.item.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;
let mut ctx = LifeCycleCtx {
global_state,
widget_state: state.item,
widget_state_children: state.children.reborrow_mut(),
widget_children: widget.children.reborrow_mut(),
};
widget
.item
.lifecycle(&mut ctx, &LifeCycle::AnimFrame(elapsed_ns));
}
let id = state.item.id;
let parent_state = state.item;
recurse_on_children(
id,
widget.reborrow_mut(),
state.children,
|widget, mut state| {
update_anim_for_widget(global_state, widget, state.reborrow_mut(), elapsed_ns);
parent_state.merge_up(state.item);
},
);
}
/// Run the animation pass.
pub(crate) fn run_update_anim_pass(root: &mut RenderRoot, elapsed_ns: u64) {
let _span = info_span!("update_anim").entered();
let (root_widget, mut root_state) = root.widget_arena.get_pair_mut(root.root.id());
update_anim_for_widget(
&mut root.state,
root_widget,
root_state.reborrow_mut(),
elapsed_ns,
);
}

View File

@ -26,7 +26,7 @@ use crate::passes::layout::root_layout;
use crate::passes::mutate::{mutate_widget, run_mutate_pass};
use crate::passes::paint::root_paint;
use crate::passes::update::{
run_update_disabled_pass, run_update_pointer_pass, run_update_scroll_pass,
run_update_anim_pass, run_update_disabled_pass, run_update_pointer_pass, run_update_scroll_pass,
};
use crate::text::TextBrush;
use crate::tree_arena::TreeArena;
@ -208,12 +208,17 @@ impl RenderRoot {
// See https://github.com/linebender/druid/issues/85 for discussion.
let last = self.last_anim.take();
let elapsed_ns = last.map(|t| now.duration_since(t).as_nanos()).unwrap_or(0) as u64;
let root_state = self.root_state();
if root_state.request_anim {
root_state.request_anim = false;
self.root_lifecycle(LifeCycle::AnimFrame(elapsed_ns));
self.last_anim = Some(now);
}
run_update_anim_pass(self, elapsed_ns);
let mut root_state = self.widget_arena.get_state_mut(self.root.id()).item.clone();
self.post_event_processing(&mut root_state);
// If this animation will continue, store the time.
// If a new animation starts, then it will have zero reported elapsed time.
let animation_continues = root_state.needs_anim;
self.last_anim = animation_continues.then_some(now);
Handled::Yes
}
WindowEvent::RebuildAccessTree => {
@ -272,8 +277,6 @@ impl RenderRoot {
// TODO - Xilem's reconciliation logic will have to be called
// by the function that calls this
// TODO - if root widget's request_anim is still set by the
// time this is called, emit a warning
if self.root_state().needs_layout {
self.root_layout();
}

View File

@ -333,12 +333,10 @@ impl<W: Widget> WidgetPod<W> {
true
}
LifeCycle::AnimFrame(_) => {
state.request_anim = false;
true
}
// Routing DisabledChanged has been moved to the update_disabled pass
LifeCycle::DisabledChanged(_) => false,
// Animations have been moved to the update_anim pass
LifeCycle::AnimFrame(_) => false,
LifeCycle::BuildFocusChain => {
if state.update_focus_chain {
// Replace has_focus to check if the value changed in the meantime

View File

@ -99,8 +99,10 @@ pub struct WidgetState {
/// The accessibility method must be called on this widget or a descendant
pub(crate) needs_accessibility: bool,
/// Any descendant has requested an animation frame.
/// An animation must run on this widget
pub(crate) request_anim: bool,
/// An animation must run on this widget or a descendant
pub(crate) needs_anim: bool,
/// This widget or a descendant changed its `explicitly_disabled` value
pub(crate) needs_update_disabled: bool,
@ -174,6 +176,7 @@ impl WidgetState {
needs_accessibility: true,
has_focus: false,
request_anim: true,
needs_anim: true,
needs_update_disabled: true,
focus_chain: Vec::new(),
children_changed: true,
@ -197,11 +200,12 @@ impl WidgetState {
needs_layout: false,
request_compose: false,
needs_compose: false,
needs_paint: false,
request_paint: false,
needs_paint: false,
request_accessibility: false,
needs_accessibility: false,
request_anim: false,
needs_anim: false,
needs_update_disabled: false,
children_changed: false,
update_focus_chain: false,
@ -231,7 +235,7 @@ impl WidgetState {
self.needs_layout |= child_state.needs_layout;
self.needs_compose |= child_state.needs_compose;
self.needs_paint |= child_state.needs_paint;
self.request_anim |= child_state.request_anim;
self.needs_anim |= child_state.needs_anim;
self.needs_accessibility |= child_state.needs_accessibility;
self.needs_update_disabled |= child_state.needs_update_disabled;
self.has_focus |= child_state.has_focus;