[california/wip/732029-gtk-312: 2/3] Final touches and some basic fixes for Gtk.Popover use



commit e152c08286172c3ac704e62f320ec79260320f37
Author: Jim Nelson <jim yorba org>
Date:   Fri Jul 18 16:02:27 2014 -0700

    Final touches and some basic fixes for Gtk.Popover use

 src/activator/activator-window.vala          |    5 ++++
 src/collection/collection-iterable.vala      |    4 ++-
 src/host/host-main-window.vala               |    6 +----
 src/host/host-show-event.vala                |    4 +-
 src/manager/manager-window.vala              |    5 ++++
 src/toolkit/toolkit-deck-window.vala         |   22 +++++++------------
 src/toolkit/toolkit-rotating-button-box.vala |   23 ++++++++++++++++++++
 src/toolkit/toolkit.vala                     |   29 ++++++++++++++++++++++++++
 8 files changed, 76 insertions(+), 22 deletions(-)
---
diff --git a/src/activator/activator-window.vala b/src/activator/activator-window.vala
index 25158b6..95ff428 100644
--- a/src/activator/activator-window.vala
+++ b/src/activator/activator-window.vala
@@ -26,6 +26,11 @@ public class Window : Toolkit.DeckWindow {
     
     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);
+        });
+        
         instance.show_all();
     }
 }
