-
Notifications
You must be signed in to change notification settings - Fork 0
Initialization Order
The initialization order of a subclass is what determines when different items will be available, when things will run, and when and where undefined may show up. GObjectify does everything in its power to avoid requiring the memorization of the init order, but it is useful to understand nonetheless.
When you write new MyWidget({ title: "Hello" }), a specific sequence of events happens before your instance is returned. Understanding this sequence is key to understanding why things are or aren't available at different points.
- GObject allocates the instance
GObject allocates memory for the instance and sets up the GType system's internal bookkeeping. Nothing visible from JS has happened yet.
-
_initruns
GJS's internal _init function runs. This is NOT your constructor. _init is a GJS hook that runs as part of GObject's construction machinery, and GObjectify hooks into _init to apply any SimpleActions.
After _init completes, template children are bound. _init runs inside the parent class's construction (super(), if you've overridden the constructor).
-
vfunc_constructedruns (if overridden)
If you've overridden the vfunc_constructed method on your subclass, it is then run here. Like with _init, vfunc_constructed also runs inside the parent class's construction. This means that vfunc_constructed does not have access to any fields on your subclass, they will all be undefined if accessed in vfunc_constructed.
vfunc_constructed does have access to most properties. "const", "readonly", and "readwrite" properties are visible, and their values are either their default value, or a value supplied via super()/new. "computed" properties are not fully available, and shouldn't be accessed at this point.
- JS field initializers run
After the parent class is done initializing (after super() returns if you've overridden the constructor), the JS engine runs all the class field initializers. These are things like count = 10, user = new User(), #keyfile = new GLib.Keyfile(). This is the reason why vfunc_constructed, and any other hooks that occur in super cannot see your class's fields: they haven't been initialized yet.
This is why "computed" properties are not flagged as CONSTRUCT and don't run their getters/setters before super() finishes. If they were and were able to, GObject would call your getter and setter during super(), before your backing fields exist at all.
- The rest of your constructor body runs
If you've overridden the constructor, this is when the rest of your constructor body runs. After super() completes, and after the fields are all initialized, only then can the JS engine move on to the rest of your constructor.
This is first point where everything is guaranteed to be available:
- Template children
- All properties
- All fields (including JS private
#fields)
-
@WatchProp's initial call and@PostInitrun (next idle)
On the next idle iteration of the GLib main loop, methods marked with @WatchProp and @PostInit will run. These run asynchronously on idle. Everything is available, and the widget is already visible and usable by this point.
One complication is that any UI callbacks may fire during the super() portion of initialization. Take a look at the following:
@GClass({ template: "resource:///path/to/ui_file.ui" })
export class MyBox extends from(Gtk.Box, {
count: Property.uint32(),
}) {
readonly prefix = "Count:"
protected _get_count_string(): string {
return `${this.#prefix} ${this.count}`
}
}This might be problematic! If the UI file relies upon this callback for, say, a label's text, then it might run _get_count_string before prefix has a value, and will instead result in undefined.
Sadly, there is no way for GObjectify to account for this, so you must be diligent about when your callbacks run, and what your callbacks rely on!