gnome-shell/js/dbusServices/extensions/extensionPrefsDialog.js
Florian Müllner 972a3b2d44 dbusServices/extensions: Load preferences as module
Now that extensions themselves are imported as modules, do the
same for their preference dialogs.

Extensions must now export a class with a `fillPreferencesWindow()`
method as default.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2795>
2023-07-06 18:41:29 +00:00

161 lines
5.0 KiB
JavaScript

// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
import Adw from 'gi://Adw?version=1';
import Gdk from 'gi://Gdk?version=4.0';
import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import GObject from 'gi://GObject';
import Gtk from 'gi://Gtk?version=4.0';
const ExtensionUtils = imports.misc.extensionUtils;
export const ExtensionPrefsDialog = GObject.registerClass({
GTypeName: 'ExtensionPrefsDialog',
}, class ExtensionPrefsDialog extends Adw.PreferencesWindow {
_init(extension) {
super._init({
title: extension.metadata.name,
search_enabled: false,
});
this._loadPrefs(extension).catch(e => {
this._showErrorPage(e);
logError(e, 'Failed to open preferences');
});
}
async _loadPrefs(extension) {
// give extension prefs access to their own extension object
ExtensionUtils.setCurrentExtension(extension);
const prefsJs = extension.dir.get_child('prefs.js');
const prefsModule = await import(prefsJs.get_uri());
const prefsObj = new prefsModule.default(extension.metadata);
prefsObj.fillPreferencesWindow(this);
if (!this.visible_page)
throw new Error('Extension did not provide any UI');
}
set titlebar(w) {
this.set_titlebar(w);
}
// eslint-disable-next-line camelcase
set_titlebar() {
// intercept fatal libadwaita error, show error page instead
GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
this._showErrorPage(
new Error('set_titlebar() is not supported for Adw.Window'));
return GLib.SOURCE_REMOVE;
});
}
_showErrorPage(e) {
while (this.visible_page)
this.remove(this.visible_page);
const extension = ExtensionUtils.getCurrentExtension();
this.add(new ExtensionPrefsErrorPage(extension, e));
}
});
const ExtensionPrefsErrorPage = GObject.registerClass({
GTypeName: 'ExtensionPrefsErrorPage',
Template: 'resource:///org/gnome/Shell/Extensions/ui/extension-error-page.ui',
InternalChildren: [
'expander',
'expanderArrow',
'revealer',
'errorView',
],
}, class ExtensionPrefsErrorPage extends Adw.PreferencesPage {
static _classInit(klass) {
super._classInit(klass);
klass.install_action('page.copy-error',
null,
self => {
const clipboard = self.get_display().get_clipboard();
clipboard.set(self._errorMarkdown);
});
klass.install_action('page.show-url',
null,
self => Gtk.show_uri(self.get_root(), self._url, Gdk.CURRENT_TIME));
return klass;
}
_init(extension, error) {
super._init();
this._addCustomStylesheet();
this._uuid = extension.uuid;
this._url = extension.metadata.url || '';
this.action_set_enabled('page.show-url', this._url !== '');
this._gesture = new Gtk.GestureClick({
button: 0,
exclusive: true,
});
this._expander.add_controller(this._gesture);
this._gesture.connect('released', (gesture, nPress) => {
if (nPress === 1)
this._revealer.reveal_child = !this._revealer.reveal_child;
});
this._revealer.connect('notify::reveal-child', () => {
this._expanderArrow.icon_name = this._revealer.reveal_child
? 'pan-down-symbolic'
: 'pan-end-symbolic';
this._syncExpandedStyle();
});
this._revealer.connect('notify::child-revealed',
() => this._syncExpandedStyle());
this._errorView.buffer.text = `${error}\n\nStack trace:\n`;
// Indent stack trace.
this._errorView.buffer.text +=
error.stack.split('\n').map(line => ` ${line}`).join('\n');
// markdown for pasting in gitlab issues
let lines = [
`The settings of extension ${this._uuid} had an error:`,
'```',
`${error}`,
'```',
'',
'Stack trace:',
'```',
error.stack.replace(/\n$/, ''), // stack without trailing newline
'```',
'',
];
this._errorMarkdown = lines.join('\n');
}
_syncExpandedStyle() {
if (this._revealer.reveal_child)
this._expander.add_css_class('expanded');
else if (!this._revealer.child_revealed)
this._expander.remove_css_class('expanded');
}
_addCustomStylesheet() {
let provider = new Gtk.CssProvider();
let uri = 'resource:///org/gnome/Shell/Extensions/css/application.css';
try {
provider.load_from_file(Gio.File.new_for_uri(uri));
} catch (e) {
logError(e, 'Failed to add application style');
}
Gtk.StyleContext.add_provider_for_display(Gdk.Display.get_default(),
provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
}
});