-
Notifications
You must be signed in to change notification settings - Fork 567
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
Conversation
There was a problem hiding this 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) |
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
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.)
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
neat, thanks for measuring!
druid/src/window.rs
Outdated
@@ -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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
orrequest_timer
in response toAnimFrame
. do_anim_frame
is currently part oflifecycle
notpaint
and so even ifpaint
calledpost_event_processing
that wouldn't be guaranteed for otherAnimFrame
calls tolifecycle
.paint
doesn't callpost_event_processing
.AnimFrame
does not guaranteepaint
in the future as discussed on zulip.
There was a problem hiding this comment.
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 aprepare_for_animation
method onWidget
that replacedAnimFrame
.submit_command
andrequest_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 fromWindow::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 thatAnimFrame
is only called from the painting code path.
There was a problem hiding this comment.
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
inWindow::lifecycle
right now, which means it also applies todo_anim_frame
. However due to borrow checking limitations, in order to keep that functionality, I had to add it todo_anim_frame
because theWindow::lifecycle
call moved into anif
block. -
Yeah right now
AnimFrame
is sent just beforepaint
. 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
.
There was a problem hiding this comment.
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.
druid/src/window.rs
Outdated
@@ -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); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes! That will work.
There was a problem hiding this 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.
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 legacyrequest_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.