summaryrefslogtreecommitdiff
path: root/doc/Mapping.md
blob: d1b0805181d8568fc4b91798097ae49f64b2b08e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
## GObject Construction, Subclassing, Templates and GType

### Constructing GObjects

GObjects can be constructed with the `new` operator, just like JavaScript objects, and usually take an Object map of properties.

The object that you pass to `new` (e.g. `Gtk.Label` in `let label = new Gtk.Label()`) is the **constructor object**, that contains constructor methods and static methods such as `Gio.File.new_for_path()`.
It's different from the **prototype object** containing instance methods.
For more information on JavaScript's prototypal inheritance, this [blog post][understanding-javascript-prototypes] is a good resource.

```js
let label = new Gtk.Label({
    label: '<a href="https://www.gnome.org">Gnome.org</a>',
    halign: Gtk.Align.CENTER,
    hexpand: true,
    use_markup: true,
    visible: true
});

let file = Gio.File.new_for_path('/proc/cpuinfo');
```

[understanding-javascript-prototypes]: https://javascriptweblog.wordpress.com/2010/06/07/understanding-javascript-prototypes/

### Subclassing GObjects

GObjects have facilities for defining properties, signals and implemented interfaces. Additionally, Gtk objects support defining a CSS name and composite template.

The **constructor object** is also passed to the `extends` keyword in class declarations when subclassing GObjects.

```js
var MyLabel = GObject.registerClass({
    // GObject
    GTypeName: 'Gjs_MyLabel',                   // GType name (see below)
    Implements: [ Gtk.Orientable ],             // Interfaces the subclass implements
    Properties: {},                             // More below on custom properties
    Signals: {},                                // More below on custom signals
    // Gtk
    CssName: '',                                // CSS name
    Template: 'resource:///path/example.ui',    // Builder template
    Children: [ 'label-child' ],                // Template children
    InternalChildren: [ 'internal-box' ]        // Template internal (private) children
}, class MyLabel extends Gtk.Label {
    // Currently GObjects use _init, not construct
    _init(params) {
        // Chaining up
        super._init(params);
    }
});
```

### GType Objects

This is the object that represents a type in the GObject type system. Internally a GType is an integer, but you can't access that integer in GJS.

The `$gtype` property gives the GType object for the given type. This is the proper way to find the GType given an object or a class. For a class, `GObject.type_from_name('GtkLabel')` would work too if you know the GType name, but only if you had previously constructed a Gtk.Label object.

```js
log(Gtk.Label.$gtype);
log(labelInstance.constructor.$gtype);
// expected output: [object GType for 'GtkLabel']
```

The `name` property of GType objects gives the GType name as a string ('GtkLabel'). This is the proper way to find the type name given an object or a class.

User defined subclasses' GType name will be the class name prefixed with `Gjs_`by default.
If you want to specify your own name, you can pass it as the value for the `GTypeName` property to `GObject.registerClass()`.
This will be relevant in situations such as defining a composite template for a GtkWidget subclass.

```js
log(Gtk.Label.$gtype.name);
log(labelInstance.constructor.$gtype.name);
// expected output: GtkLabel

log(MyLabel.$gtype.name);
// expected output: Gjs_MyLabel
```

[`instanceof`][mdn-instanceof] can be used to compare an object instance to a **constructor object**.

```js
log(typeof labelInstance);
// expected output: object

log(labelInstance instanceof Gtk.Label);
// expected output: true
```

[mdn-instanceof]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/instanceof

## Properties

GObject properties may be retrieved and set using native property style access or GObject get/set methods. Note that variables in JavaScript can't contain hyphens (-) so when a property name is *unquoted* use an underscore (_).

```js
if (!label.use_markup) {
    label.connect('notify::use-markup', () => { ... });
    label.use_markup = true;
    label['use-markup'] = true;
    label.set_use_markup(true);
}
```

GObject subclasses can register properties, which is necessary if you want to use `GObject.notify()` or `GObject.bind_property()`.

**NOTE:** Never use underscores in property names in the ParamSpec, because of the conversion between underscores and hyphens mentioned above.

