[baobab] Rejig 'devices and locations' UI



commit dc5ecdc985c56f6b3cf419790ad4f7a394d6d5a5
Author: George Barrett <bob bob131 so>
Date:   Fri Jul 29 01:50:53 2016 +1000

    Rejig 'devices and locations' UI
    
    This patch addresses some of the UX issues outlined by Allan Day in the
    associated bug report[1] and updates the landing page UI according to
    the provided wireframe.
    
    The UX issues targeted by this patch are as follows:
    * Home is almost always going to be the location the user is interested
      in - why isn't it at the top?
    * Isn't clear which locations are local devices and which are remote.
    * "Devices and locations" should use title case: "Devices & Locations".
    * The size bars are all different lengths. This looks messy, and it's
      not clear whether the length is significant.
    * The list doesn't look very good when it is very wide - everything is
      spread out to the edges.
    
    [1]: https://bugzilla.gnome.org/show_bug.cgi?id=762874

 src/baobab-location-list.ui   |   31 +++++++
 src/baobab-location-list.vala |  178 ++++++++++++++++++++++++++++-------------
 src/baobab-location-row.ui    |   31 +++++---
 src/baobab-location.vala      |   37 ++++-----
 src/baobab-main-window.ui     |   22 ++++-
 src/baobab-window.vala        |   21 ++++--
 src/baobab.gresource.xml      |    1 +
 7 files changed, 226 insertions(+), 95 deletions(-)
