A signal is defined by a class or interface and is emitted by an object (instance) when a particular event occurs. A function is called on emission of a signal by connecting to the signal and specifying the function as the handler.
The special structure provides support for working with signals in a type safe way.
Referring to signals
A signal name is string of ASCII letters and digits with words separated by a hyphen (or underscore) where the first word starts with a letter. For a signal “some-signal-name” of a class or interface Namespace.Type, the hierarchical SML name is Namespace.Type.someSignalNameSig.
For example, the signal “insert-at-cursor” of the class Gtk.TextView is Gtk.TextView.insertAtCursorSig
In order to provide a type-safe interface for using signals, each signal has an SML specification constraining the instance type that emits the signal and the type of the handler function.
The specification of Namespace.Type.someSignalNameSig in the signature NAMESPACE_TYPE has the form:
val someSignalNameSig : handlerType -> instanceType Signal.t
is 'a class which represents the class or interface on which the signal is emitted or any subclass of the class;
is the type of the handler function.
For example, the specification of Gtk.TextView.insertAtCursorSig is
val insertAtCursorSig : (string -> unit) -> 'a class Signal.t
As the example above shows, handlerType does not have an argument for the instance that emits the signal.
At present, there is support only for handling the emission of a signal from an instance of a class or interface. There is no support for
handling a signal on all instances of a class or interface, i.e. overriding the class closure
Connecting signal handlers
A handler function is connected to a signal of an object (instance) using Signal.connect or Signal.connectAfter. For a signal “a” of a class ClassNamespace.Class, the handler function f is connected to the signal on an object obj by
Signal.connect obj (ClassNamespace.Class.aSig, f)
For a signal “a” of an interface IfaceNamespace.Iface implemented by the class ClassNamespace.Class, the handler function f is connected to the signal on the object obj by
Signal.connect (ClassNamespace.Class.asIface obj) (IfaceNamespace.Iface.aSig, f)
The function returns a handler id to allow the signal connection to be modified or checked subsequently.
For example, to handle emission of the signal “insert-at-cursor” from the object textView using the function onInsert, connect to the signal as follows:
val handlerId = Signal.connect textView (Gtk.TextView.insertAtCursorSig, onInsert)
handlerId can be used to modify this signal connection subsequently.
It is not unusual for the handler function f to need to refer to the object that emitted the signal, obj, but handlerType does not have an argument to pass obj to the handler f. This is easily achieved by defining f to take the emitting object as the first argument and supplying the argument when connecting the handler as follows:
Signal.connect obj (Namespace.Type.aSig, f obj)
Type checking ensures that when a handler is connected to a signal of an object,
the handler function has the correct type for the signal;
the signal is valid for the object.
Disconnecting signal handlers
A signal handler is disconnected using Signal.disconnect. The signal to disconnect is identified by the object that emits the signal and the handler id returned when it was connected.
For example, a signal handler on textView identified by handlerId is disconnected as follows:
Signal.disconnect textView handlerId
Testing whether signal handlers are still connected
Signal.isConnected tests whether a signal is connected. The signal is identified by the object that emits the signal and the handler id returned when it was connected.
For example, a signal handler on textView identified by handlerId is tested as follows:
Signal.isConnected textView handlerId
At present, there is no support for emitting signals.