This manual describes the GTK+ 2.6 language binding for Alice.
GTK+ is a widget set that allows to build graphical user interfaces. The language binding described herein enables Alice programmers to build graphical user interfaces using this widget set. The binding provides access to GTK+ functionality by lifting its C interface almost one-to-one to Alice. It hence is a low-level interface without additional abstractions, but provides almost the full GTK+ functionality.
This manual summarizes the basics of GTK+ and describes how it is mapped to Alice. The goal is to enable Alice programmers to make use of the original reference documentation to write GTK+ applications in Alice.
GTK+ is organized into visual units called widgets. Some widget classes are defined by refinement of some parent widget class using inheritance. GTK+ has an object-oriented organization but is implemented in C. Thus, it can be easily mapped to Alice ML.
The general steps to create a GTK+ widget are:
For each wrapped type there is a distinct corresponding substructure which contains its functions and in some cases also its properties. For example the GtkButton widget is wrapped like this:
structure Gtk :
sig
type object
...
structure Button :
sig
(* constructors *)
val newWithMnemonic : string -> object
val newFromStock : string -> object
val newWithLabel : string -> object
val new : unit -> object
(* methods *)
val getImage : object -> object
val setImage : object * object -> unit
...
end
...
end
Notice that nearly all pointer types, especially those derived from GObject and structs are represented by a single type called object.
Likewise every enum type is represented by a distinct type contained in a distinct substructure of its library's structure. For example:
structure Gdk :
sig
...
structure WindowState :
sig
datatype flag =
ABOVE
| BELOW
| FULLSCREEN
| ICONIFIED
| MAXIMIZED
| STICKY
| WITHDRAWN
type t = flag list
val getType : unit -> gtype
val toInt : t -> int
val fromInt : int -> t
end
structure PropertyState :
sig
datatype t =
DELETE
| NEW_VALUE
val getType : unit -> gtype
val toInt : t -> int
val fromInt : int -> t
end
...
end
Notice that flag types and normal enum types are wrapped differently. Enum types
are wrapped directly into a corresponding datatype. Flag types are wrapped into
lists over the respective datatypes.
A widget object is created by instantiating a widget class. The constructors are the functions starting with new; some widget classes have multiple constructors. In those cases the constructor name usually contains a hint on what is different to the default constuctor. For example,
val button = Gtk.Button.newWithLabel "Hello, World!"
creates a new object for a button labelled "Hello, World!", whereas
Gtk.Button.new () would have created a button object with an empty
label.
Object creation yields a freshly created widget object whose attributes are set to reasonable default values. It will not appear on the screen until it has been packed into a container which then is made visible.
GTK+ is event-driven, which means that when an event occurs, control is passed to the appropriate function.
Signals. Events in GTK+ are implemented through signals. (Note that these signals are unrelated to Unix system signals.) When an event occurs, such as pressing a mouse button, the appropriate signal will be emitted by the corresponding widget. Some signals are common to all widget classes, such as "delete-event", while others are widget-specific, such as "toggled" for a toggle button. Signals are denoted by strings.
Connecting Handlers. The Gtk.signalConnect function allows to catch signals and cause invocation of actions. For instance,
val id = Gtk.signalConnect (widget, signal, callback)
causes callback to be invoked each time signal is emitted on widget. The result id is a unique identifier which can be used to manually remove the handler.
Callbacks. Callbacks are defined as functions:
fun myCallBack (object, args) = ...
where object denotes the object and args is the list of the arguments associated with the signal.
Low-level Events. In addition to the high-level signals described above, there is a set of events that reflect the X Window System event mechanism (simulated on non-X platforms such as Windows). These can be caught using the special signal "event". For more details, see the reference.
GTK+ widgets organize their data using properties. Some Properties are read-only, others are mutable. The latter allow for the widget to be reconfigured after creation.
Properties are named by strings and have an associated (typed) value. Many widgets define accessors for commonly used properties, but in general they can be read using the Prop.rawGet function and written to using the Gtk.rawSet function. Trying to access a non-existing attribute yields an error as well as trying to set a property of the wrong type. Both functions operate on gvalues, which can be converted from and to alice values via the functions in Value.
For some important properties of certain widgets we have predefined special values of type 'a prop where 'a is the type of the associated value. For example the type GtkTextTag is wrapped like this:
structure TextTag :
sig
(* constructors *)
val new : string -> object
(* methods *)
val event : object * object * object * object -> bool
val setPriority : object * int -> unit
val getPriority : object -> int
val getType : unit -> gtype
(* properties *)
val weightSet : bool prop
val weight : int prop
val sizeSet : bool prop
val size : int prop
val styleSet : bool prop
val style : Pango.Style.t prop
val name : string prop
val editableSet : bool prop
val editable : bool prop
val foregroundGdk : object prop
val foreground : string prop
val fontDesc : object prop
val font : string prop
val backgroundGdk : object prop
val background : string prop
end
Those properties can be accessed and modified using the functions
Prop.get : 'a prop -> object -> 'aand
Prop.set : 'a prop -> object * 'a -> unit.
Widgets are laid out on the screen using so-called containers. Container widgets themselves usually do not provide any visible information, but display their child widgets according to a built-in strategy. For example, an HBox container will align its children horizontally on the screen. A container can contain other container widgets.
GTK+ provides a variety of different container types covering the "daily" needs, which all are descendants of the container class.
Bins. Window is a subclass of Bin, which is the superclass of all containers accepting at most one child. Bins do not do any layouting. If our window had contained more than one child, we would have needed to create another container to lay out the children, which would then have been the window's single child.
The last step to start working with widgets on the screen is to make them visible. This can be done manually by a bottom-up traversal of the container tree and calling each container's show function. This is what the topmost container's showAll method does automatically.
With few exceptions, signals emitted by a widget are only caught as long as it remains visible.
GTK+ widgets do a lot of error-checking internally, but those errors are just reported to the screen instead of being raised as an Alice exception. Errors discovered in the language binding's code are reported as exceptions (this includes type errors like passing a GtkTextBuffer where a GtkWindow is expected.).
This chapter describes the details of how the C API is mapped to Alice. This knowledge is required when you want to make use of the original reference documentation. (For the Canvas, the documentation can be found here.
The Alice GTK+ library is organized into the following components:
Each module represents a namespace. The corresponding API constants and functions are mapped to datatypes and functions organized into substructures in these namespaces.
We will illustrate how C structure fields and C functions are mapped to by an example. Consider the C API for GtkButton, which is derived from GtkBin:
struct _GtkButton {
GtkBin bin;
GdkWindow *event_window;
gchar *label_text;
guint activate_timeout;
guint constructed : 1;
guint in_button : 1;
guint button_down : 1;
guint relief : 2;
guint use_underline : 1;
guint use_stock : 1;
guint depressed : 1;
guint depress_on_activate : 1;
};
/* constructors */
GtkWidget *gtk_button_new();
GtkWidget *gtk_button_new_with_label(const gchar *label);
GtkWidget *gtk_button_new_from_stock(const gchar *stock_id);
GtkWidget *gtk_button_new_with_mnemonic(const gchar *label);
/* signal emitters */
void gtk_button_pressed(GtkButton *button);
void gtk_button_released(GtkButton *button);
void gtk_button_clicked(GtkButton *button);
void gtk_button_enter(GtkButton *button);
void gtk_button_leave(GtkButton *button);
/* attribute accessors */
void gtk_button_set_relief(GtkButton *button, GtkReliefStyle newstyle);
GtkReliefStyle gtk_button_get_relief(GtkButton *button);
void gtk_button_set_label(GtkButton *button, const gchar *label);
const gchar *gtk_button_get_label(GtkButton *button);
void gtk_button_set_use_underline(Gtkbutton *button, gboolean use_underline);
gboolean gtk_button_get_use_underline(GtkButton *button);
void gtk_button_set_use_stock(GtkButton *button, gboolean use_stock);
gboolean gtk_button_get_use_stock(GtkButton *button);
In Gtk you will find a substructure Button which looks like this:
structure Button :
sig
(* constructors *)
val newWithMnemonic : string -> object
val newFromStock : string -> object
val newWithLabel : string -> object
val new : unit -> object
(* methods *)
val getImage : object -> object
val setImage : object * object -> unit
val getAlignment : object * real * real -> real * real
val setAlignment : object * real * real -> unit
val getFocusOnClick : object -> bool
val setFocusOnClick : object * bool -> unit
val getUseStock : object -> bool
val setUseStock : object * bool -> unit
val getUseUnderline : object -> bool
val setUseUnderline : object * bool -> unit
val getLabel : object -> string
val setLabel : object * string -> unit
val getRelief : object -> ReliefStyle.t
val setRelief : object * ReliefStyle.t -> unit
val leave : object -> unit
val enter : object -> unit
val clicked : object -> unit
val released : object -> unit
val pressed : object -> unit
val getType : unit -> gtype
(* properties *)
end
General Scheme. The general scheme is that all underscored identifiers are translated to use camel-casing. Both the name of the library and and the name of the type the methods belong are cut off. The first letter of each function is downcased.
Field Accessors. If access to the fields of a struct is needed accessor functions are generated. These functions follow the standard naming conventions and use the getField{FieldName} and setField{FieldName} naming pattern. For example the GdkColor type
struct _GdkColor
{
guint32 pixel;
guint16 red;
guint16 green;
guint16 blue;
};
is wrapped like this:
structure Gdk :
sig
structure Color :
sig
(* constructors *)
val new : { blue : int, green : int, red : int } -> object
(* methods *)
val parse : string * object -> int
val getFieldRed : object -> int
val setFieldRed : object * int -> unit
val getFieldGreen : object -> int
val setFieldGreen : object * int -> unit
val getFieldBlue : object -> int
val setFieldBlue : object * int -> unit
end
end
Notice that for structs which do not have a creation function
a function new is generated which takes the initial values of the
fields in a record. Also notice that internal fields of a struct are
not wrapped.
Constants. As explained above enumeration and flag types are translated into datatypes and lists over datatypes.
| C Type | Alice Type |
|---|---|
| gint, guint, glong, gulong | int |
| gboolean | bool |
| gchar, guchar | char |
| gfloat, gdouble | real |
| enumerations, flags | distinct datatypes |
| gchar*, guchar* | string |
| gchar*[], guchar*[] | string list |
| GdkEvent* | Gdk.event |
| GList* | Gtk.object list |
| all other pointers (i.e., GtkWidget*) | Gtk.object |
The above table shows the mapping of the C types onto Alice types. Values are converted back and forth transparently, preserving identity whenever possible.
Inout Arguments. The int* and double* types are considered to be inout arguments. For example the the function void gtk_widget_get_size_request(GtkWidget *,int *, int *) wrapped as val getSizeRequest : object * int * int -> int * int.
Gdk Events. The records carried by constructors of the Gdk.event type represent the structs contained in the GdkEvent union.
A small demo application using GTK is available:
Compile with:
alicec scramble.aml
Run with:
alicerun scramble