alice
library
manual.

Alice Project

gtk
library

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+ Basics _________________________________________________

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:

  1. Instantiate a widget object using the appropriate constructor.
  2. Connect appropriate handlers to all signals and events you wish to react to.
  3. Set attributes of the widget.
  4. Pack the widget into a container.
  5. Make the widget visible.

Overall structure of the library

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.

Widget Creation

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.

Signals and Callbacks

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.

Widget Properties

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 -> 'a
and
Prop.set : 'a prop -> object * 'a -> unit
.

Containers

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.

Visibility

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.

Error Handling

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.).


________ How the C API is mapped to Alice ____________________________

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.

Modules

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.

Name mapping

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.

Types

C TypeAlice Type
gint, guint, glong, gulongint
gbooleanbool
gchar, gucharchar
gfloat, gdoublereal
enumerations, flagsdistinct 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.


________ Example _____________________________________________________

A small demo application using GTK is available:

Compile with:

alicec scramble.aml

Run with:

alicerun scramble


last modified 2007/Mar/30 17:10