[california/wip/725763-google] Deck-ify entire activation process.



commit 24b9a36cb72d2a833a0cfdeb8742f4692ecd1014
Author: Jim Nelson <jim yorba org>
Date:   Thu Apr 3 16:17:37 2014 -0700

    Deck-ify entire activation process.

 src/activator/activator-instance-list.vala         |   64 ++++++++++-----
 src/activator/activator-instance.vala              |   11 ++-
 src/activator/activator-window.vala                |   21 +++---
 src/activator/activator.vala                       |    8 +--
 .../activator-google-authenticating-pane.vala      |    6 +-
 .../activator-google-calendar-list-pane.vala       |    4 +-
 .../google/activator-google-login-pane.vala        |    4 +-
 src/activator/google/activator-google.vala         |    8 +-
 src/activator/webcal/activator-webcal-pane.vala    |   15 +++-
 src/activator/webcal/activator-webcal.vala         |    9 ++-
 src/rc/activator-list.ui                           |    3 +-
 src/rc/webcal-subscribe.ui                         |    1 +
 src/util/util-deck.vala                            |   85 +++++++++++++++++---
 src/util/util-listbox-model.vala                   |   34 ++++++++
 14 files changed, 207 insertions(+), 66 deletions(-)
---
diff --git a/src/activator/activator-instance-list.vala b/src/activator/activator-instance-list.vala
index 2c94778..f7c3bcb 100644
--- a/src/activator/activator-instance-list.vala
+++ b/src/activator/activator-instance-list.vala
@@ -7,51 +7,75 @@
 namespace California.Activator {
 
 [GtkTemplate (ui = "/org/yorba/california/rc/activator-list.ui")]
-public class InstanceList : Gtk.Grid, Host.Interaction {
-    private class Item : Gtk.Label {
-        public Activator.Instance activator;
-        
-        public Item(Activator.Instance activator) {
-            this.activator = activator;
-            
-            label = activator.title;
-            xalign = 0.0f;
-            margin = 4;
-        }
-    }
+public class InstanceList : Gtk.Grid, Card {
+    public const string ID = "ActivatorInstanceList";
+    
+    public string card_id { get { return ID; } }
+    
+    public string? title { get { return null; } }
     
     public Gtk.Widget? default_widget { get { return add_button; } }
     
+    public Gtk.Widget? initial_focus { get { return listbox; } }
+    
     [GtkChild]
     private Gtk.ListBox listbox;
     
     [GtkChild]
     private Gtk.Button add_button;
     
-    public signal void selected(Activator.Instance activator);
+    private ListBoxModel<Instance> model;
     
     public InstanceList() {
-        foreach (Activator.Instance activator in activators)
-            listbox.add(new Item(activator));
+        model = new ListBoxModel<Instance>(listbox, model_presentation, activator_comparator);
+        model.add_many(activators);
+        
+        model.activated.connect(on_item_activated);
+        model.bind_property(ListBoxModel.PROP_SELECTED, add_button, "sensitive", BindingFlags.SYNC_CREATE,
+            selected_to_sensitive);
         
         show_all();
     }
     
-    [GtkCallback]
-    private void on_listbox_row_activated(Gtk.ListBoxRow? row) {
-        if (row != null)
-            selected(((Item) row.get_child()).activator);
+    private bool selected_to_sensitive(Binding binding, Value source_value, ref Value target_value) {
+        target_value = (model.selected != null);
+        
+        return true;
+    }
+    
+    public void jumped_to(Card? from, Value? message) {
+    }
+    
+    private void on_item_activated(Instance activator) {
+        start(activator);
     }
     
     [GtkCallback]
     private void on_add_button_clicked() {
-        on_listbox_row_activated(listbox.get_selected_row());
+        if (model.selected != null)
+            start(model.selected);
     }
     
     [GtkCallback]
     private void on_cancel_button_clicked() {
         dismissed(true);
     }
+    
+    private void start(Instance activator) {
+        jump_to_card_by_name(activator.first_card_id, null);
+    }
+    
+    private Gtk.Widget model_presentation(Instance activator) {
+        Gtk.Label label = new Gtk.Label(activator.title);
+        label.xalign = 0.0f;
+        label.margin = 4;
+        
+        return label;
+    }
+    
+    private int activator_comparator(Instance a, Instance b) {
+        return String.stricmp(a.title, b.title);
+    }
 }
 
 }
diff --git a/src/activator/activator-instance.vala b/src/activator/activator-instance.vala
index 01d84e0..ae9393b 100644
--- a/src/activator/activator-instance.vala
+++ b/src/activator/activator-instance.vala
@@ -33,16 +33,23 @@ public abstract class Instance : BaseObject {
      */
     public Backing.Store store { get; private set; }
     
+    /**
+     * The { link Card.card_id} of the first Card returns by { link create_cards}.
+     */
+    public abstract string first_card_id { get; }
+    
     protected Instance(string title, Backing.Store store) {
         this.title = title;
         this.store = store;
     }
     
     /**
-     * Return a { link Host.Interaction} that guides the user through the steps to create a
+     * Return a collection of { link Cards} that guides the user through the steps to create a
      * { link Backing.Source}.
+     *
+     * The first Card will be jumped to initially.
      */
-    public abstract Host.Interaction create_interaction(Soup.URI? supplied_uri);
+    public abstract Gee.List<Card> create_cards(Soup.URI? supplied_uri);
     
     public override string to_string() {
         return title;
diff --git a/src/activator/activator-window.vala b/src/activator/activator-window.vala
index 7385ae6..ed14006 100644
--- a/src/activator/activator-window.vala
+++ b/src/activator/activator-window.vala
@@ -13,22 +13,21 @@ namespace California.Activator {
 public class Window : Host.ModalWindow {
     private static Activator.Window? instance = null;
     
+    private Deck deck = new Deck();
+    
     private Window(Gtk.Window? parent) {
         base (parent);
         
-        InstanceList list = new InstanceList();
+        // The Deck is pre-populated with each of their Cards, with the InstanceList jumping to
+        // the right set when asked to (and acting as home)
+        Gee.List<Card> cards = new Gee.ArrayList<Card>();
+        cards.add(new InstanceList());
+        foreach (Instance activator in activators)
+            cards.add_all(activator.create_cards(null));
         
-        // when an Activator instance is selected from the list, swap out the list for the
-        // Activator's own interaction
-        list.selected.connect(activator => {
-            content_area.remove(list);
-            
-            Host.Interaction interaction = activator.create_interaction(null);
-            interaction.show_all();
-            content_area.add(interaction);
-        });
+        deck.add_cards(cards);
         
-        content_area.add(list);
+        content_area.add(deck);
     }
     
     public static void display(Gtk.Window? parent) {
diff --git a/src/activator/activator.vala b/src/activator/activator.vala
index a709cbe..27bddf7 100644
--- a/src/activator/activator.vala
+++ b/src/activator/activator.vala
@@ -18,7 +18,7 @@ namespace California.Activator {
 
 private int init_count = 0;
 
-private Gee.TreeSet<Instance> activators;
+private Gee.List<Instance> activators;
 
 public void init() throws Error {
     if (!Unit.do_init(ref init_count))
@@ -26,7 +26,7 @@ public void init() throws Error {
     
     Backing.init();
     
-    activators = new Gee.TreeSet<Instance>(activator_comparator);
+    activators = new Gee.ArrayList<Instance>();
     
     // All Instances that work with EDS
     Backing.EdsStore? eds_store = Backing.Manager.instance.get_store_of_type<Backing.EdsStore>()
@@ -45,9 +45,5 @@ public void terminate() {
     Backing.terminate();
 }
 
-private int activator_comparator(Instance a, Instance b) {
-    return String.stricmp(a.title, b.title);
-}
-
 }
 
diff --git a/src/activator/google/activator-google-authenticating-pane.vala 
b/src/activator/google/activator-google-authenticating-pane.vala
index caa9afd..e2738b4 100644
--- a/src/activator/google/activator-google-authenticating-pane.vala
+++ b/src/activator/google/activator-google-authenticating-pane.vala
@@ -51,7 +51,7 @@ public class GoogleAuthenticatingPane : Gtk.Grid, Card {
     public GoogleAuthenticatingPane() {
     }
     
-    public void jumped_to(Card from, Value? message) {
+    public void jumped_to(Card? from, Value? message) {
         Message? credentials = message as Message;
         assert(credentials != null);
         
@@ -66,7 +66,7 @@ public class GoogleAuthenticatingPane : Gtk.Grid, Card {
     private void on_cancel_button_clicked() {
         // spinner's active property doubles as flag if async operation is in progress
         if (!spinner.active) {
-            dismissed(true);
+            jump_home();
             
             return;
         }
@@ -146,7 +146,7 @@ public class GoogleAuthenticatingPane : Gtk.Grid, Card {
     
     private void login_cancelled() {
         spinner.active = false;
-        dismissed(true);
+        jump_home();
     }
 }
 
diff --git a/src/activator/google/activator-google-calendar-list-pane.vala 
b/src/activator/google/activator-google-calendar-list-pane.vala
index eceffd0..343bb6e 100644
--- a/src/activator/google/activator-google-calendar-list-pane.vala
+++ b/src/activator/google/activator-google-calendar-list-pane.vala
@@ -64,7 +64,7 @@ public class GoogleCalendarListPane : Gtk.Grid, Card {
         return label;
     }
     
-    public void jumped_to(Card from, Value? message) {
+    public void jumped_to(Card? from, Value? message) {
         Message? feeds = message as Message;
         assert(feeds != null);
         
@@ -92,7 +92,7 @@ public class GoogleCalendarListPane : Gtk.Grid, Card {
     
     [GtkCallback]
     private void on_cancel_button_clicked() {
-        dismissed(true);
+        jump_home();
     }
     
     [GtkCallback]
diff --git a/src/activator/google/activator-google-login-pane.vala 
b/src/activator/google/activator-google-login-pane.vala
index 877e8f2..438af3e 100644
--- a/src/activator/google/activator-google-login-pane.vala
+++ b/src/activator/google/activator-google-login-pane.vala
@@ -34,7 +34,7 @@ internal class GoogleLoginPane : Gtk.Grid, Card {
             BindingFlags.SYNC_CREATE, on_entry_changed);
     }
     
-    public void jumped_to(Card from, Value? msg) {
+    public void jumped_to(Card? from, Value? msg) {
         password_entry.text = "";
     }
     
@@ -46,7 +46,7 @@ internal class GoogleLoginPane : Gtk.Grid, Card {
     
     [GtkCallback]
     private void on_cancel_button_clicked() {
-        dismissed(true);
+        jump_home();
     }
     
     [GtkCallback]
diff --git a/src/activator/google/activator-google.vala b/src/activator/google/activator-google.vala
index 503fa53..5c8a3ba 100644
--- a/src/activator/google/activator-google.vala
+++ b/src/activator/google/activator-google.vala
@@ -7,6 +7,8 @@
 namespace California.Activator {
 
 internal class GoogleActivator : Instance {
+    public override string first_card_id { get { return GoogleLoginPane.ID; } }
+    
     private Backing.WebCalSubscribable webcal_store;
     
     public GoogleActivator(string title, Backing.WebCalSubscribable store) {
@@ -15,15 +17,13 @@ internal class GoogleActivator : Instance {
         webcal_store = store;
     }
     
-    public override Host.Interaction create_interaction(Soup.URI? supplied_uri) {
-        Deck deck = new Deck();
+    public override Gee.List<Card> create_cards(Soup.URI? supplied_uri) {
         Gee.List<Card> cards = new Gee.ArrayList<Card>();
         cards.add(new GoogleLoginPane());
         cards.add(new GoogleAuthenticatingPane());
         cards.add(new GoogleCalendarListPane(webcal_store));
-        deck.add_cards(cards);
         
-        return deck;
+        return cards;
     }
 }
 
diff --git a/src/activator/webcal/activator-webcal-pane.vala b/src/activator/webcal/activator-webcal-pane.vala
index 500794c..b2ebc65 100644
--- a/src/activator/webcal/activator-webcal-pane.vala
+++ b/src/activator/webcal/activator-webcal-pane.vala
@@ -7,9 +7,17 @@
 namespace California.Activator {
 
 [GtkTemplate (ui = "/org/yorba/california/rc/webcal-subscribe.ui")]
-internal class WebCalActivatorPane : Gtk.Grid, Host.Interaction {
+internal class WebCalActivatorPane : Gtk.Grid, Card {
+    public const string ID = "WebCalActivatorPane";
+    
+    public string card_id { get { return ID; } }
+    
+    public string? title { get { return null; } }
+    
     public Gtk.Widget? default_widget { get { return subscribe_button; } }
     
+    public Gtk.Widget? initial_focus { get { return name_entry; } }
+    
     [GtkChild]
     private Gtk.ColorButton color_button;
     
@@ -38,6 +46,9 @@ internal class WebCalActivatorPane : Gtk.Grid, Host.Interaction {
             BindingFlags.SYNC_CREATE, on_entry_changed);
     }
     
+    public void jumped_to(Card? from, Value? message) {
+    }
+    
     private bool on_entry_changed(Binding binding, Value source_value, ref Value target_value) {
         target_value =
             name_entry.text_length > 0 
@@ -49,7 +60,7 @@ internal class WebCalActivatorPane : Gtk.Grid, Host.Interaction {
     
     [GtkCallback]
     private void on_cancel_button_clicked() {
-        dismissed(true);
+        jump_home();
     }
     
     [GtkCallback]
diff --git a/src/activator/webcal/activator-webcal.vala b/src/activator/webcal/activator-webcal.vala
index c232adb..9d91dda 100644
--- a/src/activator/webcal/activator-webcal.vala
+++ b/src/activator/webcal/activator-webcal.vala
@@ -7,6 +7,8 @@
 namespace California.Activator {
 
 internal class WebCalActivator : Instance {
+    public override string first_card_id { get { return WebCalActivatorPane.ID; } }
+    
     private Backing.WebCalSubscribable webcal_store;
     
     public WebCalActivator(string title, Backing.WebCalSubscribable store) {
@@ -15,8 +17,11 @@ internal class WebCalActivator : Instance {
         webcal_store = store;
     }
     
-    public override Host.Interaction create_interaction(Soup.URI? supplied_uri) {
-        return new WebCalActivatorPane(webcal_store, supplied_uri);
+    public override Gee.List<Card> create_cards(Soup.URI? supplied_uri) {
+        Gee.List<Card> cards = new Gee.ArrayList<Card>();
+        cards.add(new WebCalActivatorPane(webcal_store, supplied_uri));
+        
+        return cards;
     }
 }
 
diff --git a/src/rc/activator-list.ui b/src/rc/activator-list.ui
index da0d3bc..37d5cba 100644
--- a/src/rc/activator-list.ui
+++ b/src/rc/activator-list.ui
@@ -21,7 +21,6 @@
             <property name="hexpand">True</property>
             <property name="vexpand">True</property>
             <property name="activate_on_single_click">False</property>
-            <signal name="row-activated" handler="on_listbox_row_activated" 
object="CaliforniaActivatorInstanceList" swapped="no"/>
           </object>
         </child>
       </object>
@@ -43,7 +42,7 @@
         <property name="layout_style">end</property>
         <child>
           <object class="GtkButton" id="cancel_button">
-            <property name="label" translatable="yes">_Cancel</property>
+            <property name="label" translatable="yes">_Close</property>
             <property name="visible">True</property>
             <property name="can_focus">True</property>
             <property name="receives_default">True</property>
diff --git a/src/rc/webcal-subscribe.ui b/src/rc/webcal-subscribe.ui
index a20ddd1..df8749a 100644
--- a/src/rc/webcal-subscribe.ui
+++ b/src/rc/webcal-subscribe.ui
@@ -117,6 +117,7 @@
         <property name="can_focus">False</property>
         <property name="valign">end</property>
         <property name="margin_top">8</property>
+        <property name="vexpand">True</property>
         <property name="spacing">8</property>
         <property name="homogeneous">True</property>
         <property name="baseline_position">bottom</property>
diff --git a/src/util/util-deck.vala b/src/util/util-deck.vala
index 89e9610..c564152 100644
--- a/src/util/util-deck.vala
+++ b/src/util/util-deck.vala
@@ -46,6 +46,11 @@ public interface Card : Gtk.Widget {
     public abstract Gtk.Widget? initial_focus { get; }
     
     /**
+     * Returns the { link Deck} this { link Card} is registered to, if any.
+     */
+    public Deck? deck { get { return parent as Deck; } }
+    
+    /**
      * Fired when the { link Card} wishes to jump to another Card in the same { link Deck.}
      *
      * Each Card can accept a message which parameterizes its activation.  It's up to Cards
@@ -108,7 +113,7 @@ public interface Card : Gtk.Widget {
      * This is called before dealing with { link default_widget} and { link initial_focus}, so
      * changes to those properties in this call, if need be.
      */
-    public abstract void jumped_to(Card from, Value? message);
+    public abstract void jumped_to(Card? from, Value? message);
 }
 
 /**
@@ -124,10 +129,27 @@ public class Deck : Gtk.Stack, Host.Interaction {
      */
     public Gtk.Widget? default_widget { get { return null; } }
     
-    public int size { get { return names.size; } }
+    /**
+     * The number of { link Card}s registered to the { link Deck}.
+     */
+    public int size { get { return list.size; } }
     
-    private Card? top = null;
-    private Card? home = null;
+    /**
+     * All registered { link Card}s returned as a read-only List.
+     */
+    public Gee.List<Card> cards { owned get { return list.read_only_view; } }
+    
+    /**
+     * The home { link Card}.
+     */
+    public Card? home { owned get { return (list.size > 0) ? list[0] : null; } }
+    
+    /**
+     * The current displayed { link Card}.
+     */
+    public Card? top { get; private set; default = null; }
+    
+    private Gee.List<Card> list = new Gee.LinkedList<Card>();
     private Gee.Deque<Card> navigation_stack = new Gee.LinkedList<Card>();
     private Gee.HashMap<string, Card> names = new Gee.HashMap<string, Card>();
     
@@ -207,19 +229,59 @@ public class Deck : Gtk.Stack, Host.Interaction {
             // i.e. home)
             card.map.connect(on_card_mapped);
             
-            // first Card seen is home card
-            if (home == null)
-                home = card;
+            // add in order to ensure order is preserved if sparsely removed later
+            list.add(card);
+        }
+        
+        if (set_home_visible && home != null) {
+            set_visible_child(home);
+            home.jumped_to(null, null);
+        }
+    }
+    
+    /**
+     * Removes { link Card}s from the { link Deck}.
+     *
+     * If the { link top} card is removed, the Deck will return { link home}, clearing the
+     * navigation stack in the process.
+     */
+    public void remove_cards(Gee.Iterable<Card> cards) {
+        bool displaying = top != null;
+        
+        foreach (Card card in cards) {
+            if (!names.has_key(card.card_id)) {
+                message("Card %s not found in Deck", card.card_id);
+                
+                continue;
+            }
+            
+            card.map.disconnect(on_card_mapped);
+            
+            remove(card);
+            
+            if (top == card)
+                top = null;
+            
+            navigation_stack.remove(card);
+            names.unset(card.card_id);
+            list.remove(card);
         }
         
-        if (set_home_visible)
+        // if was displaying a Card and now not, jump home
+        if (displaying && top == null && home != null) {
+            navigation_stack.clear();
             set_visible_child(home);
+            home.jumped_to(null, null);
+        }
     }
     
     private void on_jump_to_card(Card card, Card next, Value? message) {
         // do nothing if already visible
-        if (get_visible_child() == next)
+        if (get_visible_child() == next) {
+            debug("Already showing card %s", next.card_id);
+            
             return;
+        }
         
         // do nothing if not registered with this Deck
         if (!names.values.contains(next)) {
@@ -250,7 +312,10 @@ public class Deck : Gtk.Stack, Host.Interaction {
         // jumping home clears the navigation stack
         navigation_stack.clear();
         
-        on_jump_to_card(card, home, null);
+        if (home != null)
+            on_jump_to_card(card, home, null);
+        else
+            message("No home card in Deck");
     }
     
     private void on_dismissed(bool user_request) {
diff --git a/src/util/util-listbox-model.vala b/src/util/util-listbox-model.vala
index 090d336..fb8f0d5 100644
--- a/src/util/util-listbox-model.vala
+++ b/src/util/util-listbox-model.vala
@@ -147,6 +147,23 @@ public class ListBoxModel<G> : BaseObject {
     }
     
     /**
+     * Add a collection of { link Card}s to the { link Deck}.
+     *
+     * Returns the number of Cards added.
+     *
+     * @see add
+     */
+    public int add_many(Gee.Iterable<G> items) {
+        int count = 0;
+        foreach (G item in items) {
+            if (add(item))
+                count++;
+        }
+        
+        return count;
+    }
+    
+    /**
      * Removes an item from the model, which in turn removes it from the { link listbox}.
      *
      * Returns true if the model (and therefore the listbox) were altered due to the removal.
@@ -157,6 +174,23 @@ public class ListBoxModel<G> : BaseObject {
         return internal_remove(item, true);
     }
     
+    /**
+     * Removes a collection of { link Card}s from the { link Deck}.
+     *
+     * Returns the number of Cards removed.
+     *
+     * @see remove
+     */
+    public int remove_many(Gee.Iterable<G> items) {
+        int count = 0;
+        foreach (G item in items) {
+            if (remove(item))
+                count++;
+        }
+        
+        return count;
+    }
+    
     private bool internal_remove(G item, bool remove_from_listbox) {
         Gtk.ListBoxRow row;
         if (!items.unset(item, out row))


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