Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix timer requests in update and lifecycle. #964

Merged
merged 1 commit into from
May 22, 2020

Conversation

xStrom
Copy link
Member

@xStrom xStrom commented May 21, 2020

This PR fixes the handling of timer requests. Previously they were only collected in the window's event but I moved the collection to the general post processing step. I also removed the legacy request_timer bool which no longer served a purpose and was never being reset.

This also finally allowed to address #834. Scrollbars now briefly animate when the container is resized. This can be seen with e.g. the scroll_colors example when you resize the window.

Fixes #834.

@xStrom xStrom added the S-needs-review waits for review label May 21, 2020
Copy link
Member

@cmyr cmyr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall this looks totally reasonable, but I'm curious about processing events after anim_frame. In particular, I don't think we should be sending any events between anim_frame and paint?

@@ -176,12 +176,15 @@ impl<T, W: Widget<T>> Scroll<T, W> {
}

/// Makes the scrollbars visible, and resets the fade timer.
pub fn reset_scrollbar_fade(&mut self, ctx: &mut EventCtx, env: &Env) {
pub fn reset_scrollbar_fade<F>(&mut self, request_timer: F, env: &Env)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, this is annoying.

Something I've been wondering is whether we could have certain traits for behaviour that is common to (most?) contexts, and then for things like requesting a timer or sending a command you could have the argument be ctx: &mut dyn ContextCommon or something...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeap, I was thinking of the same thing based on the amount of code duplication we have in context.rs. Then having to do this request_timer closure here really boosted that thinking. I have it in my notes to attempt to better the situation with traits. (Would be a separate PR.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am accidentally working on this right now.

K: Eq + Hash + Copy,
V: Copy,
{
fn extend_drain(&mut self, source: &mut Self) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just an observation: this is the kind of thing I'm always curious to profile, or at least take a look at the asm; always room for funny surprises. I certainly suspect its an improvement, but it is a suspicion, it's not impossible the compiler generating code like this as an optimization already.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was a funny surprise alright. The empty target case is 0ns with this optimized function, which is a win. However unexpectedly even the slow path is faster! The slow path is faster only with drain though. If I bench extending with just iter vs iter, then the slow paths are equal. Some sort of non-generic code win I guess.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

neat, thanks for measuring!

@@ -288,6 +293,8 @@ impl<T: Data> Window<T> {
if ctx.base_state.request_anim {
self.last_anim = Some(now);
}

self.post_event_processing(&mut base_state, queue, data, env, process_commands);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a specific reason we think this is necessary? do_anim_frame is part of paint, and paint calls this already.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • It's necessary because a widget might call submit_command or request_timer in response to AnimFrame.
  • do_anim_frame is currently part of lifecycle not paint and so even if paint called post_event_processing that wouldn't be guaranteed for other AnimFrame calls to lifecycle.
  • paint doesn't call post_event_processing.
  • AnimFrame does not guarantee paint in the future as discussed on zulip.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in order:

  • to my mind, AnimFrame should only be used to update state in preparation for an imminent paint call. This is a weird case; AnimFrame is one of the bits of event flow that is really a poor fit to the current architecture, and when (like.... a year ago? 😬) I was playing around with a higher-level animation API, one of the changes was to just make a prepare_for_animation method on Widget that replaced AnimFrame. submit_command and request_timer should be discouraged here, and it's annoying that this isn't enforced by the type system.

  • do_anim_frame sort of is part of paint; LifeCycle::AnimFrame should only be sent from Window::do_paint, if it happens anywhere else that's a mistake.

  • this checks out, I must've misread again. That said, it looks like we call post_event_processing in Window::lifecycle, which is what calls do_anim_frame`?

  • I think this is false? it's not really that anim guarantees paint, it's that AnimFrame is only called from the painting code path.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Yes we do call post_event_processing in Window::lifecycle right now, which means it also applies to do_anim_frame. However due to borrow checking limitations, in order to keep that functionality, I had to add it to do_anim_frame because the Window::lifecycle call moved into an if block.

  • Yeah right now AnimFrame is sent just before paint. However based on that zulip discussion this may change. @raphlinus said It's up to the handler of that lifecycle event to request painting - from which I derive that if the handler doesn't request painting, the painting might not happen at all. (This is for future behavior, today that's not true.)

More generally, moving AnimFrame out of lifecycle makes sense if it's supposed to have different limitations. However as long as it's part of lifecycle I think it should behave like a lifecycle event. That includes a working request_timer and submit_command.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the second point: I think there's a confusion about raph's comments. AnimFrame does mean you're going to paint; there's no short-circuiting. If you see it, it means a call to paint is next. To continue the animation, you do need to request another frame.

I'll take a look at the code you're talking about, I might've missed that.

@@ -260,13 +260,18 @@ impl<T: Data> Window<T> {
};

self.root.lifecycle(&mut ctx, event, data, env);
self.post_event_processing(&mut base_state, queue, data, env, process_commands);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can move this out of the if branch if we move the base_state decl to above the block, this should solve our other little confusion?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! That will work.

Copy link
Member

@cmyr cmyr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good. I'm wondering if, for future work, we can actually just get rid of do_anin_frame at some point? it just seems like more confusion than its worth maybe.

@xStrom xStrom removed the S-needs-review waits for review label May 22, 2020
@xStrom xStrom merged commit 875ad40 into linebender:master May 22, 2020
@xStrom xStrom deleted the scroll-size branch May 22, 2020 21:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Briefly show the scrollbars any time the Scroll widget's size changes
2 participants