There are a few things you probably noticed in the previous examples
that need explaining. The gint
,
gchar
, etc. that you see are typedefs
to int
and char
,
respectively, that are part of the GLib system. This is done to get around
that nasty dependency on the size of simple data types when doing
calculations.
A good example is gint32
which will be
typedef'd to a 32-bit integer for any given platform, whether it be the
64 bit alpha, or the 32-bit i386. The typedefs are very straightforward
and intuitive.
You'll also notice GTK's ability to use GtkWidget when the function calls for an Object. GTK is an object oriented design, and a widget is an object.
Lets take another look at the
gtk_signal_connect
declaration.
function g_signal_connect (object : gpointer; name : pgchar; func : GCallback; func_data : gpointer): gulong;
Notice the gint
return value? This is a tag
that identifies your callback function. You may have as many callbacks
per signal and per object as you need, and each will be executed in turn,
in the order they were attached.
This tag allows you to remove this callback from the list by using:
procedure g_signal_handler_disconnect (object : gpointer; id : gunlong);
So, by passing in the widget you wish to remove the handler from, and the tag returned by one of the signal_connect functions, you can disconnect a signal handler.
You can also temporarily disable signal handlers with the
gtk_signal_handler_block
() and
gtk_signal_handler_unblock
() family
of functions.
procedure g_signal_handler_block (object : gpointer; id : gulong); procedure g_signal_handlers_block_by_func (object : gpointer; func : GCallback; data : gpointer); procedure g_signal_handler_unblock (object : gpointer; id : gulong); procedure g_signal_handlers_unblock_by_func (object : gpointer; func : GCallback; data : gpointer);
Let's take a look at a slightly improved helloworld with better examples of callbacks. This will also introduce us to our next topic, packing widgets.
program helloworld2; uses glib2, gdk2, gtk2, sysutils; { Our new improved callback. The data passed to this function is printed to stdout. } procedure callback (widget : PGtkWidget; data : gpointer); cdecl; begin writeln('Hello again - ' + pchar(data) + ' was pressed'); end; { Another callback } function delete_event (widget : PGtkWidget; event: PGdkEvent; data : gpointer): boolean; cdecl; begin gtk_main_quit(); delete_event := false; end; var window, button, box1 : PGtkWidget; begin { This is called in all GTK applications. Arguments are parsed from the command line and are returned to the application. } gtk_init(@argc, @argv); { Create a new window } window := gtk_window_new(GTK_WINDOW_TOPLEVEL); { This is a new call, which just sets the title of our new window to "Hello Buttons!" } gtk_window_set_title(GTK_WINDOW(window), 'Hello Buttons!'); { Here we just set a handler for delete_event that immediately exits GTK. } g_signal_connect(window, 'delete-event', G_CALLBACK(@delete_event), nil); { Sets the border width of the window. } gtk_container_set_border_width(GTK_CONTAINER(window), 10); { We create a box to pack widgets into. This is described in detail in the "packing" section. The box is not really visible, it is just used as a tool to arrange widgets. } box1 := gtk_hbox_new(false, 0); { Put the box into the main window. } gtk_container_add(GTK_CONTAINER(window), box1); { Creates a new button with the label "Button 1". } button := gtk_button_new_with_label('Button 1'); { Now when the button is clicked, we call the "callback" function with a pointer to "button 1" as its argument } g_signal_connect(button, 'clicked', G_CALLBACK(@callback), pchar('button 1')); { Instead of gtk_container_add, we pack this button into the invisible box, which has been packed into the window. } gtk_box_pack_start(GTK_BOX(box1), button, true, true, 0); { Always remember this step, this tells GTK that our preparation for this button is complete, and it can now be displayed. } gtk_widget_show(button); { Do these same steps again to create a second button } button := gtk_button_new_with_label('button 2'); { Call the same callback function with a different argument, passing a pointer to "button 2" instead. } g_signal_connect(button, 'clicked', G_CALLBACK(@callback), pchar('Button 2')); gtk_box_pack_start(GTK_BOX(box1), button, true, true, 0); { The order in which we show the widgets is not really important, but I recommend showing the window last, so it all pops up at once. } gtk_widget_show(button); gtk_widget_show(box1); gtk_widget_show(window); { Rest in gtk_main and wait for the fun to begin! } gtk_main(); end.
Compile this program using the same fpc command as for our first example.
You'll notice this time there is no easy way to exit the program, you have
to use your window manager or command line to kill it. A good exercise
for the reader would be to insert a third 'Quit' button that will exit
the program. You may also wish to play with the options
to gtk_box_pack_start
() while reading the next section.
Try resizing the window, and observe the behavior.