This commit is contained in:
Daiki Ueno
2010-06-09 16:44:26 +09:00
parent 9e61919384
commit 662f619ba2
53 changed files with 2653 additions and 4894 deletions

View File

@ -20,205 +20,52 @@
/**
* SECTION:eek-clutter-key
* @short_description: #EekKey implemented as a #ClutterActor
*
* The #EekClutterKey class implements the #EekKeyIface interface as a
* #ClutterActor.
* @short_description: #EekKey embedding a #ClutterActor
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include "eek-clutter-key.h"
#include "eek-simple-key.h"
#include "eek-keysym.h"
#include "eek-clutter-key-actor.h"
enum {
PROP_0,
PROP_KEYCODE,
PROP_KEYSYMS,
PROP_COLUMN,
PROP_ROW,
PROP_OUTLINE,
PROP_BOUNDS,
PROP_GROUP,
PROP_LEVEL,
PROP_LAST
};
static void eek_key_iface_init (EekKeyIface *iface);
G_DEFINE_TYPE_WITH_CODE (EekClutterKey, eek_clutter_key,
CLUTTER_TYPE_GROUP,
G_IMPLEMENT_INTERFACE (EEK_TYPE_KEY,
eek_key_iface_init));
G_DEFINE_TYPE (EekClutterKey, eek_clutter_key, EEK_TYPE_KEY);
#define EEK_CLUTTER_KEY_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), EEK_TYPE_CLUTTER_KEY, EekClutterKeyPrivate))
struct _EekClutterKeyPrivate
{
EekSimpleKey *simple;
ClutterActor *actor;
};
static guint
eek_clutter_key_real_get_keycode (EekKey *self)
static void
eek_clutter_key_real_set_name (EekElement *self,
const gchar *name)
{
EekClutterKeyPrivate *priv = EEK_CLUTTER_KEY_GET_PRIVATE(self);
g_return_val_if_fail (priv, EEK_INVALID_KEYCODE);
return eek_key_get_keycode (EEK_KEY(priv->simple));
EEK_ELEMENT_CLASS (eek_clutter_key_parent_class)->
set_name (self, name);
g_return_if_fail (priv->actor);
clutter_actor_set_name (CLUTTER_ACTOR(priv->actor), name);
}
static void
eek_clutter_key_real_set_keysyms (EekKey *self,
guint *keysyms,
gint groups,
gint levels)
{
EekClutterKeyPrivate *priv = EEK_CLUTTER_KEY_GET_PRIVATE(self);
g_return_if_fail (priv);
eek_key_set_keysyms (EEK_KEY(priv->simple), keysyms, groups, levels);
}
static void
eek_clutter_key_real_get_keysyms (EekKey *self,
guint **keysyms,
gint *groups,
gint *levels)
{
EekClutterKeyPrivate *priv = EEK_CLUTTER_KEY_GET_PRIVATE(self);
g_return_if_fail (priv);
eek_key_get_keysyms (EEK_KEY(priv->simple), keysyms, groups, levels);
}
static gint
eek_clutter_key_real_get_groups (EekKey *self)
{
EekClutterKeyPrivate *priv = EEK_CLUTTER_KEY_GET_PRIVATE(self);
g_return_val_if_fail (priv, -1);
return eek_key_get_groups (EEK_KEY(priv->simple));
}
static guint
eek_clutter_key_real_get_keysym (EekKey *self)
{
EekClutterKeyPrivate *priv = EEK_CLUTTER_KEY_GET_PRIVATE(self);
g_return_val_if_fail (priv, EEK_INVALID_KEYSYM);
return eek_key_get_keysym (EEK_KEY(priv->simple));
}
static void
eek_clutter_key_real_set_index (EekKey *self,
gint column,
gint row)
{
EekClutterKeyPrivate *priv = EEK_CLUTTER_KEY_GET_PRIVATE(self);
g_return_if_fail (priv);
eek_key_set_index (EEK_KEY(priv->simple), column, row);
}
static void
eek_clutter_key_real_get_index (EekKey *self,
gint *column,
gint *row)
{
EekClutterKeyPrivate *priv = EEK_CLUTTER_KEY_GET_PRIVATE(self);
g_return_if_fail (priv);
eek_key_get_index (EEK_KEY(priv->simple), column, row);
}
static void
eek_clutter_key_real_set_outline (EekKey *self, EekOutline *outline)
{
EekClutterKeyPrivate *priv = EEK_CLUTTER_KEY_GET_PRIVATE(self);
g_return_if_fail (priv);
eek_key_set_outline (EEK_KEY(priv->simple), outline);
}
static EekOutline *
eek_clutter_key_real_get_outline (EekKey *self)
{
EekClutterKeyPrivate *priv = EEK_CLUTTER_KEY_GET_PRIVATE(self);
g_return_val_if_fail (priv, NULL);
return eek_key_get_outline (EEK_KEY(priv->simple));
}
static void
eek_clutter_key_real_set_bounds (EekKey *self, EekBounds *bounds)
{
EekClutterKeyPrivate *priv = EEK_CLUTTER_KEY_GET_PRIVATE(self);
g_return_if_fail (priv);
eek_key_set_bounds (EEK_KEY(priv->simple), bounds);
clutter_actor_set_anchor_point_from_gravity (CLUTTER_ACTOR(self),
CLUTTER_GRAVITY_CENTER);
clutter_actor_set_position (CLUTTER_ACTOR(self),
bounds->x + bounds->width / 2,
bounds->y + bounds->height / 2);
}
static void
eek_clutter_key_real_get_bounds (EekKey *self,
eek_clutter_key_real_set_bounds (EekElement *self,
EekBounds *bounds)
{
EekClutterKeyPrivate *priv = EEK_CLUTTER_KEY_GET_PRIVATE(self);
g_return_if_fail (priv);
return eek_key_get_bounds (EEK_KEY(priv->simple), bounds);
}
EEK_ELEMENT_CLASS (eek_clutter_key_parent_class)->
set_bounds (self, bounds);
static void
eek_clutter_key_real_set_keysym_index (EekKey *self,
gint group,
gint level)
{
EekClutterKeyPrivate *priv = EEK_CLUTTER_KEY_GET_PRIVATE(self);
g_return_if_fail (priv->actor);
g_return_if_fail (priv);
eek_key_set_keysym_index (EEK_KEY(priv->simple), group, level);
}
static void
eek_clutter_key_real_get_keysym_index (EekKey *self, gint *group, gint *level)
{
EekClutterKeyPrivate *priv = EEK_CLUTTER_KEY_GET_PRIVATE(self);
g_return_if_fail (priv);
return eek_key_get_keysym_index (EEK_KEY(priv->simple), group, level);
}
static void
eek_key_iface_init (EekKeyIface *iface)
{
iface->get_keycode = eek_clutter_key_real_get_keycode;
iface->set_keysyms = eek_clutter_key_real_set_keysyms;
iface->get_keysyms = eek_clutter_key_real_get_keysyms;
iface->get_groups = eek_clutter_key_real_get_groups;
iface->get_keysym = eek_clutter_key_real_get_keysym;
iface->set_index = eek_clutter_key_real_set_index;
iface->get_index = eek_clutter_key_real_get_index;
iface->set_outline = eek_clutter_key_real_set_outline;
iface->get_outline = eek_clutter_key_real_get_outline;
iface->set_bounds = eek_clutter_key_real_set_bounds;
iface->get_bounds = eek_clutter_key_real_get_bounds;
iface->set_keysym_index = eek_clutter_key_real_set_keysym_index;
iface->get_keysym_index = eek_clutter_key_real_get_keysym_index;
}
static void
eek_clutter_key_dispose (GObject *object)
{
clutter_group_remove_all (CLUTTER_GROUP(object));
G_OBJECT_CLASS (eek_clutter_key_parent_class)->dispose (object);
clutter_actor_set_position (priv->actor, bounds->x, bounds->y);
clutter_actor_set_size (priv->actor, bounds->width, bounds->height);
}
static void
@ -226,358 +73,40 @@ eek_clutter_key_finalize (GObject *object)
{
EekClutterKeyPrivate *priv = EEK_CLUTTER_KEY_GET_PRIVATE(object);
g_object_unref (priv->simple);
if (priv->actor) {
clutter_group_remove_all (CLUTTER_GROUP(priv->actor));
g_object_unref (priv->actor);
}
G_OBJECT_CLASS (eek_clutter_key_parent_class)->finalize (object);
}
static void
draw_text_on_layout (PangoLayout *layout,
const gchar *text,
gdouble scale)
{
PangoFontDescription *font_desc;
#define FONT_SIZE (720 * 50)
/* FIXME: Font should be configurable through a property. */
font_desc = pango_font_description_from_string ("Sans");
pango_font_description_set_size (font_desc, FONT_SIZE * scale);
pango_layout_set_font_description (layout, font_desc);
pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
pango_layout_set_text (layout, text, -1);
pango_font_description_free (font_desc);
}
static void
draw_key_on_layout (EekKey *key,
PangoLayout *layout)
{
PangoLayout *buffer;
PangoRectangle logical_rect = { 0, };
EekBounds bounds;
guint keysym;
const gchar *label, *empty_label = "";
gdouble scale_x, scale_y;
eek_key_get_bounds (key, &bounds);
keysym = eek_key_get_keysym (key);
if (keysym == EEK_INVALID_KEYSYM)
return;
label = eek_keysym_to_string (keysym);
if (!label)
label = empty_label;
/* Compute the layout extents. */
buffer = pango_layout_copy (layout);
draw_text_on_layout (buffer, label, 1.0);
pango_layout_get_extents (buffer, NULL, &logical_rect);
scale_x = scale_y = 1.0;
if (PANGO_PIXELS(logical_rect.width) > bounds.width)
scale_x = bounds.width / PANGO_PIXELS(logical_rect.width);
if (PANGO_PIXELS(logical_rect.height) > bounds.height)
scale_y = bounds.height / PANGO_PIXELS(logical_rect.height);
g_object_unref (buffer);
/* Actually draw on the layout */
draw_text_on_layout (layout,
label,
(scale_x < scale_y ? scale_x : scale_y) * 0.8);
if (label != empty_label)
g_free ((gpointer)label);
}
static void
eek_clutter_key_paint (ClutterActor *self)
{
PangoLayout *layout;
PangoRectangle logical_rect = { 0, };
CoglColor color;
ClutterGeometry geom;
EekBounds bounds;
gfloat width, height;
gfloat x, y;
g_return_if_fail (CLUTTER_IS_ACTOR(self));
g_return_if_fail (EEK_IS_KEY(self));
/* Draw the background texture first. */
CLUTTER_ACTOR_CLASS (eek_clutter_key_parent_class)->paint (self);
/* Draw the label on the key. */
layout = clutter_actor_create_pango_layout (self, NULL);
draw_key_on_layout (EEK_KEY(self), layout);
pango_layout_get_extents (layout, NULL, &logical_rect);
/* FIXME: Color should be configurable through a property. */
cogl_color_set_from_4ub (&color, 0x80, 0x00, 0x00, 0xff);
clutter_actor_get_allocation_geometry (self, &geom);
cogl_pango_render_layout (layout,
(geom.width - logical_rect.width / PANGO_SCALE) / 2,
(geom.height - logical_rect.height / PANGO_SCALE) / 2,
&color,
0);
g_object_unref (layout);
}
/* FIXME: This is a workaround for the bug
* http://bugzilla.openedhand.com/show_bug.cgi?id=2137 A developer
* says this is not a right way to solve the original problem.
*/
static void
eek_clutter_key_get_preferred_width (ClutterActor *self,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p)
{
PangoLayout *layout;
/* Draw the label on the key - just to validate the glyph cache. */
layout = clutter_actor_create_pango_layout (self, NULL);
draw_key_on_layout (EEK_KEY(self), layout);
cogl_pango_ensure_glyph_cache_for_layout (layout);
g_object_unref (layout);
CLUTTER_ACTOR_CLASS (eek_clutter_key_parent_class)->get_preferred_width
(self, for_height, min_width_p, natural_width_p);
}
static void
eek_clutter_key_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
EekClutterKeyPrivate *priv = EEK_CLUTTER_KEY_GET_PRIVATE(object);
g_return_if_fail (priv);
switch (prop_id) {
/* Properties which affect the appearance of the key as a
ClutterActor. */
case PROP_BOUNDS:
eek_key_set_bounds (EEK_KEY(object),
g_value_get_boxed (value));
break;
case PROP_OUTLINE:
eek_key_set_outline (EEK_KEY(object),
g_value_get_pointer (value));
break;
/* Otherwise delegate to priv->simple or the parent. */
case PROP_KEYCODE:
case PROP_KEYSYMS:
case PROP_COLUMN:
case PROP_GROUP:
case PROP_ROW:
case PROP_LEVEL:
g_object_set_property (G_OBJECT(priv->simple),
g_param_spec_get_name (pspec),
value);
break;
default:
g_object_set_property (object,
g_param_spec_get_name (pspec),
value);
break;
}
}
static void
eek_clutter_key_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
EekClutterKeyPrivate *priv = EEK_CLUTTER_KEY_GET_PRIVATE(object);
EekBounds bounds;
g_return_if_fail (priv);
switch (prop_id) {
case PROP_BOUNDS:
eek_key_get_bounds (EEK_KEY(object), &bounds);
g_value_set_boxed (value, &bounds);
break;
case PROP_KEYCODE:
case PROP_KEYSYMS:
case PROP_COLUMN:
case PROP_ROW:
case PROP_OUTLINE:
case PROP_GROUP:
case PROP_LEVEL:
g_object_get_property (G_OBJECT(priv->simple),
g_param_spec_get_name (pspec),
value);
break;
default:
g_object_get_property (object,
g_param_spec_get_name (pspec),
value);
break;
}
}
static void
eek_clutter_key_class_init (EekClutterKeyClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
GParamSpec *pspec;
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
EekElementClass *element_class = EEK_ELEMENT_CLASS (klass);
g_type_class_add_private (gobject_class,
sizeof (EekClutterKeyPrivate));
actor_class->paint = eek_clutter_key_paint;
/* FIXME: This is a workaround for the bug
* http://bugzilla.openedhand.com/show_bug.cgi?id=2137 A developer
* says this is not a right way to solve the original problem.
*/
actor_class->get_preferred_width = eek_clutter_key_get_preferred_width;
gobject_class->set_property = eek_clutter_key_set_property;
gobject_class->get_property = eek_clutter_key_get_property;
gobject_class->finalize = eek_clutter_key_finalize;
gobject_class->dispose = eek_clutter_key_dispose;
g_object_class_override_property (gobject_class,
PROP_KEYCODE,
"keycode");
g_object_class_override_property (gobject_class,
PROP_KEYSYMS,
"keysyms");
g_object_class_override_property (gobject_class,
PROP_COLUMN,
"column");
g_object_class_override_property (gobject_class,
PROP_ROW,
"row");
g_object_class_override_property (gobject_class,
PROP_OUTLINE,
"outline");
g_object_class_override_property (gobject_class,
PROP_BOUNDS,
"bounds");
g_object_class_override_property (gobject_class,
PROP_GROUP,
"group");
g_object_class_override_property (gobject_class,
PROP_LEVEL,
"level");
}
static void
on_key_animate_complete (ClutterAnimation *animation,
gpointer user_data)
{
ClutterActor *actor = (ClutterActor*)user_data;
/* reset after effect */
clutter_actor_set_opacity (actor, 0xff);
clutter_actor_set_scale (actor, 1.0, 1.0);
}
static void
key_enlarge (ClutterActor *actor)
{
clutter_actor_set_scale (actor, 1.0, 1.0);
clutter_actor_animate (actor, CLUTTER_EASE_IN_SINE, 150,
"scale-x", 1.5,
"scale-y", 1.5,
NULL);
}
static void
key_shrink (ClutterActor *actor)
{
clutter_actor_set_scale (actor, 1.5, 1.5);
clutter_actor_animate (actor, CLUTTER_EASE_OUT_SINE, 150,
"scale-x", 1.0,
"scale-y", 1.0,
NULL);
}
static gboolean
on_event (ClutterActor *actor,
ClutterEvent *event,
gpointer user_data)
{
g_return_val_if_fail (EEK_IS_KEY(actor), FALSE);
if (clutter_event_get_source (event) == actor) {
ClutterActor *section;
/* Make sure the enlarged key show up on the keys which belong
to other sections. */
section = clutter_actor_get_parent (actor);
clutter_actor_raise_top (section);
clutter_actor_raise_top (actor);
if (event->type == CLUTTER_BUTTON_PRESS)
key_enlarge (actor);
else if (event->type == CLUTTER_BUTTON_RELEASE)
key_shrink (actor);
}
return FALSE;
element_class->set_name = eek_clutter_key_real_set_name;
element_class->set_bounds = eek_clutter_key_real_set_bounds;
gobject_class->finalize = eek_clutter_key_finalize;
}
static void
eek_clutter_key_init (EekClutterKey *self)
{
EekClutterKeyPrivate *priv;
priv = self->priv = EEK_CLUTTER_KEY_GET_PRIVATE(self);
priv->simple = g_object_new (EEK_TYPE_SIMPLE_KEY, NULL);
clutter_actor_set_reactive (CLUTTER_ACTOR(self), TRUE);
g_signal_connect (self, "event", G_CALLBACK (on_event), NULL);
priv = self->priv = EEK_CLUTTER_KEY_GET_PRIVATE (self);
priv->actor = NULL;
}
ClutterActor *
eek_clutter_key_create_texture (EekClutterKey *key)
eek_clutter_key_get_actor (EekClutterKey *key)
{
EekOutline *outline;
EekBounds bounds;
ClutterActor *texture;
cairo_t *cr;
cairo_pattern_t *pat;
outline = eek_key_get_outline (EEK_KEY(key));
eek_key_get_bounds (EEK_KEY(key), &bounds);
texture = clutter_cairo_texture_new (bounds.width, bounds.height);
cr = clutter_cairo_texture_create (CLUTTER_CAIRO_TEXTURE(texture));
cairo_set_line_width (cr, 1);
cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0);
cairo_pattern_add_color_stop_rgba (pat, 1, 0.5, 0.5, 0.5, 1);
cairo_pattern_add_color_stop_rgba (pat, 0, 1, 1, 1, 1);
cairo_set_source (cr, pat);
eek_cairo_draw_rounded_polygon (cr,
TRUE,
outline->corner_radius,
outline->points,
outline->num_points);
cairo_pattern_destroy (pat);
cairo_set_source_rgba (cr, 0.3, 0.3, 0.3, 0.5);
eek_cairo_draw_rounded_polygon (cr,
FALSE,
outline->corner_radius,
outline->points,
outline->num_points);
cairo_destroy (cr);
return texture;
}
/**
* eek_clutter_key_set_texture:
* @key: an #EekClutterKey
* @texture: an #ClutterActor
*
* Set the background texture of @key to @texture.
*/
void
eek_clutter_key_set_texture (EekClutterKey *key,
ClutterActor *texture)
{
clutter_actor_set_position (texture, 0, 0);
clutter_container_add_actor (CLUTTER_CONTAINER(key), texture);
EekClutterKeyPrivate *priv = EEK_CLUTTER_KEY_GET_PRIVATE(key);
if (!priv->actor)
priv->actor = eek_clutter_key_actor_new (EEK_KEY(key));
return priv->actor;
}