Hello, world

Let us write a program that creates a window containing a button. The button can be be clicked to issue the message "Hello, world". In addition, the window can be closed by a simple click.

The following code essentially outlines an implementation of such a program:

fun
mymain
(
) : GtkWindow1 = let
//
val window = window_create ()
val button = button_create ()
//
val () = window_add_button (window, button)
//
val () = button_handle_clicked (button)
val () = window_handle_destroy (window)
val () = window_handle_delete_event (window)
//
val () = gtk_widget_show (window)
val () = gtk_widget_show_unref (button)
//
in
  window
end // end of [mymain]

The interface for some of the functions in the above code is given as follows:

//
extern
fun window_create (): GtkWindow1
extern
fun button_create (): GtkButton1
extern
fun window_add_button (!GtkWindow1, !GtkButton1): void
extern
fun button_handle_clicked (!GtkButton1): void
extern
fun window_handle_destroy (!GtkWindow1): void
extern
fun window_handle_delete_event (!GtkWindow1): void
//

The type GtkWindow1 is for a GTK-window that cannot be null. Similarly, the type GtkButton1 is for a GTK-button that cannot be null. Note that both GtkWindow1 and GtkButton1 are linear. The function window_add_button adds a GTK-button into a GTK-window. The functions button_handle_clicked, window_handle_destroy, and window_handle_delete_event are called to connect signal handlers with given GTK-widgets.

The function window_create is implemented as follows:

implement
window_create () = let
//
val widget =
  gtk_window_new (GTK_WINDOW_TOPLEVEL)
//
val ((*void*)) = assertloc (ptrcast(widget) > 0)
//
val ((*void*)) =
  gtk_window_set_title (widget, (gstring)"Hello, world")
//
in
  widget
end // end of [window_create]

How the title of a window is displayed depends on the underlying window manager (WM).

The function button_create is implemented as follows:

implement
button_create () = let
//
val widget =
  gtk_button_new_with_label ((gstring)"Hello, world")
//
val ((*void*)) = assertloc (ptrcast(widget) > 0)
//
in
  widget
end // end of [button_create]

Note that the argument of gtk_button_new_with_label determines the label on the created button when the latter is displayed.

The function window_add_button is implemented as follows:

implement
window_add_button
  (window, button) = let
//
val () =
gtk_container_set_border_width (window, (guint)10)
//
in
//
gtk_container_add (window, button)
//
end (* end of [window_add_button] *)

where the funtion gtk_container_add adds a GTK-widget into a GTK-container. We will encounter functions for adding multiple GTK-widgets into a GTK-container later.

The function button_handle_clicked is implemented as follows:

implement
button_handle_clicked
  (button) = () where
{
//
fun f (): void = println! ("Hello, world")
//
val id =
g_signal_connect
(
  button, (gsignal)"clicked", G_CALLBACK(f), (gpointer)NULL
) (* end of [val] *)
//
} (* end of [button_handle_clicked] *)

where the function g_signal_connect is called to connect a signal handler with a widget (given as its first argument).

The function window_handle_destroy is implemented as follows:

implement
window_handle_destroy
  (window) = () where
{
//
val id =
g_signal_connect
(
  window, (gsignal)"destroy", G_CALLBACK(gtk_main_quit), (gpointer)NULL
) (* end of [val] *)
//
} (* end of [window_handle_destroy] *)

Note that a call to gtk_main_quit terminates the main loop of GTK.

The function window_handle_delete_event is implemented as follows:

implement
window_handle_delete_event
  (window) = () where
{
//
fun f (x: GtkWindow1): gboolean =
  let val () = gtk_widget_destroy0 (x) in GTRUE end
//
val id =
g_signal_connect
(
  window, (gsignal)"delete-event", G_CALLBACK(f), (gpointer)NULL
) (* end of [val] *)
//
} (* end of [window_handle_delete_event] *)

In GTK, a handler for a signal of some name ending with event seems to be required to return a boolean value (of the type gboolean). That the handler f returns GTRUE in the above code means that the handling of delete-event signal is finished (and thus no more handlers are to be invoked for it). Also note that calling the function gtk_widget_destroy0 on a widget sends a destroy signal to the widget. In this case, the signal triggers a call to gtk_main_quit, which terminates the main loop of GTK.

The function gtk_widget_show_unref essentially calls gtk_widget_show and then decrease the reference count of its argument by 1. If the last line in the body of mymain changes to the following one:

val () = gtk_widget_show (button)

then a type-error is to be reported during typechecking due to the linear value button being leaked. Getting type-errors as such is a great advantage that one can have when programming with ATS/GTK.

The entirety of the presented code can be found in chap_hello.dats, and there is a Makefile available for compiling the file.