summaryrefslogtreecommitdiff
path: root/docs/widget_system.txt
blob: 6607aa27689bbbdd3ddb6ceb047efb077f6dd7c6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
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.

                            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)
[ 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_REALIZED (widget)

   widget->parent && GTK_WIDGET_MAPPED (widget->parent) && 
     GTK_WIDGET_VISIBLE (widget) => GTK_WIDGET_MAPPED (widget)

Note:, the definition

[  GTK_WIDGET_DRAWABLE = GTK_WIDGET_VISIBLE && GTK_WIDGET_MAPPED
   is made in gtkwidget.c, but by 3) and 5), 
     
      GTK_WIDGET_MAPPED => GTK_WIDGET_VISIBLE
]



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

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 (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
  4) if container is mapped, and not widget and widget is GTK_VISIBLE,
     maps widget
  5) Queues a resize if the widget is mapped

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


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

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.