[geary/wip/713006-better-error-reporting: 1/6] Add initial support for using Gtk.InfoBars to display errors.



commit 80680f280ea122cfd1c4f569d107c497c392e41e
Author: Michael James Gratton <mike vee net>
Date:   Wed Nov 8 18:13:51 2017 +1100

    Add initial support for using Gtk.InfoBars to display errors.
    
    * src/client/components/main-window-info-bar.vala (MainWindowInfoBar):
      New class and UI file for displaying an info bar in the main window, as
      well as providing a way to show technical informartion if needed.
    
    * src/client/components/main-window.vala (MainWindow): Add a Gtk.Frame
      and container to hold inforbar instances. Show it when showing an
      infobar, and hide it when there are none. Add show_infobar() method to
      provide a cromulent way of adding info bars.
    
    * src/client/application/geary-controller.vala (BaseObject): Rather than
      bailing out on an account when an error occurs, display an info bar
      instead.
    
    * ui/geary.css: Style the info bar frame to only show a border between
      main content and the info bars.

 po/POTFILES.in                                  |    2 +
 src/CMakeLists.txt                              |    1 +
 src/client/application/geary-controller.vala    |   72 +++++---
 src/client/components/main-window-info-bar.vala |  245 +++++++++++++++++++++++
 src/client/components/main-window.vala          |   21 ++
 ui/CMakeLists.txt                               |    1 +
 ui/geary.css                                    |   10 +
 ui/main-window-info-bar.ui                      |  142 +++++++++++++
 ui/main-window.ui                               |  114 ++++++++---
 9 files changed, 552 insertions(+), 56 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 1f25f47..1969cba 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -31,6 +31,7 @@ src/client/components/folder-popover.vala
 src/client/components/icon-factory.vala
 src/client/components/main-toolbar.vala
 src/client/components/main-window.vala
+src/client/components/main-window-info-bar.vala
 src/client/components/monitored-progress-bar.vala
 src/client/components/monitored-spinner.vala
 src/client/components/search-bar.vala
@@ -411,6 +412,7 @@ ui/login.glade
 ui/main-toolbar.ui
 ui/main-toolbar-menus.ui
 ui/main-window.ui
+ui/main-window-info-bar.ui
 ui/password-dialog.glade
 ui/preferences-dialog.ui
 ui/remove_confirm.glade
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 218f525..efe2403 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -342,6 +342,7 @@ client/components/folder-popover.vala
 client/components/icon-factory.vala
 client/components/main-toolbar.vala
 client/components/main-window.vala
+client/components/main-window-info-bar.vala
 client/components/monitored-progress-bar.vala
 client/components/monitored-spinner.vala
 client/components/search-bar.vala
diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala
index 451218f..910af28 100644
--- a/src/client/application/geary-controller.vala
+++ b/src/client/application/geary-controller.vala
@@ -872,44 +872,66 @@ public class GearyController : Geary.BaseObject {
             debug("Error updating stored passwords: %s", e.message);
         }
     }
