From: Matthias Clasen Date: Tue, 24 Mar 2020 16:45:43 +0000 (-0400) Subject: popover: Implement auto mnemonics X-Git-Tag: archive/raspbian/4.4.1+ds1-2+rpi1^2~18^2~19^2~32^2~7 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=642503afb4e9ad726237fb2241c8df3288a2da8e;p=gtk4.git popover: Implement auto mnemonics Unfortunately, this involves copying a bunch of code from gtkwindow.c. The only difference here is that we add a private method to turn this off, which will be used by GtkPopoverMenu to implement its own auto mnemonics. --- diff --git a/gtk/gtkpopover.c b/gtk/gtkpopover.c index 88426dd7a7..1c90dc2af9 100644 --- a/gtk/gtkpopover.c +++ b/gtk/gtkpopover.c @@ -102,6 +102,7 @@ #include "gtknative.h" #include "gtkwidgetprivate.h" #include "gtkeventcontrollerkey.h" +#include "gtkeventcontrollerfocus.h" #include "gtkcssnodeprivate.h" #include "gtkbinlayout.h" #include "gtkenums.h" @@ -130,6 +131,8 @@ #include "wayland/gdkwayland.h" #endif +#define MNEMONICS_DELAY 300 /* ms */ + #define TAIL_GAP_WIDTH 24 #define TAIL_HEIGHT 12 @@ -147,6 +150,9 @@ typedef struct { gboolean autohide; gboolean has_arrow; gboolean mnemonics_visible; + gboolean disable_auto_mnemonics; + + guint mnemonics_display_timeout_id; GtkWidget *contents_widget; GtkCssNode *arrow_node; @@ -579,18 +585,137 @@ close_menu (GtkPopover *popover) } } +static gboolean +gtk_popover_has_mnemonic_modifier_pressed (GtkPopover *popover) +{ + GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); + GList *seats, *s; + gboolean retval = FALSE; + + seats = gdk_display_list_seats (gtk_widget_get_display (GTK_WIDGET (popover))); + + for (s = seats; s; s = s->next) + { + GdkDevice *dev = gdk_seat_get_pointer (s->data); + GdkModifierType mask; + + gdk_device_get_state (dev, priv->surface, NULL, &mask); + if ((mask & gtk_accelerator_get_default_mod_mask ()) == GDK_MOD1_MASK) + { + retval = TRUE; + break; + } + } + + g_list_free (seats); + + return retval; +} + +static gboolean +schedule_mnemonics_visible_cb (gpointer data) +{ + GtkPopover *popover = data; + GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); + + priv->mnemonics_display_timeout_id = 0; + + gtk_popover_set_mnemonics_visible (popover, TRUE); + + return G_SOURCE_REMOVE; +} + +static void +gtk_popover_schedule_mnemonics_visible (GtkPopover *popover) +{ + GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); + + if (priv->mnemonics_display_timeout_id) + return; + + priv->mnemonics_display_timeout_id = + g_timeout_add (MNEMONICS_DELAY, schedule_mnemonics_visible_cb, popover); + g_source_set_name_by_id (priv->mnemonics_display_timeout_id, "[gtk] popover_schedule_mnemonics_visible_cb"); +} + +static void +gtk_popover_focus_in (GtkWidget *widget) +{ + GtkPopover *popover = GTK_POPOVER (widget); + GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); + + if (priv->disable_auto_mnemonics) + return; + + if (gtk_widget_get_visible (widget)) + { + if (gtk_popover_has_mnemonic_modifier_pressed (popover)) + gtk_popover_schedule_mnemonics_visible (popover); + } +} + +static void +gtk_popover_focus_out (GtkWidget *widget) +{ + GtkPopover *popover = GTK_POPOVER (widget); + GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); + + if (priv->disable_auto_mnemonics) + return; + + gtk_popover_set_mnemonics_visible (popover, FALSE); +} + +static void +update_mnemonics_visible (GtkPopover *popover, + guint keyval, + GdkModifierType state, + gboolean visible) +{ + GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); + + if (priv->disable_auto_mnemonics) + return; + + if ((keyval == GDK_KEY_Alt_L || keyval == GDK_KEY_Alt_R) && + ((state & (gtk_accelerator_get_default_mod_mask ()) & ~(GDK_MOD1_MASK)) == 0)) + { + if (visible) + gtk_popover_schedule_mnemonics_visible (popover); + else + gtk_popover_set_mnemonics_visible (popover, FALSE); + } +} + static gboolean gtk_popover_key_pressed (GtkWidget *widget, guint keyval, guint keycode, GdkModifierType state) { + GtkPopover *popover = GTK_POPOVER (widget); + if (keyval == GDK_KEY_Escape) { - close_menu (GTK_POPOVER (widget)); + close_menu (popover); return TRUE; } + update_mnemonics_visible (popover, keyval, state, TRUE); + + return FALSE; +} + +static gboolean +gtk_popover_key_released (GtkWidget *widget, + guint keyval, + guint keycode, + GdkModifierType state) +{ + GtkPopover *popover = GTK_POPOVER (widget); + + update_mnemonics_visible (popover, keyval, state, FALSE); + return FALSE; } @@ -709,8 +834,14 @@ gtk_popover_init (GtkPopover *popover) controller = gtk_event_controller_key_new (); g_signal_connect_swapped (controller, "key-pressed", G_CALLBACK (gtk_popover_key_pressed), popover); + g_signal_connect_swapped (controller, "key-released", G_CALLBACK (gtk_popover_key_released), popover); gtk_widget_add_controller (GTK_WIDGET (popover), controller); + controller = gtk_event_controller_focus_new (); + g_signal_connect_swapped (controller, "enter", G_CALLBACK (gtk_popover_focus_in), popover); + g_signal_connect_swapped (controller, "leave", G_CALLBACK (gtk_popover_focus_out), popover); + gtk_widget_add_controller (widget, controller); + priv->arrow_node = gtk_css_node_new (); gtk_css_node_set_name (priv->arrow_node, g_quark_from_static_string ("arrow")); gtk_css_node_set_parent (priv->arrow_node, gtk_widget_get_css_node (widget)); @@ -797,6 +928,7 @@ gtk_popover_show (GtkWidget *widget) static void gtk_popover_hide (GtkWidget *widget) { + gtk_popover_set_mnemonics_visible (GTK_POPOVER (widget), FALSE); _gtk_widget_set_visible_flag (widget, FALSE); gtk_widget_unmap (widget); g_signal_emit (widget, signals[CLOSED], 0); @@ -899,6 +1031,12 @@ gtk_popover_finalize (GObject *object) g_clear_pointer (&priv->layout, gdk_popup_layout_unref); + if (priv->mnemonics_display_timeout_id) + { + g_source_remove (priv->mnemonics_display_timeout_id); + priv->mnemonics_display_timeout_id = 0; + } + G_OBJECT_CLASS (gtk_popover_parent_class)->finalize (object); } @@ -1938,6 +2076,12 @@ gtk_popover_set_mnemonics_visible (GtkPopover *popover, g_object_notify_by_pspec (G_OBJECT (popover), properties[PROP_MNEMONICS_VISIBLE]); gtk_widget_queue_resize (GTK_WIDGET (popover)); + + if (priv->mnemonics_display_timeout_id) + { + g_source_remove (priv->mnemonics_display_timeout_id); + priv->mnemonics_display_timeout_id = 0; + } } /** @@ -1957,3 +2101,11 @@ gtk_popover_get_mnemonics_visible (GtkPopover *popover) return priv->mnemonics_visible; } + +void +gtk_popover_disable_auto_mnemonics (GtkPopover *popover) +{ + GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); + + priv->disable_auto_mnemonics = TRUE; +} diff --git a/gtk/gtkpopoverprivate.h b/gtk/gtkpopoverprivate.h index db5fa6e57a..879562332b 100644 --- a/gtk/gtkpopoverprivate.h +++ b/gtk/gtkpopoverprivate.h @@ -24,6 +24,8 @@ G_BEGIN_DECLS GtkWidget *gtk_popover_get_contents_widget (GtkPopover *popover); +void gtk_popover_disable_auto_mnemonics (GtkPopover *popover); + G_END_DECLS #endif /* __GTK_POPOVER_PRIVATE_H__ */