/** * mx_bin_allocate_child: * @bin: An #MxBin * @box: The allocation box of the parent actor. * @flags: #ClutterAllocationFlags, usually provided by the. * clutter_actor_allocate function. * * Allocates the child of an #MxBin using the width and height from @box. * This function should usually only be called by subclasses of #MxBin. * * This function can be used to allocate the child of an #MxBin if no special * allocation requirements are needed. It is similar to * #mx_allocate_align_fill, except that it reads the alignment, padding and * fill values from the #MxBin, and will call #clutter_actor_allocate on the * child. * */ void mx_bin_allocate_child (MxBin *bin, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxBinPrivate *priv; g_return_if_fail (MX_IS_BIN (bin)); priv = bin->priv; if (priv->child) { MxPadding padding; ClutterActorBox allocation = { 0, }; mx_widget_get_padding (MX_WIDGET (bin), &padding); allocation.x1 = padding.left; allocation.x2 = box->x2 - box->x1 - padding.right; allocation.y1 = padding.top; allocation.y2 = box->y2 - box->y1 - padding.bottom; mx_allocate_align_fill (priv->child, &allocation, priv->x_align, priv->y_align, priv->x_fill, priv->y_fill); clutter_actor_allocate (priv->child, &allocation, flags); } }
static void mx_stack_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { GList *c; ClutterActorBox avail_space; MxStackPrivate *priv = MX_STACK (actor)->priv; CLUTTER_ACTOR_CLASS (mx_stack_parent_class)->allocate (actor, box, flags); mx_widget_get_available_area (MX_WIDGET (actor), box, &avail_space); memcpy (&priv->allocation, box, sizeof (priv->allocation)); for (c = priv->children; c; c = c->next) { gboolean x_fill, y_fill, fit, crop; MxAlign x_align, y_align; ClutterActor *child = c->data; ClutterActorBox child_box = avail_space; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; clutter_container_child_get (CLUTTER_CONTAINER (actor), child, "x-fill", &x_fill, "y-fill", &y_fill, "x-align", &x_align, "y-align", &y_align, "fit", &fit, "crop", &crop, NULL); /* when "crop" is set, fit and fill properties are ignored */ if (crop) { gfloat available_height, available_width; gfloat natural_width, natural_height; gfloat ratio_width, ratio_height, ratio_child; available_width = avail_space.x2 - avail_space.x1; available_height = avail_space.y2 - avail_space.y1; clutter_actor_get_preferred_size (child, NULL, NULL, &natural_width, &natural_height); ratio_child = natural_width / natural_height; ratio_width = available_width / natural_width; ratio_height = available_height / natural_height; if (ratio_width > ratio_height) { natural_width = available_width; natural_height = natural_width / ratio_child; } else { natural_height = available_height; natural_width = ratio_child * natural_height; } child_box.x1 = (available_width - natural_width) / 2; child_box.y1 = (available_height - natural_height) / 2; child_box.x2 = natural_width; child_box.y2 = natural_height; clutter_actor_allocate (child, &child_box, flags); continue; } /* when "fit" is set, fill properties are ignored */ if (fit) { gfloat available_height, available_width, width, height; gfloat min_width, natural_width, min_height, natural_height; ClutterRequestMode request_mode; available_height = avail_space.y2 - avail_space.y1; available_width = avail_space.x2 - avail_space.x1; request_mode = clutter_actor_get_request_mode (child); if (request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH) { clutter_actor_get_preferred_width (child, available_height, &min_width, &natural_width); width = CLAMP (natural_width, min_width, available_width); clutter_actor_get_preferred_height (child, width, &min_height, &natural_height); height = CLAMP (natural_height, min_height, available_height); } else { clutter_actor_get_preferred_height (child, available_width, &min_height, &natural_height); height = CLAMP (natural_height, min_height, available_height); clutter_actor_get_preferred_width (child, height, &min_width, &natural_width); width = CLAMP (natural_width, min_width, available_width); } child_box.x1 = 0; child_box.y1 = 0; switch (x_align) { case MX_ALIGN_START: break; case MX_ALIGN_MIDDLE: child_box.x1 += (gint)(available_width / 2 - width / 2); break; case MX_ALIGN_END: child_box.x1 = avail_space.x2 - width; break; } switch (y_align) { case MX_ALIGN_START: break; case MX_ALIGN_MIDDLE: child_box.y1 += (gint)(available_height / 2 - height / 2); break; case MX_ALIGN_END: child_box.y1 = avail_space.y2 - height; break; } child_box.x2 = child_box.x1 + width; child_box.y2 = child_box.y1 + height; clutter_actor_allocate (child, &child_box, flags); continue; } /* Adjust the available space when not filling, otherwise * actors that support width-for-height or height-for-width * allocation won't shrink correctly. */ if (!x_fill) { gfloat width; clutter_actor_get_preferred_width (child, -1, NULL, &width); switch (x_align) { case MX_ALIGN_START: break; case MX_ALIGN_MIDDLE: child_box.x1 += (gint)((avail_space.x2 - avail_space.x1) / 2 - width / 2); break; case MX_ALIGN_END: child_box.x1 = avail_space.x2 - width; break; } child_box.x2 = child_box.x1 + width; if (child_box.x2 > avail_space.x2) child_box.x2 = avail_space.x2; if (child_box.x1 < avail_space.x1) child_box.x1 = avail_space.x1; } if (!y_fill) { gfloat height; clutter_actor_get_preferred_height (child, -1, NULL, &height); switch (y_align) { case MX_ALIGN_START: break; case MX_ALIGN_MIDDLE: child_box.y1 += (gint)((avail_space.y2 - avail_space.y1) / 2 - height / 2); break; case MX_ALIGN_END: child_box.y1 = avail_space.y2 - height; break; } child_box.y2 = child_box.y1 + height; if (child_box.y2 > avail_space.y2) child_box.y2 = avail_space.y2; if (child_box.y1 < avail_space.y1) child_box.y1 = avail_space.y1; } mx_allocate_align_fill (child, &child_box, x_align, y_align, x_fill, y_fill); clutter_actor_allocate (child, &child_box, flags); } }
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); } }
static void mex_tile_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxPadding padding; ClutterActorBox child_box; gfloat available_width, available_height; ClutterEffect *fade; MexTilePrivate *priv = MEX_TILE (actor)->priv; CLUTTER_ACTOR_CLASS (mex_tile_parent_class)->allocate (actor, box, flags); mx_widget_get_padding (MX_WIDGET (actor), &padding); available_width = box->x2 - box->x1 - padding.left - padding.right; available_height = box->y2 - box->y1 - padding.top - padding.bottom; if (priv->child) { gfloat child_width, full_width, full_height; clutter_actor_get_preferred_size (priv->child, NULL, NULL, &full_width, &full_height); child_box.y1 = padding.top; if (clutter_alpha_get_alpha (priv->important_alpha) < 0.5) { child_width = full_width * (available_height / full_height); if (child_width > available_width) child_width = available_width; child_box.y2 = child_box.y1 + available_height; /* When we're in unimportant state, make sure the label * doesn't overlap the image. */ if (available_height < full_height) available_width -= child_width * ((0.5 - clutter_alpha_get_alpha (priv->important_alpha)) * 2); } else { child_width = available_width; clutter_actor_set_clip_to_allocation ( actor, (full_height > available_height)); child_box.y2 = child_box.y1 + full_height; } child_box.x2 = box->x2 - box->x1 - padding.right; child_box.x1 = child_box.x2 - child_width; mx_allocate_align_fill (priv->child, &child_box, MX_ALIGN_MIDDLE, MX_ALIGN_MIDDLE, FALSE, FALSE); clutter_actor_allocate (priv->child, &child_box, flags); } /* Allocate Header */ if (priv->header_visible) { gfloat icon1_w, icon1_h, icon2_w, icon2_h, label_h, label_w, header_h; gfloat middle_w; if (priv->header_padding) { padding.top += priv->header_padding->top; padding.right += priv->header_padding->right; padding.bottom += priv->header_padding->bottom; padding.left += priv->header_padding->left; } clutter_actor_get_preferred_size (priv->box_layout, NULL, NULL, &label_w, &label_h); if (priv->icon1) clutter_actor_get_preferred_size (priv->icon1, NULL, NULL, &icon1_w, &icon1_h); else icon1_h = icon1_w = 0; if (priv->icon2) clutter_actor_get_preferred_size (priv->icon2, NULL, NULL, &icon2_w, &icon2_h); else icon2_h = icon2_w = 0; header_h = MAX (icon1_h, MAX (icon2_h, label_h)); /* primary icon */ if (priv->icon1) { child_box.y1 = padding.top + (header_h / 2.0) - (icon1_h / 2.0); child_box.x1 = padding.left; child_box.y2 = child_box.y1 + icon1_h; child_box.x2 = child_box.x1 + icon1_w; clutter_actor_allocate (priv->icon1, &child_box, flags); child_box.x1 += icon1_w + 8; } else child_box.x1 = padding.left; /* label */ child_box.x2 = child_box.x1 + label_w; child_box.y1 = (int) (padding.top + (header_h / 2.0) - (label_h / 2.0)); child_box.y2 = child_box.y1 + label_h; fade = clutter_actor_get_effect (priv->box_layout, "fade"); middle_w = available_width - icon1_w - icon2_w; if (priv->header_padding) middle_w -= priv->header_padding->left + priv->header_padding->right; clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (fade), !(middle_w > label_w)); mx_fade_effect_set_bounds (MX_FADE_EFFECT (fade), 0, 0, middle_w, 0); clutter_actor_allocate (priv->box_layout, &child_box, flags); /* secondary icon */ if (priv->icon2) { child_box.x2 = (box->x2 - box->x1) - padding.right; child_box.x1 = child_box.x2 - icon2_w; child_box.y1 = padding.top + (header_h / 2.0) - (icon2_h / 2.0); child_box.y2 = child_box.y1 + icon2_h; clutter_actor_allocate (priv->icon2, &child_box, flags); } priv->header_height = header_h; if (priv->header_padding) priv->header_height += priv->header_padding->top + priv->header_padding->bottom; } }
static void mx_label_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxLabelPrivate *priv = MX_LABEL (actor)->priv; gboolean label_did_fade = priv->label_should_fade; ClutterActorClass *parent_class; ClutterActorBox child_box; gboolean x_fill, y_fill; gfloat avail_width; parent_class = CLUTTER_ACTOR_CLASS (mx_label_parent_class); parent_class->allocate (actor, box, flags); mx_widget_get_available_area (MX_WIDGET (actor), box, &child_box); avail_width = child_box.x2 - child_box.x1; /* The default behaviour of ClutterText is to align to the * top-left when it gets more space than is needed. Because * of this behaviour, if we're aligning to the left, we can * assign all our horizontal space to the label without * measuring it (i.e. x-fill), and the same applies for * aligning to the top and vertical space. */ x_fill = (priv->x_align == MX_ALIGN_START) ? TRUE : FALSE; y_fill = (priv->y_align == MX_ALIGN_START) ? TRUE : FALSE; mx_allocate_align_fill (priv->label, &child_box, priv->x_align, priv->y_align, x_fill, y_fill); priv->label_should_fade = FALSE; if (priv->fade_out) { /* If we're fading out, make sure the label has its full width * allocated. This ensures that the offscreen effect has the full * label inside its texture. */ gfloat label_width; clutter_actor_get_preferred_width (priv->label, -1, NULL, &label_width); if (label_width > avail_width) { priv->label_should_fade = TRUE; child_box.x2 = child_box.x1 + label_width; } mx_fade_effect_set_bounds (MX_FADE_EFFECT (priv->fade_effect), 0, 0, MIN (label_width, avail_width), 0); } /* Allocate the label */ clutter_actor_allocate (priv->label, &child_box, flags); if (priv->show_tooltip) { PangoLayout *layout; const gchar *text; layout = clutter_text_get_layout (CLUTTER_TEXT (priv->label)); if (pango_layout_is_ellipsized (layout)) text = clutter_text_get_text (CLUTTER_TEXT (priv->label)); else text = NULL; mx_widget_set_tooltip_text (MX_WIDGET (actor), text); } /* Animate in/out the faded end of the label */ if (label_did_fade != priv->label_should_fade) { /* Begin/reverse the fading timeline when necessary */ if (priv->label_should_fade) clutter_timeline_set_direction (priv->fade_timeline, CLUTTER_TIMELINE_FORWARD); else clutter_timeline_set_direction (priv->fade_timeline, CLUTTER_TIMELINE_BACKWARD); if (!clutter_timeline_is_playing (priv->fade_timeline)) clutter_timeline_rewind (priv->fade_timeline); clutter_timeline_start (priv->fade_timeline); } }
static void mx_table_preferred_allocate (ClutterActor *self, const ClutterActorBox *box, gboolean flags) { gint row_spacing, col_spacing; gint i; MxTable *table; MxTablePrivate *priv; MxPadding padding; DimensionData *rows, *columns; ClutterActorIter iter; ClutterActor *child; table = MX_TABLE (self); priv = MX_TABLE (self)->priv; mx_widget_get_padding (MX_WIDGET (self), &padding); col_spacing = (priv->col_spacing); row_spacing = (priv->row_spacing); mx_table_calculate_dimensions (table, box->x2 - box->x1, box->y2 - box->y1); rows = &g_array_index (priv->rows, DimensionData, 0); columns = &g_array_index (priv->columns, DimensionData, 0); clutter_actor_iter_init (&iter, self); while (clutter_actor_iter_next (&iter, &child)) { gint row, col, row_span, col_span; gint col_width, row_height; MxTableChild *meta; ClutterActorBox childbox; gint child_x, child_y; gdouble x_align_d, y_align_d; gboolean x_fill, y_fill; MxAlign x_align, y_align; meta = (MxTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (self), child); if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; /* get child properties */ col = meta->col; row = meta->row; row_span = meta->row_span; col_span = meta->col_span; x_align_d = meta->x_align; y_align_d = meta->y_align; x_fill = meta->x_fill; y_fill = meta->y_fill; /* Convert to MxAlign */ if (x_align_d < 1.0 / 3.0) x_align = MX_ALIGN_START; else if (x_align_d > 2.0 / 3.0) x_align = MX_ALIGN_END; else x_align = MX_ALIGN_MIDDLE; if (y_align_d < 1.0 / 3.0) y_align = MX_ALIGN_START; else if (y_align_d > 2.0 / 3.0) y_align = MX_ALIGN_END; else y_align = MX_ALIGN_MIDDLE; /* initialise the width and height */ col_width = columns[col].final_size; row_height = rows[row].final_size; /* Add the widths of the spanned columns: * * First check that we have a non-zero span. Then we loop over each of * the columns that we're spanning but we stop short if we go past the * number of columns in the table. This is necessary to avoid accessing * uninitialised memory. We add the spacing in here too since we only * want to add as much spacing as times we successfully span. */ if (col + col_span > priv->n_cols) g_warning ("MxTable: col-span exceeds number of columns"); if (row + row_span > priv->n_rows) g_warning ("MxTable: row-span exceeds number of rows"); if (col_span > 1) { for (i = col + 1; i < col + col_span && i < priv->n_cols; i++) { col_width += columns[i].final_size; col_width += col_spacing; } } /* add the height of the spanned rows */ if (row_span > 1) { for (i = row + 1; i < row + row_span && i < priv->n_rows; i++) { row_height += rows[i].final_size; row_height += row_spacing; } } /* calculate child x */ child_x = (int) padding.left; for (i = 0; i < col; i++) { if (columns[i].is_visible) { child_x += columns[i].final_size; child_x += col_spacing; } } /* calculate child y */ child_y = (int) padding.top; for (i = 0; i < row; i++) { if (rows[i].is_visible) { child_y += rows[i].final_size; child_y += row_spacing; } } /* set up childbox */ childbox.x1 = (float) child_x; childbox.x2 = (float) MAX (0, child_x + col_width); childbox.y1 = (float) child_y; childbox.y2 = (float) MAX (0, child_y + row_height); mx_allocate_align_fill (child, &childbox, x_align, y_align, x_fill, y_fill); clutter_actor_allocate (child, &childbox, flags); } }