[geary/wip/problem-report-logs: 12/22] Break up Inspector's panes into seperate components for re-use



commit 343f9745be2ee2b140403cedc69704cdfc30669e
Author: Michael Gratton <mike vee net>
Date:   Thu Jul 4 11:57:53 2019 +1000

    Break up Inspector's panes into seperate components for re-use
    
    Create new InspectorLogView and InspectorSystemView classes abd UI, move
    code from inspector into those and use them in the inspector.

 .../components/components-inspector-log-view.vala  | 257 +++++++++++++++++
 .../components-inspector-system-view.vala          |  83 ++++++
 src/client/components/components-inspector.vala    | 304 ++++-----------------
 src/client/meson.build                             |   2 +
 ui/components-inspector-log-view.ui                |  83 ++++++
 ui/components-inspector-system-view.ui             |  61 +++++
 ui/components-inspector.ui                         | 137 +---------
 ui/org.gnome.Geary.gresource.xml                   |   2 +
 8 files changed, 541 insertions(+), 388 deletions(-)
---
diff --git a/src/client/components/components-inspector-log-view.vala 
b/src/client/components/components-inspector-log-view.vala
new file mode 100644
index 00000000..19ffbbe7
--- /dev/null
+++ b/src/client/components/components-inspector-log-view.vala
@@ -0,0 +1,257 @@
+/*
+ * Copyright 2019 Michael Gratton <mike vee net>
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+/**
+ * A view that displays the contents of the Engine's log.
+ */
+[GtkTemplate (ui = "/org/gnome/Geary/components-inspector-log-view.ui")]
+public class Components.InspectorLogView : Gtk.Grid {
+
+
+    private const int COL_MESSAGE = 0;
+
+
+    /** Determines if the log record search user interface is shown. */
+    public bool search_mode_enabled {
+        get { return this.search_bar.search_mode_enabled; }
+        set { this.search_bar.search_mode_enabled = value; }
+    }
+
+    [GtkChild]
+    private Hdy.SearchBar search_bar { get; private set; }
+
+    [GtkChild]
+    private Gtk.SearchEntry search_entry { get; private set; }
+
+    [GtkChild]
+    private Gtk.ScrolledWindow logs_scroller;
+
+    [GtkChild]
+    private Gtk.TreeView logs_view;
+
+    [GtkChild]
+    private Gtk.CellRendererText log_renderer;
+
+    private Gtk.ListStore logs_store = new Gtk.ListStore.newv({
+            typeof(string)
+    });
+
+    private Gtk.TreeModelFilter logs_filter;
+
+    private string[] logs_filter_terms = new string[0];
+
+    private bool update_logs = true;
+    private Geary.Logging.Record? first_pending = null;
+
+    private bool autoscroll = true;
+
+    private Geary.AccountInformation? account_filter = null;
+
+
+    /** Emitted when the number of selected records changes. */
+    public signal void record_selection_changed();
+
+
+    public InspectorLogView(Configuration config,
+                            Geary.AccountInformation? filter_by = null) {
+        GLib.Settings system = config.gnome_interface;
+        system.bind(
+            "monospace-font-name",
+            this.log_renderer, "font",
+            SettingsBindFlags.DEFAULT
+        );
+
+        this.search_bar.connect_entry(this.search_entry);
+    }
+
+    /** Loads log records from the logging system into the view. */
+    public void load() {
+        // Install the listener then start adding the backlog
+        // (ba-doom-tish) so to avoid the race.
+        Geary.Logging.set_log_listener(this.on_log_record);
+
+        Gtk.ListStore logs_store = this.logs_store;
+        Geary.Logging.Record? logs = Geary.Logging.get_logs();
+        int index = 0;
+        while (logs != null) {
+            if (should_append(logs)) {
+                string message = logs.format();
+                Gtk.TreeIter iter;
+                logs_store.insert(out iter, index++);
+                logs_store.set_value(iter, COL_MESSAGE, message);
+            }
+            logs = logs.next;
+        }
+
+        this.logs_filter = new Gtk.TreeModelFilter(logs_store, null);
+        this.logs_filter.set_visible_func((model, iter) => {
+                bool ret = true;
+                if (this.logs_filter_terms.length > 0) {
+                    ret = true;
+                    Value value;
+                    model.get_value(iter, COL_MESSAGE, out value);
+                    string? message = (string) value;
+                    if (message != null) {
+                        message = message.casefold();
+                        foreach (string term in this.logs_filter_terms) {
+                            if (!message.contains(term)) {
+                                ret = false;
+                                break;
+                            }
+                        }
+                    }
+                }
+                return ret;
+            });
+
+        this.logs_view.set_model(this.logs_filter);
+    }
+
+    /** {@inheritDoc} */
+    public override void destroy() {
+        Geary.Logging.set_log_listener(null);
+        base.destroy();
+    }
+
+    /** Forwards a key press event to the search entry. */
+    public bool handle_key_press(Gdk.EventKey event) {
+        return this.search_entry.key_press_event(event);
+    }
+
+    /** Returns the number of currently selected log records. */
+    public int count_selected_records() {
+        return this.logs_view.get_selection().count_selected_rows();
+    }
+
+    /** Enables and disables updating log records as new ones arrive. */
+    public void enable_log_updates(bool enabled) {
+        this.update_logs = enabled;
+
+        // Disable autoscroll when not updating as well to stop the
+        // tree view jumping to the bottom when changing the filter.
+        this.autoscroll = enabled;
+
+        if (enabled) {
+            Geary.Logging.Record? logs = this.first_pending;
+            while (logs != null) {
+                append_record(logs);
+                logs = logs.next;
+            }
+            this.first_pending = null;
+        }
+    }
+
+    /** Saves all log records to the given output stream. */
+    public void save(GLib.DataOutputStream out,
+                     bool save_all,
+                     GLib.Cancellable? cancellable)
+        throws GLib.Error {
+        Gtk.TreeModel model = this.logs_view.model;
+        if (save_all) {
+            // Save all rows selected
+            Gtk.TreeIter? iter;
+            bool valid = model.get_iter_first(out iter);
+            while (valid && !cancellable.is_cancelled()) {
+                save_record(model, iter, @out, cancellable);
+                valid = model.iter_next(ref iter);
+            }
+        } else {
+            // Save only selected
+            GLib.Error? inner_err = null;
+            this.logs_view.get_selection().selected_foreach(
+                (model, path, iter) => {
+                    if (inner_err == null) {
+                        try {
+                            save_record(model, iter, @out, cancellable);
+                        } catch (GLib.Error err) {
+                            inner_err = err;
+                        }
+                    }
+                }
+            );
+            if (inner_err != null) {
+                throw inner_err;
+            }
+        }
+    }
+
+    private inline void save_record(Gtk.TreeModel model,
+                                    Gtk.TreeIter iter,
+                                    GLib.DataOutputStream @out,
+                                    GLib.Cancellable? cancellable)
+        throws GLib.Error {
+        GLib.Value value;
+        model.get_value(iter, COL_MESSAGE, out value);
+        string? message = (string) value;
+        if (message != null) {
+            out.put_string(message);
+            out.put_byte('\n');
+        }
+    }
+
+    private inline bool should_append(Geary.Logging.Record record) {
+        // Blacklist GdkPixbuf since it spams us e.g. when window
+        // focus changes, including between MainWindow and the
+        // Inspector, which is very annoying.
+        record.fill_well_known_loggables();
+        return (
+            record.domain != "GdkPixbuf" &&
+            (record.account == null ||
+             this.account_filter == null ||
+             record.account.information == this.account_filter)
+        );
+    }
+
+    private void update_scrollbar() {
+        Gtk.Adjustment adj = this.logs_scroller.get_vadjustment();
+        adj.set_value(adj.upper - adj.page_size);
+    }
+
+    private void update_logs_filter() {
+        string cleaned =
+            Geary.String.reduce_whitespace(this.search_entry.text).casefold();
+        this.logs_filter_terms = cleaned.split(" ");
+        this.logs_filter.refilter();
+    }
+
+    private void append_record(Geary.Logging.Record record) {
+        if (should_append(record)) {
+            Gtk.TreeIter inserted_iter;
+            this.logs_store.append(out inserted_iter);
+            this.logs_store.set_value(inserted_iter, COL_MESSAGE, record.format());
+        }
+    }
+
+    [GtkCallback]
+    private void on_logs_size_allocate() {
+        if (this.autoscroll) {
+            update_scrollbar();
+        }
+    }
+
+    [GtkCallback]
+    private void on_logs_search_changed() {
+        update_logs_filter();
+    }
+
+    [GtkCallback]
+    private void on_logs_selection_changed() {
+        record_selection_changed();
+    }
+
+    private void on_log_record(Geary.Logging.Record record) {
+        if (this.update_logs) {
+            GLib.MainContext.default().invoke(() => {
+                    append_record(record);
+                    return GLib.Source.REMOVE;
+                });
+        } else if (this.first_pending == null) {
+            this.first_pending = record;
+        }
+    }
+
+}
diff --git a/src/client/components/components-inspector-system-view.vala 
b/src/client/components/components-inspector-system-view.vala
new file mode 100644
index 00000000..b4122e46
--- /dev/null
+++ b/src/client/components/components-inspector-system-view.vala
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2019 Michael Gratton <mike vee net>
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+/**
+ * A view that displays system and library information.
+ */
+[GtkTemplate (ui = "/org/gnome/Geary/components-inspector-system-view.ui")]
+public class Components.InspectorSystemView : Gtk.Grid {
+
+
+
+    private class DetailRow : Gtk.ListBoxRow {
+
+
+        private Gtk.Grid layout {
+            get; private set; default = new Gtk.Grid();
+        }
+
+        private Gtk.Label label {
+            get; private set; default = new Gtk.Label("");
+        }
+
+        private Gtk.Label value {
+            get; private set; default = new Gtk.Label("");
+        }
+
+
+        public DetailRow(string label, string value) {
+            get_style_context().add_class("geary-labelled-row");
+
+            this.label.halign = Gtk.Align.START;
+            this.label.valign = Gtk.Align.CENTER;
+            this.label.set_text(label);
+            this.label.show();
+
+            this.value.halign = Gtk.Align.END;
+            this.value.hexpand = true;
+            this.value.valign = Gtk.Align.CENTER;
+            this.value.xalign = 1.0f;
+            this.value.set_text(value);
+            this.value.show();
+
+            this.layout.orientation = Gtk.Orientation.HORIZONTAL;
+            this.layout.add(this.label);
+            this.layout.add(this.value);
+            this.layout.show();
+            add(this.layout);
+
+            this.activatable = false;
+            show();
+        }
+
+    }
+
+
+    [GtkChild]
+    private Gtk.ListBox system_list;
+
+    private string details;
+
+
+    public InspectorSystemView(GearyApplication application) {
+        StringBuilder details = new StringBuilder();
+        foreach (GearyApplication.RuntimeDetail? detail
+                 in application.get_runtime_information()) {
+            this.system_list.add(
+                new DetailRow("%s:".printf(detail.name), detail.value)
+            );
+            details.append_printf("%s: %s\n", detail.name, detail.value);
+        }
+        this.details = details.str;
+    }
+
+    public void save(GLib.DataOutputStream out, GLib.Cancellable? cancellable)
+        throws GLib.Error {
+        out.put_string(this.details, cancellable);
+    }
+
+}
diff --git a/src/client/components/components-inspector.vala b/src/client/components/components-inspector.vala
index 152bbdfc..b8102295 100644
--- a/src/client/components/components-inspector.vala
+++ b/src/client/components/components-inspector.vala
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2019 Michael Gratton <mike vee net>
  *
