[california] Simplify Deck/Card interaction



commit 1c15bfe3569a9fc92a86a36b9c77a6d1ea8b3955
Author: Jim Nelson <jim yorba org>
Date:   Wed Apr 23 14:59:09 2014 -0700

    Simplify Deck/Card interaction

 src/activator/activator-instance-list.vala         |    2 +-
 src/activator/activator-window.vala                |    2 +-
 .../activator-google-calendar-list-pane.vala       |   13 ++---
 src/activator/webcal/activator-webcal-pane.vala    |    7 +-
 src/host/host-create-update-event.vala             |    5 +-
 src/host/host-main-window.vala                     |   32 +++++++---
 src/host/host-quick-create-event.vala              |    5 +-
 src/host/host-show-event.vala                      |    6 +-
 src/manager/manager-calendar-list.vala             |    2 +-
 src/manager/manager-window.vala                    |    2 +-
 src/toolkit/toolkit-card.vala                      |   64 +++++++++++++++++---
 src/toolkit/toolkit-deck-window.vala               |   35 ++++++++---
 src/toolkit/toolkit-deck.vala                      |   46 +++++++-------
 src/toolkit/toolkit.vala                           |    8 +++
 14 files changed, 153 insertions(+), 76 deletions(-)
---
diff --git a/src/activator/activator-instance-list.vala b/src/activator/activator-instance-list.vala
index b24736b..c5d560a 100644
--- a/src/activator/activator-instance-list.vala
+++ b/src/activator/activator-instance-list.vala
@@ -58,7 +58,7 @@ public class InstanceList : Gtk.Grid, Toolkit.Card {
     
     [GtkCallback]
     private void on_cancel_button_clicked() {
-        dismissed(true);
+        notify_user_closed();
     }
     
     private void start(Instance activator) {
diff --git a/src/activator/activator-window.vala b/src/activator/activator-window.vala
index 7df1c2b..bdce01a 100644
--- a/src/activator/activator-window.vala
+++ b/src/activator/activator-window.vala
@@ -14,7 +14,7 @@ public class Window : Toolkit.DeckWindow {
     private static Activator.Window? instance = null;
     
     private Window(Gtk.Window? parent) {
-        base (parent);
+        base (parent, null);
         
         // 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)
diff --git a/src/activator/google/activator-google-calendar-list-pane.vala 
b/src/activator/google/activator-google-calendar-list-pane.vala
index 5dacd1a..4e47b00 100644
--- a/src/activator/google/activator-google-calendar-list-pane.vala
+++ b/src/activator/google/activator-google-calendar-list-pane.vala
@@ -163,10 +163,7 @@ public class GoogleCalendarListPane : Gtk.Grid, Toolkit.Card {
         }
         
         if (errmsg != null) {
-            Application.instance.error_message(_("Unable to subscribe to %s: %s").printf(
-                calendar.title, errmsg));
-            
-            dismissed(false);
+            notify_failure(_("Unable to subscribe to %s: %s").printf(calendar.title, errmsg));
             
             return;
         }
@@ -175,13 +172,13 @@ public class GoogleCalendarListPane : Gtk.Grid, Toolkit.Card {
         try {
             yield store.subscribe_caldav_async(calendar.title, uri, username,
                 calendar.color.to_hexadecimal(), null);
-            completed();
         } catch (Error err) {
-            Application.instance.error_message(_("Unable to subscribe to %s: %s").printf(
-                calendar.title, err.message));
+            notify_failure(_("Unable to subscribe to %s: %s").printf(calendar.title, err.message));
+            
+            return;
         }
         
-        dismissed(true);
+        notify_success();
     }
 }
 
diff --git a/src/activator/webcal/activator-webcal-pane.vala b/src/activator/webcal/activator-webcal-pane.vala
index 20d4d83..493bf28 100644
--- a/src/activator/webcal/activator-webcal-pane.vala
+++ b/src/activator/webcal/activator-webcal-pane.vala
@@ -77,12 +77,11 @@ internal class WebCalActivatorPane : Gtk.Grid, Toolkit.Card {
         try {
             yield store.subscribe_webcal_async(name_entry.text, URI.parse(url_entry.text),
                 null, Gfx.rgb_to_uint8_rgb_string(color), null);
-            completed();
+            notify_success();
         } catch (Error err) {
-            debug("Unable to create subscription to %s: %s", url_entry.text, err.message);
+            notify_failure(_("Unable to subscribe to Web calendar at %s: %s").printf(url_entry.text,
+                err.message));
         }
-        
-        dismissed(true);
     }
 }
 
diff --git a/src/host/host-create-update-event.vala b/src/host/host-create-update-event.vala
index 1e1fec8..b77aacf 100644
--- a/src/host/host-create-update-event.vala
+++ b/src/host/host-create-update-event.vala
@@ -288,13 +288,12 @@ public class CreateUpdateEvent : Gtk.Grid, Toolkit.Card {
         else
             create_event(event);
         
-        completed();
-        dismissed(true);
+        notify_success();
     }
     
     [GtkCallback]
     private void on_cancel_button_clicked() {
-        dismissed(true);
+        notify_user_closed();
     }
 }
 
diff --git a/src/host/host-main-window.vala b/src/host/host-main-window.vala
index f4f666d..a4eec0d 100644
--- a/src/host/host-main-window.vala
+++ b/src/host/host-main-window.vala
@@ -125,13 +125,18 @@ public class MainWindow : Gtk.ApplicationWindow {
         add(layout);
     }
     
-    private void show_deck(Gtk.Widget relative_to, Gdk.Point? for_location, Gee.List<Toolkit.Card> cards) {
-        Toolkit.DeckWindow deck_window = new Toolkit.DeckWindow(this);
-        deck_window.deck.add_cards(cards);
+    private void show_deck(Gtk.Widget relative_to, Gdk.Point? for_location, Toolkit.Deck deck) {
+        Toolkit.DeckWindow deck_window = new Toolkit.DeckWindow(this, deck);
         
         // when the dialog closes, reset View.Controllable state (selection is maintained while
-        // use is viewing/editing Interaction) and destroy widgets
-        deck_window.deck.dismissed.connect(() => current_view.unselect_all());
+        // use is viewing/editing interaction) and destroy widgets
+        deck_window.deck.dismiss.connect(() => {
+            current_view.unselect_all();
+            deck_window.hide();
+            // give the dialog a change to hide before allowing other signals to fire, which may
+            // invoke another dialog (prevents multiple dialogs on screen at same time)
+            Toolkit.spin_event_loop();
+        });
         
         deck_window.show_all();
         deck_window.run();
@@ -141,7 +146,7 @@ public class MainWindow : Gtk.ApplicationWindow {
     private void on_quick_create_event() {
         QuickCreateEvent quick_create = new QuickCreateEvent();
         
-        quick_create.completed.connect(() => {
+        quick_create.success.connect(() => {
             if (quick_create.parsed_event == null)
                 return;
             
@@ -151,7 +156,10 @@ public class MainWindow : Gtk.ApplicationWindow {
                 create_event(null, null, quick_create.parsed_event, true, quick_add_button, null);
         });
         
-        show_deck(quick_add_button, null, iterate<Toolkit.Card>(quick_create).to_array_list());
+        Toolkit.Deck deck = new Toolkit.Deck();
+        deck.add_cards(iterate<Toolkit.Card>(quick_create).to_array_list());
+        
+        show_deck(quick_add_button, null, deck);
     }
     
     private void on_jump_to_today() {
@@ -199,7 +207,10 @@ public class MainWindow : Gtk.ApplicationWindow {
             update_event_async.begin(event, null);
         });
         
-        show_deck(relative_to, for_location, iterate<Toolkit.Card>(create_update_event).to_array_list());
+        Toolkit.Deck deck = new Toolkit.Deck();
+        deck.add_cards(iterate<Toolkit.Card>(create_update_event).to_array_list());
+        
+        show_deck(relative_to, for_location, deck);
     }
     
     private async void create_event_async(Component.Event event, Cancellable? cancellable) {
@@ -236,7 +247,10 @@ public class MainWindow : Gtk.ApplicationWindow {
             create_event(null, null, event, false, relative_to, for_location);
         });
         
-        show_deck(relative_to, for_location, iterate<Toolkit.Card>(show_event).to_array_list());
+        Toolkit.Deck deck = new Toolkit.Deck();
+        deck.add_cards(iterate<Toolkit.Card>(show_event).to_array_list());
+        
+        show_deck(relative_to, for_location, deck);
     }
     
     private async void remove_event_async(Component.Event event, Cancellable? cancellable) {
diff --git a/src/host/host-quick-create-event.vala b/src/host/host-quick-create-event.vala
index 09c0273..c0639f7 100644
--- a/src/host/host-quick-create-event.vala
+++ b/src/host/host-quick-create-event.vala
@@ -61,7 +61,7 @@ public class QuickCreateEvent : Gtk.Grid, Toolkit.Card {
     
     [GtkCallback]
     private void on_cancel_button_clicked() {
-        dismissed(true);
+        notify_user_closed();
     }
     
     [GtkCallback]
@@ -69,8 +69,7 @@ public class QuickCreateEvent : Gtk.Grid, Toolkit.Card {
         Component.DetailsParser parser = new Component.DetailsParser(details_entry.text, model.active);
         parsed_event = parser.event;
         
-        completed();
-        dismissed(true);
+        notify_success();
     }
 }
 
diff --git a/src/host/host-show-event.vala b/src/host/host-show-event.vala
index 169d8c7..9091e98 100644
--- a/src/host/host-show-event.vala
+++ b/src/host/host-show-event.vala
@@ -138,18 +138,18 @@ public class ShowEvent : Gtk.Grid, Toolkit.Card {
     [GtkCallback]
     private void on_remove_button_clicked() {
         remove_event(event);
-        dismissed(true);
+        notify_success();
     }
     
     [GtkCallback]
     private void on_update_button_clicked() {
         update_event(event);
-        dismissed(true);
+        notify_success();
     }
     
     [GtkCallback]
     private void on_close_button_clicked() {
-        dismissed(true);
+        notify_user_closed();
     }
 }
 
diff --git a/src/manager/manager-calendar-list.vala b/src/manager/manager-calendar-list.vala
index e7ccb33..48246b0 100644
--- a/src/manager/manager-calendar-list.vala
+++ b/src/manager/manager-calendar-list.vala
@@ -90,7 +90,7 @@ public class CalendarList : Gtk.Grid, Toolkit.Card {
     
     [GtkCallback]
     private void on_close_button_clicked() {
-        dismissed(true);
+        notify_user_closed();
     }
 }
 
diff --git a/src/manager/manager-window.vala b/src/manager/manager-window.vala
index 57b3855..ce26199 100644
--- a/src/manager/manager-window.vala
+++ b/src/manager/manager-window.vala
@@ -14,7 +14,7 @@ public class Window : Toolkit.DeckWindow {
     private static Manager.Window? instance = null;
     
     private Window(Gtk.Window? parent) {
-        base (parent);
+        base (parent, null);
         
         deck.add_cards(iterate<Toolkit.Card>(new CalendarList()).to_array_list());
     }
diff --git a/src/toolkit/toolkit-card.vala b/src/toolkit/toolkit-card.vala
index fb20277..e192943 100644
--- a/src/toolkit/toolkit-card.vala
+++ b/src/toolkit/toolkit-card.vala
@@ -83,25 +83,41 @@ public interface Card : Gtk.Widget {
     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.
+     * Fired when the { link Deck}'s work is cancelled, closed, failure, or a success, 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.
+     * user_request indicates if the dismissal is due to a user request or programmatic reasons.
+     * closed indicates that there is no qualitative signal (i.e. { link success}, { link failure})
+     * to follow.
+     *
+     * Implementing classes should use one of the notify_ methods to ensure that proper signal
+     * order is maintained.
      */
-    public signal void dismissed(bool user_request);
+    public signal void dismiss(bool user_request, bool final);
     
     /**
      * 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}.
+     * should simply fire { link dismiss}.
+     *
+     * Implementing classes should use one of the notify_ methods to ensure that proper signal
+     * order is maintained.
+     */
+    public signal void success();
+    
+    /**
+     * Fired when the { link Deck}'s work has failed to complete.
+     *
+     * 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 dismiss}.
      *
-     * "completed" implies that dismissed will be called shortly thereafter, meaning all
-     * cleanup can be handled there.
+     * Implementing classes should use one of the notify_ methods to ensure that proper signal
+     * order is maintained.
      */
-    public signal void completed();
+    public signal void failure(string? user_message);
     
     /**
      * Called by { link Deck} when the { link Card} has been activated, i.e. put to the "top" of
@@ -114,6 +130,36 @@ public interface Card : Gtk.Widget {
      * changes to those properties in this call, if need be.
      */
     public abstract void jumped_to(Card? from, Value? message);
+    
+    /**
+     * Dismiss the { link Deck} due to the user requesting it be closed or cancelled.
+     */
+    protected void notify_user_closed() {
+        dismiss(true, true);
+    }
+    
+    /**
+     * Dismiss the { link Deck} due to programmatic reasons.
+     */
+    protected void notify_aborted() {
+        dismiss(false, true);
+    }
+    
+    /**
+     * Dismiss the { link Deck} and notify that the user has successfully completed the task.
+     */
+    protected void notify_success() {
+        dismiss(true, false);
+        success();
+    }
+    
+    /**
+     * Dismiss the { link Deck} and notify that the operation has failed.
+     */
+    protected void notify_failure(string? user_message) {
+        dismiss(true, false);
+        failure(user_message);
+    }
 }
 
 }
diff --git a/src/toolkit/toolkit-deck-window.vala b/src/toolkit/toolkit-deck-window.vala
index 333f709..44d1872 100644
--- a/src/toolkit/toolkit-deck-window.vala
+++ b/src/toolkit/toolkit-deck-window.vala
@@ -17,17 +17,18 @@ namespace California.Toolkit {
  */
 
 public class DeckWindow : Gtk.Dialog {
-    public Deck deck { get; private set; default = new Deck(); }
+    public Deck deck { get; private set; }
     
-    private Gtk.ResponseType response_type = Gtk.ResponseType.CLOSE;
-    
-    public DeckWindow(Gtk.Window? parent) {
+    public DeckWindow(Gtk.Window? parent, Deck? starter_deck) {
+        this.deck = starter_deck ?? new Deck();
+        
         transient_for = parent;
         modal = true;
         resizable = false;
         
-        deck.dismissed.connect(on_deck_dismissed);
-        deck.completed.connect(on_deck_completed);
+        deck.dismiss.connect(on_deck_dismissed);
+        deck.success.connect(on_deck_success);
+        deck.failure.connect(on_deck_failure);
         
         Gtk.Box content_area = (Gtk.Box) get_content_area();
         content_area.margin = 8;
@@ -37,12 +38,26 @@ public class DeckWindow : Gtk.Dialog {
         get_action_area().no_show_all = true;
     }
     
-    private void on_deck_completed() {
-        response_type = Gtk.ResponseType.OK;
+    ~DeckWindow() {
+        deck.dismiss.disconnect(on_deck_dismissed);
+        deck.success.disconnect(on_deck_success);
+        deck.failure.disconnect(on_deck_failure);
+    }
+    
+    private void on_deck_dismissed(bool user_request, bool final) {
+        if (final)
+            response(Gtk.ResponseType.CLOSE);
     }
     
-    private void on_deck_dismissed() {
-        response(response_type);
+    private void on_deck_success() {
+        response(Gtk.ResponseType.OK);
+    }
+    
+    private void on_deck_failure(string? user_message) {
+        if (!String.is_empty(user_message))
+            Application.instance.error_message(user_message);
+        
+        response(Gtk.ResponseType.CLOSE);
     }
 }
 
diff --git a/src/toolkit/toolkit-deck.vala b/src/toolkit/toolkit-deck.vala
index 20c587d..306ba85 100644
--- a/src/toolkit/toolkit-deck.vala
+++ b/src/toolkit/toolkit-deck.vala
@@ -44,25 +44,19 @@ public class Deck : Gtk.Stack {
     private Gee.HashMap<string, Card> names = new Gee.HashMap<string, Card>();
     
     /**
-     * Fired when the { link Deck}'s work is cancelled, closed, or dismissed, whether due to
-     * programmatic reasons or by user request.
-     *
-     * This will be fired after firing the { link completed signal} so subscribers can maintain
-     * their cleanup in a single handler.
+     * @see Card.dismiss
      */
-    public signal void dismissed(bool user_request);
+    public signal void dismiss(bool user_request, bool final);
     
     /**
-     * Fired when the { link Deck}'s work has completed successfully.
-     *
-     * This will 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.
+     * @see Card.success
+     */
+    public signal void success();
+    
+    /**
+     * @see Card.failure
      */
-    public signal void completed();
+    public signal void failure(string? user_message);
     
     /**
      * Create a new { link Deck}.
@@ -88,8 +82,9 @@ public class Deck : Gtk.Stack {
             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);
+            top.dismiss.disconnect(on_dismiss);
+            top.success.disconnect(on_success);
+            top.failure.disconnect(on_failure);
             
             navigation_stack.offer_head(top);
             top = null;
@@ -102,8 +97,9 @@ public class Deck : Gtk.Stack {
             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);
+            top.dismiss.connect(on_dismiss);
+            top.success.connect(on_success);
+            top.failure.connect(on_failure);
         }
     }
     
@@ -229,12 +225,16 @@ public class Deck : Gtk.Stack {
             message("No home card in Deck");
     }
     
-    private void on_dismissed(bool user_request) {
-        dismissed(user_request);
+    private void on_dismiss(bool user_request, bool final) {
+        dismiss(user_request, final);
+    }
+    
+    private void on_success() {
+        success();
     }
     
-    private void on_completed() {
-        completed();
+    private void on_failure(string? user_message) {
+        failure(user_message);
     }
     
     private void on_card_mapped(Gtk.Widget widget) {
diff --git a/src/toolkit/toolkit.vala b/src/toolkit/toolkit.vala
index 7349b94..c8afc0a 100644
--- a/src/toolkit/toolkit.vala
+++ b/src/toolkit/toolkit.vala
@@ -26,4 +26,12 @@ public void terminate() {
     Calendar.terminate();
 }
 
+/**
+ * Spin the GTK event loop until all pending events are completed.
+ */
+public void spin_event_loop() {
+    while (Gtk.events_pending())
+        Gtk.main_iteration();
+}
+
 }


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