[gtk/wip/ebassi/shortcut: 53/85] shortcuttrigger: Add support for mnemonics



commit cf2d77346848209afe7043f7eaf0d7bbc98ffd44
Author: Benjamin Otte <otte redhat com>
Date:   Thu Aug 16 03:59:24 2018 +0200

    shortcuttrigger: Add support for mnemonics
    
    Mnemonics need to be triggered with help from the controllers (who
    determine the modifiers). Support for that has been added, too.
    
    Mnemonics do not use this yet though.

 docs/reference/gtk/gtk4-sections.txt |   6 ++
 gtk/gtkshortcut.c                    |   7 --
 gtk/gtkshortcut.h                    |   3 -
 gtk/gtkshortcutcontroller.c          | 101 +++++++++++++++++++++--
 gtk/gtkshortcutcontroller.h          |   5 ++
 gtk/gtkshortcuttrigger.c             | 154 +++++++++++++++++++++++++++++++++--
 gtk/gtkshortcuttrigger.h             |  11 ++-
 7 files changed, 259 insertions(+), 28 deletions(-)
---
diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt
index d028d60250..da47ca688c 100644
--- a/docs/reference/gtk/gtk4-sections.txt
+++ b/docs/reference/gtk/gtk4-sections.txt
@@ -6442,6 +6442,10 @@ gtk_keyval_trigger_new
 gtk_keyval_trigger_get_modifiers
 gtk_keyval_trigger_get_keyval
 
+<SUBSECTION>
+gtk_mnemonic_trigger_new
+gtk_mnemonic_trigger_get_keyval
+
 <SUBSECTION Private>
 gtk_shortcut_trigger_get_type
 </SECTION>
@@ -6480,6 +6484,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 1fac8d489d..d06966c694 100644
--- a/gtk/gtkshortcut.c
+++ b/gtk/gtkshortcut.c
@@ -237,13 +237,6 @@ gtk_shortcut_new (void)
   return g_object_new (GTK_TYPE_SHORTCUT, NULL);
 }
 
-gboolean
-gtk_shortcut_trigger (GtkShortcut    *self,
-                      const 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 8df123597d..96bba17d78 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,
-                                                                 const 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 684aef917f..0841397e50 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,
-                                          const GdkEvent        *event)
+                                          const 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)));
@@ -140,7 +152,8 @@ gtk_shortcut_controller_trigger_shortcut (GtkShortcutController *self,
 
 static gboolean
 gtk_shortcut_controller_run_controllers (GtkEventController *controller,
-                                         const GdkEvent     *event)
+                                         const GdkEvent     *event,
+                                         gboolean            enable_mnemonics)
 {
   GtkShortcutController *self = GTK_SHORTCUT_CONTROLLER (controller);
   GtkWidget *widget;
@@ -148,7 +161,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;
     }
 
@@ -158,7 +171,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;
         }
     }
@@ -173,7 +186,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))
+          if (gtk_shortcut_controller_run_controllers (l->data, event, enable_mnemonics))
             return TRUE;
         }
     }
@@ -186,11 +199,23 @@ gtk_shortcut_controller_handle_event (GtkEventController *controller,
                                       const GdkEvent     *event)
 {
   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);
+  if (gdk_event_get_event_type (event) == GDK_KEY_PRESS)
+    {
+      GdkModifierType modifiers;
+      gdk_event_get_state (event, &modifiers);
+      enable_mnemonics = (modifiers & gtk_accelerator_get_default_mod_mask ()) == self->mnemonics_modifiers;
+    }
+  else
+    {
+      enable_mnemonics = FALSE;
+    }
+
+  return gtk_shortcut_controller_run_controllers (controller, event, enable_mnemonics);
 }
 
 static void
@@ -231,6 +256,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:
    *
@@ -245,12 +283,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
@@ -437,3 +475,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 3a6359d520..28d3362756 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  *controller,
                                                                          GtkShortcutScope        scope);
diff --git a/gtk/gtkshortcuttrigger.c b/gtk/gtkshortcuttrigger.c
index a5e67b1370..d4c661a419 100644
--- a/gtk/gtkshortcuttrigger.c
+++ b/gtk/gtkshortcuttrigger.c
@@ -60,7 +60,8 @@ struct _GtkShortcutTriggerClass
 
   void            (* finalize)    (GtkShortcutTrigger  *trigger);
   gboolean        (* trigger)     (GtkShortcutTrigger  *trigger,
-                                   const GdkEvent      *event);
+                                   const 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.
  *
@@ -165,12 +169,13 @@ gtk_shortcut_trigger_get_trigger_type (GtkShortcutTrigger *self)
  **/
 gboolean
 gtk_shortcut_trigger_trigger (GtkShortcutTrigger *self,
-                              const GdkEvent     *event)
+                              const GdkEvent     *event,
+                              gboolean            enable_mnemonics)
 {
   g_return_val_if_fail (GTK_IS_SHORTCUT_TRIGGER (self), FALSE);
   g_return_val_if_fail (GDK_IS_EVENT (event), 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,
-                           const GdkEvent     *event)
+                           const GdkEvent     *event,
+                           gboolean            enable_mnemonics)
                   
 {
   return FALSE;
@@ -369,7 +375,8 @@ gtk_keyval_trigger_finalize (GtkShortcutTrigger *trigger)
 
 static gboolean
 gtk_keyval_trigger_trigger (GtkShortcutTrigger *trigger,
-                            const GdkEvent     *event)
+                            const GdkEvent     *event,
+                            gboolean            enable_mnemonics)
 {
   GtkKeyvalTrigger *self = (GtkKeyvalTrigger *) trigger;
   GdkModifierType modifiers;
@@ -490,6 +497,136 @@ gtk_keyval_trigger_get_keyval (GtkShortcutTrigger *trigger)
   return self->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,
+                              const 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 */
+  gdk_event_get_keyval (event, &keyval);
+
+  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 (&GTK_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;
@@ -513,14 +650,15 @@ gtk_alternative_trigger_finalize (GtkShortcutTrigger *trigger)
 
 static gboolean
 gtk_alternative_trigger_trigger (GtkShortcutTrigger *trigger,
-                                 const GdkEvent     *event)
+                                 const 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 5b58bafb39..4cc6593929 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,
-                                                                 const GdkEvent     *event);
+                                                                 const 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);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]