[california/wip/732029-gtk-312: 7/7] Use DeckPopover for simple windows, DeckWindow for more complicated editing



commit 614d8b74fe0085378b40c815df656990e8fc77a1
Author: Jim Nelson <jim yorba org>
Date:   Tue Aug 5 18:47:35 2014 -0700

    Use DeckPopover for simple windows, DeckWindow for more complicated editing

 src/Makefile.am                              |    1 +
 src/activator/activator-window.vala          |   14 ++--
 src/application/california-application.vala  |    4 +-
 src/host/host-main-window.vala               |   91 ++++++++++++++++++-----
 src/host/host-quick-create-event.vala        |   10 +-
 src/host/host-show-event.vala                |   13 ++--
 src/manager/manager-window.vala              |   14 ++--
 src/toolkit/toolkit-deck-popover.vala        |  102 ++++++++++++++++++++++++++
 src/toolkit/toolkit-deck-window.vala         |   94 +++++++-----------------
 src/toolkit/toolkit-rotating-button-box.vala |   28 +++++++
 10 files changed, 254 insertions(+), 117 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index eebcc31..f3006d1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -123,6 +123,7 @@ california_VALASOURCES = \
        toolkit/toolkit-card.vala \
        toolkit/toolkit-combo-box-text-model.vala \
        toolkit/toolkit-deck.vala \
+       toolkit/toolkit-deck-popover.vala \
        toolkit/toolkit-deck-window.vala \
        toolkit/toolkit-editable-label.vala \
        toolkit/toolkit-entry-clear-text-connector.vala \
