1## GObject Construction, Subclassing, Templates and GType
2
3### Constructing GObjects
4
5GObjects can be constructed with the `new` operator, just like JavaScript objects, and usually take an Object map of properties.
6
7The 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()`.
8It's different from the **prototype object** containing instance methods.
9For more information on JavaScript's prototypal inheritance, this [blog post][understanding-javascript-prototypes] is a good resource.
10
11```js
12let label = new Gtk.Label({
13    label: '<a href="https://www.gnome.org">gnome.org</a>',
14    halign: Gtk.Align.CENTER,
15    hexpand: true,
16    use_markup: true,
17    visible: true
18});
19
20let file = Gio.File.new_for_path('/proc/cpuinfo');
21```
22
23[understanding-javascript-prototypes]: https://javascriptweblog.wordpress.com/2010/06/07/understanding-javascript-prototypes/
24
25### Subclassing GObjects
26
27GObjects have facilities for defining properties, signals and implemented interfaces. Additionally, Gtk objects support defining a CSS name and composite template.
28
29The **constructor object** is also passed to the `extends` keyword in class declarations when subclassing GObjects.
30
31```js
32var MyLabel = GObject.registerClass({
33    // GObject
34    GTypeName: 'Gjs_MyLabel',                   // GType name (see below)
35    Implements: [ Gtk.Orientable ],             // Interfaces the subclass implements
36    Properties: {},                             // More below on custom properties
37    Signals: {},                                // More below on custom signals
38    // Gtk
39    CssName: '',                                // CSS name
40    Template: 'resource:///path/example.ui',    // Builder template
41    Children: [ 'label-child' ],                // Template children
42    InternalChildren: [ 'internal-box' ]        // Template internal (private) children
43}, class MyLabel extends Gtk.Label {
44    // Currently GObjects use _init, not construct
45    _init(params) {
46        // Chaining up
47        super._init(params);
48    }
49});
50```
51
52### GType Objects
53
54This 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.
55
56The `$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.
57
58```js
59log(Gtk.Label.$gtype);
60log(labelInstance.constructor.$gtype);
61// expected output: [object GType for 'GtkLabel']
62```
63
64The `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.
65
66User defined subclasses' GType name will be the class name prefixed with `Gjs_`by default.
67If you want to specify your own name, you can pass it as the value for the `GTypeName` property to `GObject.registerClass()`.
68This will be relevant in situations such as defining a composite template for a GtkWidget subclass.
69
70```js
71log(Gtk.Label.$gtype.name);
72log(labelInstance.constructor.$gtype.name);
73// expected output: GtkLabel
74
75log(MyLabel.$gtype.name);
76// expected output: Gjs_MyLabel
77```
78
79[`instanceof`][mdn-instanceof] can be used to compare an object instance to a **constructor object**.
80
81```js
82log(typeof labelInstance);
83// expected output: object
84
85log(labelInstance instanceof Gtk.Label);
86// expected output: true
87```
88
89[mdn-instanceof]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/instanceof
90
91## Properties
92
93GObject 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 (_).
94
95```js
96if (!label.use_markup) {
97    label.connect('notify::use-markup', () => { ... });
98    label.use_markup = true;
99    label['use-markup'] = true;
100    label.set_use_markup(true);
101}
102```
103
104GObject subclasses can register properties, which is necessary if you want to use `GObject.notify()` or `GObject.bind_property()`.
105
106**NOTE:** Never use underscores in property names in the ParamSpec, because of the conversion between underscores and hyphens mentioned above.
107
108```js
109var MyLabel = GObject.registerClass({
110    Properties: {
111        'example-prop': GObject.ParamSpec.string(
112            'example-prop',                     // property name
113            'ExampleProperty',                  // nickname
114            'An example read write property',   // description
115            GObject.ParamFlags.READWRITE,       // READABLE/READWRITE/CONSTRUCT/etc
116            'A default'  // default value if omitting getter/setter
117        )
118    }
119}, class MyLabel extends Gtk.Label {
120    get example_prop() {
121        if (!('_example_prop' in this)
122            return 'A default';
123        return this._example_prop;
124    }
125
126    set example_prop(value) {
127        if (this._example_prop !== value) {
128            this._example_prop = value;
129            this.notify('example-prop');
130        }
131    }
132});
133```
134
135If you just want a simple property that you can get change notifications from, you can leave out the getter and setter and GJS will attempt to do the right thing.
136However, if you define one, you have to define both (unless the property is read-only or write-only).
137
138The 'default value' parameter passed to `GObject.ParamSpec` will be taken into account if you omit the getter and setter.
139If you write your own getter and setter, you have to implement the default value yourself, as in the above example.
140
141## Signals
142
143Every object inherited from GObject has `connect()`, `connect_after()`, `disconnect()` and `emit()` methods.
144
145```js
146let handlerId = label.connect('activate-link', (label, uri) => {
147    Gtk.show_uri_on_window(
148        label.get_toplevel(),
149        uri,
150        Gdk.get_current_time()
151    );
152    return true;
153});
154
155label.emit('activate-link', 'https://www.gnome.org');
156
157label.disconnect(handlerId);
158```
159
160GObject subclasses can also register their own signals.
161
162```js
163var MyLabel = GObject.registerClass({
164    Signals: {
165        'my-signal': {
166            flags: GObject.SignalFlags.RUN_FIRST,
167            param_types: [ GObject.TYPE_STRING ]
168        }
169    }
170}, class ExampleApplication extends GObject.Object {
171    _init() {
172        super._init();
173        this.emit('my-signal', 'a string parameter');
174    }
175});
176```
177
178**NOTE:** GJS also includes a built-in [`signals`](Modules#signals) module for applying signals to native JavaScript classes.
179
180## Enumerations and Flags
181
182Both 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].
183
184```js
185// enum GtkAlign, member GTK_ALIGN_CENTER
186Gtk.Align.CENTER;
187// enum GtkWindowType, member GTK_WINDOW_TOPLEVEL
188Gtk.WindowType.TOPLEVEL;
189// enum GApplicationFlags, member G_APPLICATION_FLAGS_NONE
190Gio.ApplicationFlags.FLAGS_NONE
191```
192
193Flags can be manipulated using native [bitwise operators][mdn-bitwise].
194
195```js
196let myApp = new Gio.Application({
197    flags: Gio.ApplicationFlags.HANDLES_OPEN | Gio.ApplicationFlags.HANDLES_COMMAND_LINE
198});
199
200if (myApp.flags & Gio.ApplicationFlags.HANDLES_OPEN) {
201    myApp.flags &= ~Gio.ApplicationFlags.HANDLES_OPEN;
202}
203```
204
205[gjs-docs]: http://devdocs.baznga.org/
206[mdn-bitwise]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators
207
208## Structs and Unions
209
210C 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.
211
212```js
213widget.connect("key-press-event", (widget, event) => {
214    log(event);
215    // expected output: [union instance proxy GIName:Gdk.Event jsobj@0x7f19a00b6400 native@0x5620c6a7c6e0]
216    log(event.get_event_type() === Gdk.EventType.KEY_PRESS);
217    // expected output: true
218    let [ok, keyval] = event.get_keyval();
219    log(keyval);
220    // example output: 65507
221});
222```
223
224[gdk-event]: http://devdocs.baznga.org/gdk30~3.22p/gdk.event
225
226## Multiple return values (caller-allocates)
227
228In 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.
229
230```js
231let [minimumSize, naturalSize] = label.get_preferred_size();
232
233// Functions with boolean 'success' returns often still throw an Error on failure
234try {
235    let file = new Gio.File({ path: '/proc/cpuinfo' });
236    let [ok, contents, etag_out] = file.load_contents(null);
237    // "ok" is actually useless in this scenario, since if it is false,
238    // an exception will have been thrown. You can skip return values
239    // you don't want with array elision:
240    let [, contents2] = file.load_contents(null);
241} catch(e) {
242    log('Failed to read file: ' + e.message);
243}
244```