Now that we know the theory behind this, let's clarify by walking through the example helloworld.st program.
Lines 9-77 define the HelloWorld class
that contains all the callbacks as object methods and the object instance
show method. Let's examine the callback methods.
Lines 14-16 define the hello callback
method that will be called when the button is "clicked". When called the
method, prints "Hello World" to the console.
hello [ 'Hello World' printNl ]
The next callback (lines 18-29) is a bit special. The "delete_event" occurs when the window manager sends this event to the application. We have a choice here as to what to do about these events. We can ignore them, make some sort of response, or simply quit the application.
The value you return in this callback lets GTK+ know what action to take. By returning true, we let it know that we don't want to have the "destroy" signal emitted, keeping our application running. By returning false, we ask that "destroy" be emitted, which in turn will call our "destroy" signal handler. Note the comments have been removed for clarity.
delete_event [ 'delete event occurred' printNl. ^ false ]
The destroy callback method (lines
31-34) causes the program to quit by calling
GTK.Gtk mainQuit . This function tells GTK+ that it is
to exit from GTK.Gtk main when control is returned to
it.
destroy [ 'destroy signal occurred' printNl. GTK.Gtk mainQuit ]
Lines 36-76 define the show method that creates
the window and widgets used by the program.
Line 38 creates a new window, but it is not displayed until we direct GTK+ to show the window near the end of our program. The window reference is saved in an object instance variable (window) for later access.
window := GTK.GtkWindow new:GTK.Gtk gtkWindowToplevel.
Lines 45 and 50 illustrate two examples of connecting a signal
handler to an object, in this case, the window. Here, the
"delete_event" and "destroy" signals are caught. The first is emitted when
we use the window manager to kill the window, or when we use the
GtkWidget destroy method
call. The second is emitted when, in the "delete_event" handler, we return
false.
window connectSignal: 'delete_event' to: self selector: #delete userData: nil.
window connectSignal: 'destroy' to: self selector: #destroy userData: nil.
Line 53 sets an attribute of a container object (in this case
the window) to have a blank area along the inside of it
10 pixels wide where no widgets will be placed. There are other similar
methods that we will look at in Chapter 18, Setting Widget Attributes
window setBorderWidth: 10.
Line 56 creates a new button and saves a reference to it in
button. The button will have the label "Hello World"
when displayed.
button := GTK.GtkButton newWithLabel: 'Hello World'.
In line 61 we attach a signal handler to the button so when it
emits the "clicked" signal, our hello callback
method is called. We are not passing any data to
hello so we just pass nil as
the data. Obviously, the "clicked" signal is emitted when we click the
button with our mouse pointer. The callback would then be called with one less parameter.
button connectSignal: 'clicked' to: self selector: #hello userData: nil.
We are also going to use this button to exit our program. Line
66 illustrates how the "destroy" signal may come from either the window
manager, or from our program. When the button is "clicked", same as above,
it calls the hello callback first, and then the
following one in the order they are set up. You may have as many callbacks
as you need, and all will be executed in the order you connected
them.
Since we want to use the GtkWidget
destroy method that accepts one argument (the
widget to be destroyed - in this case the window), we use
the connectSignal:to:selector:userData: method and pass it the
reference to the window. The connect
method arranges to pass the window as the first
callback argument instead of the button.
When the GtkWidget
destroy method is called it will cause the
"destroy" signal to be emitted from the window which will in turn cause the
HelloWorld destroy method
to be called to end the program.
button connectSignal: 'clicked' to: self selector: #destroy userData: window.
Line 69 is a packing call, which will be explained in depth later on in Chapter 4, Packing Widgets . But it is fairly easy to understand. It simply tells GTK+ that the button is to be placed in the window where it will be displayed. Note that a GTK+ container can only contain one widget. There are other widgets, described later, that are designed to layout multiple widgets in various ways.
window add: button.
Now we have everything set up the way we want it to be. With all the signal handlers in place, and the button placed in the window where it should be, we ask GTK+ (lines 72 and 75) to "show" the widgets on the screen. The window widget is shown last so the whole window will pop up at once rather than seeing the window pop up, and then the button forming inside of it. Although with such a simple example, you'd never notice.
button show.
window show.
Widgets also have a hide that is the
opposite of show. It doesn't actually
destroy the widget, but it removes the widget renderining from your
display. This can be reversed with another
show call.
Line 82 creates an instance of the HelloWorld class
and saves a reference to it in the hello variable. Line 82 show
method to build the main window and the button. Line 83 start the GTK+ event processing loop.
hello := HelloWorld new. hello show. GTK.Gtk main
Now, when we click the mouse button on a GTK+ button, the widget
emits a "clicked" signal. In order for us to use this information, our
program sets up a signal handler to catch that signal, which dispatches the
function of our choice. In our example, when the button we created is
"clicked", the hello method is called,
and then the next handler for this signal is called. The next handler calls the widget
destroy function with the window as its argument
thereby causing the window to emit the "destroy" signal, which is caught,
and calls our HelloWorld
destroy method
Another course of events is to use the window manager to kill
the window, which will cause the "delete_event" to be emitted. This will
call our "delete_event" handler. If we return true here,
the window will be left as is and nothing will happen. Returning
false will cause GTK+ to emit the "destroy" signal that
causes the HelloWorld "destroy" callback to be
called, exiting GTK.