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 ... endNotice 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 endThose 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 endNotice 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