[gtk/widget-class-actions: 2/3] Allow registering actions per-class
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/widget-class-actions: 2/3] Allow registering actions per-class
- Date: Fri, 14 Jun 2019 16:52:10 +0000 (UTC)
commit 0465d7b27acda9f9db0f89fd0233373acc9f1afd
Author: Matthias Clasen <mclasen redhat com>
Date: Fri Jun 14 12:12:10 2019 +0000
Allow registering actions per-class
Add a facility to register and install actions.
To register them, call gtk_widget_class_install_action
in class_init. To install them, call
gtk_widget_add_class_actions in init. This adds
one or more action groups to the widgets action
muxer. There's also some convenience api to
notify about action state changes.
gtk/gtkwidget.c | 156 ++++++++++++++++++++++
gtk/gtkwidget.h | 31 +++++
gtk/gtkwidgetactiongroup.c | 270 ++++++++++++++++++++++++++++++++++++++
gtk/gtkwidgetactiongroupprivate.h | 52 ++++++++
gtk/meson.build | 1 +
5 files changed, 510 insertions(+)
---
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 0bc88b06b2..17033f6f80 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -70,6 +70,7 @@
#include "gtkversion.h"
#include "gtkwidgetpaintableprivate.h"
#include "gtkwidgetpathprivate.h"
+#include "gtkwidgetactiongroupprivate.h"
#include "gtkwindowgroup.h"
#include "gtkwindowprivate.h"
#include "gtknativeprivate.h"
@@ -501,6 +502,7 @@ struct _GtkWidgetClassPrivate
AtkRole accessible_role;
const char *css_name;
GType layout_manager_type;
+ GPtrArray *actions;
};
enum {
@@ -13479,3 +13481,157 @@ gtk_widget_should_layout (GtkWidget *widget)
return TRUE;
}
+void
+gtk_widget_class_install_action (GtkWidgetClass *widget_class,
+ const char *prefixed_name,
+ GtkWidgetActionActivate activate,
+ GtkWidgetActionQuery query,
+ GtkWidgetActionChange change)
+{
+ GtkWidgetClassPrivate *priv = widget_class->priv;
+ GtkWidgetAction *action;
+ int i;
+ char *p;
+ char *prefix;
+ char *name;
+
+ g_return_if_fail (GTK_IS_WIDGET_CLASS (widget_class));
+
+ p = strchr (prefixed_name, '.');
+ if (p == 0)
+ {
+ g_warning ("Action name %s does not contain a '.'", prefixed_name);
+ return;
+ }
+ prefix = g_strndup (prefixed_name, p - prefixed_name);
+ name = g_strdup (p + 1);
+
+ if (priv->actions == NULL)
+ priv->actions = g_ptr_array_new ();
+
+ for (i = 0; i < priv->actions->len; i++)
+ {
+ action = g_ptr_array_index (priv->actions, i);
+
+ if (strcmp (action->prefix, prefix) == 0 &&
+ strcmp (action->name, name) == 0)
+ {
+ g_warning ("Duplicate action name %s.%s", prefix, name);
+ g_free (prefix);
+ g_free (name);
+ return;
+ }
+ }
+
+ action = g_new0 (GtkWidgetAction, 1);
+ action->prefix = prefix;
+ action->name = name;
+ action->activate = activate;
+ action->query = query;
+ action->change = change;
+
+ GTK_NOTE(ACTIONS,
+ g_message ("%sClass: Adding %s.%s action\n",
+ g_type_name (G_TYPE_FROM_CLASS (widget_class)),
+ prefix, name));
+
+ g_ptr_array_add (priv->actions, action);
+}
+
+void
+gtk_widget_add_class_actions (GtkWidget *widget)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_GET_CLASS (widget);
+ GtkWidgetClassPrivate *priv = widget_class->priv;
+ int i;
+ GHashTable *prefixes;
+ GHashTableIter iter;
+ const char *prefix;
+
+ if (priv->actions == NULL)
+ {
+ g_warning ("No class actions registered for %s", G_OBJECT_TYPE_NAME (widget));
+ return;
+ }
+
+ prefixes = g_hash_table_new (g_str_hash, g_str_equal);
+
+ for (i = 0; i < priv->actions->len; i++)
+ {
+ GtkWidgetAction *action = g_ptr_array_index (priv->actions, i);
+ g_hash_table_add (prefixes, action->prefix);
+ }
+
+ g_hash_table_iter_init (&iter, prefixes);
+ while (g_hash_table_iter_next (&iter, (gpointer *)&prefix, NULL))
+ {
+ GActionGroup *group;
+
+ group = gtk_widget_action_group_new (widget, prefix, priv->actions);
+ gtk_widget_insert_action_group (widget, prefix, group);
+ g_object_unref (group);
+ }
+
+ g_hash_table_unref (prefixes);
+}
+
+void
+gtk_widget_notify_class_action_enabled (GtkWidget *widget,
+ const char *prefixed_name)
+{
+ GActionGroup *group;
+ gboolean enabled;
+ char *p;
+ char *prefix;
+ const char *name;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ p = strchr (prefixed_name, '.');
+ if (p == 0)
+ {
+ g_warning ("Action name %s does not contain a '.'", prefixed_name);
+ return;
+ }
+ prefix = g_strndup (prefixed_name, p - prefixed_name);
+ name = p + 1;
+
+ group = gtk_widget_get_action_group (widget, prefix);
+ g_return_if_fail (group != NULL);
+
+ enabled = g_action_group_get_action_enabled (group, name);
+ g_action_group_action_enabled_changed (group, name, enabled);
+
+ g_free (prefix);
+}
+
+void
+gtk_widget_notify_class_action_state (GtkWidget *widget,
+ const char *prefixed_name)
+{
+ GActionGroup *group;
+ GVariant *state;
+ char *p;
+ char *prefix;
+ const char *name;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ p = strchr (prefixed_name, '.');
+ if (p == 0)
+ {
+ g_warning ("Action name %s does not contain a '.'", prefixed_name);
+ return;
+ }
+ prefix = g_strndup (prefixed_name, p - prefixed_name);
+ name = p + 1;
+
+ group = gtk_widget_get_action_group (widget, prefix);
+
+ g_return_if_fail (group != NULL);
+
+ state = g_action_group_get_action_state (group, name);
+ g_action_group_action_state_changed (group, name, state);
+
+ g_free (prefix);
+}
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index bb2766078b..d5703003d2 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -1030,6 +1030,37 @@ GDK_AVAILABLE_IN_ALL
gboolean gtk_widget_should_layout (GtkWidget *widget);
+typedef void (* GtkWidgetActionActivate) (GtkWidget *widget,
+ const char *action_name,
+ GVariant *parameter);
+typedef gboolean (* GtkWidgetActionQuery) (GtkWidget *widget,
+ const char *action_name,
+ gboolean *enabled,
+ const GVariantType **parameter_type,
+ const GVariantType **state_type,
+ GVariant **state_hint,
+ GVariant **state);
+typedef void (*GtkWidgetActionChange) (GtkWidget *widget,
+ const char *action_name,
+ GVariant *state);
+
+GDK_AVAILABLE_IN_ALL
+void gtk_widget_class_install_action (GtkWidgetClass *widget_class,
+ const char *prefixed_name,
+ GtkWidgetActionActivate activate,
+ GtkWidgetActionQuery query,
+ GtkWidgetActionChange change);
+
+GDK_AVAILABLE_IN_ALL
+void gtk_widget_add_class_actions (GtkWidget *widget);
+GDK_AVAILABLE_IN_ALL
+void gtk_widget_notify_class_action_enabled (GtkWidget *widget,
+ const char *prefixed_name);
+GDK_AVAILABLE_IN_ALL
+void gtk_widget_notify_class_action_state (GtkWidget *widget,
+ const char *prefixed_name);
+
+
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkWidget, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkRequisition, gtk_requisition_free)
diff --git a/gtk/gtkwidgetactiongroup.c b/gtk/gtkwidgetactiongroup.c
new file mode 100644
index 0000000000..ec9f8e3526
--- /dev/null
+++ b/gtk/gtkwidgetactiongroup.c
@@ -0,0 +1,270 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2019 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gtkwidgetactiongroupprivate.h"
+
+
+enum {
+ PROP_WIDGET = 1,
+ PROP_PREFIX,
+ PROP_ACTIONS
+};
+
+struct _GtkWidgetActionGroup {
+ GObject parent;
+
+ GtkWidget *widget;
+ char *prefix;
+
+ GPtrArray *actions;
+};
+
+typedef struct {
+ GObjectClass parent_class;
+} GtkWidgetActionGroupClass;
+
+static void gtk_widget_action_group_iface_init (GActionGroupInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GtkWidgetActionGroup, gtk_widget_action_group, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, gtk_widget_action_group_iface_init))
+
+static char **
+gtk_widget_action_group_list_actions (GActionGroup *action_group)
+{
+ GtkWidgetActionGroup *group = GTK_WIDGET_ACTION_GROUP (action_group);
+ GPtrArray *actions;
+ int i;
+
+ actions = g_ptr_array_new ();
+
+ for (i = 0; i < group->actions->len; i++)
+ {
+ GtkWidgetAction *action = g_ptr_array_index (group->actions, i);
+
+ if (strcmp (group->prefix, action->prefix) == 0)
+ g_ptr_array_add (actions, g_strdup (action->name));
+ }
+
+ g_ptr_array_add (actions, NULL);
+
+ return (char **)g_ptr_array_free (actions, FALSE);
+}
+
+static gboolean
+gtk_widget_action_group_query_action (GActionGroup *action_group,
+ const gchar *action_name,
+ gboolean *enabled,
+ const GVariantType **parameter_type,
+ const GVariantType **state_type,
+ GVariant **state_hint,
+ GVariant **state)
+{
+ GtkWidgetActionGroup *group = GTK_WIDGET_ACTION_GROUP (action_group);
+ int i;
+
+ for (i = 0; i < group->actions->len; i++)
+ {
+ GtkWidgetAction *action = g_ptr_array_index (group->actions, i);
+
+ if (strcmp (action->prefix, group->prefix) == 0 &&
+ strcmp (action->name, action_name) == 0)
+ {
+ return action->query (group->widget,
+ action->name,
+ enabled,
+ parameter_type,
+ state_type,
+ state_hint,
+ state);
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+gtk_widget_action_group_change_action_state (GActionGroup *action_group,
+ const gchar *action_name,
+ GVariant *value)
+{
+ GtkWidgetActionGroup *group = GTK_WIDGET_ACTION_GROUP (action_group);
+ int i;
+
+ for (i = 0; i < group->actions->len; i++)
+ {
+ GtkWidgetAction *action = g_ptr_array_index (group->actions, i);
+
+ if (strcmp (action->prefix, group->prefix) == 0 &&
+ strcmp (action->name, action_name) == 0)
+ {
+ if (action->change)
+ action->change (group->widget, action->name, value);
+
+ break;
+ }
+ }
+}
+
+static void
+gtk_widget_action_group_activate_action (GActionGroup *action_group,
+ const char *action_name,
+ GVariant *parameter)
+{
+ GtkWidgetActionGroup *group = GTK_WIDGET_ACTION_GROUP (action_group);
+ int i;
+
+ for (i = 0; i < group->actions->len; i++)
+ {
+ GtkWidgetAction *action = g_ptr_array_index (group->actions, i);
+
+ if (strcmp (action->prefix, group->prefix) == 0 &&
+ strcmp (action->name, action_name) == 0)
+ {
+ action->activate (group->widget, action->name, parameter);
+ break;
+ }
+ }
+}
+
+static void
+gtk_widget_action_group_iface_init (GActionGroupInterface *iface)
+{
+ iface->list_actions = gtk_widget_action_group_list_actions;
+ iface->query_action = gtk_widget_action_group_query_action;
+ iface->change_action_state = gtk_widget_action_group_change_action_state;
+ iface->activate_action = gtk_widget_action_group_activate_action;
+}
+
+static void
+gtk_widget_action_group_init (GtkWidgetActionGroup *group)
+{
+}
+
+static void
+gtk_widget_action_group_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkWidgetActionGroup *group = GTK_WIDGET_ACTION_GROUP (object);
+
+ switch (prop_id)
+ {
+ case PROP_WIDGET:
+ group->widget = g_value_get_object (value);
+ break;
+
+ case PROP_PREFIX:
+ group->prefix = g_value_dup_string (value);
+ break;
+
+ case PROP_ACTIONS:
+ group->actions = g_value_dup_boxed (value);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+gtk_widget_action_group_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkWidgetActionGroup *group = GTK_WIDGET_ACTION_GROUP (object);
+
+ switch (prop_id)
+ {
+ case PROP_WIDGET:
+ g_value_set_object (value, group->widget);
+ break;
+
+ case PROP_PREFIX:
+ g_value_set_string (value, group->prefix);
+ break;
+
+ case PROP_ACTIONS:
+ g_value_set_boxed (value, group->actions);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+static void
+gtk_widget_action_group_finalize (GObject *object)
+{
+ GtkWidgetActionGroup *group = GTK_WIDGET_ACTION_GROUP (object);
+
+ g_free (group->prefix);
+ g_ptr_array_unref (group->actions);
+
+ G_OBJECT_CLASS (gtk_widget_action_group_parent_class)->finalize (object);
+}
+
+static void
+gtk_widget_action_group_class_init (GtkWidgetActionGroupClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ object_class->set_property = gtk_widget_action_group_set_property;
+ object_class->get_property = gtk_widget_action_group_get_property;
+ object_class->finalize = gtk_widget_action_group_finalize;
+
+ g_object_class_install_property (object_class, PROP_WIDGET,
+ g_param_spec_object ("widget",
+ "The widget",
+ "The widget to which this action group belongs",
+ GTK_TYPE_WIDGET,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class, PROP_PREFIX,
+ g_param_spec_string ("prefix",
+ "The prefix",
+ "The prefix for actions in this group",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class, PROP_ACTIONS,
+ g_param_spec_boxed ("actions",
+ "The actions",
+ "The actions",
+ G_TYPE_PTR_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
+
+GActionGroup *
+gtk_widget_action_group_new (GtkWidget *widget,
+ const char *prefix,
+ GPtrArray *actions)
+{
+ return (GActionGroup *)g_object_new (GTK_TYPE_WIDGET_ACTION_GROUP,
+ "widget", widget,
+ "prefix", prefix,
+ "actions", actions,
+ NULL);
+}
diff --git a/gtk/gtkwidgetactiongroupprivate.h b/gtk/gtkwidgetactiongroupprivate.h
new file mode 100644
index 0000000000..f12ca2605e
--- /dev/null
+++ b/gtk/gtkwidgetactiongroupprivate.h
@@ -0,0 +1,52 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2019 Red Hat, Inc.
+ *
+ * Authors:
+ * - Matthias Clasen <mclasen redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GTK_WIDGET_ACTION_GROUP_PRIVATE_H__
+#define __GTK_WIDGET_ACTION_GROUP_PRIVATE_H__
+
+#include <gtk/gtkwidget.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_WIDGET_ACTION_GROUP (gtk_widget_action_group_get_type ())
+#define GTK_WIDGET_ACTION_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
GTK_TYPE_WIDGET_ACTION_GROUP, GtkWidgetActionGroup))
+#define GTK_IS_WIDGET_ACTION_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
GTK_TYPE_WIDGET_ACTION_GROUP))
+
+typedef struct _GtkWidgetActionGroup GtkWidgetActionGroup;
+
+typedef struct {
+ char *prefix;
+ char *name;
+
+ GtkWidgetActionActivate activate;
+ GtkWidgetActionQuery query;
+ GtkWidgetActionChange change;
+} GtkWidgetAction;
+
+
+GType gtk_widget_action_group_get_type (void) G_GNUC_CONST;
+
+GActionGroup * gtk_widget_action_group_new (GtkWidget *widget,
+ const char *prefix,
+ GPtrArray *actions);
+
+G_END_DECLS
+
+#endif /* __GTK_WIDGET_ACTION_GROUP_PRIVATE_H__ */
diff --git a/gtk/meson.build b/gtk/meson.build
index c5bd9b154f..77b4465660 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -394,6 +394,7 @@ gtk_public_sources = files([
'gtkviewport.c',
'gtkvolumebutton.c',
'gtkwidget.c',
+ 'gtkwidgetactiongroup.c',
'gtkwidgetfocus.c',
'gtkwidgetpaintable.c',
'gtkwidgetpath.c',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]