[gtk/widget-class-actions: 6/8] widget: Add api to bind accels to actions



commit f4f09d15a8d76975d2699cf72ff0fce55eeb6244
Author: Matthias Clasen <mclasen redhat com>
Date:   Mon Jun 17 12:50:25 2019 +0000

    widget: Add api to bind accels to actions
    
    This is meant to be used at class-init time,
    and will replace bindings, eventually.
    
    We are reusing GtkApplicationAccels here.
    It should probably be renamed.

 docs/reference/gtk/gtk4-sections.txt |   1 +
 gtk/gtkwidget.c                      | 147 +++++++++++++++++++++++++++++++++++
 gtk/gtkwidget.h                      |   8 ++
 3 files changed, 156 insertions(+)
---
diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt
index dfcf55c548..42ceaa0bec 100644
--- a/docs/reference/gtk/gtk4-sections.txt
+++ b/docs/reference/gtk/gtk4-sections.txt
@@ -4636,6 +4636,7 @@ gtk_widget_class_install_action
 gtk_widget_class_install_stateful_action
 gtk_widget_notify_class_action_enabled
 gtk_widget_notify_class_action_state
+gtk_widget_class_bind_action
 
 <SUBSECTION Standard>
 GTK_WIDGET
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 2d29fe1f46..5f1850994e 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -73,6 +73,7 @@
 #include "gtkwindowgroup.h"
 #include "gtkwindowprivate.h"
 #include "gtknativeprivate.h"
+#include "gtkapplicationaccelsprivate.h"
 
 #include "a11y/gtkwidgetaccessible.h"
 #include "inspector/window.h"
@@ -502,6 +503,7 @@ struct _GtkWidgetClassPrivate
   const char *css_name;
   GType layout_manager_type;
   GPtrArray *actions;
+  GtkApplicationAccels *accels;
 };
 
 enum {
@@ -713,6 +715,9 @@ static gboolean gtk_widget_class_get_visible_by_default (GtkWidgetClass *widget_
 static void remove_parent_surface_transform_changed_listener (GtkWidget *widget);
 static void add_parent_surface_transform_changed_listener (GtkWidget *widget);
 
+static gboolean gtk_widget_activate_accels (GtkWidget      *widget,
+                                            const GdkEvent *event);
+
 
 /* --- variables --- */
 static gint             GtkWidget_private_offset = 0;
@@ -5360,6 +5365,11 @@ gtk_widget_event_internal (GtkWidget      *widget,
        event->any.type == GDK_KEY_RELEASE))
     return_val |= gtk_bindings_activate_event (G_OBJECT (widget), (GdkEventKey *) event);
 
+  if (return_val == FALSE &&
+      (event->any.type == GDK_KEY_PRESS ||
+       event->any.type == GDK_KEY_RELEASE))
+    return_val = gtk_widget_activate_accels (widget, event);
+
   return return_val;
 }
 
@@ -13572,3 +13582,140 @@ gtk_widget_notify_class_action_state (GtkWidget  *widget,
   muxer = _gtk_widget_get_action_muxer (widget, TRUE);
   gtk_action_muxer_action_state_changed (muxer, action_name, state);
 }
