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.
This commit is contained in:
Matthias Clasen
2019-09-08 18:45:58 -04:00
parent ea44eade21
commit 3cbf1845a9

View File

@@ -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 ();