[california/wip/725763-google] Updated documentation, small fixes in new classes



commit 635b807b8c15b6e16e3c3fb973309b1da0e5ba98
Author: Jim Nelson <jim yorba org>
Date:   Thu Apr 3 14:24:51 2014 -0700

    Updated documentation, small fixes in new classes

 src/util/util-deck.vala          |  110 +++++++++++++++++++++++++-
 src/util/util-listbox-model.vala |  159 ++++++++++++++++++++++++++++++++++----
 2 files changed, 251 insertions(+), 18 deletions(-)
---
diff --git a/src/util/util-deck.vala b/src/util/util-deck.vala
index 2cd2693..bf1f587 100644
--- a/src/util/util-deck.vala
+++ b/src/util/util-deck.vala
@@ -6,24 +6,98 @@
 
 namespace California {
 
+/**
+ * A Card is a single pane of widget(s) in a { link Deck}.
+ *
+ * The navigation of Cards is tracked within their Deck, and Cards can request navigation via their
+ * various signals.  They're also notified when nevigation which affects them is made.
+ */
+
 public interface Card : Gtk.Widget {
+    /**
+     * Each { link Card} has its own identifier that should be unique within the { link Deck}.
+     *
+     * In the Gtk.Stack, this is its name.
+     */
     public abstract string card_id { get; }
     
+    /**
+     * A user-visible string that may be used elsewhere in the application.
+     *
+     * Gtk.StackSwitcher uses this title.  { link Deck} does not use the title in any way.
+     */
     public abstract string? title { get; }
     
+    /**
+     * 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
+     * navigating to the new one to construct and pass an appropriate message.
+     *
+     * @see jump_to_card_by_name
+     */
     public signal void jump_to_card(Card next, Value? message);
     
+    /**
+     * Fired when the { link Card} wishes to jump to another Card by its name.
+     *
+     * @see jump_to_card
+     */
     public signal void jump_to_card_by_name(string name, Value? message);
     
+    /**
+     * Fired when the { link Card} wishes to jump to the previous Card in the { link Deck}.
+     *
+     * Note that this Card's position in the navigation stack is lost; there is no "jump forward".
+     */
     public signal void jump_back();
     
+    /**
+     * Fired when the { link Card} wishes to jump to the first Card in the { link Deck}.
+     *
+     * This clears the Deck's navigation stack, meaning { link jump_back} will not return to
+     * this Card.
+     */
+    public signal void jump_home();
+    
+    /**
+     * Fired when the { link Deck}'s work is cancelled, closed, or dismissed, whether due to
+     * programmatic reasons or by user request.
+     *
+     * Implementing classes should fire this after firing the { link completed signal} so
+     * subscribers can maintain their cleanup in a single handler.
+     */
     public signal void dismissed(bool user_request);
     
+    /**
+     * Fired when the { link Deck}'s work has completed successfully.
+     *
+     * This should only be fired if the Deck requires valid input from the user to perform
+     * some intensive operation.  Merely displaying information and closing the Deck
+     * should simply fire { link dismissed}.
+     *
+     * "completed" implies that dismissed will be called shortly thereafter, meaning all
+     * cleanup can be handled there.
+     */
     public signal void completed();
     
+    /**
+     * Called by { link Deck} when the { link Card} has been activated, i.e. put to the "top" of
+     * the Deck.
+     *
+     * message may be null even if the Card expects one; generally this means { link jump_back}
+     * or { link jump_home} was invoked, resulting in this Card being activated.
+     */
     public abstract void jumped_to(Card from, Value? message);
 }
 
+/**
+ * A Deck is a collection of { link Card}s maintained within a Gtk.Stack.
+ *
+ * Cards control navigation through their various signals, which Deck monitors and acts upon.
+ * It also notifies Cards of nagivation changes which affect them via their abstract methods.
+ */
+
 public class Deck : Gtk.Stack, Host.Interaction {
     /**
      * @inheritedDoc
@@ -33,9 +107,17 @@ public class Deck : Gtk.Stack, Host.Interaction {
     public int size { get { return names.size; } }
     
     private Card? top = null;
+    private Card? home = null;
     private Gee.Deque<Card> navigation_stack = new Gee.LinkedList<Card>();
     private Gee.HashMap<string, Card> names = new Gee.HashMap<string, Card>();
     
+    /**
+     * Create a new { link Deck}.
+     *
+     * By default the Deck configures the underlying Gtk.Stack to slide left and right, depending
+     * on the position of the { link Card}s.  This can be changed, but the recommended
+     * transition types are SLIDE_LEFT_RIGHT and SLIDE_UP_DOWN.
+     */
     public Deck() {
         transition_type = Gtk.StackTransitionType.SLIDE_LEFT_RIGHT;
         notify["visible-child"].connect(on_child_to_top);
@@ -47,6 +129,7 @@ public class Deck : Gtk.Stack, Host.Interaction {
             top.jump_to_card.disconnect(on_jump_to_card);
             top.jump_to_card_by_name.disconnect(on_jump_to_card_by_name);
             top.jump_back.disconnect(on_jump_back);
+            top.jump_home.disconnect(on_jump_home);
             top.dismissed.disconnect(on_dismissed);
             top.completed.disconnect(on_completed);
             
@@ -60,16 +143,26 @@ public class Deck : Gtk.Stack, Host.Interaction {
             top.jump_to_card.connect(on_jump_to_card);
             top.jump_to_card_by_name.connect(on_jump_to_card_by_name);
             top.jump_back.connect(on_jump_back);
+            top.jump_home.connect(on_jump_home);
             top.dismissed.connect(on_dismissed);
             top.completed.connect(on_completed);
         }
     }
     
+    /**
+     * Add { link Card}s to the { link Deck}.
+     *
+     * Cards can be added in multiple batches, but the ordering is important as it dictates how
+     * they're presented to the user via transitions and slides.
+     *
+     * The first Card added is the "home" Card.  The Deck will automatically show it first.
+     */
     public void add_cards(Gee.List<Card> cards) {
         if (cards.size == 0)
             return;
         
-        bool set_first_visible = size == 0;
+        // if empty, first card is home and should be made visible when added
+        bool set_home_visible = size == 0;
         
         // add each Card using the title if possible, otherwise by ID
         foreach (Card card in cards) {
@@ -83,10 +176,14 @@ public class Deck : Gtk.Stack, Host.Interaction {
                 add_titled(card, card.card_id, card.title);
             
             names.set(card.card_id, card);
+            
+            // first Card seen is home card
+            if (home == null)
+                home = card;
         }
         
-        if (set_first_visible)
-            set_visible_child(cards[0]);
+        if (set_home_visible)
+            set_visible_child(home);
     }
     
     private void on_jump_to_card(Card card, Card next, Value? message) {
@@ -119,6 +216,13 @@ public class Deck : Gtk.Stack, Host.Interaction {
             on_jump_to_card(card, navigation_stack.poll_head(), null);
     }
     
+    private void on_jump_home(Card card) {
+        // jumping home clears the navigation stack
+        navigation_stack.clear();
+        
+        on_jump_to_card(card, home, null);
+    }
+    
     private void on_dismissed(bool user_request) {
         dismissed(user_request);
     }
diff --git a/src/util/util-listbox-model.vala b/src/util/util-listbox-model.vala
index 370c9d9..090d336 100644
--- a/src/util/util-listbox-model.vala
+++ b/src/util/util-listbox-model.vala
@@ -6,31 +6,86 @@
 
 namespace California {
 
+/**
+ * A { link Mutable} is an Object which can internally change state (i.e. is no immutable).
+ *
+ * { link ListBoxModel} recognizes when an Object supports this interface and will monitor its
+ * { link mutated} signal.
+ */
+
 public interface Mutable : Object {
+    /**
+     * Fired when important internal state has changed.
+     *
+     * This can be used by collections and other containers to update their own state, such as
+     * re-sorting or re-applying filters.
+     */
     public signal void mutated();
 }
 
+/**
+ * A simple model for Gtk.ListBox.
+ *
+ * ListBoxModel is designed to make it easier to maintain a sorted list of objects and make sure
+ * the associated Gtk.ListBox is always up-to-date reflecting the state of the model.
+ *
+ * If the added objects implement the { link Mutable} interface, their { link Mutable.mutated}
+ * signsl is monitored.  When fired, the listbox's sort and filters will be invalidated.
+ */
+
 public class ListBoxModel<G> : BaseObject {
     public const string PROP_SELECTED = "selected";
     
     private const string KEY = "org.yorba.california.listbox-model.model";
     
+    /**
+     * Returns a Gtk.Widget that is held by the Gtk.ListBox representing the particular item.
+     */
     public delegate Gtk.Widget ModelPresentation<G>(G item);
     
     public Gtk.ListBox listbox { get; private set; }
     
+    /**
+     * The number if items in the { link ListBoxModel}.
+     */
     public int size { get { return items.size; } }
     
+    /**
+     * The item currently selected by the { link listbox}, null if no selection has been made.
+     */
     public G? selected { get; private set; default = null; }
     
     private unowned ModelPresentation model_presentation;
     private unowned CompareDataFunc<G>? comparator;
-    private Gee.HashSet<G> items;
+    private Gee.HashMap<G, Gtk.ListBoxRow> items;
     
+    /**
+     * Fired when an item is added to the { link ListBoxModel}.
+     *
+     * @see add
+     */
     public signal void added(G item);
     
+    /**
+     * Fired when an item is removed from the { link ListBoxModel}.
+     *
+     * @see remove
+     */
+    public signal void removed(G item);
+    
+    /**
+     * Fired when the { link listbox} activates an item.
+     *
+     * Gtk.ListBox can activate an item with a double- or single-click, depending on configuration.
+     */
     public signal void activated(G item);
     
+    /**
+     * Create a { link ListBoxModel} and tie it to a Gtk.ListBox.
+     *
+     * The list will be sorted if a comparator is supplied, otherwise added items are appended to
+     * the list.
+     */
     public ListBoxModel(Gtk.ListBox listbox, ModelPresentation<G> model_presentation,
         CompareDataFunc<G>? comparator = null, owned Gee.HashDataFunc<G>? hash_func = null,
         owned Gee.EqualDataFunc<G>? equal_func = null) {
@@ -38,8 +93,9 @@ public class ListBoxModel<G> : BaseObject {
         this.model_presentation = model_presentation;
         this.comparator = comparator;
         
-        items = new Gee.HashSet<G>((owned) hash_func, (owned) equal_func);
+        items = new Gee.HashMap<G, Gtk.ListBoxRow>((owned) hash_func, (owned) equal_func);
         
+        listbox.remove.connect(on_listbox_removed);
         listbox.set_sort_func(listbox_sort_func);
         listbox.row_activated.connect(on_row_activated);
         listbox.row_selected.connect(on_row_selected);
@@ -49,42 +105,108 @@ public class ListBoxModel<G> : BaseObject {
         listbox.row_activated.disconnect(on_row_activated);
         listbox.row_selected.disconnect(on_row_selected);
         
-        foreach (G item in items) {
+        foreach (G item in items.keys) {
             Mutable? mutable = item as Mutable;
             if (mutable != null)
                 mutable.mutated.disconnect(on_mutated);
         }
     }
     
+    /**
+     * Add an item to the model, which in turns adds it to the { link listbox}.
+     *
+     * If the item implements the { link Mutable} interface, its { link Mutable.mutated} signal
+     * is monitored and will invalidate the listbox's sort and filters.
+     *
+     * Returns true if the model (and therefore the listbox) were altered due to the addition.
+     *
+     * @see added
+     */
     public bool add(G item) {
-        if (!items.add(item))
+        if (items.has_key(item))
             return false;
         
         Mutable? mutable = item as Mutable;
         if (mutable != null)
             mutable.mutated.connect(on_mutated);
         
-        Gtk.Widget widget = model_presentation(item);
-        widget.set_data<G>(KEY, item);
+        // item -> Gtk.ListBoxRow
+        Gtk.ListBoxRow row = new Gtk.ListBoxRow();
+        row.add(model_presentation(item));
         
-        listbox.add(widget);
-        widget.show_all();
+        // mappings
+        row.set_data<G>(KEY, item);
+        items.set(item, row);
+        
+        listbox.add(row);
+        row.show_all();
         
         added(item);
         
         return true;
     }
     
+    /**
+     * 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.
+     *
+     * @see removed
+     */
+    public bool remove(G item) {
+        return internal_remove(item, true);
+    }
+    
+    private bool internal_remove(G item, bool remove_from_listbox) {
+        Gtk.ListBoxRow row;
+        if (!items.unset(item, out row))
+            return false;
+        
+        Mutable? mutable = item as Mutable;
+        if (mutable != null)
+            mutable.mutated.disconnect(on_mutated);
+        
+        if (remove_from_listbox)
+            listbox.remove(row);
+        
+        removed(item);
+        
+        return true;
+    }
+    
+    /**
+     * Returns true if the model holds the item.
+     */
     public bool contains(G item) {
-        return items.contains(item);
+        return items.has_key(item);
     }
     
+    /**
+     * Clears all items from the { link ListBoxModel}.
+     *
+     * Each removed item generates a { link removed} signal.
+     */
     public void clear() {
+        foreach (G item in items.keys)
+            remove(item);
+    }
+    
+    // This can be called by our add() method or externally, so don't be too absolutist here
+    private void on_listbox_removed(Gtk.Widget widget) {
+        // get the actual widget, not the wrapping object
+        Gtk.ListBoxRow? row = widget as Gtk.ListBoxRow;
+        if (row == null) {
+            message("GtkListBox removed non-GtkListBoxRow child");
+            
+            return;
+        }
+        
+        internal_remove(row.get_data<G>(KEY), false);
     }
     
     private int listbox_sort_func(Gtk.ListBoxRow a, Gtk.ListBoxRow b) {
-        unowned G item_a = a.get_child().get_data<G>(KEY);
-        unowned G item_b = b.get_child().get_data<G>(KEY);
+        unowned G item_a = a.get_data<G>(KEY);
+        unowned G item_b = b.get_data<G>(KEY);
         
         if (comparator != null)
             return comparator(item_a, item_b);
@@ -93,15 +215,22 @@ public class ListBoxModel<G> : BaseObject {
     }
     
     private void on_row_activated(Gtk.ListBoxRow row) {
-        activated(row.get_child().get_data<G>(KEY));
+        activated(row.get_data<G>(KEY));
     }
     
     private void on_row_selected(Gtk.ListBoxRow? row) {
-        selected = (row != null) ? row.get_child().get_data<G>(KEY) : null;
+        selected = (row != null) ? row.get_data<G>(KEY) : null;
     }
     
-    private void on_mutated() {
-        listbox.invalidate_sort();
+    private void on_mutated(Mutable mutable) {
+        Gtk.ListBoxRow? row = items.get((G) mutable);
+        if (row == null) {
+            message("Mutable not found in ListBoxRow");
+            
+            return;
+        }
+        
+        row.changed();
     }
     
     public override string to_string() {


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