1 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> |
---|
2 | <HTML> |
---|
3 | <HEAD> |
---|
4 | <META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.9"> |
---|
5 | <TITLE>Didacticiel: Écriture de vos propres widgets </TITLE> |
---|
6 | <LINK HREF="gtk_tut_fr-20.html" REL=next> |
---|
7 | <LINK HREF="gtk_tut_fr-18.html" REL=previous> |
---|
8 | <LINK HREF="gtk_tut_fr.html#toc19" REL=contents> |
---|
9 | </HEAD> |
---|
10 | <BODY BGCOLOR="#FFFFFF"> |
---|
11 | <A HREF="gtk_tut_fr-20.html">Page suivante</A> |
---|
12 | <A HREF="gtk_tut_fr-18.html">Page précédente</A> |
---|
13 | <A HREF="gtk_tut_fr.html#toc19">Table des matières</A> |
---|
14 | <HR NOSHADE> |
---|
15 | <H2><A NAME="s19">19. Écriture de vos propres widgets </A></H2> |
---|
16 | |
---|
17 | <P> |
---|
18 | <H2><A NAME="ss19.1">19.1 Vue d'ensemble</A> |
---|
19 | </H2> |
---|
20 | |
---|
21 | <P>Bien que la distribution GTK fournisse de nombreux types de widgets |
---|
22 | qui devraient couvrir la plupart des besoins de base, il peut arriver |
---|
23 | un moment où vous aurez besoin de créer votre propre type de |
---|
24 | widget. Comme GTK utilise l'héritage de widget de façon intensive et |
---|
25 | qu'il y a déjà un widget ressemblant à celui que vous voulez, il est |
---|
26 | souvent possible de créer un nouveau type de widget en seulement |
---|
27 | quelques lignes de code. Mais, avant de travailler sur un nouveau |
---|
28 | widget, il faut vérifier d'abord que quelqu'un ne l'a pas déjà |
---|
29 | écrit. Ceci éviter la duplication des efforts et maintient au minimum |
---|
30 | le nombre de widgets, ce qui permet de garder la cohérence du code et |
---|
31 | de l'interface des différentes applications. Un effet de bord est que, |
---|
32 | lorsque l'on a créé un nouveau widget, il faut l'annoncer afin que les |
---|
33 | autres puissent en bénéficier. Le meilleur endroit pour faire cela |
---|
34 | est, sans doute, la <CODE>gtk-list</CODE>. |
---|
35 | <P> |
---|
36 | <H2><A NAME="ss19.2">19.2 Anatomie d'un widget</A> |
---|
37 | </H2> |
---|
38 | |
---|
39 | <P>Afin de créer un nouveau widget, il importe de comprendre comment |
---|
40 | fonctionnent les objets GTK. Cette section ne se veut être qu'un |
---|
41 | rapide survol. Consultez la documentation de référence pour plus de |
---|
42 | détails. |
---|
43 | <P> |
---|
44 | <P>Les widgets sont implantés selon une méthode orientée |
---|
45 | objet. Cependant, ils sont écrits en C standard. Ceci améliore |
---|
46 | beaucoup la portabilité et la stabilité, par contre cela signifie que |
---|
47 | celui qui écrit des widgets doit faire attention à certains détails |
---|
48 | d'implantation. Les informations communes à toutes les instances d'une |
---|
49 | classe de widget (tous les widgets boutons, par exemple) sont stockées |
---|
50 | dans la <EM>structure de la classe</EM>. Il n'y en a qu'une copie dans |
---|
51 | laquelle sont stockées les informations sur les signaux de la |
---|
52 | classe (fonctionnement identique aux fonctions virtuelles en C). Pour |
---|
53 | permettre l'héritage, le premier champ de la structure de classe doit |
---|
54 | être une copie de la structure de classe du père. La déclaration de la |
---|
55 | structure de classe de <EM>GtkButton</EM> ressemble à ceci : |
---|
56 | <P> |
---|
57 | <BLOCKQUOTE><CODE> |
---|
58 | <PRE> |
---|
59 | struct _GtkButtonClass |
---|
60 | { |
---|
61 | GtkContainerClass parent_class; |
---|
62 | |
---|
63 | void (* pressed) (GtkButton *button); |
---|
64 | void (* released) (GtkButton *button); |
---|
65 | void (* clicked) (GtkButton *button); |
---|
66 | void (* enter) (GtkButton *button); |
---|
67 | void (* leave) (GtkButton *button); |
---|
68 | }; |
---|
69 | </PRE> |
---|
70 | </CODE></BLOCKQUOTE> |
---|
71 | <P> |
---|
72 | <P>Lorsqu'un bouton est traité comme un container (par exemple, lorsqu'il |
---|
73 | change de taille), sa structure de classe peut être convertie en |
---|
74 | <EM>GtkContainerClass</EM> et les champs adéquats utilisés pour gérer les |
---|
75 | signaux. |
---|
76 | <P> |
---|
77 | <P>Il y a aussi une structure pour chaque widget créé sur une base |
---|
78 | d'instance. Cette structure a des champs pour stocker les informations |
---|
79 | qui sont différentes pour chaque instance du widget. Nous l'appelerons |
---|
80 | <EM>structure d'objet</EM>. Pour la classe <EM>Button</EM>, elle ressemble |
---|
81 | à : |
---|
82 | <P> |
---|
83 | <BLOCKQUOTE><CODE> |
---|
84 | <PRE> |
---|
85 | struct _GtkButton |
---|
86 | { |
---|
87 | GtkContainer container; |
---|
88 | |
---|
89 | GtkWidget *child; |
---|
90 | |
---|
91 | guint in_button : 1; |
---|
92 | guint button_down : 1; |
---|
93 | }; |
---|
94 | </PRE> |
---|
95 | </CODE></BLOCKQUOTE> |
---|
96 | <P> |
---|
97 | <P>Notez que, comme pour la structure de classe, le premier champ est la |
---|
98 | structure d'objet de la classe parente, cette structure peut donc être |
---|
99 | convertie dans la structure d'objet de la classe parente si besoin |
---|
100 | est. |
---|
101 | <P> |
---|
102 | <H2><A NAME="ss19.3">19.3 Création d'un widget composé</A> |
---|
103 | </H2> |
---|
104 | |
---|
105 | <H3>Introduction</H3> |
---|
106 | |
---|
107 | <P>Un type de widget qui peut être intéressant à créer est un widget qui |
---|
108 | est simplement un agrégat d'autres widgets GTK. Ce type de widget ne |
---|
109 | fait rien qui ne pourrait être fait sans créer de nouveaux widgets, |
---|
110 | mais offre une méthode pratique pour empaqueter les éléments d'une |
---|
111 | interface utilisateur afin de la réutiliser facilement. Les widgets |
---|
112 | <EM>FileSelection</EM> et <EM>ColorSelection</EM> de la distribution standard |
---|
113 | sont des exemples de ce type de widget. |
---|
114 | <P> |
---|
115 | <P>L'exemple de widget que nous allons créer dans cette section créera un |
---|
116 | widget <EM>Tictactoe</EM>, un tableau de 3x3 boutons commutateurs qui |
---|
117 | déclenche un signal lorsque tous les boutons d'une ligne, d'une |
---|
118 | colonne, ou d'une diagonale sont pressés. |
---|
119 | <P> |
---|
120 | <H3>Choix d'une classe parent</H3> |
---|
121 | |
---|
122 | <P>La classe parent d'un widget composé est, typiquement, la classe |
---|
123 | container contenant tous les éléments du widget composé. Par exemple, |
---|
124 | la classe parent du widget <EM>FileSelection</EM> est la classe |
---|
125 | <EM>Dialog</EM>. Comme nos boutons seront mis sous la forme d'un tableau, |
---|
126 | il semble naturel d'utiliser la classe <EM>GtkTable</EM> comme |
---|
127 | parent. Malheureusement, cela ne peut marcher. La création d'un widget |
---|
128 | est divisée en deux fonctions -- <EM>WIDGETNAME_new()</EM> que |
---|
129 | l'utilisateur appelle, et <EM>WIDGETNAME_init()</EM> qui réalise le |
---|
130 | travail d'initialisation du widget indépendamment des paramètre passés |
---|
131 | à la fonction <CODE>_new()</CODE>. Les widgets fils n'appellent que la |
---|
132 | fonction <EM>_init</EM> de leur widget parent. Mais cette division du |
---|
133 | travail ne fonctionne pas bien avec les tableaux qui, lorsqu'ils sont |
---|
134 | créés, ont besoin de connaître leue nombre de lignes et de |
---|
135 | colonnes. Sauf à dupliquer la plupart des fonctionnalités de |
---|
136 | <EM>gtk_table_new()</EM> dans notre widget <EM>Tictactoe</EM>, nous ferions |
---|
137 | mieux d'éviter de le dériver de <EM>GtkTable</EM>. Pour cette raison, nous |
---|
138 | la dériverons plutôt de <EM>GtkVBox</EM> et nous placerons notre table |
---|
139 | dans la VBox. |
---|
140 | <P> |
---|
141 | <H3>The header file</H3> |
---|
142 | |
---|
143 | <P>Chaque classe de widget possède un fichier en-tête qui déclare les |
---|
144 | structures d'objet et de classe pour ce widget, en plus de fonctions |
---|
145 | publiques. Quelques caractéristiques méritent d'être indiquées. Afin |
---|
146 | d'éviter des définitions multiples, on enveloppe le fichier en-tête |
---|
147 | avec : |
---|
148 | <P> |
---|
149 | <BLOCKQUOTE><CODE> |
---|
150 | <PRE> |
---|
151 | #ifndef __TICTACTOE_H__ |
---|
152 | #define __TICTACTOE_H__ |
---|
153 | . |
---|
154 | . |
---|
155 | . |
---|
156 | #endif /* __TICTACTOE_H__ */ |
---|
157 | </PRE> |
---|
158 | </CODE></BLOCKQUOTE> |
---|
159 | <P>Et, pour faire plaisir aux programmes C++ qui inclueront ce fichier, on l'enveloppe aussi dans : |
---|
160 | <P> |
---|
161 | <BLOCKQUOTE><CODE> |
---|
162 | <PRE> |
---|
163 | #ifdef __cplusplus |
---|
164 | extern "C" { |
---|
165 | #endif /* __cplusplus */ |
---|
166 | . |
---|
167 | . |
---|
168 | . |
---|
169 | #ifdef __cplusplus |
---|
170 | } |
---|
171 | #endif /* __cplusplus */ |
---|
172 | </PRE> |
---|
173 | </CODE></BLOCKQUOTE> |
---|
174 | <P>En plus des fonctions et structures, nous déclarons trois macros |
---|
175 | standard, <CODE>TICTACTOE(obj)</CODE>, <CODE>TICTACTOE_CLASS(class)</CODE>, et |
---|
176 | <CODE>IS_TICTACTOE(obj)</CODE>, qui, respectivement, convertissent un pointeur |
---|
177 | en un pointeur vers une structure d'objet ou de classe, et vérifient |
---|
178 | si un objet est un widget Tictactoe. |
---|
179 | <P> |
---|
180 | <P>Voici le fichier en-tête complet : |
---|
181 | <P> |
---|
182 | <BLOCKQUOTE><CODE> |
---|
183 | <PRE> |
---|
184 | |
---|
185 | #ifndef __TICTACTOE_H__ |
---|
186 | #define __TICTACTOE_H__ |
---|
187 | |
---|
188 | #include <gdk/gdk.h> |
---|
189 | #include <gtk/gtkvbox.h> |
---|
190 | |
---|
191 | #ifdef __cplusplus |
---|
192 | extern "C" { |
---|
193 | #endif /* __cplusplus */ |
---|
194 | |
---|
195 | #define TICTACTOE(obj) GTK_CHECK_CAST (obj, tictactoe_get_type (), Tictactoe) |
---|
196 | #define TICTACTOE_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, tictactoe_get_type (), TictactoeClass) |
---|
197 | #define IS_TICTACTOE(obj) GTK_CHECK_TYPE (obj, tictactoe_get_type ()) |
---|
198 | |
---|
199 | |
---|
200 | typedef struct _Tictactoe Tictactoe; |
---|
201 | typedef struct _TictactoeClass TictactoeClass; |
---|
202 | |
---|
203 | struct _Tictactoe |
---|
204 | { |
---|
205 | GtkVBox vbox; |
---|
206 | |
---|
207 | GtkWidget *buttons[3][3]; |
---|
208 | }; |
---|
209 | |
---|
210 | struct _TictactoeClass |
---|
211 | { |
---|
212 | GtkVBoxClass parent_class; |
---|
213 | |
---|
214 | void (* tictactoe) (Tictactoe *ttt); |
---|
215 | }; |
---|
216 | |
---|
217 | guint tictactoe_get_type (void); |
---|
218 | GtkWidget* tictactoe_new (void); |
---|
219 | void tictactoe_clear (Tictactoe *ttt); |
---|
220 | |
---|
221 | #ifdef __cplusplus |
---|
222 | } |
---|
223 | #endif /* __cplusplus */ |
---|
224 | |
---|
225 | #endif /* __TICTACTOE_H__ */ |
---|
226 | </PRE> |
---|
227 | </CODE></BLOCKQUOTE> |
---|
228 | <P> |
---|
229 | <H3>La fonction <CODE>_get_type()</CODE></H3> |
---|
230 | |
---|
231 | <P>Continuons maintenant avec l'implantation de notre widget. La fonction |
---|
232 | centrale pour chaque widget est <EM>WIDGETNAME_get_type()</EM>. Cette |
---|
233 | fonction, lorsqu'elle est appelée pour la première fois, informe le |
---|
234 | GTK de la classe et récupère un ID permettant d'identifier celle-ci de |
---|
235 | façon unique. Lors des appels suivants, elle ne fait que retourner cet |
---|
236 | ID. |
---|
237 | <P> |
---|
238 | <BLOCKQUOTE><CODE> |
---|
239 | <PRE> |
---|
240 | guint |
---|
241 | tictactoe_get_type () |
---|
242 | { |
---|
243 | static guint ttt_type = 0; |
---|
244 | |
---|
245 | if (!ttt_type) |
---|
246 | { |
---|
247 | GtkTypeInfo ttt_info = |
---|
248 | { |
---|
249 | "Tictactoe", |
---|
250 | sizeof (Tictactoe), |
---|
251 | sizeof (TictactoeClass), |
---|
252 | (GtkClassInitFunc) tictactoe_class_init, |
---|
253 | (GtkObjectInitFunc) tictactoe_init, |
---|
254 | (GtkArgFunc) NULL, |
---|
255 | }; |
---|
256 | |
---|
257 | ttt_type = gtk_type_unique (gtk_vbox_get_type (), &ttt_info); |
---|
258 | } |
---|
259 | |
---|
260 | return ttt_type; |
---|
261 | } |
---|
262 | </PRE> |
---|
263 | </CODE></BLOCKQUOTE> |
---|
264 | <P> |
---|
265 | <P>La structure <EM>GtkTypeInfo</EM> est définie de la façon suivante : |
---|
266 | <P> |
---|
267 | <BLOCKQUOTE><CODE> |
---|
268 | <PRE> |
---|
269 | struct _GtkTypeInfo |
---|
270 | { |
---|
271 | gchar *type_name; |
---|
272 | guint object_size; |
---|
273 | guint class_size; |
---|
274 | GtkClassInitFunc class_init_func; |
---|
275 | GtkObjectInitFunc object_init_func; |
---|
276 | GtkArgFunc arg_func; |
---|
277 | }; |
---|
278 | </PRE> |
---|
279 | </CODE></BLOCKQUOTE> |
---|
280 | <P> |
---|
281 | <P>Les champs de cette structure s'expliquent d'eux-mêmes. Nous |
---|
282 | ignorerons le champ <EM>arg_func</EM> ici : il a un rôle important |
---|
283 | permettant aux options des widgets d'être correctement initialisées à |
---|
284 | partir des langages interprétés, mais cette fonctionnalité est encore |
---|
285 | très peu implantée. Lorsque GTK dispose d'une copie correctement |
---|
286 | remplie de cette structure, il sait comment créer des objets d'un type |
---|
287 | particulier de widget. |
---|
288 | <P> |
---|
289 | <H3>La fonction <EM>_class_init()</EM></H3> |
---|
290 | |
---|
291 | <P>La fonction <EM>WIDGETNAME_class_init()</EM> initialise les champs de la |
---|
292 | structure de classe du widget et configure tous les signaux de cette |
---|
293 | classe. Pour notre widget Tictactoe, cet appel est : |
---|
294 | <P> |
---|
295 | <BLOCKQUOTE><CODE> |
---|
296 | <PRE> |
---|
297 | |
---|
298 | enum { |
---|
299 | TICTACTOE_SIGNAL, |
---|
300 | LAST_SIGNAL |
---|
301 | }; |
---|
302 | |
---|
303 | static gint tictactoe_signals[LAST_SIGNAL] = { 0 }; |
---|
304 | |
---|
305 | static void |
---|
306 | tictactoe_class_init (TictactoeClass *class) |
---|
307 | { |
---|
308 | GtkObjectClass *object_class; |
---|
309 | |
---|
310 | object_class = (GtkObjectClass*) class; |
---|
311 | |
---|
312 | tictactoe_signals[TICTACTOE_SIGNAL] = gtk_signal_new ("tictactoe", |
---|
313 | GTK_RUN_FIRST, |
---|
314 | object_class->type, |
---|
315 | GTK_SIGNAL_OFFSET (TictactoeClass, tictactoe), |
---|
316 | gtk_signal_default_marshaller, GTK_ARG_NONE, 0); |
---|
317 | |
---|
318 | |
---|
319 | gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL); |
---|
320 | |
---|
321 | class->tictactoe = NULL; |
---|
322 | } |
---|
323 | </PRE> |
---|
324 | </CODE></BLOCKQUOTE> |
---|
325 | <P> |
---|
326 | <P>Notre widget n'a qu'un signal : "tictactoe", invoqué lorsqu'une |
---|
327 | ligne, une colonne ou une diagonale est complètement remplie. Tous les |
---|
328 | widgets composés n'ont pas besoin de signaux. Si vous lisez ceci pour |
---|
329 | la première fois, vous pouvez passer directement à la section suivante |
---|
330 | car les choses vont se compliquer un peu |
---|
331 | <P>La fonction : |
---|
332 | <P> |
---|
333 | <BLOCKQUOTE><CODE> |
---|
334 | <PRE> |
---|
335 | gint gtk_signal_new (gchar *name, |
---|
336 | GtkSignalRunType run_type, |
---|
337 | gint object_type, |
---|
338 | gint function_offset, |
---|
339 | GtkSignalMarshaller marshaller, |
---|
340 | GtkArgType return_val, |
---|
341 | gint nparams, |
---|
342 | ...); |
---|
343 | </PRE> |
---|
344 | </CODE></BLOCKQUOTE> |
---|
345 | <P>crée un nouveau signal. Les paramètres sont : |
---|
346 | <P> |
---|
347 | <UL> |
---|
348 | <LI> <EM>name</EM> : Le nom du signal signal.</LI> |
---|
349 | <LI> <EM>run_type</EM> : Indique si le gestionnaire par défaut doit être |
---|
350 | lancé avant ou après le gestionnaire de l'utilisateur. Le plus |
---|
351 | souvent, ce sera <CODE>GTK_RUN_FIRST</CODE>, ou <CODE>GTK_RUN_LAST</CODE>, bien qu'il |
---|
352 | y ait d'autres possibilités. |
---|
353 | </LI> |
---|
354 | <LI> <EM>object_type</EM> : L'ID de l'objet auquel s'applique ce signal |
---|
355 | (il s'appliquera aussi au descendants de l'objet). |
---|
356 | </LI> |
---|
357 | <LI> <EM>function_offset</EM> : L'offset d'un pointeur vers le |
---|
358 | gestionnaire par défaut dans la structure de classe. |
---|
359 | </LI> |
---|
360 | <LI> <EM>marshaller</EM> : Fonction utilisée pour invoquer le |
---|
361 | gestionnaire de signal. Pour les gestionnaires de signaux n'ayant pas |
---|
362 | d'autres paramètres que l'objet émetteur et les données utilisateur, |
---|
363 | on peut utiliser la fonction prédéfinie |
---|
364 | <EM>gtk_signal_default_marshaller()</EM>. |
---|
365 | </LI> |
---|
366 | <LI> <EM>return_val</EM> : Type de la valeur retournée. |
---|
367 | </LI> |
---|
368 | <LI> <EM>nparams</EM> : Nombre de paramètres du gestionnaire de signal |
---|
369 | (autres que les deux par défaut mentionnés plus haut). |
---|
370 | </LI> |
---|
371 | <LI> <EM>...</EM> : Types des paramètres.</LI> |
---|
372 | </UL> |
---|
373 | <P>Lorsque l'on spécifie les types, on utilise l'énumération |
---|
374 | <EM>GtkArgType</EM> : |
---|
375 | <P> |
---|
376 | <BLOCKQUOTE><CODE> |
---|
377 | <PRE> |
---|
378 | typedef enum |
---|
379 | { |
---|
380 | GTK_ARG_INVALID, |
---|
381 | GTK_ARG_NONE, |
---|
382 | GTK_ARG_CHAR, |
---|
383 | GTK_ARG_SHORT, |
---|
384 | GTK_ARG_INT, |
---|
385 | GTK_ARG_LONG, |
---|
386 | GTK_ARG_POINTER, |
---|
387 | GTK_ARG_OBJECT, |
---|
388 | GTK_ARG_FUNCTION, |
---|
389 | GTK_ARG_SIGNAL |
---|
390 | } GtkArgType; |
---|
391 | </PRE> |
---|
392 | </CODE></BLOCKQUOTE> |
---|
393 | <P> |
---|
394 | <P><EM>gtk_signal_new()</EM> retourne un identificateur entier pour le |
---|
395 | signal, que l'on stocke dans le tableau <EM>tictactoe_signals</EM>, indicé |
---|
396 | par une énumération (conventionnellement, les éléments de |
---|
397 | l'énumération sont le nom du signal, en majuscules, mais, ici, il y |
---|
398 | aurait un conflit avec la macro <CODE>TICTACTOE()</CODE>, nous l'appellerons |
---|
399 | donc <CODE>TICTACTOE_SIGNAL</CODE> à la place. |
---|
400 | <P>Après avoir créé nos signaux, nous devons demander à GTK d'associer |
---|
401 | ceux-ci à la classe Tictactoe. Ceci est fait en appelant |
---|
402 | <EM>gtk_object_class_add_signals()</EM>. Puis nous configurons le pointeur |
---|
403 | qui pointe sur le gestionnaire par défaut du signal "tictactoe" à |
---|
404 | NULL, pour indiquer qu'il n'y a pas d'action par défaut. |
---|
405 | <P> |
---|
406 | <H3>La fonction <EM>_init()</EM></H3> |
---|
407 | |
---|
408 | <P> |
---|
409 | <P>Chaque classe de widget a aussi besoin d'une fonction pour initialiser |
---|
410 | la structure d'objet. Habituellement, cette fonction a le rôle, plutôt |
---|
411 | limité, d'initialiser les champs de la structure avec des valeurs par |
---|
412 | défaut. Cependant, pour les widgets composés, cette fonction crée |
---|
413 | aussi les widgets composants. |
---|
414 | <P> |
---|
415 | <BLOCKQUOTE><CODE> |
---|
416 | <PRE> |
---|
417 | |
---|
418 | static void |
---|
419 | tictactoe_init (Tictactoe *ttt) |
---|
420 | { |
---|
421 | GtkWidget *table; |
---|
422 | gint i,j; |
---|
423 | |
---|
424 | table = gtk_table_new (3, 3, TRUE); |
---|
425 | gtk_container_add (GTK_CONTAINER(ttt), table); |
---|
426 | gtk_widget_show (table); |
---|
427 | |
---|
428 | for (i=0;i<3; i++) |
---|
429 | for (j=0;j<3; j++) |
---|
430 | { |
---|
431 | ttt->buttons[i][j] = gtk_toggle_button_new (); |
---|
432 | gtk_table_attach_defaults (GTK_TABLE(table), ttt->buttons[i][j], |
---|
433 | i, i+1, j, j+1); |
---|
434 | gtk_signal_connect (GTK_OBJECT (ttt->buttons[i][j]), "toggled", |
---|
435 | GTK_SIGNAL_FUNC (tictactoe_toggle), ttt); |
---|
436 | gtk_widget_set_usize (ttt->buttons[i][j], 20, 20); |
---|
437 | gtk_widget_show (ttt->buttons[i][j]); |
---|
438 | } |
---|
439 | } |
---|
440 | </PRE> |
---|
441 | </CODE></BLOCKQUOTE> |
---|
442 | <P> |
---|
443 | <H3>Et le reste...</H3> |
---|
444 | |
---|
445 | <P> |
---|
446 | <P>Il reste une fonction que chaque widget (sauf pour les types widget de |
---|
447 | base, comme <EM>GtkBin</EM>, qui ne peuvent être instanciés) à besoin |
---|
448 | d'avoir -- celle que l'utilisateur appelle pour créer un objet de ce |
---|
449 | type. Elle est conventionnellement appelée <EM>WIDGETNAME_new()</EM>. Pour |
---|
450 | certains widgets, par pour ceux de Tictactoe, cette fonction prend des |
---|
451 | paramètres et réalise certaines initialisations dépendantes des |
---|
452 | paramètres. Les deux autres fonctions sont spécifiques au widget |
---|
453 | Tictactoe. |
---|
454 | <P> |
---|
455 | <P><EM>tictactoe_clear()</EM> est une fonction publique qui remet tous les |
---|
456 | boutons du widget en position relâchée. Notez l'utilisation de |
---|
457 | <EM>gtk_signal_handler_block_by_data()</EM> pour empêcher notre |
---|
458 | gestionnaire de signaux des boutons commutateurs d'être déclenché sans |
---|
459 | besoin. |
---|
460 | <P> |
---|
461 | <P><EM>tictactoe_toggle()</EM> est le gestionnaire de signal invoqué |
---|
462 | lorsqu'on clique sur un bouton. Il vérifie s'il y a des combinaisons |
---|
463 | gagnantes concernant le bouton qui vient d'être commuté et, si c'est |
---|
464 | le cas, émet le signal "tictactoe". |
---|
465 | <P> |
---|
466 | <BLOCKQUOTE><CODE> |
---|
467 | <PRE> |
---|
468 | |
---|
469 | GtkWidget* |
---|
470 | tictactoe_new () |
---|
471 | { |
---|
472 | return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ())); |
---|
473 | } |
---|
474 | |
---|
475 | void |
---|
476 | tictactoe_clear (Tictactoe *ttt) |
---|
477 | { |
---|
478 | int i,j; |
---|
479 | |
---|
480 | for (i=0;i<3;i++) |
---|
481 | for (j=0;j<3;j++) |
---|
482 | { |
---|
483 | gtk_signal_handler_block_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt); |
---|
484 | gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (ttt->buttons[i][j]), |
---|
485 | FALSE); |
---|
486 | gtk_signal_handler_unblock_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt); |
---|
487 | } |
---|
488 | } |
---|
489 | |
---|
490 | static void |
---|
491 | tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt) |
---|
492 | { |
---|
493 | int i,k; |
---|
494 | |
---|
495 | static int rwins[8][3] = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 }, |
---|
496 | { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 }, |
---|
497 | { 0, 1, 2 }, { 0, 1, 2 } }; |
---|
498 | static int cwins[8][3] = { { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 }, |
---|
499 | { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 }, |
---|
500 | { 0, 1, 2 }, { 2, 1, 0 } }; |
---|
501 | |
---|
502 | int success, found; |
---|
503 | |
---|
504 | for (k=0; k<8; k++) |
---|
505 | { |
---|
506 | success = TRUE; |
---|
507 | found = FALSE; |
---|
508 | |
---|
509 | for (i=0;i<3;i++) |
---|
510 | { |
---|
511 | success = success && |
---|
512 | GTK_TOGGLE_BUTTON(ttt->buttons[rwins[k][i]][cwins[k][i]])->active; |
---|
513 | found = found || |
---|
514 | ttt->buttons[rwins[k][i]][cwins[k][i]] == widget; |
---|
515 | } |
---|
516 | |
---|
517 | if (success && found) |
---|
518 | { |
---|
519 | gtk_signal_emit (GTK_OBJECT (ttt), |
---|
520 | tictactoe_signals[TICTACTOE_SIGNAL]); |
---|
521 | break; |
---|
522 | } |
---|
523 | } |
---|
524 | } |
---|
525 | </PRE> |
---|
526 | </CODE></BLOCKQUOTE> |
---|
527 | <P> |
---|
528 | <P> |
---|
529 | <P>Enfin, un exemple de programme utilisant notre widget Tictactoe |
---|
530 | <P> |
---|
531 | <BLOCKQUOTE><CODE> |
---|
532 | <PRE> |
---|
533 | #include <gtk/gtk.h> |
---|
534 | #include "tictactoe.h" |
---|
535 | |
---|
536 | /* Invoqué lorsqu'une ligne, une colonne ou une diagonale est complète */ |
---|
537 | |
---|
538 | void win (GtkWidget *widget, gpointer data) |
---|
539 | { |
---|
540 | g_print ("Ouais !\n"); |
---|
541 | tictactoe_clear (TICTACTOE (widget)); |
---|
542 | } |
---|
543 | |
---|
544 | int main (int argc, char *argv[]) |
---|
545 | { |
---|
546 | GtkWidget *window; |
---|
547 | GtkWidget *ttt; |
---|
548 | |
---|
549 | gtk_init (&argc, &argv); |
---|
550 | |
---|
551 | window = gtk_window_new (GTK_WINDOW_TOPLEVEL); |
---|
552 | |
---|
553 | gtk_window_set_title (GTK_WINDOW (window), "Aspect Frame"); |
---|
554 | |
---|
555 | gtk_signal_connect (GTK_OBJECT (window), "destroy", |
---|
556 | GTK_SIGNAL_FUNC (gtk_exit), NULL); |
---|
557 | |
---|
558 | gtk_container_border_width (GTK_CONTAINER (window), 10); |
---|
559 | |
---|
560 | /* Création d'un widget Tictactoe */ |
---|
561 | ttt = tictactoe_new (); |
---|
562 | gtk_container_add (GTK_CONTAINER (window), ttt); |
---|
563 | gtk_widget_show (ttt); |
---|
564 | |
---|
565 | /* On lui attache le signal "tictactoe" */ |
---|
566 | gtk_signal_connect (GTK_OBJECT (ttt), "tictactoe", |
---|
567 | GTK_SIGNAL_FUNC (win), NULL); |
---|
568 | |
---|
569 | gtk_widget_show (window); |
---|
570 | |
---|
571 | gtk_main (); |
---|
572 | |
---|
573 | return 0; |
---|
574 | } |
---|
575 | </PRE> |
---|
576 | </CODE></BLOCKQUOTE> |
---|
577 | <P> |
---|
578 | <H2><A NAME="ss19.4">19.4 Création d'un widget à partir de zéro</A> |
---|
579 | </H2> |
---|
580 | |
---|
581 | <H3>Introduction</H3> |
---|
582 | |
---|
583 | <P> |
---|
584 | <P>Dans cette section, nous en apprendrons plus sur la façon dont les |
---|
585 | widgets s'affichent eux-mêmes à l'écran et comment ils interagissent |
---|
586 | avec les événements. Comme exemple, nous créerons un widget d'appel |
---|
587 | télephonique interactif avec un pointeur que l'utilisateur pourra |
---|
588 | déplacer pour initialiser la valeur. |
---|
589 | <P> |
---|
590 | <H3>Afficher un widget à l'écran</H3> |
---|
591 | |
---|
592 | <P>Il y a plusieurs étapes mises en jeu lors de l'affichage. Lorsque le widget est |
---|
593 | créé par l'appel <EM>WIDGETNAME_new()</EM>, plusieurs autres fonctions |
---|
594 | supplémentaires sont requises. |
---|
595 | <P> |
---|
596 | <UL> |
---|
597 | <LI> <EM>WIDGETNAME_realize()</EM> s'occupe de créer une fenêtre X pour le |
---|
598 | widget, s'il en a une.</LI> |
---|
599 | <LI> <EM>WIDGETNAME_map()</EM> est invoquée après l'appel de |
---|
600 | <EM>gtk_widget_show()</EM>. Elle s'assure que le widget est bien tracé à l'écran |
---|
601 | (<EM>mappé</EM>). Dans le cas d'une classe container, elle doit aussi appeler des |
---|
602 | fonctions <EM>map()</EM>> pour chaque widget fils.</LI> |
---|
603 | <LI> <EM>WIDGETNAME_draw()</EM> est invoquée lorsque <EM>gtk_widget_draw()</EM> est |
---|
604 | appelé pour le widget ou l'un de ces ancêtres. Elle réalise les véritables |
---|
605 | appels aux fonctions de dessin pour tracer le widget à l'écran. Pour les |
---|
606 | widgets containers, cette fonction doit appeler <EM>gtk_widget_draw()</EM> pour ses |
---|
607 | widgets fils.</LI> |
---|
608 | <LI> <EM>WIDGETNAME_expose()</EM> est un gestionnaire pour les événements |
---|
609 | d'exposition du widget. Il réalise les appels nécessaires aux fonctions de |
---|
610 | dessin pour tracer la partie exposée à l'écran. Pour les widgets containers, |
---|
611 | cette fonction doit générer les événements d'exposition pour ses widgets |
---|
612 | enfants n'ayant pas leurs propres fenêtres (s'ils ont leurs propres fenêtres, X |
---|
613 | génèrera les événements d'exposition nécessaires).</LI> |
---|
614 | </UL> |
---|
615 | <P> |
---|
616 | <P>Vous avez pu noter que les deux dernières fonctions sont assez similaires -- |
---|
617 | chacune se charge de tracer le widget à l'écran. En fait, de nombreux types de |
---|
618 | widgets ne se préoccupent pas vraiment de la différence entre les deux. La |
---|
619 | fonction <EM>draw()</EM> par défaut de la classe widget génère simplement un |
---|
620 | événement d'exposition synthétique pour la zone à redessiner. Cependant, |
---|
621 | certains types de widgets peuvent économiser du travail en faisant la |
---|
622 | différence entre les deux fonctions. Par exemple, si un widget a plusieurs |
---|
623 | fenêtres X et puisque les événements d'exposition identifient la fenêtre |
---|
624 | exposée, il peut redessiner seulement la fenêtre concernée, ce qui n'est pas |
---|
625 | possible avec des appels à <EM>draw()</EM>. |
---|
626 | <P> |
---|
627 | <P>Les widgets container, même s'ils ne se soucient pas eux-mêmes de la |
---|
628 | différence, ne peuvent pas utiliser simplement la fonction <EM>draw()</EM> car |
---|
629 | leurs widgets enfants tiennent compte de cette différence. Cependant, ce serait |
---|
630 | du gaspillage de dupliquer le code de tracé pour les deux |
---|
631 | fonctions. Conventionnellement, de tels widgets possèdent une fonction nommée |
---|
632 | <EM>WIDGETNAME_paint()</EM> qui réalise le véritable travail de tracé du widget et |
---|
633 | qui est appelée par les fonctions <CODE>draw()</CODE> et <CODE>expose()</CODE>. |
---|
634 | <P> |
---|
635 | <P>Dans notre exemple, comme le widget d'appel n'est pas un widget container et |
---|
636 | n'a qu'une fenêtre, nous pouvons utiliser l'approche la plus simple : |
---|
637 | utiliser la fonction <EM>draw()</EM> par défaut et n'implanter que la fonction |
---|
638 | <EM>expose()</EM>. |
---|
639 | <P> |
---|
640 | <H3>Origines du widget Dial</H3> |
---|
641 | |
---|
642 | <P>Exactement comme les animaux terrestres ne sont que des variantes des premiers |
---|
643 | amphibiens qui rampèrent hors de la boue, les widgets GTK sont des variantes |
---|
644 | d'autres widgets, déjà écrits. Ainsi, bien que cette section s'appelle « créer |
---|
645 | un widget à partir de zéro », le widget Dial commence réellement avec le code |
---|
646 | source du widget Range. Celui-ci a été pris comme point de départ car ce serait |
---|
647 | bien que notre Dial ait la même interface que les widgets Scale qui ne sont que |
---|
648 | des descendants spécialisés du widget Range. Par conséquent, bien que le code |
---|
649 | source soit présenté ci-dessous sous une forme achevée, cela n'implique pas |
---|
650 | qu'il a été écrit <EM>deus ex machina</EM>. De plus, si vous ne savez pas comment |
---|
651 | fonctionnent les widgets Scale du point de vue du programmeur de l'application, |
---|
652 | il est préférable de les étudier avant de continuer. |
---|
653 | <P> |
---|
654 | <H3>Les bases</H3> |
---|
655 | |
---|
656 | <P>Un petite partie de notre widget devrait ressembler au widget Tictactoe. Nous |
---|
657 | avons d'abord le fichier en-tête : |
---|
658 | <P> |
---|
659 | <BLOCKQUOTE><CODE> |
---|
660 | <PRE> |
---|
661 | /* GTK - The GIMP Toolkit |
---|
662 | * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald |
---|
663 | * |
---|
664 | * This library is free software; you can redistribute it and/or |
---|
665 | * modify it under the terms of the GNU Library General Public |
---|
666 | * License as published by the Free Software Foundation; either |
---|
667 | * version 2 of the License, or (at your option) any later version. |
---|
668 | * |
---|
669 | * This library is distributed in the hope that it will be useful, |
---|
670 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
671 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
672 | * Library General Public License for more details. |
---|
673 | * |
---|
674 | * You should have received a copy of the GNU Library General Public |
---|
675 | * License along with this library; if not, write to the Free |
---|
676 | * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
---|
677 | */ |
---|
678 | |
---|
679 | #ifndef __GTK_DIAL_H__ |
---|
680 | #define __GTK_DIAL_H__ |
---|
681 | |
---|
682 | #include <gdk/gdk.h> |
---|
683 | #include <gtk/gtkadjustment.h> |
---|
684 | #include <gtk/gtkwidget.h> |
---|
685 | |
---|
686 | |
---|
687 | #ifdef __cplusplus |
---|
688 | extern "C" { |
---|
689 | #endif /* __cplusplus */ |
---|
690 | |
---|
691 | |
---|
692 | #define GTK_DIAL(obj) GTK_CHECK_CAST (obj, gtk_dial_get_type (), GtkDial) |
---|
693 | #define GTK_DIAL_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_dial_get_type (), GtkDialClass) |
---|
694 | #define GTK_IS_DIAL(obj) GTK_CHECK_TYPE (obj, gtk_dial_get_type ()) |
---|
695 | |
---|
696 | |
---|
697 | typedef struct _GtkDial GtkDial; |
---|
698 | typedef struct _GtkDialClass GtkDialClass; |
---|
699 | |
---|
700 | struct _GtkDial |
---|
701 | { |
---|
702 | GtkWidget widget; |
---|
703 | |
---|
704 | /* politique de mise à jour |
---|
705 | (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */ |
---|
706 | |
---|
707 | guint policy : 2; |
---|
708 | |
---|
709 | /* Le bouton qui est pressé, 0 si aucun */ |
---|
710 | guint8 button; |
---|
711 | |
---|
712 | /* Dimensions des composants de dial */ |
---|
713 | gint radius; |
---|
714 | gint pointer_width; |
---|
715 | |
---|
716 | /* ID du timer de mise à jour, 0 si aucun */ |
---|
717 | guint32 timer; |
---|
718 | |
---|
719 | /* Angle courant*/ |
---|
720 | gfloat angle; |
---|
721 | |
---|
722 | /* Anciennes valeurs d'ajustement stockées. On sait donc quand quelque |
---|
723 | chose change */ |
---|
724 | gfloat old_value; |
---|
725 | gfloat old_lower; |
---|
726 | gfloat old_upper; |
---|
727 | |
---|
728 | /* L'objet ajustment qui stocke les données de cet appel */ |
---|
729 | GtkAdjustment *adjustment; |
---|
730 | }; |
---|
731 | |
---|
732 | struct _GtkDialClass |
---|
733 | { |
---|
734 | GtkWidgetClass parent_class; |
---|
735 | }; |
---|
736 | |
---|
737 | |
---|
738 | GtkWidget* gtk_dial_new (GtkAdjustment *adjustment); |
---|
739 | guint gtk_dial_get_type (void); |
---|
740 | GtkAdjustment* gtk_dial_get_adjustment (GtkDial *dial); |
---|
741 | void gtk_dial_set_update_policy (GtkDial *dial, |
---|
742 | GtkUpdateType policy); |
---|
743 | |
---|
744 | void gtk_dial_set_adjustment (GtkDial *dial, |
---|
745 | GtkAdjustment *adjustment); |
---|
746 | #ifdef __cplusplus |
---|
747 | } |
---|
748 | #endif /* __cplusplus */ |
---|
749 | |
---|
750 | |
---|
751 | #endif /* __GTK_DIAL_H__ */ |
---|
752 | </PRE> |
---|
753 | </CODE></BLOCKQUOTE> |
---|
754 | <P>Comme il y a plus de choses à faire avec ce widget par rapport à l'autre, nous |
---|
755 | avons plus de champs dans la structure de données, mais à part ça, les choses |
---|
756 | sont plutôt similaires. |
---|
757 | <P> |
---|
758 | <P> |
---|
759 | <P>Puis, après avoir inclus les fichiers en-tête et déclaré quelques constantes, |
---|
760 | nous devons fournir quelques fonctions pour donner des informations sur le |
---|
761 | widget et pour l'initialiser : |
---|
762 | <P> |
---|
763 | <BLOCKQUOTE><CODE> |
---|
764 | <PRE> |
---|
765 | #include <math.h> |
---|
766 | #include <stdio.h> |
---|
767 | #include <gtk/gtkmain.h> |
---|
768 | #include <gtk/gtksignal.h> |
---|
769 | |
---|
770 | #include "gtkdial.h" |
---|
771 | |
---|
772 | #define SCROLL_DELAY_LENGTH 300 |
---|
773 | #define DIAL_DEFAULT_SIZE 100 |
---|
774 | |
---|
775 | /* Déclararations des prototypes */ |
---|
776 | |
---|
777 | [ omis pour gagner de la place ] |
---|
778 | |
---|
779 | /* Données locales */ |
---|
780 | |
---|
781 | static GtkWidgetClass *parent_class = NULL; |
---|
782 | |
---|
783 | guint |
---|
784 | gtk_dial_get_type () |
---|
785 | { |
---|
786 | static guint dial_type = 0; |
---|
787 | |
---|
788 | if (!dial_type) |
---|
789 | { |
---|
790 | GtkTypeInfo dial_info = |
---|
791 | { |
---|
792 | "GtkDial", |
---|
793 | sizeof (GtkDial), |
---|
794 | sizeof (GtkDialClass), |
---|
795 | (GtkClassInitFunc) gtk_dial_class_init, |
---|
796 | (GtkObjectInitFunc) gtk_dial_init, |
---|
797 | (GtkArgFunc) NULL, |
---|
798 | }; |
---|
799 | |
---|
800 | dial_type = gtk_type_unique (gtk_widget_get_type (), &dial_info); |
---|
801 | } |
---|
802 | |
---|
803 | return dial_type; |
---|
804 | } |
---|
805 | |
---|
806 | static void |
---|
807 | gtk_dial_class_init (GtkDialClass *class) |
---|
808 | { |
---|
809 | GtkObjectClass *object_class; |
---|
810 | GtkWidgetClass *widget_class; |
---|
811 | |
---|
812 | object_class = (GtkObjectClass*) class; |
---|
813 | widget_class = (GtkWidgetClass*) class; |
---|
814 | |
---|
815 | parent_class = gtk_type_class (gtk_widget_get_type ()); |
---|
816 | |
---|
817 | object_class->destroy = gtk_dial_destroy; |
---|
818 | |
---|
819 | widget_class->realize = gtk_dial_realize; |
---|
820 | widget_class->expose_event = gtk_dial_expose; |
---|
821 | widget_class->size_request = gtk_dial_size_request; |
---|
822 | widget_class->size_allocate = gtk_dial_size_allocate; |
---|
823 | widget_class->button_press_event = gtk_dial_button_press; |
---|
824 | widget_class->button_release_event = gtk_dial_button_release; |
---|
825 | widget_class->motion_notify_event = gtk_dial_motion_notify; |
---|
826 | } |
---|
827 | |
---|
828 | static void |
---|
829 | gtk_dial_init (GtkDial *dial) |
---|
830 | { |
---|
831 | dial->button = 0; |
---|
832 | dial->policy = GTK_UPDATE_CONTINUOUS; |
---|
833 | dial->timer = 0; |
---|
834 | dial->radius = 0; |
---|
835 | dial->pointer_width = 0; |
---|
836 | dial->angle = 0.0; |
---|
837 | dial->old_value = 0.0; |
---|
838 | dial->old_lower = 0.0; |
---|
839 | dial->old_upper = 0.0; |
---|
840 | dial->adjustment = NULL; |
---|
841 | } |
---|
842 | |
---|
843 | GtkWidget* |
---|
844 | gtk_dial_new (GtkAdjustment *adjustment) |
---|
845 | { |
---|
846 | GtkDial *dial; |
---|
847 | |
---|
848 | dial = gtk_type_new (gtk_dial_get_type ()); |
---|
849 | |
---|
850 | if (!adjustment) |
---|
851 | adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0); |
---|
852 | |
---|
853 | gtk_dial_set_adjustment (dial, adjustment); |
---|
854 | |
---|
855 | return GTK_WIDGET (dial); |
---|
856 | } |
---|
857 | |
---|
858 | static void |
---|
859 | gtk_dial_destroy (GtkObject *object) |
---|
860 | { |
---|
861 | GtkDial *dial; |
---|
862 | |
---|
863 | g_return_if_fail (object != NULL); |
---|
864 | g_return_if_fail (GTK_IS_DIAL (object)); |
---|
865 | |
---|
866 | dial = GTK_DIAL (object); |
---|
867 | |
---|
868 | if (dial->adjustment) |
---|
869 | gtk_object_unref (GTK_OBJECT (dial->adjustment)); |
---|
870 | |
---|
871 | if (GTK_OBJECT_CLASS (parent_class)->destroy) |
---|
872 | (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); |
---|
873 | } |
---|
874 | </PRE> |
---|
875 | </CODE></BLOCKQUOTE> |
---|
876 | <P>Notez que cette fonction <EM>init()</EM> fait moins de choses que pour le widget |
---|
877 | Tictactoe car ce n'est pas un widget composé et que la fonction <EM>new()</EM> en |
---|
878 | fait plus car elle a maintenant un paramètre. Notez aussi que lorsque nous |
---|
879 | stockons un pointeur vers l'objet Adjustement, nous incrémentons son nombre de |
---|
880 | références (et nous le décrémentons lorsque nous ne l'utilisons plus) afin que |
---|
881 | GTK puisse savoir quand il pourra être détruit sans danger. |
---|
882 | <P> |
---|
883 | <P>Il y a aussi quelques fonctions pour manipuler les options du widget : |
---|
884 | <P> |
---|
885 | <BLOCKQUOTE><CODE> |
---|
886 | <PRE> |
---|
887 | GtkAdjustment* |
---|
888 | gtk_dial_get_adjustment (GtkDial *dial) |
---|
889 | { |
---|
890 | g_return_val_if_fail (dial != NULL, NULL); |
---|
891 | g_return_val_if_fail (GTK_IS_DIAL (dial), NULL); |
---|
892 | |
---|
893 | return dial->adjustment; |
---|
894 | } |
---|
895 | |
---|
896 | void |
---|
897 | gtk_dial_set_update_policy (GtkDial *dial, |
---|
898 | GtkUpdateType policy) |
---|
899 | { |
---|
900 | g_return_if_fail (dial != NULL); |
---|
901 | g_return_if_fail (GTK_IS_DIAL (dial)); |
---|
902 | |
---|
903 | dial->policy = policy; |
---|
904 | } |
---|
905 | |
---|
906 | void |
---|
907 | gtk_dial_set_adjustment (GtkDial *dial, |
---|
908 | GtkAdjustment *adjustment) |
---|
909 | { |
---|
910 | g_return_if_fail (dial != NULL); |
---|
911 | g_return_if_fail (GTK_IS_DIAL (dial)); |
---|
912 | |
---|
913 | if (dial->adjustment) |
---|
914 | { |
---|
915 | gtk_signal_disconnect_by_data (GTK_OBJECT (dial->adjustment), (gpointer) dial); |
---|
916 | gtk_object_unref (GTK_OBJECT (dial->adjustment)); |
---|
917 | } |
---|
918 | |
---|
919 | dial->adjustment = adjustment; |
---|
920 | gtk_object_ref (GTK_OBJECT (dial->adjustment)); |
---|
921 | |
---|
922 | gtk_signal_connect (GTK_OBJECT (adjustment), "changed", |
---|
923 | (GtkSignalFunc) gtk_dial_adjustment_changed, |
---|
924 | (gpointer) dial); |
---|
925 | gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed", |
---|
926 | (GtkSignalFunc) gtk_dial_adjustment_value_changed, |
---|
927 | (gpointer) dial); |
---|
928 | |
---|
929 | dial->old_value = adjustment->value; |
---|
930 | dial->old_lower = adjustment->lower; |
---|
931 | dial->old_upper = adjustment->upper; |
---|
932 | |
---|
933 | gtk_dial_update (dial); |
---|
934 | } |
---|
935 | </PRE> |
---|
936 | </CODE></BLOCKQUOTE> |
---|
937 | <P> |
---|
938 | <H3><EM>gtk_dial_realize()</EM></H3> |
---|
939 | |
---|
940 | <P>Nous arrivons maintenant à quelques nouveaux types de fonctions. D'abord, nous |
---|
941 | avons une fonction qui réalise la création de la fenêtre X. Notez que l'on |
---|
942 | passe un masque à la fonction <EM>gdk_window_new()</EM> pour spécifier quels sont |
---|
943 | les champs de la structure GdkWindowAttr qui contiennent des données (les |
---|
944 | autres recevront des valeurs par défaut). Notez aussi la façon dont est créé le |
---|
945 | masque d'événement du widget. On appelle <EM>gtk_widget_get_events()</EM> pour |
---|
946 | récupérer le masque d'événement que l'utilisateur a spécifié pour ce widget |
---|
947 | (avec <EM>gtk_widget_set_events()</EM>) et ajouter les événements qui nous |
---|
948 | intéressent. |
---|
949 | <P> |
---|
950 | <P>Après avoir créé la fenêtre, nous configurons son style et son fond et mettons |
---|
951 | un pointeur vers le widget dans le champ user de la GdkWindow. Cette dernière |
---|
952 | étape permet à GTK de distribuer les événements pour cette fenêtre au widget |
---|
953 | correct. |
---|
954 | <P> |
---|
955 | <BLOCKQUOTE><CODE> |
---|
956 | <PRE> |
---|
957 | static void |
---|
958 | gtk_dial_realize (GtkWidget *widget) |
---|
959 | { |
---|
960 | GtkDial *dial; |
---|
961 | GdkWindowAttr attributes; |
---|
962 | gint attributes_mask; |
---|
963 | |
---|
964 | g_return_if_fail (widget != NULL); |
---|
965 | g_return_if_fail (GTK_IS_DIAL (widget)); |
---|
966 | |
---|
967 | GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); |
---|
968 | dial = GTK_DIAL (widget); |
---|
969 | |
---|
970 | attributes.x = widget->allocation.x; |
---|
971 | attributes.y = widget->allocation.y; |
---|
972 | attributes.width = widget->allocation.width; |
---|
973 | attributes.height = widget->allocation.height; |
---|
974 | attributes.wclass = GDK_INPUT_OUTPUT; |
---|
975 | attributes.window_type = GDK_WINDOW_CHILD; |
---|
976 | attributes.event_mask = gtk_widget_get_events (widget) | |
---|
977 | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | |
---|
978 | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | |
---|
979 | GDK_POINTER_MOTION_HINT_MASK; |
---|
980 | attributes.visual = gtk_widget_get_visual (widget); |
---|
981 | attributes.colormap = gtk_widget_get_colormap (widget); |
---|
982 | |
---|
983 | attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; |
---|
984 | widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask); |
---|
985 | |
---|
986 | widget->style = gtk_style_attach (widget->style, widget->window); |
---|
987 | |
---|
988 | gdk_window_set_user_data (widget->window, widget); |
---|
989 | |
---|
990 | gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE); |
---|
991 | } |
---|
992 | </PRE> |
---|
993 | </CODE></BLOCKQUOTE> |
---|
994 | <P> |
---|
995 | <H3>Négotiation de la taille</H3> |
---|
996 | |
---|
997 | <P>Avant le premier affichage de la fenêtre contenant un widget et à chaque fois |
---|
998 | que la forme de la fenêtre change, GTK demande à chaque widget fils la taille |
---|
999 | qu'il désire avoir. Cette requête est gérée par la fonction |
---|
1000 | <EM>gtk_dial_size_request()</EM>. Comme notre widget n'est pas un widget container, |
---|
1001 | et n'a pas de contraintes réelles sur sa taille, nous ne faisons que retourner |
---|
1002 | une valeur raisonnable par défaut. |
---|
1003 | <P> |
---|
1004 | <BLOCKQUOTE><CODE> |
---|
1005 | <PRE> |
---|
1006 | static void |
---|
1007 | gtk_dial_size_request (GtkWidget *widget, |
---|
1008 | GtkRequisition *requisition) |
---|
1009 | { |
---|
1010 | requisition->width = DIAL_DEFAULT_SIZE; |
---|
1011 | requisition->height = DIAL_DEFAULT_SIZE; |
---|
1012 | } |
---|
1013 | </PRE> |
---|
1014 | </CODE></BLOCKQUOTE> |
---|
1015 | <P> |
---|
1016 | <P>Lorsque tous les widgets on demandé une taille idéale, le forme de la fenêtre |
---|
1017 | est calculée et chaque widget fils est averti de sa taille. Habituellement, ce |
---|
1018 | sera autant que la taille requise, mais si, par exemple, l'utilisateur a |
---|
1019 | redimensionné la fenêtre, cette taille peut occasionnellement être plus petite |
---|
1020 | que la taille requise. La notification de la taille est gérée par la fonction |
---|
1021 | <EM>gtk_dial_size_allocate()</EM>. Notez qu'en même temps qu'elle calcule les |
---|
1022 | tailles de certains composants pour une utilisation future, cette routine fait |
---|
1023 | aussi le travail de base consistant à déplacer les widgets X Window dans leur |
---|
1024 | nouvelles positions et tailles. |
---|
1025 | <P> |
---|
1026 | <BLOCKQUOTE><CODE> |
---|
1027 | <PRE> |
---|
1028 | static void |
---|
1029 | gtk_dial_size_allocate (GtkWidget *widget, |
---|
1030 | GtkAllocation *allocation) |
---|
1031 | { |
---|
1032 | GtkDial *dial; |
---|
1033 | |
---|
1034 | g_return_if_fail (widget != NULL); |
---|
1035 | g_return_if_fail (GTK_IS_DIAL (widget)); |
---|
1036 | g_return_if_fail (allocation != NULL); |
---|
1037 | |
---|
1038 | widget->allocation = *allocation; |
---|
1039 | if (GTK_WIDGET_REALIZED (widget)) |
---|
1040 | { |
---|
1041 | dial = GTK_DIAL (widget); |
---|
1042 | |
---|
1043 | gdk_window_move_resize (widget->window, |
---|
1044 | allocation->x, allocation->y, |
---|
1045 | allocation->width, allocation->height); |
---|
1046 | |
---|
1047 | dial->radius = MAX(allocation->width,allocation->height) * 0.45; |
---|
1048 | dial->pointer_width = dial->radius / 5; |
---|
1049 | } |
---|
1050 | } |
---|
1051 | </PRE> |
---|
1052 | </CODE></BLOCKQUOTE> |
---|
1053 | . |
---|
1054 | <P> |
---|
1055 | <H3><EM>gtk_dial_expose()</EM></H3> |
---|
1056 | |
---|
1057 | <P>Comme cela est mentionné plus haut, tout le dessin de ce widget est réalisé |
---|
1058 | dans le gestionnaire pour les événements d'exposition. Il n'y a pas grand chose |
---|
1059 | de plus à dire là dessus, sauf constater l'utilisation de la fonction |
---|
1060 | <EM>gtk_draw_polygon</EM> pour dessiner le pointeur avec une forme en trois |
---|
1061 | dimensions selon les couleurs stockées dans le style du widget. |
---|
1062 | style. |
---|
1063 | <P> |
---|
1064 | <BLOCKQUOTE><CODE> |
---|
1065 | <PRE> |
---|
1066 | static gint |
---|
1067 | gtk_dial_expose (GtkWidget *widget, |
---|
1068 | GdkEventExpose *event) |
---|
1069 | { |
---|
1070 | GtkDial *dial; |
---|
1071 | GdkPoint points[3]; |
---|
1072 | gdouble s,c; |
---|
1073 | gdouble theta; |
---|
1074 | gint xc, yc; |
---|
1075 | gint tick_length; |
---|
1076 | gint i; |
---|
1077 | |
---|
1078 | g_return_val_if_fail (widget != NULL, FALSE); |
---|
1079 | g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE); |
---|
1080 | g_return_val_if_fail (event != NULL, FALSE); |
---|
1081 | |
---|
1082 | if (event->count > 0) |
---|
1083 | return FALSE; |
---|
1084 | |
---|
1085 | dial = GTK_DIAL (widget); |
---|
1086 | |
---|
1087 | gdk_window_clear_area (widget->window, |
---|
1088 | 0, 0, |
---|
1089 | widget->allocation.width, |
---|
1090 | widget->allocation.height); |
---|
1091 | |
---|
1092 | xc = widget->allocation.width/2; |
---|
1093 | yc = widget->allocation.height/2; |
---|
1094 | |
---|
1095 | /* Draw ticks */ |
---|
1096 | |
---|
1097 | for (i=0; i<25; i++) |
---|
1098 | { |
---|
1099 | theta = (i*M_PI/18. - M_PI/6.); |
---|
1100 | s = sin(theta); |
---|
1101 | c = cos(theta); |
---|
1102 | |
---|
1103 | tick_length = (i%6 == 0) ? dial->pointer_width : dial->pointer_width/2; |
---|
1104 | |
---|
1105 | gdk_draw_line (widget->window, |
---|
1106 | widget->style->fg_gc[widget->state], |
---|
1107 | xc + c*(dial->radius - tick_length), |
---|
1108 | yc - s*(dial->radius - tick_length), |
---|
1109 | xc + c*dial->radius, |
---|
1110 | yc - s*dial->radius); |
---|
1111 | } |
---|
1112 | |
---|
1113 | /* Draw pointer */ |
---|
1114 | |
---|
1115 | s = sin(dial->angle); |
---|
1116 | c = cos(dial->angle); |
---|
1117 | |
---|
1118 | |
---|
1119 | points[0].x = xc + s*dial->pointer_width/2; |
---|
1120 | points[0].y = yc + c*dial->pointer_width/2; |
---|
1121 | points[1].x = xc + c*dial->radius; |
---|
1122 | points[1].y = yc - s*dial->radius; |
---|
1123 | points[2].x = xc - s*dial->pointer_width/2; |
---|
1124 | points[2].y = yc - c*dial->pointer_width/2; |
---|
1125 | |
---|
1126 | gtk_draw_polygon (widget->style, |
---|
1127 | widget->window, |
---|
1128 | GTK_STATE_NORMAL, |
---|
1129 | GTK_SHADOW_OUT, |
---|
1130 | points, 3, |
---|
1131 | TRUE); |
---|
1132 | |
---|
1133 | return FALSE; |
---|
1134 | } |
---|
1135 | </PRE> |
---|
1136 | </CODE></BLOCKQUOTE> |
---|
1137 | <P> |
---|
1138 | <H3>Gestion des événements</H3> |
---|
1139 | |
---|
1140 | <P>Le reste du code du widget gère différents types d'événements et n'est pas |
---|
1141 | trop différent de ce que l'on trouve dans la plupart des applications GTK. Deux |
---|
1142 | types d'événements peuvent survenir -- l'utilisateur peut cliquer sur le widget |
---|
1143 | avec la souris et faire glisser pour déplacer le pointeur, ou bien la valeur de |
---|
1144 | l'objet Adjustment peut changer à cause d'une circonstance extérieure. |
---|
1145 | <P> |
---|
1146 | <P>Lorsque l'utilisateur clique sur le widget, on vérifie si le clic s'est bien |
---|
1147 | passé près du pointeur et si c'est le cas, on stocke alors le bouton avec |
---|
1148 | lequel l'utilisateur a cliqué dans le champ <EM>button</EM> de la structure du |
---|
1149 | widget et on récupère tous les événements souris avec un appel à |
---|
1150 | <EM>gtk_grab_add()</EM>. Un déplacement ultérieur de la souris provoque le recalcul |
---|
1151 | de la valeur de contrôle (par la fonction <EM>gtk_dial_update_mouse</EM>). Selon la |
---|
1152 | politique qui a été choisie, les événements "value_changed" sont, soit générés |
---|
1153 | instantanément (<EM>GTK_UPDATE_CONTINUOUS</EM>), après un délai ajouté au timer |
---|
1154 | avec <EM>gtk_timeout_add()</EM> (<EM>GTK_UPDATE_DELAYED</EM>), ou seulement lorsque le |
---|
1155 | bouton est relâché (<EM>GTK_UPDATE_DISCONTINUOUS</EM>). |
---|
1156 | <P> |
---|
1157 | <BLOCKQUOTE><CODE> |
---|
1158 | <PRE> |
---|
1159 | static gint |
---|
1160 | gtk_dial_button_press (GtkWidget *widget, |
---|
1161 | GdkEventButton *event) |
---|
1162 | { |
---|
1163 | GtkDial *dial; |
---|
1164 | gint dx, dy; |
---|
1165 | double s, c; |
---|
1166 | double d_parallel; |
---|
1167 | double d_perpendicular; |
---|
1168 | |
---|
1169 | g_return_val_if_fail (widget != NULL, FALSE); |
---|
1170 | g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE); |
---|
1171 | g_return_val_if_fail (event != NULL, FALSE); |
---|
1172 | |
---|
1173 | dial = GTK_DIAL (widget); |
---|
1174 | |
---|
1175 | /* Détermine si le bouton pressé est dans la région du pointeur. |
---|
1176 | On fait cela en calculant les distances parallèle et perpendiculaire |
---|
1177 | du point où la souris a été pressée par rapport à la ligne passant |
---|
1178 | par le pointeur */ |
---|
1179 | |
---|
1180 | dx = event->x - widget->allocation.width / 2; |
---|
1181 | dy = widget->allocation.height / 2 - event->y; |
---|
1182 | |
---|
1183 | s = sin(dial->angle); |
---|
1184 | c = cos(dial->angle); |
---|
1185 | |
---|
1186 | d_parallel = s*dy + c*dx; |
---|
1187 | d_perpendicular = fabs(s*dx - c*dy); |
---|
1188 | |
---|
1189 | if (!dial->button && |
---|
1190 | (d_perpendicular < dial->pointer_width/2) && |
---|
1191 | (d_parallel > - dial->pointer_width)) |
---|
1192 | { |
---|
1193 | gtk_grab_add (widget); |
---|
1194 | |
---|
1195 | dial->button = event->button; |
---|
1196 | |
---|
1197 | gtk_dial_update_mouse (dial, event->x, event->y); |
---|
1198 | } |
---|
1199 | |
---|
1200 | return FALSE; |
---|
1201 | } |
---|
1202 | |
---|
1203 | static gint |
---|
1204 | gtk_dial_button_release (GtkWidget *widget, |
---|
1205 | GdkEventButton *event) |
---|
1206 | { |
---|
1207 | GtkDial *dial; |
---|
1208 | |
---|
1209 | g_return_val_if_fail (widget != NULL, FALSE); |
---|
1210 | g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE); |
---|
1211 | g_return_val_if_fail (event != NULL, FALSE); |
---|
1212 | |
---|
1213 | dial = GTK_DIAL (widget); |
---|
1214 | |
---|
1215 | if (dial->button == event->button) |
---|
1216 | { |
---|
1217 | gtk_grab_remove (widget); |
---|
1218 | |
---|
1219 | dial->button = 0; |
---|
1220 | |
---|
1221 | if (dial->policy == GTK_UPDATE_DELAYED) |
---|
1222 | gtk_timeout_remove (dial->timer); |
---|
1223 | |
---|
1224 | if ((dial->policy != GTK_UPDATE_CONTINUOUS) && |
---|
1225 | (dial->old_value != dial->adjustment->value)) |
---|
1226 | gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed"); |
---|
1227 | } |
---|
1228 | |
---|
1229 | return FALSE; |
---|
1230 | } |
---|
1231 | |
---|
1232 | static gint |
---|
1233 | gtk_dial_motion_notify (GtkWidget *widget, |
---|
1234 | GdkEventMotion *event) |
---|
1235 | { |
---|
1236 | GtkDial *dial; |
---|
1237 | GdkModifierType mods; |
---|
1238 | gint x, y, mask; |
---|
1239 | |
---|
1240 | g_return_val_if_fail (widget != NULL, FALSE); |
---|
1241 | g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE); |
---|
1242 | g_return_val_if_fail (event != NULL, FALSE); |
---|
1243 | |
---|
1244 | dial = GTK_DIAL (widget); |
---|
1245 | |
---|
1246 | if (dial->button != 0) |
---|
1247 | { |
---|
1248 | x = event->x; |
---|
1249 | y = event->y; |
---|
1250 | |
---|
1251 | if (event->is_hint || (event->window != widget->window)) |
---|
1252 | gdk_window_get_pointer (widget->window, &x, &y, &mods); |
---|
1253 | |
---|
1254 | switch (dial->button) |
---|
1255 | { |
---|
1256 | case 1: |
---|
1257 | mask = GDK_BUTTON1_MASK; |
---|
1258 | break; |
---|
1259 | case 2: |
---|
1260 | mask = GDK_BUTTON2_MASK; |
---|
1261 | break; |
---|
1262 | case 3: |
---|
1263 | mask = GDK_BUTTON3_MASK; |
---|
1264 | break; |
---|
1265 | default: |
---|
1266 | mask = 0; |
---|
1267 | break; |
---|
1268 | } |
---|
1269 | |
---|
1270 | if (mods & mask) |
---|
1271 | gtk_dial_update_mouse (dial, x,y); |
---|
1272 | } |
---|
1273 | |
---|
1274 | return FALSE; |
---|
1275 | } |
---|
1276 | |
---|
1277 | static gint |
---|
1278 | gtk_dial_timer (GtkDial *dial) |
---|
1279 | { |
---|
1280 | g_return_val_if_fail (dial != NULL, FALSE); |
---|
1281 | g_return_val_if_fail (GTK_IS_DIAL (dial), FALSE); |
---|
1282 | |
---|
1283 | if (dial->policy == GTK_UPDATE_DELAYED) |
---|
1284 | gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed"); |
---|
1285 | |
---|
1286 | return FALSE; |
---|
1287 | } |
---|
1288 | |
---|
1289 | static void |
---|
1290 | gtk_dial_update_mouse (GtkDial *dial, gint x, gint y) |
---|
1291 | { |
---|
1292 | gint xc, yc; |
---|
1293 | gfloat old_value; |
---|
1294 | |
---|
1295 | g_return_if_fail (dial != NULL); |
---|
1296 | g_return_if_fail (GTK_IS_DIAL (dial)); |
---|
1297 | |
---|
1298 | xc = GTK_WIDGET(dial)->allocation.width / 2; |
---|
1299 | yc = GTK_WIDGET(dial)->allocation.height / 2; |
---|
1300 | |
---|
1301 | old_value = dial->adjustment->value; |
---|
1302 | dial->angle = atan2(yc-y, x-xc); |
---|
1303 | |
---|
1304 | if (dial->angle < -M_PI/2.) |
---|
1305 | dial->angle += 2*M_PI; |
---|
1306 | |
---|
1307 | if (dial->angle < -M_PI/6) |
---|
1308 | dial->angle = -M_PI/6; |
---|
1309 | |
---|
1310 | if (dial->angle > 7.*M_PI/6.) |
---|
1311 | dial->angle = 7.*M_PI/6.; |
---|
1312 | |
---|
1313 | dial->adjustment->value = dial->adjustment->lower + (7.*M_PI/6 - dial->angle) * |
---|
1314 | (dial->adjustment->upper - dial->adjustment->lower) / (4.*M_PI/3.); |
---|
1315 | |
---|
1316 | if (dial->adjustment->value != old_value) |
---|
1317 | { |
---|
1318 | if (dial->policy == GTK_UPDATE_CONTINUOUS) |
---|
1319 | { |
---|
1320 | gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed"); |
---|
1321 | } |
---|
1322 | else |
---|
1323 | { |
---|
1324 | gtk_widget_draw (GTK_WIDGET(dial), NULL); |
---|
1325 | |
---|
1326 | if (dial->policy == GTK_UPDATE_DELAYED) |
---|
1327 | { |
---|
1328 | if (dial->timer) |
---|
1329 | gtk_timeout_remove (dial->timer); |
---|
1330 | |
---|
1331 | dial->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH, |
---|
1332 | (GtkFunction) gtk_dial_timer, |
---|
1333 | (gpointer) dial); |
---|
1334 | } |
---|
1335 | } |
---|
1336 | } |
---|
1337 | } |
---|
1338 | </PRE> |
---|
1339 | </CODE></BLOCKQUOTE> |
---|
1340 | <P> |
---|
1341 | <P>Les changements de l'Adjustement par des moyens extérieurs sont communiqués à |
---|
1342 | notre widget par les signaux "changed" et "value_changed". Les gestionnaires |
---|
1343 | pour ces fonctions appellent <EM>gtk_dial_update()</EM> pour valider les |
---|
1344 | paramètres, calculer le nouvel angle du pointeur et redessiner le widget (en |
---|
1345 | appelant <EM>gtk_widget_draw()</EM>). |
---|
1346 | <P> |
---|
1347 | <BLOCKQUOTE><CODE> |
---|
1348 | <PRE> |
---|
1349 | static void |
---|
1350 | gtk_dial_update (GtkDial *dial) |
---|
1351 | { |
---|
1352 | gfloat new_value; |
---|
1353 | |
---|
1354 | g_return_if_fail (dial != NULL); |
---|
1355 | g_return_if_fail (GTK_IS_DIAL (dial)); |
---|
1356 | |
---|
1357 | new_value = dial->adjustment->value; |
---|
1358 | |
---|
1359 | if (new_value < dial->adjustment->lower) |
---|
1360 | new_value = dial->adjustment->lower; |
---|
1361 | |
---|
1362 | if (new_value > dial->adjustment->upper) |
---|
1363 | new_value = dial->adjustment->upper; |
---|
1364 | |
---|
1365 | if (new_value != dial->adjustment->value) |
---|
1366 | { |
---|
1367 | dial->adjustment->value = new_value; |
---|
1368 | gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed"); |
---|
1369 | } |
---|
1370 | |
---|
1371 | dial->angle = 7.*M_PI/6. - (new_value - dial->adjustment->lower) * 4.*M_PI/3. / |
---|
1372 | (dial->adjustment->upper - dial->adjustment->lower); |
---|
1373 | |
---|
1374 | gtk_widget_draw (GTK_WIDGET(dial), NULL); |
---|
1375 | } |
---|
1376 | |
---|
1377 | static void |
---|
1378 | gtk_dial_adjustment_changed (GtkAdjustment *adjustment, |
---|
1379 | gpointer data) |
---|
1380 | { |
---|
1381 | GtkDial *dial; |
---|
1382 | |
---|
1383 | g_return_if_fail (adjustment != NULL); |
---|
1384 | g_return_if_fail (data != NULL); |
---|
1385 | |
---|
1386 | dial = GTK_DIAL (data); |
---|
1387 | |
---|
1388 | if ((dial->old_value != adjustment->value) || |
---|
1389 | (dial->old_lower != adjustment->lower) || |
---|
1390 | (dial->old_upper != adjustment->upper)) |
---|
1391 | { |
---|
1392 | gtk_dial_update (dial); |
---|
1393 | |
---|
1394 | dial->old_value = adjustment->value; |
---|
1395 | dial->old_lower = adjustment->lower; |
---|
1396 | dial->old_upper = adjustment->upper; |
---|
1397 | } |
---|
1398 | } |
---|
1399 | |
---|
1400 | static void |
---|
1401 | gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment, |
---|
1402 | gpointer data) |
---|
1403 | { |
---|
1404 | GtkDial *dial; |
---|
1405 | |
---|
1406 | g_return_if_fail (adjustment != NULL); |
---|
1407 | g_return_if_fail (data != NULL); |
---|
1408 | |
---|
1409 | dial = GTK_DIAL (data); |
---|
1410 | |
---|
1411 | if (dial->old_value != adjustment->value) |
---|
1412 | { |
---|
1413 | gtk_dial_update (dial); |
---|
1414 | |
---|
1415 | dial->old_value = adjustment->value; |
---|
1416 | } |
---|
1417 | } |
---|
1418 | </PRE> |
---|
1419 | </CODE></BLOCKQUOTE> |
---|
1420 | <P> |
---|
1421 | <H3>Améliorations possibles</H3> |
---|
1422 | |
---|
1423 | <P> |
---|
1424 | <P>Le widget Dial décrit jusqu'à maintenant exécute à peu près 670 lignes de |
---|
1425 | code. Bien que cela puisse sembler beaucoup, nous en avons vraiment fait |
---|
1426 | beaucoup avec ce code, notamment parce que la majeure partie de cette longueur |
---|
1427 | est due aux en-têtes et à la préparation. Cependant, certaines améliorations |
---|
1428 | peuvent être apportées à ce widget : |
---|
1429 | <P> |
---|
1430 | <UL> |
---|
1431 | <LI> Si vous testez ce widget, vous vous apercevrez qu'il y a un peu de |
---|
1432 | scintillement lorsque le pointeur est déplacé. Ceci est dû au fait que le |
---|
1433 | widget entier est effacé, puis redessiné à chaque mouvement du |
---|
1434 | pointeur. Souvent, la meilleure façon de gérer ce problème est de dessiner sur |
---|
1435 | un pixmap non affiché, puis de copier le résultat final sur l'écran en une |
---|
1436 | seule étape (le widget <EM>ProgressBar</EM> se dessine de cette façon). |
---|
1437 | </LI> |
---|
1438 | <LI> L'utilisateur devrait pouvoir utiliser les flèches du curseur vers le |
---|
1439 | haut et vers le bas pour incrémenter et décrémenter la valeur. |
---|
1440 | </LI> |
---|
1441 | <LI> Ce serait bien si le widget avait des boutons pour augmenter et diminuer |
---|
1442 | la valeur dans de petites ou de grosses proportions. Bien qu'il serait possible |
---|
1443 | d'utiliser les widgets <EM>Button</EM> pour cela, nous voudrions aussi que les |
---|
1444 | boutons s'auto-répètent lorsqu'ils sont maintenus appuyés, comme font les |
---|
1445 | flèches d'une barre de défilement. La majeure partie du code pour implanter ce |
---|
1446 | type de comportement peut se trouver dans le widget <EM>GtkRange</EM>. |
---|
1447 | </LI> |
---|
1448 | <LI> Le widget Dial pourrait être fait dans un widget container avec un seul |
---|
1449 | widget fils positionnée en bas, entre les boutons mentionnés |
---|
1450 | ci-dessus. L'utilisateur pourrait alors ajouter au choix, un widget label ou |
---|
1451 | entrée pour afficher la valeur courante de l'appel. |
---|
1452 | </LI> |
---|
1453 | </UL> |
---|
1454 | <P> |
---|
1455 | <H2><A NAME="ss19.5">19.5 En savoir plus</A> |
---|
1456 | </H2> |
---|
1457 | |
---|
1458 | <P>Seule une petite partie des nombreux détails de la création des widgets a pu |
---|
1459 | être décrite. Si vous désirez écrire vos propres widgets, la meilleure source |
---|
1460 | d'exemples est le source de GTK lui-même. Posez-vous quelques questions sur les |
---|
1461 | widgets que vous voulez écrire : est-ce un widget container ? possède-t-il |
---|
1462 | sa propre fenêtre ? est-ce une modification d'un widget existant ? Puis, |
---|
1463 | trouvez un widget identique et commencez à faire les modifications. Bonne |
---|
1464 | chance ! |
---|
1465 | <P> |
---|
1466 | <HR NOSHADE> |
---|
1467 | <A HREF="gtk_tut_fr-20.html">Page suivante</A> |
---|
1468 | <A HREF="gtk_tut_fr-18.html">Page précédente</A> |
---|
1469 | <A HREF="gtk_tut_fr.html#toc19">Table des matières</A> |
---|
1470 | </BODY> |
---|
1471 | </HTML> |
---|