diff --git a/js/extensionPrefs/data/css/application.css b/js/extensionPrefs/data/css/application.css
index 37105d081..5641c5d23 100644
--- a/js/extensionPrefs/data/css/application.css
+++ b/js/extensionPrefs/data/css/application.css
@@ -9,3 +9,6 @@
}
image.warning { color: @warning_color; }
+
+.expander-frame > * { border-top-width: 0; }
+.expander-toolbar { border: 0 solid @borders; border-top-width: 1px; }
diff --git a/js/extensionPrefs/data/org.gnome.Extensions.data.gresource.xml b/js/extensionPrefs/data/org.gnome.Extensions.data.gresource.xml
index 659c03ffc..de65e0823 100644
--- a/js/extensionPrefs/data/org.gnome.Extensions.data.gresource.xml
+++ b/js/extensionPrefs/data/org.gnome.Extensions.data.gresource.xml
@@ -5,6 +5,7 @@
dbus-interfaces/org.gnome.Shell.Extensions.xml
+ ui/extension-prefs-dialog.ui
ui/extension-row.ui
ui/extensions-window.ui
diff --git a/js/extensionPrefs/data/ui/extension-prefs-dialog.ui b/js/extensionPrefs/data/ui/extension-prefs-dialog.ui
new file mode 100644
index 000000000..fc08eae3c
--- /dev/null
+++ b/js/extensionPrefs/data/ui/extension-prefs-dialog.ui
@@ -0,0 +1,197 @@
+
+
+
+
+
+ 600
+ 400
+
+
+
+
+
+
+
+
diff --git a/js/extensionPrefs/js/main.js b/js/extensionPrefs/js/main.js
index d1da73a02..fd8c00959 100644
--- a/js/extensionPrefs/js/main.js
+++ b/js/extensionPrefs/js/main.js
@@ -191,31 +191,10 @@ var ExtensionsWindow = GObject.registerClass({
if (!row || !row.hasPrefs)
return false;
- let widget;
-
- try {
- widget = row.prefsModule.buildPrefsWidget();
- } catch (e) {
- widget = this._buildErrorUI(row, e);
- }
-
- this._prefsDialog = new Gtk.Window({
- application: this.application,
- default_width: 600,
- default_height: 400,
- modal: this.visible,
- type_hint: Gdk.WindowTypeHint.DIALOG,
- window_position: Gtk.WindowPosition.CENTER,
- });
-
- this._prefsDialog.set_titlebar(new Gtk.HeaderBar({
- show_close_button: true,
- title: row.name,
- visible: true,
- }));
+ this._prefsDialog = new ExtensionPrefsDialog(row);
if (this.visible)
- this._prefsDialog.transient_for = this;
+ this._prefsDialog.set({ transient_for: this, modal: true });
this._prefsDialog.connect('destroy', () => {
this._prefsDialog = null;
@@ -224,7 +203,6 @@ var ExtensionsWindow = GObject.registerClass({
this.destroy();
});
- this._prefsDialog.add(widget);
this._prefsDialog.show();
return true;
@@ -266,121 +244,6 @@ var ExtensionsWindow = GObject.registerClass({
});
}
- _buildErrorUI(row, exc) {
- let scroll = new Gtk.ScrolledWindow({
- hscrollbar_policy: Gtk.PolicyType.NEVER,
- propagate_natural_height: true,
- });
-
- let box = new Gtk.Box({
- orientation: Gtk.Orientation.VERTICAL,
- spacing: 12,
- margin: 100,
- margin_bottom: 60,
- });
- scroll.add(box);
-
- let label = new Gtk.Label({
- label: '%s'.format(_("Something’s gone wrong")),
- use_markup: true,
- });
- label.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL);
- box.add(label);
-
- label = new Gtk.Label({
- label: _("We’re very sorry, but there’s been a problem: the settings for this extension can’t be displayed. We recommend that you report the issue to the extension authors."),
- justify: Gtk.Justification.CENTER,
- wrap: true,
- });
- box.add(label);
-
- let expander = new Expander({
- label: _("Technical Details"),
- margin_top: 12,
- });
- box.add(expander);
-
- let errortext = '%s\n\nStack trace:\n'.format(exc);
- // Indent stack trace.
- errortext +=
- exc.stack.split('\n').map(line => ' %s'.format(line)).join('\n');
-
- let buffer = new Gtk.TextBuffer({ text: errortext });
- let textview = new Gtk.TextView({
- buffer,
- wrap_mode: Gtk.WrapMode.WORD,
- monospace: true,
- editable: false,
- top_margin: 12,
- bottom_margin: 12,
- left_margin: 12,
- right_margin: 12,
- });
-
- let toolbar = new Gtk.Toolbar();
- let provider = new Gtk.CssProvider();
- provider.load_from_data(`* {
- border: 0 solid @borders;
- border-top-width: 1px;
- }`);
- toolbar.get_style_context().add_provider(
- provider,
- Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
- );
-
- let copyButton = new Gtk.ToolButton({
- icon_name: 'edit-copy-symbolic',
- tooltip_text: _("Copy Error"),
- });
- toolbar.add(copyButton);
-
- copyButton.connect('clicked', w => {
- let clipboard = Gtk.Clipboard.get_default(w.get_display());
- // markdown for pasting in gitlab issues
- let lines = [
- 'The settings of extension %s had an error:'.format(row.uuid),
- '```', // '`' (xgettext throws up on odd number of backticks)
- exc.toString(),
- '```', // '`'
- '',
- 'Stack trace:',
- '```', // '`'
- exc.stack.replace(/\n$/, ''), // stack without trailing newline
- '```', // '`'
- '',
- ];
- clipboard.set_text(lines.join('\n'), -1);
- });
-
- let spacing = new Gtk.SeparatorToolItem({ draw: false });
- toolbar.add(spacing);
- toolbar.child_set_property(spacing, "expand", true);
-
- let urlButton = new Gtk.ToolButton({
- label: _("Homepage"),
- tooltip_text: _("Visit extension homepage"),
- no_show_all: true,
- visible: row.url !== '',
- });
- toolbar.add(urlButton);
-
- urlButton.connect('clicked', w => {
- let context = w.get_display().get_app_launch_context();
- Gio.AppInfo.launch_default_for_uri(row.url, context);
- });
-
- let expandedBox = new Gtk.Box({
- orientation: Gtk.Orientation.VERTICAL,
- });
- expandedBox.add(textview);
- expandedBox.add(toolbar);
-
- expander.add(expandedBox);
-
- scroll.show_all();
- return scroll;
- }
-
_sortList(row1, row2) {
return row1.name.localeCompare(row2.name);
}
@@ -504,105 +367,6 @@ var ExtensionsWindow = GObject.registerClass({
}
});
-var Expander = GObject.registerClass({
- Properties: {
- 'label': GObject.ParamSpec.string(
- 'label', 'label', 'label',
- GObject.ParamFlags.READWRITE,
- null
- ),
- },
-}, class Expander extends Gtk.Box {
- _init(params = {}) {
- this._labelText = null;
-
- super._init(Object.assign(params, {
- orientation: Gtk.Orientation.VERTICAL,
- spacing: 0,
- }));
-
- this._frame = new Gtk.Frame({
- shadow_type: Gtk.ShadowType.IN,
- hexpand: true,
- });
-
- let eventBox = new Gtk.EventBox();
- this._frame.add(eventBox);
-
- let hbox = new Gtk.Box({
- spacing: 6,
- margin: 12,
- });
- eventBox.add(hbox);
-
- this._arrow = new Gtk.Image({
- icon_name: 'pan-end-symbolic',
- });
- hbox.add(this._arrow);
-
- this._label = new Gtk.Label({ label: this._labelText });
- hbox.add(this._label);
-
- this._revealer = new Gtk.Revealer();
-
- this._childBin = new Gtk.Frame({
- shadow_type: Gtk.ShadowType.IN,
- });
- this._revealer.add(this._childBin);
-
- // Directly chain up to parent for internal children
- super.add(this._frame);
- super.add(this._revealer);
-
- let provider = new Gtk.CssProvider();
- provider.load_from_data('* { border-top-width: 0; }');
- this._childBin.get_style_context().add_provider(
- provider,
- Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
- );
-
- this._gesture = new Gtk.GestureMultiPress({
- widget: this._frame,
- button: 0,
- exclusive: true,
- });
- this._gesture.connect('released', (gesture, nPress) => {
- if (nPress == 1)
- this._revealer.reveal_child = !this._revealer.reveal_child;
- });
- this._revealer.connect('notify::reveal-child', () => {
- if (this._revealer.reveal_child)
- this._arrow.icon_name = 'pan-down-symbolic';
- else
- this._arrow.icon_name = 'pan-end-symbolic';
- });
- }
-
- get label() {
- return this._labelText;
- }
-
- set label(text) {
- if (this._labelText == text)
- return;
-
- if (this._label)
- this._label.label = text;
- this._labelText = text;
- this.notify('label');
- }
-
- add(child) {
- // set expanded child
- this._childBin.get_children().forEach(c => {
- this._childBin.remove(c);
- });
-
- if (child)
- this._childBin.add(child);
- }
-});
-
var ExtensionRow = GObject.registerClass({
GTypeName: 'ExtensionRow',
Template: 'resource:///org/gnome/Extensions/ui/extension-row.ui',
@@ -768,6 +532,105 @@ var ExtensionRow = GObject.registerClass({
}
});
+var ExtensionPrefsDialog = GObject.registerClass({
+ GTypeName: 'ExtensionPrefsDialog',
+ Template: 'resource:///org/gnome/Extensions/ui/extension-prefs-dialog.ui',
+ InternalChildren: [
+ 'headerBar',
+ 'stack',
+ 'expander',
+ 'expanderArrow',
+ 'revealer',
+ 'errorView',
+ ],
+}, class ExtensionPrefsDialog extends Gtk.Window {
+ _init(extension) {
+ super._init();
+
+ this._uuid = extension.uuid;
+ this._url = extension.url;
+
+ this._headerBar.title = extension.name;
+
+ this._actionGroup = new Gio.SimpleActionGroup();
+ this.insert_action_group('win', this._actionGroup);
+
+ this._initActions();
+
+ this._gesture = new Gtk.GestureMultiPress({
+ widget: this._expander,
+ button: 0,
+ exclusive: true,
+ });
+
+ 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';
+ });
+
+ try {
+ const widget = extension.prefsModule.buildPrefsWidget();
+ this._stack.add(widget);
+ this._stack.visible_child = widget;
+ } catch (e) {
+ this._setError(e);
+ }
+ }
+
+ _setError(exc) {
+ this._errorView.buffer.text = '%s\n\nStack trace:\n'.format(exc);
+ // Indent stack trace.
+ this._errorView.buffer.text +=
+ exc.stack.split('\n').map(line => ' %s'.format(line)).join('\n');
+
+ // markdown for pasting in gitlab issues
+ let lines = [
+ 'The settings of extension %s had an error:'.format(this._uuid),
+ '```', // '`' (xgettext throws up on odd number of backticks)
+ exc.toString(),
+ '```', // '`'
+ '',
+ 'Stack trace:',
+ '```', // '`'
+ exc.stack.replace(/\n$/, ''), // stack without trailing newline
+ '```', // '`'
+ '',
+ ];
+ this._errorMarkdown = lines.join('\n');
+ this._actionGroup.lookup('copy-error').enabled = true;
+ }
+
+ _initActions() {
+ let action;
+
+ action = new Gio.SimpleAction({
+ name: 'copy-error',
+ enabled: false,
+ });
+ action.connect('activate', () => {
+ const clipboard = Gtk.Clipboard.get_default(this.get_display());
+ clipboard.set_text(this._errorMarkdown, -1);
+ });
+ this._actionGroup.add_action(action);
+
+ action = new Gio.SimpleAction({
+ name: 'show-url',
+ enabled: this._url !== '',
+ });
+ action.connect('activate', () => {
+ Gio.AppInfo.launch_default_for_uri(this._url,
+ this.get_display().get_app_launch_context());
+ });
+ this._actionGroup.add_action(action);
+ }
+});
+
function initEnvironment() {
// Monkey-patch in a "global" object that fakes some Shell utilities
// that ExtensionUtils depends on.
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 3a0c0a151..ba8a5b13b 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -7,6 +7,7 @@ data/org.gnome.Shell.PortalHelper.desktop.in.in
js/extensionPrefs/data/metainfo/org.gnome.Extensions.metainfo.xml.in
js/extensionPrefs/data/org.gnome.Extensions.desktop.in.in
js/extensionPrefs/js/main.js
+js/extensionPrefs/data/ui/extension-prefs-dialog.ui
js/extensionPrefs/data/ui/extension-row.ui
js/extensionPrefs/data/ui/extensions-window.ui
js/gdm/authPrompt.js