 07e7719441
			
		
	
	07e7719441
	
	
	
		
			
			2006-10-08 Matthias Clasen <mclasen@redhat.com> * Apply a cleanup patch by Kjartan Maraas (#341812)
		
			
				
	
	
		
			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);
 | |
|       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
 | |
|   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.
 |