#include #if 1 #define USE_CLIP #endif #define ROTATION 45 #define FPS 30 const char* const items[] = { "Item1", "Item2", "Item3", "Item4", "Item5", NULL }; #define MENU_TYPE menu_get_type() #define MENU(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MENU_TYPE, Menu)) #define MENU_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MENU_TYPE, MenuClass)) #define IS_MENU(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MENU_TYPE)) #define IS_MENU_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MENU_TYPE)) #define MENU_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MENU_TYPE, MenuClass)) typedef struct _Menu Menu; typedef struct _MenuClass MenuClass; typedef struct _MenuItem { ClutterActor *item; gint rotation; } MenuItem; struct _Menu { ClutterGroup parent; MenuItem **items; gint availitems; gint nitems; gint selected; ClutterTimeline *timeline; GSList *moving_items; }; struct _MenuClass { /*< private >*/ ClutterGroupClass parent_class; }; G_DEFINE_TYPE (Menu, menu, CLUTTER_TYPE_GROUP); static void menu_dispose (GObject *object) { Menu *self = MENU (object); G_OBJECT_CLASS (menu_parent_class)->dispose (object); } static void menu_finalize (GObject *object) { G_OBJECT_CLASS (menu_parent_class)->finalize (object); } static void menu_class_init (MenuClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); /* GObject */ object_class->finalize = menu_finalize; object_class->dispose = menu_dispose; } static void menu_init (Menu *self) { self->items = NULL; self->availitems = 0; self->nitems = 0; self->selected = 0; self->timeline = clutter_timeline_new(FPS, FPS); clutter_timeline_set_loop(self->timeline, TRUE); self->moving_items = NULL; } Menu *test_menu; static void menu_frame_cb(ClutterTimeline *timeline, gint frame_num, Menu *menu) { GSList *items = menu->moving_items; GSList **prev = &menu->moving_items; while (items != NULL) { MenuItem *item = items->data; gint rot = item->rotation; if (menu->items[menu->selected - 1] == item) rot += 1; else rot -= 1; if (rot < 0) rot = 0; else if (rot > ROTATION) rot = ROTATION; item->rotation = rot; clutter_actor_set_opacity(item->item, 55 + ((float)rot/ROTATION)*200); clutter_actor_rotate_y(item->item, -rot, -100, 0); if (rot == 0 || rot == ROTATION) { *prev = items->next; items->next = NULL; g_slist_free_1(items); items = *prev; } else { prev = &items->next; items = items->next; } } if (menu->moving_items == NULL) clutter_timeline_stop(timeline); } static void menu_add_item(Menu *menu, ClutterActor *item) { MenuItem *mitem = g_new0(MenuItem, 1); if (!menu->availitems) { menu->availitems = 4; menu->items = g_malloc(menu->availitems * sizeof(MenuItem *)); } else if (menu->availitems == menu->nitems) { menu->availitems *= 2; menu->items = g_realloc(menu->items, menu->availitems * sizeof(MenuItem *)); } mitem->item = item; menu->items[menu->nitems++] = mitem; } static void menu_up(Menu *menu) { guint unselected = menu->selected--; if (!menu->selected) menu->selected = menu->nitems; if (g_slist_find(menu->moving_items, menu->items[menu->selected-1]) == NULL) menu->moving_items = g_slist_prepend(menu->moving_items, menu->items[menu->selected-1]); if (g_slist_find(menu->moving_items, menu->items[unselected-1]) == NULL) menu->moving_items = g_slist_prepend(menu->moving_items, menu->items[unselected-1]); clutter_timeline_start(menu->timeline); } static void menu_down(Menu *menu) { guint unselected = menu->selected++; if (menu->selected > menu->nitems) menu->selected = 1; if (g_slist_find(menu->moving_items, menu->items[menu->selected-1]) == NULL) menu->moving_items = g_slist_prepend(menu->moving_items, menu->items[menu->selected-1]); if (g_slist_find(menu->moving_items, menu->items[unselected-1]) == NULL) menu->moving_items = g_slist_prepend(menu->moving_items, menu->items[unselected-1]); clutter_timeline_start(menu->timeline); } static Menu * create_menu(ClutterGroup *parent) { Menu *menu = g_object_new(MENU_TYPE, NULL); static ClutterColor text_color = { 0xff, 0xff, 0xff, 0xee }; static ClutterColor bgcolor = { 0x12, 0x34, 0x56, 0xee }; const char *const *item = items; ClutterActor *i; gint pos = 50; i = clutter_rectangle_new_with_color(&bgcolor); clutter_actor_set_size(i, 600, 450); #ifdef USE_CLIP clutter_actor_set_clip(i, 50, 50, 500, 350); #endif clutter_actor_set_position(i, 10, 20); clutter_group_add(CLUTTER_GROUP (menu), i); while (*item) { i = clutter_label_new_with_text("Sans Bold 48", *item); clutter_label_set_color (CLUTTER_LABEL(i), &text_color); clutter_actor_set_position(i, 100, pos); clutter_actor_set_opacity(i, 55); clutter_group_add(CLUTTER_GROUP(menu), i); pos += clutter_actor_get_height(i); menu_add_item(menu, i); ++item; } menu->selected = 1; menu->moving_items = g_slist_prepend(menu->moving_items, menu->items[0]); clutter_timeline_start(menu->timeline); g_signal_connect (menu->timeline, "new-frame", G_CALLBACK (menu_frame_cb), menu); clutter_actor_rotate_y(CLUTTER_ACTOR(menu), ROTATION, 0, 0); clutter_group_show_all (CLUTTER_GROUP (menu)); clutter_group_add(parent, CLUTTER_ACTOR(menu)); return menu; } static void input_cb (ClutterStage *stage, ClutterEvent *event, gpointer user_data) { switch (event->type) { case CLUTTER_MOTION: break; case CLUTTER_BUTTON_PRESS: { ClutterButtonEvent *bev = (ClutterButtonEvent *) event; } break; case CLUTTER_KEY_RELEASE: { ClutterKeyEvent* kev = (ClutterKeyEvent *) event; switch (clutter_key_event_symbol (kev)) { case CLUTTER_Up: menu_up(test_menu); break; case CLUTTER_Down: menu_down(test_menu); break; case CLUTTER_q: case CLUTTER_Escape: clutter_main_quit (); break; default: break; } } default: break; } } int main (int argc, char *argv[]) { ClutterColor stage_color = { 0x61, 0x64, 0x8c, 0xff }; ClutterActor *stage; clutter_init (&argc, &argv); stage = clutter_stage_get_default (); g_object_set (stage, "fullscreen", FALSE, NULL); clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); g_signal_connect (stage, "input-event", G_CALLBACK (input_cb), NULL); test_menu = create_menu(CLUTTER_GROUP(stage)); clutter_group_show_all (CLUTTER_GROUP (stage)); clutter_main(); return 0; }