[gtk/nested-popover-menu: 4/5] Add a timeout for open submenus



commit 3cbf1845a97690f6c006fc839442c6dc909e4959
Author: Matthias Clasen <mclasen redhat com>
Date:   Sun Sep 8 18:45:58 2019 -0400

    Add a timeout for open submenus
    
    When a popover menu has an open submenu,
    delay activating another item until after
    the pointer is stationary for a little
    while. This avoids the need for precise
    horizontal motion when moving towards the
    submenu.

 gtk/gtkmodelbutton.c | 83 ++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 68 insertions(+), 15 deletions(-)
---
diff --git a/gtk/gtkmodelbutton.c b/gtk/gtkmodelbutton.c
index 98d70e2226..a2ae000df2 100644
--- a/gtk/gtkmodelbutton.c
+++ b/gtk/gtkmodelbutton.c
@@ -171,6 +171,7 @@ struct _GtkModelButton
   GtkButtonRole role;
   GtkSizeGroup *indicators;
   char *accel;
+  guint open_timeout;
 };
 
 typedef GtkButtonClass GtkModelButtonClass;
@@ -935,8 +936,6 @@ close_menu (GtkModelButton *button)
     }
 }
 
-static void open_submenu (GtkPopover *popover);
-
 static void
 gtk_model_button_clicked (GtkButton *button)
 {
@@ -973,6 +972,9 @@ gtk_model_button_finalize (GObject *object)
   g_free (button->accel);
   g_clear_pointer (&button->popover, gtk_widget_unparent);
 
+  if (button->open_timeout)
+    g_source_remove (button->open_timeout);
+
   G_OBJECT_CLASS (gtk_model_button_parent_class)->finalize (object);
 }
 
@@ -1226,31 +1228,60 @@ close_submenus (GtkPopover *popover)
     }
 }
 
-static void
-open_submenu (GtkPopover *popover)
+static gboolean
+open_submenu (gpointer data)
 {
+  GtkModelButton *button = data;
+  GtkPopover *popover;
+
+  popover = (GtkPopover*)gtk_widget_get_ancestor (GTK_WIDGET (button), GTK_TYPE_POPOVER);
+
   if (GTK_IS_POPOVER_MENU (popover))
     {
-      GtkWidget *active_item;
+      gtk_popover_menu_set_active_item (GTK_POPOVER_MENU (popover), GTK_WIDGET (button));
 
-      active_item = gtk_popover_menu_get_active_item (GTK_POPOVER_MENU (popover));
-      if (GTK_IS_MODEL_BUTTON (active_item) &&
-          GTK_MODEL_BUTTON (active_item)->popover)
+      if (button->popover)
         {
-          GtkWidget *submenu;
+          GtkWidget *submenu = button->popover;
 
-          submenu = GTK_MODEL_BUTTON (active_item)->popover;
           if (gtk_popover_menu_get_open_submenu (GTK_POPOVER_MENU (popover)) != submenu)
-{
-g_print ("close submenus %p %p\n", gtk_popover_menu_get_open_submenu (GTK_POPOVER_MENU (popover)), submenu);
             close_submenus (popover);
-}
 
           gtk_popover_popup (GTK_POPOVER (submenu));
           gtk_popover_menu_set_open_submenu (GTK_POPOVER_MENU (popover), submenu);
           gtk_popover_menu_set_parent_menu (GTK_POPOVER_MENU (submenu), GTK_WIDGET (popover));
         }
     }
+
+  button->open_timeout = 0;
+
+  return G_SOURCE_REMOVE;
+}
+
+#define OPEN_TIMEOUT 80
+
+static void
+start_open (GtkModelButton *button)
+{
+  if (button->open_timeout)
+    g_source_remove (button->open_timeout);
+
+  if (button->popover &&
+      gtk_widget_get_visible (button->popover))
+    return;
+
+  button->open_timeout = g_timeout_add (OPEN_TIMEOUT, open_submenu, button);
+  g_source_set_name_by_id (button->open_timeout, "[gtk] open_submenu");
+}
+
+static void
+stop_open (GtkModelButton *button)
+{
+  if (button->open_timeout)
+    {
+      g_source_remove (button->open_timeout);
+      button->open_timeout = 0;
+    }
 }
 
 static void
@@ -1276,11 +1307,31 @@ enter_cb (GtkEventController *controller,
 
   if (popover && (is || contains))
     {
-      gtk_popover_menu_set_active_item (GTK_POPOVER_MENU (popover), target);
-      open_submenu (GTK_POPOVER (popover));
+      if (gtk_popover_menu_get_open_submenu (GTK_POPOVER_MENU (popover)) != NULL)
+        start_open (GTK_MODEL_BUTTON (target));
+      else
+        open_submenu (target);
     }
 }
 
+static void
+motion_cb (GtkEventController *controller,
+           double              x,
+           double              y,
+           gpointer            data)
+{
+  start_open (GTK_MODEL_BUTTON (data));
+}
+
+static void
+leave_cb (GtkEventController *controller,
+          GdkCrossingMode     mode,
+          GdkNotifyType       type,
+          gpointer            data)
+{
+  stop_open (GTK_MODEL_BUTTON (data));
+}
+
 static void
 focus_in_cb (GtkEventController *controller,
              GdkCrossingMode     mode,
@@ -1336,6 +1387,8 @@ gtk_model_button_init (GtkModelButton *button)
 
   controller = gtk_event_controller_motion_new ();
   g_signal_connect (controller, "enter", G_CALLBACK (enter_cb), button);
+  g_signal_connect (controller, "motion", G_CALLBACK (motion_cb), button);
+  g_signal_connect (controller, "leave", G_CALLBACK (leave_cb), button);
   gtk_widget_add_controller (GTK_WIDGET (button), controller);
 
   controller = gtk_event_controller_key_new ();


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