events for no-window widgets



Hi,

I've been exploring how widgets with no GdkWindow could receive
events. Here are some notes so far in case people have thoughts.

Event Types
===

Events separate very cleanly into "weird lowlevel stuff only matters
for GdkWindow" and "things widgets in general including no-window
widgets care about"

A. Events all widgets care about have two subdivisions:
A1. events that bubble: GDK_MOTION_NOTIFY, GDK_BUTTON_PRESS,
GDK_2BUTTON_PRESS, GDK_3BUTTON_PRESS, GDK_BUTTON_RELEASE, GDK_SCROLL,
GDK_KEY_PRESS, GDK_KEY_RELEASE
A2. events that are glorified property notifies: GDK_ENTER_NOTIFY,
GDK_LEAVE_NOTIFY, GDK_FOCUS_CHANGE

B. Freaky events of interest only in context of GdkWindow or GdkDisplay:
GDK_DELETE, GDK_DESTROY, GDK_EXPOSE, GDK_CONFIGURE, GDK_MAP,
GDK_UNMAP, GDK_PROPERTY_NOTIFY, GDK_SELECTION_CLEAR,
GDK_SELECTION_REQUEST, GDK_SELECTION_NOTIFY, GDK_PROXIMITY_IN,
GDK_PROXIMITY_OUT, GDK_DRAG_ENTER, GDK_DRAG_LEAVE, GDK_DRAG_MOTION,
GDK_DRAG_STATUS, GDK_DROP_START, GDK_DROP_FINISHED, GDK_CLIENT_EVENT,
GDK_VISIBILITY_NOTIFY, GDK_NO_EXPOSE, GDK_WINDOW_STATE, GDK_SETTING,
GDK_OWNER_CHANGE, GDK_GRAB_BROKEN, GDK_DAMAGE

Unfortunately, the current ::event signal (and per-event-type
sub-signals) dump all this together. These are logically very distinct
and ideally if we were starting from scratch, I would probably want to
see:

A1. a "GtkEvent" with some of the obscure lowlevel fields removed, no
->window field, add ->widget field, coords are widget-relative
A2. just use property notifies on "contains-pointer" and "has-focus" properties
B. GdkEvent as it is now (well, cleaned up, but similar idea)

Splitting out B (Window Owners)
===

A challenge is to think about all code related to B as conceptually a
new type, GtkWindowOwner or something. The only instances of this type
would be GtkWindow, GtkPlug, GtkSocket, and maybe a couple other
"weird" widgets like GL area. Unfortunately this type is sort of a
mixin; if it were an abstract base class, then widgets could not be
both a GtkWindowOwner and a GtkContainer. This probably means that the
WindowOwner functionality has to remain in GtkWidget.

Stuff that belongs to the conceptual window owner type could include:
the signals for all the B events; event mask; set_window;
set_double_buffered; GdkVisual; style_attach(); shape mask.

I don't think this stuff can easily be actually split out of
GtkWidget. However, I do think it's helpful to think of it as
distinct. _Possibly_ it would be clearer to add an interface that
explicitly had all this, and make GtkWidget implement that interface,
and deprecate the old gtk_widget_ names someday.

Or maybe there's a nice solution involving delegation, where window
widgets have a helper object and there's a base class for that.

Enter/Leave Notify
===

Here is how I think enter/leave should work:
http://bugzilla.clutter-project.org/show_bug.cgi?id=1576

Though I'm not sure events are required at all. Currently, something
like GtkButton needs the input-only window just to prelight. I think a
simple boolean property "button or any child of button contains
pointer" would be sufficient for this, however.
Multiple GdkDevice pointers complicates that a bit, though it doesn't
look like GtkButton handles that right now. The boolean could be
defined to mean that greater-than-zero devices are inside the widget
and that would probably work fine for prelighting, better than the
current enter/leave events in fact.

My current thought here is to have a boolean prop which is set on the
leaf widget containing each device and all ancestors of that widget.
For convenience, maybe a vfunc or vfunc pair which is invoked whenever
this property changes. notify::contains-pointer would already be a
signal for it but that's sort of annoying in subclasses.

I guess this flag would simply mean "prelight" and that might be handy
for the new theme stuff.

Given the property, the event would be only on conceptual
GtkWindowOwner. No-window widgets would use the property not the
event.

Implementing "contains-pointer" for no-window widgets would require
some kind of pick() virtual method for identifying the widget under
the pointer, GtkWidget default implementation just looks at widget
allocations, GtkContainer default recurses into its children. Picking
would start at the window widget receiving a GdkWindow native motion
notify or enter/leave and then generate synthetic events for no-window
widgets inside that GdkWindow.

Focus Change
===

I believe the existing has-focus pretty much handles this; similar to
the above-proposed notify::contains-pointer, it might be handy to have
a virtual function to go with it, or virtual function pair. The fields
in the focus change event are only of interest for GtkWindowOwner not
for widgets in general, it looks like.

Given the property, the event would be only on conceptual
GtkWindowOwner. No-window widgets would use the property not the
event.