```js
var MyLabel = GObject.registerClass({
    Properties: {
        'example-prop': GObject.ParamSpec.string(
            'example-prop',                     // property name
            'ExampleProperty',                  // nickname
            'An example read write property',   // description
            GObject.ParamFlags.READWRITE,       // READABLE/READWRITE/CONSTRUCT/etc
            ''                                  // implement defaults manually
        )
    }
}, class MyLabel extends Gtk.Label {
    _init(params) {
        super._init(params);
    }

    get example_prop() {
        if (this._example_prop === undefined) {
            return 'A default';
        }

        return this._example_prop;
    }

    set example_prop(value) {
        this._example_prop = value;
        this.notify('example-prop');
    }
});
```

## Signals

Every object inherited from GObject has `connect()`, `connect_after()`, `disconnect()` and `emit()` methods.

```js
let handlerId = label.connect('activate-link', (label, uri) => {
    Gtk.show_uri_on_window(
        label.get_toplevel(),
        uri,
        Gdk.get_current_time()
    );
    return true;
});

label.emit('activate-link', 'https://www.gnome.org');

label.disconnect(handlerId);
```

GObject subclasses can also register their own signals.

```js
var MyLabel = GObject.registerClass({
    Signals: {
        'my-signal': {
            flags: GObject.SignalFlags.RUN_FIRST,
            param_types: [ GObject.TYPE_STRING ]
        }
    }
}, class ExampleApplication extends GObject.Object {
    _init() {
        super._init();
        this.emit('my-signal', 'a string parameter');
    }
});
```

**NOTE:** GJS also includes a built-in [`signals`](Modules#signals) module for applying signals to native JavaScript classes.

## Enumerations and Flags

Both enumerations and flags appear as entries under the namespace, with associated member properties. These are available in the official GJS [Gnome API documentation][gjs-docs].

```js
// enum GtkAlign, member GTK_ALIGN_CENTER
Gtk.Align.CENTER;
// enum GtkWindowType, member GTK_WINDOW_TOPLEVEL
Gtk.WindowType.TOPLEVEL;
// enum GApplicationFlags, member G_APPLICATION_FLAGS_NONE
Gio.ApplicationFlags.FLAGS_NONE
```

Flags can be manipulated using native [bitwise operators][mdn-bitwise].

```js
let myApp = new Gio.Application({
    flags: Gio.ApplicationFlags.HANDLES_OPEN | Gio.ApplicationFlags.HANDLES_COMMAND_LINE
});

if (myApp.flags & Gio.ApplicationFlags.HANDLES_OPEN) {
    myApp.flags &= ~Gio.ApplicationFlags.HANDLES_OPEN;
}
```

[gjs-docs]: http://devdocs.baznga.org/
[mdn-bitwise]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators

## Structs and Unions

C structures and unions are documented in the [Gnome API documentation][gjs-docs] (e.g. [Gdk.Event][gdk-event]) and generally have either JavaScript properties or getter methods for each member. Results may vary when trying to modify structs or unions.

```js
widget.connect("key-press-event", (widget, event) => {
    log(event);
    // expected output: [union instance proxy GIName:Gdk.Event jsobj@0x7f19a00b6400 native@0x5620c6a7c6e0]
    log(event.get_event_type() === Gdk.EventType.KEY_PRESS);
    // expected output: true
    let [ok, keyval] = event.get_keyval();
    log(keyval);
    // example output: 65507
});
```

[gdk-event]: http://devdocs.baznga.org/gdk30~3.22p/gdk.event

## Multiple return values (caller-allocates)

In GJS caller-allocates (variables passed into a function) and functions with multiple out parameters are returned as an array of return values. If the function has a return value, it will be the first element of that array.

```js
let [minimumSize, naturalSize] = label.get_preferred_size();

// Functions with boolean 'success' returns often still throw an Error on failure
try {
    let file = new Gio.File({ path: '/proc/cpuinfo' });
    let [ok, contents, etag_out] = file.load_contents(null);
    // "ok" is actually useless in this scenario, since if it is false,
    // an exception will have been thrown. You can skip return values
    // you don't want with array elision:
    let [, contents2] = file.load_contents(null);
} catch(e) {
    log('Failed to read file: ' + e.message);
}
```