参考:Gio.Action
GAction代表一个命名的action。那什么是action呢?我们运行一下gtk-3.14.15的gtk3-demo-application来使action变得可见。
是不是GAction所代表的action与鼠标点击动作之间可以划等号呢?暂时不清楚。那,什么是action呢,在这里我们可以理解为一个鼠标点击动作。
首先,我们看一下对于GAction的定义。
G_DEFINE_INTERFACE (GAction, g_action, G_TYPE_OBJECT)
GAction是一个Interface,那么Interface对应的数据结构又是什么呢。
struct _GActionInterface
{
GTypeInterface g_iface;
/* virtual functions */
const gchar * (* get_name) (GAction *action);
const GVariantType * (* get_parameter_type) (GAction *action);
const GVariantType * (* get_state_type) (GAction *action);
GVariant * (* get_state_hint) (GAction *action);
gboolean (* get_enabled) (GAction *action);
GVariant * (* get_state) (GAction *action);
void (* change_state) (GAction *action,
GVariant *value);
void (* activate) (GAction *action,
GVariant *parameter);
};
在GLib中,Interface实际上是对一组函数的描述。详见GObject – 2.0: Type System Concepts
我们之前在Gobject tutorial 六-优快云博客中举例说明过interface的定义及用法。不像我们的例子,GAction在GLib中有具体的使用功能。由于Interface中函数的实现依赖于具体的类。在GLib中,对于GAction来说,这个类就是GSimpleAction。那么,在GSimpleAction中GAction是怎么实现的呢。
G_DEFINE_TYPE_WITH_CODE (GSimpleAction, g_simple_action, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_ACTION, g_simple_action_iface_init))
void
g_simple_action_iface_init (GActionInterface *iface)
{
iface->get_name = g_simple_action_get_name;
iface->get_parameter_type = g_simple_action_get_parameter_type;
iface->get_state_type = g_simple_action_get_state_type;
iface->get_state_hint = g_simple_action_get_state_hint;
iface->get_enabled = g_simple_action_get_enabled;
iface->get_state = g_simple_action_get_state;
iface->change_state = g_simple_action_change_state;
iface->activate = g_simple_action_activate;
}
下面,我们以glib-2.78.2/gio/tests/actions.c为例,来了解一下GAction的用法。此篇,我们只了解GAction的用法这一个点,而不深究GAction与GObject的关系。由于GAction属于interface,而interface与具体类的关系,之前我们在Gobject tutorial 六-优快云博客 做过梳理。
现在,我们来看看actions.c的第一个测试函数test_basic,并在函数内注释。
static void
activate (GAction *action,
GVariant *parameter, //g_action_activate函数的第二个参数
gpointer user_data) //g_signal_connect函数的第三个参数
{
Activation *activation = user_data;
if (parameter)
activation->params = g_variant_ref (parameter);
else
activation->params = NULL;
activation->did_run = TRUE;
}
test_basic (void)
{
Activation a = { 0, };
GSimpleAction *action;
gchar *name;
GVariantType *parameter_type;
gboolean enabled;
GVariantType *state_type;
GVariant *state;
//创建GSimpleAction实例
action = g_simple_action_new ("foo", NULL);
//测试GSimpleAction实现的GAction的功能
g_assert_true (g_action_get_enabled (G_ACTION (action)));//与下面的函数调用
//g_simple_action_set_enabled (action, FALSE);形成对比,此处的验证,说明当调用函数
//g_simple_action_new创建GSimpleAction时,
//与GSimpleAction相关的GAction处于enabled状态。
g_assert_null (g_action_get_parameter_type (G_ACTION (action)));
g_assert_null (g_action_get_state_type (G_ACTION (action)));
g_assert_null (g_action_get_state_hint (G_ACTION (action)));
g_assert_null (g_action_get_state (G_ACTION (action)));
g_object_get (action,
"name", &name,
"parameter-type", ¶meter_type,
"enabled", &enabled,
"state-type", &state_type,
"state", &state,
NULL);
g_assert_cmpstr (name, ==, "foo");
g_assert_null (parameter_type);
g_assert_true (enabled);
g_assert_null (state_type);
g_assert_null (state);
g_free (name);
g_signal_connect (action, "activate", G_CALLBACK (activate), &a);
g_assert_false (a.did_run);
//生成active信号
g_action_activate (G_ACTION (action), NULL);
g_assert_true (a.did_run);
a.did_run = FALSE;
//设置action为不可用状态。
//详见https://docs.gtk.org/gio/method.SimpleAction.set_enabled.html
g_simple_action_set_enabled (action, FALSE);
g_action_activate (G_ACTION (action), NULL);
g_assert_false (a.did_run);
.......
//与action = g_simple_action_new ("foo", NULL);对比,
//说明函数g_simple_action_new的第二个参数的用法。
action = g_simple_action_new ("foo", G_VARIANT_TYPE_STRING);
g_assert_true (g_action_get_enabled (G_ACTION (action)));
g_assert_true (g_variant_type_equal (g_action_get_parameter_type (G_ACTION (action)), G_VARIANT_TYPE_STRING));
g_assert_null (g_action_get_state_type (G_ACTION (action)));
g_assert_null (g_action_get_state_hint (G_ACTION (action)));
g_assert_null (g_action_get_state (G_ACTION (action)));
g_signal_connect (action, "activate", G_CALLBACK (activate), &a);
g_assert_false (a.did_run);
//同样的,与g_simple_action_new ("foo", G_VARIANT_TYPE_STRING)一致,
//形成与g_action_activate (G_ACTION (action), NULL);的对比。
g_action_activate (G_ACTION (action), g_variant_new_string ("Hello world"));
g_assert_true (a.did_run);
g_assert_cmpstr (g_variant_get_string (a.params, NULL), ==, "Hello world");
g_variant_unref (a.params);
......
}
在官方文档Gio.Action 中,有这么一句话,An activation has a GVariant
parameter (which may be NULL
),至此,我们可以确定,这个GVariant就是函数
g_action_activate的第二个参数。g_action_get_*这些函数最终都是使用GSimpleAction实现的GAction的函数。我们就以g_action_get_enabled来举例说明。
gboolean
g_action_get_enabled (GAction *action)
{
g_return_val_if_fail (G_IS_ACTION (action), FALSE);
return G_ACTION_GET_IFACE (action)
->get_enabled (action);
}
对于GSimpleAction来说,此处的get_enabled就是函数 g_simple_action_get_enabled。
接下来我们分析test_simple_group。
test_simple_group (void)
{
GSimpleActionGroup *group;
Activation a = { 0, };
GSimpleAction *simple;
GAction *action;
gchar **actions;
GVariant *state;
simple = g_simple_action_new ("foo", NULL);
g_signal_connect (simple, "activate", G_CALLBACK (activate), &a);
g_assert_false (a.did_run);
g_action_activate (G_ACTION (simple), NULL);
g_assert_true (a.did_run);
a.did_run = FALSE;
//新建一个GSimpleActionGroup
group = g_simple_action_group_new ();
g_simple_action_group_insert (group, G_ACTION (simple));
g_object_unref (simple);
g_assert_false (a.did_run);
g_action_group_activate_action (G_ACTION_GROUP (group), "foo", NULL);
g_assert_true (a.did_run);
//相对于test_basic,这里出现新的创建GSimpleAction的方式。
//定义参看https://docs.gtk.org/gio/ctor.SimpleAction.new_stateful.html
simple = g_simple_action_new_stateful ("bar", G_VARIANT_TYPE_STRING, g_variant_new_string ("hihi"));
g_simple_action_group_insert (group, G_ACTION (simple));
g_object_unref (simple);
g_assert_true (g_action_group_has_action (G_ACTION_GROUP (group), "foo"));
g_assert_true (g_action_group_has_action (G_ACTION_GROUP (group), "bar"));
g_assert_false (g_action_group_has_action (G_ACTION_GROUP (group), "baz"));
actions = g_action_group_list_actions (G_ACTION_GROUP (group));
g_assert_cmpint (g_strv_length (actions), ==, 2);
g_assert_true (strv_set_equal ((const gchar * const *) actions, "foo", "bar", NULL));
g_strfreev (actions);
g_assert_true (g_action_group_get_action_enabled (G_ACTION_GROUP (group), "foo"));
//与g_simple_action_new类似,g_simple_action_new_stateful也会使相关Action处于
//enabled状态。
g_assert_true (g_action_group_get_action_enabled (G_ACTION_GROUP (group), "bar"));
g_assert_null (g_action_group_get_action_parameter_type (G_ACTION_GROUP (group), "foo"));
g_assert_true (g_variant_type_equal (g_action_group_get_action_parameter_type (G_ACTION_GROUP (group), "bar"), G_VARIANT_TYPE_STRING));
g_assert_null (g_action_group_get_action_state_type (G_ACTION_GROUP (group), "foo"));
g_assert_true (g_variant_type_equal (g_action_group_get_action_state_type (G_ACTION_GROUP (group), "bar"), G_VARIANT_TYPE_STRING));
g_assert_null (g_action_group_get_action_state_hint (G_ACTION_GROUP (group), "foo"));
g_assert_null (g_action_group_get_action_state_hint (G_ACTION_GROUP (group), "bar"));
g_assert_null (g_action_group_get_action_state (G_ACTION_GROUP (group), "foo"));
state = g_action_group_get_action_state (G_ACTION_GROUP (group), "bar");
g_assert_true (g_variant_type_equal (g_variant_get_type (state), G_VARIANT_TYPE_STRING));
g_assert_cmpstr (g_variant_get_string (state, NULL), ==, "hihi");
g_variant_unref (state);
g_action_group_change_action_state (G_ACTION_GROUP (group), "bar", g_variant_new_string ("boo"));
state = g_action_group_get_action_state (G_ACTION_GROUP (group), "bar");
g_assert_cmpstr (g_variant_get_string (state, NULL), ==, "boo");
g_variant_unref (state);
action = g_simple_action_group_lookup (group, "bar");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
g_assert_false (g_action_group_get_action_enabled (G_ACTION_GROUP (group), "bar"));
g_simple_action_group_remove (group, "bar");
action = g_simple_action_group_lookup (group, "foo");
g_assert_cmpstr (g_action_get_name (action), ==, "foo");
action = g_simple_action_group_lookup (group, "bar");
g_assert_null (action);
//之前以同样的名字新建过GSimpleAction,这里在测试什么呢?为了测试函数
//g_simple_action_group_insert的行为。
//详见https://docs.gtk.org/gio/method.SimpleActionGroup.insert.html
simple = g_simple_action_new ("foo", NULL);
g_simple_action_group_insert (group, G_ACTION (simple));
g_object_unref (simple);
......
}
在test_simple_group函数中,出现了一个新概念GSimpleActionGroup,和一个新的创建GSimpleAction的函数g_simple_action_new_stateful。
我们先来看看GSimpleActionGroup。官方解释Gio.SimpleActionGroup,GSimpleActionGroup是一个用于存放GAction的哈希表,且GSimpleActionGroup实现了GActionGroup interface和GActionMap interface。下面我们看看这句话在GLib中是如何体现的。我们看看GSimpleActionGroup的定义。
/**
* GSimpleActionGroup:
*
* The #GSimpleActionGroup structure contains private data and should only be accessed using the provided API.
*
* Since: 2.28
*/
struct _GSimpleActionGroup
{
/*< private >*/
GObject parent_instance;
GSimpleActionGroupPrivate *priv;
};
/**
* SECTION:gsimpleactiongroup
* @title: GSimpleActionGroup
* @short_description: A simple GActionGroup implementation
* @include: gio/gio.h
*
* #GSimpleActionGroup is a hash table filled with #GAction objects,
* implementing the #GActionGroup and #GActionMap interfaces.
**/
struct _GSimpleActionGroupPrivate
{
GHashTable *table; /* string -> GAction */
};
G_DEFINE_TYPE_WITH_CODE (GSimpleActionGroup,
g_simple_action_group, G_TYPE_OBJECT,
G_ADD_PRIVATE (GSimpleActionGroup)
G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP,
g_simple_action_group_iface_init);
G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_MAP,
g_simple_action_group_map_iface_init))
那么,在GSimpleActionGroup中,GActionGroup和GActionMap是如何被使用的呢?对此问题,我们先来看看这两个接口的定义,以及GSimpleActionGroup对这两个接口的实现。
struct _GActionGroupInterface
{
GTypeInterface g_iface;
/* virtual functions */
gboolean (* has_action) (GActionGroup *action_group,
const gchar *action_name);
gchar ** (* list_actions) (GActionGroup *action_group);
gboolean (* get_action_enabled) (GActionGroup *action_group,
const gchar *action_name);
const GVariantType * (* get_action_parameter_type) (GActionGroup *action_group,
const gchar *action_name);
const GVariantType * (* get_action_state_type) (GActionGroup *action_group,
const gchar *action_name);
GVariant * (* get_action_state_hint) (GActionGroup *action_group,
const gchar *action_name);
GVariant * (* get_action_state) (GActionGroup *action_group,
const gchar *action_name);
void (* change_action_state) (GActionGroup *action_group,
const gchar *action_name,
GVariant *value);
void (* activate_action) (GActionGroup *action_group,
const gchar *action_name,
GVariant *parameter);
/* signals */
void (* action_added) (GActionGroup *action_group,
const gchar *action_name);
void (* action_removed) (GActionGroup *action_group,
const gchar *action_name);
void (* action_enabled_changed) (GActionGroup *action_group,
const gchar *action_name,
gboolean enabled);
void (* action_state_changed) (GActionGroup *action_group,
const gchar *action_name,
GVariant *state);
/* more virtual functions */
gboolean (* query_action) (GActionGroup *action_group,
const gchar *action_name,
gboolean *enabled,
const GVariantType **parameter_type,
const GVariantType **state_type,
GVariant **state_hint,
GVariant **state);
};
struct _GActionMapInterface
{
GTypeInterface g_iface;
GAction * (* lookup_action) (GActionMap *action_map,
const gchar *action_name);
void (* add_action) (GActionMap *action_map,
GAction *action);
void (* remove_action) (GActionMap *action_map,
const gchar *action_name);
};
static void
g_simple_action_group_iface_init (GActionGroupInterface *iface)
{
iface->list_actions = g_simple_action_group_list_actions;
iface->query_action = g_simple_action_group_query_action;
iface->change_action_state = g_simple_action_group_change_state;
iface->activate_action = g_simple_action_group_activate;
}
static void
g_simple_action_group_map_iface_init (GActionMapInterface *iface)
{
iface->add_action = g_simple_action_group_add_action;
iface->remove_action = g_simple_action_group_remove_action;
iface->lookup_action = g_simple_action_group_lookup_action;
}
现在,我们举例来说明GSimpleAction是如何使用GActionGroup和GActionMap的。以test_simple_group中的g_action_group_get_action_enabled 和g_simple_action_group_lookup为例。
gboolean
g_action_group_get_action_enabled (GActionGroup *action_group,
const gchar *action_name)
{
g_return_val_if_fail (G_IS_ACTION_GROUP (action_group), FALSE);
return G_ACTION_GROUP_GET_IFACE (action_group)
->get_action_enabled (action_group, action_name);
}
GAction *
g_simple_action_group_lookup (GSimpleActionGroup *simple,
const gchar *action_name)
{
g_return_val_if_fail (G_IS_SIMPLE_ACTION_GROUP (simple), NULL);
return g_action_map_lookup_action (G_ACTION_MAP (simple), action_name);
}
GAction *
g_action_map_lookup_action (GActionMap *action_map,
const gchar *action_name)
{
return G_ACTION_MAP_GET_IFACE (action_map)
->lookup_action (action_map, action_name);
}
待续。。。。。。