summaryrefslogtreecommitdiff
path: root/docs/widget_states.txt
blob: 37aae7691e05ddf33534804d45a099204a69f2d6 (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
This file contains some notes as to how the widget system does
and should work. It consists of three parts:

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

 II) Some notes about the ways that a widget changes states

 III) A list of responsibilities of various widget signals when
    the states change.

Any necessary actions which is implied by the I) section but not
mentioned in III) are the responsibility of the core GTK code, 
which is roughly defined as:

  gtkobject.c
  gtkwidget.c
  gtkcontainer.c
  gtkmain.c

I. Invariants:
-------------

1)  GTK_DESTROYED => !GTK_REALIZED
                  => !GTK_VISIBLE
[ The latter is not currently in place, but it should be ] 
 
2) GTK_MAPPED => GTK_REALIZED

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)

(When a widget is force-unrealized, to maintain this invariant,
 the widget is removed from its parent. Not currently implemented)

5) if !GTK_WIDGET_TOPLEVEL (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

II. 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_unrealize
 sets !GTK_REALIZED for widget
(can we say this is never to be called from user code?)

gtk_widget_(un)map are not to be called from applications.
(can we get rid of them completely?)


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

gtk_widget_destroy is called (as above).


III. 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, realizes widget
  5) Queues a resize

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. (Special case)

 gtk_widget_unparent is quite different than
 gtk_widget_set_parent (widget, NULL) which is for setting
   up toplevel windows.


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 a window, gdk_window_show that window
  3) If the widget does not hae a window, then map all child
     windows.
  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

Note: Could we do a better job at providing default map/unmap handlers?

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


As a side effect of the unrealize signal, the widget will be
removed from its parent


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