[gnome-boxes] Use a Gtk.IconView instead of Clutter.Box



commit 01a09cde365cd84bd7ea61bfd7e5ae5b02c160fd
Author: Marc-Andrà Lureau <marcandre lureau gmail com>
Date:   Fri Nov 4 23:35:13 2011 +0100

    Use a Gtk.IconView instead of Clutter.Box

 data/gtk-style.css       |    2 +-
 src/app.vala             |   31 ++++--
 src/collection-view.vala |  242 ++++++++++++++++++++++------------------------
 src/machine.vala         |   47 ++++++---
 src/sidebar.vala         |    7 --
 src/topbar.vala          |   30 ++++--
 src/util.vala            |   24 +++++-
 7 files changed, 210 insertions(+), 173 deletions(-)
---
diff --git a/data/gtk-style.css b/data/gtk-style.css
index 774d813..dd59c06 100644
--- a/data/gtk-style.css
+++ b/data/gtk-style.css
@@ -13,7 +13,7 @@
 }
 
 .view {
-    background-color: @theme_bg_color;
+    background-color: @boxes_bg_color;
 }
 
 .boxes-bg {
diff --git a/src/app.vala b/src/app.vala
index a6d4620..1db5384 100644
--- a/src/app.vala
+++ b/src/app.vala
@@ -44,11 +44,6 @@ private class Boxes.App: Boxes.UI {
         collection = new Collection (this);
         connections = new HashTable<string, GVir.Connection> (str_hash, str_equal);
         collection.item_added.connect ((item) => {
-            item.actor.set_reactive (true);
-            item.actor.button_press_event.connect ((actor, event) => {
-                return item_clicked (item, event);
-            });
-
             view.add_item (item);
         });
 
@@ -271,17 +266,33 @@ private class Boxes.App: Boxes.UI {
             break;
 
         case UIState.COLLECTION:
+            set_main_ui_state ("collection");
+            actor_remove (topbar.actor);
+            actor_remove (sidebar.actor);
+            actor_remove (view.actor);
+            box.pack (topbar.actor, "column", 0, "row", 0,
+                      "x-expand", true, "y-expand", false);
+            box.pack (view.actor, "column", 0, "row", 1,
+                      "x-expand", true, "y-expand", true);
             if (current_item is Machine) {
                 var machine = current_item as Machine;
 
                 machine.disconnect_display ();
                 machine.update_screenshot.begin ();
             }
-            set_main_ui_state ("collection");
             break;
 
         case UIState.PROPERTIES:
         case UIState.WIZARD:
+            actor_remove (topbar.actor);
+            actor_remove (sidebar.actor);
+            actor_remove (view.actor);
+            box.pack (topbar.actor, "column", 0, "row", 0, "column-span", 2,
+                      "x-expand", true, "y-expand", false);
+            box.pack (sidebar.actor, "column", 0, "row", 1,
+                      "x-expand", false, "y-expand", true);
+            box.pack (view.actor, "column", 1, "row", 1,
+                      "x-expand", true, "y-expand", true);
             set_main_ui_state ("collection");
             break;
 
@@ -311,17 +322,15 @@ private class Boxes.App: Boxes.UI {
         return false;
     }
 
-    private bool item_clicked (CollectionItem item, Clutter.ButtonEvent event) {
+    public bool item_selected (CollectionItem item) {
         if (ui_state == UIState.COLLECTION) {
             current_item = item;
 
             if (current_item is Machine) {
                 var machine = current_item as Machine;
 
-                if (event.button == 1) {
-                    machine.connect_display ();
-                    ui_state = UIState.CREDS;
-                }
+                machine.connect_display ();
+                ui_state = UIState.CREDS;
             } else
                 warning ("unknown item, fix your code");
         }
diff --git a/src/collection-view.vala b/src/collection-view.vala
index 1274cb7..bb444ea 100644
--- a/src/collection-view.vala
+++ b/src/collection-view.vala
@@ -2,13 +2,10 @@
 using Clutter;
 
 private class Boxes.CollectionView: Boxes.UI {
-    public override Clutter.Actor actor { get { return margin; } }
+    public override Clutter.Actor actor { get { return gtkactor; } }
 
     private App app;
-    private Clutter.Group margin; // the surrounding actor, for the margin
-    private Clutter.Box boxes; // the boxes list box
-    private SortedCollection sorted;
-    private Clutter.FlowLayout layout;
+    private GtkClutter.Actor gtkactor;
     private Clutter.Box over_boxes; // a box on top of boxes list
 
     private Category _category;
@@ -16,106 +13,88 @@ private class Boxes.CollectionView: Boxes.UI {
         get { return _category; }
         set {
             _category = value;
-            if (sorted != null)
-                update_view ();
+            // FIXME: update view
         }
     }
 
+    private Gtk.IconView icon_view;
+    private enum ModelColumns {
+        SCREENSHOT,
+        TITLE,
+        ITEM
+    }
+    private Gtk.ListStore model;
+
     public CollectionView (App app, Category category) {
         this.app = app;
         this.category = category;
-        sorted = new SortedCollection ((a, b) => {
-            return strcmp (a.name.down (), b.name.down ());
-        });
         setup_view ();
     }
 
-    private void set_main_ui_state () {
-        actor_remove (app.wizard.actor);
-        actor_remove (app.properties.actor);
-        if (app.current_item != null)
-            actor_remove (app.current_item.actor);
-
-        /* follow main table layout again */
-        actor_unpin (boxes);
-        boxes.set_position (15f, 15f);
-        margin.add_constraint_with_name ("boxes-left",
-                                         new Clutter.SnapConstraint (app.stage, SnapEdge.RIGHT, SnapEdge.RIGHT, 0));
-        margin.add_constraint_with_name ("boxes-bottom",
-                                         new Clutter.SnapConstraint (app.stage, SnapEdge.BOTTOM, SnapEdge.RIGHT.BOTTOM, 0));
-        /* normal flow items */
-        boxes.set_layout_manager (layout);
-
-        actor_remove (over_boxes);
-    }
+    public void set_over_boxes (Clutter.Actor actor, bool center = false) {
+        if (center)
+            over_boxes.pack (actor,
+                             "x-align", Clutter.BinAlignment.CENTER,
+                             "y-align", Clutter.BinAlignment.CENTER);
+        else
+            over_boxes.pack (actor);
 
-    public void set_over_boxes () {
-        remove_item (app.current_item);
-        over_boxes.pack (app.current_item.actor,
-                         "x-align", Clutter.BinAlignment.CENTER,
-                         "y-align", Clutter.BinAlignment.CENTER);
         actor_add (over_boxes, app.stage);
     }
 
     public override void ui_state_changed () {
         switch (ui_state) {
-        case UIState.CREDS:
-            set_over_boxes ();
-            app.current_item.ui_state = UIState.CREDS;
-
-            /* item don't move anymore */
-            boxes.set_layout_manager (new Clutter.FixedLayout ());
+        case UIState.COLLECTION:
+            icon_view.unselect_all ();
+            actor_remove (app.wizard.actor);
+            actor_remove (over_boxes);
+            if (app.current_item != null)
+                actor_remove (app.current_item.actor);
+            break;
 
+        case UIState.CREDS:
+            set_over_boxes (app.current_item.actor, true);
             break;
 
         case UIState.DISPLAY: {
             float x, y;
             var display = app.current_item.actor;
 
+            actor_remove (app.properties.actor);
+
             if (previous_ui_state == UIState.CREDS) {
                 /* move display/machine actor to stage, keep same position */
                 display.get_transformed_position (out x, out y);
                 actor_remove (display);
                 actor_add (display, app.stage);
                 display.set_position (x, y);
-
-                /* make sure the boxes stay where they are */
-                boxes.get_transformed_position (out x, out y);
-                boxes.set_position (x, y);
-                actor_pin (boxes);
-                margin.remove_constraint_by_name ("boxes-left");
-                margin.remove_constraint_by_name ("boxes-bottom");
             }
-
-            app.current_item.ui_state = UIState.DISPLAY;
-
             break;
         }
 
-        case UIState.COLLECTION:
-            set_main_ui_state ();
-            if (app.current_item != null)
-                add_item (app.current_item);
+        case UIState.WIZARD:
+            app.wizard.actor.add_constraint (new Clutter.BindConstraint (over_boxes, BindCoordinate.SIZE, 0));
+            set_over_boxes (app.wizard.actor);
             break;
 
-        case UIState.WIZARD:
         case UIState.PROPERTIES:
-            set_main_ui_state ();
-            over_boxes.pack (ui_state == UIState.WIZARD ? app.wizard.actor : app.properties.actor);
-            app.wizard.actor.add_constraint (new Clutter.BindConstraint (over_boxes, BindCoordinate.SIZE, 0));
-            actor_add (over_boxes, app.stage);
-            if (app.current_item != null)
-                app.current_item.ui_state = ui_state;
+            actor_remove (app.current_item.actor);
+            app.properties.actor.add_constraint (new Clutter.BindConstraint (over_boxes, BindCoordinate.SIZE, 0));
+            set_over_boxes (app.properties.actor);
             break;
 
         default:
             break;
         }
+
+        if (app.current_item != null)
+            app.current_item.ui_state = ui_state;
     }
 
     public void update_item_visible (CollectionItem item) {
         var visible = false;
 
+        // FIXME
         if (item is Machine) {
             var machine = item as Machine;
 
@@ -132,101 +111,110 @@ private class Boxes.CollectionView: Boxes.UI {
         item.actor.visible = visible;
     }
 
-    public void update_view () {
-        var iter = sorted.sequence.get_begin_iter ();
+    private Gtk.TreeIter append (Gdk.Pixbuf pixbuf,
+                                 string title,
+                                 CollectionItem item)
+    {
+        Gtk.TreeIter iter;
 
-        while (!iter.is_end ()) {
-            var item = iter.get ();
-            update_item_visible (item);
-            iter = iter.next ();
-        }
+        model.append (out iter);
+        model.set (iter, ModelColumns.SCREENSHOT, pixbuf);
+        model.set (iter, ModelColumns.TITLE, title);
+        model.set (iter, ModelColumns.ITEM, item);
+
+        set_data<Gtk.TreeIter?> ("iter", iter);
+
+        return iter;
     }
 
     public void add_item (CollectionItem item) {
         var machine = item as Machine;
 
         if (machine == null) {
-            warning ("Cannot add item %p in %s".printf (&item, sorted.to_string ()));
+            warning ("Cannot add item %p".printf (&item));
             return;
         }
 
+        var iter = append (machine.pixbuf, machine.name, item);
+        machine.notify["pixbuf"].connect (() => {
+            // apparently iter is stable after insertion/removal/sort
+            model.set (iter, ModelColumns.SCREENSHOT, machine.pixbuf);
+        });
+
         item.ui_state = UIState.COLLECTION;
         actor_remove (item.actor);
 
-        var iter = sorted.insert (item);
-
-        if (iter.is_begin ())
-            boxes.pack_before (item.actor, null);
-        else {
-            var prev = iter.prev ().get ();
-            boxes.pack_after (item.actor, prev.actor);
-        }
-
         update_item_visible (item);
     }
 
     public void remove_item (CollectionItem item) {
-        if (item is Machine) {
-            var machine = item as Machine;
-            var actor = machine.actor;
+        var iter = item.get_data<Gtk.TreeIter?> ("iter");
+        return_if_fail (iter != null);
 
-            sorted.remove (machine);
-            if (actor.get_parent () == boxes)
-                boxes.remove_actor (actor);
-        } else
-            warning ("Cannot remove item %p".printf (&item));
+        model.remove (iter);
+        item.set_data<Gtk.TreeIter?> ("iter", null);
     }
 
     private void setup_view () {
-        layout = new Clutter.FlowLayout (Clutter.FlowOrientation.HORIZONTAL);
-        margin = new Clutter.Group ();
-        margin.set_clip_to_allocation (true);
-        margin.set_reactive (true);
-        /* this helps to keep the app table inside the window, otherwise, it allocated large */
-        margin.set_size (1f, 1f);
-
-        boxes = new Clutter.Box (layout);
-        layout.set_column_spacing (35);
-        layout.set_row_spacing (25);
-        margin.add (boxes);
-        app.box.pack (margin, "column", 1, "row", 1, "x-expand", true, "y-expand", true);
-
-        margin.scroll_event.connect ((event) => {
-            float scrollable_height = boxes.get_height ();
-
-            boxes.get_preferred_height (boxes.get_width (), null, out scrollable_height);
-            var viewport_height = margin.get_height ();
-
-            if (scrollable_height < viewport_height)
-                return true;
-            var y = boxes.get_y ();
-            switch (event.direction) {
-            case ScrollDirection.UP:
-                y += 50f;
-                break;
-            case ScrollDirection.DOWN:
-                y -= 50f;
-                break;
-            default:
-                break;
-            }
-            y = y.clamp (viewport_height - scrollable_height, 0.0f);
-            boxes.animate (AnimationMode.LINEAR, 50, "y", y);
-            return true;
+        model = new Gtk.ListStore (3,
+                                   typeof (Gdk.Pixbuf),
+                                   typeof (string),
+                                   typeof (CollectionItem));
+        model.set_default_sort_func ((model, a, b) => {
+            CollectionItem item_a, item_b;
+
+            model.get (a, ModelColumns.ITEM, out item_a);
+            model.get (b, ModelColumns.ITEM, out item_b);
+
+            if (item_a == null || item_b == null) // FIXME?!
+                return 0;
+
+            return strcmp (item_a.name.down (), item_b.name.down ());
+        });
+        model.set_sort_column_id (Gtk.SortColumn.DEFAULT, Gtk.SortType.ASCENDING);
+
+        icon_view = new Gtk.IconView.with_model (model);
+        icon_view.item_width = 185;
+        icon_view.column_spacing = 20;
+        icon_view.margin = 16;
+        icon_view_activate_on_single_click (icon_view, true);
+        icon_view.set_selection_mode (Gtk.SelectionMode.SINGLE);
+        icon_view.item_activated.connect ((view, path) => {
+            Gtk.TreeIter iter;
+            GLib.Value value;
+
+            model.get_iter (out iter, path);
+            model.get_value (iter, ModelColumns.ITEM, out value);
+            app.item_selected ((CollectionItem) value);
         });
 
-        boxes.add_constraint_with_name ("boxes-width",
-                                        new Clutter.BindConstraint (margin, BindCoordinate.WIDTH, -25f));
+        var pixbuf_renderer = new Gtk.CellRendererPixbuf ();
+        pixbuf_renderer.xalign = 0.5f;
+        pixbuf_renderer.yalign = 0.5f;
+        icon_view.pack_start (pixbuf_renderer, false);
+        icon_view.add_attribute (pixbuf_renderer, "pixbuf", ModelColumns.SCREENSHOT);
+
+        var text_renderer = new Gtk.CellRendererText ();
+        text_renderer.xalign = 0.5f;
+        text_renderer.foreground = "white";
+        icon_view.pack_start (text_renderer, false);
+        icon_view.add_attribute(text_renderer, "text", ModelColumns.TITLE);
+
+        var scrolled_window = new Gtk.ScrolledWindow (null, null);
+        scrolled_window.add (icon_view);
+        scrolled_window.show_all ();
+
+        gtkactor = new GtkClutter.Actor.with_contents (scrolled_window);
 
         over_boxes = new Clutter.Box (new Clutter.BinLayout (Clutter.BinAlignment.FILL, Clutter.BinAlignment.FILL));
         over_boxes.add_constraint_with_name ("top-box-size",
-                                             new Clutter.BindConstraint (margin, BindCoordinate.SIZE, 0));
+                                             new Clutter.BindConstraint (gtkactor, BindCoordinate.SIZE, 0));
         over_boxes.add_constraint_with_name ("top-box-position",
-                                             new Clutter.BindConstraint (margin, BindCoordinate.POSITION, 0));
+                                             new Clutter.BindConstraint (gtkactor, BindCoordinate.POSITION, 0));
 
-        app.state.set_key (null, "creds", boxes, "opacity", AnimationMode.EASE_OUT_QUAD, (uint) 0, 0, 0);
-        app.state.set_key (null, "display", boxes, "opacity", AnimationMode.EASE_OUT_QUAD, (uint) 0, 0, 0);
-        app.state.set_key (null, "collection", boxes, "opacity", AnimationMode.EASE_OUT_QUAD, (uint) 255, 0, 0);
+        app.state.set_key (null, "creds", actor, "opacity", AnimationMode.EASE_OUT_QUAD, (uint) 0, 0, 0);
+        app.state.set_key (null, "display", actor, "opacity", AnimationMode.EASE_OUT_QUAD, (uint) 0, 0, 0);
+        app.state.set_key (null, "collection", actor, "opacity", AnimationMode.EASE_OUT_QUAD, (uint) 255, 0, 0);
         app.state.set_key (null, "display", over_boxes, "x", AnimationMode.EASE_OUT_QUAD, (float) 0, 0, 0);
         app.state.set_key (null, "display", over_boxes, "y", AnimationMode.EASE_OUT_QUAD, (float) 0, 0, 0);
     }
diff --git a/src/machine.vala b/src/machine.vala
index 11759b4..5cf3ac9 100644
--- a/src/machine.vala
+++ b/src/machine.vala
@@ -9,6 +9,7 @@ private abstract class Boxes.Machine: Boxes.CollectionItem, Boxes.IProperties {
     public MachineActor machine_actor;
     public Boxes.CollectionSource source;
     public Boxes.DisplayConfig config;
+    public Gdk.Pixbuf? pixbuf { get; set; }
 
     private ulong show_id;
     private ulong hide_id;
@@ -16,6 +17,8 @@ private abstract class Boxes.Machine: Boxes.CollectionItem, Boxes.IProperties {
     private ulong need_password_id;
     private ulong need_username_id;
     private uint screenshot_id;
+    public static const int SCREENSHOT_WIDTH = 180;
+    public static const int SCREENSHOT_HEIGHT = 134;
 
     private Display? _display;
     public Display? display {
@@ -81,6 +84,7 @@ private abstract class Boxes.Machine: Boxes.CollectionItem, Boxes.IProperties {
         this.name = name;
         this.source = source;
 
+        pixbuf = draw_fallback_vm ();
         machine_actor = new MachineActor (this);
 
         app.notify["ui-state"].connect (() => {
@@ -126,26 +130,20 @@ private abstract class Boxes.Machine: Boxes.CollectionItem, Boxes.IProperties {
     public abstract void connect_display ();
     public abstract void disconnect_display ();
 
-    public async void update_screenshot (int width = 128, int height = 96) {
-        Gdk.Pixbuf? pixbuf = null;
-
+    public async void update_screenshot (int width = SCREENSHOT_WIDTH, int height = SCREENSHOT_HEIGHT) {
         try {
             yield take_screenshot ();
             pixbuf = new Gdk.Pixbuf.from_file (get_screenshot_filename ());
+            machine_actor.set_screenshot (pixbuf); // high resolution
+            pixbuf = draw_vm (pixbuf, width, height);
         } catch (GLib.Error error) {
             if (!(error is FileError.NOENT))
                 warning ("%s: %s".printf (name, error.message));
         }
 
-        if (pixbuf == null)
+        if (pixbuf == null) {
             pixbuf = draw_fallback_vm (width, height);
-        else
-            pixbuf = draw_vm (pixbuf, pixbuf.get_width (), pixbuf.get_height ());
-
-        try {
             machine_actor.set_screenshot (pixbuf);
-        } catch (GLib.Error err) {
-            warning (err.message);
         }
     }
 
@@ -179,7 +177,7 @@ private abstract class Boxes.Machine: Boxes.CollectionItem, Boxes.IProperties {
             context.paint ();
 
             context.identity_matrix ();
-            context.scale (0.1875 / 128 * width, 0.1875 / 96 * height);
+            context.scale (0.1875 / SCREENSHOT_WIDTH * width, 0.1875 / SCREENSHOT_HEIGHT * height);
             var grid = new Cairo.Pattern.for_surface (new Cairo.ImageSurface.from_png (get_pixmap ("boxes-grid.png")));
             grid.set_extend (Cairo.Extend.REPEAT);
             context.set_source_rgba (0, 0, 0, 1);
@@ -190,9 +188,18 @@ private abstract class Boxes.Machine: Boxes.CollectionItem, Boxes.IProperties {
         return Gdk.pixbuf_get_from_surface (surface, 0, 0, width, height);
     }
 
-    private static Gdk.Pixbuf draw_fallback_vm (int width, int height) {
+    private static Gdk.Pixbuf? default_fallback = null;
+    private static Gdk.Pixbuf draw_fallback_vm (int width = SCREENSHOT_WIDTH,
+                                                int height = SCREENSHOT_HEIGHT,
+                                                bool force = false) {
         Gdk.Pixbuf pixbuf = null;
 
+        if (width == SCREENSHOT_WIDTH && height == SCREENSHOT_HEIGHT && !force)
+            if (default_fallback != null)
+                return default_fallback;
+            else
+                default_fallback = draw_fallback_vm (width, height, true);
+
         try {
             var surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, width, height);
             var context = new Cairo.Context (surface);
@@ -242,7 +249,7 @@ private class Boxes.MachineActor: Boxes.UI {
 
         screenshot = new GtkClutter.Texture ();
         screenshot.name = "screenshot";
-
+        set_screenshot (machine.pixbuf);
         scale_screenshot ();
         actor_add (screenshot, box);
         screenshot.keep_aspect_ratio = true;
@@ -277,14 +284,20 @@ private class Boxes.MachineActor: Boxes.UI {
         password_entry.hide ();
 
         actor_add (gtk_vbox, box);
+        actor.set_reactive (true);
     }
 
     public void scale_screenshot (float scale = 1.5f) {
-        screenshot.set_size (128 * scale, 96 * scale);
+        screenshot.set_size (Machine.SCREENSHOT_WIDTH * scale,
+                             Machine.SCREENSHOT_HEIGHT * scale);
     }
 
-    public void set_screenshot (Gdk.Pixbuf pixbuf) throws GLib.Error {
-        screenshot.set_from_pixbuf (pixbuf);
+    public void set_screenshot (Gdk.Pixbuf pixbuf) {
+        try {
+            screenshot.set_from_pixbuf (pixbuf);
+        } catch (GLib.Error err) {
+            warning (err.message);
+        }
     }
 
     public void set_password_needed (bool needed) {
@@ -325,6 +338,7 @@ private class Boxes.MachineActor: Boxes.UI {
                                "y", 0.0f);
             } else {
                 if (display != null) {
+                    // zoom in, back from properties
                     var anim = display.animate (Clutter.AnimationMode.LINEAR, Boxes.App.duration,
                                                 "x", 0.0f,
                                                 "y", 0.0f,
@@ -369,7 +383,6 @@ private class Boxes.MachineActor: Boxes.UI {
             break;
 
         default:
-            message ("Unhandled UI state " + ui_state.to_string ());
             break;
         }
     }
diff --git a/src/sidebar.vala b/src/sidebar.vala
index 0c058de..9ad9d13 100644
--- a/src/sidebar.vala
+++ b/src/sidebar.vala
@@ -44,8 +44,6 @@ private class Boxes.Sidebar: Boxes.UI {
     public override void ui_state_changed () {
         switch (ui_state) {
         case UIState.COLLECTION:
-            actor_remove (gtk_actor);
-            app.box.pack (gtk_actor, "column", 0, "row", 0, "row-span", 2, "x-expand", false, "y-expand", true);
             notebook.page = SidebarPage.COLLECTION;
             break;
 
@@ -55,8 +53,6 @@ private class Boxes.Sidebar: Boxes.UI {
 
         case UIState.WIZARD:
         case UIState.PROPERTIES:
-            actor_remove (gtk_actor);
-            app.box.pack (gtk_actor, "column", 0, "row", 1, "row-span", 1, "x-expand", false, "y-expand", true);
             notebook.page = ui_state == UIState.WIZARD ? SidebarPage.WIZARD : SidebarPage.PROPERTIES;
             break;
         }
@@ -111,9 +107,6 @@ private class Boxes.Sidebar: Boxes.UI {
         tree_view.headers_visible = false;
 
         var pixbuf_renderer = new CellRendererPixbuf ();
-        // pixbuf_renderer.width = 20;
-        // pixbuf_renderer.mode = CellRendererMode.INERT;
-        // pixbuf_renderer.xalign = 1f;
         pixbuf_renderer.xpad = 5;
         tree_view.insert_column_with_attributes (-1, "", pixbuf_renderer, "icon-name", 3);
         var renderer = new CellRendererText ();
diff --git a/src/topbar.vala b/src/topbar.vala
index be6b809..f1beb09 100644
--- a/src/topbar.vala
+++ b/src/topbar.vala
@@ -24,6 +24,7 @@ private class Boxes.Topbar: Boxes.UI {
     private Gtk.ToolButton cancel_btn;
     private Gtk.ToolButton spinner_btn;
     private Gtk.ToolButton back_btn;
+    private Gtk.Button new_btn;
 
     public Topbar (App app) {
         this.app = app;
@@ -48,18 +49,28 @@ private class Boxes.Topbar: Boxes.UI {
         hbox.pack_start (toolbar_start, true, true, 0);
 
         back_btn = new Gtk.ToolButton (null, null);
+        back_btn.valign = Gtk.Align.CENTER;
         back_btn.icon_name =  "go-previous-symbolic";
         back_btn.get_style_context ().add_class ("raised");
         back_btn.clicked.connect ((button) => { app.ui_state = UIState.COLLECTION; });
         toolbar_start.insert (back_btn, 0);
 
-        label = new Gtk.Label (_("New and Recent"));
+        new_btn = new Gtk.Button.with_label (_("New"));
+        new_btn.set_size_request (70, -1);
+        new_btn.get_style_context ().add_class ("raised");
+        var tool_item = new Gtk.ToolItem ();
+        tool_item.child = new_btn;
+        tool_item.valign = Gtk.Align.CENTER;
+        new_btn.clicked.connect ((button) => { app.ui_state = UIState.WIZARD; });
+        toolbar_start.insert (tool_item, 1);
+
+        label = new Gtk.Label ("");
         label.name = "TopbarLabel";
         label.set_halign (Gtk.Align.START);
-        var tool_item = new Gtk.ToolItem ();
+        tool_item = new Gtk.ToolItem ();
         tool_item.set_expand (true);
         tool_item.child = label;
-        toolbar_start.insert (tool_item, 1);
+        toolbar_start.insert (tool_item, 2);
 
         var toolbar_end = new Gtk.Toolbar ();
         toolbar_end.icon_size = Gtk.IconSize.MENU;
@@ -133,15 +144,15 @@ private class Boxes.Topbar: Boxes.UI {
     public override void ui_state_changed () {
         switch (ui_state) {
         case UIState.COLLECTION:
-            back_btn.hide ();
-            actor_remove (gtk_actor);
-            app.box.pack (gtk_actor, "row", 0, "column", 1, "x-expand", true, "y-expand", false);
             notebook.page = TopbarPage.COLLECTION;
+            back_btn.hide ();
             spinner_btn.hide ();
             select_btn.show ();
+            new_btn.show ();
             break;
 
         case UIState.CREDS:
+            new_btn.hide ();
             back_btn.show ();
             spinner_btn.show ();
             select_btn.hide ();
@@ -152,10 +163,11 @@ private class Boxes.Topbar: Boxes.UI {
             break;
 
         case UIState.PROPERTIES:
+            notebook.page = TopbarPage.PROPERTIES;
+            break;
+
         case UIState.WIZARD:
-            actor_remove (gtk_actor);
-            app.box.pack (gtk_actor, "row", 0, "column", 0, "column-span", 2, "x-expand", true, "y-expand", false);
-            notebook.page = ui_state == UIState.WIZARD ? TopbarPage.WIZARD : TopbarPage.PROPERTIES;
+            notebook.page = TopbarPage.WIZARD;
             break;
 
         default:
diff --git a/src/util.vala b/src/util.vala
index 1af2193..2e4a41a 100644
--- a/src/util.vala
+++ b/src/util.vala
@@ -116,7 +116,7 @@ namespace Boxes {
         if (id != 0 && should_activate == false) {
             tree_view.disconnect (id);
             tree_view.set_data<ulong> ("boxes-tree-view-activate", 0);
-        } else {
+        } else if (id == 0 && should_activate) {
             id = tree_view.button_press_event.connect ((w, event) => {
                 Gtk.TreePath? path;
                 unowned Gtk.TreeViewColumn? column;
@@ -134,6 +134,28 @@ namespace Boxes {
         }
     }
 
+    public void icon_view_activate_on_single_click (Gtk.IconView icon_view, bool should_activate) {
+        var id = icon_view.get_data<ulong> ("boxes-icon-view-activate");
+
+        if (id != 0 && should_activate == false) {
+            icon_view.disconnect (id);
+            icon_view.set_data<ulong> ("boxes-icon-view-activate", 0);
+        } else if (id == 0 && should_activate) {
+            id = icon_view.button_press_event.connect ((w, event) => {
+                Gtk.TreePath? path;
+
+                if (event.button == 1 && event.type == Gdk.EventType.BUTTON_PRESS) {
+                    path = icon_view.get_path_at_pos ((int) event.x, (int) event.y);
+                    if (path != null)
+                        icon_view.item_activated (path);
+                }
+
+                return false;
+            });
+            icon_view.set_data<ulong> ("boxes-icon-view-activate", id);
+        }
+    }
+
     public async void output_stream_write (OutputStream stream, uint8[] buffer) throws GLib.IOError {
         var length = buffer.length;
         ssize_t i = 0;



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