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```