shortcuttrigger: Do elaborate matching for key events

Copy the logic from GtkKeyHash for matching key events
to shortcuts.

Adapt shortcuts test to work with the better matching,
by creating more complete key events.
This commit is contained in:
Matthias Clasen 2020-03-22 09:54:15 -04:00
parent 904835d4b1
commit 586e7749d5
2 changed files with 109 additions and 29 deletions

View File

@ -40,6 +40,7 @@
#include "gtkshortcuttrigger.h"
#include "gtkaccelgroupprivate.h"
#include "gtkprivate.h"
typedef struct _GtkShortcutTriggerClass GtkShortcutTriggerClass;
@ -499,25 +500,99 @@ gtk_keyval_trigger_trigger (GtkShortcutTrigger *trigger,
gboolean enable_mnemonics)
{
GtkKeyvalTrigger *self = (GtkKeyvalTrigger *) trigger;
GdkModifierType modifiers;
guint keycode;
GdkModifierType state;
GdkModifierType mask;
int group;
GdkKeymap *keymap;
guint keyval;
int effective_group;
int level;
GdkModifierType consumed_modifiers;
GdkModifierType shift_group_mask;
gboolean group_mod_is_accel_mod = FALSE;
const GdkModifierType xmods = GDK_MOD2_MASK|GDK_MOD3_MASK|GDK_MOD4_MASK|GDK_MOD5_MASK;
const GdkModifierType vmods = GDK_SUPER_MASK|GDK_HYPER_MASK|GDK_META_MASK;
GdkModifierType modifiers;
if (gdk_event_get_event_type (event) != GDK_KEY_PRESS)
return GTK_SHORTCUT_TRIGGER_MATCH_NONE;
/* XXX: This needs to deal with groups */
modifiers = gdk_event_get_modifier_state (event);
keyval = gdk_key_event_get_keyval (event);
mask = gtk_accelerator_get_default_mod_mask ();
if (keyval == GDK_KEY_ISO_Left_Tab)
keyval = GDK_KEY_Tab;
else
keyval = gdk_keyval_to_lower (keyval);
keycode = gdk_key_event_get_keycode (event);
state = gdk_event_get_modifier_state (event);
group = gdk_key_event_get_group (event);
keymap = gdk_display_get_keymap (gdk_event_get_display (event));
if (keyval != self->keyval || modifiers != self->modifiers)
return GTK_SHORTCUT_TRIGGER_MATCH_NONE;
/* We don't want Caps_Lock to affect keybinding lookups.
*/
state &= ~GDK_LOCK_MASK;
return GTK_SHORTCUT_TRIGGER_MATCH_EXACT;
_gtk_translate_keyboard_accel_state (keymap,
keycode, state, mask, group,
&keyval,
&effective_group, &level,
&consumed_modifiers);
/* if the group-toggling modifier is part of the default accel mod
* mask, and it is active, disable it for matching
*/
shift_group_mask = gdk_keymap_get_modifier_mask (keymap,
GDK_MODIFIER_INTENT_SHIFT_GROUP);
if (mask & shift_group_mask)
group_mod_is_accel_mod = TRUE;
gdk_keymap_map_virtual_modifiers (keymap, &mask);
gdk_keymap_add_virtual_modifiers (keymap, &state);
modifiers = self->modifiers;
if (gdk_keymap_map_virtual_modifiers (keymap, &modifiers) &&
((modifiers & ~consumed_modifiers & mask & ~vmods) == (state & ~consumed_modifiers & mask & ~vmods) ||
(modifiers & ~consumed_modifiers & mask & ~xmods) == (state & ~consumed_modifiers & mask & ~xmods)))
{
/* modifier match */
GdkKeymapKey *keys;
int n_keys;
int i;
guint key;
/* Shift gets consumed and applied for the event,
* so apply it to our keyval to match
*/
key = self->keyval;
if (self->modifiers & GDK_SHIFT_MASK)
key = gdk_keyval_to_upper (key);
if (keyval == key && /* exact match */
(!group_mod_is_accel_mod ||
(state & shift_group_mask) == (self->modifiers & shift_group_mask)))
return GTK_SHORTCUT_TRIGGER_MATCH_EXACT;
gdk_keymap_get_entries_for_keyval (keymap,
self->keyval,
&keys, &n_keys);
for (i = 0; i < n_keys; i++)
{
if (keys[i].keycode == keycode &&
keys[i].level == level &&
/* Only match for group if it's an accel mod */
(!group_mod_is_accel_mod ||
keys[i].group == effective_group))
{
/* partial match */
g_free (keys);
return GTK_SHORTCUT_TRIGGER_MATCH_PARTIAL;
}
}
g_free (keys);
}
return GTK_SHORTCUT_TRIGGER_MATCH_NONE;
}
static guint