+
+static void
+gtk_widget_class_add_accel_for_action (GtkWidgetClass *widget_class,
+                                       const char     *detailed_action,
+                                       const char     *accel)
+{
+  GtkWidgetClassPrivate *priv = widget_class->priv;
+  char **accels;
+  int n;
+
+  if (priv->accels == NULL)
+    priv->accels = gtk_application_accels_new ();
+
+  accels = gtk_application_accels_get_accels_for_action (priv->accels, detailed_action);
+
+  if (accels)
+    {
+      n = g_strv_length (accels);
+      accels = g_renew (char *, accels, n + 2);
+    }
+  else
+    {
+      n = 0;
+      accels = g_new (char *, 2);
+    }
+
+  accels[n] = g_strdup (accel);
+  accels[n + 1] = NULL;
+
+  gtk_application_accels_set_accels_for_action (priv->accels, detailed_action, (const char **)accels);
+}
+
+/**
+ * gtk_widget_class_bind_action:
+ * @widget_class: a #GtkWidgetClass
+ * @keyval: key value of the binding
+ * @modifiers: key modifiers of the binding
+ * @action_name: the name of the action to bind
+ * @format_string: (allow-none): GVariant format string for
+ *     the parameters, or %NULL
+ *
+ * Binds the @keyval, @modifiers shortcut to
+ * trigger the action named @action_name with the
+ * given parameters.
+ *
+ * This function should be called at class-init time.
+ *
+ * The action must be defined using
+ * gtk_widget_class_install_action() and the parameters
+ * must match the @parameter_type passed to that function.
+ */
+void
+gtk_widget_class_bind_action (GtkWidgetClass  *widget_class,
+                              guint            keyval,
+                              GdkModifierType  modifiers,
+                              const char      *action_name,
+                              const char      *format_string,
+                              ...)
+{
+  GtkWidgetClassPrivate *priv = widget_class->priv;
+  gboolean found = FALSE;
+  GVariantType *type = NULL;
+  GVariant *parameters = NULL;
+  char *detailed_action = NULL;
+  char *accel = NULL;
+
+  if (priv->actions)
+    {
+      int i;
+      for (i = 0; i < priv->actions->len; i++)
+        {
+          GtkWidgetAction *action = g_ptr_array_index (priv->actions, i);
+          if (strcmp (action->name, action_name) == 0)
+            {
+              type = action->parameter_type;
+              found = TRUE;
+              break;
+            }
+        }
+    }
+
+  if (!found)
+    {
+      g_warning ("Widget action %s not found", action_name);
+      return;
+    }
+
+  if (format_string)
+    {
+      va_list args;
+      va_start (args, format_string);
+      parameters = g_variant_ref_sink (g_variant_new_va (format_string, NULL, &args));
+      va_end (args);
+    }
+
+  if (parameters && !g_variant_is_of_type (parameters, type))
+    {
+      g_warning ("Parameters don't match expected type for action %s", action_name);
+      g_variant_unref (parameters);
+      return;
+    }
+
+  detailed_action = g_action_print_detailed_name (action_name, parameters);
+
+  accel = gtk_accelerator_name (keyval, modifiers);
+
+  gtk_widget_class_add_accel_for_action (widget_class, detailed_action, accel);
+
+  g_free (detailed_action);
+  g_free (accel);
+}
+
+static gboolean
+gtk_widget_activate_accels (GtkWidget      *widget,
+                            const GdkEvent *event)
+{
+  GtkWidgetClass *class = GTK_WIDGET_GET_CLASS (widget);
+  GtkWidgetClassPrivate *priv = class->priv;
+  GtkApplicationAccels *accels = priv->accels;
+  GtkActionMuxer *muxer;
+  guint keyval;
+  GdkModifierType modifiers;
+
+  if (!accels)
+    return FALSE;
+
+  muxer = _gtk_widget_get_action_muxer (widget, FALSE);
+  if (!muxer)
+    return FALSE;
+
+  gdk_event_get_keyval ((GdkEvent *)event, &keyval);
+  gdk_event_get_state ((GdkEvent *)event, &modifiers);
+
+  return gtk_application_accels_activate (accels,
+                                          G_ACTION_GROUP (muxer),
+                                          keyval, modifiers);
+}
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index 618aeca883..9b6408fd4b 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -1092,6 +1092,14 @@ void                    gtk_widget_notify_class_action_state (GtkWidget  *widget
                                                               const char *action_name,
                                                               GVariant   *state);
 
+GDK_AVAILABLE_IN_ALL
+void                    gtk_widget_class_bind_action           (GtkWidgetClass  *widget_class,
+                                                                guint            keyval,
+                                                                GdkModifierType  modifiers,
+                                                                const char      *action_name,
+                                                                const char      *format_string,
+                                                                ...);
+
 
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkWidget, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkRequisition, gtk_requisition_free)


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