[gtk+] popover: Track toplevel focus changes



commit dcba77fcc454229079049f41f4ddc23e5c27353f
Author: Carlos Garnacho <carlosg gnome org>
Date:   Thu Mar 6 14:50:35 2014 +0100

    popover: Track toplevel focus changes
    
    Make the popover temporarily undo the GTK+ grab, so it remains modal
    to its window, but does not attempt to steal focus on other non-modal
    windows that get the focus.
    
    This was most confusing with keyboard navigation, as the focus would
    remain stuck on the popover, and not move to the newly focused window
    after the popover was dismissed. It didn't have as much effect on
    pointer operations as only the first click would be consumed in order
    to hide the popover.

 gtk/gtkpopover.c |   53 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 53 insertions(+), 0 deletions(-)
---
diff --git a/gtk/gtkpopover.c b/gtk/gtkpopover.c
index 1cc9e9c..2286cff 100644
--- a/gtk/gtkpopover.c
+++ b/gtk/gtkpopover.c
@@ -102,6 +102,7 @@ struct _GtkPopoverPrivate
   guint modal              : 1;
   guint button_pressed     : 1;
   guint apply_shape        : 1;
+  guint grab_notify_blocked : 1;
 };
 
 static GQuark quark_widget_popovers = 0;
@@ -254,6 +255,52 @@ gtk_popover_realize (GtkWidget *widget)
   gtk_widget_set_realized (widget, TRUE);
 }
 
+static gboolean
+window_focus_in (GtkWidget  *widget,
+                 GdkEvent   *event,
+                 GtkPopover *popover)
+{
+  GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover);
+
+  /* Regain the grab when the window is focused */
+  if (priv->modal &&
+      gtk_widget_is_drawable (GTK_WIDGET (popover)))
+    {
+      GtkWidget *focus;
+
+      gtk_grab_add (GTK_WIDGET (popover));
+
+      focus = gtk_window_get_focus (GTK_WINDOW (widget));
+
+      if (!gtk_widget_is_ancestor (focus, GTK_WIDGET (popover)))
+        gtk_widget_grab_focus (GTK_WIDGET (popover));
+
+      if (priv->grab_notify_blocked)
+        g_signal_handler_unblock (priv->widget, priv->grab_notify_id);
+
+      priv->grab_notify_blocked = FALSE;
+    }
+  return FALSE;
+}
+
+static gboolean
+window_focus_out (GtkWidget  *widget,
+                  GdkEvent   *event,
+                  GtkPopover *popover)
+{
+  GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover);
+
+  /* Temporarily remove the grab when unfocused */
+  if (priv->modal &&
+      gtk_widget_is_drawable (GTK_WIDGET (popover)))
+    {
+      g_signal_handler_block (priv->widget, priv->grab_notify_id);
+      gtk_grab_remove (GTK_WIDGET (popover));
+      priv->grab_notify_blocked = TRUE;
+    }
+  return FALSE;
+}
+
 static void
 gtk_popover_apply_modality (GtkPopover *popover,
                             gboolean    modal)
@@ -270,9 +317,15 @@ gtk_popover_apply_modality (GtkPopover *popover,
         g_object_ref (prev_focus);
       gtk_grab_add (GTK_WIDGET (popover));
       gtk_widget_grab_focus (GTK_WIDGET (popover));
+
+      g_signal_connect (priv->window, "focus-in-event",
+                        G_CALLBACK (window_focus_in), popover);
+      g_signal_connect (priv->window, "focus-out-event",
+                        G_CALLBACK (window_focus_out), popover);
     }
   else
     {
+      g_signal_handlers_disconnect_by_data (priv->window, popover);
       gtk_grab_remove (GTK_WIDGET (popover));
 
       if (priv->prev_focus_widget)


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