Node-Gtk is essentially a thin layer over native libraries. As such, understanding how to use the different gobject-introspected libraries depends on understanding first how the library itself works, second on how to call the library with node-gtk. This documentation covers the node-gtk part, and aims at giving you the information you need to be able to easily translate any C code into nodejs code. Refer to the library's documentation to understand how to use it.
- Loading a library
- Data types
- Structs & Unions
- GObjects
- Naming conventions
- Function calls
- Common pitfalls
Loading a library is done with simply with gi.require(name: string, version: string)
. For example, GTK is loaded as
such:
const gi = require('node-gtk')
const Gtk = gi.require('Gtk', '3.0')
// Use GTK
See api.md for more information on the node-gtk
API.
The GLib Object System is a library and a set of C conventions used to implement an object-oriented type system in C, which lacks such construct. When using a library, the different data types will be translated each in their own way. Here are the main ones:
- Primitive types: integer, char, string
Those types usually map directly to javascript types and don't require any special handling on your part.
Strings may be an exception to this rule in that they may be required to be passed as an array of bytes in some cases. - Enums & Flags
Those data types are converted to primitive javascript values. Flags are also known as bitmasks. They are grouped in objects.
For example, theGTK_ALIGN_FILL
enum value is available asGtk.Align.FILL
. - Structs & Unions (Boxed)
Those are converted to javascript objects. Documented below. - GObjects
These are objects organized in a class hierarchy. Documented below.
These two types are called boxed types. They represent simple bags of data. They may have several methods attached to them.
They can usually be created through a constructor if they have one, such as GdkRGBA:
const color = new Gdk.RGBA({
red: 0.5,
blue: 0.5,
green: 0.5,
alpha: 0.5
})
Or through a creation function, such as GdkCursor:
const cursor = Gdk.Cursor.newFromName('pointer')
The boxed fields are accessible through dot-notation. They are transformed in lowerCamelCase notation:
console.log(color.red)
console.log(cursor.name)
GObjects are the most important objects usually. They form the basis of the GTK framework and represent instances of classes.
Similarly to boxed types, they can be created through a constructor or through a creation function:
// Constructor with initial properties
const label = new Gtk.Label({ text: "I'm a label!" })
// Creation function
const button = Gtk.Button.newFromStock(Gtk.STOCK_YES)
They have access to their constructor methods, as well as all those of their parents. For example, both elements above derive from GtkWidget and can access all its methods.
console.log(label.getPreferredSize())
console.log(button.getPreferredSize())
Methods are transformed to use lowerCamelCase syntax and are called on their
instance. For example, the getPreferredSize()
method's original C signature
reads like this:
void
gtk_widget_get_preferred_size (GtkWidget *widget,
GtkRequisition *minimum_size,
GtkRequisition *natural_size);
Node-Gtk translates it so that you can call it directly on the instances, so the new signature looks like this:
GtkWidget.prototype.getPreferredSize = function() {
/* C code called like this:
gtk_widget_get_preferred_size(
this,
[out argument, handled by node-gtk],
[out argument, handled by node-gtk])
*/
/* [native code] */
/* @returns [Gtk.Requisition, Gtk.Requisition]*/
}
GObjects also have their own event-emitting system, called signals. They are
usually connected with g_signal_connect
, and node-gtk makes them available through the more familiar .on
/.once
/.off
syntax.
Here is an example with GtkEntry
:
const input = new Gtk.Entry()
/**
* GObject.on - associates a callback to an event
* @param {String} name - Name of the event
* @param {Function} callback - Event handler
* @param {Boolean} [after=false] - Run after the signal
* @returns {GObject}
*/
input.on('key-press-event', onKeyPress, /* optional */ false)
/**
* GObject.off - dissociates callback from an event
* @param {String} name - Name of the event
* @param {Function} callback - Event handler
* @returns {GObject}
*/
input.off('key-press-event', onKeyPress)
/**
* GObject.once - as GObject.on, but only runs once
* @param {String} name - Name of the event
* @param {Function} callback - Event handler
* @param {Boolean} [after=false] - Run after the signal
* @returns {GObject}
*/
input.once('key-press-event', onKeyPress, /* optional */ false)
function onKeyPress(event) {
// event.__proto__ === Gdk.EventKey
console.log(event.string, event.keyval)
}
/**
* GObject.emit - Emits a signal on the GObject
* @param {String} name - Name of the signal
* @param {...*} args - Signal's arguments (refer to the library doc)
*/
input.emit('key-press-event', new Gdk.EventKey({ keyval: Gdk.Key_g }))
NOTE: Returning GLib.SOURCE_CONTINUE
(true
) or GLib.SOURCE_REMOVE
(false
) from an event handler can have the special semantic of continuing or stopping the event
from being propagated or preventing the default behavior. Refer to appropriate documentation for details.
(E.g. GtkWidget signals)
Low-level methods .connect(name: string, callback: Fn): number
and
.disconnect(name: string, handleID: number): void
are also available but not
recommended.
It is possible to extend existing GObjects by inheriting from them. You also need to register them with the type system for fuller integration and to enable virtual functions.
class CustomWidget extends Gtk.Widget {
static GTypeName = 'NodeGTKCustomWidget'
focus() {} /* This is a virtual function */
}
gi.registerClass(CustomWidget)
Here is a recap of the naming conventions.
-
Functions, Methods & Virtual Functions:
lowerCamelCase
Methods on GObject, structs, unions and functions on namespaces.
Example:
GLib.randomIntRange(0, 100)
textBuffer.placeCursor(0)
-
Fields & Properties:
lowerCamelCase
Fields are on structs and unions.
Properties are on GObjects.
Example:
textView.showLineNumbers = true
new Gdk.Color().blue = 200
-
Structs, Unions, GObjects & Interfaces:
UpperCamelCase
Defined on namespaces.
Example:
Gtk.Button
Gdk.Color
-
Enums, Flags:
UpperCamelCase
Defined on namespaces.
Example:
Gtk.AttachOptions
Gdk.EventType
-
Constants & Values:
SNAKE_CASE
(not modified, may contain lowercase)
Can be attached on namespaces or on specific objects.
Example:
Gdk.KEY_g !== Gdk.KEY_G
Gdk.EventType.KEY_PRESS
-
Signals:
dash-case
Events triggered by GObjects.
Example:
gtkEntry.on('key-press-event', (ev) => { ... })
Translating function calls from and to native languages comes with a few gotchas, given that some concepts are not available in javascript, such as pointers.
Out arguments are arguments that are used to return a value from a function by passing a pointer to a pointer. When JS code is calling into C code, node-gtk provides them automatically and you don't need to worry about them. If there is more than one return value from the C function, such as the return value plus one out-argument, or no return value plus two out-arguments, then node-gtk will wrap all the return values in an array, starting with the real return value.
In the case of C code calling into JS code, such as with the GtkWidget.measure
virtual function, the JS code must return the out-arguments as return values, plus
the real return value in the first position of the array if there is one.
void
gtk_widget_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline);
class NewWidget extends Gtk.Widget {
measure(orientation, forSize, ...args) {
// all out-argument values in `args` have been replace with `null`
// ...calculate dimensions...
return [min, nat, minBaseline, natBaseline]
}
}
The bindings are sometimes a bit raw in that they provide you direct access to C functions. It is very possible for you to cause a segfault by misusing any library. Here are a few common errors.