[gtk/focus-ring-experiment: 30/30] window: Tweak visible focus behavior



commit 6e1a310c5d563553dcf6f4c3b041e89b1cfbc59b
Author: Matthias Clasen <mclasen redhat com>
Date:   Tue Apr 21 18:14:40 2020 -0400

    window: Tweak visible focus behavior
    
    Only turn on visible focus when a key event actually leads
    to a change in focus location (ie, 'keynav').
    
    Make the visible focus disappear after 5 seconds of no
    keyboard interaction, to avoid permanent focus ring
    distraction.
    
    As an extra bonus, make it so that we make the focus
    visible while the Alt key is pressed. This gives us
    a 'find my focus!' shortcut, and goes well with the
    prexisting use of Alt for finding mnemonics.
    
    Discussed in: #2644

 gtk/gtkwindow.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 81 insertions(+), 5 deletions(-)
---
diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
index 9d87e68d35..194d0ef0c4 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -191,12 +191,15 @@ typedef struct
 
   guint    mnemonics_display_timeout_id;
 
+  guint    focus_visible_timeout;
+
   gint     scale;
 
   gint title_height;
   GtkWidget *title_box;
   GtkWidget *titlebar;
   GtkWidget *popup_menu;
+  GtkWidget *key_press_focus;
 
   GdkMonitor *initial_fullscreen_monitor;
   guint      edge_constraints;
@@ -4068,6 +4071,12 @@ gtk_window_finalize (GObject *object)
       priv->mnemonics_display_timeout_id = 0;
     }
 
+  if (priv->focus_visible_timeout)
+    {
+      g_source_remove (priv->focus_visible_timeout);
+      priv->focus_visible_timeout = 0;
+    }
+
   g_clear_object (&priv->constraint_solver);
   g_clear_object (&priv->renderer);
   g_clear_object (&priv->resize_cursor);
@@ -5362,6 +5371,36 @@ update_mnemonics_visible (GtkWindow       *window,
     }
 }
 
+static void
+update_focus_visible (GtkWindow       *window,
+                      guint            keyval,
+                      GdkModifierType  state,
+                      gboolean         visible)
+{
+  GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
+
+  if (visible)
+    {
+      if (priv->focus_visible)
+        priv->key_press_focus = NULL;
+      else
+        priv->key_press_focus = priv->focus_widget;
+
+      if ((keyval == GDK_KEY_Alt_L || keyval == GDK_KEY_Alt_R) &&
+               ((state & (gtk_accelerator_get_default_mod_mask ()) & ~(GDK_ALT_MASK)) == 0))
+        gtk_window_set_focus_visible (window, TRUE);
+    }
+  else
+    {
+      if (priv->key_press_focus == priv->focus_widget)
+        gtk_window_set_focus_visible (window, FALSE);
+      else
+        gtk_window_set_focus_visible (window, TRUE);
+
+      priv->key_press_focus = NULL;
+    }
+}
+
 static gboolean
 gtk_window_key_pressed (GtkWidget       *widget,
                         guint            keyval,
@@ -5371,8 +5410,7 @@ gtk_window_key_pressed (GtkWidget       *widget,
 {
   GtkWindow *window = GTK_WINDOW (widget);
 
-  gtk_window_set_focus_visible (window, TRUE);
-
+  update_focus_visible (window, keyval, state, TRUE);
   update_mnemonics_visible (window, keyval, state, TRUE);
 
   return FALSE;
@@ -5387,6 +5425,7 @@ gtk_window_key_released (GtkWidget       *widget,
 {
   GtkWindow *window = GTK_WINDOW (widget);
 
+  update_focus_visible (window, keyval, state, FALSE);
   update_mnemonics_visible (window, keyval, state, FALSE);
 
   return FALSE;
@@ -7406,6 +7445,19 @@ gtk_window_get_focus_visible (GtkWindow *window)
   return priv->focus_visible;
 }
 
+static gboolean
+unset_focus_visible (gpointer data)
+{
+  GtkWindow *window = data;
+  GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
+
+  priv->focus_visible_timeout = 0;
+
+  gtk_window_set_focus_visible (window, FALSE);
+
+  return G_SOURCE_REMOVE;
+}
+
 /**
  * gtk_window_set_focus_visible:
  * @window: a #GtkWindow
@@ -7417,15 +7469,39 @@ void
 gtk_window_set_focus_visible (GtkWindow *window,
                               gboolean   setting)
 {
+  gboolean changed;
+
   GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
 
   g_return_if_fail (GTK_IS_WINDOW (window));
 
-  setting = setting != FALSE;
+  changed = priv->focus_visible != setting;
 
-  if (priv->focus_visible != setting)
+  priv->focus_visible = setting;
+
+  if (priv->focus_visible_timeout)
+    {
+      g_source_remove (priv->focus_visible_timeout);
+      priv->focus_visible_timeout = 0;
+    }
+
+  if (priv->focus_visible)
+    priv->focus_visible_timeout = g_timeout_add_seconds (5, unset_focus_visible, window);
+
+  if (changed)
     {
-      priv->focus_visible = setting;
+      if (priv->focus_widget)
+        {
+          GtkWidget *widget;
+
+          for (widget = priv->focus_widget; widget; widget = gtk_widget_get_parent (widget))
+            {
+              if (priv->focus_visible)
+                gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_FOCUS_VISIBLE, FALSE);
+              else
+                gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_FOCUS_VISIBLE);
+            }
+        }
       g_object_notify_by_pspec (G_OBJECT (window), window_props[PROP_FOCUS_VISIBLE]);
     }
 }


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