Bubble Events
===

If we solve "A2" enter/leave/focus events with properties, "A1"
remains. I've played around with code on A1 and here are some of the
issues.

1. GdkEvent's window-relative coordinates don't mean anything on a
no-window widget.

My initial experiment is to add new API gdk_event_get_window_coords().
gdk_event_get_coords() always returns window coords when the event
comes out of GDK. However, some (or all) APIs in GTK would have the
ability to gdk_event_set_coords(), at which point the "coords" and the
"window coords" would not be the same. The idea would be to allow
widget-relative events.

2. GdkEvent's "window" field doesn't mean anything on a no-window
widget or as the event bubbles.

My initial experiment is to add gdk_event_set_source(event, GObject)
which GTK uses to store the event widget on the event.

3. Include no-window widgets in the "pick the widget to bubble from"
code in gtkmain.c, which is the grab logic plus mapping windows to
widgets.

My initial experiment is to add a GtkEventReceiver interface,
implemented by Widget, which has a pick_source() method. pick_source()
takes a GdkEvent and comes up with the source widget. pick_source() is
almost the same as the pick() used to track contains-pointer, except
that it handles key events also.

I haven't really sorted out grabs yet but basically I think it's a
vfunc used to recursively punt up to the toplevel which would then
implement the actual logic.

4. Replace gtk_propagate_event() with something that translates the
event to widget-relative coords at each step.

My initial experiment is a GtkEventReceiver interface with
capture_event and bubble_event vfuncs. These are recursively called to
first capture then bubble the event. GtkWindow uses its capture_event
to do the special-cased key event handling that's in gtkmain.c now. As
events are captured and bubbled, their coordinates are translated.

In addition to enabling no-window widgets, this adds capture
functionality to GTK and cleans up some of the "global omniscience"
design of gtkmain.c in favor of recursive vfuncs.

5. Event masks.

If you're a no-window widget, and you want button presses, then you
need your containing window widget to have selected for button press
event. Possible solutions include:
 - my preference so far: just always selecting for button, key,
scroll, and motion events on any GdkWindow used for a
gtk_widget_set_window()
 - tracking a combined event mask of all children inside window widgets

6. Delivering events to each widget during capture and bubble. This
issue, how to emit a widget-relative event on GtkWidget, is THE big
central API question.

During capture, a signal like ::event-captured seems logical.

During bubble, a signal like ::event seems logical. Unfortunately that
signal already exists but its GdkEvent is window-relative to the
original event window, rather than widget-relative to the widget
currently getting bubbled through.

Possible solutions include:
 - add a new signal ::event-bubbled with the widget-relative events.
 - break ::event to be widget-relative, but in my initial
experimentation this looked like it'd be pretty disruptive even within
GTK
 - gtk_widget_class_set_widget_relative_events() toggles what kind of
coords each subclass receives to ::event
 - leave event->x, event->y window-relative and add new GdkEvent
fields for the widget-relative

problem with ::event-bubbled is that it would be nice to have the
button-press-event etc. individual signals too and
::button-press-event-bubbled is a mouthful of a name. plus it's
confusing that the signals you really want are named weirdly
"-bubbled" and the old ones you don't are named better. i.e. you'd be
supposed to always use ::event-bubbled and ignore ::event, etc.  This
quickly leads to dreams of just making the existing signals
widget-relative, but that change is very disruptive and the compiler
can't detect it at all.

gtk_widget_class_set_widget_relative_events() works pretty well for
subclassing but makes a mess if apps connect to the event signals
since they have to care how the widget set them up.

Adding widget coords to GdkEvent would be nice, but the intractable
problem here is that the widget coords really need to be event->x,
event->y and the window coords event->x_window, y_window.
Otherwise, 1) there's no name for the widget coords unless we use the
concept of widget in GDK and 2) we'd have to explain in the docs and
on mailing lists that event->x, event->y were red herrings, don't use
them. Tough to do without embarrassment.

Summary
===

The implementation is not hard. (Though some care is needed to keep
the semantics of grabs and key events and such.)

The main problem is that the existing window-relative signals and
event->x,y use up the namespace you'd want to use for widget-relative
signals.

My preference so far would be to go with a new ::event-bubbled signal
I think, plus (ouch)
{button-press,button-release,key-press,key-release,scroll,motion}-bubbled.
(or some better names...?) And have event->x, event->y be either
window or widget relative depending on the signal the event is passed
to. Basically there's an old and a new set of signals. They could be
on two new broken-out interfaces for clarity, GtkWindowOwner and
GtkEventReceiver.

Assuming the disruptive API changes are avoided (they are all
optional, just prettier), I think this is a very doable project. Far
smaller in scope than the rendering cleanup, for example.

This change would kill all the input-only windows. So the remaining
windows would be the ones used for scroll and clip. Solving scroll and
clip would then make GdkWindow fall away entirely except for toplevels
and the plug/socket/GL stuff that genuinely cares about a native
window.

Havoc


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]