JS developers are used to pulling dependencies from npm. The bad news is that many of packages there will not work, simply because they rely on Node.js APIs, which are not there in GJS. However, there are also a bunch of libraries that do work, as long as they do not call any Node.js functions.
GJS, as a language binding for the Gnome ecosystem, enables the use of GObject-based libraries. The full list of APIs and their documentation may be found at GJS API Docs.
To build GJS apps we need:
gjs(comes with gnome)
- Flatpak Builder -
flatpak install org.flatpak.Builder
- Gnome Builder IDE -
flatpak install flathub org.gnome.Builder
Building the app:
Running the app:
We can use the ECMAScript import syntax:
We can also specify the exact version of the library we’re imporing:
There is a global object called
globalThis, which is similar to
the browsers. We can assign values to it and access them from anywhere else.
Our app will consist of:
JS code (duh..):
Entry Point (
app.id.js) - starting point of the app.
window.js- an app typically subclasses a base
GtkWindowto define code and UI of a window
application.js- an app typically subclasses a base
main.js- most likely, you will instantiate the app here:
GResource - an XML listing of files that will be included in the final binary. Usually, there are separate GResource files for the source code, and for the other data (images, CSS, UI, icons, etc.). There are APIs to access GResources (like icons) from the JS code.
assets like icons, images
CSS - GTK apps may be styled with CSS, similar to HTML. The difference is that GTK CSS does not support positioning (e.g. with flexbox). That is done via specific widget containers, similarly to how WPF does it in the .NET Platform.
UI files - XML describing the layout of various views, very similar to WPF.
meson build file(s)
GTK toolkit wouldn’t be of much use without its widgets. Their documenatation can be found here.
Widgets have a tree of inheritance (
GObject is always a root), making the
whole thing very similar to WPF.
Widgets include also layout containers, which help with positioning of other widgets on the screen. In web development, we’d typically use CSS for that. In GTK, widgets are the way to do it.
Widgets may be placed on the screen in two ways:
in JS code
in UI file
The XML above also shows an example of binding.
A UI file like this needs its own class to be defined in JS as well:
Our widgets may have their own properties, and we might set them in UI files of the widget. These properties are similar to Angular’s @Inputs.
We can bind to that property from a UI file like this:
Then, when some parent widget displays
MyWidget, it can set the value of the
property, like this:
We can create our own CSS classes to style widgets, and we can use already
existing classes, which come with the GTK widgets. Documentation of GTK
specifies these classes under “CSS Nodes” section of each widget. For example, a
Label widget has a node
with the following classes:
.selection- when selected
.link- for each URL included in the label’s text
Also, GTK defines a bunch of classes that we can freely apply to various
widgets. For example, the
.keycap class makes label look like a keyboard key.
GObject brings over the concept of Signals, which is analogical to events
from the .NET world. Various widgets have their own signals defined (like a
clicked signal of a button), which we can subscribe and react to. Custom
widgets also can have custom signals defined.
Her’s how to use a signal:
Connecting to a signal
Defining the handler:
Some signals have additional parameters that can be used in handlers. The first parameter is always the object emitting it.
In order to define our own signals, we have to:
Specify the signal to be a part of our widget:
The parent of the widget with our signal may subscribe to it via the UI file, as shown before, or in code:
Relying on signals too much can quickly bring us at the similar problem that we have in web frameworks with bubbling signals/events up the components tree - it becomes cumbersome.
This is why GTK defines another concept - Actions. These are defined in once place and can be called from another (even from another app!).
If an Action is defined on a Window, any widget within that window can invoke it. If an Action is defined on an Application, we can call it from anywhere. It is kind of like a global function. Individual widgets can also add actions (and then, who can call them?)
Defining an Action:
Activating an Action:
We can either do it in code with the
gtk_widget_activate_action() function of
any widget, or in the UI file, with a button:
The Gio.Settings (GSettings) may be used to store the app’s settings.
GSettings stores values as
GVariant, with any type. GSettings can be bound to
a widget, making changes to settings automatic with the UI.
Settings of our app should have a predefined schema, in XML:
This should be defined in a file named like
Now, we can use settings in various ways:
bind to them in our widgets
read a setting
subscribe to changes
This “guide” has been heavily inspired by the GJS & GTK 4 tutorial. It also contains a bunch of information taken from: