static void mx_combo_box_update_menu (MxComboBox *box) { MxComboBoxPrivate *priv = box->priv; GSList *l; gint index; MxMenu *menu; menu = mx_widget_get_menu (MX_WIDGET (box)); if (!menu) return; mx_menu_remove_all (menu); for (index = 0, l = priv->actions; l; l = g_slist_next (l), index++) { MxAction *action; action = (MxAction *)l->data; g_object_set_data ((GObject*) action, "index", GINT_TO_POINTER (index)); mx_menu_add_action (menu, action); } /* queue a relayout so the combobox size can match the new menu */ clutter_actor_queue_relayout ((ClutterActor*) box); }
static gboolean mx_combo_box_open_menu (MxComboBox *actor) { ClutterActor *menu; menu = (ClutterActor *) mx_widget_get_menu (MX_WIDGET (actor)); if (!menu) return FALSE; clutter_actor_show (menu); return TRUE; }
static void mx_combo_box_style_changed (MxComboBox *combo, MxStyleChangedFlags flags) { MxBorderImage *marker_filename; gint spacing; MxComboBoxPrivate *priv = combo->priv; mx_stylable_get (MX_STYLABLE (combo), "x-mx-spacing", &spacing, "x-mx-marker-image", &marker_filename, NULL); if (spacing != priv->spacing) priv->spacing = spacing; if (priv->marker) { clutter_actor_destroy (priv->marker); priv->marker = NULL; } if (marker_filename) { MxTextureCache *cache = mx_texture_cache_get_default (); priv->marker = (ClutterActor *) mx_texture_cache_get_texture (cache, marker_filename->uri); if (priv->marker) clutter_actor_add_child (CLUTTER_ACTOR (combo), priv->marker); g_boxed_free (MX_TYPE_BORDER_IMAGE, marker_filename); } mx_stylable_apply_clutter_text_attributes (MX_STYLABLE (combo), CLUTTER_TEXT (combo->priv->label)); /* make sure the popup is also up-to-date */ mx_stylable_style_changed (MX_STYLABLE (mx_widget_get_menu (MX_WIDGET (combo))), flags | MX_STYLE_CHANGED_FORCE); clutter_actor_queue_relayout (CLUTTER_ACTOR (combo)); }
static void mx_combo_box_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxComboBoxPrivate *priv = MX_COMBO_BOX (actor)->priv; MxPadding padding; gfloat x, y, width, height; gfloat min_menu_h, nat_menu_h; gfloat label_h; gfloat nat_icon_h, icon_h, icon_w; gfloat nat_marker_h, marker_h, marker_w; ClutterActorBox childbox; ClutterActor *menu, *stage; CLUTTER_ACTOR_CLASS (mx_combo_box_parent_class)->allocate (actor, box, flags); mx_widget_get_padding (MX_WIDGET (actor), &padding); x = padding.left; y = padding.top; width = box->x2 - box->x1 - padding.left - padding.right; height = box->y2 - box->y1 - padding.top - padding.bottom; icon_w = marker_w = 0; if (priv->icon) { /* Allocate the icon, if there is one, the space not used by the text */ clutter_actor_get_preferred_height (priv->icon, -1, NULL, &nat_icon_h); if (height >= nat_icon_h) { icon_h = nat_icon_h; clutter_actor_get_preferred_width (priv->icon, -1, NULL, &icon_w); } else { icon_h = height; clutter_actor_get_preferred_width (priv->icon, icon_h, NULL, &icon_w); } childbox.x1 = (int)(x); childbox.y1 = (int)(y + (height - icon_h) / 2); childbox.x2 = (int)(x + icon_w); childbox.y2 = (int)(childbox.y1 + icon_h); clutter_actor_allocate (priv->icon, &childbox, flags); icon_w += priv->spacing; } if (priv->marker) { clutter_actor_get_preferred_height (priv->marker, -1, NULL, &nat_marker_h); if (height >= nat_marker_h) { marker_h = nat_marker_h; clutter_actor_get_preferred_width (priv->marker, -1, NULL, &marker_w); } else { marker_h = height; clutter_actor_get_preferred_width (priv->marker, marker_h, NULL, &marker_w); } childbox.x2 = (int)(x + width); childbox.x1 = (int)(childbox.x2 - marker_w); childbox.y1 = (int)(y + (height - marker_h) / 2); childbox.y2 = (int)(childbox.y1 + marker_h); clutter_actor_allocate (priv->marker, &childbox, flags); marker_w += priv->spacing; } clutter_actor_get_preferred_height (priv->label, -1, NULL, &label_h); childbox.x1 = (int)(x + icon_w); childbox.y1 = (int)(y + (height / 2 - label_h / 2)); childbox.x2 = (int)(x + width - marker_w); childbox.y2 = (int)(childbox.y1 + label_h); clutter_actor_allocate (priv->label, &childbox, flags); menu = (ClutterActor*) mx_widget_get_menu (MX_WIDGET (actor)); clutter_actor_get_preferred_height (menu, (box->x2 - box->x1), &min_menu_h, &nat_menu_h); childbox.x1 = 0; childbox.x2 = (box->x2 - box->x1); childbox.y1 = (box->y2 - box->y1); stage = clutter_actor_get_stage (actor); if (stage != NULL) { ClutterVertex point = { 0, }; gfloat stage_w, stage_h, combo_h = box->y2 - box->y1; clutter_actor_get_size (stage, &stage_w, &stage_h); point.y = combo_h + nat_menu_h; clutter_actor_apply_transform_to_point (actor, &point, &point); /* If the menu would appear off the stage, flip it around. */ if ((point.x < 0) || (point.x >= stage_w) || (point.y < 0) || (point.y >= stage_h)) { childbox.y1 = -nat_menu_h; } } childbox.y2 = childbox.y1 + nat_menu_h; clutter_actor_allocate (menu, &childbox, flags); }
static void mx_combo_box_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *natural_height_p) { gfloat height; gfloat min_w, nat_w; gfloat min_label_w, nat_label_w; gfloat min_icon_w = 0, nat_icon_w = 0; gfloat min_menu_w = 0, nat_menu_w = 0; gfloat min_marker_w = 0, nat_marker_w = 0; MxComboBoxPrivate *priv = MX_COMBO_BOX (actor)->priv; MxPadding padding; ClutterActor *menu; mx_widget_get_padding (MX_WIDGET (actor), &padding); height = for_height - padding.top - padding.bottom; menu = (ClutterActor *) mx_widget_get_menu (MX_WIDGET (actor)); if (menu) { clutter_actor_get_preferred_width (menu, -1, &min_menu_w, &nat_menu_w); } clutter_actor_get_preferred_width (priv->label, height, &min_label_w, &nat_label_w); min_w = min_label_w; nat_w = nat_label_w; if (priv->icon) { clutter_actor_get_preferred_width (priv->icon, height, &min_icon_w, &nat_icon_w); min_w += min_icon_w + priv->spacing; nat_w += nat_icon_w + priv->spacing; } if (min_menu_w > min_w) min_w = min_menu_w; if (nat_menu_w > nat_w) nat_w = nat_menu_w; if (priv->marker) { clutter_actor_get_preferred_width (priv->marker, height, &min_marker_w, &nat_marker_w); min_w += min_marker_w + priv->spacing; nat_w += nat_marker_w + priv->spacing; } if (min_width_p) *min_width_p = padding.left + padding.right + min_w; if (natural_height_p) *natural_height_p = padding.left + padding.right + nat_w; }
static void mx_combo_box_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxComboBoxPrivate *priv = MX_COMBO_BOX (actor)->priv; MxPadding padding; gfloat x, y, width, height; gfloat min_menu_h, nat_menu_h; gfloat label_h; gfloat nat_icon_h, icon_h, icon_w; gfloat nat_marker_h, marker_h, marker_w; ClutterActorBox childbox; ClutterActor *menu, *stage; CLUTTER_ACTOR_CLASS (mx_combo_box_parent_class)->allocate (actor, box, flags); mx_widget_get_padding (MX_WIDGET (actor), &padding); x = padding.left; y = padding.top; width = box->x2 - box->x1 - padding.left - padding.right; height = box->y2 - box->y1 - padding.top - padding.bottom; icon_w = marker_w = 0; if (priv->icon) { /* Allocate the icon, if there is one, the space not used by the text */ clutter_actor_get_preferred_height (priv->icon, -1, NULL, &nat_icon_h); if (height >= nat_icon_h) { icon_h = nat_icon_h; clutter_actor_get_preferred_width (priv->icon, -1, NULL, &icon_w); } else { icon_h = height; clutter_actor_get_preferred_width (priv->icon, icon_h, NULL, &icon_w); } childbox.x1 = (int)(x); childbox.y1 = (int)(y + (height - icon_h) / 2); childbox.x2 = (int)(x + icon_w); childbox.y2 = (int)(childbox.y1 + icon_h); clutter_actor_allocate (priv->icon, &childbox, flags); icon_w += priv->spacing; } if (priv->marker) { clutter_actor_get_preferred_height (priv->marker, -1, NULL, &nat_marker_h); if (height >= nat_marker_h) { marker_h = nat_marker_h; clutter_actor_get_preferred_width (priv->marker, -1, NULL, &marker_w); } else { marker_h = height; clutter_actor_get_preferred_width (priv->marker, marker_h, NULL, &marker_w); } childbox.x2 = (int)(x + width); childbox.x1 = (int)(childbox.x2 - marker_w); childbox.y1 = (int)(y + (height - marker_h) / 2); childbox.y2 = (int)(childbox.y1 + marker_h); clutter_actor_allocate (priv->marker, &childbox, flags); marker_w += priv->spacing; } clutter_actor_get_preferred_height (priv->label, -1, NULL, &label_h); childbox.x1 = (int)(x + icon_w); childbox.y1 = (int)(y + (height / 2 - label_h / 2)); childbox.x2 = (int)(x + width - marker_w); childbox.y2 = (int)(childbox.y1 + label_h); clutter_actor_allocate (priv->label, &childbox, flags); menu = (ClutterActor*) mx_widget_get_menu (MX_WIDGET (actor)); clutter_actor_get_preferred_height (menu, (box->x2 - box->x1), &min_menu_h, &nat_menu_h); childbox.x1 = 0; childbox.x2 = (box->x2 - box->x1); childbox.y1 = (box->y2 - box->y1); childbox.y2 = childbox.y1 + nat_menu_h; stage = clutter_actor_get_stage (actor); if (stage != NULL) { ClutterVertex point = { 0, }; gfloat stage_w, stage_h, combo_h = box->y2 - box->y1; clutter_actor_get_size (stage, &stage_w, &stage_h); point.y = combo_h + nat_menu_h; clutter_actor_apply_transform_to_point (actor, &point, &point); /* If the menu would appear off the stage, flip it around. */ if ((point.y < 0) || (point.y >= stage_h)) { childbox.y1 = -nat_menu_h; point.y = -nat_menu_h; clutter_actor_apply_transform_to_point (actor, &point, &point); /* if the menu would still appear out of the stage, force * it to appear on the top of the stage. */ if (point.y < 0) { gfloat xactor, yactor; clutter_actor_get_transformed_position (actor, &xactor, &yactor); childbox.y1 = -yactor; } } point.y = childbox.y1 + nat_menu_h; clutter_actor_apply_transform_to_point (actor, &point, &point); if (point.y >= stage_h) { gfloat xactor, yactor; clutter_actor_get_transformed_position (actor, &xactor, &yactor); /* * clamp so that the menu doesn't appear out of the screen */ clutter_actor_transform_stage_point (actor, xactor, stage_h, NULL, &childbox.y2); /* * The previous transformation can lead to negative height * allocation if the top-left corner of the menu is already * flipped around. This happens when you put a combobox deep * enough in a scrollview taller that the stage. */ childbox.y2 = MAX (childbox.y1, childbox.y2); } else { childbox.y2 = childbox.y1 + nat_menu_h; } } clutter_actor_allocate (menu, &childbox, flags); }