@@ -13,8 +12,6 @@
 public class Components.Inspector : Gtk.ApplicationWindow {
 
 
-    private const int COL_MESSAGE = 0;
-
     private const string ACTION_CLOSE = "inspector-close";
     private const string ACTION_PLAY_TOGGLE = "toggle-play";
     private const string ACTION_SEARCH_TOGGLE = "toggle-search";
@@ -45,150 +42,69 @@ public class Components.Inspector : Gtk.ApplicationWindow {
     [GtkChild]
     private Gtk.Button copy_button;
 
-    [GtkChild]
-    private Gtk.Widget logs_pane;
-
     [GtkChild]
     private Gtk.ToggleButton play_button;
 
     [GtkChild]
     private Gtk.ToggleButton search_button;
 
-    [GtkChild]
-    private Hdy.SearchBar search_bar;
-
-    [GtkChild]
-    private Gtk.SearchEntry search_entry;
-
-    [GtkChild]
-    private Gtk.ScrolledWindow logs_scroller;
-
-    [GtkChild]
-    private Gtk.TreeView logs_view;
-
-    [GtkChild]
-    private Gtk.CellRendererText log_renderer;
-
-    [GtkChild]
-    private Gtk.Widget detail_pane;
-
-    [GtkChild]
-    private Gtk.ListBox detail_list;
-
-    private Gtk.ListStore logs_store = new Gtk.ListStore.newv({
-            typeof(string)
-    });
-
-    private Gtk.TreeModelFilter logs_filter;
-
-    private string[] logs_filter_terms = new string[0];
-
-    private string details;
-
-    private bool update_logs = true;
-    private Geary.Logging.Record? first_pending = null;
+    private InspectorLogView log_pane;
+    private InspectorSystemView system_pane;
 
-    private bool autoscroll = true;
 
-
-    public Inspector(GearyApplication app) {
-        Object(application: app);
+    public Inspector(GearyApplication application) {
+        Object(application: application);
         this.title = this.header_bar.title = _("Inspector");
 
         add_action_entries(Inspector.action_entries, this);
 
-        this.search_bar.connect_entry(this.search_entry);
-
-        GLib.Settings system = app.config.gnome_interface;
-        system.bind(
-            "monospace-font-name",
-            this.log_renderer, "font",
-            SettingsBindFlags.DEFAULT
+        this.log_pane = new InspectorLogView(application.config, null);
+        this.log_pane.record_selection_changed.connect(
+            on_logs_selection_changed
         );
+        /// Translators: Title for Inspector logs pane
+        this.stack.add_titled(this.log_pane, "log_pane", _("Logs"));
 
-        StringBuilder details = new StringBuilder();
-        foreach (GearyApplication.RuntimeDetail? detail
-                 in app.get_runtime_information()) {
-            this.detail_list.add(
-                new DetailRow("%s:".printf(detail.name), detail.value)
-            );
-            details.append_printf("%s: %s\n", detail.name, detail.value);
-        }
-        this.details = details.str;
+        this.system_pane = new InspectorSystemView(application);
+        /// Translators: Title for Inspector system system information pane
+        this.stack.add_titled(this.system_pane, "system_pane", _("System"));
 
-        // Enable updates to get the log marker
+        // Enable updates to get the log marker, then load log records in
         enable_log_updates(true);
-
-        // Install the listener then starting add the backlog
-        // (ba-doom-tish) so to avoid the race.
-        Geary.Logging.set_log_listener(this.on_log_record);
-
-        Gtk.ListStore logs_store = this.logs_store;
-        Geary.Logging.Record? logs = Geary.Logging.get_logs();
-        int index = 0;
-        while (logs != null) {
-            if (should_append(logs)) {
-                string message = logs.format();
-                Gtk.TreeIter iter;
-                logs_store.insert(out iter, index++);
-                logs_store.set_value(iter, COL_MESSAGE, message);
-            }
-            logs = logs.next;
-        }
-
-        this.logs_filter = new Gtk.TreeModelFilter(logs_store, null);
-        this.logs_filter.set_visible_func((model, iter) => {
-                bool ret = true;
-                if (this.logs_filter_terms.length > 0) {
-                    ret = true;
-                    Value value;
-                    model.get_value(iter, COL_MESSAGE, out value);
-                    string? message = (string) value;
-                    if (message != null) {
-                        message = message.casefold();
-                        foreach (string term in this.logs_filter_terms) {
-                            if (!message.contains(term)) {
-                                ret = false;
-                                break;
-                            }
-                        }
-                    }
-                }
-                return ret;
-            });
-
-        this.logs_view.set_model(this.logs_filter);
-    }
-
-    public override void destroy() {
-        Geary.Logging.set_log_listener(null);
-        base.destroy();
+        this.log_pane.load();
     }
 
     public override bool key_press_event(Gdk.EventKey event) {
         bool ret = Gdk.EVENT_PROPAGATE;
 
-        if (this.search_bar.search_mode_enabled &&
+        if (this.log_pane.search_mode_enabled &&
             event.keyval == Gdk.Key.Escape) {
             // Manually deactivate search so the button stays in sync
             this.search_button.set_active(false);
             ret = Gdk.EVENT_STOP;
         }
 
-        if (ret == Gdk.EVENT_PROPAGATE) {
-            ret = this.search_bar.handle_event(event);
-        }
-
         if (ret == Gdk.EVENT_PROPAGATE &&
-            this.search_bar.search_mode_enabled) {
+            this.log_pane.search_mode_enabled) {
             // Ensure <Space> and others are passed to the search
             // entry before getting used as an accelerator.
-            ret = this.search_entry.key_press_event(event);
+            ret = this.log_pane.handle_key_press(event);
         }
 
         if (ret == Gdk.EVENT_PROPAGATE) {
             ret = base.key_press_event(event);
         }
+
+        if (ret == Gdk.EVENT_PROPAGATE &&
+            !this.log_pane.search_mode_enabled) {
+            // Nothing has handled the event yet, and search is not
+            // active, so see if we want to activate it now.
+            ret = this.log_pane.handle_key_press(event);
+            if (ret == Gdk.EVENT_STOP) {
+                this.search_button.set_active(true);
+            }
+        }
+
         return ret;
     }
 
@@ -196,31 +112,11 @@ public class Components.Inspector : Gtk.ApplicationWindow {
         // Log a marker to indicate when it was started/stopped
         debug(
             "---- 8< ---- %s %s ---- 8< ----",
-            this.header_bar.title,
+            this.title,
             enabled ? "▶" : "■"
         );
 
-        this.update_logs = enabled;
-
-        // Disable autoscroll when not updating as well to stop the
-        // tree view jumping to the bottom when changing the filter.
-        this.autoscroll = enabled;
-
-        if (enabled) {
-            Geary.Logging.Record? logs = this.first_pending;
-            while (logs != null) {
-                append_record(logs);
-                logs = logs.next;
-            }
-            this.first_pending = null;
-        }
-    }
-
-    private inline bool should_append(Geary.Logging.Record record) {
-        // Blacklist GdkPixbuf since it spams us e.g. when window
-        // focus changes, including between MainWindow and the
-        // Inspector, which is very annoying.
-        return (record.domain != "GdkPixbuf");
+        this.log_pane.enable_log_updates(enabled);
     }
 
     private async void save(string path,
@@ -236,85 +132,48 @@ public class Components.Inspector : Gtk.ApplicationWindow {
             new GLib.BufferedOutputStream(dest_io.get_output_stream())
         );
 
-        out.put_string(this.details);
+        this.system_pane.save(@out, cancellable);
         out.put_byte('\n');
         out.put_byte('\n');
-
-        Gtk.TreeModel model = this.logs_view.model;
-        Gtk.TreeIter? iter;
-        bool valid = model.get_iter_first(out iter);
-        while (valid && !cancellable.is_cancelled()) {
-            Value value;
-            model.get_value(iter, COL_MESSAGE, out value);
-            string? message = (string) value;
-            if (message != null) {
-                out.put_string(message);
-                out.put_byte('\n');
-            }
-            valid = model.iter_next(ref iter);
-        }
+        this.log_pane.save(@out, true, cancellable);
 
         yield out.close_async();
         yield dest_io.close_async();
     }
 
     private void update_ui() {
-        bool logs_visible = this.stack.visible_child == this.logs_pane;
-        uint logs_selected = this.logs_view.get_selection().count_selected_rows();
+        bool logs_visible = this.stack.visible_child == this.log_pane;
+        uint logs_selected = this.log_pane.count_selected_records();
         this.copy_button.set_sensitive(!logs_visible || logs_selected > 0);
         this.play_button.set_visible(logs_visible);
         this.search_button.set_visible(logs_visible);
     }
 
-    private void update_scrollbar() {
-        Gtk.Adjustment adj = this.logs_scroller.get_vadjustment();
-        adj.set_value(adj.upper - adj.page_size);
-    }
-
-    private void update_logs_filter() {
-        string cleaned =
-            Geary.String.reduce_whitespace(this.search_entry.text).casefold();
-        this.logs_filter_terms = cleaned.split(" ");
-        this.logs_filter.refilter();
-    }
-
-    private void append_record(Geary.Logging.Record record) {
-        if (should_append(record)) {
-            Gtk.TreeIter inserted_iter;
-            this.logs_store.append(out inserted_iter);
-            this.logs_store.set_value(inserted_iter, COL_MESSAGE, record.format());
-        }
-    }
-
     [GtkCallback]
     private void on_visible_child_changed() {
         update_ui();
     }
 
     private void on_copy_clicked() {
-        string clipboard_value = "";
-        if (this.stack.visible_child == this.logs_pane) {
-            StringBuilder rows = new StringBuilder();
-            Gtk.TreeModel model = this.logs_view.model;
-            foreach (Gtk.TreePath path in
-                     this.logs_view.get_selection().get_selected_rows(null)) {
-                Gtk.TreeIter iter;
-                if (model.get_iter(out iter, path)) {
-                    Value value;
-                    model.get_value(iter, COL_MESSAGE, out value);
-
-                    string? message = (string) value;
-                    if (message != null) {
-                        rows.append(message);
-                        rows.append_c('\n');
-                    }
-                }
+        GLib.MemoryOutputStream bytes = new GLib.MemoryOutputStream.resizable();
+        GLib.DataOutputStream out = new GLib.DataOutputStream(bytes);
+        try {
+            if (this.stack.visible_child == this.log_pane) {
+                this.log_pane.save(@out, false, null);
+            } else if (this.stack.visible_child == this.system_pane) {
+                this.system_pane.save(@out, null);
             }
-            clipboard_value = rows.str;
-        } else if (this.stack.visible_child == this.detail_pane) {
-            clipboard_value = this.details;
+
+            // Ensure the data is a valid string
+            out.put_byte(0, null);
+        } catch (GLib.Error err) {
+            warning(
+                "Error saving inspector data for clipboard: %s",
+                err.message
+            );
         }
 
+        string clipboard_value = (string) bytes.get_data();
         if (!Geary.String.is_empty(clipboard_value)) {
             get_clipboard(Gdk.SELECTION_CLIPBOARD).set_text(clipboard_value, -1);
         }
@@ -348,14 +207,6 @@ public class Components.Inspector : Gtk.ApplicationWindow {
         }
     }
 
-    [GtkCallback]
-    private void on_logs_size_allocate() {
-        if (this.autoscroll) {
-            update_scrollbar();
-        }
-    }
-
-    [GtkCallback]
     private void on_logs_selection_changed() {
         update_ui();
     }
@@ -363,13 +214,12 @@ public class Components.Inspector : Gtk.ApplicationWindow {
     private void on_logs_search_toggled(GLib.SimpleAction action,
                                         GLib.Variant? param) {
         bool enabled = !((bool) action.state);
-        this.search_bar.set_search_mode(enabled);
+        this.log_pane.search_mode_enabled = enabled;
         action.set_state(enabled);
     }
 
     private void on_logs_search_activated() {
         this.search_button.set_active(true);
-        this.search_entry.grab_focus();
     }
 
     private void on_logs_play_toggled(GLib.SimpleAction action,
@@ -379,60 +229,8 @@ public class Components.Inspector : Gtk.ApplicationWindow {
         action.set_state(enabled);
     }
 
-    [GtkCallback]
-    private void on_logs_search_changed() {
-        update_logs_filter();
-    }
-
-    private void on_log_record(Geary.Logging.Record record) {
-        if (this.update_logs) {
-            GLib.MainContext.default().invoke(() => {
-                    append_record(record);
-                    return GLib.Source.REMOVE;
-                });
-        } else if (this.first_pending == null) {
-            this.first_pending = record;
-        }
-    }
-
     private void on_close() {
         destroy();
     }
 
 }
-
-
-private class Components.DetailRow : Gtk.ListBoxRow {
-
-
-    private Gtk.Grid layout { get; private set; default = new Gtk.Grid(); }
-    private Gtk.Label label { get; private set; default = new Gtk.Label(""); }
-    private Gtk.Label value { get; private set; default = new Gtk.Label(""); }
-
-
-    public DetailRow(string label, string value) {
-        get_style_context().add_class("geary-labelled-row");
-
-        this.label.halign = Gtk.Align.START;
-        this.label.valign = Gtk.Align.CENTER;
-        this.label.set_text(label);
-        this.label.show();
-
-        this.value.halign = Gtk.Align.END;
-        this.value.hexpand = true;
-        this.value.valign = Gtk.Align.CENTER;
-        this.value.xalign = 1.0f;
-        this.value.set_text(value);
-        this.value.show();
-
-        this.layout.orientation = Gtk.Orientation.HORIZONTAL;
-        this.layout.add(this.label);
-        this.layout.add(this.value);
-        this.layout.show();
-        add(this.layout);
-
-        this.activatable = false;
-        show();
-    }
-
-}
diff --git a/src/client/meson.build b/src/client/meson.build
index df1cacdc..95aab1e3 100644
--- a/src/client/meson.build
+++ b/src/client/meson.build
@@ -24,6 +24,8 @@ geary_client_vala_sources = files(
 
   'components/client-web-view.vala',
   'components/components-inspector.vala',
+  'components/components-inspector-log-view.vala',
+  'components/components-inspector-system-view.vala',
   'components/components-placeholder-pane.vala',
   'components/components-validator.vala',
   'components/count-badge.vala',
diff --git a/ui/components-inspector-log-view.ui b/ui/components-inspector-log-view.ui
new file mode 100644
index 00000000..6777cc1d
--- /dev/null
+++ b/ui/components-inspector-log-view.ui
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface>
+  <requires lib="gtk+" version="3.20"/>
+  <requires lib="libhandy" version="0.0"/>
+  <object class="GtkListStore" id="logs_store">
+    <columns>
+      <!-- column-name log -->
+      <column type="gchararray"/>
+    </columns>
+    <data>
+      <row>
+        <col id="0" translatable="yes">Inspector opened</col>
+      </row>
+    </data>
+  </object>
+  <template class="ComponentsInspectorLogView" parent="GtkGrid">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <child>
+      <object class="HdySearchBar" id="search_bar">
+        <property name="name">search_bar</property>
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="hexpand">True</property>
+        <child>
+          <object class="GtkSearchEntry" id="search_entry">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="primary_icon_name">edit-find-symbolic</property>
+            <property name="primary_icon_activatable">False</property>
+            <property name="primary_icon_sensitive">False</property>
+            <signal name="search-changed" handler="on_logs_search_changed" swapped="no"/>
+          </object>
+        </child>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">0</property>
+              </packing>
+    </child>
+    <child>
+      <object class="GtkScrolledWindow" id="logs_scroller">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="vexpand">True</property>
+        <property name="shadow_type">in</property>
+        <child>
+          <object class="GtkTreeView" id="logs_view">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="model">logs_store</property>
+            <property name="headers_visible">False</property>
+            <property name="enable_search">False</property>
+            <property name="show_expanders">False</property>
+            <signal name="size-allocate" handler="on_logs_size_allocate" swapped="no"/>
+            <child internal-child="selection">
+              <object class="GtkTreeSelection">
+                <property name="mode">multiple</property>
+                <signal name="changed" handler="on_logs_selection_changed" swapped="no"/>
+              </object>
+            </child>
+            <child>
+              <object class="GtkTreeViewColumn" id="log_column">
+                <property name="title" translatable="yes">column</property>
+                <child>
+                  <object class="GtkCellRendererText" id="log_renderer"/>
+                  <attributes>
+                    <attribute name="text">0</attribute>
+                  </attributes>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">1</property>
+      </packing>
+    </child>
+  </template>
+</interface>
diff --git a/ui/components-inspector-system-view.ui b/ui/components-inspector-system-view.ui
new file mode 100644
index 00000000..8ef50dc4
--- /dev/null
+++ b/ui/components-inspector-system-view.ui
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface>
+  <requires lib="gtk+" version="3.20"/>
+  <requires lib="libhandy" version="0.0"/>
+  <template class="ComponentsInspectorSystemView" parent="GtkGrid">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <child>
+      <object class="GtkScrolledWindow">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="hscrollbar_policy">never</property>
+        <property name="shadow_type">in</property>
+        <property name="max_content_width">600</property>
+        <property name="vexpand">True</property>
+        <child>
+          <object class="GtkViewport">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <child>
+              <object class="HdyColumn">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="margin_left">16</property>
+                <property name="margin_right">16</property>
+                <property name="margin_top">32</property>
+                <property name="margin_bottom">32</property>
+                <property name="maximum_width">500</property>
+                <property name="linear_growth_width">1</property>
+                <child>
+                  <object class="GtkFrame">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label_xalign">0</property>
+                    <child>
+                      <object class="GtkListBox" id="system_list">
+                        <property name="name">system_list</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="selection_mode">none</property>
+                        <property name="activate_on_single_click">False</property>
+                      </object>
+                    </child>
+                    <child type="label_item">
+                      <placeholder/>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">0</property>
+      </packing>
+    </child>
+  </template>
+</interface>
diff --git a/ui/components-inspector.ui b/ui/components-inspector.ui
index 9c0080ac..8669200e 100644
--- a/ui/components-inspector.ui
+++ b/ui/components-inspector.ui
@@ -3,17 +3,6 @@
 <interface>
   <requires lib="gtk+" version="3.20"/>
   <requires lib="libhandy" version="0.0"/>
-  <object class="GtkListStore" id="logs_store">
-    <columns>
-      <!-- column-name log -->
-      <column type="gchararray"/>
-    </columns>
-    <data>
-      <row>
-        <col id="0" translatable="yes">Inspector opened</col>
-      </row>
-    </data>
-  </object>
   <template class="ComponentsInspector" parent="GtkApplicationWindow">
     <property name="can_focus">False</property>
     <property name="default_width">750</property>
@@ -91,7 +80,8 @@
             <property name="visible">True</property>
             <property name="can_focus">True</property>
             <property name="receives_default">True</property>
-            <property name="tooltip_text" translatable="yes" comments="Tooltip for inspector button">Copy 
selected log entries</property>
+            <property name="tooltip_text" translatable="yes"
+                      comments="Tooltip for inspector button">Copy to clipboard</property>
             <property name="action_name">win.copy</property>
             <child>
               <object class="GtkImage">
@@ -113,129 +103,6 @@
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <signal name="notify::visible-child" handler="on_visible_child_changed" swapped="no"/>
-        <child>
-          <object class="GtkGrid" id="logs_pane">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <child>
-              <object class="HdySearchBar" id="search_bar">
-                <property name="name">search_bar</property>
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="hexpand">True</property>
-                <child>
-                  <object class="GtkSearchEntry" id="search_entry">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="primary_icon_name">edit-find-symbolic</property>
-                    <property name="primary_icon_activatable">False</property>
-                    <property name="primary_icon_sensitive">False</property>
-                    <signal name="search-changed" handler="on_logs_search_changed" swapped="no"/>
-                  </object>
-                </child>
-              </object>
-              <packing>
-                <property name="left_attach">0</property>
-                <property name="top_attach">0</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkScrolledWindow" id="logs_scroller">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="vexpand">True</property>
-                <property name="shadow_type">in</property>
-                <child>
-                  <object class="GtkTreeView" id="logs_view">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="model">logs_store</property>
-                    <property name="headers_visible">False</property>
-                    <property name="enable_search">False</property>
-                    <property name="show_expanders">False</property>
-                    <signal name="size-allocate" handler="on_logs_size_allocate" swapped="no"/>
-                    <child internal-child="selection">
-                      <object class="GtkTreeSelection">
-                        <property name="mode">multiple</property>
-                        <signal name="changed" handler="on_logs_selection_changed" swapped="no"/>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkTreeViewColumn" id="log_column">
-                        <property name="title" translatable="yes">column</property>
-                        <child>
-                          <object class="GtkCellRendererText" id="log_renderer"/>
-                          <attributes>
-                            <attribute name="text">0</attribute>
-                          </attributes>
-                        </child>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-              </object>
-              <packing>
-                <property name="left_attach">0</property>
-                <property name="top_attach">1</property>
-              </packing>
-            </child>
-          </object>
-          <packing>
-            <property name="name">logs_pane</property>
-            <property name="title" translatable="yes" comments="Inspector stack title">Logs</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkScrolledWindow" id="detail_pane">
-            <property name="visible">True</property>
-            <property name="can_focus">True</property>
-            <property name="hscrollbar_policy">never</property>
-            <property name="shadow_type">in</property>
-            <property name="max_content_width">600</property>
-            <child>
-              <object class="GtkViewport">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <child>
-                  <object class="HdyColumn">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="margin_left">16</property>
-                    <property name="margin_right">16</property>
-                    <property name="margin_top">32</property>
-                    <property name="margin_bottom">32</property>
-                    <property name="maximum_width">500</property>
-                    <property name="linear_growth_width">1</property>
-                    <child>
-                      <object class="GtkFrame">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="label_xalign">0</property>
-                        <child>
-                          <object class="GtkListBox" id="detail_list">
-                            <property name="name">detail_list</property>
-                            <property name="visible">True</property>
-                            <property name="can_focus">False</property>
-                            <property name="selection_mode">none</property>
-                            <property name="activate_on_single_click">False</property>
-                          </object>
-                        </child>
-                        <child type="label_item">
-                          <placeholder/>
-                        </child>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-              </object>
-            </child>
-          </object>
-          <packing>
-            <property name="name">general_pane</property>
-            <property name="title" translatable="yes" comments="Inspector stack title">General</property>
-            <property name="position">1</property>
-          </packing>
-        </child>
       </object>
     </child>
   </template>
diff --git a/ui/org.gnome.Geary.gresource.xml b/ui/org.gnome.Geary.gresource.xml
index 3fd3b76e..1065416e 100644
--- a/ui/org.gnome.Geary.gresource.xml
+++ b/ui/org.gnome.Geary.gresource.xml
@@ -11,6 +11,8 @@
     <file compressed="true">client-web-view.js</file>
     <file compressed="true">client-web-view-allow-remote-images.js</file>
     <file compressed="true" preprocess="xml-stripblanks">components-inspector.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks">components-inspector-log-view.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks">components-inspector-system-view.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">components-placeholder-pane.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">composer-headerbar.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">composer-link-popover.ui</file>



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