Skip to content

Commit

Permalink
Implement update_anim pass (#539)
Browse files Browse the repository at this point in the history
This is part of the Pass Specification RFC:
linebender/rfcs#7

---------

Co-authored-by: Daniel McNab <[email protected]>
  • Loading branch information
PoignardAzur and DJMcNab authored Sep 12, 2024
1 parent 3726e91 commit 2fa8a05
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 16 deletions.
1 change: 1 addition & 0 deletions masonry/src/contexts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
57 changes: 57 additions & 0 deletions masonry/src/passes/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
);
}
21 changes: 12 additions & 9 deletions masonry/src/render_root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 => {
Expand Down Expand Up @@ -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();
}
Expand Down
6 changes: 2 additions & 4 deletions masonry/src/widget/widget_pod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 7 additions & 3 deletions masonry/src/widget/widget_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 2fa8a05

Please sign in to comment.