mirror of
https://github.com/brl/mutter.git
synced 2024-12-29 14:22:13 +00:00
4b9e672bc1
A basic example that shows how to build a composite actor starting from a GListStore of model objects.
443 lines
12 KiB
C
443 lines
12 KiB
C
#include <glib-object.h>
|
|
#include <gio/gio.h>
|
|
#include <clutter/clutter.h>
|
|
|
|
/* {{{ MenuItem */
|
|
#define EXAMPLE_TYPE_MENU_ITEM (example_menu_item_get_type ())
|
|
|
|
G_DECLARE_FINAL_TYPE (ExampleMenuItem, example_menu_item, EXAMPLE, MENU_ITEM, ClutterText)
|
|
|
|
struct _ExampleMenuItem
|
|
{
|
|
ClutterText parent_instance;
|
|
|
|
gboolean is_selected;
|
|
};
|
|
|
|
struct _ExampleMenuItemClass
|
|
{
|
|
ClutterTextClass parent_class;
|
|
};
|
|
|
|
G_DEFINE_TYPE (ExampleMenuItem, example_menu_item, CLUTTER_TYPE_TEXT)
|
|
|
|
enum {
|
|
MENU_ITEM_PROP_SELECTED = 1,
|
|
MENU_ITEM_N_PROPS
|
|
};
|
|
|
|
static GParamSpec *menu_item_props[MENU_ITEM_N_PROPS] = { NULL, };
|
|
|
|
static void
|
|
example_menu_item_set_selected (ExampleMenuItem *self,
|
|
gboolean selected)
|
|
{
|
|
selected = !!selected;
|
|
if (self->is_selected == selected)
|
|
return;
|
|
|
|
self->is_selected = selected;
|
|
|
|
if (self->is_selected)
|
|
clutter_text_set_color (CLUTTER_TEXT (self), CLUTTER_COLOR_LightSkyBlue);
|
|
else
|
|
clutter_text_set_color (CLUTTER_TEXT (self), CLUTTER_COLOR_White);
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (self), menu_item_props[MENU_ITEM_PROP_SELECTED]);
|
|
}
|
|
|
|
static void
|
|
example_menu_item_set_property (GObject *gobject,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
switch (prop_id)
|
|
{
|
|
case MENU_ITEM_PROP_SELECTED:
|
|
example_menu_item_set_selected (EXAMPLE_MENU_ITEM (gobject),
|
|
g_value_get_boolean (value));
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
example_menu_item_get_property (GObject *gobject,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
switch (prop_id)
|
|
{
|
|
case MENU_ITEM_PROP_SELECTED:
|
|
g_value_set_boolean (value, EXAMPLE_MENU_ITEM (gobject)->is_selected);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
example_menu_item_class_init (ExampleMenuItemClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
gobject_class->set_property = example_menu_item_set_property;
|
|
gobject_class->get_property = example_menu_item_get_property;
|
|
|
|
menu_item_props[MENU_ITEM_PROP_SELECTED] =
|
|
g_param_spec_boolean ("selected", NULL, NULL,
|
|
FALSE,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_STATIC_STRINGS);
|
|
|
|
g_object_class_install_properties (gobject_class, MENU_ITEM_N_PROPS, menu_item_props);
|
|
}
|
|
|
|
static void
|
|
example_menu_item_init (ExampleMenuItem *self)
|
|
{
|
|
ClutterText *text = CLUTTER_TEXT (self);
|
|
ClutterActor *actor = CLUTTER_ACTOR (self);
|
|
|
|
clutter_text_set_font_name (text, "Sans Bold 24px");
|
|
clutter_text_set_color (text, CLUTTER_COLOR_White);
|
|
|
|
clutter_actor_set_margin_left (actor, 12);
|
|
clutter_actor_set_margin_right (actor, 12);
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
/* {{{ Menu */
|
|
#define EXAMPLE_TYPE_MENU (example_menu_get_type ())
|
|
|
|
G_DECLARE_FINAL_TYPE (ExampleMenu, example_menu, EXAMPLE, MENU, ClutterActor)
|
|
|
|
struct _ExampleMenu
|
|
{
|
|
ClutterActor parent_instance;
|
|
|
|
int current_idx;
|
|
};
|
|
|
|
struct _ExampleMenuClass
|
|
{
|
|
ClutterActorClass parent_class;
|
|
};
|
|
|
|
G_DEFINE_TYPE (ExampleMenu, example_menu, CLUTTER_TYPE_ACTOR)
|
|
|
|
static void
|
|
example_menu_class_init (ExampleMenuClass *klass)
|
|
{
|
|
}
|
|
|
|
static void
|
|
example_menu_init (ExampleMenu *self)
|
|
{
|
|
ClutterActor *actor = CLUTTER_ACTOR (self);
|
|
ClutterLayoutManager *layout;
|
|
|
|
layout = clutter_box_layout_new ();
|
|
clutter_box_layout_set_orientation (CLUTTER_BOX_LAYOUT (layout), CLUTTER_ORIENTATION_VERTICAL);
|
|
clutter_box_layout_set_spacing (CLUTTER_BOX_LAYOUT (layout), 12);
|
|
|
|
clutter_actor_set_layout_manager (actor, layout);
|
|
clutter_actor_set_background_color (actor, CLUTTER_COLOR_Black);
|
|
|
|
self->current_idx = -1;
|
|
}
|
|
|
|
static ClutterActor *
|
|
example_menu_select_item (ExampleMenu *self,
|
|
int idx)
|
|
{
|
|
ClutterActor *item;
|
|
|
|
if (idx == self->current_idx)
|
|
return clutter_actor_get_child_at_index (CLUTTER_ACTOR (self), self->current_idx);
|
|
|
|
item = clutter_actor_get_child_at_index (CLUTTER_ACTOR (self), self->current_idx);
|
|
if (item != NULL)
|
|
example_menu_item_set_selected ((ExampleMenuItem *) item, FALSE);
|
|
|
|
if (idx < 0)
|
|
idx = clutter_actor_get_n_children (CLUTTER_ACTOR (self)) - 1;
|
|
else if (idx >= clutter_actor_get_n_children (CLUTTER_ACTOR (self)))
|
|
idx = 0;
|
|
|
|
self->current_idx = idx;
|
|
|
|
item = clutter_actor_get_child_at_index (CLUTTER_ACTOR (self), self->current_idx);
|
|
if (item != NULL)
|
|
example_menu_item_set_selected ((ExampleMenuItem *) item, TRUE);
|
|
|
|
return item;
|
|
}
|
|
|
|
static ClutterActor *
|
|
example_menu_select_next (ExampleMenu *self)
|
|
{
|
|
return example_menu_select_item (self, self->current_idx + 1);
|
|
}
|
|
|
|
static ClutterActor *
|
|
example_menu_select_prev (ExampleMenu *self)
|
|
{
|
|
return example_menu_select_item (self, self->current_idx - 1);
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
/* {{{ MenuItemModel */
|
|
#define EXAMPLE_TYPE_MENU_ITEM_MODEL (example_menu_item_model_get_type ())
|
|
|
|
G_DECLARE_FINAL_TYPE (ExampleMenuItemModel, example_menu_item_model, EXAMPLE, MENU_ITEM_MODEL, GObject)
|
|
|
|
struct _ExampleMenuItemModel
|
|
{
|
|
GObject parent_instance;
|
|
|
|
char *label;
|
|
|
|
gboolean selected;
|
|
};
|
|
|
|
struct _ExampleMenuItemModelClass
|
|
{
|
|
GObjectClass parent_class;
|
|
};
|
|
|
|
enum {
|
|
MENU_ITEM_MODEL_PROP_LABEL = 1,
|
|
MENU_ITEM_MODEL_PROP_SELECTED,
|
|
MENU_ITEM_MODEL_N_PROPS
|
|
};
|
|
|
|
static GParamSpec *menu_item_model_props[MENU_ITEM_MODEL_N_PROPS] = { NULL, };
|
|
|
|
G_DEFINE_TYPE (ExampleMenuItemModel, example_menu_item_model, G_TYPE_OBJECT)
|
|
|
|
static void
|
|
example_menu_item_model_finalize (GObject *gobject)
|
|
{
|
|
ExampleMenuItemModel *self = (ExampleMenuItemModel *) gobject;
|
|
|
|
g_free (self->label);
|
|
|
|
G_OBJECT_CLASS (example_menu_item_model_parent_class)->finalize (gobject);
|
|
}
|
|
|
|
static void
|
|
example_menu_item_model_set_property (GObject *gobject,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ExampleMenuItemModel *self = (ExampleMenuItemModel *) gobject;
|
|
|
|
switch (prop_id)
|
|
{
|
|
case MENU_ITEM_MODEL_PROP_LABEL:
|
|
g_free (self->label);
|
|
self->label = g_value_dup_string (value);
|
|
break;
|
|
|
|
case MENU_ITEM_MODEL_PROP_SELECTED:
|
|
self->selected = g_value_get_boolean (value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
example_menu_item_model_get_property (GObject *gobject,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ExampleMenuItemModel *self = (ExampleMenuItemModel *) gobject;
|
|
|
|
switch (prop_id)
|
|
{
|
|
case MENU_ITEM_MODEL_PROP_LABEL:
|
|
g_value_set_string (value, self->label);
|
|
break;
|
|
|
|
case MENU_ITEM_MODEL_PROP_SELECTED:
|
|
g_value_set_boolean (value, self->selected);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
example_menu_item_model_class_init (ExampleMenuItemModelClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
gobject_class->set_property = example_menu_item_model_set_property;
|
|
gobject_class->get_property = example_menu_item_model_get_property;
|
|
gobject_class->finalize = example_menu_item_model_finalize;
|
|
|
|
menu_item_model_props[MENU_ITEM_MODEL_PROP_LABEL] =
|
|
g_param_spec_string ("label", NULL, NULL,
|
|
NULL,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
|
menu_item_model_props[MENU_ITEM_MODEL_PROP_SELECTED] =
|
|
g_param_spec_boolean ("selected", NULL, NULL,
|
|
FALSE,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
|
g_object_class_install_properties (gobject_class, MENU_ITEM_MODEL_N_PROPS, menu_item_model_props);
|
|
}
|
|
|
|
static void
|
|
example_menu_item_model_init (ExampleMenuItemModel *self)
|
|
{
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ main */
|
|
static gboolean
|
|
on_key_press (ClutterActor *stage,
|
|
ClutterEvent *event)
|
|
{
|
|
ClutterActor *scroll = clutter_actor_get_first_child (stage);
|
|
ClutterActor *menu = clutter_actor_get_first_child (scroll);
|
|
ClutterActor *item = NULL;
|
|
guint key = clutter_event_get_key_symbol (event);
|
|
ClutterPoint p;
|
|
|
|
switch (key)
|
|
{
|
|
case CLUTTER_KEY_q:
|
|
clutter_main_quit ();
|
|
break;
|
|
|
|
case CLUTTER_KEY_Up:
|
|
item = example_menu_select_prev ((ExampleMenu *) menu);
|
|
clutter_actor_get_position (item, &p.x, &p.y);
|
|
break;
|
|
|
|
case CLUTTER_KEY_Down:
|
|
item = example_menu_select_next ((ExampleMenu *) menu);
|
|
clutter_actor_get_position (item, &p.x, &p.y);
|
|
break;
|
|
}
|
|
|
|
if (item != NULL)
|
|
clutter_scroll_actor_scroll_to_point (CLUTTER_SCROLL_ACTOR (scroll), &p);
|
|
|
|
return CLUTTER_EVENT_PROPAGATE;
|
|
}
|
|
|
|
static void
|
|
on_model_item_selection (GObject *model_item,
|
|
GParamSpec *pspec,
|
|
gpointer data)
|
|
{
|
|
char *label = NULL;
|
|
gboolean is_selected = FALSE;
|
|
|
|
g_object_get (model_item, "label", &label, "selected", &is_selected, NULL);
|
|
|
|
if (is_selected)
|
|
g_print ("Item '%s' selected!\n", label);
|
|
|
|
g_free (label);
|
|
}
|
|
|
|
static ClutterActor *
|
|
create_menu_item (gpointer item,
|
|
gpointer data G_GNUC_UNUSED)
|
|
{
|
|
ClutterActor *res = g_object_new (EXAMPLE_TYPE_MENU_ITEM, NULL);
|
|
|
|
/* The label goes from the model to the view */
|
|
g_object_bind_property (item, "label",
|
|
res, "text",
|
|
G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
|
|
|
|
/* The selected state goes in either direction */
|
|
g_object_bind_property (item, "selected",
|
|
res, "selected",
|
|
G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
|
|
|
|
g_signal_connect (item, "notify::selected", G_CALLBACK (on_model_item_selection), NULL);
|
|
|
|
return res;
|
|
}
|
|
|
|
static ClutterActor *
|
|
create_menu_actor (void)
|
|
{
|
|
GListStore *model = g_list_store_new (EXAMPLE_TYPE_MENU_ITEM_MODEL);
|
|
ClutterActor *menu = g_object_new (EXAMPLE_TYPE_MENU, NULL);
|
|
int i;
|
|
|
|
/* Populate the model */
|
|
for (i = 0; i < 12; i++)
|
|
{
|
|
char *label = g_strdup_printf ("Option %02d", i + 1);
|
|
|
|
ExampleMenuItemModel *item = g_object_new (EXAMPLE_TYPE_MENU_ITEM_MODEL,
|
|
"label", label,
|
|
NULL);
|
|
|
|
g_list_store_append (model, item);
|
|
|
|
g_object_unref (item);
|
|
g_free (label);
|
|
}
|
|
|
|
clutter_actor_bind_model (menu, G_LIST_MODEL (model),
|
|
create_menu_item,
|
|
NULL, NULL);
|
|
|
|
/* The actor owns the model */
|
|
g_object_unref (model);
|
|
|
|
example_menu_select_item ((ExampleMenu *) menu, 0);
|
|
|
|
return menu;
|
|
}
|
|
|
|
static ClutterActor *
|
|
create_scroll_actor (void)
|
|
{
|
|
ClutterActor *menu = clutter_scroll_actor_new ();
|
|
clutter_actor_set_name (menu, "scroll");
|
|
clutter_scroll_actor_set_scroll_mode (CLUTTER_SCROLL_ACTOR (menu),
|
|
CLUTTER_SCROLL_VERTICALLY);
|
|
clutter_actor_set_easing_duration (menu, 250);
|
|
clutter_actor_add_child (menu, create_menu_actor ());
|
|
|
|
return menu;
|
|
}
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
ClutterActor *stage, *menu;
|
|
|
|
if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
|
|
return 1;
|
|
|
|
stage = clutter_stage_new ();
|
|
clutter_stage_set_title (CLUTTER_STAGE (stage), "Actor Model");
|
|
clutter_stage_set_user_resizable (CLUTTER_STAGE (stage), TRUE);
|
|
g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
|
|
g_signal_connect (stage, "key-press-event", G_CALLBACK (on_key_press), NULL);
|
|
clutter_actor_show (stage);
|
|
|
|
#define PADDING 18.f
|
|
|
|
menu = create_scroll_actor ();
|
|
clutter_actor_set_position (menu, 0, PADDING);
|
|
clutter_actor_add_constraint (menu, clutter_align_constraint_new (stage, CLUTTER_ALIGN_X_AXIS, 0.5));
|
|
clutter_actor_add_constraint (menu, clutter_bind_constraint_new (stage, CLUTTER_BIND_HEIGHT, -PADDING * 2));
|
|
clutter_actor_add_child (stage, menu);
|
|
|
|
clutter_main ();
|
|
|
|
return 0;
|
|
}
|
|
/* }}} */
|