[gtk/widget-class-actions: 1/12] Allow registering actions per-class
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/widget-class-actions: 1/12] Allow registering actions per-class
- Date: Mon, 17 Jun 2019 16:18:33 +0000 (UTC)
commit 16027c9ca2e1f3ff124060b69840e3bcf506c635
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
at class init time. We avoid creating an action
group for these by teaching the action muxer
about these actions.
docs/reference/gtk/gtk4-sections.txt | 17 +++-
gtk/gtkactionmuxer.c | 138 ++++++++++++++++++++++++++++---
gtk/gtkactionmuxerprivate.h | 22 ++++-
gtk/gtkapplication.c | 2 +-
gtk/gtkwidget.c | 154 ++++++++++++++++++++++++++++++++++-
gtk/gtkwidget.h | 100 +++++++++++++++++++++++
6 files changed, 414 insertions(+), 19 deletions(-)
---
diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt
index 786bcde361..8dfe7b67e3 100644
--- a/docs/reference/gtk/gtk4-sections.txt
+++ b/docs/reference/gtk/gtk4-sections.txt
@@ -4549,10 +4549,6 @@ gtk_widget_get_opacity
gtk_widget_set_opacity
gtk_widget_get_overflow
gtk_widget_set_overflow
-gtk_widget_insert_action_group
-gtk_widget_list_action_prefixes
-gtk_widget_activate_action
-gtk_widget_activate_default
gtk_widget_measure
gtk_widget_snapshot_child
gtk_widget_get_next_sibling
@@ -4629,6 +4625,19 @@ gtk_widget_class_set_connect_func
gtk_widget_observe_children
gtk_widget_observe_controllers
+<SUBSECTION Actions>
+gtk_widget_insert_action_group
+gtk_widget_activate_action
+gtk_widget_activate_default
+GtkWidgetActionActivateFunc
+GtkWidgetActionQueryFunc
+GtkWidgetActionChangeStateFunc
+GtkWidgetActionQueryStateFunc
+gtk_widget_class_install_action
+gtk_widget_class_install_stateful_action
+gtk_widget_notify_class_action_enabled
+gtk_widget_notify_class_action_state
+
<SUBSECTION Standard>
GTK_WIDGET
GTK_IS_WIDGET
diff --git a/gtk/gtkactionmuxer.c b/gtk/gtkactionmuxer.c
index f358f8cbbd..bcc3e3370a 100644
--- a/gtk/gtkactionmuxer.c
+++ b/gtk/gtkactionmuxer.c
@@ -72,6 +72,7 @@ struct _GtkActionMuxer
GtkActionMuxer *parent;
GtkWidget *widget;
+ GPtrArray *widget_actions;
};
G_DEFINE_TYPE_WITH_CODE (GtkActionMuxer, gtk_action_muxer, G_TYPE_OBJECT,
@@ -83,6 +84,7 @@ enum
PROP_0,
PROP_PARENT,
PROP_WIDGET,
+ PROP_WIDGET_ACTIONS,
NUM_PROPERTIES
};
@@ -108,7 +110,7 @@ typedef struct
static void
gtk_action_muxer_append_group_actions (const char *prefix,
Group *group,
- GArray *actions)
+ GHashTable *actions)
{
gchar **group_actions;
gchar **action;
@@ -116,10 +118,8 @@ gtk_action_muxer_append_group_actions (const char *prefix,
group_actions = g_action_group_list_actions (group->group);
for (action = group_actions; *action; action++)
{
- gchar *fullname;
-
- fullname = g_strconcat (prefix, ".", *action, NULL);
- g_array_append_val (actions, fullname);
+ char *name = g_strconcat (prefix, ".", *action, NULL);
+ g_hash_table_add (actions, name);
}
g_strfreev (group_actions);
@@ -129,9 +129,11 @@ static gchar **
gtk_action_muxer_list_actions (GActionGroup *action_group)
{
GtkActionMuxer *muxer = GTK_ACTION_MUXER (action_group);
- GArray *actions;
+ GHashTable *actions;
+ char **keys;
- actions = g_array_new (TRUE, FALSE, sizeof (gchar *));
+ actions = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, NULL);
for ( ; muxer != NULL; muxer = muxer->parent)
{
@@ -139,12 +141,28 @@ gtk_action_muxer_list_actions (GActionGroup *action_group)
const char *prefix;
Group *group;
+ if (muxer->widget_actions)
+ {
+ int i;
+
+ for (i = 0; i < muxer->widget_actions->len; i++)
+ {
+ GtkWidgetAction *action = g_ptr_array_index (muxer->widget_actions, i);
+ g_hash_table_add (actions, g_strdup (action->name));
+ }
+ }
+
g_hash_table_iter_init (&iter, muxer->groups);
while (g_hash_table_iter_next (&iter, (gpointer *)&prefix, (gpointer *)&group))
gtk_action_muxer_append_group_actions (prefix, group, actions);
}
- return (gchar **)(void *) g_array_free (actions, FALSE);
+ keys = (char **)g_hash_table_get_keys_as_array (actions, NULL);
+
+ g_hash_table_steal_all (actions);
+ g_hash_table_unref (actions);
+
+ return (char **)keys;
}
static Group *
@@ -183,7 +201,7 @@ gtk_action_muxer_find (GtkActionMuxer *muxer,
return group->group;
}
-static void
+void
gtk_action_muxer_action_enabled_changed (GtkActionMuxer *muxer,
const gchar *action_name,
gboolean enabled)
@@ -223,7 +241,7 @@ gtk_action_muxer_parent_action_enabled_changed (GActionGroup *action_group,
gtk_action_muxer_action_enabled_changed (muxer, action_name, enabled);
}
-static void
+void
gtk_action_muxer_action_state_changed (GtkActionMuxer *muxer,
const gchar *action_name,
GVariant *state)
@@ -401,6 +419,53 @@ gtk_action_muxer_query_action (GActionGroup *action_group,
Group *group;
const gchar *unprefixed_name;
+ if (muxer->widget_actions)
+ {
+ int i;
+
+ for (i = 0; i < muxer->widget_actions->len; i++)
+ {
+ GtkWidgetAction *action = g_ptr_array_index (muxer->widget_actions, i);
+ if (strcmp (action->name, action_name) == 0)
+ {
+ if (action->query)
+ {
+ action->query (muxer->widget,
+ action->name,
+ enabled,
+ parameter_type);
+ }
+ else
+ {
+ if (enabled)
+ *enabled = TRUE;
+ if (parameter_type)
+ *parameter_type = NULL;
+ }
+
+ if (action->query_state)
+ {
+ action->query_state (muxer->widget,
+ action->name,
+ state_type,
+ state_hint,
+ state);
+ }
+ else
+ {
+ if (state_type)
+ *state_type = NULL;
+ if (state_hint)
+ *state_hint = NULL;
+ if (state)
+ *state = NULL;
+ }
+
+ return TRUE;
+ }
+ }
+ }
+
group = gtk_action_muxer_find_group (muxer, action_name, &unprefixed_name);
if (group)
@@ -424,6 +489,22 @@ gtk_action_muxer_activate_action (GActionGroup *action_group,
Group *group;
const gchar *unprefixed_name;
+ if (muxer->widget_actions)
+ {
+ int i;
+
+ for (i = 0; i < muxer->widget_actions->len; i++)
+ {
+ GtkWidgetAction *action = g_ptr_array_index (muxer->widget_actions, i);
+ if (strcmp (action->name, action_name) == 0)
+ {
+ action->activate (muxer->widget, action->name, parameter);
+
+ return;
+ }
+ }
+ }
+
group = gtk_action_muxer_find_group (muxer, action_name, &unprefixed_name);
if (group)
@@ -441,6 +522,23 @@ gtk_action_muxer_change_action_state (GActionGroup *action_group,
Group *group;
const gchar *unprefixed_name;
+ if (muxer->widget_actions)
+ {
+ int i;
+
+ for (i = 0; i < muxer->widget_actions->len; i++)
+ {
+ GtkWidgetAction *action = g_ptr_array_index (muxer->widget_actions, i);
+ if (strcmp (action->name, action_name) == 0)
+ {
+ if (action->change_state)
+ action->change_state (muxer->widget, action->name, state);
+
+ return;
+ }
+ }
+ }
+
group = gtk_action_muxer_find_group (muxer, action_name, &unprefixed_name);
if (group)
@@ -600,6 +698,10 @@ gtk_action_muxer_get_property (GObject *object,
g_value_set_object (value, muxer->widget);
break;
+ case PROP_WIDGET_ACTIONS:
+ g_value_set_boxed (value, muxer->widget_actions);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
@@ -623,6 +725,10 @@ gtk_action_muxer_set_property (GObject *object,
muxer->widget = g_value_get_object (value);
break;
+ case PROP_WIDGET_ACTIONS:
+ muxer->widget_actions = g_value_get_boxed (value);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
@@ -683,6 +789,13 @@ gtk_action_muxer_class_init (GObjectClass *class)
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
+ properties[PROP_WIDGET_ACTIONS] = g_param_spec_boxed ("widget-actions", "Widget actions",
+ "Widget actions",
+ G_TYPE_PTR_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
g_object_class_install_properties (class, NUM_PROPERTIES, properties);
}
@@ -791,14 +904,17 @@ gtk_action_muxer_lookup (GtkActionMuxer *muxer,
/*< private >
* gtk_action_muxer_new:
* @widget: the widget to which the muxer belongs
+ * @actions: widget actions
*
* Creates a new #GtkActionMuxer.
*/
GtkActionMuxer *
-gtk_action_muxer_new (GtkWidget *widget)
+gtk_action_muxer_new (GtkWidget *widget,
+ GPtrArray *actions)
{
return g_object_new (GTK_TYPE_ACTION_MUXER,
"widget", widget,
+ "widget-actions", actions,
NULL);
}
diff --git a/gtk/gtkactionmuxerprivate.h b/gtk/gtkactionmuxerprivate.h
index 190728fafd..33b96452d1 100644
--- a/gtk/gtkactionmuxerprivate.h
+++ b/gtk/gtkactionmuxerprivate.h
@@ -31,10 +31,20 @@ G_BEGIN_DECLS
#define GTK_IS_ACTION_MUXER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst),
\
GTK_TYPE_ACTION_MUXER))
+typedef struct {
+ char *name;
+
+ GtkWidgetActionActivateFunc activate;
+ GtkWidgetActionQueryFunc query;
+ GtkWidgetActionChangeStateFunc change_state;
+ GtkWidgetActionQueryStateFunc query_state;
+} GtkWidgetAction;
+
typedef struct _GtkActionMuxer GtkActionMuxer;
GType gtk_action_muxer_get_type (void);
-GtkActionMuxer * gtk_action_muxer_new (GtkWidget *widget);
+GtkActionMuxer * gtk_action_muxer_new (GtkWidget *widget,
+ GPtrArray *actions);
void gtk_action_muxer_insert (GtkActionMuxer *muxer,
const gchar *prefix,
@@ -59,6 +69,16 @@ void gtk_action_muxer_set_primary_accel (GtkActi
const gchar * gtk_action_muxer_get_primary_accel (GtkActionMuxer *muxer,
const gchar *action_and_target);
+void
+gtk_action_muxer_action_enabled_changed (GtkActionMuxer *muxer,
+ const char *action_name,
+ gboolean enabled);
+void
+gtk_action_muxer_action_state_changed (GtkActionMuxer *muxer,
+ const gchar *action_name,
+ GVariant *state);
+
+
/* No better place for these... */
gchar * gtk_print_action_and_target (const gchar *action_namespace,
const gchar *action_name,
diff --git a/gtk/gtkapplication.c b/gtk/gtkapplication.c
index f90fd827fc..3b3fe1e5ac 100644
--- a/gtk/gtkapplication.c
+++ b/gtk/gtkapplication.c
@@ -394,7 +394,7 @@ gtk_application_init (GtkApplication *application)
{
GtkApplicationPrivate *priv = gtk_application_get_instance_private (application);
- priv->muxer = gtk_action_muxer_new (NULL);
+ priv->muxer = gtk_action_muxer_new (NULL, NULL);
priv->accels = gtk_application_accels_new ();
}
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 444ecbe4dd..69bc56e987 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -501,6 +501,7 @@ struct _GtkWidgetClassPrivate
AtkRole accessible_role;
const char *css_name;
GType layout_manager_type;
+ GPtrArray *actions;
};
enum {
@@ -11897,14 +11898,16 @@ _gtk_widget_get_action_muxer (GtkWidget *widget,
gboolean create)
{
GtkActionMuxer *muxer;
+ GtkWidgetClass *widget_class = GTK_WIDGET_GET_CLASS (widget);
+ GtkWidgetClassPrivate *priv = widget_class->priv;
muxer = (GtkActionMuxer*)g_object_get_qdata (G_OBJECT (widget), quark_action_muxer);
if (muxer)
return muxer;
- if (create)
+ if (create || priv->actions)
{
- muxer = gtk_action_muxer_new (widget);
+ muxer = gtk_action_muxer_new (widget, priv->actions);
g_object_set_qdata_full (G_OBJECT (widget),
quark_action_muxer,
muxer,
@@ -13427,3 +13430,150 @@ gtk_widget_should_layout (GtkWidget *widget)
return TRUE;
}
+/*
+ * gtk_widget_class_install_action:
+ * @widget_class: a #GtkWidgetClass
+ * @action_name: a prefixed action name, such as "clipboard.paste"
+ * @activate: callback to use when the action is activated
+ * @query: (allow-none): callback to use when the action properties
+ are queried, or %NULL for always-enabled, parameterless actions
+ *
+ * This should be called at class initialization time to specify
+ * actions to be added for all instances of this class.
+ *
+ * Actions installed by this function are stateless. The only state
+ * they have is whether they are enabled or not. For more complicated
+ * stateful actions, see gtk_widget_class_install_stateful_action().
+ */
+void
+gtk_widget_class_install_action (GtkWidgetClass *widget_class,
+ const char *action_name,
+ GtkWidgetActionActivateFunc activate,
+ GtkWidgetActionQueryFunc query)
+{
+ gtk_widget_class_install_stateful_action (widget_class, action_name,
+ activate, query,
+ NULL, NULL);
+}
+
+/*
+ * gtk_widget_class_install_stateful_action:
+ * @widget_class: a #GtkWidgetClass
+ * @action_name: a prefixed action name, such as "clipboard.paste"
+ * @activate: callback to use when the action is activated
+ * @query: (allow-none): callback to use when the action properties
+ are queried, or %NULL for always-enabled stateless actions
+ * @change: (allow-none): callback to use when the action state is
+ * changed, or %NULL for stateless actions
+ * @query_state: (allow-none): callback to use when the action state
+ is queried, or %NULL for stateless actions
+ *
+ * This should be called at class initialization time to specify
+ * actions to be added for all instances of this class.
+ *
+ * Actions installed in this way can be simple or stateful.
+ * See the #GAction documentation for more information.
+ */
+void
+gtk_widget_class_install_stateful_action (GtkWidgetClass *widget_class,
+ const char *action_name,
+ GtkWidgetActionActivateFunc activate,
+ GtkWidgetActionQueryFunc query,
+ GtkWidgetActionChangeStateFunc change_state,
+ GtkWidgetActionQueryStateFunc query_state)
+{
+ GtkWidgetClassPrivate *priv = widget_class->priv;
+ GtkWidgetAction *action;
+
+ g_return_if_fail (GTK_IS_WIDGET_CLASS (widget_class));
+
+ if (priv->actions == NULL)
+ priv->actions = g_ptr_array_new ();
+ else if (GTK_IS_WIDGET_CLASS (&widget_class->parent_class))
+ {
+ GtkWidgetClass *parent_class = GTK_WIDGET_CLASS (&widget_class->parent_class);
+ GtkWidgetClassPrivate *parent_priv = parent_class->priv;
+ GPtrArray *parent_actions = parent_priv->actions;
+
+ if (priv->actions == parent_actions)
+ {
+ int i;
+
+ priv->actions = g_ptr_array_new ();
+ for (i = 0; i < parent_actions->len; i++)
+ g_ptr_array_add (priv->actions, g_ptr_array_index (parent_actions, i));
+ }
+ }
+
+ action = g_new0 (GtkWidgetAction, 1);
+ action->name = g_strdup (action_name);
+ action->activate = activate;
+ action->query = query;
+ action->change_state = change_state;
+ action->query_state = query_state;
+
+ GTK_NOTE(ACTIONS,
+ g_message ("%sClass: Adding %s action\n",
+ g_type_name (G_TYPE_FROM_CLASS (widget_class)),
+ action_name));
+
+ g_ptr_array_add (priv->actions, action);
+}
+
+/**
+ * gtk_widget_notify_class_action_enabled:
+ * @widget: a #GtkWidget
+ * @action_name: a prefixed action name, such as "clipboard.paste"
+ * @enabled: whether the action is now enabled
+ *
+ * Convenience API to notify when an action installed
+ * with gtk_widget_class_install_action() changes its
+ * enabled state. It must be called after the change
+ * has taken place (we expect the @query callback to
+ * already return the new state).
+ *
+ * This function is a more convenient alternative
+ * to calling g_action_group_action_enabled_changed()
+ * directly.
+ */
+void
+gtk_widget_notify_class_action_enabled (GtkWidget *widget,
+ const char *action_name,
+ gboolean enabled)
+{
+ GtkActionMuxer *muxer;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ muxer = _gtk_widget_get_action_muxer (widget, TRUE);
+ gtk_action_muxer_action_enabled_changed (muxer, action_name, enabled);
+}
+
+/**
+ * gtk_widget_notify_class_action_state:
+ * @widget: a #GtkWidget
+ * @action_name: a prefixed action name, such as "clipboard.paste"
+ * @state: the new state
+ *
+ * Convenience API to notify when an action installed
+ * with gtk_widget_class_install_action() changes its
+ * state. It must be called after the change has taken
+ * place (we expect the @query callback to already
+ * return the new state).
+ *
+ * This function is a more convenient alternative
+ * to calling g_action_group_action_state_changed()
+ * directly.
+ */
+void
+gtk_widget_notify_class_action_state (GtkWidget *widget,
+ const char *action_name,
+ GVariant *state)
+{
+ GtkActionMuxer *muxer;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ muxer = _gtk_widget_get_action_muxer (widget, TRUE);
+ gtk_action_muxer_action_state_changed (muxer, action_name, state);
+}
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index ca9d768ea6..196cb96224 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -1023,6 +1023,106 @@ GDK_AVAILABLE_IN_ALL
gboolean gtk_widget_should_layout (GtkWidget *widget);
+/**
+ * GtkWidgetActionActivateFunc:
+ * @widget: the widget to which the action belongs
+ * @action_name: the (unprefixed) action name
+ * @parameter: parameter for activation
+ *
+ * The type of the callback functions used for activating
+ * actions installed with gtk_widget_class_install_action().
+ *
+ * The @parameter must match the @parameter_type of the action.
+ */
+typedef void (* GtkWidgetActionActivateFunc) (GtkWidget *widget,
+ const char *action_name,
+ GVariant *parameter);
+
+/**
+ * GtkWidgetActionQueryFunc:
+ * @widget: the widget to which the action belongs
+ * @action_name: the (unprefixed) action name
+ * @enabled: (out) (optional): return location for the enabled state
+ * @parameter_type: (out) (optional): return location for the parameter type
+ *
+ * The type of the callback functions used to query
+ * the enabledness and parameter type of actions installed with
+ * gtk_widget_class_install_action().
+ *
+ * See the #GAction documentation for more details about the
+ * meaning of these properties.
+ */
+typedef void (* GtkWidgetActionQueryFunc) (GtkWidget *widget,
+ const char *action_name,
+ gboolean *enabled,
+ const GVariantType **parameter_type);
+
+/**
+ * GtkWidgetActionQueryStateFunc:
+ * @widget: the widget to which the action belongs
+ * @action_name: the (unprefixed) action name
+ * @state_type: (out) (optional): return location for the state type
+ * @state_hint: (out) (optional): return location for the state hint
+ * @state: (out) (optional): return location for the state
+ *
+ * The type of the callback functions used to query the state
+ * of stateful actions installed with gtk_widget_class_install_action().
+ *
+ * See the #GAction documentation for more details about the
+ * meaning of these properties.
+ */
+typedef void (* GtkWidgetActionQueryStateFunc) (GtkWidget *widget,
+ const char *action_name,
+ const GVariantType **state_type,
+ GVariant **state_hint,
+ GVariant **state);
+
+/**
+ * GtkWidgetActionChangeStateFunc:
+ * @widget: the widget to which the action belongs
+ * @action_name: the (unprefixed) action name
+ * @state: the new state
+ *
+ * The type of the callback functions used to change the
+ * state of actions installed with gtk_widget_class_install_action().
+ *
+ * The @state must match the @state_type of the action.
+ *
+ * Note that you can change the enabledness and state
+ * of widget actions by other means, as long as you
+ * emit the required #GActionGroup notification signals,
+ * which can be done with GtkWidget convenience API.
+ * This callback is used when the action state is
+ * changed via the #GActionGroup API.
+ */
+typedef void (*GtkWidgetActionChangeStateFunc) (GtkWidget *widget,
+ const char *action_name,
+ GVariant *state);
+
+GDK_AVAILABLE_IN_ALL
+void gtk_widget_class_install_action (GtkWidgetClass *widget_class,
+ const char *action_name,
+ GtkWidgetActionActivateFunc activate,
+ GtkWidgetActionQueryFunc query);
+
+GDK_AVAILABLE_IN_ALL
+void gtk_widget_class_install_stateful_action (GtkWidgetClass
*widget_class,
+ const char *action_name,
+ GtkWidgetActionActivateFunc activate,
+ GtkWidgetActionQueryFunc query,
+ GtkWidgetActionChangeStateFunc
change_state,
+ GtkWidgetActionQueryStateFunc
query_state);
+
+GDK_AVAILABLE_IN_ALL
+void gtk_widget_notify_class_action_enabled (GtkWidget *widget,
+ const char *action_name,
+ gboolean enabled);
+GDK_AVAILABLE_IN_ALL
+void gtk_widget_notify_class_action_state (GtkWidget *widget,
+ const char *action_name,
+ GVariant *state);
+
+
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]