diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index cfc8e03fde..534653d922 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -6061,6 +6061,10 @@ gtk_keyval_trigger_new gtk_keyval_trigger_get_modifiers gtk_keyval_trigger_get_keyval + +gtk_mnemonic_trigger_new +gtk_mnemonic_trigger_get_keyval + gtk_shortcut_trigger_get_type @@ -6099,6 +6103,8 @@ gtk_shortcut_get_type GtkShortcutController gtk_shortcut_controller_new GtkShortcutScope +gtk_shortcut_controller_set_mnemonics_modifiers +gtk_shortcut_controller_get_mnemonics_modifiers gtk_shortcut_controller_set_scope gtk_shortcut_controller_get_scope gtk_shortcut_controller_add_shortcut diff --git a/gtk/gtkshortcut.c b/gtk/gtkshortcut.c index 6f8012c20f..6d017e3721 100644 --- a/gtk/gtkshortcut.c +++ b/gtk/gtkshortcut.c @@ -259,13 +259,6 @@ gtk_shortcut_new (void) return g_object_new (GTK_TYPE_SHORTCUT, NULL); } -gboolean -gtk_shortcut_trigger (GtkShortcut *self, - GdkEvent *event) -{ - return gtk_shortcut_trigger_trigger (self->trigger, event); -} - static gboolean binding_compose_params (GObject *object, GVariantIter *args, diff --git a/gtk/gtkshortcut.h b/gtk/gtkshortcut.h index 9ed37ea90d..a3a02a7514 100644 --- a/gtk/gtkshortcut.h +++ b/gtk/gtkshortcut.h @@ -44,9 +44,6 @@ GtkShortcutTrigger * gtk_shortcut_get_trigger (GtkShortcut *self); GDK_AVAILABLE_IN_ALL -gboolean gtk_shortcut_trigger (GtkShortcut *self, - GdkEvent *event); -GDK_AVAILABLE_IN_ALL gboolean gtk_shortcut_activate (GtkShortcut *self, GtkWidget *widget); diff --git a/gtk/gtkshortcutcontroller.c b/gtk/gtkshortcutcontroller.c index ed56a98dc6..c47d657f39 100644 --- a/gtk/gtkshortcutcontroller.c +++ b/gtk/gtkshortcutcontroller.c @@ -34,6 +34,7 @@ #include "gtkeventcontrollerprivate.h" #include "gtkintl.h" #include "gtkshortcut.h" +#include "gtkshortcuttrigger.h" #include "gtktypebuiltins.h" #include "gtkwidgetprivate.h" @@ -45,6 +46,7 @@ struct _GtkShortcutController GSList *shortcuts; GtkShortcutScope scope; + GdkModifierType mnemonics_modifiers; guint run_class : 1; guint run_managed : 1; @@ -57,6 +59,7 @@ struct _GtkShortcutControllerClass enum { PROP_0, + PROP_MNEMONICS_MODIFIERS, PROP_SCOPE, N_PROPS @@ -88,6 +91,10 @@ gtk_shortcut_controller_set_property (GObject *object, switch (prop_id) { + case PROP_MNEMONICS_MODIFIERS: + gtk_shortcut_controller_set_mnemonics_modifiers (self, g_value_get_flags (value)); + break; + case PROP_SCOPE: gtk_shortcut_controller_set_scope (self, g_value_get_enum (value)); break; @@ -107,6 +114,10 @@ gtk_shortcut_controller_get_property (GObject *object, switch (prop_id) { + case PROP_MNEMONICS_MODIFIERS: + g_value_set_flags (value, self->mnemonics_modifiers); + break; + case PROP_SCOPE: g_value_set_enum (value, self->scope); break; @@ -130,9 +141,10 @@ gtk_shortcut_controller_dispose (GObject *object) static gboolean gtk_shortcut_controller_trigger_shortcut (GtkShortcutController *self, GtkShortcut *shortcut, - GdkEvent *event) + GdkEvent *event, + gboolean enable_mnemonics) { - if (!gtk_shortcut_trigger (shortcut, event)) + if (!gtk_shortcut_trigger_trigger (gtk_shortcut_get_trigger (shortcut), event, enable_mnemonics)) return FALSE; return gtk_shortcut_activate (shortcut, gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self))); @@ -142,7 +154,8 @@ static gboolean gtk_shortcut_controller_run_controllers (GtkEventController *controller, GdkEvent *event, double x, - double y) + double y, + gboolean enable_mnemonics) { GtkShortcutController *self = GTK_SHORTCUT_CONTROLLER (controller); GtkWidget *widget; @@ -150,7 +163,7 @@ gtk_shortcut_controller_run_controllers (GtkEventController *controller, for (l = self->shortcuts; l; l = l->next) { - if (gtk_shortcut_controller_trigger_shortcut (self, l->data, event)) + if (gtk_shortcut_controller_trigger_shortcut (self, l->data, event, enable_mnemonics)) return TRUE; } @@ -160,7 +173,7 @@ gtk_shortcut_controller_run_controllers (GtkEventController *controller, for (l = gtk_widget_class_get_shortcuts (GTK_WIDGET_GET_CLASS (widget)); l; l = l->next) { - if (gtk_shortcut_controller_trigger_shortcut (self, l->data, event)) + if (gtk_shortcut_controller_trigger_shortcut (self, l->data, event, enable_mnemonics)) return TRUE; } } @@ -175,7 +188,7 @@ gtk_shortcut_controller_run_controllers (GtkEventController *controller, if (gtk_event_controller_get_propagation_phase (l->data) != current_phase) continue; - if (gtk_shortcut_controller_run_controllers (l->data, event, x, y)) + if (gtk_shortcut_controller_run_controllers (l->data, event, x, y, enable_mnemonics)) return TRUE; } } @@ -190,11 +203,23 @@ gtk_shortcut_controller_handle_event (GtkEventController *controller, double y) { GtkShortcutController *self = GTK_SHORTCUT_CONTROLLER (controller); + gboolean enable_mnemonics; if (self->scope != GTK_SHORTCUT_SCOPE_LOCAL) return FALSE; - return gtk_shortcut_controller_run_controllers (controller, event, x, y); + if (gdk_event_get_event_type (event) == GDK_KEY_PRESS) + { + GdkModifierType modifiers; + modifiers = gdk_event_get_modifier_state (event); + enable_mnemonics = (modifiers & gtk_accelerator_get_default_mod_mask ()) == self->mnemonics_modifiers; + } + else + { + enable_mnemonics = FALSE; + } + + return gtk_shortcut_controller_run_controllers (controller, event, x, y, enable_mnemonics); } static void @@ -235,6 +260,19 @@ gtk_shortcut_controller_class_init (GtkShortcutControllerClass *klass) controller_class->set_widget = gtk_shortcut_controller_set_widget; controller_class->unset_widget = gtk_shortcut_controller_unset_widget; + /** + * GtkShortcutController:mnemonic-modifiers: + * + * The modifiers that need to be pressed to allow mnemonics activation. + */ + properties[PROP_MNEMONICS_MODIFIERS] = + g_param_spec_flags ("mnemonic-modifiers", + P_("Mnemonic modifers"), + P_("The modifiers to be pressed to allow mnemonics activation"), + GDK_TYPE_MODIFIER_TYPE, + GDK_MOD1_MASK, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + /** * GtkShortcutController:scope: * @@ -249,12 +287,12 @@ gtk_shortcut_controller_class_init (GtkShortcutControllerClass *klass) G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, N_PROPS, properties); - } static void -gtk_shortcut_controller_init (GtkShortcutController *controller) +gtk_shortcut_controller_init (GtkShortcutController *self) { + self->mnemonics_modifiers = GDK_MOD1_MASK; } static void @@ -441,3 +479,48 @@ gtk_shortcut_controller_get_scope (GtkShortcutController *self) return self->scope; } +/** + * gtk_shortcut_controller_set_mnemonics_modifiers: + * @self: a #GtkShortcutController + * @modifiers: the new mnemonics_modifiers to use + * + * Sets the controller to have the given @mnemonics_modifiers. + * + * The mnemonics modifiers determines which modifiers need to be pressed to allow + * activation of shortcuts with mnemonics triggers. + * + * This value is only relevant for local shortcut controllers. Global and managed + * shortcut controllers will have their shortcuts activated from other places which + * have their own modifiers for activating mnemonics. + **/ +void +gtk_shortcut_controller_set_mnemonics_modifiers (GtkShortcutController *self, + GdkModifierType modifiers) +{ + g_return_if_fail (GTK_IS_SHORTCUT_CONTROLLER (self)); + + if (self->mnemonics_modifiers == modifiers) + return; + + self->mnemonics_modifiers = modifiers; + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MNEMONICS_MODIFIERS]); +} + +/** + * gtk_shortcut_controller_get_mnemonics_modifiers: + * @self: a #GtkShortcutController + * + * Gets the mnemonics modifiers for when this controller activates its shortcuts. See + * gtk_shortcut_controller_set_mnemonics_modifiers() for details. + * + * Returns: the controller's mnemonics modifiers + **/ +GdkModifierType +gtk_shortcut_controller_get_mnemonics_modifiers (GtkShortcutController *self) +{ + g_return_val_if_fail (GTK_IS_SHORTCUT_CONTROLLER (self), GTK_SHORTCUT_SCOPE_LOCAL); + + return self->mnemonics_modifiers; +} + diff --git a/gtk/gtkshortcutcontroller.h b/gtk/gtkshortcutcontroller.h index fea66f3455..fb31ffe8cb 100644 --- a/gtk/gtkshortcutcontroller.h +++ b/gtk/gtkshortcutcontroller.h @@ -45,6 +45,11 @@ GType gtk_shortcut_controller_get_type (void) G GDK_AVAILABLE_IN_ALL GtkEventController * gtk_shortcut_controller_new (void); +GDK_AVAILABLE_IN_ALL +void gtk_shortcut_controller_set_mnemonics_modifiers (GtkShortcutController *self, + GdkModifierType modifiers); +GDK_AVAILABLE_IN_ALL +GdkModifierType gtk_shortcut_controller_get_mnemonics_modifiers (GtkShortcutController *self); GDK_AVAILABLE_IN_ALL void gtk_shortcut_controller_set_scope (GtkShortcutController *self, GtkShortcutScope scope); diff --git a/gtk/gtkshortcuttrigger.c b/gtk/gtkshortcuttrigger.c index cdefa82059..b56cf6d0ee 100644 --- a/gtk/gtkshortcuttrigger.c +++ b/gtk/gtkshortcuttrigger.c @@ -60,7 +60,8 @@ struct _GtkShortcutTriggerClass void (* finalize) (GtkShortcutTrigger *trigger); gboolean (* trigger) (GtkShortcutTrigger *trigger, - GdkEvent *event); + GdkEvent *event, + gboolean enable_mnemonics); void (* print) (GtkShortcutTrigger *trigger, GString *string); gboolean (* print_label) (GtkShortcutTrigger *trigger, @@ -158,6 +159,9 @@ gtk_shortcut_trigger_get_trigger_type (GtkShortcutTrigger *self) * gtk_shortcut_trigger_trigger: * @self: a #GtkShortcutTrigger * @event: the event to check + * @enable_mnemonics: %TRUE if mnemonics should trigger. Usually the + * value of this property is determined by checking that the passed + * in @event is a Key event and has the right modifiers set. * * Checks if the given @event triggers @self. If so, * returns %TRUE. @@ -166,11 +170,12 @@ gtk_shortcut_trigger_get_trigger_type (GtkShortcutTrigger *self) **/ gboolean gtk_shortcut_trigger_trigger (GtkShortcutTrigger *self, - GdkEvent *event) + GdkEvent *event, + gboolean enable_mnemonics) { g_return_val_if_fail (GTK_IS_SHORTCUT_TRIGGER (self), FALSE); - return self->trigger_class->trigger (self, event); + return self->trigger_class->trigger (self, event, enable_mnemonics); } /** @@ -302,7 +307,8 @@ gtk_never_trigger_finalize (GtkShortcutTrigger *trigger) static gboolean gtk_never_trigger_trigger (GtkShortcutTrigger *trigger, - GdkEvent *event) + GdkEvent *event, + gboolean enable_mnemonics) { return FALSE; } @@ -368,7 +374,8 @@ gtk_keyval_trigger_finalize (GtkShortcutTrigger *trigger) static gboolean gtk_keyval_trigger_trigger (GtkShortcutTrigger *trigger, - GdkEvent *event) + GdkEvent *event, + gboolean enable_mnemonics) { GtkKeyvalTrigger *self = (GtkKeyvalTrigger *) trigger; GdkModifierType modifiers; @@ -489,6 +496,134 @@ gtk_keyval_trigger_get_keyval (GtkShortcutTrigger *self) return trigger->keyval; } +/*** GTK_MNEMONIC_TRIGGER ***/ + +typedef struct _GtkMnemonicTrigger GtkMnemonicTrigger; + +struct _GtkMnemonicTrigger +{ + GtkShortcutTrigger trigger; + + guint keyval; +}; + +static void +gtk_mnemonic_trigger_finalize (GtkShortcutTrigger *trigger) +{ +} + +static gboolean +gtk_mnemonic_trigger_trigger (GtkShortcutTrigger *trigger, + GdkEvent *event, + gboolean enable_mnemonics) +{ + GtkMnemonicTrigger *self = (GtkMnemonicTrigger *) trigger; + guint keyval; + + if (!enable_mnemonics) + return FALSE; + + if (gdk_event_get_event_type (event) != GDK_KEY_PRESS) + return FALSE; + + /* XXX: This needs to deal with groups */ + keyval = gdk_key_event_get_keyval (event); + + if (keyval == GDK_KEY_ISO_Left_Tab) + keyval = GDK_KEY_Tab; + else + keyval = gdk_keyval_to_lower (keyval); + + return keyval == self->keyval; +} + +static void +gtk_mnemonic_trigger_print (GtkShortcutTrigger *trigger, + GString *string) +{ + GtkMnemonicTrigger *self = (GtkMnemonicTrigger *) trigger; + const char *keyval_str; + + keyval_str = gdk_keyval_name (self->keyval); + if (keyval_str == NULL) + keyval_str = "???"; + + g_string_append (string, keyval_str); +} + +static gboolean +gtk_mnemonic_trigger_print_label (GtkShortcutTrigger *trigger, + GdkDisplay *display, + GString *string) +{ + GtkMnemonicTrigger *self = (GtkMnemonicTrigger *) trigger; + const char *keyval_str; + + keyval_str = gdk_keyval_name (self->keyval); + if (keyval_str == NULL) + return FALSE; + + g_string_append (string, keyval_str); + + return TRUE; +} + +static const GtkShortcutTriggerClass GTK_MNEMONIC_TRIGGER_CLASS = { + GTK_SHORTCUT_TRIGGER_MNEMONIC, + sizeof (GtkMnemonicTrigger), + "GtkMnemonicTrigger", + gtk_mnemonic_trigger_finalize, + gtk_mnemonic_trigger_trigger, + gtk_mnemonic_trigger_print, + gtk_mnemonic_trigger_print_label +}; + +/** + * gtk_mnemonic_trigger_new: + * @keyval: The keyval to trigger for + * + * Creates a #GtkShortcutTrigger that will trigger whenever the key with + * the given @keyval is pressed and mnemonics have been activated. + * + * Mnemonics are activated by calling code when a key event with the right + * modifiers is detected. + * + * Returns: A new #GtkShortcutTrigger + */ +GtkShortcutTrigger * +gtk_mnemonic_trigger_new (guint keyval) +{ + GtkMnemonicTrigger *self; + + self = (GtkMnemonicTrigger *) gtk_shortcut_trigger_new (>K_MNEMONIC_TRIGGER_CLASS); + + /* We store keyvals as lower key */ + if (keyval == GDK_KEY_ISO_Left_Tab) + self->keyval = GDK_KEY_Tab; + else + self->keyval = gdk_keyval_to_lower (keyval); + + return &self->trigger; +} + +/** + * gtk_mnemonic_trigger_get_keyval: + * @trigger: a mnemonic #GtkShortcutTrigger + * + * Gets the keyval that must be pressed to succeed triggering @self. + * + * Returns: the keyval + **/ +guint +gtk_mnemonic_trigger_get_keyval (GtkShortcutTrigger *trigger) +{ + GtkMnemonicTrigger *self = (GtkMnemonicTrigger *) trigger; + + g_return_val_if_fail (GTK_IS_SHORTCUT_TRIGGER_TYPE (trigger, GTK_SHORTCUT_TRIGGER_MNEMONIC), 0); + + return self->keyval; +} + /*** GTK_ALTERNATIVE_TRIGGER ***/ typedef struct _GtkAlternativeTrigger GtkAlternativeTrigger; @@ -512,14 +647,15 @@ gtk_alternative_trigger_finalize (GtkShortcutTrigger *trigger) static gboolean gtk_alternative_trigger_trigger (GtkShortcutTrigger *trigger, - GdkEvent *event) + GdkEvent *event, + gboolean enable_mnemonics) { GtkAlternativeTrigger *self = (GtkAlternativeTrigger *) trigger; - if (gtk_shortcut_trigger_trigger (self->first, event)) + if (gtk_shortcut_trigger_trigger (self->first, event, enable_mnemonics)) return TRUE; - if (gtk_shortcut_trigger_trigger (self->second, event)) + if (gtk_shortcut_trigger_trigger (self->second, event, enable_mnemonics)) return TRUE; return FALSE; diff --git a/gtk/gtkshortcuttrigger.h b/gtk/gtkshortcuttrigger.h index 2323bb0d13..c9b499e74d 100644 --- a/gtk/gtkshortcuttrigger.h +++ b/gtk/gtkshortcuttrigger.h @@ -37,6 +37,8 @@ G_BEGIN_DECLS * @GTK_SHORTCUT_TRIGGER_NEVER: Never ever trigger * @GTK_SHORTCUT_TRIGGER_KEYVAL: Trigger if a key event with matching * modifiers and keyval is received. + * @GTK_SHORTCUT_TRIGGER_MNEMONIC: Trigger if a key event with matching + * keyval is received and mnemonics are enabled for this event. * @GTK_SHORTCUT_TRIGGER_ALTERNAITVE: Trigger if either if two * alternatives triggers * @@ -45,6 +47,7 @@ G_BEGIN_DECLS typedef enum { GTK_SHORTCUT_TRIGGER_NEVER, GTK_SHORTCUT_TRIGGER_KEYVAL, + GTK_SHORTCUT_TRIGGER_MNEMONIC, GTK_SHORTCUT_TRIGGER_ALTERNATIVE } GtkShortcutTriggerType; @@ -74,7 +77,8 @@ gboolean gtk_shortcut_trigger_print_label (GtkShortcutTrig GDK_AVAILABLE_IN_ALL gboolean gtk_shortcut_trigger_trigger (GtkShortcutTrigger *self, - GdkEvent *event); + GdkEvent *event, + gboolean enable_mnemonics); GDK_AVAILABLE_IN_ALL GtkShortcutTrigger * gtk_never_trigger_get (void); @@ -87,6 +91,11 @@ GdkModifierType gtk_keyval_trigger_get_modifiers (GtkShortcutTrig GDK_AVAILABLE_IN_ALL guint gtk_keyval_trigger_get_keyval (GtkShortcutTrigger *self); +GDK_AVAILABLE_IN_ALL +GtkShortcutTrigger * gtk_mnemonic_trigger_new (guint keyval); +GDK_AVAILABLE_IN_ALL +guint gtk_mnemonic_trigger_get_keyval (GtkShortcutTrigger *self); + GDK_AVAILABLE_IN_ALL GtkShortcutTrigger * gtk_alternative_trigger_new (GtkShortcutTrigger *one, GtkShortcutTrigger *two);