Tricky GtkPlug, hierarchy_changed issue.
- From: Owen Taylor <otaylor redhat com>
- To: gtk-devel-list gnome org
- Subject: Tricky GtkPlug, hierarchy_changed issue.
- Date: 04 Jul 2001 21:36:48 -0400
I've been working some today on really robustifying GtkPlug/GtkSocket
today, in particular making same application plug/socket embedding
work well, using the normal widget mechanisms.
In doing so, I ran into a mildly nasty corner case with
gtk_widget_get_toplevel() that I thought I should write up in case
anybody had bright ideas or at least for posterity.
* The ::hierarchy_changed signal is a recently introduced
signal that is sent to a widget when it its
"anchored state" changed. A widget is anchored
when its topmost ancestor is a real toplevel widget
(only anchored widgets can be realized or mapped).
::hierarchy_changed is emitted when the widget changes
from anchored to unanchored or unanchored to anchored.
If a widget is moved from one toplevel to another,
::hierarchy_changed is emitted on it twice.
gtk_widget_get_toplevel() doesn't always get a real
toplevel - it just gets the topmost ancestor. So,
before my recent GtkPlug changes, the way to find
the real toplevel was typically one of:
toplevel = gtk_widget_get_toplevel (widget);
if (GTK_IS_WINDOW (toplevel))
{
}
or:
toplevel = gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW);
if (toplevel)
{
}
* In order to have efficient local GtkPlug/GtkSocket embedding,
I made GtkPlug able to act as either a toplevel or a child
widget. This involves code like:
static void
gtk_plug_hide (GtkWidget *widget)
{
if (GTK_WIDGET_TOPLEVEL (widget))
GTK_WIDGET_CLASS (parent_class)->hide (widget);
else
GTK_WIDGET_CLASS (bin_class)->hide (widget);
}
Which works quite well. However,
gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW) no longer
is reliable, because a GtkPlug can be a intermediate
ancestor, and a GtkPlug is a descendent of GtkWindow.
So, it's necessary to encourage people to use
gtk_widget_get_toplevel (widget) instead. Which is
fine ... it's considerably faster anyways.
* But it turns out that
toplevel = gtk_widget_get_toplevel (widget);
if (GTK_IS_WINDOW (toplevel))
{
}
Is no longer quite correct either. By correct, I mean
that it doesn't correspond to the quantity that
::hierarchy changed is notifying on.
The reason why is that when I transform a child GtkPlug
into a toplevel GtkPlug, the sequence of operations
is:
- Unparent the GtkPlug (::hierarchy_changed emitted
for unanchoring)
- Change the GtkPlug into a toplevel
- Emit ::hierarchy_changed for anchoring.
During the first ::hierarchy_changed, the children
of the GtkPlug are unanchored - they are not descendents
of a real toplevel. However, gtk_widget_get_toplevel (widget);
does return a GtkWindow. So, the _actual_ correct thing
to write is:
toplevel = gtk_widget_get_toplevel (widget);
if (GTK_IS_TOPLEVEL (toplevel))
{
}
I've documented this in the gtk_widget_get_toplevel() docs,
and that may be good enough since this corner case only matters
if:
a) You are writing a widget
b) You care that your widget works inside a GtkPlug
c) You connect to ::hierarchy_changed
d) You get the toplevel inside your ::hierarchy_changed
signal without checking whether you are being anchored
or unanchored. (You can tell which is the case
from the previous_toplevel argument which will
be NULL for anchoring and the the old toplevel
for anchoring.)
Still it makes me a little nervous ... if I we could ignore
backwards compatibility, gtk_widget_get_toplevel() should
return NULL unlesss GTK_IS_TOPLEVEL (result). BUt that
makes a lot of existing code that doesn't check the
result less robust.
The other solution would be to remove the idea of first
unanchoring and then anchoring, and then write some
code so that for this case (and for gtk_widget_reparent
as well), the deanchoring ::hierarchy_changed signals
could be skipped entirely.
Regards,
Owen
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]