-    
+
     private void on_report_problem(Geary.Account account, Geary.Account.Problem problem, Error? err) {
         debug("Reported problem: %s Error: %s", problem.to_string(), err != null ? err.message : "(N/A)");
-        
+
         switch (problem) {
-            case Geary.Account.Problem.DATABASE_FAILURE:
-            case Geary.Account.Problem.HOST_UNREACHABLE:
-            case Geary.Account.Problem.NETWORK_UNAVAILABLE:
-                // TODO
+        case Geary.Account.Problem.CONNECTION_FAILURE:
+            ErrorDialog dialog = new ErrorDialog(
+                main_window,
+                _("Error connecting to the server"),
+                _("Geary encountered an error while connecting to the server.  Please try again in a few 
moments.")
+            );
+            dialog.run();
             break;
-            
-            case Geary.Account.Problem.RECV_EMAIL_LOGIN_FAILED:
-            case Geary.Account.Problem.SEND_EMAIL_LOGIN_FAILED:
-                // At this point, we've prompted them for the password and
-                // they've hit cancel, so there's not much for us to do here.
-                close_account(account);
+
+        case Geary.Account.Problem.DATABASE_FAILURE:
+        case Geary.Account.Problem.HOST_UNREACHABLE:
+        case Geary.Account.Problem.NETWORK_UNAVAILABLE:
+        case Geary.Account.Problem.RECV_EMAIL_LOGIN_FAILED:
+        case Geary.Account.Problem.SEND_EMAIL_ERROR:
+        case Geary.Account.Problem.SEND_EMAIL_LOGIN_FAILED:
+            MainWindowInfoBar info_bar = new MainWindowInfoBar.for_problem(
+                problem, account, err
+            );
+            info_bar.retry.connect(on_retry_problem);
+            this.main_window.show_infobar(info_bar);
+            break;
+
+        case Geary.Account.Problem.SEND_EMAIL_DELIVERY_FAILURE:
+            handle_outbox_failure(StatusBar.Message.OUTBOX_SEND_FAILURE);
             break;
 
-            case Geary.Account.Problem.SEND_EMAIL_DELIVERY_FAILURE:
-                handle_outbox_failure(StatusBar.Message.OUTBOX_SEND_FAILURE);
+        case Geary.Account.Problem.SEND_EMAIL_SAVE_FAILED:
+            handle_outbox_failure(StatusBar.Message.OUTBOX_SAVE_SENT_MAIL_FAILED);
             break;
 
-            case Geary.Account.Problem.SEND_EMAIL_SAVE_FAILED:
-                handle_outbox_failure(StatusBar.Message.OUTBOX_SAVE_SENT_MAIL_FAILED);
+        default:
+            assert_not_reached();
+        }
+    }
+
+    private void on_retry_problem(MainWindowInfoBar info_bar) {
+        switch (info_bar.problem) {
+        case Geary.Account.Problem.RECV_EMAIL_LOGIN_FAILED:
             break;
 
-            case Geary.Account.Problem.CONNECTION_FAILURE:
-                ErrorDialog dialog = new ErrorDialog(main_window,
-                    _("Error connecting to the server"),
-                    _("Geary encountered an error while connecting to the server.  Please try again in a few 
moments."));
-                dialog.run();
+        case Geary.Account.Problem.SEND_EMAIL_ERROR:
             break;
 
-            default:
-                assert_not_reached();
+        case Geary.Account.Problem.SEND_EMAIL_LOGIN_FAILED:
+            break;
+
+        default:
+            debug("Un-handled problem retry for %s: %s".printf(
+                      info_bar.account.information.id,
+                      info_bar.problem.to_string()
+                  ));
+            break;
         }
     }
-    
+
     private void handle_outbox_failure(StatusBar.Message message) {
         bool activate_message = false;
         try {
@@ -2817,5 +2839,5 @@ public class GearyController : Geary.BaseObject {
                 });
         }
     }
-}
 
+}
diff --git a/src/client/components/main-window-info-bar.vala b/src/client/components/main-window-info-bar.vala
new file mode 100644
index 0000000..a5a6691
--- /dev/null
+++ b/src/client/components/main-window-info-bar.vala
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2017 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.
+ */
+
+/**
+ * Displays application-wide or important account-related messages.
+ */
+[GtkTemplate (ui = "/org/gnome/Geary/main-window-info-bar.ui")]
+public class MainWindowInfoBar : Gtk.InfoBar {
+
+
+    private enum ResponseType { COPY, DETAILS, RETRY; }
+
+
+    /** If reporting a problem returns, the specific problem else null. */
+    public Geary.Account.Problem? problem { get; private set; default = null; }
+
+    /** If reporting a problem for an account, returns the account else null. */
+    public Geary.Account? account { get; private set; default = null; }
+
+    /** If reporting a problem, returns the error thrown, if any. */
+    public Error error { get; private set; default = null; }
+
+
+    /** Emitted when the user clicks the Retry button, if any. */
+    public signal void retry();
+
+
+    [GtkChild]
+    private Gtk.Label title;
+
+    [GtkChild]
+    private Gtk.Label description;
+
+    [GtkChild]
+    private Gtk.Grid problem_details;
+
+    [GtkChild]
+    private Gtk.TextView detail_text;
+
+
+    public MainWindowInfoBar.for_problem(Geary.Account.Problem problem,
+                                         Geary.Account account,
+                                         GLib.Error? error) {
+        string name = account.information.display_name;
+        Gtk.MessageType type = Gtk.MessageType.WARNING;
+        string title = "";
+        string descr = "";
+        string? retry = null;
+        bool show_close = false;
+        switch (problem) {
+        case Geary.Account.Problem.DATABASE_FAILURE:
+            type = Gtk.MessageType.ERROR;
+            title = _("A database problem has occurred");
+            descr = _("Messages for %s must be downloaded again.").printf(name);
+            show_close = true;
+            break;
+
+        case Geary.Account.Problem.HOST_UNREACHABLE:
+            // XXX should really be displaying the server name here
+            title = _("Could not contact server");
+            descr = _("Please check %s server names are correct and are working.").printf(name);
+            show_close = true;
+            break;
+
+        case Geary.Account.Problem.NETWORK_UNAVAILABLE:
+            title = _("Not connected to the Internet");
+            descr = _("Please check your connection to the Internet.");
+            show_close = true;
+            break;
+
+        case Geary.Account.Problem.RECV_EMAIL_LOGIN_FAILED:
+            title = _("Incoming mail password required");
+            descr = _("Messages cannot be received for %s without the correct password.").printf(name);
+            retry = _("Retry receiving email, you will be prompted for a password");
+            break;
+
+        case Geary.Account.Problem.SEND_EMAIL_ERROR:
+            type = Gtk.MessageType.ERROR;
+            title = _("A problem occurred sending mail");
+            descr = _("A message was unable to be sent for %s, try again in a moment").printf(name);
+            retry = _("Retry sending queued messages");
+            break;
+
+        case Geary.Account.Problem.SEND_EMAIL_LOGIN_FAILED:
+            title = _("Outgoing mail password required");
+            descr = _("Messages cannot be sent for %s without the correct password.").printf(name);
+            retry = _("Retry sending queued messages, you will be prompted for a password");
+            break;
+
+        default:
+            debug("Un-handled problem type for %s: %s".printf(
+                      account.information.id, problem.to_string()
+                  ));
+            break;
+        }
+
+        this(type, title, descr, show_close);
+        this.problem = problem;
+        this.account = account;
+        this.error = error;
+
+        if (this.error != null) {
+            Gtk.Button details = add_button(_("_Details"), ResponseType.DETAILS);
+            details.tooltip_text = _("View technical details about the error");
+        }
+
+        if (retry != null) {
+            Gtk.Button retry_btn = add_button(_("_Retry"), ResponseType.RETRY);
+            retry_btn.tooltip_text = retry;
+        }
+    }
+
+    protected MainWindowInfoBar(Gtk.MessageType type,
+                                string title,
+                                string description,
+                                bool show_close) {
+        this.message_type = type;
+        this.title.label = title;
+        this.description.label = description;
+        this.show_close_button = show_close;
+    }
+
+    private string format_details() {
+        string type = "";
+        if (this.error != null) {
+            const string QUARK_SUFFIX = "-quark";
+            string ugly_domain = this.error.domain.to_string();
+            if (ugly_domain.has_suffix(QUARK_SUFFIX)) {
+                ugly_domain = ugly_domain.substring(
+                    0, ugly_domain.length - QUARK_SUFFIX.length
+                );
+            }
+            StringBuilder nice_domain = new StringBuilder();
+            foreach (string part in ugly_domain.split("_")) {
+                nice_domain.append(part.up(1));
+                nice_domain.append(part.substring(1));
+            }
+
+            type = "%s %i".printf(nice_domain.str, this.error.code);
+        }
+
+        return """Geary version: %s
+GTK+ version: %u.%u.%u
+Desktop: %s
+Error type: %s
+Message: %s
+""".printf(
+        GearyApplication.VERSION,
+        Gtk.get_major_version(), Gtk.get_minor_version(), Gtk.get_micro_version(),
+        Environment.get_variable("XDG_CURRENT_DESKTOP") ?? "Unknown",
+        type,
+        (this.error != null) ? error.message : ""
+    );
+    }
+
+    private void show_details() {
+        this.detail_text.buffer.text = format_details();
+
+        // Would love to construct the dialog in Builder, but we to
+        // construct the dialog manually since we can't adjust the
+        // Headerbar setting afterwards. If the user re-clicks on the
+        // Details button to re-show it, a whole bunch of GTK
+        // criticals are spewed and the dialog appears b0rked, so just
+        // do it from scratch ever time anyway.
+        bool use_header = Gtk.Settings.get_default().gtk_dialogs_use_header;
+        Gtk.DialogFlags flags = Gtk.DialogFlags.MODAL;
+        if (use_header) {
+            flags |= Gtk.DialogFlags.USE_HEADER_BAR;
+        }
+        Gtk.Dialog dialog = new Gtk.Dialog.with_buttons(
+            _("Details"), // same as the button
+            get_toplevel() as Gtk.Window,
+            flags
+        );
+        dialog.set_default_size(600, -1);
+        dialog.get_content_area().add(this.problem_details);
+
+        Gtk.HeaderBar? header_bar = dialog.get_header_bar() as Gtk.HeaderBar;
+        use_header = (header_bar != null);
+        if (use_header) {
+            header_bar.show_close_button = true;
+        } else {
+            dialog.add_button(_("_Close"), Gtk.ResponseType.CLOSE);
+        }
+
+        Gtk.Widget copy = dialog.add_button(
+            _("Copy to Clipboard"), ResponseType.COPY
+        );
+        copy.tooltip_text =
+            _("Copy technical details to clipboard for pasting into an email or bug report");
+
+
+        dialog.set_default_response(ResponseType.COPY);
+        dialog.response.connect(on_details_response);
+        dialog.show();
+        copy.grab_focus();
+    }
+
+    private void copy_details() {
+        get_clipboard(Gdk.SELECTION_CLIPBOARD).set_text(format_details(), -1);
+    }
+
+    [GtkCallback]
+    private void on_info_bar_response(int response) {
+        switch(response) {
+        case ResponseType.DETAILS:
+            show_details();
+            break;
+
+        case ResponseType.RETRY:
+            retry();
+            this.hide();
+            break;
+
+        default:
+            this.hide();
+            break;
+        }
+    }
+
+    [GtkCallback]
+    private void on_hide() {
+        this.parent.remove(this);
+    }
+
+    private void on_details_response(Gtk.Dialog dialog, int response) {
+        switch(response) {
+        case ResponseType.COPY:
+            copy_details();
+            break;
+
+        default:
+            // fml
+            dialog.get_content_area().remove(this.problem_details);
+            dialog.hide();
+            break;
+        }
+    }
+
+
+}
diff --git a/src/client/components/main-window.vala b/src/client/components/main-window.vala
index 13fedf7..4161fa3 100644
--- a/src/client/components/main-window.vala
+++ b/src/client/components/main-window.vala
@@ -50,6 +50,12 @@ public class MainWindow : Gtk.ApplicationWindow {
     [GtkChild]
     private Gtk.ScrolledWindow conversation_list_scrolled;
 
+    // This is a frame so users can use F6/Shift-F6 to get to it
+    [GtkChild]
+    private Gtk.Frame info_bar_frame;
+
+    [GtkChild]
+    private Gtk.Grid info_bar_container;
 
     /** Fired when the shift key is pressed or released. */
     public signal void on_shift_key(bool pressed);
@@ -72,6 +78,11 @@ public class MainWindow : Gtk.ApplicationWindow {
         on_change_orientation();
     }
 
+    public void show_infobar(MainWindowInfoBar info_bar) {
+        this.info_bar_container.add(info_bar);
+        this.info_bar_frame.show();
+    }
+
     private void load_config(Configuration config) {
         // This code both loads AND saves the pane positions with live updating. This is more
         // resilient against crashes because the value in dconf changes *immediately*, and
@@ -422,4 +433,14 @@ public class MainWindow : Gtk.ApplicationWindow {
         }
         return Gdk.EVENT_STOP;
     }
+
+    [GtkCallback]
+    private void on_info_bar_container_remove() {
+        // Ensure the info bar frame is hidden when the last info bar
+        // is removed from the container.
+        if (this.info_bar_container.get_children().length() == 0) {
+            this.info_bar_frame.hide();
+        }
+    }
+
 }
diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt
index 05a74e7..4513df6 100644
--- a/ui/CMakeLists.txt
+++ b/ui/CMakeLists.txt
@@ -30,6 +30,7 @@ set(RESOURCE_LIST
   STRIPBLANKS "main-toolbar.ui"
   STRIPBLANKS "main-toolbar-menus.ui"
   STRIPBLANKS "main-window.ui"
+  STRIPBLANKS "main-window-info-bar.ui"
   STRIPBLANKS "password-dialog.glade"
   STRIPBLANKS "preferences-dialog.ui"
   STRIPBLANKS "remove_confirm.glade"
diff --git a/ui/geary.css b/ui/geary.css
index cfeb72d..d92ca39 100644
--- a/ui/geary.css
+++ b/ui/geary.css
@@ -6,6 +6,8 @@
  * (version 2.1 or later). See the COPYING file in this distribution.
  */
 
+/* MainWindow */
+
 .geary-folder-frame > border {
   border-left-width: 0;
   border-top-width: 0;
@@ -39,6 +41,14 @@
   border-top-left-radius: 0px;
 }
 
+/* MainWindowInfoBarSet */
+
+.geary-info-bar-frame > border {
+  border-top-width: 0;
+  border-left-width: 0;
+  border-right-width: 0;
+}
+
 /* FolderPopover */
 
 row.geary-folder-popover-list-row {
diff --git a/ui/main-window-info-bar.ui b/ui/main-window-info-bar.ui
new file mode 100644
index 0000000..465f441
--- /dev/null
+++ b/ui/main-window-info-bar.ui
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
+<interface>
+  <requires lib="gtk+" version="3.20"/>
+  <template class="MainWindowInfoBar" parent="GtkInfoBar">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="hexpand">True</property>
+    <signal name="hide" handler="on_hide" after="yes" swapped="no"/>
+    <signal name="response" handler="on_info_bar_response" swapped="no"/>
+    <child internal-child="action_area">
+      <object class="GtkButtonBox">
+        <property name="can_focus">False</property>
+        <property name="spacing">6</property>
+        <property name="layout_style">end</property>
+        <child>
+          <placeholder/>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">False</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+    <child internal-child="content_area">
+      <object class="GtkBox">
+        <property name="can_focus">False</property>
+        <property name="spacing">16</property>
+        <child>
+          <object class="GtkGrid">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <child>
+              <object class="GtkLabel" id="title">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">start</property>
+                <property name="label">Title</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="description">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">start</property>
+                <property name="label">Description.</property>
+                <property name="ellipsize">end</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <style>
+          <class name="sigh"/>
+        </style>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">False</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+  </template>
+  <object class="GtkGrid" id="problem_details">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="margin_left">18</property>
+    <property name="margin_right">18</property>
+    <property name="margin_top">18</property>
+    <property name="margin_bottom">18</property>
+    <property name="row_spacing">6</property>
+    <property name="column_spacing">12</property>
+    <child>
+      <object class="GtkLabel">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="halign">start</property>
+        <property name="valign">baseline</property>
+        <property name="margin_bottom">12</property>
+        <property name="hexpand">True</property>
+        <property name="label" translatable="yes">If the problem is serious or persists, please copy and 
send these details to the &lt;a href="https://wiki.gnome.org/Apps/Geary/Contact"&gt;mailing list&lt;/a&gt; or 
lodge a &lt;a href="https://wiki.gnome.org/Apps/Geary/ReportingABug"&gt;bug report&lt;/a&gt;.</property>
+        <property name="use_markup">True</property>
+        <property name="wrap">True</property>
+        <property name="xalign">0</property>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">0</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkLabel">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="halign">start</property>
+        <property name="valign">baseline</property>
+        <property name="label" translatable="yes">Details:</property>
+        <property name="track_visited_links">False</property>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">1</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkTextView" id="detail_text">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="hexpand">True</property>
+        <property name="vexpand">True</property>
+        <property name="editable">False</property>
+        <property name="wrap_mode">word</property>
+        <property name="left_margin">6</property>
+        <property name="right_margin">6</property>
+        <property name="top_margin">6</property>
+        <property name="bottom_margin">6</property>
+        <property name="cursor_visible">False</property>
+        <property name="monospace">True</property>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">2</property>
+      </packing>
+    </child>
+  </object>
+</interface>
diff --git a/ui/main-window.ui b/ui/main-window.ui
index 1ed5894..e09e308 100644
--- a/ui/main-window.ui
+++ b/ui/main-window.ui
@@ -1,69 +1,60 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
 <interface>
-  <requires lib="gtk+" version="3.14"/>
+  <requires lib="gtk+" version="3.20"/>
   <template class="MainWindow" parent="GtkApplicationWindow">
-    <property name="visible">False</property>
-    <property name="show_menubar">False</property>
     <property name="name">GearyMainWindow</property>
+    <property name="can_focus">False</property>
     <property name="events">GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_FOCUS_CHANGE_MASK | 
GDK_STRUCTURE_MASK</property>
-    <signal name="delete_event" handler="on_delete_event"/>
-    <signal name="key_release_event" handler="on_key_release_event"/>
-    <signal name="focus_in_event" handler="on_focus_event"/>
+    <property name="show_menubar">False</property>
+    <signal name="delete-event" handler="on_delete_event" swapped="no"/>
+    <signal name="focus-in-event" handler="on_focus_event" swapped="no"/>
+    <signal name="key-release-event" handler="on_key_release_event" swapped="no"/>
     <child>
       <object class="GtkBox" id="main_layout">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="orientation">vertical</property>
-        <property name="spacing">0</property>
         <child>
           <object class="GtkPaned" id="conversations_paned">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
-            <property name="orientation">horizontal</property>
             <child>
               <object class="GtkBox" id="search_bar_box">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
                 <property name="orientation">vertical</property>
-                <property name="spacing">0</property>
-                <style>
-                  <class name="sidebar"/>
-                </style>
                 <child>
                   <object class="GtkPaned" id="folder_paned">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="orientation">horizontal</property>
-                    <style>
-                      <class name="geary-sidebar-pane-separator"/>
-                    </style>
                     <child>
                       <object class="GtkBox" id="folder_box">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
                         <property name="orientation">vertical</property>
-                        <property name="spacing">0</property>
                         <child>
                           <object class="GtkFrame" id="folder_frame">
                             <property name="visible">True</property>
                             <property name="can_focus">False</property>
+                            <property name="label_xalign">0</property>
                             <property name="shadow_type">in</property>
-                            <style>
-                              <class name="geary-folder-frame"/>
-                            </style>
                             <child>
                               <object class="GtkScrolledWindow" id="folder_list_scrolled">
+                                <property name="width_request">100</property>
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
-                                <property name="width_request">100</property>
                                 <property name="hscrollbar_policy">never</property>
-                                <property name="vscrollbar_policy">automatic</property>
                               </object>
                             </child>
+                            <style>
+                              <class name="geary-folder-frame"/>
+                            </style>
                           </object>
                           <packing>
                             <property name="expand">True</property>
                             <property name="fill">True</property>
+                            <property name="position">0</property>
                           </packing>
                         </child>
                       </object>
@@ -77,28 +68,27 @@
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
                         <property name="orientation">vertical</property>
-                        <property name="spacing">0</property>
                         <child>
                           <object class="GtkFrame" id="conversation_frame">
                             <property name="visible">True</property>
                             <property name="can_focus">False</property>
+                            <property name="label_xalign">0</property>
                             <property name="shadow_type">in</property>
-                            <style>
-                              <class name="geary-conversation-frame"/>
-                            </style>
                             <child>
                               <object class="GtkScrolledWindow" id="conversation_list_scrolled">
+                                <property name="width_request">250</property>
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
-                                <property name="width_request">250</property>
-                                <property name="hscrollbar_policy">automatic</property>
-                                <property name="vscrollbar_policy">automatic</property>
                               </object>
                             </child>
+                            <style>
+                              <class name="geary-conversation-frame"/>
+                            </style>
                           </object>
                           <packing>
                             <property name="expand">True</property>
                             <property name="fill">True</property>
+                            <property name="position">0</property>
                           </packing>
                         </child>
                       </object>
@@ -107,13 +97,20 @@
                         <property name="shrink">False</property>
                       </packing>
                     </child>
+                    <style>
+                      <class name="geary-sidebar-pane-separator"/>
+                    </style>
                   </object>
                   <packing>
-                    <property name="pack_type">end</property>
                     <property name="expand">True</property>
                     <property name="fill">True</property>
+                    <property name="pack_type">end</property>
+                    <property name="position">0</property>
                   </packing>
                 </child>
+                <style>
+                  <class name="sidebar"/>
+                </style>
               </object>
               <packing>
                 <property name="resize">False</property>
@@ -122,9 +119,64 @@
             </child>
           </object>
           <packing>
-            <property name="pack_type">end</property>
             <property name="expand">True</property>
             <property name="fill">True</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkFrame" id="info_bar_frame">
+            <property name="can_focus">False</property>
+            <property name="no_show_all">True</property>
+            <property name="label_xalign">0</property>
+            <property name="shadow_type">in</property>
+            <child>
+              <object class="GtkGrid" id="info_bar_container">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="orientation">vertical</property>
+                <signal name="remove" handler="on_info_bar_container_remove" swapped="no"/>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+            </child>
+            <child type="label_item">
+              <placeholder/>
+            </child>
+            <style>
+              <class name="geary-info-bar-frame"/>
+            </style>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
           </packing>
         </child>
       </object>


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