mirror of
https://github.com/brl/mutter.git
synced 2024-12-23 03:22:04 +00:00
2008-12-08 Emmanuele Bassi <ebassi@linux.intel.com>
* clutter/Makefile.am: * clutter/clutter.h: Add ClutterBindingPool to the build. * clutter/clutter-binding-pool.c: * clutter/clutter-binding-pool.h: Add ClutterBindingPool, a data structure meant to hold (key symbol, modifiers) pairs and associate them to a closure. The ClutterBindingPool can be used to install key bindings for actors and then execute closures inside the key-press-event signal handlers, removing the need for big switch() or if() blocks for each key. * clutter/clutter-event.c: Consistently use "key symbol" instead of "key value". * clutter/clutter-event.h: Add more modifier masks. * clutter/clutter-marshal.list: * tests/conform/Makefile.am: * tests/conform/test-binding-pool.c: * tests/conform/test-conform-main.c: Add ClutterBindingPool conformance test. * tests/interactive/Makefile.am: * tests/interactive/test-binding-pool.c: Add interactive test (and example code) for the ClutterBindingPool usage.
This commit is contained in:
parent
008693d8b2
commit
8a537b6299
29
ChangeLog
29
ChangeLog
@ -1,3 +1,32 @@
|
||||
2008-12-08 Emmanuele Bassi <ebassi@linux.intel.com>
|
||||
|
||||
* clutter/Makefile.am:
|
||||
* clutter/clutter.h: Add ClutterBindingPool to the build.
|
||||
|
||||
* clutter/clutter-binding-pool.c:
|
||||
* clutter/clutter-binding-pool.h: Add ClutterBindingPool, a data
|
||||
structure meant to hold (key symbol, modifiers) pairs and associate
|
||||
them to a closure. The ClutterBindingPool can be used to install
|
||||
key bindings for actors and then execute closures inside the
|
||||
key-press-event signal handlers, removing the need for big
|
||||
switch() or if() blocks for each key.
|
||||
|
||||
* clutter/clutter-event.c: Consistently use "key symbol" instead
|
||||
of "key value".
|
||||
|
||||
* clutter/clutter-event.h: Add more modifier masks.
|
||||
|
||||
* clutter/clutter-marshal.list:
|
||||
|
||||
* tests/conform/Makefile.am:
|
||||
* tests/conform/test-binding-pool.c:
|
||||
* tests/conform/test-conform-main.c: Add ClutterBindingPool
|
||||
conformance test.
|
||||
|
||||
* tests/interactive/Makefile.am:
|
||||
* tests/interactive/test-binding-pool.c: Add interactive test (and
|
||||
example code) for the ClutterBindingPool usage.
|
||||
|
||||
2008-12-08 Neil Roberts <neil@linux.intel.com>
|
||||
|
||||
* clutter/clutter-main.c (_clutter_do_pick): Restore the GL_DITHER
|
||||
|
@ -56,6 +56,7 @@ source_h = \
|
||||
$(srcdir)/clutter-behaviour-path.h \
|
||||
$(srcdir)/clutter-behaviour-rotate.h \
|
||||
$(srcdir)/clutter-behaviour-scale.h \
|
||||
$(srcdir)/clutter-binding-pool.h \
|
||||
$(srcdir)/clutter-child-meta.h \
|
||||
$(srcdir)/clutter-clone-texture.h \
|
||||
$(srcdir)/clutter-color.h \
|
||||
@ -146,6 +147,7 @@ source_c = \
|
||||
clutter-behaviour-rotate.c \
|
||||
clutter-behaviour-scale.c \
|
||||
clutter-bezier.c \
|
||||
clutter-binding-pool.c \
|
||||
clutter-child-meta.c \
|
||||
clutter-clone-texture.c \
|
||||
clutter-color.c \
|
||||
|
694
clutter/clutter-binding-pool.c
Normal file
694
clutter/clutter-binding-pool.c
Normal file
@ -0,0 +1,694 @@
|
||||
/*
|
||||
* Clutter.
|
||||
*
|
||||
* An OpenGL based 'interactive canvas' library.
|
||||
*
|
||||
* Copyright (C) 2008 Intel Corporation.
|
||||
*
|
||||
* Authored By: Emmanuele Bassi <ebassi@linux.intel.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/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:clutter-binding-pool
|
||||
* @short_description: Pool for key bindings
|
||||
*
|
||||
* #ClutterBindingPool is a data structure holding a set of key bindings.
|
||||
* Each key binding associates a key symbol (eventually with modifiers)
|
||||
* to an action. A callback function is associated to each action.
|
||||
*
|
||||
* For a given key symbol and modifier mask combination there can be only one
|
||||
* action; for each action there can be only one callback. There can be
|
||||
* multiple actions with the same name, and the same callback can be used
|
||||
* to handle multiple key bindings.
|
||||
*
|
||||
* Actors requiring key bindings should create a new #ClutterBindingPool
|
||||
* inside their class initialization function and then install actions
|
||||
* like this:
|
||||
*
|
||||
* |[
|
||||
* static void
|
||||
* foo_class_init (FooClass *klass)
|
||||
* {
|
||||
* ClutterBindingPool *binding_pool;
|
||||
*
|
||||
* binding_pool = clutter_binding_pool_get_for_class (klass);
|
||||
*
|
||||
* clutter_binding_pool_install_action (binding_pool, "move-up",
|
||||
* CLUTTER_Up, 0,
|
||||
* G_CALLBACK (foo_action_move_up),
|
||||
* NULL, NULL);
|
||||
* clutter_binding_pool_install_action (binding_pool, "move-up",
|
||||
* CLUTTER_KP_Up, 0,
|
||||
* G_CALLBACK (foo_action_move_up),
|
||||
* NULL, NULL);
|
||||
* }
|
||||
* ]|
|
||||
*
|
||||
* The callback has a signature of:
|
||||
*
|
||||
* |[
|
||||
* gboolean (* callback) (GObject *instance,
|
||||
* const gchar *action_name,
|
||||
* guint key_val,
|
||||
* ClutterModifierType modifiers,
|
||||
* gpointer user_data);
|
||||
* ]|
|
||||
*
|
||||
* The actor should then override the #ClutterActor::key-press-event and
|
||||
* use clutter_binding_pool_activate() to match a #ClutterKeyEvent structure
|
||||
* to one of the actions:
|
||||
*
|
||||
* |[
|
||||
* ClutterBindingPool *pool;
|
||||
*
|
||||
* /* retrieve the binding pool for the type of the actor */
|
||||
* pool = clutter_binding_pool_find (G_OBJECT_TYPE_NAME (actor));
|
||||
*
|
||||
* /* activate any callback matching the key symbol and modifiers
|
||||
* * mask of the key event. the returned value can be directly
|
||||
* * used to signal that the actor has handled the event.
|
||||
* */
|
||||
* return clutter_binding_pool_activate (pool, G_OBJECT (actor),
|
||||
* key_event->keyval,
|
||||
* key_event->modifier_state);
|
||||
* ]|
|
||||
*
|
||||
* The clutter_binding_pool_activate() function will return %FALSE if
|
||||
* no action for the given key binding was found, if the action was
|
||||
* blocked (using clutter_binding_pool_block_action()) or if the
|
||||
* key binding handler returned %FALSE.
|
||||
*
|
||||
* #ClutterBindingPool is available since Clutter 1.0
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "clutter-binding-pool.h"
|
||||
#include "clutter-debug.h"
|
||||
#include "clutter-enum-types.h"
|
||||
#include "clutter-marshal.h"
|
||||
#include "clutter-private.h"
|
||||
|
||||
#define BINDING_MOD_MASK ((CLUTTER_SHIFT_MASK | \
|
||||
CLUTTER_CONTROL_MASK | \
|
||||
CLUTTER_MOD1_MASK | \
|
||||
CLUTTER_SUPER_MASK | \
|
||||
CLUTTER_HYPER_MASK | \
|
||||
CLUTTER_META_MASK) | CLUTTER_RELEASE_MASK)
|
||||
|
||||
typedef struct _ClutterBindingEntry ClutterBindingEntry;
|
||||
|
||||
static GSList *binding_pools = NULL;
|
||||
static GQuark key_class_bindings = 0;
|
||||
|
||||
struct _ClutterBindingPool
|
||||
{
|
||||
gchar *name; /* interned string, do not free */
|
||||
|
||||
GSList *entries;
|
||||
GHashTable *entries_hash;
|
||||
};
|
||||
|
||||
struct _ClutterBindingEntry
|
||||
{
|
||||
gchar *name; /* interned string, do not free */
|
||||
|
||||
guint key_val;
|
||||
ClutterModifierType modifiers;
|
||||
|
||||
GClosure *closure;
|
||||
|
||||
guint is_blocked : 1;
|
||||
};
|
||||
|
||||
static guint
|
||||
binding_entry_hash (gconstpointer v)
|
||||
{
|
||||
const ClutterBindingEntry *e = v;
|
||||
guint h;
|
||||
|
||||
h = e->key_val;
|
||||
h ^= e->modifiers;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static gint
|
||||
binding_entry_compare (gconstpointer v1,
|
||||
gconstpointer v2)
|
||||
{
|
||||
const ClutterBindingEntry *e1 = v1;
|
||||
const ClutterBindingEntry *e2 = v2;
|
||||
|
||||
return (e1->key_val == e2->key_val && e1->modifiers == e2->modifiers);
|
||||
}
|
||||
|
||||
static ClutterBindingEntry *
|
||||
binding_entry_new (const gchar *name,
|
||||
guint key_val,
|
||||
ClutterModifierType modifiers)
|
||||
{
|
||||
ClutterBindingEntry *entry;
|
||||
|
||||
modifiers = modifiers & BINDING_MOD_MASK;
|
||||
|
||||
entry = g_slice_new (ClutterBindingEntry);
|
||||
entry->key_val = key_val;
|
||||
entry->modifiers = modifiers;
|
||||
entry->name = (gchar *) g_intern_string (name);
|
||||
entry->closure = NULL;
|
||||
entry->is_blocked = FALSE;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static ClutterBindingEntry *
|
||||
binding_pool_lookup_entry (ClutterBindingPool *pool,
|
||||
guint key_val,
|
||||
ClutterModifierType modifiers)
|
||||
{
|
||||
ClutterBindingEntry lookup_entry = { 0, };
|
||||
|
||||
lookup_entry.key_val = key_val;
|
||||
lookup_entry.modifiers = modifiers;
|
||||
|
||||
return g_hash_table_lookup (pool->entries_hash, &lookup_entry);
|
||||
}
|
||||
|
||||
static void
|
||||
binding_entry_free (gpointer data)
|
||||
{
|
||||
if (G_LIKELY (data))
|
||||
{
|
||||
ClutterBindingEntry *entry = data;
|
||||
|
||||
g_closure_unref (entry->closure);
|
||||
|
||||
g_slice_free (ClutterBindingEntry, entry);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
binding_pool_free (gpointer data)
|
||||
{
|
||||
if (G_LIKELY (data))
|
||||
{
|
||||
ClutterBindingPool *pool = data;
|
||||
|
||||
/* remove from the pools */
|
||||
binding_pools = g_slist_remove (binding_pools, pool);
|
||||
|
||||
g_hash_table_destroy (pool->entries_hash);
|
||||
|
||||
g_slist_foreach (pool->entries, (GFunc) binding_entry_free, NULL);
|
||||
g_slist_free (pool->entries);
|
||||
|
||||
g_slice_free (ClutterBindingPool, pool);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_binding_pool_new:
|
||||
* @name: the name of the binding pool
|
||||
*
|
||||
* Creates a new #ClutterBindingPool that can be used to store
|
||||
* key bindings for an actor. The @name must be a unique identifier
|
||||
* for the binding pool, so that clutter_binding_pool_find() will
|
||||
* be able to return the correct binding pool.
|
||||
*
|
||||
* Return value: the newly created binding pool with the given
|
||||
* name. The binding pool is owned by Clutter and should not
|
||||
* be freed directly
|
||||
*
|
||||
* Since: 1.0
|
||||
*/
|
||||
ClutterBindingPool *
|
||||
clutter_binding_pool_new (const gchar *name)
|
||||
{
|
||||
ClutterBindingPool *pool;
|
||||
|
||||
g_return_val_if_fail (name != NULL, NULL);
|
||||
|
||||
pool = clutter_binding_pool_find (name);
|
||||
if (G_UNLIKELY (pool))
|
||||
{
|
||||
g_warning ("A binding pool named '%s' is already present "
|
||||
"in the binding pools list",
|
||||
pool->name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pool = g_slice_new (ClutterBindingPool);
|
||||
pool->name = (gchar *) g_intern_string (name);
|
||||
pool->entries = NULL;
|
||||
pool->entries_hash = g_hash_table_new (binding_entry_hash,
|
||||
binding_entry_compare);
|
||||
|
||||
binding_pools = g_slist_prepend (binding_pools, pool);
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_binding_pool_get_for_class:
|
||||
* @klass: a #GObjectClass pointer
|
||||
*
|
||||
* Retrieves the #ClutterBindingPool for the given #GObject class
|
||||
* and, eventually, creates it. This function is a wrapper around
|
||||
* clutter_binding_pool_new() and uses the class type name as the
|
||||
* unique name for the binding pool.
|
||||
*
|
||||
* Calling this function multiple times will return the same
|
||||
* #ClutterBindingPool.
|
||||
*
|
||||
* A binding pool for a class can also be retrieved using
|
||||
* clutter_binding_pool_find() with the class type name:
|
||||
*
|
||||
* |[
|
||||
* pool = clutter_binding_pool_find (G_OBJECT_TYPE_NAME (instance));
|
||||
* ]|
|
||||
*
|
||||
* Return value: the binding pool for the given class. The returned
|
||||
* #ClutterBindingPool is owned by Clutter and should not be freed
|
||||
* directly
|
||||
*
|
||||
* Since: 1.0
|
||||
*/
|
||||
ClutterBindingPool *
|
||||
clutter_binding_pool_get_for_class (gpointer klass)
|
||||
{
|
||||
ClutterBindingPool *pool;
|
||||
|
||||
g_return_val_if_fail (G_IS_OBJECT_CLASS (klass), NULL);
|
||||
|
||||
if (G_UNLIKELY (key_class_bindings == 0))
|
||||
key_class_bindings = g_quark_from_static_string ("clutter-bindings-set");
|
||||
|
||||
pool = g_dataset_id_get_data (klass, key_class_bindings);
|
||||
if (pool)
|
||||
return pool;
|
||||
|
||||
pool = clutter_binding_pool_new (G_OBJECT_CLASS_NAME (klass));
|
||||
g_dataset_id_set_data_full (klass, key_class_bindings,
|
||||
pool,
|
||||
binding_pool_free);
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_binding_pool_find:
|
||||
* @name: the name of the binding pool to find
|
||||
*
|
||||
* Finds the #ClutterBindingPool with @name.
|
||||
*
|
||||
* Return value: a pointer to the #ClutterBindingPool, or %NULL
|
||||
*
|
||||
* Since: 1.0
|
||||
*/
|
||||
ClutterBindingPool *
|
||||
clutter_binding_pool_find (const gchar *name)
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
g_return_val_if_fail (name != NULL, NULL);
|
||||
|
||||
for (l = binding_pools; l != NULL; l = l->next)
|
||||
{
|
||||
ClutterBindingPool *pool = l->data;
|
||||
|
||||
if (g_str_equal (pool->name, (gpointer) name))
|
||||
return pool;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_binding_pool_install_action:
|
||||
* @pool: a #ClutterBindingPool
|
||||
* @action_name: the name of the action
|
||||
* @key_val: key symbol
|
||||
* @modifiers: bitmask of modifiers
|
||||
* @callback: function to be called when the action is activated
|
||||
* @data: data to be passed to @callback
|
||||
* @notify: function to be called when the action is removed
|
||||
* from the pool
|
||||
*
|
||||
* Installs a new action inside a #ClutterBindingPool. The action
|
||||
* is bound to @key_val and @modifiers.
|
||||
*
|
||||
* The same action name can be used for multiple @key_val, @modifiers
|
||||
* pairs.
|
||||
*
|
||||
* When an action has been activated using clutter_binding_pool_activate()
|
||||
* the passed @callback will be invoked (with @data).
|
||||
*
|
||||
* Actions can be blocked with clutter_binding_pool_block_action()
|
||||
* and then unblocked using clutter_binding_pool_unblock_action().
|
||||
*
|
||||
* Since: 1.0
|
||||
*/
|
||||
void
|
||||
clutter_binding_pool_install_action (ClutterBindingPool *pool,
|
||||
const gchar *action_name,
|
||||
guint key_val,
|
||||
ClutterModifierType modifiers,
|
||||
GCallback callback,
|
||||
gpointer data,
|
||||
GDestroyNotify notify)
|
||||
{
|
||||
ClutterBindingEntry *entry;
|
||||
GClosure *closure;
|
||||
|
||||
g_return_if_fail (pool != NULL);
|
||||
g_return_if_fail (action_name != NULL);
|
||||
g_return_if_fail (key_val != 0);
|
||||
g_return_if_fail (callback != NULL);
|
||||
|
||||
entry = binding_pool_lookup_entry (pool, key_val, modifiers);
|
||||
if (G_UNLIKELY (entry))
|
||||
{
|
||||
g_warning ("There already is an action '%s' for the given "
|
||||
"key symbol of %d (modifiers: %d) installed inside "
|
||||
"the binding pool.",
|
||||
entry->name,
|
||||
entry->key_val, entry->modifiers);
|
||||
return;
|
||||
}
|
||||
else
|
||||
entry = binding_entry_new (action_name, key_val, modifiers);
|
||||
|
||||
closure = g_cclosure_new (callback, data, (GClosureNotify) notify);
|
||||
entry->closure = g_closure_ref (closure);
|
||||
g_closure_sink (closure);
|
||||
|
||||
if (G_CLOSURE_NEEDS_MARSHAL (closure))
|
||||
{
|
||||
GClosureMarshal marshal;
|
||||
|
||||
marshal = clutter_marshal_BOOLEAN__STRING_UINT_ENUM;
|
||||
g_closure_set_marshal (closure, marshal);
|
||||
}
|
||||
|
||||
pool->entries = g_slist_prepend (pool->entries, entry);
|
||||
g_hash_table_insert (pool->entries_hash, entry, entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_binding_pool_install_closure:
|
||||
* @pool: a #ClutterBindingPool
|
||||
* @action_name: the name of the action
|
||||
* @key_val: key symbol
|
||||
* @modifiers: bitmask of modifiers
|
||||
* @closure: a #GClosure
|
||||
*
|
||||
* A #GClosure variant of clutter_binding_pool_install_action().
|
||||
*
|
||||
* Installs a new action inside a #ClutterBindingPool. The action
|
||||
* is bound to @key_val and @modifiers.
|
||||
*
|
||||
* The same action name can be used for multiple @key_val, @modifiers
|
||||
* pairs.
|
||||
*
|
||||
* When an action has been activated using clutter_binding_pool_activate()
|
||||
* the passed @closure will be invoked.
|
||||
*
|
||||
* Actions can be blocked with clutter_binding_pool_block_action()
|
||||
* and then unblocked using clutter_binding_pool_unblock_action().
|
||||
*
|
||||
* Since: 1.0
|
||||
*/
|
||||
void
|
||||
clutter_binding_pool_install_closure (ClutterBindingPool *pool,
|
||||
const gchar *action_name,
|
||||
guint key_val,
|
||||
ClutterModifierType modifiers,
|
||||
GClosure *closure)
|
||||
{
|
||||
ClutterBindingEntry *entry;
|
||||
|
||||
g_return_if_fail (pool != NULL);
|
||||
g_return_if_fail (action_name != NULL);
|
||||
g_return_if_fail (key_val != 0);
|
||||
g_return_if_fail (closure != NULL);
|
||||
|
||||
entry = binding_pool_lookup_entry (pool, key_val, modifiers);
|
||||
if (G_UNLIKELY (entry))
|
||||
{
|
||||
g_warning ("There already is an action '%s' for the given "
|
||||
"key symbol of %d (modifiers: %d) installed inside "
|
||||
"the binding pool.",
|
||||
entry->name,
|
||||
entry->key_val, entry->modifiers);
|
||||
return;
|
||||
}
|
||||
else
|
||||
entry = binding_entry_new (action_name, key_val, modifiers);
|
||||
|
||||
entry->closure = g_closure_ref (closure);
|
||||
g_closure_sink (closure);
|
||||
|
||||
if (G_CLOSURE_NEEDS_MARSHAL (closure))
|
||||
{
|
||||
GClosureMarshal marshal;
|
||||
|
||||
marshal = clutter_marshal_BOOLEAN__STRING_UINT_ENUM;
|
||||
g_closure_set_marshal (closure, marshal);
|
||||
}
|
||||
|
||||
pool->entries = g_slist_prepend (pool->entries, entry);
|
||||
g_hash_table_insert (pool->entries_hash, entry, entry);
|
||||
}
|
||||
|
||||
gchar **
|
||||
clutter_binding_pool_list_actions (ClutterBindingPool *pool)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_binding_pool_find_action:
|
||||
* @pool: a #ClutterBindingPool
|
||||
* @key_val: a key symbol
|
||||
* @modifiers: a bitmask for the modifiers
|
||||
*
|
||||
* Retrieves the name of the action matching the given key symbol
|
||||
* and modifiers bitmask.
|
||||
*
|
||||
* Return value: the name of the action, if found, or %NULL. The
|
||||
* returned string is owned by the binding pool and should never
|
||||
* be modified or freed
|
||||
*
|
||||
* Since: 1.0
|
||||
*/
|
||||
G_CONST_RETURN gchar *
|
||||
clutter_binding_pool_find_action (ClutterBindingPool *pool,
|
||||
guint key_val,
|
||||
ClutterModifierType modifiers)
|
||||
{
|
||||
ClutterBindingEntry *entry;
|
||||
|
||||
g_return_val_if_fail (pool != NULL, NULL);
|
||||
g_return_val_if_fail (key_val != 0, NULL);
|
||||
|
||||
entry = binding_pool_lookup_entry (pool, key_val, modifiers);
|
||||
if (!entry)
|
||||
return NULL;
|
||||
|
||||
return entry->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_binding_pool_remove_action:
|
||||
* @pool: a #ClutterBindingPool
|
||||
* @key_val: a key symbol
|
||||
* @modifiers: a bitmask for the modifiers
|
||||
*
|
||||
* Removes the action matching the given @key_val, @modifiers pair,
|
||||
* if any exists.
|
||||
*
|
||||
* Since: 1.0
|
||||
*/
|
||||
void
|
||||
clutter_binding_pool_remove_action (ClutterBindingPool *pool,
|
||||
guint key_val,
|
||||
ClutterModifierType modifiers)
|
||||
{
|
||||
ClutterBindingEntry remove_entry = { 0, };
|
||||
|
||||
g_return_if_fail (pool != NULL);
|
||||
g_return_if_fail (key_val != 0);
|
||||
|
||||
modifiers = modifiers & BINDING_MOD_MASK;
|
||||
|
||||
remove_entry.key_val = key_val;
|
||||
remove_entry.modifiers = modifiers;
|
||||
|
||||
g_hash_table_remove (pool->entries_hash, &remove_entry);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
clutter_binding_entry_invoke (ClutterBindingEntry *entry,
|
||||
GObject *gobject)
|
||||
{
|
||||
GValue params[4] = { { 0, }, { 0, }, { 0, }, { 0, } };
|
||||
GValue result = { 0, };
|
||||
gboolean retval = TRUE;
|
||||
|
||||
g_value_init (¶ms[0], G_TYPE_OBJECT);
|
||||
g_value_set_object (¶ms[0], gobject);
|
||||
|
||||
g_value_init (¶ms[1], G_TYPE_STRING);
|
||||
g_value_set_string (¶ms[1], entry->name);
|
||||
|
||||
g_value_init (¶ms[2], G_TYPE_UINT);
|
||||
g_value_set_uint (¶ms[2], entry->key_val);
|
||||
|
||||
g_value_init (¶ms[3], CLUTTER_TYPE_MODIFIER_TYPE);
|
||||
g_value_set_flags (¶ms[3], entry->modifiers);
|
||||
|
||||
g_value_init (&result, G_TYPE_BOOLEAN);
|
||||
|
||||
g_closure_invoke (entry->closure, &result, 4, params, NULL);
|
||||
|
||||
retval = g_value_get_boolean (&result);
|
||||
|
||||
g_value_unset (&result);
|
||||
|
||||
g_value_unset (¶ms[0]);
|
||||
g_value_unset (¶ms[1]);
|
||||
g_value_unset (¶ms[2]);
|
||||
g_value_unset (¶ms[3]);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_binding_pool_activate:
|
||||
* @pool: a #ClutterBindingPool
|
||||
* @key_val: the key symbol
|
||||
* @modifiers: bitmask for the modifiers
|
||||
* @gobject: a #GObject
|
||||
*
|
||||
* Activates the callback associated to the action that is
|
||||
* bound to the @key_val and @modifiers pair.
|
||||
*
|
||||
* The callback has the following signature:
|
||||
*
|
||||
* |[
|
||||
* void (* callback) (GObject *gobject,
|
||||
* const gchar *action_name,
|
||||
* guint key_val,
|
||||
* ClutterModifierType modifiers,
|
||||
* gpointer user_data);
|
||||
* ]|
|
||||
*
|
||||
* Where the #GObject instance is @gobject and the user data
|
||||
* is the one passed when installing the action with
|
||||
* clutter_binding_pool_install_action().
|
||||
*
|
||||
* If the action bound to the @key_val, @modifiers pair has been
|
||||
* blocked using clutter_binding_pool_block_action(), the callback
|
||||
* will not be invoked, and this function will return %FALSE.
|
||||
*
|
||||
* Return value: %TRUE if an action was found and was activated
|
||||
*
|
||||
* Since: 1.0
|
||||
*/
|
||||
gboolean
|
||||
clutter_binding_pool_activate (ClutterBindingPool *pool,
|
||||
guint key_val,
|
||||
ClutterModifierType modifiers,
|
||||
GObject *gobject)
|
||||
{
|
||||
ClutterBindingEntry *entry = NULL;
|
||||
|
||||
g_return_val_if_fail (pool != NULL, FALSE);
|
||||
g_return_val_if_fail (key_val != 0, FALSE);
|
||||
g_return_val_if_fail (G_IS_OBJECT (gobject), FALSE);
|
||||
|
||||
modifiers = (modifiers & BINDING_MOD_MASK);
|
||||
|
||||
entry = binding_pool_lookup_entry (pool, key_val, modifiers);
|
||||
if (!entry)
|
||||
return FALSE;
|
||||
|
||||
if (!entry->is_blocked)
|
||||
return clutter_binding_entry_invoke (entry, gobject);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_binding_pool_block_action:
|
||||
* @pool: a #ClutterBindingPool
|
||||
* @action_name: an action name
|
||||
*
|
||||
* Blocks all the actions with name @action_name inside @pool.
|
||||
*
|
||||
* Since: 1.0
|
||||
*/
|
||||
void
|
||||
clutter_binding_pool_block_action (ClutterBindingPool *pool,
|
||||
const gchar *action_name)
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
g_return_if_fail (pool != NULL);
|
||||
g_return_if_fail (action_name != NULL);
|
||||
|
||||
for (l = pool->entries; l != NULL; l = l->next)
|
||||
{
|
||||
ClutterBindingEntry *entry = l->data;
|
||||
|
||||
if (g_str_equal (entry->name, (gpointer) action_name))
|
||||
entry->is_blocked = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_binding_pool_unblock_action:
|
||||
* @pool: a #ClutterBindingPool
|
||||
* @action_name: an action name
|
||||
*
|
||||
* Unblockes all the actions with name @action_name inside @pool.
|
||||
*
|
||||
* Unblocking an action does not cause the callback bound to it to
|
||||
* be invoked in case clutter_binding_pool_activate() was called on
|
||||
* an action previously blocked with clutter_binding_pool_block_action().
|
||||
*
|
||||
* Since: 1.0
|
||||
*/
|
||||
void
|
||||
clutter_binding_pool_unblock_action (ClutterBindingPool *pool,
|
||||
const gchar *action_name)
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
g_return_if_fail (pool != NULL);
|
||||
g_return_if_fail (action_name != NULL);
|
||||
|
||||
for (l = pool->entries; l != NULL; l = l->next)
|
||||
{
|
||||
ClutterBindingEntry *entry = l->data;
|
||||
|
||||
if (g_str_equal (entry->name, (gpointer) action_name))
|
||||
entry->is_blocked = FALSE;
|
||||
}
|
||||
}
|
96
clutter/clutter-binding-pool.h
Normal file
96
clutter/clutter-binding-pool.h
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Clutter.
|
||||
*
|
||||
* An OpenGL based 'interactive canvas' library.
|
||||
*
|
||||
* Copyright (C) 2008 Intel Corporation.
|
||||
*
|
||||
* Authored By: Emmanuele Bassi <ebassi@linux.intel.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/>.
|
||||
*/
|
||||
|
||||
#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
|
||||
#error "Only <clutter/clutter.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#ifndef __CLUTTER_BINDING_POOL_H__
|
||||
#define __CLUTTER_BINDING_POOL_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <clutter/clutter-event.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _ClutterBindingPool ClutterBindingPool;
|
||||
|
||||
/**
|
||||
* ClutterBindingActionFunc:
|
||||
* @gobject: a #GObject
|
||||
* @action_name: the name of the action
|
||||
* @key_val: the key symbol
|
||||
* @modifiers: bitmask of the modifier flags
|
||||
*
|
||||
* The prototype for the callback function registered with
|
||||
* clutter_binding_pool_install_action() and invoked by
|
||||
* clutter_binding_pool_activate().
|
||||
*
|
||||
* The function should return %TRUE if the key binding was
|
||||
* handled, and return %FALSE otherwise.
|
||||
*
|
||||
* Since: 1.0
|
||||
*/
|
||||
typedef gboolean (* ClutterBindingActionFunc) (GObject *gobject,
|
||||
const gchar *action_name,
|
||||
guint key_val,
|
||||
ClutterModifierType modifiers);
|
||||
|
||||
ClutterBindingPool * clutter_binding_pool_new (const gchar *name);
|
||||
ClutterBindingPool * clutter_binding_pool_get_for_class (gpointer klass);
|
||||
ClutterBindingPool * clutter_binding_pool_find (const gchar *name);
|
||||
|
||||
void clutter_binding_pool_install_action (ClutterBindingPool *pool,
|
||||
const gchar *action_name,
|
||||
guint key_val,
|
||||
ClutterModifierType modifiers,
|
||||
GCallback callback,
|
||||
gpointer data,
|
||||
GDestroyNotify notify);
|
||||
void clutter_binding_pool_install_closure (ClutterBindingPool *pool,
|
||||
const gchar *action_name,
|
||||
guint key_val,
|
||||
ClutterModifierType modifiers,
|
||||
GClosure *closure);
|
||||
|
||||
gchar ** clutter_binding_pool_list_actions (ClutterBindingPool *pool);
|
||||
G_CONST_RETURN gchar *clutter_binding_pool_find_action (ClutterBindingPool *pool,
|
||||
guint key_val,
|
||||
ClutterModifierType modifiers);
|
||||
void clutter_binding_pool_remove_action (ClutterBindingPool *pool,
|
||||
guint key_val,
|
||||
ClutterModifierType modifiers);
|
||||
|
||||
gboolean clutter_binding_pool_activate (ClutterBindingPool *pool,
|
||||
guint key_val,
|
||||
ClutterModifierType modifiers,
|
||||
GObject *gobject);
|
||||
|
||||
void clutter_binding_pool_block_action (ClutterBindingPool *pool,
|
||||
const gchar *action_name);
|
||||
void clutter_binding_pool_unblock_action (ClutterBindingPool *pool,
|
||||
const gchar *action_name);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __CLUTTER_BINDING_POOL_H__ */
|
@ -224,9 +224,9 @@ clutter_button_event_button (ClutterButtonEvent *buttev)
|
||||
* clutter_key_event_symbol:
|
||||
* @keyev: A #ClutterKeyEvent
|
||||
*
|
||||
* Retrieves the value of the key that caused @keyev.
|
||||
* Retrieves the symbol of the key that caused @keyev.
|
||||
*
|
||||
* Return value: The keysym representing the key
|
||||
* Return value: The key symbol representing the key
|
||||
*/
|
||||
guint
|
||||
clutter_key_event_symbol (ClutterKeyEvent *keyev)
|
||||
@ -273,7 +273,7 @@ clutter_key_event_unicode (ClutterKeyEvent *keyev)
|
||||
|
||||
/**
|
||||
* clutter_keysym_to_unicode:
|
||||
* @keyval: a clutter key symbol
|
||||
* @keyval: a key symbol
|
||||
*
|
||||
* Convert from a Clutter key symbol to the corresponding ISO10646 (Unicode)
|
||||
* character.
|
||||
|
@ -68,6 +68,11 @@ G_BEGIN_DECLS
|
||||
* @CLUTTER_BUTTON3_MASK: Mask applied by the third pointer button
|
||||
* @CLUTTER_BUTTON4_MASK: Mask applied by the fourth pointer button
|
||||
* @CLUTTER_BUTTON5_MASK: Mask applied by the fifth pointer button
|
||||
* @CLUTTER_SUPER_MASK: Mask applied by the Super key
|
||||
* @CLUTTER_HYPER_MASK: Mask applied by the Hyper key
|
||||
* @CLUTTER_META_MASK: Mask applied by the Meta key
|
||||
* @CLUTTER_RELEASE_MASK: Mask applied during release
|
||||
* @CLUTTER_MODIFIER_MASK: A mask covering all modifier types
|
||||
*
|
||||
* Masks applied to a #ClutterEvent by modifiers.
|
||||
*
|
||||
@ -86,7 +91,17 @@ typedef enum {
|
||||
CLUTTER_BUTTON2_MASK = 1 << 9,
|
||||
CLUTTER_BUTTON3_MASK = 1 << 10,
|
||||
CLUTTER_BUTTON4_MASK = 1 << 11,
|
||||
CLUTTER_BUTTON5_MASK = 1 << 12
|
||||
CLUTTER_BUTTON5_MASK = 1 << 12,
|
||||
|
||||
/* bits 15 to 25 are currently unused; bit 29 is used internally */
|
||||
|
||||
CLUTTER_SUPER_MASK = 1 << 26,
|
||||
CLUTTER_HYPER_MASK = 1 << 27,
|
||||
CLUTTER_META_MASK = 1 << 28,
|
||||
|
||||
CLUTTER_RELEASE_MASK = 1 << 30,
|
||||
|
||||
CLUTTER_MODIFIER_MASK = 0x5c001fff
|
||||
} ClutterModifierType;
|
||||
|
||||
/**
|
||||
|
@ -1,4 +1,5 @@
|
||||
BOOLEAN:BOXED
|
||||
BOOLEAN:STRING,UINT,ENUM
|
||||
UINT:VOID
|
||||
VOID:BOXED
|
||||
VOID:INT
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include "clutter-behaviour-path.h"
|
||||
#include "clutter-behaviour-rotate.h"
|
||||
#include "clutter-behaviour-scale.h"
|
||||
#include "clutter-binding-pool.h"
|
||||
#include "clutter-child-meta.h"
|
||||
#include "clutter-clone-texture.h"
|
||||
#include "clutter-deprecated.h"
|
||||
|
@ -21,7 +21,8 @@ test_conformance_SOURCES = \
|
||||
test-clutter-fixed.c \
|
||||
test-actor-invariants.c \
|
||||
test-paint-opacity.c \
|
||||
test-backface-culling.c
|
||||
test-backface-culling.c \
|
||||
test-binding-pool.c
|
||||
|
||||
# For convenience, this provides a way to easily run individual unit tests:
|
||||
.PHONY: wrappers
|
||||
|
313
tests/conform/test-binding-pool.c
Normal file
313
tests/conform/test-binding-pool.c
Normal file
@ -0,0 +1,313 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
#include <clutter/clutter-keysyms.h>
|
||||
|
||||
#include "test-conform-common.h"
|
||||
|
||||
#define TYPE_KEY_GROUP (key_group_get_type ())
|
||||
#define KEY_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_KEY_GROUP, KeyGroup))
|
||||
#define IS_KEY_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_KEY_GROUP))
|
||||
#define KEY_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_KEY_GROUP, KeyGroupClass))
|
||||
#define IS_KEY_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_KEY_GROUP))
|
||||
|
||||
typedef struct _KeyGroup KeyGroup;
|
||||
typedef struct _KeyGroupClass KeyGroupClass;
|
||||
|
||||
struct _KeyGroup
|
||||
{
|
||||
ClutterGroup parent_instance;
|
||||
|
||||
gint selected_index;
|
||||
};
|
||||
|
||||
struct _KeyGroupClass
|
||||
{
|
||||
ClutterGroupClass parent_class;
|
||||
|
||||
void (* activate) (KeyGroup *group,
|
||||
ClutterActor *child);
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (KeyGroup, key_group, CLUTTER_TYPE_GROUP);
|
||||
|
||||
enum
|
||||
{
|
||||
ACTIVATE,
|
||||
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint group_signals[LAST_SIGNAL] = { 0, };
|
||||
|
||||
static gboolean
|
||||
key_group_action_move_left (KeyGroup *self,
|
||||
const gchar *action_name,
|
||||
guint key_val,
|
||||
ClutterModifierType modifiers)
|
||||
{
|
||||
gint n_children;
|
||||
|
||||
g_assert_cmpstr (action_name, ==, "move-left");
|
||||
g_assert_cmpint (key_val, ==, CLUTTER_Left);
|
||||
|
||||
n_children = clutter_group_get_n_children (CLUTTER_GROUP (self));
|
||||
|
||||
self->selected_index -= 1;
|
||||
|
||||
if (self->selected_index < 0)
|
||||
self->selected_index = n_children - 1;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
key_group_action_move_right (KeyGroup *self,
|
||||
const gchar *action_name,
|
||||
guint key_val,
|
||||
ClutterModifierType modifiers)
|
||||
{
|
||||
gint n_children;
|
||||
|
||||
g_assert_cmpstr (action_name, ==, "move-right");
|
||||
g_assert_cmpint (key_val, ==, CLUTTER_Right);
|
||||
|
||||
n_children = clutter_group_get_n_children (CLUTTER_GROUP (self));
|
||||
|
||||
self->selected_index += 1;
|
||||
|
||||
if (self->selected_index >= n_children)
|
||||
self->selected_index = 0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
key_group_action_activate (KeyGroup *self,
|
||||
const gchar *action_name,
|
||||
guint key_val,
|
||||
ClutterModifierType modifiers)
|
||||
{
|
||||
ClutterActor *child = NULL;
|
||||
|
||||
g_assert_cmpstr (action_name, ==, "activate");
|
||||
g_assert (key_val == CLUTTER_Return ||
|
||||
key_val == CLUTTER_KP_Enter ||
|
||||
key_val == CLUTTER_ISO_Enter);
|
||||
|
||||
if (self->selected_index == -1)
|
||||
return FALSE;
|
||||
|
||||
child = clutter_group_get_nth_child (CLUTTER_GROUP (self),
|
||||
self->selected_index);
|
||||
|
||||
if (child)
|
||||
{
|
||||
g_signal_emit (self, group_signals[ACTIVATE], 0, child);
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
key_group_key_press (ClutterActor *actor,
|
||||
ClutterKeyEvent *event)
|
||||
{
|
||||
ClutterBindingPool *pool;
|
||||
gboolean res;
|
||||
|
||||
pool = clutter_binding_pool_find (G_OBJECT_TYPE_NAME (actor));
|
||||
g_assert (pool != NULL);
|
||||
|
||||
res = clutter_binding_pool_activate (pool,
|
||||
event->keyval,
|
||||
event->modifier_state,
|
||||
G_OBJECT (actor));
|
||||
|
||||
/* if we activate a key binding, redraw the actor */
|
||||
if (res)
|
||||
clutter_actor_queue_redraw (actor);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void
|
||||
key_group_paint (ClutterActor *actor)
|
||||
{
|
||||
KeyGroup *self = KEY_GROUP (actor);
|
||||
GList *children, *l;
|
||||
gint i;
|
||||
|
||||
children = clutter_container_get_children (CLUTTER_CONTAINER (self));
|
||||
|
||||
for (l = children, i = 0; l != NULL; l = l->next, i++)
|
||||
{
|
||||
ClutterActor *child = l->data;
|
||||
|
||||
/* paint the selection rectangle */
|
||||
if (i == self->selected_index)
|
||||
{
|
||||
ClutterActorBox box = { 0, };
|
||||
|
||||
clutter_actor_get_allocation_box (child, &box);
|
||||
|
||||
box.x1 -= CLUTTER_UNITS_FROM_DEVICE (2);
|
||||
box.y1 -= CLUTTER_UNITS_FROM_DEVICE (2);
|
||||
box.x2 += CLUTTER_UNITS_FROM_DEVICE (2);
|
||||
box.y2 += CLUTTER_UNITS_FROM_DEVICE (2);
|
||||
|
||||
cogl_set_source_color4ub (255, 255, 0, 224);
|
||||
cogl_rectangle (CLUTTER_UNITS_TO_DEVICE (box.x1),
|
||||
CLUTTER_UNITS_TO_DEVICE (box.y1),
|
||||
CLUTTER_UNITS_TO_DEVICE (box.x2 - box.x1),
|
||||
CLUTTER_UNITS_TO_DEVICE (box.y2 - box.y1));
|
||||
}
|
||||
|
||||
if (CLUTTER_ACTOR_IS_VISIBLE (child))
|
||||
clutter_actor_paint (child);
|
||||
}
|
||||
|
||||
g_list_free (children);
|
||||
}
|
||||
|
||||
static void
|
||||
key_group_finalize (GObject *gobject)
|
||||
{
|
||||
G_OBJECT_CLASS (key_group_parent_class)->finalize (gobject);
|
||||
}
|
||||
|
||||
static void
|
||||
key_group_class_init (KeyGroupClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||
ClutterBindingPool *binding_pool;
|
||||
|
||||
gobject_class->finalize = key_group_finalize;
|
||||
|
||||
actor_class->paint = key_group_paint;
|
||||
actor_class->key_press_event = key_group_key_press;
|
||||
|
||||
group_signals[ACTIVATE] =
|
||||
g_signal_new (g_intern_static_string ("activate"),
|
||||
G_OBJECT_CLASS_TYPE (gobject_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (KeyGroupClass, activate),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__OBJECT,
|
||||
G_TYPE_NONE, 1,
|
||||
CLUTTER_TYPE_ACTOR);
|
||||
|
||||
binding_pool = clutter_binding_pool_get_for_class (klass);
|
||||
|
||||
clutter_binding_pool_install_action (binding_pool, "move-right",
|
||||
CLUTTER_Right, 0,
|
||||
G_CALLBACK (key_group_action_move_right),
|
||||
NULL, NULL);
|
||||
clutter_binding_pool_install_action (binding_pool, "move-left",
|
||||
CLUTTER_Left, 0,
|
||||
G_CALLBACK (key_group_action_move_left),
|
||||
NULL, NULL);
|
||||
clutter_binding_pool_install_action (binding_pool, "activate",
|
||||
CLUTTER_Return, 0,
|
||||
G_CALLBACK (key_group_action_activate),
|
||||
NULL, NULL);
|
||||
clutter_binding_pool_install_action (binding_pool, "activate",
|
||||
CLUTTER_KP_Enter, 0,
|
||||
G_CALLBACK (key_group_action_activate),
|
||||
NULL, NULL);
|
||||
clutter_binding_pool_install_action (binding_pool, "activate",
|
||||
CLUTTER_ISO_Enter, 0,
|
||||
G_CALLBACK (key_group_action_activate),
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
key_group_init (KeyGroup *self)
|
||||
{
|
||||
self->selected_index = -1;
|
||||
}
|
||||
|
||||
static void
|
||||
init_event (ClutterKeyEvent *event)
|
||||
{
|
||||
event->type = CLUTTER_KEY_PRESS;
|
||||
event->time = 0; /* not needed */
|
||||
event->flags = CLUTTER_EVENT_FLAG_SYNTHETIC;
|
||||
event->stage = NULL; /* not needed */
|
||||
event->source = NULL; /* not needed */
|
||||
event->modifier_state = 0;
|
||||
event->hardware_keycode = 0; /* not needed */
|
||||
}
|
||||
|
||||
static void
|
||||
send_keyval (KeyGroup *group, int keyval)
|
||||
{
|
||||
ClutterKeyEvent event;
|
||||
|
||||
init_event (&event);
|
||||
event.keyval = keyval;
|
||||
event.unicode_value = 0; /* should be ignored for cursor keys etc. */
|
||||
|
||||
clutter_actor_event (CLUTTER_ACTOR (group), (ClutterEvent *) &event, FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
on_activate (KeyGroup *key_group,
|
||||
ClutterActor *child,
|
||||
gpointer data)
|
||||
{
|
||||
gint index = GPOINTER_TO_INT (data);
|
||||
|
||||
g_assert_cmpint (key_group->selected_index, ==, index);
|
||||
}
|
||||
|
||||
void
|
||||
test_binding_pool (TestConformSimpleFixture *fixture,
|
||||
gconstpointer data)
|
||||
{
|
||||
KeyGroup *key_group = g_object_new (TYPE_KEY_GROUP, NULL);
|
||||
|
||||
clutter_container_add (CLUTTER_CONTAINER (key_group),
|
||||
g_object_new (CLUTTER_TYPE_RECTANGLE,
|
||||
"width", 50,
|
||||
"height", 50,
|
||||
"x", 0, "y", 0,
|
||||
NULL),
|
||||
g_object_new (CLUTTER_TYPE_RECTANGLE,
|
||||
"width", 50,
|
||||
"height", 50,
|
||||
"x", 75, "y", 0,
|
||||
NULL),
|
||||
g_object_new (CLUTTER_TYPE_RECTANGLE,
|
||||
"width", 50,
|
||||
"height", 50,
|
||||
"x", 150, "y", 0,
|
||||
NULL),
|
||||
NULL);
|
||||
|
||||
g_assert_cmpint (key_group->selected_index, ==, -1);
|
||||
|
||||
send_keyval (key_group, CLUTTER_Left);
|
||||
g_assert_cmpint (key_group->selected_index, ==, 2);
|
||||
|
||||
send_keyval (key_group, CLUTTER_Left);
|
||||
g_assert_cmpint (key_group->selected_index, ==, 1);
|
||||
|
||||
send_keyval (key_group, CLUTTER_Right);
|
||||
g_assert_cmpint (key_group->selected_index, ==, 2);
|
||||
|
||||
send_keyval (key_group, CLUTTER_Right);
|
||||
g_assert_cmpint (key_group->selected_index, ==, 0);
|
||||
|
||||
g_signal_connect (key_group,
|
||||
"activate", G_CALLBACK (on_activate),
|
||||
GINT_TO_POINTER (0));
|
||||
|
||||
send_keyval (key_group, CLUTTER_Return);
|
||||
|
||||
clutter_actor_destroy (CLUTTER_ACTOR (key_group));
|
||||
}
|
@ -107,5 +107,7 @@ main (int argc, char **argv)
|
||||
|
||||
TEST_CONFORM_SIMPLE ("/path", test_path);
|
||||
|
||||
TEST_CONFORM_SIMPLE ("/binding-pool", test_binding_pool);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
@ -37,7 +37,8 @@ UNIT_TESTS = \
|
||||
test-texture-quality.c \
|
||||
test-layout.c \
|
||||
test-animation.c \
|
||||
test-easing.c
|
||||
test-easing.c \
|
||||
test-binding-pool.c
|
||||
|
||||
if X11_TESTS
|
||||
UNIT_TESTS += test-pixmap.c
|
||||
|
313
tests/interactive/test-binding-pool.c
Normal file
313
tests/interactive/test-binding-pool.c
Normal file
@ -0,0 +1,313 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <gmodule.h>
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
#include <clutter/clutter-keysyms.h>
|
||||
|
||||
#define TYPE_KEY_GROUP (key_group_get_type ())
|
||||
#define KEY_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_KEY_GROUP, KeyGroup))
|
||||
#define IS_KEY_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_KEY_GROUP))
|
||||
#define KEY_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_KEY_GROUP, KeyGroupClass))
|
||||
#define IS_KEY_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_KEY_GROUP))
|
||||
|
||||
typedef struct _KeyGroup KeyGroup;
|
||||
typedef struct _KeyGroupClass KeyGroupClass;
|
||||
|
||||
struct _KeyGroup
|
||||
{
|
||||
ClutterGroup parent_instance;
|
||||
|
||||
gint selected_index;
|
||||
};
|
||||
|
||||
struct _KeyGroupClass
|
||||
{
|
||||
ClutterGroupClass parent_class;
|
||||
|
||||
void (* activate) (KeyGroup *group,
|
||||
ClutterActor *child);
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (KeyGroup, key_group, CLUTTER_TYPE_GROUP);
|
||||
|
||||
enum
|
||||
{
|
||||
ACTIVATE,
|
||||
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint group_signals[LAST_SIGNAL] = { 0, };
|
||||
|
||||
static gboolean
|
||||
key_group_action_move_left (KeyGroup *self,
|
||||
const gchar *action_name,
|
||||
guint key_val,
|
||||
ClutterModifierType modifiers)
|
||||
{
|
||||
gint n_children;
|
||||
|
||||
g_debug ("%s: activated '%s' (k:%d, m:%d)",
|
||||
G_STRLOC,
|
||||
action_name,
|
||||
key_val,
|
||||
modifiers);
|
||||
|
||||
n_children = clutter_group_get_n_children (CLUTTER_GROUP (self));
|
||||
|
||||
self->selected_index -= 1;
|
||||
|
||||
if (self->selected_index < 0)
|
||||
self->selected_index = n_children - 1;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
key_group_action_move_right (KeyGroup *self,
|
||||
const gchar *action_name,
|
||||
guint key_val,
|
||||
ClutterModifierType modifiers)
|
||||
{
|
||||
gint n_children;
|
||||
|
||||
g_debug ("%s: activated '%s' (k:%d, m:%d)",
|
||||
G_STRLOC,
|
||||
action_name,
|
||||
key_val,
|
||||
modifiers);
|
||||
|
||||
n_children = clutter_group_get_n_children (CLUTTER_GROUP (self));
|
||||
|
||||
self->selected_index += 1;
|
||||
|
||||
if (self->selected_index >= n_children)
|
||||
self->selected_index = 0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
key_group_action_activate (KeyGroup *self,
|
||||
const gchar *action_name,
|
||||
guint key_val,
|
||||
ClutterModifierType modifiers)
|
||||
{
|
||||
ClutterActor *child = NULL;
|
||||
|
||||
g_debug ("%s: activated '%s' (k:%d, m:%d)",
|
||||
G_STRLOC,
|
||||
action_name,
|
||||
key_val,
|
||||
modifiers);
|
||||
|
||||
if (self->selected_index == -1)
|
||||
return FALSE;
|
||||
|
||||
child = clutter_group_get_nth_child (CLUTTER_GROUP (self),
|
||||
self->selected_index);
|
||||
|
||||
if (child)
|
||||
{
|
||||
g_signal_emit (self, group_signals[ACTIVATE], 0, child);
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
key_group_key_press (ClutterActor *actor,
|
||||
ClutterKeyEvent *event)
|
||||
{
|
||||
ClutterBindingPool *pool;
|
||||
gboolean res;
|
||||
|
||||
pool = clutter_binding_pool_find (G_OBJECT_TYPE_NAME (actor));
|
||||
g_assert (pool != NULL);
|
||||
|
||||
res = clutter_binding_pool_activate (pool,
|
||||
event->keyval,
|
||||
event->modifier_state,
|
||||
G_OBJECT (actor));
|
||||
|
||||
/* if we activate a key binding, redraw the actor */
|
||||
if (res)
|
||||
clutter_actor_queue_redraw (actor);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void
|
||||
key_group_paint (ClutterActor *actor)
|
||||
{
|
||||
KeyGroup *self = KEY_GROUP (actor);
|
||||
GList *children, *l;
|
||||
gint i;
|
||||
|
||||
children = clutter_container_get_children (CLUTTER_CONTAINER (self));
|
||||
|
||||
for (l = children, i = 0; l != NULL; l = l->next, i++)
|
||||
{
|
||||
ClutterActor *child = l->data;
|
||||
|
||||
/* paint the selection rectangle */
|
||||
if (i == self->selected_index)
|
||||
{
|
||||
ClutterActorBox box = { 0, };
|
||||
|
||||
clutter_actor_get_allocation_box (child, &box);
|
||||
|
||||
box.x1 -= CLUTTER_UNITS_FROM_DEVICE (2);
|
||||
box.y1 -= CLUTTER_UNITS_FROM_DEVICE (2);
|
||||
box.x2 += CLUTTER_UNITS_FROM_DEVICE (2);
|
||||
box.y2 += CLUTTER_UNITS_FROM_DEVICE (2);
|
||||
|
||||
cogl_set_source_color4ub (255, 255, 0, 224);
|
||||
cogl_rectangle (CLUTTER_UNITS_TO_DEVICE (box.x1),
|
||||
CLUTTER_UNITS_TO_DEVICE (box.y1),
|
||||
CLUTTER_UNITS_TO_DEVICE (box.x2 - box.x1),
|
||||
CLUTTER_UNITS_TO_DEVICE (box.y2 - box.y1));
|
||||
}
|
||||
|
||||
if (CLUTTER_ACTOR_IS_VISIBLE (child))
|
||||
clutter_actor_paint (child);
|
||||
}
|
||||
|
||||
g_list_free (children);
|
||||
}
|
||||
|
||||
static void
|
||||
key_group_finalize (GObject *gobject)
|
||||
{
|
||||
G_OBJECT_CLASS (key_group_parent_class)->finalize (gobject);
|
||||
}
|
||||
|
||||
static void
|
||||
key_group_class_init (KeyGroupClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||
ClutterBindingPool *binding_pool;
|
||||
|
||||
gobject_class->finalize = key_group_finalize;
|
||||
|
||||
actor_class->paint = key_group_paint;
|
||||
actor_class->key_press_event = key_group_key_press;
|
||||
|
||||
group_signals[ACTIVATE] =
|
||||
g_signal_new (g_intern_static_string ("activate"),
|
||||
G_OBJECT_CLASS_TYPE (gobject_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (KeyGroupClass, activate),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__OBJECT,
|
||||
G_TYPE_NONE, 1,
|
||||
CLUTTER_TYPE_ACTOR);
|
||||
|
||||
binding_pool = clutter_binding_pool_get_for_class (klass);
|
||||
|
||||
clutter_binding_pool_install_action (binding_pool, "move-right",
|
||||
CLUTTER_Right, 0,
|
||||
G_CALLBACK (key_group_action_move_right),
|
||||
NULL, NULL);
|
||||
clutter_binding_pool_install_action (binding_pool, "move-left",
|
||||
CLUTTER_Left, 0,
|
||||
G_CALLBACK (key_group_action_move_left),
|
||||
NULL, NULL);
|
||||
clutter_binding_pool_install_action (binding_pool, "activate",
|
||||
CLUTTER_Return, 0,
|
||||
G_CALLBACK (key_group_action_activate),
|
||||
NULL, NULL);
|
||||
clutter_binding_pool_install_action (binding_pool, "activate",
|
||||
CLUTTER_KP_Enter, 0,
|
||||
G_CALLBACK (key_group_action_activate),
|
||||
NULL, NULL);
|
||||
clutter_binding_pool_install_action (binding_pool, "activate",
|
||||
CLUTTER_ISO_Enter, 0,
|
||||
G_CALLBACK (key_group_action_activate),
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
key_group_init (KeyGroup *self)
|
||||
{
|
||||
self->selected_index = -1;
|
||||
}
|
||||
|
||||
static void
|
||||
on_key_group_activate (KeyGroup *group,
|
||||
ClutterActor *child)
|
||||
{
|
||||
g_print ("Child '%d' activated!\n", clutter_actor_get_gid (child));
|
||||
}
|
||||
|
||||
G_MODULE_EXPORT int
|
||||
test_binding_pool_main (int argc, char *argv[])
|
||||
{
|
||||
ClutterActor *stage, *key_group;
|
||||
ClutterColor red_color = { 255, 0, 0, 255 };
|
||||
ClutterColor green_color = { 0, 255, 0, 255 };
|
||||
ClutterColor blue_color = { 0, 0, 255, 255 };
|
||||
gint group_x, group_y;
|
||||
|
||||
clutter_init (&argc, &argv);
|
||||
|
||||
stage = clutter_stage_get_default ();
|
||||
g_signal_connect (stage,
|
||||
"button-press-event", G_CALLBACK (clutter_main_quit),
|
||||
NULL);
|
||||
|
||||
key_group = g_object_new (TYPE_KEY_GROUP, NULL);
|
||||
clutter_container_add_actor (CLUTTER_CONTAINER (stage), key_group);
|
||||
|
||||
/* add three rectangles to the key group */
|
||||
clutter_container_add (CLUTTER_CONTAINER (key_group),
|
||||
g_object_new (CLUTTER_TYPE_RECTANGLE,
|
||||
"color", &red_color,
|
||||
"width", 50,
|
||||
"height", 50,
|
||||
"x", 0,
|
||||
"y", 0,
|
||||
NULL),
|
||||
g_object_new (CLUTTER_TYPE_RECTANGLE,
|
||||
"color", &green_color,
|
||||
"width", 50,
|
||||
"height", 50,
|
||||
"x", 75,
|
||||
"y", 0,
|
||||
NULL),
|
||||
g_object_new (CLUTTER_TYPE_RECTANGLE,
|
||||
"color", &blue_color,
|
||||
"width", 50,
|
||||
"height", 50,
|
||||
"x", 150,
|
||||
"y", 0,
|
||||
NULL),
|
||||
NULL);
|
||||
|
||||
g_signal_connect (key_group,
|
||||
"activate", G_CALLBACK (on_key_group_activate),
|
||||
NULL);
|
||||
|
||||
group_x =
|
||||
(clutter_actor_get_width (stage) - clutter_actor_get_width (key_group))
|
||||
/ 2;
|
||||
group_y =
|
||||
(clutter_actor_get_height (stage) - clutter_actor_get_height (key_group))
|
||||
/ 2;
|
||||
|
||||
clutter_actor_set_position (key_group, group_x, group_y);
|
||||
clutter_actor_set_reactive (key_group, TRUE);
|
||||
|
||||
clutter_stage_set_key_focus (CLUTTER_STAGE (stage), key_group);
|
||||
|
||||
clutter_actor_show (stage);
|
||||
|
||||
clutter_main ();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
Loading…
Reference in New Issue
Block a user