| 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. |