mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-28 22:41:43 +00:00
0f24fddaf7
We need to adapt to both the change in the name of the project, and to the name change in the pkg-config file.
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 GdkSurface.
|
|
|
|
GTK_NO_WINDOW:
|
|
This flag is indicative for a widget that does not provide
|
|
its own GdkSurface. Visible action (e.g. drawing) is performed
|
|
on the parent's GdkSurface.
|
|
|
|
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 GdkSurface (XWindow).
|
|
|
|
GTK_MAPPED:
|
|
Set by gtk_widget_map, unset by gtk_widget_unmap.
|
|
May only be set if GTK_WIDGET_REALIZED (widget).
|
|
Means: gdk_surface_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_surface (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_surface (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_surface (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_surface_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_surface_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_surface_set_user_data (window, NULL);
|
|
gdk_surface_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.
|