1 /* -*- mode:C; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
5 #define DEFAULT_FONT_SIZE 20
6 #define DEFAULT_FONT "Sans Bold 20"
11 G_DEFINE_TYPE (OptMenu, opt_menu, CLUTTER_TYPE_GROUP);
13 static void opt_menu_up (OptMenu * menu);
14 static void opt_menu_down (OptMenu * menu);
15 static void opt_menu_activate (OptMenu * menu);
16 static void opt_menu_select_item (OptMenu * menu, gint slide_no);
25 ClutterColor color_normal;
26 ClutterColor color_sel;
27 ClutterColor color_bg;
30 ClutterActor *background;
31 ClutterActor *selection;
33 ClutterTimeline *timeline;
35 ClutterBehaviour *behaviour_s;
36 ClutterBehaviour *behaviour_o;
41 gulong button_release_signal_id;
42 gulong key_release_signal_id;
45 /* Set sizes for background and selection -- called once the
46 * menu is fully populated
49 opt_menu_init_size (OptMenu * menu)
52 clutter_actor_get_size (CLUTTER_ACTOR (menu), &width, &height);
54 width += 2 * TEXT_BORDER;
56 clutter_actor_set_size (CLUTTER_ACTOR (menu),
59 clutter_actor_set_size (CLUTTER_ACTOR (menu->priv->background),
62 clutter_actor_set_size (CLUTTER_ACTOR (menu->priv->selection),
63 width - 2 * MENU_BORDER, ITEM_HEIGHT);
65 menu->priv->height = height;
66 menu->priv->size_set = TRUE;
72 opt_menu_key_release_cb (ClutterStage *stage,
76 OptMenu *menu = OPT_MENU (user_data);
78 if (!CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR (menu)))
81 switch (clutter_key_event_symbol (kev))
90 opt_menu_activate (menu);
94 opt_menu_popdown (menu);
100 opt_menu_button_release_cb (ClutterStage *stage,
101 ClutterButtonEvent *bev,
104 OptMenu *menu = OPT_MENU (user_data);
106 if (!CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR (menu)))
109 /* Allow a mouse wheel to control the menu (cannot handle
110 * buttons 1 and 3 here, because those are used to control the slides).
113 if (bev->button == 4)
115 else if (bev->button == 5)
116 opt_menu_down (menu);
117 else if (bev->button == 2)
118 opt_menu_activate (menu);
122 opt_menu_finalize (GObject *object)
124 OptMenu *self = OPT_MENU(object);
126 g_object_unref (G_OBJECT (self->priv->behaviour_s));
127 g_object_unref (G_OBJECT (self->priv->behaviour_o));
128 g_object_unref (G_OBJECT (self->priv->timeline));
136 G_OBJECT_CLASS (opt_menu_parent_class)->finalize (object);
140 opt_menu_class_init (OptMenuClass *klass)
142 GObjectClass * object_class = (GObjectClass*) klass;
143 object_class->finalize = opt_menu_finalize;
147 opt_menu_init (OptMenu *self)
149 OptMenuPrivate *priv = g_new0 (OptMenuPrivate, 1);
154 opt_menu_hide_cb (ClutterTimeline * timeline, gpointer data)
156 OptMenu *menu = OPT_MENU (data);
158 if (CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR (menu)) &&
161 ClutterActor * stage = clutter_stage_get_default();
163 clutter_actor_hide_all (CLUTTER_ACTOR (menu));
164 clutter_group_remove (CLUTTER_GROUP (stage), CLUTTER_ACTOR (menu));
165 opt_menu_select_item (menu, 0);
166 menu->priv->hiding = FALSE;
168 if (menu->priv->timeout_id)
170 g_source_remove (menu->priv->timeout_id);
171 menu->priv->timeout_id = 0;
177 opt_menu_new (OptShow * show)
179 OptMenu *menu = g_object_new (OPT_TYPE_MENU, NULL);
181 /* TODO -- maybe allow these to be customised
183 menu->priv->color_normal.red = 0xff;
184 menu->priv->color_normal.green = 0xff;
185 menu->priv->color_normal.blue = 0xff;
186 menu->priv->color_normal.alpha = 0xff;
188 menu->priv->color_sel.red = 0;
189 menu->priv->color_sel.green = 0;
190 menu->priv->color_sel.blue = 0;
191 menu->priv->color_sel.alpha = 0xff;
193 menu->priv->color_bg.red = 0x7f;
194 menu->priv->color_bg.green = 0x7f;
195 menu->priv->color_bg.blue = 0x7f;
196 menu->priv->color_bg.alpha = 0xcf;
198 menu->priv->show = show;
200 menu->priv->background =
201 clutter_rectangle_new_with_color (&menu->priv->color_bg);
203 clutter_rectangle_set_border_color(CLUTTER_RECTANGLE(menu->priv->background),
204 &menu->priv->color_normal);
205 clutter_rectangle_set_border_width(CLUTTER_RECTANGLE(menu->priv->background),
208 clutter_group_add (CLUTTER_GROUP (menu),
209 CLUTTER_ACTOR (menu->priv->background));
211 menu->priv->selection =
212 clutter_rectangle_new_with_color (&menu->priv->color_normal);
214 clutter_group_add (CLUTTER_GROUP (menu),
215 CLUTTER_ACTOR (menu->priv->selection));
216 clutter_actor_set_position (CLUTTER_ACTOR (menu->priv->selection),
219 menu->priv->timeline = clutter_timeline_new (10, 26);
221 g_signal_connect (menu->priv->timeline, "completed",
222 G_CALLBACK (opt_menu_hide_cb), menu);
224 menu->priv->alpha = clutter_alpha_new_full (menu->priv->timeline,
227 menu->priv->behaviour_s =
228 clutter_behaviour_scale_new (menu->priv->alpha,
231 clutter_behaviour_apply (menu->priv->behaviour_s, CLUTTER_ACTOR (menu));
233 menu->priv->behaviour_o =
234 clutter_behaviour_opacity_new (menu->priv->alpha, 0x00, 0xff);
236 clutter_behaviour_apply (menu->priv->behaviour_o, CLUTTER_ACTOR (menu));
242 * Adjusts the postition of the menu if the selected item is
246 opt_menu_adjust_postion (OptMenu * menu)
248 if (menu->priv->height > CLUTTER_STAGE_HEIGHT ())
250 gint x = clutter_actor_get_x (CLUTTER_ACTOR (menu));
251 gint y = clutter_actor_get_y (CLUTTER_ACTOR (menu));
252 gint item_offset = menu->priv->active_item * ITEM_HEIGHT + y;
256 /* attemp to shift the item to the middle of screen, but no so that
257 * the the menu would detach from the top of stage
259 gint screen_itms = CLUTTER_STAGE_HEIGHT () / ITEM_HEIGHT;
260 gint shift = ITEM_HEIGHT * screen_itms / 2 - item_offset;
267 else if (item_offset > CLUTTER_STAGE_HEIGHT () - ITEM_HEIGHT)
269 /* attemp to shift the item to the middle of screen, but no so that
270 * the the menu would detach from the bottom of stage
272 gint screen_itms = CLUTTER_STAGE_HEIGHT () / ITEM_HEIGHT;
273 gint shift = ITEM_HEIGHT * screen_itms / 2 + item_offset;
274 gint max_shft = (menu->priv->item_count - screen_itms)*ITEM_HEIGHT;
276 if (shift > max_shft)
282 clutter_actor_set_position (CLUTTER_ACTOR (menu), x, y);
287 * Selects nth item in the menu
290 opt_menu_select_item (OptMenu * menu, gint slide_no)
292 if (slide_no < 0 || slide_no >= menu->priv->item_count)
295 if (menu->priv->active_item != slide_no)
297 /* Plus two, because the first two children are the background
298 * and selection rectangles
300 ClutterActor * active =
301 clutter_group_get_nth_child (CLUTTER_GROUP (menu),
302 menu->priv->active_item + 2);
304 clutter_text_set_color (CLUTTER_TEXT (active),
305 &menu->priv->color_normal);
307 active = clutter_group_get_nth_child (CLUTTER_GROUP (menu),
310 clutter_text_set_color (CLUTTER_TEXT (active), &menu->priv->color_sel);
312 clutter_actor_set_position (CLUTTER_ACTOR (menu->priv->selection),
313 MENU_BORDER, slide_no * ITEM_HEIGHT);
315 menu->priv->active_item = slide_no;
317 opt_menu_adjust_postion (menu);
322 * Callback to automatically close the menu after given period of inactivity
325 opt_menu_timeout_cb (gpointer data)
327 OptMenu * menu = data;
329 opt_menu_popdown (menu);
330 menu->priv->timeout_id = 0;
336 * move one item up in the menu
339 opt_menu_up (OptMenu * menu)
341 opt_menu_select_item (menu, menu->priv->active_item - 1);
343 if (menu->priv->timeout_id)
345 g_source_remove (menu->priv->timeout_id);
346 menu->priv->timeout_id = g_timeout_add (5000, opt_menu_timeout_cb, menu);
350 /* move one item down in the menu */
352 opt_menu_down (OptMenu * menu)
354 opt_menu_select_item (menu, menu->priv->active_item + 1);
356 if (menu->priv->timeout_id)
358 g_source_remove (menu->priv->timeout_id);
359 menu->priv->timeout_id = g_timeout_add (5000, opt_menu_timeout_cb, menu);
364 * Jump to the slide represented by the active menu item
367 opt_menu_activate (OptMenu * menu)
369 int step = menu->priv->active_item - menu->priv->current_slide;
371 opt_menu_popdown (menu);
374 opt_show_skip (menu->priv->show, step);
378 * Called when we mode to a different slide
381 opt_menu_set_current_slide (OptMenu * menu, gint slide_no)
383 opt_menu_select_item (menu, slide_no);
384 menu->priv->current_slide = slide_no;
388 * Adds a slide to the menu
391 opt_menu_add_slide (OptMenu * menu, OptSlide * slide)
396 const gchar * font = DEFAULT_FONT;
397 const ClutterText * title = CLUTTER_TEXT (opt_slide_get_title (slide));
398 ClutterActor * label;
401 text = g_strdup_printf ("Slide %d: %s", menu->priv->item_count + 1,
402 clutter_text_get_text ((ClutterText*)title));
404 text = g_strdup_printf ("Slide %d", menu->priv->item_count + 1);
406 if (!menu->priv->item_count)
407 label = clutter_text_new_full (font, text, &menu->priv->color_sel);
409 label = clutter_text_new_full (font, text, &menu->priv->color_normal);
413 clutter_actor_set_position (label, TEXT_BORDER, y);
416 clutter_group_add (CLUTTER_GROUP (menu), label);
417 menu->priv->item_count++;
424 opt_menu_pop (OptMenu * menu)
426 if (!CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR (menu)))
430 ClutterActor * stage = clutter_stage_get_default();
432 if (!menu->priv->size_set)
433 opt_menu_init_size (menu);
435 clutter_actor_get_size (CLUTTER_ACTOR (menu), &width, &height);
437 clutter_actor_set_scale (CLUTTER_ACTOR (menu), 0.0, 0.0);
438 clutter_timeline_set_direction (menu->priv->timeline,
439 CLUTTER_TIMELINE_FORWARD);
441 clutter_group_add (CLUTTER_GROUP(stage), CLUTTER_ACTOR(menu));
443 clutter_actor_set_position (CLUTTER_ACTOR (menu), 0, 0);
445 /* Connect up for input event */
446 menu->priv->button_release_signal_id =
447 g_signal_connect (stage, "button-release-event",
448 G_CALLBACK (opt_menu_button_release_cb), menu);
449 menu->priv->key_release_signal_id =
450 g_signal_connect (stage, "key-release-event",
451 G_CALLBACK (opt_menu_key_release_cb), menu);
453 opt_menu_select_item (menu, menu->priv->current_slide);
454 clutter_actor_show_all (CLUTTER_ACTOR (menu));
456 menu->priv->timeout_id = g_timeout_add (5000, opt_menu_timeout_cb, menu);
457 menu->priv->hiding = FALSE;
458 clutter_timeline_rewind (menu->priv->timeline);
459 clutter_timeline_start (menu->priv->timeline);
464 * Hides menu, if shown
467 opt_menu_popdown (OptMenu * menu)
469 if (CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR (menu)))
471 ClutterActor * stage = clutter_stage_get_default();
473 if (menu->priv->button_release_signal_id)
475 g_signal_handler_disconnect (stage,
476 menu->priv->button_release_signal_id);
477 menu->priv->button_release_signal_id = 0;
480 if (menu->priv->key_release_signal_id)
482 g_signal_handler_disconnect (stage,
483 menu->priv->key_release_signal_id);
484 menu->priv->key_release_signal_id = 0;
487 clutter_actor_set_scale (CLUTTER_ACTOR (menu), 1.0, 1.0);
488 clutter_timeline_set_direction (menu->priv->timeline,
489 CLUTTER_TIMELINE_BACKWARD);
491 menu->priv->hiding = TRUE;
492 clutter_timeline_rewind (menu->priv->timeline);
493 clutter_timeline_start (menu->priv->timeline);