Preferences
Our preferences dialog will be written in Gtkopen in new window, which gives us a lot of options for how we present settings to the user. You may consider looking through the GNOME Human Interface Guidelines, or widget galleries for ideas.
See Also
- GNOME HIGopen in new window
- GTK4 Widget Galleryopen in new window
- libadwaita Widget Galleryopen in new window
GSettings
GSettingsopen in new window provides a simple, extremely fast API for storing application settings, that can also be used by GNOME Shell extensions.
Creating a Schema
TIP
A GSettings schema ID with the prefix org.gnome.shell.extensions
and a path with the prefix /org/gnome/shell/extensions
is the standard for extensions.
Schema files describe the types and default values of a particular group of settings, using the same type format as GVariantopen in new window. The first thing to do is create a subdirectory for your settings schema and open the schema file in your editor:
$ cd ~/.local/share/gnome-shell/extensions/example@gjs.guide/
$ mkdir schemas/
$ gedit schemas/org.gnome.shell.extensions.example.gschema.xml
2
3
Then using your edit, create a schema describing the settings for your extension:
<?xml version="1.0" encoding="UTF-8"?>
<schemalist>
<schema id="org.gnome.shell.extensions.example" path="/org/gnome/shell/extensions/example/">
<!-- See also: https://docs.gtk.org/glib/gvariant-format-strings.html -->
<key name="show-indicator" type="b">
<default>true</default>
</key>
</schema>
</schemalist>
2
3
4
5
6
7
8
9
Compiling a Schema
TIP
As of GNOME 44, settings schemas are compiled automatically for extensions installed with the gnome-extensions
tool, GNOME Extensionsopen in new window website, or a compatible application like Extension Manageropen in new window.
Before it can be used, a GSettings schema must be compiled. If not using the gnome-extensions
tool, glib-compile-schemas
can be used to compile schemas:
$ glib-compile-schemas schemas/
$ ls schemas/
org.gnome.shell.extensions.example.gschema.xml gschemas.compiled
2
3
Integrating GSettings
TIP
For complex settings, see the GVariant Guide
for examples of what data types can be stored with GSettings.
To make using GSettings easier in an extension, set the settings-schema
field in metadata.json
:
{
"uuid": "example@gjs.guide",
"name": "Example",
"description": "This is an example extension.",
"shell-version": [ "3.38", "40" ],
"url": "https://gitlab.gnome.org/World/ShellExtensions/example",
"settings-schema": "org.gnome.shell.extensions.example"
}
2
3
4
5
6
7
8
With this field set, ExtensionUtils.getSettings()
can be called with no arguments. Otherwise you may pass any valid GSettings schema ID.
Methods like [Gio.Settings.get_boolean()
][gsettings-getboolean] are used for native values, or methods like [Gio.Settings.set_value()
][gsettings-setvalue] can be used to work with GLib.Variant
directly.
For simple types like Boolean
, Gio.Settings.bind()
open in new window can bind to a GObject Property. For JavaScript properties and other use cases, Gio.Settings
emits Gio.Settings::changed
open in new window with the property name as the signal detail (e.g. changed::show-indicator
).
const Gio = imports.gi.Gio;
const St = imports.gi.St;
const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const _ = ExtensionUtils.gettext;
class Extension {
constructor() {
this._indicator = null;
}
enable() {
console.debug(`enabling ${Me.metadata.name}`);
const indicatorName = _('%s Indicator').format(Me.metadata.name);
// Create a panel button
this._indicator = new PanelMenu.Button(0.0, indicatorName, false);
// Open the preferences when the indicator is clicked
this._indicator.connect('clicked', () => ExtensionUtils.openPrefs());
// Add an icon
const icon = new St.Icon({
icon_name: 'face-laugh-symbolic',
style_class: 'system-status-icon',
});
this._indicator.add_child(icon);
// `Main.panel` is the actual panel you see at the top of the screen,
// not a class constructor.
Main.panel.addToStatusArea(indicatorName, this._indicator);
// Create a new GSettings object, and bind the "show-indicator"
// setting to the "visible" property.
this.settings = ExtensionUtils.getSettings();
this.settings.bind('show-indicator', this._indicator, 'visible',
Gio.SettingsBindFlags.DEFAULT);
// Watch for changes to a specific setting
this.setting.connect('changed::show-indicator', (settings, key) => {
console.debug(`${key} = ${settings.get_value(key).print(true)}`);
});
}
disable() {
console.debug(`disabling ${Me.metadata.name}`);
this._indicator.destroy();
this._indicator = null;
this.settings = null;
}
}
function init() {
console.debug(`initializing ${Me.metadata.name}`);
ExtensionUtils.initTranslations();
return new Extension();
}
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
Preferences Window
TIP
Extension preferences run in a separate process, without access to code in GNOME Shell, and are written with GTKopen in new window and libadwaitaopen in new window.
Extensions should implement fillPreferencesWindow()
, which is passed a new instance of Adw.PreferencesWindow
open in new window before it is displayed to the user.
libadwaita has many widgets that make building a preferences dialog easier, with screenshots available in the Widget Galleryopen in new window. You may also use any of the other APIsopen in new window that are compatible with GTK4 (notable exceptions include Meta
, Clutter
, Shell
and St
).
'use strict';
const { Adw, Gio, Gtk } = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const _ = ExtensionUtils.gettext;
function init() {
ExtensionUtils.initTranslations();
}
function fillPreferencesWindow(window) {
const settings = ExtensionUtils.getSettings();
// Create a preferences page, with a single group
const page = new Adw.PreferencesPage();
window.add(page);
const group = new Adw.PreferencesGroup();
page.add(group);
// Create a new preferences row
const row = new Adw.ActionRow({ title: _('Show Extension Indicator') });
group.add(row);
// Create a switch and bind its value to the `show-indicator` key
const toggle = new Gtk.Switch({
active: settings.get_boolean('show-indicator'),
valign: Gtk.Align.CENTER,
});
settings.bind('show-indicator', toggle, 'active',
Gio.SettingsBindFlags.DEFAULT);
// Add the switch to the row
row.add_suffix(toggle);
row.activatable_widget = toggle;
// Make sure the window doesn't outlive the settings object
window._settings = settings;
}
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
The preference dialog can be opened with gnome-extensions prefs
, or any other tool for managing extensions:

Debugging
Because preferences are not run within gnome-shell
but in a separate process, the logs will appear in the gjs
process:
journalctl -f -o cat /usr/bin/gjs