diff --git a/src/activator/activator-window.vala b/src/activator/activator-window.vala
index 95ff428..29fce40 100644
--- a/src/activator/activator-window.vala
+++ b/src/activator/activator-window.vala
@@ -11,8 +11,8 @@ namespace California.Activator {
  */
 
 public class Window : Toolkit.DeckWindow {
-    private Window(Gtk.Widget relative_to, Gdk.Point? for_location) {
-        base (relative_to, for_location, null);
+    private Window(Gtk.Window? window) {
+        base (window, 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)
@@ -24,14 +24,12 @@ public class Window : Toolkit.DeckWindow {
         deck.add_cards(cards);
     }
     
-    public static void display(Gtk.Widget relative_to, Gdk.Point? for_location) {
-        Activator.Window instance = new Activator.Window(relative_to, for_location);
-        
-        instance.dismiss.connect(() => {
-            Toolkit.destroy_later(instance);
-        });
+    public static void display(Gtk.Window? window) {
+        Activator.Window instance = new Activator.Window(window);
         
         instance.show_all();
+        instance.run();
+        instance.destroy();
     }
 }
 
diff --git a/src/application/california-application.vala b/src/application/california-application.vala
index 72a3296..321dae2 100644
--- a/src/application/california-application.vala
+++ b/src/application/california-application.vala
@@ -212,11 +212,11 @@ public class Application : Gtk.Application {
     }
     
     private void on_new_calendar() {
-        Activator.Window.display(main_window.calendar_button, null);
+        Activator.Window.display(main_window);
     }
     
     private void on_calendar_manager() {
-        Manager.Window.display(main_window.calendar_button, null);
+        Manager.Window.display(main_window);
     }
     
     private void on_process_file(SimpleAction action, Variant? variant) {
diff --git a/src/host/host-main-window.vala b/src/host/host-main-window.vala
index c5bf312..e343cfc 100644
--- a/src/host/host-main-window.vala
+++ b/src/host/host-main-window.vala
@@ -333,14 +333,17 @@ public class MainWindow : Gtk.ApplicationWindow {
         }
     }
     
-    private void show_deck(Gtk.Widget relative_to, Gdk.Point? for_location, Toolkit.Deck deck) {
-        Toolkit.DeckWindow deck_window = new Toolkit.DeckWindow(relative_to, for_location, deck);
+    private void show_deck_window(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.dismiss.connect(() => {
+        deck_window.deck.dismiss.connect(() => {
             current_controller.unselect_all();
-            Toolkit.destroy_later(deck_window);
+            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.deck.failure.connect((msg) => {
@@ -348,6 +351,25 @@ public class MainWindow : Gtk.ApplicationWindow {
         });
         
         deck_window.show_all();
+        deck_window.run();
+        deck_window.destroy();
+    }
+    
+    private void show_deck_popover(Gtk.Widget relative_to, Gdk.Point? for_location, Toolkit.Deck deck) {
+        Toolkit.DeckPopover deck_popover = new Toolkit.DeckPopover(relative_to, for_location, deck);
+        
+        // when the popover closes, reset View.Controllable state (selection is maintained while
+        // use is viewing/editing interaction) and destroy widgets
+        deck_popover.dismiss.connect(() => {
+            current_controller.unselect_all();
+            Toolkit.destroy_later(deck_popover);
+        });
+        
+        deck_popover.deck.failure.connect((msg) => {
+            Application.instance.error_message(msg);
+        });
+        
+        deck_popover.show_all();
     }
     
     private void on_quick_create_event() {
@@ -420,29 +442,58 @@ public class MainWindow : Gtk.ApplicationWindow {
     private void quick_create_event(Component.Event? initial, Gtk.Widget relative_to, Gdk.Point? 
for_location) {
         QuickCreateEvent quick_create = new QuickCreateEvent();
         
-        CreateUpdateEvent create_update = new CreateUpdateEvent();
-        create_update.is_update = false;
-        
-        CreateUpdateRecurring create_update_recurring = new CreateUpdateRecurring();
-        
-        EventTimeSettings event_time_settings = new EventTimeSettings();
-        
         Toolkit.Deck deck = new Toolkit.Deck();
-        deck.add_cards(
-            iterate<Toolkit.Card>(quick_create, create_update, create_update_recurring, event_time_settings)
-            .to_array_list()
-        );
+        deck.add_cards(iterate<Toolkit.Card>(quick_create).to_array_list());
         
-        // initialize the Deck with the initial event (if any)
         deck.go_home(initial);
         
-        show_deck(relative_to, for_location, deck);
+        deck.dismiss.connect(() => {
+            if (quick_create.edit_required)
+                edit_event(quick_create.event);
+        });
+        
+        show_deck_popover(relative_to, for_location, deck);
     }
     
     private void on_request_display_event(Component.Event event, Gtk.Widget relative_to,
         Gdk.Point? for_location) {
         ShowEvent show_event = new ShowEvent();
         
+        Toolkit.Deck deck = new Toolkit.Deck();
+        deck.add_cards(iterate<Toolkit.Card>(show_event).to_array_list());
+        
+        deck.go_home(event);
+        
+        deck.dismiss.connect(() => {
+            if (!show_event.edit_requested)
+                return;
+                
+            // pass a clone of the existing event for editing
+            Component.Event clone;
+            try {
+                clone = event.clone() as Component.Event;
+            } catch (Error err) {
+                Application.instance.error_message(_("Unable to edit event: %s").printf(err.message));
+                
+                return;
+            }
+            
+            edit_event(clone);
+        });
+        
+        show_deck_popover(relative_to, for_location, deck);
+    }
+    
+    private void edit_event(Component.Event event) {
+        // use Idle loop so popovers have a chance to hide before bringing up DeckWindow
+        Idle.add(() => {
+            on_edit_event(event);
+            
+            return false;
+        }, Priority.LOW + 100);
+    }
+    
+    private void on_edit_event(Component.Event event) {
         CreateUpdateEvent create_update = new CreateUpdateEvent();
         create_update.is_update = true;
         
@@ -452,14 +503,14 @@ public class MainWindow : Gtk.ApplicationWindow {
         
         Toolkit.Deck deck = new Toolkit.Deck();
         deck.add_cards(
-            iterate<Toolkit.Card>(show_event, create_update, create_update_recurring, event_time_settings)
+            iterate<Toolkit.Card>(create_update, create_update_recurring, event_time_settings)
             .to_array_list()
         );
         
-        // "initialize" the Deck with the requested Event (because ShowEvent is first, it's home)
+        // "initialize" the Deck with the requested Event
         deck.go_home(event);
         
-        show_deck(relative_to, for_location, deck);
+        show_deck_window(deck);
     }
 }
 
diff --git a/src/host/host-quick-create-event.vala b/src/host/host-quick-create-event.vala
index 5d9c5a9..734a3a1 100644
--- a/src/host/host-quick-create-event.vala
+++ b/src/host/host-quick-create-event.vala
@@ -16,6 +16,8 @@ public class QuickCreateEvent : Gtk.Grid, Toolkit.Card {
     
     public new Component.Event? event { get; private set; default = null; }
     
+    public bool edit_required { get; private set; default = false; }
+    
     public Gtk.Widget? default_widget { get { return create_button; } }
     
     public Gtk.Widget? initial_focus { get { return details_entry; } }
@@ -142,11 +144,9 @@ public class QuickCreateEvent : Gtk.Grid, Toolkit.Card {
         if (!event.is_valid(false))
             event.set_event_date_span(Calendar.System.today.to_date_span());
         
-        // jump to Create/Update dialog and remove this Card from the Deck ... this ensures
-        // that if the user presses Cancel in the Create/Update dialog the Deck exits rather
-        // than returns here (via jump_home_or_user_closed())
-        jump_to_card_by_name(CreateUpdateEvent.ID, event);
-        deck.remove_cards(iterate<Toolkit.Card>(this).to_array_list());
+        edit_required = true;
+        
+        notify_user_closed();
     }
     
     private async void create_event_async(Cancellable? cancellable) {
diff --git a/src/host/host-show-event.vala b/src/host/host-show-event.vala
index 1a6270f..9a47ebc 100644
--- a/src/host/host-show-event.vala
+++ b/src/host/host-show-event.vala
@@ -25,6 +25,8 @@ public class ShowEvent : Gtk.Grid, Toolkit.Card {
     
     public Gtk.Widget? initial_focus { get { return close_button; } }
     
+    public bool edit_requested { get; private set; default = false; }
+    
     [GtkChild]
     private Gtk.Label summary_text;
     
@@ -115,6 +117,8 @@ public class ShowEvent : Gtk.Grid, Toolkit.Card {
         description_text.bind_property("no-show-all", description_text_window, "no-show-all",
             BindingFlags.SYNC_CREATE);
         
+        rotating_button_box.show_hide_family(FAMILY_REMOVING, event.is_generated_instance);
+        
         build_display();
     }
     
@@ -207,12 +211,9 @@ public class ShowEvent : Gtk.Grid, Toolkit.Card {
     }
     
     private void on_update_button_clicked() {
-        // pass a clone of the existing event for editing
-        try {
-            jump_to_card_by_name(CreateUpdateEvent.ID, event.clone() as Component.Event);
-        } catch (Error err) {
-            notify_failure(_("Unable to update event: %s").printf(err.message));
-        }
+        edit_requested = true;
+        
+        notify_user_closed();
     }
     
     private void on_close_button_clicked() {
diff --git a/src/manager/manager-window.vala b/src/manager/manager-window.vala
index a798243..65a5d96 100644
--- a/src/manager/manager-window.vala
+++ b/src/manager/manager-window.vala
@@ -13,20 +13,18 @@ namespace California.Manager {
 public class Window : Toolkit.DeckWindow {
     private CalendarList calendar_list = new CalendarList();
     
-    private Window(Gtk.Widget relative_to, Gdk.Point? for_location) {
-        base (relative_to, for_location, null);
+    private Window(Gtk.Window? window) {
+        base (window, null);
         
         deck.add_cards(iterate<Toolkit.Card>(calendar_list).to_array_list());
     }
     
-    public static void display(Gtk.Widget relative_to, Gdk.Point? for_location) {
-        Manager.Window instance = new Manager.Window(relative_to, for_location);
-        
-        instance.dismiss.connect(() => {
-            Toolkit.destroy_later(instance);
-        });
+    public static void display(Gtk.Window? window) {
+        Manager.Window instance = new Manager.Window(window);
         
         instance.show_all();
+        instance.run();
+        instance.destroy();
     }
     
     public override bool key_release_event(Gdk.EventKey event) {
diff --git a/src/toolkit/toolkit-deck-popover.vala b/src/toolkit/toolkit-deck-popover.vala
new file mode 100644
index 0000000..8cf0613
--- /dev/null
+++ b/src/toolkit/toolkit-deck-popover.vala
@@ -0,0 +1,102 @@
+/* Copyright 2014 Yorba Foundation
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later).  See the COPYING file in this distribution.
+ */
+
+namespace California.Toolkit {
+
+/**
+ * A GtkPopover with special support for { link Deck}s.
+ */
+
+public class DeckPopover : Gtk.Popover {
+    public Deck deck { get; private set; }
+    
+    /**
+     * See { link Card.dismiss}
+     */
+    public signal void dismiss(bool user_request, bool final);
+    
+    private bool preserve_mode;
+    private bool forcing_mode = false;
+    
+    public DeckPopover(Gtk.Widget rel_to, Gdk.Point? for_location, Deck? starter_deck) {
+        Object (relative_to: rel_to);
+        
+        preserve_mode = modal;
+        
+        // treat "closed" signal as dismissal by user request
+        closed.connect(() => {
+            dismiss(true, true);
+        });
+        
+        notify["modal"].connect(on_modal_changed);
+        
+        this.deck = starter_deck ?? new Deck();
+        
+        if (for_location != null) {
+            Gdk.Rectangle for_location_rect = Gdk.Rectangle() { x = for_location.x, y = for_location.y,
+                width = 1, height = 1 };
+            pointing_to = for_location_rect;
+        }
+        
+        // because adding/removing cards can cause deep in Gtk.Widget the Popover to lose focus,
+        // those operations can prematurely close the Popover.  Catching these signals allow for
+        // DeckWindow to go modeless during the operation and not close.  (RotatingButtonBox has a
+        // similar issue.)
+        //
+        // TODO: This is fixed in GTK+ 3.13.6.  When 3.14 is baseline requirement, this code can
+        // be removed.
+        deck.notify["transition-running"].connect(on_transition_running_changed);
+        deck.adding_removing_cards.connect(on_adding_removing_cards);
+        deck.added_removed_cards.connect(on_added_removed_cards);
+        deck.dismiss.connect(on_deck_dismissed);
+        
+        // store Deck in box so margin can be applied
+        Gtk.Box box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
+        box.margin = 4;
+        box.add(deck);
+        
+        add(box);
+    }
+    
+    ~DeckWindow() {
+        deck.notify["transition-running"].disconnect(on_transition_running_changed);
+        deck.adding_removing_cards.disconnect(on_adding_removing_cards);
+        deck.added_removed_cards.disconnect(on_added_removed_cards);
+        deck.dismiss.disconnect(on_deck_dismissed);
+    }
+    
+    // if the modal value changes, preserve it (unless it's changing because we're forcing it to
+    // go modal/modeless during transitions we're attempting to work around)
+    private void on_modal_changed() {
+        if (!forcing_mode)
+            preserve_mode = modal;
+    }
+    
+    private void force_mode(bool modal) {
+        forcing_mode = true;
+        this.modal = modal;
+        forcing_mode = false;
+    }
+    
+    private void on_transition_running_changed() {
+        force_mode(deck.transition_running ? false : preserve_mode);
+    }
+    
+    private void on_adding_removing_cards() {
+        force_mode(false);
+    }
+    
+    private void on_added_removed_cards() {
+        force_mode(preserve_mode);
+    }
+    
+    private void on_deck_dismissed(bool user_request, bool final) {
+        dismiss(user_request, final);
+    }
+}
+
+}
+
diff --git a/src/toolkit/toolkit-deck-window.vala b/src/toolkit/toolkit-deck-window.vala
index 60877ce..6c26f60 100644
--- a/src/toolkit/toolkit-deck-window.vala
+++ b/src/toolkit/toolkit-deck-window.vala
@@ -7,96 +7,54 @@
 namespace California.Toolkit {
 
 /**
- * A GtkPopover with special support for { link Deck}s.
+ * A GtkDialog with no visible action area that holds { link Deck}s.
  */
 
-public class DeckWindow : Gtk.Popover {
+public class DeckWindow : Gtk.Dialog {
     public Deck deck { get; private set; }
     
-    /**
-     * See { link Card.dismiss}
-     */
-    public signal void dismiss(bool user_request, bool final);
-    
-    private bool preserve_mode;
-    private bool forcing_mode = false;
-    
-    public DeckWindow(Gtk.Widget rel_to, Gdk.Point? for_location, Deck? starter_deck) {
-        Object (relative_to: rel_to);
-        
-        preserve_mode = modal;
-        
-        // treat "closed" signal as dismissal by user request
-        closed.connect(() => {
-            dismiss(true, true);
-        });
-        
-        notify["modal"].connect(on_modal_changed);
-        
+    public DeckWindow(Gtk.Window? parent, Deck? starter_deck) {
         this.deck = starter_deck ?? new Deck();
         
-        if (for_location != null) {
-            Gdk.Rectangle for_location_rect = Gdk.Rectangle() { x = for_location.x, y = for_location.y,
-                width = 1, height = 1 };
-            pointing_to = for_location_rect;
-        }
+        transient_for = parent;
+        modal = true;
+        resizable = false;
         
-        // because adding/removing cards can cause deep in Gtk.Widget the Popover to lose focus,
-        // those operations can prematurely close the Popover.  Catching these signals allow for
-        // DeckWindow to go modeless during the operation and not close.  (RotatingButtonBox has a
-        // similar issue.)
-        //
-        // TODO: This is fixed in GTK+ 3.13.6.  When 3.14 is baseline requirement, this code can
-        // be removed.
-        deck.notify["transition-running"].connect(on_transition_running_changed);
-        deck.adding_removing_cards.connect(on_adding_removing_cards);
-        deck.added_removed_cards.connect(on_added_removed_cards);
         deck.dismiss.connect(on_deck_dismissed);
+        deck.success.connect(on_deck_success);
+        deck.failure.connect(on_deck_failure);
         
-        // store Deck in box so margin can be applied
-        Gtk.Box box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
-        box.margin = 4;
-        box.add(deck);
+        Gtk.Box content_area = (Gtk.Box) get_content_area();
+        content_area.margin = 8;
+        content_area.add(deck);
         
-        add(box);
+        get_action_area().visible = false;
+        get_action_area().no_show_all = true;
     }
     
     ~DeckWindow() {
-        deck.notify["transition-running"].disconnect(on_transition_running_changed);
-        deck.adding_removing_cards.disconnect(on_adding_removing_cards);
-        deck.added_removed_cards.disconnect(on_added_removed_cards);
         deck.dismiss.disconnect(on_deck_dismissed);
+        deck.success.disconnect(on_deck_success);
+        deck.failure.disconnect(on_deck_failure);
     }
     
-    // if the modal value changes, preserve it (unless it's changing because we're forcing it to
-    // go modal/modeless during transitions we're attempting to work around)
-    private void on_modal_changed() {
-        if (!forcing_mode)
-            preserve_mode = modal;
-    }
-    
-    private void force_mode(bool modal) {
-        forcing_mode = true;
-        this.modal = modal;
-        forcing_mode = false;
-    }
-    
-    private void on_transition_running_changed() {
-        force_mode(deck.transition_running ? false : preserve_mode);
-    }
-    
-    private void on_adding_removing_cards() {
-        force_mode(false);
+    private void on_deck_dismissed(bool user_request, bool final) {
+        if (final)
+            response(Gtk.ResponseType.CLOSE);
     }
     
-    private void on_added_removed_cards() {
-        force_mode(preserve_mode);
+    private void on_deck_success() {
+        response(Gtk.ResponseType.OK);
     }
     
-    private void on_deck_dismissed(bool user_request, bool final) {
-        dismiss(user_request, final);
+    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-rotating-button-box.vala b/src/toolkit/toolkit-rotating-button-box.vala
index 3cf1e9f..bb3dad9 100644
--- a/src/toolkit/toolkit-rotating-button-box.vala
+++ b/src/toolkit/toolkit-rotating-button-box.vala
@@ -109,6 +109,34 @@ public class RotatingButtonBox : Gtk.Stack {
         
         return button_box;
     }
+    
+    /**
+     * Removes (or adds back) a family from the { link RotatingButtonBox}.
+     *
+     * The family remains under the RotatingButtonBox's control, it's simply removed from the
+     * widget heirarchy.  This is useful for sizing purposes.
+     */
+    public void show_hide_family(string family, bool show) {
+        if (!button_boxes.has_key(family))
+            return;
+        
+        Gtk.ButtonBox button_box = button_boxes.get(family);
+        
+        bool shown = false;
+        foreach (Gtk.Widget widget in  get_children()) {
+            if (widget == button_box) {
+                shown = true;
+                
+                break;
+            }
+        }
+        
+        if (show && !shown) {
+            add_named(button_box, family);
+        } else if (shown) {
+            remove(button_box);
+        }
+    }
 }
 
 }


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