1
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
5
>Creating a Composite widget</TITLE
8
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
10
TITLE="GTK+ 2.0 Tutorial"
11
HREF="book1.html"><LINK
13
TITLE="Writing Your Own Widgets"
14
HREF="c2182.html"><LINK
16
TITLE="The Anatomy Of A Widget"
17
HREF="x2191.html"><LINK
19
TITLE="Creating a widget from scratch"
20
HREF="x2312.html"></HEAD
31
SUMMARY="Header navigation table"
40
>GTK+ 2.0 Tutorial</TH
50
><<< Previous</A
56
>Writing Your Own Widgets</TD
64
>Next >>></A
76
NAME="SEC-CREATINGACOMPOSITEWIDGET"
77
>Creating a Composite widget</A
88
>One type of widget that you may be interested in creating is a
89
widget that is merely an aggregate of other GTK widgets. This type of
90
widget does nothing that couldn't be done without creating new
91
widgets, but provides a convenient way of packaging user interface
92
elements for reuse. The FileSelection and ColorSelection widgets in
93
the standard distribution are examples of this type of widget.</P
95
>The example widget that we'll create in this section is the Tictactoe
96
widget, a 3x3 array of toggle buttons which triggers a signal when all
97
three buttons in a row, column, or on one of the diagonals are
102
>Note: the full source code for the Tictactoe example described
104
HREF="a2903.html#SEC-TICTACTOE"
105
>Code Examples Appendix</A
110
CLASS="INLINEMEDIAOBJECT"
112
SRC="images/tictactoe.png"></SPAN
121
>Choosing a parent class</A
124
>The parent class for a composite widget is typically the container
125
class that holds all of the elements of the composite widget. For
126
example, the parent class of the FileSelection widget is the
127
Dialog class. Since our buttons will be arranged in a table, it
128
is natural to make our parent class the Table class.</P
139
>Each GObject class has a header file which declares the object and
140
class structures for that object, along with public functions.
141
A couple of features are worth pointing out. To prevent duplicate
142
definitions, we wrap the entire header file in:</P
150
CLASS="PROGRAMLISTING"
151
>#ifndef __TICTACTOE_H__
152
#define __TICTACTOE_H__
156
#endif /* __TICTACTOE_H__ */</PRE
161
>And to keep C++ programs that include the header file happy, in:</P
169
CLASS="PROGRAMLISTING"
170
>#include <glib.h>
181
>Along with the functions and structures, we declare five standard
182
macros in our header file, <TT
192
>TICTACTOE_CLASS(klass)</TT
196
>IS_TICTACTOE(obj)</TT
200
>IS_TICTACTOE_CLASS(klass)</TT
202
pointer into a pointer to the object or class structure, and check
203
if an object is a Tictactoe widget respectively.</P
217
>We now continue on to the implementation of our widget. A core
218
function for every object is the function
221
>WIDGETNAME_get_type()</TT
222
>. This function, when first called, tells
223
Glib about the new class, and gets an ID that uniquely identifies
224
the class. Upon subsequent calls, it just returns the ID.</P
232
CLASS="PROGRAMLISTING"
234
tictactoe_get_type (void)
236
static GType ttt_type = 0;
240
static const GTypeInfo ttt_info =
242
sizeof (TictactoeClass),
243
NULL, /* base_init */
244
NULL, /* base_finalize */
245
(GClassInitFunc) tictactoe_class_init,
246
NULL, /* class_finalize */
247
NULL, /* class_data */
250
(GInstanceInitFunc) tictactoe_init,
253
ttt_type = g_type_register_static (GTK_TYPE_TABLE,
265
>The GTypeInfo structure has the following definition:</P
273
CLASS="PROGRAMLISTING"
276
/* interface types, classed types, instantiated types */
279
GBaseInitFunc base_init;
280
GBaseFinalizeFunc base_finalize;
282
/* classed types, instantiated types */
283
GClassInitFunc class_init;
284
GClassFinalizeFunc class_finalize;
285
gconstpointer class_data;
287
/* instantiated types */
288
guint16 instance_size;
290
GInstanceInitFunc instance_init;
293
const GTypeValueTable *value_table;
299
>The important fields of this structure are pretty self-explanatory.
311
fields here. Once Glib has a correctly filled in copy of
312
this structure, it knows how to create objects of a particular type. </P
328
>WIDGETNAME_class_init()</TT
329
> function initializes the fields of
330
the widget's class structure, and sets up any signals for the
331
class. For our Tictactoe widget it looks like:</P
339
CLASS="PROGRAMLISTING"
346
static guint tictactoe_signals[LAST_SIGNAL] = { 0 };
349
tictactoe_class_init (TictactoeClass *klass)
351
tictactoe_signals[TICTACTOE_SIGNAL] =
352
g_signal_new ("tictactoe",
353
G_TYPE_FROM_CLASS (klass),
354
G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
355
G_STRUCT_OFFSET (TictactoeClass, tictactoe),
357
g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
363
>Our widget has just one signal, the <TT
367
invoked when a row, column, or diagonal is completely filled in. Not
368
every composite widget needs signals, so if you are reading this for
369
the first time, you may want to skip to the next section now, as
370
things are going to get a bit complicated.</P
380
CLASS="PROGRAMLISTING"
381
>guint g_signal_new( const gchar *signal_name,
383
GSignalFlags signal_flags,
385
GSignalAccumulator *accumulator,
387
GSignalCMarshaller *c_marshaller,
395
>Creates a new signal. The parameters are:</P
404
>: The name of the signal.</P
411
>: The ID of the object that this signal applies
412
to. (It will also apply to that objects descendants.)</P
419
>: Whether the default handler runs before or after
420
user handlers and other flags. Usually this will be one of
423
>G_SIGNAL_RUN_FIRST</TT
426
>G_SIGNAL_RUN_LAST</TT
428
although there are other possibilities. The flag
432
> specifies that no extra code needs to
433
run that performs special pre or post emission adjustments. This means that
434
the signal can also be emitted from object external code.</P
441
>: The offset within the class structure of
442
a pointer to the default handler.</P
449
>: For most classes this can
457
>: User data that will be handed
458
to the accumulator function.</P
465
>: A function that is used to invoke the signal
466
handler. For signal handlers that have no arguments other than the
467
object that emitted the signal and user data, we can use the
468
pre-supplied marshaller function <TT
470
>g_cclosure_marshal_VOID__VOID</TT
478
>: The type of the return value.</P
485
>: The number of parameters of the signal handler
486
(other than the two default ones mentioned above)</P
493
>: The types of the parameters.</P
497
>When specifying types, the following standard types can be used:</P
505
CLASS="PROGRAMLISTING"
534
> returns a unique integer identifier for the
535
signal, that we store in the <TT
537
>tictactoe_signals</TT
539
index using an enumeration. (Conventionally, the enumeration elements
540
are the signal name, uppercased, but here there would be a conflict
544
> macro, so we called it <TT
546
>TICTACTOE_SIGNAL</TT
562
>Each class also needs a function to initialize the object
563
structure. Usually, this function has the fairly limited role of
564
setting the fields of the structure to default values. For composite
565
widgets, however, this function also creates the component widgets.</P
573
CLASS="PROGRAMLISTING"
575
tictactoe_init (Tictactoe *ttt)
579
gtk_table_resize (GTK_TABLE (ttt), 3, 3);
580
gtk_table_set_homogeneous (GTK_TABLE (ttt), TRUE);
582
for (i=0;i<3; i++)
583
for (j=0;j<3; j++)
585
ttt->buttons[i][j] = gtk_toggle_button_new ();
586
gtk_table_attach_defaults (GTK_TABLE (ttt), ttt->buttons[i][j],
588
g_signal_connect (G_OBJECT (ttt->buttons[i][j]), "toggled",
589
G_CALLBACK (tictactoe_toggle), ttt);
590
gtk_widget_set_size_request (ttt->buttons[i][j], 20, 20);
591
gtk_widget_show (ttt->buttons[i][j]);
607
>There is one more function that every object (except for abstract
608
classes like Bin that cannot be instantiated) needs to have - the
609
function that the user calls to create an object of that type. This is
610
conventionally called <TT
612
>OBJECTNAME_new()</TT
614
widgets, though not for the Tictactoe widgets, this function takes
615
arguments, and does some setup based on the arguments. The other two
616
functions are specific to the Tictactoe widget. </P
620
>tictactoe_clear()</TT
621
> is a public function that resets all the
622
buttons in the widget to the up position. Note the use of
625
>g_signal_handlers_block_matched()</TT
626
> to keep our signal handler for
627
button toggles from being triggered unnecessarily.</P
631
>tictactoe_toggle()</TT
632
> is the signal handler that is invoked when the
633
user clicks on a button. It checks to see if there are any winning
634
combinations that involve the toggled button, and if so, emits
635
the "tictactoe" signal.</P
643
CLASS="PROGRAMLISTING"
647
return GTK_WIDGET ( g_object_new (TICTACTOE_TYPE, NULL));
651
tictactoe_clear (Tictactoe *ttt)
655
for (i=0;i<3;i++)
656
for (j=0;j<3;j++)
658
g_signal_handlers_block_matched (G_OBJECT (ttt->buttons[i][j]),
660
0, 0, NULL, NULL, ttt);
661
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ttt->buttons[i][j]),
663
g_signal_handlers_unblock_matched (G_OBJECT (ttt->buttons[i][j]),
665
0, 0, NULL, NULL, ttt);
670
tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt)
674
static int rwins[8][3] = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
675
{ 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
676
{ 0, 1, 2 }, { 0, 1, 2 } };
677
static int cwins[8][3] = { { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
678
{ 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
679
{ 0, 1, 2 }, { 2, 1, 0 } };
683
for (k=0; k<8; k++)
688
for (i=0;i<3;i++)
690
success = success &&
691
GTK_TOGGLE_BUTTON(ttt->buttons[rwins[k][i]][cwins[k][i]])->active;
693
ttt->buttons[rwins[k][i]][cwins[k][i]] == widget;
696
if (success && found)
698
g_signal_emit (G_OBJECT (ttt),
699
tictactoe_signals[TICTACTOE_SIGNAL], 0);
708
>And finally, an example program using our Tictactoe widget:</P
716
CLASS="PROGRAMLISTING"
717
>#include <gtk/gtk.h>
718
#include "tictactoe.h"
720
/* Invoked when a row, column or diagonal is completed */
722
win (GtkWidget *widget, gpointer data)
725
tictactoe_clear (TICTACTOE (widget));
729
main (int argc, char *argv[])
734
gtk_init (&argc, &argv);
736
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
738
gtk_window_set_title (GTK_WINDOW (window), "Aspect Frame");
740
g_signal_connect (G_OBJECT (window), "destroy",
741
G_CALLBACK (exit), NULL);
743
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
745
/* Create a new Tictactoe widget */
746
ttt = tictactoe_new ();
747
gtk_container_add (GTK_CONTAINER (window), ttt);
748
gtk_widget_show (ttt);
750
/* And attach to its "tictactoe" signal */
751
g_signal_connect (G_OBJECT (ttt), "tictactoe",
752
G_CALLBACK (win), NULL);
754
gtk_widget_show (window);
770
SUMMARY="Footer navigation table"
783
><<< Previous</A
801
>Next >>></A
809
>The Anatomy Of A Widget</TD
823
>Creating a widget from scratch</TD
b'\\ No newline at end of file'