View File

@ -186,7 +186,7 @@ test_trigger_parse (void)
static void
test_trigger_trigger (void)
{
GtkShortcutTrigger *trigger1, *trigger2, *trigger3, *trigger4;
GtkShortcutTrigger *trigger[4];
GdkDisplay *display;
GdkSurface *surface;
GdkDevice *device;
@ -204,13 +204,13 @@ test_trigger_trigger (void)
{ GDK_KEY_u, GDK_SHIFT_MASK, FALSE, { GTK_SHORTCUT_TRIGGER_MATCH_NONE, GTK_SHORTCUT_TRIGGER_MATCH_NONE, GTK_SHORTCUT_TRIGGER_MATCH_NONE, GTK_SHORTCUT_TRIGGER_MATCH_NONE } },
{ GDK_KEY_u, GDK_SHIFT_MASK, TRUE, { GTK_SHORTCUT_TRIGGER_MATCH_NONE, GTK_SHORTCUT_TRIGGER_MATCH_NONE, GTK_SHORTCUT_TRIGGER_MATCH_EXACT, GTK_SHORTCUT_TRIGGER_MATCH_EXACT } },
};
int i;
int i, j;
trigger1 = gtk_shortcut_trigger_ref (gtk_never_trigger_get ());
trigger2 = gtk_keyval_trigger_new (GDK_KEY_a, GDK_CONTROL_MASK);
trigger3 = gtk_mnemonic_trigger_new (GDK_KEY_u);
trigger4 = gtk_alternative_trigger_new (gtk_shortcut_trigger_ref (trigger2),
gtk_shortcut_trigger_ref (trigger3));
trigger[0] = gtk_shortcut_trigger_ref (gtk_never_trigger_get ());
trigger[1] = gtk_keyval_trigger_new (GDK_KEY_a, GDK_CONTROL_MASK);
trigger[2] = gtk_mnemonic_trigger_new (GDK_KEY_u);
trigger[3] = gtk_alternative_trigger_new (gtk_shortcut_trigger_ref (trigger[1]),
gtk_shortcut_trigger_ref (trigger[2]));
display = gdk_display_get_default ();
device = gdk_seat_get_keyboard (gdk_display_get_default_seat (display));
@ -218,6 +218,13 @@ test_trigger_trigger (void)
for (i = 0; i < G_N_ELEMENTS (tests); i++)
{
GdkKeymapKey *keys;
int n_keys;
if (!gdk_keymap_get_entries_for_keyval (gdk_display_get_keymap (display),
tests[i].keyval, &keys, &n_keys))
continue;
event = gdk_event_key_new (GDK_KEY_PRESS,
surface,
device,
@ -225,24 +232,22 @@ test_trigger_trigger (void)
GDK_CURRENT_TIME,
tests[i].state,
tests[i].keyval,
0,
0,
0,
keys[0].keycode,
keys[0].keycode,
keys[0].group,
FALSE);
g_assert_cmpint (gtk_shortcut_trigger_trigger (trigger1, event, tests[i].mnemonic), ==, tests[i].result[0]);
g_assert_cmpint (gtk_shortcut_trigger_trigger (trigger2, event, tests[i].mnemonic), ==, tests[i].result[1]);
g_assert_cmpint (gtk_shortcut_trigger_trigger (trigger3, event, tests[i].mnemonic), ==, tests[i].result[2]);
g_assert_cmpint (gtk_shortcut_trigger_trigger (trigger4, event, tests[i].mnemonic), ==, tests[i].result[3]);
for (j = 0; j < 4; j++)
g_assert_cmpint (gtk_shortcut_trigger_trigger (trigger[j], event, tests[i].mnemonic), ==, tests[i].result[j]);
gdk_event_unref (event);
}
g_object_unref (surface);
gtk_shortcut_trigger_unref (trigger1);
gtk_shortcut_trigger_unref (trigger2);
gtk_shortcut_trigger_unref (trigger3);
gtk_shortcut_trigger_unref (trigger4);
gtk_shortcut_trigger_unref (trigger[0]);
gtk_shortcut_trigger_unref (trigger[1]);
gtk_shortcut_trigger_unref (trigger[2]);
gtk_shortcut_trigger_unref (trigger[3]);
}
static int callback_count;