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
GtkSignaland marshalling functions. This interface is provided byGUtiland 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_signalsin place ofwidget_signalsandadd_ml_signals.It is now easy to address the points 1, 2, 3, and 4 above:
GUtilto define new signals instead ofGtkSignalcallmethod of['a] GUtil.signal.GtkSignalanymore, there is actually no problem.