1998-02-01 08:22:51 +00:00
|
|
|
This file contains some notes as to how the widget system does
|
|
|
|
and should work. It consists of three parts:
|
|
|
|
|
1998-02-03 23:31:21 +00:00
|
|
|
I) A description of the meaning of the various flags
|
|
|
|
|
|
|
|
II) A list of invariants about the states of the widgets.
|
1998-02-01 08:22:51 +00:00
|
|
|
(Throughout this document, we refer to the states of the
|
|
|
|
widgets by referring to the flags for gtkwidget)
|
|
|
|
|
1998-02-03 23:31:21 +00:00
|
|
|
III) Some notes about the ways that a widget changes states
|
1998-02-01 08:22:51 +00:00
|
|
|
|
1998-02-03 23:31:21 +00:00
|
|
|
IV) A list of responsibilities of various widget signals when
|
1998-02-01 08:22:51 +00:00
|
|
|
the states change.
|
|
|
|
|
1998-02-03 23:31:21 +00:00
|
|
|
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:
|
1998-02-01 08:22:51 +00:00
|
|
|
|
|
|
|
gtkobject.c
|
|
|
|
gtkwidget.c
|
|
|
|
gtkcontainer.c
|
|
|
|
gtkmain.c
|
1998-02-03 23:31:21 +00:00
|
|
|
gtksignal.c
|
|
|
|
|
|
|
|
Section II is mostly of interest to those maintaining GTK, the
|
|
|
|
other sections may also be interesting to people writing
|
|
|
|
new widgets.
|
|
|
|
|
|
|
|
Owen Taylor <owt1@cornell.edu>
|
|
|
|
98/02/03
|
|
|
|
|
|
|
|
I. Flags
|
|
|
|
--------
|
|
|
|
|
|
|
|
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 GTK_MAPPED as soon as it's
|
|
|
|
parent is GTK_MAPPED.
|
|
|
|
!GTK_VISIBLE:
|
|
|
|
set by gtk_widget_hide
|
|
|
|
implies that a widget is not onscreen, therefore !GTK_MAPPED
|
|
|
|
|
|
|
|
GTK_REDRAW_PENDING:
|
|
|
|
relies on GTK_WIDGET_MAPPED(widget)
|
|
|
|
[this is not really enforced throughout the code, but should
|
|
|
|
be. it only requires a few checks for GTK_WIDGET_MAPPED and
|
|
|
|
minor changes to gtk_widget_unmap, we can then remove the check
|
|
|
|
in gtk_widget_real_destroy]
|
|
|
|
means: there is an idle handler waiting for the widget, that
|
|
|
|
will cause a full redraw (gtk_widget_draw(widget, NULL))
|
|
|
|
|
|
|
|
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_RELIZED 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)
|
|
|
|
[puhhh, this isn't rely enforced either..., bla bla ...remove check
|
|
|
|
in gtk_widget_real_destroy]
|
|
|
|
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 internaly by gtkwindow.c.
|
|
|
|
|
|
|
|
GTK_LEAVE_PENDING:
|
|
|
|
a widget is flagged as such if there is a leave_notify event
|
|
|
|
pending for it. it receives this event regardless of a grab or
|
|
|
|
its current sensitivity.
|
|
|
|
[this should be made relying on GTK_REALIZED]
|
|
|
|
|
|
|
|
GTK_HAS_GRAB:
|
|
|
|
set by gtk_grab_add, unset by gtk_grab_remove
|
|
|
|
means: widget is in the grab_widgets stack.
|
|
|
|
|
|
|
|
GTK_WIDGET_DRAWABLE:
|
|
|
|
a widget is flagged as GTK_WIDGET_VISIBLE and GTK_WIDGET_MAPPED
|
|
|
|
means: it _makes sense_ to draw in a widgets window.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 B <= A)
|
|
|
|
|
|
|
|
|
|
|
|
1) GTK_WIDGET_DESTROYED (widget) => !GTK_WIDGET_REALIZED (widget)
|
|
|
|
=> !GTK_WIDGET_VISIBLE (widget)
|
1998-02-01 08:22:51 +00:00
|
|
|
[ The latter is not currently in place, but it should be ]
|
|
|
|
|
1998-02-03 23:31:21 +00:00
|
|
|
2) GTK_WIDGET_MAPPED (widget) => GTK_WIDGET_REALIZED (widget)
|
1998-02-01 08:22:51 +00:00
|
|
|
|
|
|
|
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):
|
|
|
|
|
1998-02-03 23:31:21 +00:00
|
|
|
GTK_WIDGET_MAPPED (widget) => GTK_WIDGET_VISIBLE (widget)
|
|
|
|
=> GTK_WIDGET_REALIZED (widget)
|
|
|
|
|
|
|
|
widget->parent && GTK_WIDGET_MAPPED (widget->parent) &&
|
|
|
|
GTK_WIDGET_VISIBLE (widget) => GTK_WIDGET_MAPPED (widget)
|
1998-02-01 08:22:51 +00:00
|
|
|
|
|
|
|
Note:, the definition
|
|
|
|
|
1998-02-03 23:31:21 +00:00
|
|
|
[ GTK_WIDGET_DRAWABLE = GTK_WIDGET_VISIBLE && GTK_WIDGET_MAPPED
|
|
|
|
is made in gtkwidget.c, but by 3) and 5),
|
1998-02-01 08:22:51 +00:00
|
|
|
|
1998-02-03 23:31:21 +00:00
|
|
|
GTK_WIDGET_MAPPED => GTK_WIDGET_VISIBLE
|
|
|
|
]
|
|
|
|
|
|
|
|
|
1998-02-01 08:22:51 +00:00
|
|
|
|
1998-02-03 23:31:21 +00:00
|
|
|
III. How states are changed:
|
|
|
|
----------------------------
|
1998-02-01 08:22:51 +00:00
|
|
|
|
|
|
|
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 descendent
|
|
|
|
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
|
|
|
|
|
1998-02-03 23:31:21 +00:00
|
|
|
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.
|
1998-02-01 08:22:51 +00:00
|
|
|
|
|
|
|
|
1998-02-03 23:31:21 +00:00
|
|
|
gtk_widget_unrealize
|
|
|
|
gtk_widget_map
|
|
|
|
gtk_widget_unmap
|
1998-02-01 08:22:51 +00:00
|
|
|
|
1998-02-03 23:31:21 +00:00
|
|
|
These functions are not meant to be used by applications - they
|
|
|
|
are used only by GTK and widgets to enforce invariants on the
|
|
|
|
state.
|
1998-02-01 08:22:51 +00:00
|
|
|
|
|
|
|
When The X window corresponding to a GTK window is destroyed:
|
|
|
|
-------------------------------------------------------------
|
|
|
|
|
|
|
|
gtk_widget_destroy is called (as above).
|
|
|
|
|
|
|
|
|
1998-02-03 23:31:21 +00:00
|
|
|
|
|
|
|
IV. Responsibilities of widgets
|
1998-02-01 08:22:51 +00:00
|
|
|
--------------------------------
|
|
|
|
|
|
|
|
Adding to a container
|
|
|
|
---------------------
|
|
|
|
|
|
|
|
When a widget is added to a container, the container:
|
|
|
|
|
|
|
|
1) calls gtk_widget_set_parent (widget, container)
|
|
|
|
2) calls gtk_widget_set_parent_window (widget, window) if
|
|
|
|
the widget is being added to something other than container->window
|
|
|
|
3) if container is realized, and not widget, realizes widget
|
1998-02-03 23:31:21 +00:00
|
|
|
4) if container is mapped, and not widget and widget is GTK_VISIBLE,
|
|
|
|
maps widget
|
|
|
|
5) Queues a resize if the widget is mapped
|
1998-02-01 08:22:51 +00:00
|
|
|
|
|
|
|
Note: It would be nice to remove 3) and 4) out of widget specific code
|
|
|
|
since they are of the invariant-enforcing nature, but it is
|
|
|
|
a bit hard, since they can't be done until after 2)
|
|
|
|
|
|
|
|
|
|
|
|
Removing from a container
|
|
|
|
-------------------------
|
|
|
|
|
|
|
|
When a widget is removed to a container, the container:
|
|
|
|
|
|
|
|
1) Calls gtk_widget_unparent (widget)
|
|
|
|
2) Sets widget->parent to NULL
|
|
|
|
3) Queues a resize.
|
|
|
|
|
|
|
|
Notes:
|
|
|
|
|
|
|
|
gtk_widget_unparent unrealizes the widget except in the
|
1998-02-03 23:31:21 +00:00
|
|
|
special case GTK_IN_REPARENT is set.
|
1998-02-01 08:22:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
At widget creation
|
|
|
|
------------------
|
|
|
|
|
|
|
|
Widgets are created in an unrealized state.
|
|
|
|
|
|
|
|
1) The widget should allocate and initialize needed data structures
|
|
|
|
|
1998-02-03 23:31:21 +00:00
|
|
|
|
1998-02-01 08:22:51 +00:00
|
|
|
The Realize signal
|
|
|
|
------------------
|
|
|
|
|
|
|
|
When a widget recieves 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);
|
|
|
|
gdk_window_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
|
1998-02-03 23:31:21 +00:00
|
|
|
2) If the widget has any windows, gdk_window_show those windows
|
|
|
|
3) call gtk_widget_map for all child windows that are
|
|
|
|
VISIBLE and !MAPPED.
|
1998-02-01 08:22:51 +00:00
|
|
|
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...)
|
|
|
|
3) 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
|
|
|
|
|
|
|
|
|
1998-02-03 23:31:21 +00:00
|
|
|
The Widget class unrealize handler will take care of unrealizing
|
|
|
|
all children if necessary. [should this be made consistent with
|
|
|
|
unmap???]
|
1998-02-01 08:22:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
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
|
1998-02-03 23:31:21 +00:00
|
|
|
call gtk_widget_destroy () for them.
|
1998-02-01 08:22:51 +00:00
|
|
|
|
|
|
|
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 sectionalize 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.
|
|
|
|
|
1998-02-03 23:31:21 +00:00
|
|
|
2) Call the parent class's finalize signal
|
1998-02-01 08:22:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|