events-delivery branch review - crossing events
- Date: Mon, 03 Apr 2017 22:58:12 +0200
- From: Alexander Larsson <alexl@xxxxxxxxxx>
- Subject: events-delivery branch review - crossing events
So, I took a quick look at the event-delivery branch. One fundamental
thing that it is currently missing is the handling of crossing events
due to size allocation changes. In the simple case this is just a
label changing context, making the widget wider which changes the
widget under the pointer.
However, there is also some real complexity in this. For instance,
it's possible to get loops this way, such as if the :hover state
causes the new widget position to be outside the pointer.
If you look at how CSS handles this, the specification says:
User agents are not required to reflow a currently displayed
document due to pseudo-class transitions. For instance, a style
sheet may specify that the 'font-size' of an :active link should be
larger than that of an inactive link, but since this may cause
letters to change position when the reader selects the link, a UA
may ignore the corresponding style rule.
However, testing firefox and chrome it seems in practice what
happens is that :hover causes a reflow, but the new reflow does not in
turn cause re-calculating the :hover state (until the next mouse event
at least). This seems like like a pretty nice behaviour in a weird
In the current pre-event-delivery branch what happens in the layout
cycle is this:
1) emit all events in the queue then freeze the event queue
2) emit update which triggers all animations etc
3) Do size machinery, possibly loop up to 4 times
5) Unfreeze events, possibly queueing a new frame
In the above, a mouse enter event in 1 would cause a css property
change, which would cause the cause the relayout in 3 to produce a new
GdkWindow geometry, which in turn will emit new enter/leave events
when the queue is unfrozen at 5 and which cause a new cycle the next
frame. The frame clock will limit the cpu use from 100%, but its still
not ideal to constantly switch between two states. Also, even if we
don't get a loop the correct rendering is always delayed one frame.
The question is how to handle this in the new model. The naive version
would cause the hover css property to change immediately from size
allocate, which will cause a layout loop that runs 4 times, and then
paint. Another alternative is to keep a crossing event for this so
that we can store it on the event queue, and this way we can reproduce
the current behaviour. However, that strikes me as non-ideal too.
An alternative would be to treat crossing events as level-triggered
instead of edge-triggered, at most once a frame. Every frame, after
the first iteration in the layout machinery we pick the current
position for all pointers, and emit css state changes (as well as
generic widget event callbacks). If any of these queue a resize we'll
handle these in the next iteration, but we never generate further
crossing events this frame, nor do we automatically schedule a new
frame just due to this.
This has some complications in semantics though. If you move the
pointer between multiple widgets in one frame we will miss some
crossing events that we would otherwise have seen. I don't think this
is a problem in itself, because these would unlikely have resulted in
something that would affect the final frame result (it would
essentially be like the user moved the mousepointer so fast that it
jumped over the inbetween widget completely). However, it is not
entierly clear how to report the motion events that land in-between
the two widgets that had the enter notify reported to them. Getting
motion events without enter events is quite a change compared to
the current semantics. Can we just drop these events?
Alexander Larsson Red Hat, Inc
He's an immortal moralistic Green Beret looking for 'the Big One.' She's
an elegant hypochondriac magician's assistant with her own daytime radio
talk show. They fight crime!
gtk-devel-list mailing list