---
diff --git a/src/baobab-location-list.ui b/src/baobab-location-list.ui
new file mode 100644
index 0000000..d70eb66
--- /dev/null
+++ b/src/baobab-location-list.ui
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.0 -->
+  <template class="BaobabBaseLocationListWidget" parent="GtkBox">
+    <property name="visible">True</property>
+    <property name="orientation">vertical</property>
+    <property name="spacing">6</property>
+    <child>
+      <object class="GtkLabel" id="label_widget">
+        <property name="visible">True</property>
+        <property name="halign">start</property>
+        <style>
+          <class name="dim-label"/>
+        </style>
+      </object>
+    </child>
+    <child>
+      <object class="GtkFrame">
+        <property name="visible">True</property>
+        <child>
+          <object class="GtkListBox" id="list">
+            <property name="visible">True</property>
+            <style>
+              <class name="view"/>
+            </style>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/baobab-location-list.vala b/src/baobab-location-list.vala
index 672d3f1..1b3acd6 100644
--- a/src/baobab-location-list.vala
+++ b/src/baobab-location-list.vala
@@ -29,7 +29,9 @@ namespace Baobab {
         [GtkChild]
         private Gtk.Label path_label;
         [GtkChild]
-        private Gtk.Label usage_label;
+        private Gtk.Label available_label;
+        [GtkChild]
+        private Gtk.Label total_size_label;
         [GtkChild]
         private Gtk.LevelBar usage_bar;
 
@@ -44,31 +46,51 @@ namespace Baobab {
             name_label.label = "<b>%s</b>".printf (escaped);
 
             escaped = location.file != null ? GLib.Markup.escape_text (location.file.get_parse_name (), -1) 
: "";
-            path_label.label = "<small>%s</small>".printf (escaped);
+            path_label.label = escaped;
+
+            // assume for local mounts the end of the mount path is the
+            // relevant information, and for remote mounts the beginning is
+            // more important
+            path_label.ellipsize = location.is_remote ? Pango.EllipsizeMode.END : Pango.EllipsizeMode.START;
+
+            if (location.is_volume || location.is_main_volume) {
+                if (location.size != null) {
+                    total_size_label.label = "%s Total".printf (format_size (location.size));
+                    total_size_label.show ();
 
-            if (location.is_volume && location.used != null && location.size != null) {
-                usage_label.label = "<small>%s / %s</small>".printf (format_size (location.used), 
format_size (location.size));
-                usage_label.show ();
+                    if (location.used != null) {
+                        available_label.label = "%s Available".printf (format_size (location.size - 
location.used));
 
-                usage_bar.max_value = location.size;
+                        usage_bar.max_value = location.size;
 
-                // Set critical color at 90% of the size
-                usage_bar.add_offset_value (Gtk.LEVEL_BAR_OFFSET_LOW, 0.9 * location.size);
-                usage_bar.value = location.used;
-                usage_bar.show ();
+                        // Set critical color at 90% of the size
+                        usage_bar.add_offset_value (Gtk.LEVEL_BAR_OFFSET_LOW, 0.9 * location.size);
+                        usage_bar.value = location.used;
+                        usage_bar.show ();
+                    } else {
+                        available_label.label = "Unknown";
+                    }
+                }
+
+                if (location.used != null) {
+                    // useful for some remote mounts where we don't know the
+                    // size but do have a usage figure
+                    available_label.label = "%s Used".printf (format_size (location.used));
+                }
+
+                available_label.show ();
             }
         }
     }
 
-    public class LocationList : Gtk.ListBox {
+    public class LocationList : Object {
         private const int MAX_RECENT_LOCATIONS = 5;
 
         private VolumeMonitor monitor;
 
         private List<Location> locations = null;
 
-        public delegate void LocationAction (Location l);
-        private LocationAction? location_action;
+        public signal void update ();
 
         construct {
             monitor = VolumeMonitor.get ();
@@ -79,28 +101,18 @@ namespace Baobab {
             monitor.volume_removed.connect (volume_removed);
             monitor.volume_added.connect (volume_added);
 
-            selection_mode = Gtk.SelectionMode.NONE;
-            set_header_func (update_header);
-
             populate ();
         }
 
-        void update_header (Gtk.ListBoxRow row, Gtk.ListBoxRow? before_row) {
-            if (before_row != null && row.get_header () == null) {
-                row.set_header (new Gtk.Separator (Gtk.Orientation.HORIZONTAL));
-            } else {
-                row.set_header (null);
-            }
+        public void append (Location location) {
+            locations.append(location);
         }
 
-        public override void row_activated (Gtk.ListBoxRow row) {
-            if (location_action != null) {
-                var location_widget = row as LocationRow;
-                location_action (location_widget.location);
-            }
+        public void @foreach (Func<Location> func) {
+            locations.foreach (func);
         }
 
-        bool already_present (File file) {
+        public bool already_present (File file) {
             foreach (var l in locations) {
                 if (l.file != null && l.file.equal (file)) {
                     return true;
@@ -109,10 +121,6 @@ namespace Baobab {
             return false;
         }
 
-        void append_to_volumes (Location location) {
-            locations.insert_before (locations.find (Location.get_home_location ()), location);
-        }
-
         void volume_changed (Volume volume) {
             update ();
         }
@@ -129,7 +137,7 @@ namespace Baobab {
         }
 
         void volume_added (Volume volume) {
-            append_to_volumes (new Location.from_volume (volume));
+            append (new Location.from_volume (volume));
             update ();
         }
 
@@ -151,7 +159,7 @@ namespace Baobab {
             var volume = mount.get_volume ();
             if (volume == null) {
                 if (!already_present (mount.get_root ())) {
-                    append_to_volumes (new Location.from_mount (mount));
+                    append (new Location.from_mount (mount));
                 }
             } else {
                 foreach (var location in locations) {
@@ -166,28 +174,17 @@ namespace Baobab {
         }
 
         void populate () {
-            locations.append (new Location.for_main_volume ());
+            append (new Location.for_home_folder ());
+            append (new Location.for_main_volume ());
 
             foreach (var volume in monitor.get_volumes ()) {
-                var location = new Location.from_volume (volume);
-                if (!location.is_home) {
-                    locations.append (location);
-                }
+                volume_added (volume);
             }
 
             foreach (var mount in monitor.get_mounts ()) {
-                if (mount.get_volume () == null) {
-                    var location = new Location.from_mount (mount);
-                    if (!location.is_home) {
-                        locations.append (location);
-                    }
-                } else {
-                    // Already added as volume
-                }
+                mount_added (mount);
             }
 
-            locations.append (Location.get_home_location ());
-
             Gtk.RecentManager recent_manager = Gtk.RecentManager.get_default ();
             List<Gtk.RecentInfo> recent_items = recent_manager.get_items ();
 
@@ -212,22 +209,79 @@ namespace Baobab {
             recent_items.reverse ();
 
             foreach (var info in recent_items) {
-                locations.append (new Location.for_recent_info (info));
+                append (new Location.for_recent_info (info));
             }
 
             update ();
         }
+    }
+
+    [GtkTemplate (ui = "/org/gnome/baobab/ui/baobab-location-list.ui")]
+    public abstract class BaseLocationListWidget : Gtk.Box {
+        [GtkChild]
+        private Gtk.Label label_widget;
+        [GtkChild]
+        private Gtk.ListBox list;
+
+        public abstract string label { get; }
+
+        private LocationList locations = null;
+        public void set_locations (LocationList locations) {
+            this.locations = locations;
+            locations.update.connect (update);
+        }
+
+        public delegate void LocationAction (Location l);
+        private LocationAction? location_action;
+
+        construct {
+            label_widget.label = _(label);
+            list.selection_mode = Gtk.SelectionMode.NONE;
+            list.set_header_func (update_header);
+            list.row_activated.connect (row_activated);
+        }
+
+        public abstract bool allow_display (Location location);
+
+        public override void show_all () {
+            base.show_all (); // set children to visible
+
+            if (list.get_children ().length () == 0) {
+                visible = false;
+            }
+        }
+
+        public void set_adjustment (Gtk.Adjustment adj) {
+            list.set_adjustment (adj);
+        }
+
+        void update_header (Gtk.ListBoxRow row, Gtk.ListBoxRow? before_row) {
+            if (before_row != null && row.get_header () == null) {
+                row.set_header (new Gtk.Separator (Gtk.Orientation.HORIZONTAL));
+            } else {
+                row.set_header (null);
+            }
+        }
+
+        void row_activated (Gtk.ListBoxRow row) {
+            if (location_action != null) {
+                var location_widget = row as LocationRow;
+                location_action (location_widget.location);
+            }
+        }
 
         public void set_action (owned LocationAction? action) {
             location_action = (owned)action;
         }
 
         public void update () {
-            this.foreach ((widget) => { widget.destroy (); });
+            list.foreach ((widget) => { widget.destroy (); });
 
-            foreach (var location in locations) {
-                add (new LocationRow (location));
-            }
+            locations.foreach((location) => {
+                if (allow_display (location)) {
+                    list.add (new LocationRow (location));
+                }
+            });
 
             show_all ();
         }
@@ -237,7 +291,7 @@ namespace Baobab {
                 return;
             }
 
-            if (!already_present (location.file)) {
+            if (!locations.already_present (location.file)) {
                 locations.append (location);
             }
 
@@ -257,4 +311,18 @@ namespace Baobab {
             Gtk.RecentManager.get_default ().add_full (location.file.get_uri (), data);
         }
     }
+
+    public class LocalLocationList : BaseLocationListWidget {
+        public override string label { get { return "This Computer"; } }
+        public override bool allow_display (Location location) {
+            return !location.is_remote;
+        }
+    }
+
+    public class RemoteLocationList : BaseLocationListWidget {
+        public override string label { get { return "Remote Locations"; } }
+        public override bool allow_display (Location location) {
+            return location.is_remote;
+        }
+    }
 }
diff --git a/src/baobab-location-row.ui b/src/baobab-location-row.ui
index 4f18bf7..ac8ecf0 100644
--- a/src/baobab-location-row.ui
+++ b/src/baobab-location-row.ui
@@ -30,6 +30,7 @@
             <property name="valign">end</property>
             <property name="hexpand">True</property>
             <property name="use_markup">True</property>
+            <property name="ellipsize">end</property>
           </object>
           <packing>
             <property name="left_attach">1</property>
@@ -58,13 +59,12 @@
           </packing>
         </child>
         <child>
-          <object class="GtkLabel" id="usage_label">
+          <object class="GtkLabel" id="available_label">
             <property name="visible">False</property>
             <property name="can_focus">False</property>
             <property name="no_show_all">True</property>
             <property name="halign">end</property>
             <property name="valign">end</property>
-            <property name="use_markup">True</property>
           </object>
           <packing>
             <property name="left_attach">2</property>
@@ -74,13 +74,15 @@
           </packing>
         </child>
         <child>
-          <object class="GtkLevelBar" id="usage_bar">
+          <object class="GtkLabel" id="total_size_label">
             <property name="visible">False</property>
             <property name="can_focus">False</property>
             <property name="no_show_all">True</property>
-            <property name="halign">fill</property>
+            <property name="halign">end</property>
             <property name="valign">start</property>
-            <property name="hexpand">True</property>
+            <style>
+              <class name="dim-label"/>
+            </style>
           </object>
           <packing>
             <property name="left_attach">2</property>
@@ -90,14 +92,21 @@
           </packing>
         </child>
         <child>
-          <object class="GtkImage" id="arrow">
-            <property name="icon_name">go-next-symbolic</property>
+          <object class="GtkLevelBar" id="usage_bar">
+            <property name="visible">False</property>
+            <property name="can_focus">False</property>
+            <property name="no_show_all">True</property>
+            <property name="halign">fill</property>
+            <property name="valign">start</property>
+            <property name="hexpand">True</property>
+            <property name="margin-top">6</property>
+            <property name="margin-bottom">6</property>
           </object>
           <packing>
-            <property name="left_attach">3</property>
-            <property name="top_attach">0</property>
-            <property name="width">1</property>
-            <property name="height">2</property>
+            <property name="left_attach">0</property>
+            <property name="top_attach">2</property>
+            <property name="width">3</property>
+            <property name="height">1</property>
           </packing>
         </child>
       </object>
diff --git a/src/baobab-location.vala b/src/baobab-location.vala
index 0722ddf..12ecbb2 100644
--- a/src/baobab-location.vala
+++ b/src/baobab-location.vala
@@ -41,13 +41,10 @@ namespace Baobab {
         public Volume? volume { get; private set; }
         public Mount? mount { get; private set; }
 
-        public Scanner? scanner { get; private set; }
+        public bool is_main_volume { get; private set; default = false; }
+        public bool is_remote { get; private set; default = false; }
 
-        public bool is_home {
-            get {
-                return home_location == this;
-            }
-        }
+        public Scanner? scanner { get; private set; }
 
         private static const string FS_ATTRIBUTES =
             FileAttribute.FILESYSTEM_SIZE + "," +
@@ -58,8 +55,6 @@ namespace Baobab {
             FileAttribute.STANDARD_ICON + "," +
             FileAttribute.STANDARD_TYPE;
 
-        private static Location? home_location = null;
-
         string get_hostname () throws Error {
             HostnameIface hostname_iface;
             hostname_iface = Bus.get_proxy_sync (BusType.SYSTEM,
@@ -76,11 +71,9 @@ namespace Baobab {
         void make_this_home_location () {
             name = _("Home folder");
             icon = new ThemedIcon ("user-home");
-
-            home_location = this;
         }
 
-        Location.for_home_folder () {
+        public Location.for_home_folder () {
             is_volume = false;
             file = File.new_for_path (GLib.Environment.get_home_dir ());
             get_file_info ();
@@ -91,14 +84,6 @@ namespace Baobab {
             scanner = new Scanner (file, ScanFlags.EXCLUDE_MOUNTS);
         }
 
-        public static Location get_home_location () {
-            if (home_location == null) {
-                home_location = new Location.for_home_folder ();
-            }
-
-            return home_location;
-        }
-
         public Location.from_volume (Volume volume_) {
             volume = volume_;
             volume.changed.connect((vol) => {
@@ -126,6 +111,7 @@ namespace Baobab {
             file = File.new_for_path ("/");
             get_file_info ();
             icon = new ThemedIcon.with_default_fallbacks ("drive-harddisk-system");
+            is_main_volume = true;
 
             get_fs_usage ();
 
@@ -170,6 +156,8 @@ namespace Baobab {
         void update_volume_info () {
             mount = volume.get_mount ();
 
+            is_remote = volume.get_identifier (VolumeIdentifier.CLASS) == "network";
+
             if (mount != null) {
                 fill_from_mount ();
             } else {
@@ -189,6 +177,11 @@ namespace Baobab {
             file = mount.get_root ();
             get_file_info ();
 
+            // a little hacky, but it gets around all those null returns
+            if ("folder-remote" in icon.to_string() && !is_volume) {
+                is_remote = true;
+            }
+
             if (file != null && file.equal (File.new_for_path (Environment.get_home_dir ()))) {
                 make_this_home_location ();
             }
@@ -224,6 +217,12 @@ namespace Baobab {
                 }
             } catch (Error e) {
             }
+            // this can happen sometimes with remote mounts. The result, if
+            // unchecked, is a uint64 underflow and the drive being presented
+            // with 18.4 EB free
+            if (size != null && used != null && used > size) {
+                size = null;
+            }
         }
 
         public async void mount_volume () throws Error {
diff --git a/src/baobab-main-window.ui b/src/baobab-main-window.ui
index 1e0cecd..4fbd809 100644
--- a/src/baobab-main-window.ui
+++ b/src/baobab-main-window.ui
@@ -187,12 +187,26 @@
                     <property name="visible">True</property>
                     <property name="vexpand">True</property>
                     <property name="hexpand">True</property>
+                    <property name="hscrollbar-policy">never</property>
                     <child>
-                      <object class="BaobabLocationList" id="location_list">
+                      <object class="GtkBox">
                         <property name="visible">True</property>
-                        <style>
-                          <class name="view"/>
-                        </style>
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">32</property>
+                        <property name="margin">32</property>
+                        <property name="border-width">12</property>
+                        <property name="halign">center</property>
+                        <property name="width-request">700</property>
+                        <child>
+                          <object class="BaobabLocalLocationList" id="local_location_list">
+                            <property name="visible">True</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="BaobabRemoteLocationList" id="remote_location_list">
+                            <property name="visible">True</property>
+                          </object>
+                        </child>
                       </object>
                     </child>
                   </object>
diff --git a/src/baobab-window.vala b/src/baobab-window.vala
index 1675a2e..e5f20aa 100644
--- a/src/baobab-window.vala
+++ b/src/baobab-window.vala
@@ -50,7 +50,9 @@ namespace Baobab {
         [GtkChild]
         private Gtk.ScrolledWindow location_scrolled_window;
         [GtkChild]
-        private LocationList location_list;
+        private LocalLocationList local_location_list;
+        [GtkChild]
+        private RemoteLocationList remote_location_list;
         [GtkChild]
         private Gtk.TreeView treeview;
         [GtkChild]
@@ -127,9 +129,13 @@ namespace Baobab {
             var action = ui_settings.create_action ("active-chart");
             add_action (action);
 
-            location_list.set_adjustment (location_scrolled_window.get_vadjustment ());
-            location_list.set_action (on_scan_location_activate);
-            location_list.update ();
+            var locations = new LocationList();
+            foreach (BaseLocationListWidget location_list in new BaseLocationListWidget[] 
{local_location_list, remote_location_list}) {
+                location_list.set_locations (locations);
+                location_list.set_adjustment (location_scrolled_window.get_vadjustment ());
+                location_list.set_action (on_scan_location_activate);
+                location_list.update ();
+            }
 
             setup_treeview ();
 
@@ -222,7 +228,10 @@ namespace Baobab {
             active_location = location;
 
             // Update the timestamp for GtkRecentManager
-            location_list.add_location (location);
+            if (location.is_remote)
+                remote_location_list.add_location (location);
+            else
+                local_location_list.add_location (location);
         }
 
         void on_scan_location_activate (Location location) {
@@ -486,7 +495,7 @@ namespace Baobab {
             if (child == home_page) {
                 var action = lookup_action ("reload") as SimpleAction;
                 action.set_enabled (false);
-                header_bar.title = _("Devices and locations");
+                header_bar.title = _("Devices & Locations");
             } else {
                 var action = lookup_action ("reload") as SimpleAction;
                 action.set_enabled (true);
diff --git a/src/baobab.gresource.xml b/src/baobab.gresource.xml
index 8cd82be..7cd35b0 100644
--- a/src/baobab.gresource.xml
+++ b/src/baobab.gresource.xml
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <gresources>
   <gresource prefix="/org/gnome/baobab/ui">
+    <file compressed="true">baobab-location-list.ui</file>
     <file compressed="true">baobab-location-row.ui</file>
     <file compressed="true">baobab-main-window.ui</file>
   </gresource>


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