widget_system.txt 15.7 KB
Newer Older
Tim Janik's avatar
Tim Janik committed
1 2 3
Notes about the inner workings of the widget system of GTK+
===========================================================

4 5 6
This file contains some notes as to how the widget system does
and should work. It consists of three parts:

7 8 9
 I) A description of the meaning of the various flags

 II) A list of invariants about the states of the widgets. 
10
    (Throughout this document, we refer to the states of the
Tim Janik's avatar
Tim Janik committed
11
     widgets by referring to the flags for GtkWidget)
12

13
 III) Some notes about the ways that a widget changes states
14

15
 IV) A list of responsibilities of various widget signals when
16 17
    the states change.

18 19 20
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:
21 22 23 24 25

  gtkobject.c
  gtkwidget.c
  gtkcontainer.c
  gtkmain.c
26 27 28 29 30 31
  gtksignal.c

Section II is mostly of interest to those maintaining GTK, the
other sections may also be interesting to people writing
new widgets.

Tim Janik's avatar
Tim Janik committed
32 33
Main outline:
	- Owen Taylor <owt1@cornell.edu>
34
	  1998/02/03
Tim Janik's avatar
Tim Janik committed
35 36 37

Flag descriptions:
	- Tim Janik <timj@gimp.org>
38
	  1998/02/04
39 40 41 42

I. Flags
--------

Tim Janik's avatar
Tim Janik committed
43 44 45 46 47
GtkObject:

GTK_DESTROYED:
	This flagged is set for a GtkObject right before its
	destruction code is executed. Its main use is the
48
	prevention of multiple destruction invocations.
Tim Janik's avatar
Tim Janik committed
49 50 51 52 53 54 55 56 57 58 59 60 61 62
	
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:
63
	Widgets without a real parent, as there are GtkWindows and
Tim Janik's avatar
Tim Janik committed
64 65 66 67 68 69
	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
70
	on the parent's GdkWindow.
Tim Janik's avatar
Tim Janik committed
71

72
GTK_REALIZED:
Tim Janik's avatar
Tim Janik committed
73 74 75 76
	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).
77 78

GTK_MAPPED:
Tim Janik's avatar
Tim Janik committed
79 80 81
	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).
82 83

GTK_VISIBLE:
Tim Janik's avatar
Tim Janik committed
84 85 86
	Set by gtk_widget_show.
	Implies that a widget will be flagged GTK_MAPPED as soon as its
	parent is mapped.
87
!GTK_VISIBLE:
Tim Janik's avatar
Tim Janik committed
88 89 90
	Set by gtk_widget_hide.
	Implies that a widget is not onscreen, therefore !GTK_MAPPED.

91 92 93 94 95 96
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().
97
        (Should be very rarely necessary.)
98

Tim Janik's avatar
Tim Janik committed
99 100
GTK_SENSITIVE:
	Set and unset by gtk_widget_set_sensitive.
101
	The sensitivity of a widget determines whether it will receive
Tim Janik's avatar
Tim Janik committed
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
	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.
117
	This flag determines whether a widget is able to handle focus grabs.
Tim Janik's avatar
Tim Janik committed
118 119 120

GTK_HAS_FOCUS:
	This flag will be set by gtk_widget_grab_focus for widgets that also
121
	have GTK_CAN_FOCUS set. The flag will be unset once another widget
Tim Janik's avatar
Tim Janik committed
122 123 124 125 126
	grabs the focus.
	
GTK_CAN_DEFAULT:
GTK_HAS_DEFAULT:
	These two flags are mostly equal in functionality to their *_FOCUS
127
	counterparts, but for the default widget.
128

Tim Janik's avatar
Tim Janik committed
129 130 131 132 133 134
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:
135 136 137 138 139 140 141 142 143
	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.
144 145 146 147 148 149 150 151

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.

Tim Janik's avatar
Tim Janik committed
152 153 154 155 156 157

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
158
	which share a global user style from the ones which got a certain
Tim Janik's avatar
Tim Janik committed
159 160
	style assign from outside the toolkit.
	
161
GTK_RESIZE_PENDING:
Tim Janik's avatar
Tim Janik committed
162
	First, this is only valid for GtkContainers.
163
	[some of the code should move to gtkcontainer.c therefore]
Tim Janik's avatar
Tim Janik committed
164
	Relies on GTK_WIDGET_REALIZED(widget)
165
	[this is not really enforced throughout the code, but should
166
	 be. it only requires a few checks for GTK_WIDGET_REALIZED and
167 168
	 minor changes to gtk_widget_unrealize, we can then remove the check
	 in gtk_widget_real_destroy]
Tim Janik's avatar
Tim Janik committed
169
	Means: there is an idle handler waiting for the container to
170 171 172
	resize it.

GTK_RESIZE_NEEDED:
Tim Janik's avatar
Tim Janik committed
173 174 175 176 177
	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
178
	its _toplevel_ container (keep this in mind for GtkViewport).
179
	Remark: this flag is also used internally by gtkwindow.c during
Tim Janik's avatar
Tim Janik committed
180
	the evaluation of resizing worthy widgets.
