[gnome-shell/wip/fmuellner/prefs-update: 22/22] extensionPrefs: Improve error UI



commit 6e6e100d8ab60f9a2d56f529d9ce8a110daff137
Author: Florian Müllner <fmuellner gnome org>
Date:   Mon Aug 6 04:15:12 2018 +0200

    extensionPrefs: Improve error UI
    
    Currently when a preference widget fails to load, we throw a raw
    backtrace at the user. While that is undoubtedly useful information
    for extension developers and bug reports, it is gibberish to most
    users and hardly the first thing they should be exposed to.
    
    https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/193

 js/extensionPrefs/main.js | 214 ++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 199 insertions(+), 15 deletions(-)
---
diff --git a/js/extensionPrefs/main.js b/js/extensionPrefs/main.js
index 2c92bac24..805f7ddc0 100644
--- a/js/extensionPrefs/main.js
+++ b/js/extensionPrefs/main.js
@@ -104,29 +104,111 @@ var Application = class {
     }
 
     _buildErrorUI(extension, exc) {
-        let box = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
+        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: _("There was an error loading the preferences dialog for 
%s:").format(extension.metadata.name)
+            label: `<span size="x-large">${_("Something’s gone wrong")}</span>`,
+            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 errortext = '';
-        errortext += exc;
-        errortext += '\n\n';
-        errortext += 'Stack trace:\n';
+        let expander = new Expander({
+            label: _("Technical Details"),
+            margin_top: 12
+        });
+        box.add(expander);
 
-        // Indent stack trace.
-        errortext += exc.stack.split('\n').map(line => '  ' + line).join('\n');
+        let errortext = `${exc}\n\nStack trace:\n${
+            // Indent stack trace.
+            exc.stack.split('\n').map(line => `  ${line}`).join('\n')
+        }`;
 
-        let scroll = new Gtk.ScrolledWindow({ vexpand: true });
         let buffer = new Gtk.TextBuffer({ text: errortext });
-        let textview = new Gtk.TextView({ buffer: buffer });
-        textview.override_font(Pango.font_description_from_string('monospace'));
-        scroll.add(textview);
-        box.add(scroll);
+        let textview = new Gtk.TextView({
+            buffer: 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());
+            let backticks = '```';
+            clipboard.set_text(
+                // markdown for pasting in gitlab issues
+                `The settings of extension ${extension.uuid} had an error:\n${
+                backticks}\n${exc}\n${backticks}\n\nStack trace:\n${
+                backticks}\n${exc.stack}${backticks}\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: extension.metadata.url != null
+        });
+        toolbar.add(urlButton);
+
+        urlButton.connect('clicked', w => {
+            let context = w.get_display().get_app_launch_context();
+            Gio.AppInfo.launch_default_for_uri(extension.metadata.url, context);
+        });
+
+        let expandedBox = new Gtk.Box({
+            orientation: Gtk.Orientation.VERTICAL
+        });
+        expandedBox.add(textview);
+        expandedBox.add(toolbar);
+
+        expander.add(expandedBox);
 
-        box.show_all();
-        return box;
+        scroll.show_all();
+        return scroll;
     }
 
     _buildUI(app) {
@@ -250,6 +332,108 @@ var Application = class {
     }
 };
 
+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._frame.connect('event', (w, ev) => {
+            this._gesture.handle_event(ev) || this._gesture.is_recognized()
+        });
+        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 EmptyPlaceholder = GObject.registerClass(
 class EmptyPlaceholder extends Gtk.Box {
     _init() {


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]