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.
This commit is contained in:
Matthias Clasen 2020-03-24 12:45:43 -04:00
parent fc0b0b14a8
commit 642503afb4
2 changed files with 155 additions and 1 deletions

View File

@ -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;
}

View File

@ -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__ */