181 182

GTK_LEAVE_PENDING:
Tim Janik's avatar
Tim Janik committed
183 184 185
	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.
186 187
	[this should be made relying on GTK_REALIZED]

Tim Janik's avatar
Tim Janik committed
188 189 190 191 192 193 194 195 196 197
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.

198 199 200 201 202
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
203
        however, widgets with this flag set are only added to the resize 
204 205 206 207 208 209 210 211
	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.
 
Tim Janik's avatar
Tim Janik committed
212
Related Macros:
213 214

GTK_WIDGET_DRAWABLE:
215
	This macro examines whether a widget is flagged as GTK_WIDGET_VISIBLE
Tim Janik's avatar
Tim Janik committed
216 217
	and GTK_WIDGET_MAPPED.
	Means: it _makes sense_ to draw in a widgets window.
218

Tim Janik's avatar
Tim Janik committed
219 220
GTK_WIDGET_IS_SENSITIVE:
	This macro tells the real sensitivity state of a widget. It returns
221
	whether both the widget and all its parents are in sensitive state.
222 223 224 225 226 227 228 229 230 231 232 233


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
Tim Janik's avatar
Tim Janik committed
234
                    (equivalent to A => B and A <= B)
235 236 237 238


1)  GTK_WIDGET_DESTROYED (widget) => !GTK_WIDGET_REALIZED (widget)
                                  => !GTK_WIDGET_VISIBLE (widget)
239 240
[ The latter is not currently in place, but it should be ] 
 
241
2) GTK_WIDGET_MAPPED (widget) => GTK_WIDGET_REALIZED (widget)
242 243 244 245 246 247 248 249 250 251

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):

252
   GTK_WIDGET_MAPPED (widget) => GTK_WIDGET_VISIBLE (widget)
253
                              => GTK_WIDGET_CHILD_VISIBLE (widget)
254 255 256
                              => GTK_WIDGET_REALIZED (widget)

   widget->parent && GTK_WIDGET_MAPPED (widget->parent) && 
257 258
     GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_CHILD_VISIBLE 
       => GTK_WIDGET_MAPPED (widget)
259 260 261

Note:, the definition

262
[  GTK_WIDGET_DRAWABLE = GTK_WIDGET_VISIBLE && GTK_WIDGET_MAPPED
263
   is made in gtkwidget.h, but by 3) and 5), 
264
     
265 266 267
      GTK_WIDGET_MAPPED => GTK_WIDGET_VISIBLE
]

268 269 270 271
6) GTK_REDRAW_PENDING => GTK_WIDGET_REALIZED
   GTK_RESIZE_PENDING =>         "
   GTK_LEAVE_PENDING  =>         "
   GTK_RESIZE_NEEDED  =>         "
272

273 274
III. How states are changed:
----------------------------
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293

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
294
- Calling gtk_widget_realize when the widget is not a descendant
295 296 297 298 299 300 301 302
  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

303 304 305 306
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.
307 308


309 310 311
gtk_widget_unrealize
gtk_widget_map
gtk_widget_unmap
312

313 314 315
These functions are not meant to be used by applications - they
are used only by GTK and widgets to enforce invariants on the
state.
316 317 318 319 320 321 322

When The X window corresponding to a GTK window is destroyed:
-------------------------------------------------------------

gtk_widget_destroy is called (as above).


323 324

IV. Responsibilities of widgets
325 326 327 328 329 330 331
--------------------------------

Adding to a container
---------------------

When a widget is added to a container, the container:

332
  1) calls gtk_widget_set_parent_window (widget, window) if 
333
     the widget is being added to something other than container->window
334
  2) calls gtk_widget_set_parent (widget, container)
335 336 337 338 339 340 341

Removing from a container
-------------------------

When a widget is removed to a container, the container:

  1) Calls gtk_widget_unparent (widget)
Owen Taylor's avatar
Owen Taylor committed
342
  2) Queues a resize.
343 344 345 346

Notes:

 gtk_widget_unparent unrealizes the widget except in the 
347
   special case GTK_IN_REPARENT is set.
348 349 350 351 352 353 354 355 356


At widget creation
------------------

Widgets are created in an unrealized state. 

 1) The widget should allocate and initialize needed data structures

357

358 359 360
The Realize signal
------------------

361
When a widget receives the "realize" signal it should:
362 363 364 365 366 367

 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);
368
      g_object_ref (widget->window);
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
  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
385
  2) If the widget has any windows, gdk_window_show those windows
Owen Taylor's avatar
Owen Taylor committed
386
  3) call gtk_widget_map for all child widgets that are 
387 388 389
     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.)
390 391 392 393 394 395 396 397 398 399 400 401
  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...)
402
 4) Unset GTK_MAPPED
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417


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


418 419 420
The Widget class unrealize handler will take care of unrealizing
all children if necessary. [should this be made consistent with
unmap???]
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449


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
450
     call gtk_widget_destroy () for them.
451 452 453 454 455 456

  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
457
signal (but see the section on "Finalize" below)
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475

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.

476
2) Call the parent class's finalize signal
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500


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.