I am writing a family of new widgets for lablgtk2, the OCaml bindings for Gtk+. Some of these widgets can edit or present a fairly complex information, I am therefore interested in using model-view-controler or subject-observer, similar to what can be found in the GTree
module.
This module defines a GTree.model
and a GTree.view
class, each having signals which can be connected to, and a GTree.model
can be attached to one or more GTree.view
's.
Imitating this organisation in pure OCaml is not that trivial, because the code available in the library is a binding of the C-library. I need to go through the following steps:
- Defining new widgets
- Defining new signals
- Triggering these new signals
- Defining new models
I could go through 1 and 2 but I am not sure how to do 3 and 4. How to do these right?
Defining new widgets
The definition of new widgets itself is not problematic. The new widget is typically a specialised version of the Gnome canvas or a composite. In the former case, our new widget can inherit from the Gnome canvas as a GObj.widget and in the latter case, we can use the GObj.widget provided by the container used to hold the composite. This typically looks like
class view () =
let vbox = GPack.vbox () in
…
object(self)
inherit GObj.widget vbox#as_widget
…
end
Defining new signals
The bindings give plenty of examples for code defining new signals, so that we can define new signals for our widgets, as illustrated by the following snippet, considering the simple case of signals without parameters:
open GtkSignal
module Event =
struct
let plop : ([>`widget], unit -> unit) t = {
name = "plop_event";
classe = `widget;
marshaller = marshal_unit;
}
let fizz : ([>`widget], unit -> unit) t = {
name = "fizz_event";
classe = `widget;
marshaller = marshal_unit;
}
end
class pill_signals obj =
object (self)
inherit ['a] GObj.gobject_signals (obj :> Gtk.widget Gobject.obj)
method plop = self#connect Event.plop
method fizz = self#connect Event.fizz
end
With these definitions, our view
widget can expose these signals by defining an appropriate connect
method:
method connect =
new pill_signals obj
Triggering the new signals
It seems that the function GtkSignal.emit
serves the purpose of emitting a signal to an object, triggering the registered callbacks. This functions as the following signature:
val emit :
'a Gobject.obj ->
sgn:('a, 'b) GtkSignal.t ->
emitter:(cont:('c Gobject.data_set array -> 'd) -> 'b) ->
conv:(Gobject.g_value -> 'd) -> 'b
The first two parameters are self-explaining, but it is not that clear, what the two remaining ones are. Unfortunately, there is no use example in lablgtk source code, as signals are emitted from the C-side of the code. These two arguments seems to be related with the preparation of the arguments of the signal, materialised as a 'c Gobject.data_set array
and the retrieval of the yielded value with the argument labeled ~conv
. Nevertheless, the role of the ~cont
-argument in the emitter still has to be cleared.
Defining the new model
The tricky part in the definition of the model, is that it should inherit from GObj.object
in order to be able to send an receive signals. Unfortunately, there is no function allowing to directly define a minimal Gtk+ object. The farthest I went in this direction was
module Model =
struct
let create () =
GtkObject.make ~classe:"GObject" []
end
let model () =
new model (Model.create ())
Calling the function model
to instantiate the corresponding object yields the message:
Gtk-CRITICAL **: IA__gtk_object_sink: assertion 'GTK_IS_OBJECT (object)' failed
Clearly, there is something fishy here, most probably the parameter list (the empty list in the snippet above) was too small.
LablGTK provides a nice interface to Gtk signaling mechanisms, which allows us to use it without tinkering with
GtkSignal
and marshalling functions. This interface is provided byGUtil
and is neatly documented.How to use GUtil, as described in the module documentation
To add ML signals to a LablGTK object:
You can also add ML signals to an arbitrary object; just inherit from
ml_signals
in place ofwidget_signals
andadd_ml_signals
.It is now easy to address the points 1, 2, 3, and 4 above:
GUtil
to define new signals instead ofGtkSignal
call
method of['a] GUtil.signal
.GtkSignal
anymore, there is actually no problem.