mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-25 05:01:09 +00:00
b67c5af55b
Requires fixes to GtkContainer and GtkWindow to unmap their children, rather than just withdrawing or hiding the container window. Requires fix to GtkHandleBox to chain up to GtkContainer unmap. Historically we avoided these unmaps for efficiency reasons, but these days it's a bigger problem that there's no way for child widgets to know that one of their ancestors has become unmapped.
501 lines
16 KiB
Plaintext
501 lines
16 KiB
Plaintext
Notes about the inner workings of the widget system of GTK+
|
|
===========================================================
|
|
|
|
This file contains some notes as to how the widget system does
|
|
and should work. It consists of three parts:
|
|
|
|
I) A description of the meaning of the various flags
|
|
|
|
II) A list of invariants about the states of the widgets.
|
|
(Throughout this document, we refer to the states of the
|
|
widgets by referring to the flags for GtkWidget)
|
|
|
|
III) Some notes about the ways that a widget changes states
|
|
|
|
IV) A list of responsibilities of various widget signals when
|
|
the states change.
|
|
|
|
Any action necessary to maintain the invariants in II which is not
|
|
explicitly mentioned in IV), is the responsibility of the core GTK
|
|
code, which is roughly defined as:
|
|
|
|
gtkobject.c
|
|
gtkwidget.c
|
|
gtkcontainer.c
|
|
gtkmain.c
|
|
gtksignal.c
|
|
|
|
Section II is mostly of interest to those maintaining GTK, the
|
|
other sections may also be interesting to people writing
|
|
new widgets.
|
|
|
|
Main outline:
|
|
- Owen Taylor <owt1@cornell.edu>
|
|
1998/02/03
|
|
|
|
Flag descriptions:
|
|
- Tim Janik <timj@gimp.org>
|
|
1998/02/04
|
|
|
|
I. Flags
|
|
--------
|
|
|
|
GtkObject:
|
|
|
|
GTK_DESTROYED:
|
|
This flagged is set for a GtkObject right before its
|
|
destruction code is executed. Its main use is the
|
|
prevention of multiple destruction invocations.
|
|
|
|
GTK_FLOATING:
|
|
This flag reflects the fact that the holder of the
|
|
initial reference count is unknown. Refer to refcounting.txt
|
|
for further details.
|
|
|
|
GTK_RESERVED_1:
|
|
GTK_RESERVED_2:
|
|
Reserved flags.
|
|
|
|
|
|
GtkWidget, public flags:
|
|
|
|
GTK_TOPLEVEL:
|
|
Widgets without a real parent, as there are GtkWindows and
|
|
GtkMenus have this flag set throughout their lifetime.
|
|
Toplevel widgets always contain their own GdkWindow.
|
|
|
|
GTK_NO_WINDOW:
|
|
This flag is indicative for a widget that does not provide
|
|
its own GdkWindow. Visible action (e.g. drawing) is performed
|
|
on the parent's GdkWindow.
|
|
|
|
GTK_REALIZED:
|
|
Set by gtk_widget_realize, unset by gtk_widget_unrealize.
|
|
Relies on ((widget->parent && widget->parent->window)
|
|
|| GTK_WIDGET_TOPLEVEL (widget));
|
|
Means: widget has an associated GdkWindow (XWindow).
|
|
|
|
GTK_MAPPED:
|
|
Set by gtk_widget_map, unset by gtk_widget_unmap.
|
|
May only be set if GTK_WIDGET_REALIZED (widget).
|
|
Means: gdk_window_show() has been called on the widgets window(s).
|
|
|
|
GTK_VISIBLE:
|
|
Set by gtk_widget_show.
|
|
Implies that a widget will be flagged GTK_MAPPED as soon as its
|
|
parent is mapped.
|
|
!GTK_VISIBLE:
|
|
Set by gtk_widget_hide.
|
|
Implies that a widget is not onscreen, therefore !GTK_MAPPED.
|
|
|
|
GTK_CHILD_VISIBLE
|
|
Set by gtk_widget_set_child_visible, and if FALSE indicates that
|
|
the widget should not be mapped even if the parent is mapped
|
|
and visible. Containers like GtkNotebook use this flag.
|
|
A private flag, not a public flag, so if you need to check
|
|
this flag, you should call gtk_widget_get_child_visible().
|
|
(Should be very rarely necessary.)
|
|
|
|
GTK_SENSITIVE:
|
|
Set and unset by gtk_widget_set_sensitive.
|
|
The sensitivity of a widget determines whether it will receive
|
|
certain events (e.g. button or key presses). One premise for
|
|
the widgets sensitivity is to have GTK_SENSITIVE set.
|
|
|
|
GTK_PARENT_SENSITIVE:
|
|
Set and unset by gtk_widget_set_sensitive operations on the
|
|
parents of the widget.
|
|
This is the second premise for the widgets sensitivity. Once
|
|
it has GTK_SENSITIVE and GTK_PARENT_SENSITIVE set, its state is
|
|
effectively sensitive. This is expressed (and can be examined) by
|
|
the GTK_WIDGET_IS_SENSITIVE macro.
|
|
|
|
GTK_CAN_FOCUS:
|
|
There are no directly corresponding functions for setting/unsetting
|
|
this flag, but it can be affected by the GtkWidget::has_focus argument
|
|
via gtk_widget_set_arg.
|
|
This flag determines whether a widget is able to handle focus grabs.
|
|
|
|
GTK_HAS_FOCUS:
|
|
This flag will be set by gtk_widget_grab_focus for widgets that also
|
|
have GTK_CAN_FOCUS set. The flag will be unset once another widget
|
|
grabs the focus.
|
|
|
|
GTK_CAN_DEFAULT:
|
|
GTK_HAS_DEFAULT:
|
|
These two flags are mostly equal in functionality to their *_FOCUS
|
|
counterparts, but for the default widget.
|
|
|
|
GTK_HAS_GRAB:
|
|
Set by gtk_grab_add, unset by gtk_grab_remove.
|
|
Means: widget is in the grab_widgets stack, and will be the preferred
|
|
one for receiving events other than ones of cosmetic value.
|
|
|
|
GTK_BASIC:
|
|
The GTK_BASIC flag is an attempt at making a distinction
|
|
between widgets that handle user input e.g. key/button presses
|
|
and those that don't. Subsequent parent<->child relation ships
|
|
of non `basic' widgets should be avoided. The checking for
|
|
this is currently not properly enforced in the code. For
|
|
example GtkButton is a non `basic' widget, that will therefore
|
|
disallow to act as a container for another GtkButton. Now the
|
|
gnit is, one can add a GtkHBox (which is a `basic' widget) to
|
|
the first button, and put the second into the box.
|
|
|
|
GTK_RESERVED_3:
|
|
|
|
GTK_RC_STYLE:
|
|
This flag indicates that its style has been looked up through
|
|
the rc mechanism. It does not imply that the widget actually
|
|
had a style defined through the rc mechanism.
|
|
|
|
|
|
GtkWidget, private flags:
|
|
|
|
GTK_USER_STYLE:
|
|
A widget is flagged to have a user style, once gtk_widget_set_style
|
|
has been invoked for it. The use of this flag is to tell widgets
|
|
which share a global user style from the ones which got a certain
|
|
style assign from outside the toolkit.
|
|
|
|
GTK_RESIZE_PENDING:
|
|
First, this is only valid for GtkContainers.
|
|
[some of the code should move to gtkcontainer.c therefore]
|
|
Relies on GTK_WIDGET_REALIZED(widget)
|
|
[this is not really enforced throughout the code, but should
|
|
be. it only requires a few checks for GTK_WIDGET_REALIZED and
|
|
minor changes to gtk_widget_unrealize, we can then remove the check
|
|
in gtk_widget_real_destroy]
|
|
Means: there is an idle handler waiting for the container to
|
|
resize it.
|
|
|
|
GTK_RESIZE_NEEDED:
|
|
Relies on GTK_WIDGET_REALIZED(widget)
|
|
[this is not really enforced throughout the code, but should
|
|
be. once this is done special checking in gtk_widget_real_destroy
|
|
can be avoided]
|
|
Means: a widget has been added to the resize_widgets list of
|
|
its _toplevel_ container (keep this in mind for GtkViewport).
|
|
Remark: this flag is also used internally by gtkwindow.c during
|
|
the evaluation of resizing worthy widgets.
|
|
|
|
GTK_LEAVE_PENDING:
|
|
A widget is flagged as such if there is a leave_notify event
|
|
pending for it. It will receive this event regardless of a grab
|
|
through another widget or its current sensitivity.
|
|
[this should be made relying on GTK_REALIZED]
|
|
|
|
GTK_HAS_SHAPE_MASK:
|
|
Set by gtk_widget_shape_combine_mask if a widget got a shape mask
|
|
assigned (making use of the X11 shaped window extension).
|
|
|
|
GTK_IN_REPARENT:
|
|
During the act of reparentation widgets which are already
|
|
realized and will be added to an already realized parent need
|
|
to have this flag set to prevent natural unrealization on the
|
|
process of getting unparented.
|
|
|
|
GTK_NEED_REQUEST:
|
|
This flag is set if the widget doesn't have an up to date
|
|
requisition. If this flag is set, we must actually emit ::size-request
|
|
when gtk_widget_size_request() is called. Otherwise, we can
|
|
simply widget->requisition. We keep track of this all the time
|
|
however, widgets with this flag set are only added to the resize
|
|
queue if they are viewable.
|
|
|
|
GTK_NEED_ALLOCATION:
|
|
This flag is set if the widget doesn't have an up to date
|
|
allocation. If this flag is set, we must actually emit ::size-allocate
|
|
when gtk_widget_size_allocate() is called, even if the new allocation
|
|
is the same as the current allocation.
|
|
|
|
Related Macros:
|
|
|
|
GTK_WIDGET_DRAWABLE:
|
|
This macro examines whether a widget is flagged as GTK_WIDGET_VISIBLE
|
|
and GTK_WIDGET_MAPPED.
|
|
Means: it _makes sense_ to draw in a widgets window.
|
|
|
|
GTK_WIDGET_IS_SENSITIVE:
|
|
This macro tells the real sensitivity state of a widget. It returns
|
|
whether both the widget and all its parents are in sensitive state.
|
|
|
|
|
|
II. Invariants:
|
|
---------------
|
|
|
|
This section describes various constraints on the states of
|
|
the widget:
|
|
|
|
In the following
|
|
|
|
A => B means if A is true, than B is true
|
|
A <=> B means A is true, if and only if B is true
|
|
(equivalent to A => B and A <= B)
|
|
|
|
|
|
1) GTK_WIDGET_DESTROYED (widget) => !GTK_WIDGET_REALIZED (widget)
|
|
=> !GTK_WIDGET_VISIBLE (widget)
|
|
[ The latter is not currently in place, but it should be ]
|
|
|
|
2) GTK_WIDGET_MAPPED (widget) => GTK_WIDGET_REALIZED (widget)
|
|
|
|
3) if GTK_WIDGET_TOPLEVEL (widget):
|
|
GTK_WIDGET_VISIBLE (widget) <=> GTK_WIDGET_MAPPED (widget)
|
|
|
|
4) if !GTK_WIDGET_TOPLEVEL (widget):
|
|
widget->parent && GTK_WIDGET_REALIZED (widget->parent) <=>
|
|
GTK_WIDGET_REALIZED (widget)
|
|
|
|
5) if !GTK_WIDGET_TOPLEVEL (widget):
|
|
|
|
GTK_WIDGET_MAPPED (widget) => GTK_WIDGET_VISIBLE (widget)
|
|
=> GTK_WIDGET_CHILD_VISIBLE (widget)
|
|
=> GTK_WIDGET_REALIZED (widget)
|
|
|
|
widget->parent && GTK_WIDGET_MAPPED (widget->parent) &&
|
|
GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_CHILD_VISIBLE
|
|
<=> GTK_WIDGET_MAPPED (widget)
|
|
|
|
Note:, the definition
|
|
|
|
[ GTK_WIDGET_DRAWABLE = GTK_WIDGET_VISIBLE && GTK_WIDGET_MAPPED
|
|
is made in gtkwidget.h, but by 3) and 5),
|
|
|
|
GTK_WIDGET_MAPPED => GTK_WIDGET_VISIBLE
|
|
]
|
|
|
|
6) GTK_REDRAW_PENDING => GTK_WIDGET_REALIZED
|
|
GTK_RESIZE_PENDING => "
|
|
GTK_LEAVE_PENDING => "
|
|
GTK_RESIZE_NEEDED => "
|
|
|
|
III. How states are changed:
|
|
----------------------------
|
|
|
|
How can the user control the state of a widget:
|
|
-----------------------------------------------
|
|
|
|
(In the following, set flag means set the flag, do appropriate
|
|
actions, and enforce above invariants)
|
|
|
|
gtk_widget_show:
|
|
if !GTK_DESTROYED sets GTK_VISIBLE
|
|
|
|
gtk_widget_hide:
|
|
if !GTK_VISIBLE for widget
|
|
|
|
gtk_widget_destroy:
|
|
sets GTK_DESTROYED
|
|
For a top-level widget
|
|
|
|
gtk_widget_realize:
|
|
if !GTK_DESTROYED sets GTK_REALIZED
|
|
- Calling gtk_widget_realize when the widget is not a descendant
|
|
of a toplevel is an ERROR.
|
|
|
|
gtk_container_add (container, widget) [ and container-specific variants ]
|
|
Sets widget->parent
|
|
|
|
gtk_container_remove (container, widget)
|
|
unsets widget->parent
|
|
|
|
gtk_widget_reparent (widget, new_parent)
|
|
Equivalent to removing widget from old parent and adding it to
|
|
the new parent, except that the widget will not be temporarily
|
|
unrealized if both the old parent and the new parent are realized.
|
|
|
|
|
|
gtk_widget_unrealize
|
|
gtk_widget_map
|
|
gtk_widget_unmap
|
|
|
|
These functions are not meant to be used by applications - they
|
|
are used only by GTK and widgets to enforce invariants on the
|
|
state.
|
|
|
|
When The X window corresponding to a GTK window is destroyed:
|
|
-------------------------------------------------------------
|
|
|
|
gtk_widget_destroy is called (as above).
|
|
|
|
|
|
|
|
IV. Responsibilities of widgets
|
|
--------------------------------
|
|
|
|
Adding to a container
|
|
---------------------
|
|
|
|
When a widget is added to a container, the container:
|
|
|
|
1) calls gtk_widget_set_parent_window (widget, window) if
|
|
the widget is being added to something other than container->window
|
|
2) calls gtk_widget_set_parent (widget, container)
|
|
|
|
Removing from a container
|
|
-------------------------
|
|
|
|
When a widget is removed to a container, the container:
|
|
|
|
1) Calls gtk_widget_unparent (widget)
|
|
2) Queues a resize.
|
|
|
|
Notes:
|
|
|
|
gtk_widget_unparent unrealizes the widget except in the
|
|
special case GTK_IN_REPARENT is set.
|
|
|
|
|
|
At widget creation
|
|
------------------
|
|
|
|
Widgets are created in an unrealized state.
|
|
|
|
1) The widget should allocate and initialize needed data structures
|
|
|
|
|
|
The Realize signal
|
|
------------------
|
|
|
|
When a widget receives the "realize" signal it should:
|
|
|
|
NO_WINDOW widgets: (probably OK to use default handler)
|
|
|
|
1) set the realized flag
|
|
2) set widget->window
|
|
widget->window = gtk_widget_get_parent_window (widget);
|
|
g_object_ref (widget->window);
|
|
3) attach the widget's style
|
|
|
|
widget->style = gtk_style_attach (widget->style, widget->window);
|
|
|
|
widget with window(s)
|
|
|
|
1) set the REALIZED flag
|
|
2) create windows with the parent obtained from
|
|
gtk_widget_get_parent_window (widget);
|
|
3) attach the widget's style
|
|
4) set the background color for the new window based on the style
|
|
|
|
The Map signal
|
|
--------------
|
|
|
|
1) Set the MAPPED flag
|
|
2) If the widget has any windows, gdk_window_show those windows
|
|
3) call gtk_widget_map for all child widgets that are
|
|
VISIBLE, CHILD_VISIBLE and !MAPPED. (A widget will only
|
|
be !CHILD_VISIBLE if the container set it that way, so
|
|
most containers will not have to check this.)
|
|
3) Do any other functions related to putting the widget onscreen.
|
|
(for instance, showing extra popup windows...)
|
|
|
|
The Unmap signal
|
|
----------------
|
|
|
|
When a widget receives the unmap signal, it must:
|
|
|
|
1) If the widget has a window, gdk_window_hide that window,
|
|
2) If the widget does not have a window, unmap all child widgets
|
|
3) Do any other functions related to taking the widget offscreen
|
|
(for instance, removing popup windows...)
|
|
4) Unset GTK_MAPPED
|
|
|
|
|
|
The Unrealize signal
|
|
--------------------
|
|
|
|
When a widget receives the unrealize signal, it must
|
|
|
|
1) For any windows other than widget->window do:
|
|
|
|
gdk_window_set_user_data (window, NULL);
|
|
gdk_window_destroy (window);
|
|
|
|
2) Call the parent's unrealize handler
|
|
|
|
|
|
The Widget class unrealize handler will take care of unrealizing
|
|
all children if necessary. [should this be made consistent with
|
|
unmap???]
|
|
|
|
|
|
The Destroy Signal
|
|
------------------
|
|
|
|
Commentary:
|
|
|
|
The destroy signal probably shouldn't exist at all. A widget
|
|
should merely be unrealized and removed from its parent
|
|
when the user calls gtk_widget_destroy or a GDK_DESTROY event
|
|
is received. However, a large body of code depends on
|
|
getting a definitive signal when a widget goes away.
|
|
|
|
That could be put in the finalization step, but, especially
|
|
with language bindings, the cleanup step may need to refer
|
|
back to the widget. (To use gtk_widget_get_data, for instance)
|
|
If it does so via a pointer in a closure (natural for
|
|
Scheme, or Perl), then the finalization procedure will never
|
|
be called.
|
|
|
|
Also, if we made that the finalization step, we would have
|
|
to propagate the GDK_DESTROY event in any case, since it is
|
|
at that point at which user-visible actions need to be taken.
|
|
|
|
|
|
When a widget receives the destroy signal, it must:
|
|
|
|
1) If the widget "owns" any widgets other than its child
|
|
widgets, (for instance popup windows) it should
|
|
call gtk_widget_destroy () for them.
|
|
|
|
2) Call the parent class's destroy handler.
|
|
|
|
|
|
The "destroy" signal will only be received once. A widget
|
|
will never receive any other signals after the destroy
|
|
signal (but see the section on "Finalize" below)
|
|
|
|
The widget must handle calls to all publically accessible
|
|
functions in an innocuous manner even after a "destroy"
|
|
signal. (A widget can assume that it will not be realized
|
|
after a "destroy" signal is received, which may simplify
|
|
handling this requirement)
|
|
|
|
|
|
The Finalize Pseudo-signal
|
|
--------------------------
|
|
|
|
The finalize pseudo-signal is received after all references
|
|
to the widget have been removed. The finalize callback
|
|
cannot make any GTK calls with the widget as a parameter.
|
|
|
|
1) Free any memory allocated by the widget. (But _not_
|
|
the widget structure itself.
|
|
|
|
2) Call the parent class's finalize signal
|
|
|
|
|
|
A note on chaining "destroy" signals and finalize signals:
|
|
---------------------------------------------------------
|
|
|
|
This is done by code like:
|
|
|
|
if (GTK_OBJECT_CLASS (parent_class)->destroy)
|
|
(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
|
|
|
|
It may not be completely obvious why this works. Note
|
|
that parent_class is a static variable on a per-class
|
|
basis. So say: we have
|
|
|
|
GtkFoo <- GtkBar <- GtkWidget <-GtkObject
|
|
|
|
And that Foo, Widget, and Object all have destructors, but
|
|
not Bar.
|
|
|
|
Then gtk_foo_destroy will call gtk_widget_destroy (because
|
|
it was not overridden in the Bar class structure) and
|
|
gtk_widget_destroy will call gtk_object_destroy because
|
|
the parent_class variable referenced by gtk_foo_destroy is the
|
|
static variable in gtkwidget.c: GtkObjectClass.
|