static gboolean mx_expander_button_release (ClutterActor *actor, ClutterButtonEvent *event) { MxExpanderPrivate *priv = MX_EXPANDER (actor)->priv; mx_expander_set_expanded (MX_EXPANDER (actor), !priv->expanded); return TRUE; }
static void mx_expander_dispose (GObject *object) { MxExpanderPrivate *priv = MX_EXPANDER (object)->priv; if (priv->label) { clutter_actor_remove_child (CLUTTER_ACTOR (object), priv->label); priv->label = NULL; } if (priv->arrow) { clutter_actor_remove_child (CLUTTER_ACTOR (object), priv->arrow); priv->arrow = NULL; } if (priv->timeline) { g_object_unref (priv->timeline); priv->timeline = NULL; } G_OBJECT_CLASS (mx_expander_parent_class)->dispose (object); }
static void mx_expander_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height, gfloat *pref_height) { MxExpanderPrivate *priv = MX_EXPANDER (actor)->priv; ClutterActor *child; MxPadding padding; gfloat min_child_h, pref_child_h, min_label_h, pref_label_h, arrow_h; gfloat available_w; child = mx_bin_get_child (MX_BIN (actor)); mx_widget_get_padding (MX_WIDGET (actor), &padding); available_w = for_width - padding.left - padding.right; if (child) { clutter_actor_get_preferred_height (child, available_w, &min_child_h, &pref_child_h); min_child_h += priv->spacing; pref_child_h += priv->spacing; /* allocate the space multiplied by the progress of the "expansion" * animation */ min_child_h *= priv->progress; pref_child_h *= priv->progress; } else { min_child_h = 0; pref_child_h = 0; } clutter_actor_get_preferred_height (priv->label, available_w, &min_label_h, &pref_label_h); clutter_actor_get_preferred_height (priv->arrow, -1, NULL, &arrow_h); min_label_h = MAX (min_label_h, arrow_h); pref_label_h = MAX (pref_label_h, arrow_h); if (min_height) *min_height = padding.top + min_child_h + min_label_h + padding.bottom; if (pref_height) *pref_height = padding.top + pref_child_h + pref_label_h + padding.bottom; }
static void mx_expander_actor_removed (ClutterActor *container, ClutterActor *child) { MxExpanderPrivate *priv = MX_EXPANDER (container)->priv; if (priv->child == child) priv->child = NULL; }
static void mx_expander_apply_style (MxWidget *widget, MxStyle *style) { MxExpanderPrivate *priv = MX_EXPANDER (widget)->priv; if (priv->arrow != NULL) mx_stylable_set_style (MX_STYLABLE (priv->arrow), style); }
static void mx_expander_unmap (ClutterActor *actor) { MxExpanderPrivate *priv = MX_EXPANDER (actor)->priv; CLUTTER_ACTOR_CLASS (mx_expander_parent_class)->unmap (actor); clutter_actor_unmap (priv->label); clutter_actor_unmap (priv->arrow); }
static gboolean mx_expander_button_release (ClutterActor *actor, ClutterButtonEvent *event) { MxExpander *expander = MX_EXPANDER (actor); mx_expander_set_expanded (expander, !expander->priv->expanded); return FALSE; }
static void new_frame (ClutterTimeline *timeline, gint frame_num, ClutterActor *expander) { MxExpanderPrivate *priv = MX_EXPANDER (expander)->priv; priv->progress = clutter_timeline_get_progress (priv->timeline); clutter_actor_queue_relayout (expander); }
static void mx_expander_map (ClutterActor *actor) { MxExpanderPrivate *priv = MX_EXPANDER (actor)->priv; ClutterActorClass *parent_parent_class = g_type_class_peek_parent (mx_expander_parent_class); CLUTTER_ACTOR_CLASS (parent_parent_class)->map (actor); clutter_actor_map (priv->label); clutter_actor_map (priv->arrow); }
static void mx_expander_style_changed (MxStylable *stylable) { MxExpander *expander = MX_EXPANDER (stylable); MxExpanderPrivate *priv = expander->priv; const gchar *pseudo_class; pseudo_class = mx_stylable_get_style_pseudo_class (stylable); mx_stylable_set_style_pseudo_class (MX_STYLABLE (expander->priv->arrow), pseudo_class); mx_stylable_apply_clutter_text_attributes (stylable, CLUTTER_TEXT (priv->label)); }
static void mx_expander_actor_added (ClutterActor *container, ClutterActor *child) { MxExpanderPrivate *priv = MX_EXPANDER (container)->priv; if (MX_IS_TOOLTIP (child)) return; if (priv->child) clutter_actor_remove_child (container, priv->child); priv->child = child; if (!priv->expanded) clutter_actor_hide (child); }
static void mx_expander_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width, gfloat *pref_width) { MxExpanderPrivate *priv = MX_EXPANDER (actor)->priv; ClutterActor *child; MxPadding padding; gfloat min_child_w, pref_child_w, min_label_w, pref_label_w, arrow_w; child = mx_bin_get_child (MX_BIN (actor)); mx_widget_get_padding (MX_WIDGET (actor), &padding); if (child) { clutter_actor_get_preferred_width (child, -1, &min_child_w, &pref_child_w); } else { min_child_w = 0; pref_child_w = 0; } clutter_actor_get_preferred_width (priv->label, -1, &min_label_w, &pref_label_w); clutter_actor_get_preferred_width (priv->arrow, -1, NULL, &arrow_w); /* TODO: create a style property for this padding between arrow and label */ if (arrow_w) arrow_w += 6.0f; if (min_width) *min_width = padding.left + MAX (min_child_w, min_label_w + arrow_w) + padding.right; if (pref_width) *pref_width = padding.left + MAX (pref_child_w, pref_label_w + arrow_w) + padding.right; }
static void timeline_complete (ClutterTimeline *timeline, ClutterActor *expander) { guchar opacity; MxExpanderPrivate *priv = MX_EXPANDER (expander)->priv; g_signal_emit (expander, expander_signals[EXPAND_COMPLETE], 0); /* if the expander is now closed, update the style */ if (!priv->expanded) { clutter_actor_set_name (priv->arrow, "mx-expander-arrow-closed"); mx_stylable_set_style_class (MX_STYLABLE (expander), "closed-expander"); clutter_actor_queue_relayout (expander); } if (!priv->child) return; /* continue only if we are "opening" */ if (!priv->expanded) return; /* we can't do an animation if there is already one in progress, * because we cannot get the actors original opacity */ if (clutter_actor_get_transition (priv->child, "opacity")) { clutter_actor_show (priv->child); return; } opacity = clutter_actor_get_opacity (priv->child); clutter_actor_set_opacity (priv->child, 0); clutter_actor_show (priv->child); clutter_actor_save_easing_state (priv->child); clutter_actor_set_easing_mode (priv->child, CLUTTER_EASE_IN_SINE); clutter_actor_set_easing_duration (priv->child, 100); clutter_actor_set_opacity (priv->child, opacity); clutter_actor_restore_easing_state (priv->child); }
static void mx_expander_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxExpanderPrivate *priv = MX_EXPANDER (object)->priv; switch (property_id) { case PROP_EXPANDED: g_value_set_boolean (value, priv->expanded); break; case PROP_LABEL: g_value_set_string (value, clutter_text_get_text (CLUTTER_TEXT (priv->label))); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } }
static void mx_expander_add (ClutterContainer *container, ClutterActor *actor) { MxExpander *expander = MX_EXPANDER (container); MxExpanderPrivate *priv = expander->priv; /* Override the container add method so we can hide the actor if the expander is not expanded */ /* chain up */ container_parent_class->add (container, actor); if (!priv->expanded) { actor = mx_bin_get_child (MX_BIN (container)); if (actor) clutter_actor_hide (actor); } }
static void mx_expander_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxExpanderPrivate *priv = MX_EXPANDER (actor)->priv; ClutterActorBox child_box; MxPadding padding; gfloat label_w, label_h; gfloat available_w, available_h, min_w, min_h, arrow_h, arrow_w; /* chain up to store allocation */ CLUTTER_ACTOR_CLASS (mx_expander_parent_class)->allocate (actor, box, flags); mx_widget_get_padding (MX_WIDGET (actor), &padding); available_w = (box->x2 - box->x1) - padding.left - padding.right; available_h = (box->y2 - box->y1) - padding.top - padding.bottom; /* arrow */ clutter_actor_get_preferred_width (priv->arrow, -1, NULL, &arrow_w); arrow_w = MIN (arrow_w, available_w); clutter_actor_get_preferred_height (priv->arrow, -1, NULL, &arrow_h); arrow_h = MIN (arrow_h, available_h); child_box.x1 = padding.left; child_box.x2 = child_box.x1 + arrow_w; child_box.y1 = padding.top; child_box.y2 = child_box.y1 + arrow_h; clutter_actor_allocate (priv->arrow, &child_box, flags); /* label */ min_h = 0; min_w = 0; clutter_actor_get_preferred_width (priv->label, available_h, &min_w, &label_w); label_w = CLAMP (label_w, min_w, available_w); clutter_actor_get_preferred_height (priv->label, label_w, &min_h, &label_h); label_h = CLAMP (label_h, min_h, available_h); /* TODO: make a style property for padding between arrow and label */ child_box.x1 = padding.left + arrow_w + 6.0f; child_box.x2 = child_box.x1 + label_w; child_box.y1 = padding.top; child_box.y2 = child_box.y1 + MAX (label_h, arrow_h); mx_allocate_align_fill (priv->label, &child_box, MX_ALIGN_START, MX_ALIGN_MIDDLE, FALSE, FALSE); clutter_actor_allocate (priv->label, &child_box, flags); /* remove label height and spacing for child calculations */ available_h -= MAX (label_h, arrow_h) + priv->spacing; /* child */ if (priv->expanded && priv->child && CLUTTER_ACTOR_IS_VISIBLE (priv->child)) { child_box.x1 = padding.left; child_box.x2 = child_box.x1 + available_w; child_box.y1 = padding.top + priv->spacing + MAX (label_h, arrow_h); child_box.y2 = child_box.y1 + available_h; clutter_actor_allocate (priv->child, &child_box, flags); } }