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



commit f9a6b9b7a2740f8f712692dfff756596f96d3a92
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 | 217 ++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 202 insertions(+), 15 deletions(-)
---
diff --git a/js/extensionPrefs/main.js b/js/extensionPrefs/main.js
index efec7f9cc..71d238c30 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);
 
-        let errortext = '';
-        errortext += exc;
-        errortext += '\n\n';
-        errortext += 'Stack trace:\n';
+        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);
 
-        // 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
+            );
+        });
 
-        box.show_all();
-        return box;
+        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);
+
+        scroll.show_all();
+        return scroll;
     }
 
     _buildUI(app) {
@@ -250,6 +332,111 @@ 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
+        });
+        this.add(this._frame);
+
+        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.add(this._revealer);
+
+        this._childBin = new Gtk.Frame({
+            shadow_type: Gtk.ShadowType.IN
+        });
+        this._revealer.add(this._childBin);
+
+        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) {
+        if (child == this._frame || child == this._revealer) {
+            // add internal child
+            super.add(child);
+        } else {
+            // 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]