[gtk] popover menu: Unify hover and focus



commit cbc0a8447d12aee306d2c2e56a53527384142240
Author: Matthias Clasen <mclasen redhat com>
Date:   Thu Jun 13 00:13:21 2019 +0000

    popover menu: Unify hover and focus
    
    Menus traditionally don't have separate
    hover and focus locations. Make the same
    change here that we already did for
    popover menubars: Track the active item
    and set its selected state. Both keynav
    and mouse change the active item.

 gtk/gtkmodelbutton.c        | 79 ++++++++++++++++++++++++++++++++++++++++++++-
 gtk/gtkpopovermenu.c        | 23 +++++++++++++
 gtk/gtkpopovermenuprivate.h | 30 +++++++++++++++++
 3 files changed, 131 insertions(+), 1 deletion(-)
---
diff --git a/gtk/gtkmodelbutton.c b/gtk/gtkmodelbutton.c
index 57c333536b..80552ec33b 100644
--- a/gtk/gtkmodelbutton.c
+++ b/gtk/gtkmodelbutton.c
@@ -31,7 +31,7 @@
 #include "gtkstylecontext.h"
 #include "gtktypebuiltins.h"
 #include "gtkstack.h"
-#include "gtkpopover.h"
+#include "gtkpopovermenuprivate.h"
 #include "gtkintl.h"
 #include "gtkcssnodeprivate.h"
 #include "gtkcsstypesprivate.h"
@@ -41,6 +41,8 @@
 #include "gtksizegroup.h"
 #include "gtkaccellabelprivate.h"
 #include "gtkactionable.h"
+#include "gtkeventcontrollermotion.h"
+#include "gtkeventcontrollerkey.h"
 
 /**
  * SECTION:gtkmodelbutton
@@ -1127,9 +1129,75 @@ gtk_model_button_class_init (GtkModelButtonClass *class)
   gtk_widget_class_set_css_name (GTK_WIDGET_CLASS (class), I_("modelbutton"));
 }
 
+static void
+enter_cb (GtkEventController *controller,
+          double              x,
+          double              y,
+          GdkCrossingMode     mode,
+          GdkNotifyType       type,
+          gpointer            data)
+{
+  GtkWidget *target;
+  GtkWidget *popover;
+  gboolean is;
+  gboolean contains;
+
+  target = gtk_event_controller_get_widget (controller);
+  popover = gtk_widget_get_ancestor (target, GTK_TYPE_POPOVER_MENU);
+
+  g_object_get (controller,
+                "is-pointer-focus", &is,
+                "contains-pointer-focus", &contains,
+                NULL);
+
+  if (popover && (is || contains))
+    gtk_popover_menu_set_active_item (GTK_POPOVER_MENU (popover), target);
+}
+
+static void
+leave_cb (GtkEventController *controller,
+          GdkCrossingMode     mode,
+          GdkNotifyType       type,
+          gpointer            data)
+{
+  GtkWidget *target;
+  GtkWidget *popover;
+  gboolean is;
+  gboolean contains;
+
+  target = gtk_event_controller_get_widget (controller);
+  popover = gtk_widget_get_ancestor (target, GTK_TYPE_POPOVER_MENU);
+
+  g_object_get (controller,
+                "is-pointer-focus", &is,
+                "contains-pointer-focus", &contains,
+                NULL);
+
+  if (popover && !(is || contains))
+    gtk_popover_menu_set_active_item (GTK_POPOVER_MENU (popover), NULL);
+}
+
+static void
+focus_in_cb (GtkEventController *controller,
+             GdkCrossingMode     mode,
+             GdkNotifyType       type,
+             gpointer            data)
+{
+  GtkWidget *target;
+  GtkWidget *popover;
+
+  target = gtk_event_controller_get_widget (controller);
+  popover = gtk_widget_get_ancestor (target, GTK_TYPE_POPOVER_MENU);
+
+  if (popover)
+    gtk_popover_menu_set_active_item (GTK_POPOVER_MENU (popover), target);
+}
+
 static void
 gtk_model_button_init (GtkModelButton *button)
 {
+  GtkEventController *controller;
+
   button->role = GTK_BUTTON_ROLE_NORMAL;
   gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
   button->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
@@ -1161,6 +1229,15 @@ gtk_model_button_init (GtkModelButton *button)
   gtk_widget_hide (button->start_indicator);
   gtk_widget_hide (button->end_indicator);
   update_node_ordering (button);
+
+  controller = gtk_event_controller_motion_new ();
+  g_signal_connect (controller, "enter", G_CALLBACK (enter_cb), NULL);
+  g_signal_connect (controller, "leave", G_CALLBACK (leave_cb), NULL);
+  gtk_widget_add_controller (GTK_WIDGET (button), controller);
+
+  controller = gtk_event_controller_key_new ();
+  g_signal_connect (controller, "focus-in", G_CALLBACK (focus_in_cb), NULL);
+  gtk_widget_add_controller (GTK_WIDGET (button), controller);
 }
 
 /**
diff --git a/gtk/gtkpopovermenu.c b/gtk/gtkpopovermenu.c
index 93a0ba8953..856b017cb5 100644
--- a/gtk/gtkpopovermenu.c
+++ b/gtk/gtkpopovermenu.c
@@ -17,6 +17,8 @@
 
 #include "config.h"
 #include "gtkpopovermenu.h"
+#include "gtkpopovermenuprivate.h"
+
 #include "gtkstack.h"
 #include "gtkstylecontext.h"
 #include "gtkintl.h"
@@ -120,6 +122,8 @@ typedef struct _GtkPopoverMenuClass GtkPopoverMenuClass;
 struct _GtkPopoverMenu
 {
   GtkPopover parent_instance;
+
+  GtkWidget *active_item;
 };
 
 struct _GtkPopoverMenuClass
@@ -133,6 +137,25 @@ enum {
 
 G_DEFINE_TYPE (GtkPopoverMenu, gtk_popover_menu, GTK_TYPE_POPOVER)
 
+void
+gtk_popover_menu_set_active_item (GtkPopoverMenu *menu,
+                                  GtkWidget      *item)
+{
+  if (menu->active_item != item)
+    {
+      if (menu->active_item)
+        gtk_widget_unset_state_flags (menu->active_item, GTK_STATE_FLAG_SELECTED);
+
+      menu->active_item = item;
+
+      if (menu->active_item)
+        {
+          gtk_widget_set_state_flags (menu->active_item, GTK_STATE_FLAG_SELECTED, FALSE);
+          gtk_widget_grab_focus (menu->active_item);
+       }
+    }
+}
+
 static void
 visible_submenu_changed (GObject        *object,
                          GParamSpec     *pspec,
diff --git a/gtk/gtkpopovermenuprivate.h b/gtk/gtkpopovermenuprivate.h
new file mode 100644
index 0000000000..024d20c91c
--- /dev/null
+++ b/gtk/gtkpopovermenuprivate.h
@@ -0,0 +1,30 @@
+/* GTK - The GIMP Toolkit
+ * Copyright © 2019 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GTK_POPOVER_MENU_PRIVATE_H__
+#define __GTK_POPOVER_MENU_PRIVATE_H__
+
+#include "gtkpopovermenu.h"
+
+G_BEGIN_DECLS
+
+void gtk_popover_menu_set_active_item (GtkPopoverMenu *popover,
+                                       GtkWidget      *item);
+
+G_END_DECLS
+
+#endif /* __GTK_POPOVER_PRIVATE_H__ */


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