diff --git a/src/collection/collection-iterable.vala b/src/collection/collection-iterable.vala
index 5648456..cc20101 100644
--- a/src/collection/collection-iterable.vala
+++ b/src/collection/collection-iterable.vala
@@ -22,7 +22,9 @@ public California.Iterable<G> traverse<G>(Gee.Iterable<G>? gee_iterable) {
  * it.
  *
  * "Safe iteration" means later operations that remove elements while iterating do not cause an
- * assertion.
+ * assertion.  This involves creating a copy of the supplied Gee.Iterable, meaning that any changes
+ * made in subsequence operations (i.e. { link California.Iterable.filter} are not reflected in
+ * the passed-in collection.
  *
  * An empty Gee.Iterable is created and used if null is passed in.
  */
diff --git a/src/host/host-main-window.vala b/src/host/host-main-window.vala
index f96f325..fd55705 100644
--- a/src/host/host-main-window.vala
+++ b/src/host/host-main-window.vala
@@ -339,12 +339,8 @@ public class MainWindow : Gtk.ApplicationWindow {
         // when the dialog closes, reset View.Controllable state (selection is maintained while
         // use is viewing/editing interaction) and destroy widgets
         deck_window.dismiss.connect(() => {
-            debug("dismissed");
             current_controller.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();
+            Toolkit.destroy_later(deck_window);
         });
         
         deck_window.deck.failure.connect((msg) => {
diff --git a/src/host/host-show-event.vala b/src/host/host-show-event.vala
index ebd1bc1..e406dd5 100644
--- a/src/host/host-show-event.vala
+++ b/src/host/host-show-event.vala
@@ -131,10 +131,10 @@ public class ShowEvent : Gtk.Grid, Toolkit.Card {
         bool read_only = event.calendar_source != null && event.calendar_source.read_only;
         
         update_button.visible = !read_only;
-        update_button.no_show_all = !read_only;
+        update_button.no_show_all = read_only;
         
         remove_button.visible = !read_only;
-        remove_button.no_show_all = !read_only;
+        remove_button.no_show_all = read_only;
     }
     
     private string? escape(string? plain) {
diff --git a/src/manager/manager-window.vala b/src/manager/manager-window.vala
index 4c1a440..a798243 100644
--- a/src/manager/manager-window.vala
+++ b/src/manager/manager-window.vala
@@ -21,6 +21,11 @@ public class Window : Toolkit.DeckWindow {
     
     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);
+        });
+        
         instance.show_all();
     }
     
diff --git a/src/toolkit/toolkit-deck-window.vala b/src/toolkit/toolkit-deck-window.vala
index f1d9d8f..80b2d41 100644
--- a/src/toolkit/toolkit-deck-window.vala
+++ b/src/toolkit/toolkit-deck-window.vala
@@ -7,26 +7,24 @@
 namespace California.Toolkit {
 
 /**
- * A GtkDialog with no visible action area.
- *
- * This is designed for UI panes that want to control their own interaction with the user (in
- * particular, button placement) but need all the benefits interaction-wise of GtkDialog.
- *
- * It's expected this will go away when we move to GTK+ 3.12 and can use GtkPopovers for these
- * interactions.
+ * A GtkPopover with special support for { link Deck}s.
  */
 
 public class DeckWindow : Gtk.Popover {
     public Deck deck { get; private set; }
     
+    /**
+     * See { link Card.dismiss}
+     */
     public signal void dismiss(bool user_request, bool final);
     
     public DeckWindow(Gtk.Widget rel_to, Gdk.Point? for_location, Deck? starter_deck) {
         Object (relative_to: rel_to);
         
-        // Toolkit.RotatingButtonBox requires DeckWindow not be modal because when rotating the
-        // buttons something occurs (probably a focus switch) that causes it to dismiss
-        modal = false;
+        // treat "closed" signal as dismissal by user request
+        closed.connect(() => {
+            dismiss(true, true);
+        });
         
         this.deck = starter_deck ?? new Deck();
         
@@ -48,14 +46,10 @@ public class DeckWindow : Gtk.Popover {
     
     ~DeckWindow() {
         deck.dismiss.disconnect(on_deck_dismissed);
-        debug("CTOR");
     }
     
     private void on_deck_dismissed(bool user_request, bool final) {
-        debug("deck dismissed");
         dismiss(user_request, final);
-        if (final)
-            destroy();
     }
 }
 
diff --git a/src/toolkit/toolkit-rotating-button-box.vala b/src/toolkit/toolkit-rotating-button-box.vala
index 13778f7..716463d 100644
--- a/src/toolkit/toolkit-rotating-button-box.vala
+++ b/src/toolkit/toolkit-rotating-button-box.vala
@@ -33,16 +33,39 @@ public class RotatingButtonBox : Gtk.Stack {
     public string? family { get; set; }
     
     private Gee.HashMap<string, Gtk.ButtonBox> button_boxes = new Gee.HashMap<string, Gtk.ButtonBox>();
+    private Gtk.Popover? parent_popover = null;
+    private bool parent_popover_modal = false;
     
     public RotatingButtonBox() {
         homogeneous = true;
         transition_duration = SLOW_STACK_TRANSITION_DURATION_MSEC;
         transition_type = Gtk.StackTransitionType.SLIDE_LEFT_RIGHT;
         
+        notify["transition-running"].connect(on_transition_running);
+        
         bind_property("visible-child-name", this, PROP_FAMILY,
             BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
     }
     
+    // unfortunately, RotatingButtonBox can cause modal Popovers to close because the focus
+    // changes from one button box to another, triggering a situation in GtkWidget where the
+    // Popover thinks it has lost focus ... this hacks around the problem by setting the popover
+    // to modeless until the transition is complete
+    private void on_transition_running() {
+        if (transition_running && parent_popover == null) {
+            // set to modeless to hack around problem
+            parent_popover = get_ancestor(typeof (Gtk.Popover)) as Gtk.Popover;
+            if (parent_popover != null) {
+                parent_popover_modal = parent_popover.modal;
+                parent_popover.modal = false;
+            }
+        } else if (!transition_running && parent_popover != null) {
+            // reset to original mode
+            parent_popover.modal = parent_popover_modal;
+            parent_popover = null;
+        }
+    }
+    
     /**
      * Pack a Gtk.Button at the start of a particular family, creating the family if necessary.
      *
diff --git a/src/toolkit/toolkit.vala b/src/toolkit/toolkit.vala
index 2630ed5..ca38290 100644
--- a/src/toolkit/toolkit.vala
+++ b/src/toolkit/toolkit.vala
@@ -33,19 +33,28 @@ public const bool STOP = true;
 public const bool PROPAGATE = false;
 
 private int init_count = 0;
+private Gee.Set<Gtk.Widget>? dead_pool = null;
+private Scheduled? dead_pool_gc = null;
 
 public void init() throws Error {
     if (!Unit.do_init(ref init_count))
         return;
     
+    dead_pool = new Gee.HashSet<Gtk.Widget>();
+    
     Calendar.init();
+    Collection.init();
 }
 
 public void terminate() {
     if (!Unit.do_terminate(ref init_count))
         return;
     
+    Collection.terminate();
     Calendar.terminate();
+    
+    dead_pool = null;
+    dead_pool_gc = null;
 }
 
 /**
@@ -94,4 +103,24 @@ public void set_unbusy(Gtk.Widget widget, Gdk.Cursor? unbusy_cursor) {
     toplevel.get_window().set_cursor(unbusy_cursor);
 }
 
+/**
+ * Destroy a Gtk.Widget when the event loop is idle.
+ */
+public void destroy_later(Gtk.Widget widget) {
+    if (!dead_pool.add(widget))
+        return;
+    
+    // always reschedule
+    dead_pool_gc = new Scheduled.once_at_idle(() => {
+        // traverse_safely makes a copy of the dead_pool, so filter() won't work to remove destroyed
+        // elements, but that also means we can safely remove elements from it in the iterate()
+        // handler ... need to jump through these hoops because the widget.destroy() signal handlers
+        // may turn around and add more widgets to the dead pool during traversal
+        traverse_safely<Gtk.Widget>(dead_pool).iterate((widget) => {
+            widget.destroy();
+            dead_pool.remove(widget);
+        });
+    }, Priority.LOW);
+}
+
 }


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