static void clutter_flow_layout_allocate (ClutterLayoutManager *manager, ClutterContainer *container, const ClutterActorBox *allocation, ClutterAllocationFlags flags) { ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->priv; ClutterActor *actor, *child; ClutterActorIter iter; gfloat x_off, y_off; gfloat avail_width, avail_height; gfloat item_x, item_y; gint line_item_count; gint items_per_line; gint line_index; actor = CLUTTER_ACTOR (container); if (clutter_actor_get_n_children (actor) == 0) return; clutter_actor_box_get_origin (allocation, &x_off, &y_off); clutter_actor_box_get_size (allocation, &avail_width, &avail_height); /* blow the cached preferred size and re-compute with the given * available size in case the FlowLayout wasn't given the exact * size it requested */ if ((priv->req_width >= 0 && avail_width != priv->req_width) || (priv->req_height >= 0 && avail_height != priv->req_height)) { clutter_flow_layout_get_preferred_width (manager, container, avail_height, NULL, NULL); clutter_flow_layout_get_preferred_height (manager, container, avail_width, NULL, NULL); } items_per_line = compute_lines (CLUTTER_FLOW_LAYOUT (manager), avail_width, avail_height); item_x = x_off; item_y = y_off; line_item_count = 0; line_index = 0; clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_next (&iter, &child)) { ClutterActorBox child_alloc; gfloat item_width, item_height; gfloat new_x, new_y; gfloat child_min, child_natural; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; new_x = new_y = 0; if (!priv->snap_to_grid) clutter_actor_get_preferred_size (child, NULL, NULL, &item_width, &item_height); if (priv->orientation == CLUTTER_FLOW_HORIZONTAL) { if ((priv->snap_to_grid && line_item_count == items_per_line && line_item_count > 0) || (!priv->snap_to_grid && item_x + item_width > avail_width)) { item_y += g_array_index (priv->line_natural, gfloat, line_index); if (line_index >= 0) item_y += priv->row_spacing; line_item_count = 0; line_index += 1; item_x = x_off; } if (priv->snap_to_grid) { new_x = x_off + ((line_item_count + 1) * (avail_width + priv->col_spacing)) / items_per_line; item_width = new_x - item_x - priv->col_spacing; } else { new_x = item_x + item_width + priv->col_spacing; } item_height = g_array_index (priv->line_natural, gfloat, line_index); } else { if ((priv->snap_to_grid && line_item_count == items_per_line && line_item_count > 0) || (!priv->snap_to_grid && item_y + item_height > avail_height)) { item_x += g_array_index (priv->line_natural, gfloat, line_index); if (line_index >= 0) item_x += priv->col_spacing; line_item_count = 0; line_index += 1; item_y = y_off; } if (priv->snap_to_grid) { new_y = y_off + ((line_item_count + 1) * (avail_height + priv->row_spacing)) / items_per_line; item_height = new_y - item_y - priv->row_spacing; } else { new_y = item_y + item_height + priv->row_spacing; } item_width = g_array_index (priv->line_natural, gfloat, line_index); } if (!priv->is_homogeneous && !clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_HORIZONTAL)) { clutter_actor_get_preferred_width (child, item_height, &child_min, &child_natural); item_width = MIN (item_width, child_natural); } if (!priv->is_homogeneous && !clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_VERTICAL)) { clutter_actor_get_preferred_height (child, item_width, &child_min, &child_natural); item_height = MIN (item_height, child_natural); } CLUTTER_NOTE (LAYOUT, "flow[line:%d, item:%d/%d] =" "{ %.2f, %.2f, %.2f, %.2f }", line_index, line_item_count + 1, items_per_line, item_x, item_y, item_width, item_height); child_alloc.x1 = ceil (item_x); child_alloc.y1 = ceil (item_y); child_alloc.x2 = ceil (child_alloc.x1 + item_width); child_alloc.y2 = ceil (child_alloc.y1 + item_height); clutter_actor_allocate (child, &child_alloc, flags); if (priv->orientation == CLUTTER_FLOW_HORIZONTAL) item_x = new_x; else item_y = new_y; line_item_count += 1; } }
static void clutter_bin_layout_allocate (ClutterLayoutManager *manager, ClutterContainer *container, const ClutterActorBox *allocation, ClutterAllocationFlags flags) { gfloat allocation_x, allocation_y; gfloat available_w, available_h; ClutterActor *actor, *child; ClutterActorIter iter; gboolean use_animations; ClutterAnimationMode easing_mode; guint easing_duration, easing_delay; clutter_actor_box_get_origin (allocation, &allocation_x, &allocation_y); clutter_actor_box_get_size (allocation, &available_w, &available_h); actor = CLUTTER_ACTOR (container); use_animations = clutter_layout_manager_get_easing_state (manager, &easing_mode, &easing_duration, &easing_delay); clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_next (&iter, &child)) { ClutterLayoutMeta *meta; ClutterBinLayer *layer; ClutterActorBox child_alloc = { 0, }; gdouble x_align, y_align; gboolean x_fill, y_fill; meta = clutter_layout_manager_get_child_meta (manager, container, child); layer = CLUTTER_BIN_LAYER (meta); if (layer->x_align == CLUTTER_BIN_ALIGNMENT_FIXED) child_alloc.x1 = clutter_actor_get_x (child); else child_alloc.x1 = allocation_x; if (layer->y_align == CLUTTER_BIN_ALIGNMENT_FIXED) child_alloc.y1 = clutter_actor_get_y (child); else child_alloc.y1 = allocation_y; child_alloc.x2 = available_w; child_alloc.y2 = available_h; if (clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_HORIZONTAL)) { ClutterActorAlign align; align = _clutter_actor_get_effective_x_align (child); x_fill = align == CLUTTER_ACTOR_ALIGN_FILL; x_align = get_actor_align_factor (align); } else { x_fill = (layer->x_align == CLUTTER_BIN_ALIGNMENT_FILL); x_align = get_bin_alignment_factor (layer->x_align); } if (clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_VERTICAL)) { ClutterActorAlign align; align = clutter_actor_get_y_align (child); y_fill = align == CLUTTER_ACTOR_ALIGN_FILL; y_align = get_actor_align_factor (align); } else { y_fill = (layer->y_align == CLUTTER_BIN_ALIGNMENT_FILL); y_align = get_bin_alignment_factor (layer->y_align); } if (use_animations) { clutter_actor_save_easing_state (child); clutter_actor_set_easing_mode (child, easing_mode); clutter_actor_set_easing_duration (child, easing_duration); clutter_actor_set_easing_delay (child, easing_delay); } clutter_actor_allocate_align_fill (child, &child_alloc, x_align, y_align, x_fill, y_fill, flags); if (use_animations) clutter_actor_restore_easing_state (child); } }
static void calculate_col_widths (ClutterTableLayout *self, ClutterContainer *container, gint for_width) { ClutterTableLayoutPrivate *priv = self->priv; ClutterLayoutManager *manager = CLUTTER_LAYOUT_MANAGER (self); ClutterActor *actor, *child; gint i; DimensionData *columns; ClutterOrientation orientation = CLUTTER_ORIENTATION_HORIZONTAL; update_row_col (self, container); g_array_set_size (priv->columns, 0); g_array_set_size (priv->columns, priv->n_cols); columns = (DimensionData *) (void *) priv->columns->data; /* reset the visibility of all columns */ priv->visible_cols = 0; for (i = 0; i < priv->n_cols; i++) { columns[i].expand = FALSE; columns[i].visible = FALSE; } actor = CLUTTER_ACTOR (container); /* STAGE ONE: calculate column widths for non-spanned children */ for (child = clutter_actor_get_first_child (actor); child != NULL; child = clutter_actor_get_next_sibling (child)) { ClutterTableChild *meta; DimensionData *col; gfloat c_min, c_pref; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; meta = CLUTTER_TABLE_CHILD (clutter_layout_manager_get_child_meta (manager, container, child)); if (meta->col_span > 1) continue; col = &columns[meta->col]; if (!col->visible) { col->visible = TRUE; priv->visible_cols += 1; } clutter_actor_get_preferred_width (child, -1, &c_min, &c_pref); col->min_size = MAX (col->min_size, c_min); col->pref_size = MAX (col->pref_size, c_pref); if (!col->expand) col->expand = clutter_actor_needs_expand (child, orientation); } /* STAGE TWO: take spanning children into account */ for (child = clutter_actor_get_first_child (actor); child != NULL; child = clutter_actor_get_next_sibling (child)) { ClutterTableChild *meta; gfloat c_min, c_pref; gfloat min_width, pref_width; gint start_col, end_col; gint n_expand; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; meta = CLUTTER_TABLE_CHILD (clutter_layout_manager_get_child_meta (manager, container, child)); if (meta->col_span < 2) continue; start_col = meta->col; end_col = meta->col + meta->col_span - 1; clutter_actor_get_preferred_width (child, -1, &c_min, &c_pref); /* check there is enough room for this actor */ min_width = 0; pref_width = 0; n_expand = 0; for (i = start_col; i <= end_col; i++) { min_width += columns[i].min_size; pref_width += columns[i].pref_size; if (columns[i].expand) n_expand++; if (!columns[i].visible) { columns[i].visible = TRUE; priv->visible_cols += 1; } if (!columns[i].expand) columns[i].expand = clutter_actor_needs_expand (child, orientation); } min_width += priv->col_spacing * (meta->col_span - 1); pref_width += priv->col_spacing * (meta->col_span - 1); /* see calculate_row_heights() for comments */ /* (1) */ if (c_min > min_width) { /* (2) */ /* we can start from preferred width and decrease */ if (pref_width > c_min) { for (i = start_col; i <= end_col; i++) columns[i].final_size = columns[i].pref_size; while (pref_width > c_min) { for (i = start_col; i <= end_col; i++) { if (columns[i].final_size > columns[i].min_size) { columns[i].final_size--; pref_width--; } } } for (i = start_col; i <= end_col; i++) columns[i].min_size = columns[i].final_size; } else { /* (3) */ /* we can expand from preferred size */ gfloat expand_by; expand_by = c_pref - pref_width; for (i = start_col; i <= end_col; i++) { if (n_expand) { if (columns[i].expand) columns[i].min_size = columns[i].pref_size + expand_by / n_expand; } else columns[i].min_size = columns[i].pref_size + expand_by / meta->col_span; } } } } /* calculate final widths */ if (for_width >= 0) { gfloat min_width, pref_width; gint n_expand; min_width = 0; pref_width = 0; n_expand = 0; for (i = 0; i < self->priv->n_cols; i++) { pref_width += columns[i].pref_size; min_width += columns[i].min_size; if (columns[i].expand) n_expand++; } pref_width += priv->col_spacing * (priv->n_cols - 1); min_width += priv->col_spacing * (priv->n_cols - 1); if (for_width <= min_width) { /* erk, we can't shrink this! */ for (i = 0; i < priv->n_cols; i++) columns[i].final_size = columns[i].min_size; return; } if (for_width == pref_width) { /* perfect! */ for (i = 0; i < self->priv->n_cols; i++) columns[i].final_size = columns[i].pref_size; return; } /* for_width is between min_width and pref_width */ if (for_width < pref_width && for_width > min_width) { gfloat width; /* shrink columns until they reach min_width */ /* start with all columns at preferred size */ for (i = 0; i < self->priv->n_cols; i++) columns[i].final_size = columns[i].pref_size; width = pref_width; while (width > for_width) { for (i = 0; i < self->priv->n_cols; i++) { if (columns[i].final_size > columns[i].min_size) { columns[i].final_size--; width--; } } } return; } /* expand columns */ if (for_width > pref_width) { gfloat extra_width = for_width - pref_width; gint remaining; if (n_expand) remaining = (gint) extra_width % n_expand; else remaining = (gint) extra_width % priv->n_cols; for (i = 0; i < self->priv->n_cols; i++) { if (columns[i].expand) { columns[i].final_size = columns[i].pref_size + (extra_width / n_expand); } else columns[i].final_size = columns[i].pref_size; } /* distribute the remainder among children */ i = 0; while (remaining) { columns[i].final_size++; i++; remaining--; } } } }
static void calculate_row_heights (ClutterTableLayout *self, ClutterContainer *container, gint for_height) { ClutterTableLayoutPrivate *priv = self->priv; ClutterLayoutManager *manager = CLUTTER_LAYOUT_MANAGER (self); ClutterActor *actor, *child; gint i; DimensionData *rows, *columns; ClutterOrientation orientation = CLUTTER_ORIENTATION_VERTICAL; update_row_col (self, container); g_array_set_size (priv->rows, 0); g_array_set_size (priv->rows, self->priv->n_rows); rows = (DimensionData *) (void *) priv->rows->data; columns = (DimensionData *) (void *) priv->columns->data; /* reset the visibility of all rows */ priv->visible_rows = 0; for (i = 0; i < priv->n_rows; i++) { rows[i].expand = FALSE; rows[i].visible = FALSE; } actor = CLUTTER_ACTOR (container); /* STAGE ONE: calculate row heights for non-spanned children */ for (child = clutter_actor_get_first_child (actor); child != NULL; child = clutter_actor_get_next_sibling (child)) { ClutterTableChild *meta; DimensionData *row; gfloat c_min, c_pref; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; meta = CLUTTER_TABLE_CHILD (clutter_layout_manager_get_child_meta (manager, container, child)); if (meta->row_span > 1) continue; row = &rows[meta->row]; if (!row->visible) { row->visible = TRUE; priv->visible_rows += 1; } clutter_actor_get_preferred_height (child, columns[meta->col].final_size, &c_min, &c_pref); row->min_size = MAX (row->min_size, c_min); row->pref_size = MAX (row->pref_size, c_pref); if (!row->expand) row->expand = clutter_actor_needs_expand (child, orientation); } /* STAGE TWO: take spanning children into account */ for (child = clutter_actor_get_first_child (actor); child != NULL; child = clutter_actor_get_next_sibling (child)) { ClutterTableChild *meta; gfloat c_min, c_pref; gfloat min_height, pref_height; gint start_row, end_row; gint n_expand; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; meta = CLUTTER_TABLE_CHILD (clutter_layout_manager_get_child_meta (manager, container, child)); if (meta->row_span < 2) continue; start_row = meta->row; end_row = meta->row + meta->row_span - 1; clutter_actor_get_preferred_height (child, columns[meta->col].final_size, &c_min, &c_pref); /* check there is enough room for this actor */ min_height = 0; pref_height = 0; n_expand = 0; for (i = start_row; i <= end_row; i++) { min_height += rows[i].min_size; pref_height += rows[i].pref_size; if (rows[i].expand) n_expand++; if (!rows[i].visible) { rows[i].visible = TRUE; priv->visible_rows += 1; } if (!rows[i].expand) rows[i].expand = clutter_actor_needs_expand (child, orientation); } min_height += priv->row_spacing * (meta->row_span - 1); pref_height += priv->row_spacing * (meta->row_span - 1); /* 1) If the minimum height of the rows spanned is less than the * minimum height of the child that is spanning them, then we * must increase the minimum height of the rows spanned. * * 2) If the preferred height of the spanned rows is more than * the minimum height of the spanning child, then we can start * at this size and decrease each row evenly. * * 3) If the preferred height of the rows is more than the minimum * height of the spanned child, then we can start at the preferred * height and expand. */ /* (1) */ if (c_min > min_height) { /* (2) */ /* we can start from preferred height and decrease */ if (pref_height > c_min) { for (i = start_row; i <= end_row; i++) rows[i].final_size = rows[i].pref_size; while (pref_height > c_min) { for (i = start_row; i <= end_row; i++) { if (rows[i].final_size > rows[i].min_size) { rows[i].final_size--; pref_height--; } } } for (i = start_row; i <= end_row; i++) rows[i].min_size = rows[i].final_size; } else { /* (3) */ /* we can expand from preferred size */ gfloat expand_by = c_pref - pref_height; for (i = start_row; i <= end_row; i++) { if (n_expand) { if (rows[i].expand) rows[i].min_size = rows[i].pref_size + expand_by / n_expand; } else rows[i].min_size = rows[i].pref_size + expand_by / meta->row_span; } } } } /* calculate final heights */ if (for_height >= 0) { gfloat min_height, pref_height; gint n_expand; min_height = 0; pref_height = 0; n_expand = 0; for (i = 0; i < self->priv->n_rows; i++) { pref_height += rows[i].pref_size; min_height += rows[i].min_size; if (rows[i].expand) n_expand++; } pref_height += priv->row_spacing * (priv->n_rows - 1); min_height += priv->row_spacing * (priv->n_rows - 1); if (for_height <= min_height) { /* erk, we can't shrink this! */ for (i = 0; i < self->priv->n_rows; i++) rows[i].final_size = rows[i].min_size; return; } if (for_height == pref_height) { /* perfect! */ for (i = 0; i < self->priv->n_rows; i++) rows[i].final_size = rows[i].pref_size; return; } /* for_height is between min_height and pref_height */ if (for_height < pref_height && for_height > min_height) { gfloat height; /* shrink rows until they reach min_height */ /* start with all rows at preferred size */ for (i = 0; i < self->priv->n_rows; i++) rows[i].final_size = rows[i].pref_size; height = pref_height; while (height > for_height) { for (i = 0; i < priv->n_rows; i++) { if (rows[i].final_size > rows[i].min_size) { rows[i].final_size--; height--; } } } return; } /* expand rows */ if (for_height > pref_height) { gfloat extra_height = for_height - pref_height; gint remaining; if (n_expand) remaining = (gint) extra_height % n_expand; else remaining = (gint) extra_height % self->priv->n_rows; for (i = 0; i < self->priv->n_rows; i++) { if (rows[i].expand) { rows[i].final_size = rows[i].pref_size + (extra_height / n_expand); } else rows[i].final_size = rows[i].pref_size; } /* distribute the remainder among children */ i = 0; while (remaining) { rows[i].final_size++; i++; remaining--; } } } }
static void clutter_bin_layout_allocate (ClutterLayoutManager *manager, ClutterContainer *container, const ClutterActorBox *allocation, ClutterAllocationFlags flags) { gfloat allocation_x, allocation_y; gfloat available_w, available_h; ClutterActor *actor, *child; ClutterActorIter iter; clutter_actor_box_get_origin (allocation, &allocation_x, &allocation_y); clutter_actor_box_get_size (allocation, &available_w, &available_h); actor = CLUTTER_ACTOR (container); clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_next (&iter, &child)) { ClutterLayoutMeta *meta; ClutterBinLayer *layer; ClutterActorBox child_alloc = { 0, }; gdouble x_align, y_align; gboolean x_fill, y_fill, is_fixed_position_set; float fixed_x, fixed_y; if (!clutter_actor_is_visible (child)) continue; meta = clutter_layout_manager_get_child_meta (manager, container, child); layer = CLUTTER_BIN_LAYER (meta); fixed_x = fixed_y = 0.f; g_object_get (child, "fixed-position-set", &is_fixed_position_set, "fixed-x", &fixed_x, "fixed-y", &fixed_y, NULL); /* XXX:2.0 - remove the FIXED alignment, and just use the fixed position * of the actor if one is set */ if (is_fixed_position_set || layer->x_align == CLUTTER_BIN_ALIGNMENT_FIXED) { if (is_fixed_position_set) child_alloc.x1 = fixed_x; else child_alloc.x1 = clutter_actor_get_x (child); } else child_alloc.x1 = allocation_x; if (is_fixed_position_set || layer->y_align == CLUTTER_BIN_ALIGNMENT_FIXED) { if (is_fixed_position_set) child_alloc.y1 = fixed_y; else child_alloc.y1 = clutter_actor_get_y (child); } else child_alloc.y1 = allocation_y; child_alloc.x2 = allocation_x + available_w; child_alloc.y2 = allocation_y + available_h; if (clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_HORIZONTAL)) { ClutterActorAlign align; align = clutter_actor_get_x_align (child); x_fill = align == CLUTTER_ACTOR_ALIGN_FILL; x_align = get_actor_align_factor (align); } else { ClutterTextDirection text_dir; x_fill = (layer->x_align == CLUTTER_BIN_ALIGNMENT_FILL); text_dir = clutter_actor_get_text_direction (child); if (!is_fixed_position_set) x_align = get_bin_alignment_factor (layer->x_align, text_dir); else x_align = 0.0; } if (clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_VERTICAL)) { ClutterActorAlign align; align = clutter_actor_get_y_align (child); y_fill = align == CLUTTER_ACTOR_ALIGN_FILL; y_align = get_actor_align_factor (align); } else { y_fill = (layer->y_align == CLUTTER_BIN_ALIGNMENT_FILL); if (!is_fixed_position_set) y_align = get_bin_alignment_factor (layer->y_align, CLUTTER_TEXT_DIRECTION_LTR); else y_align = 0.0; } clutter_actor_allocate_align_fill (child, &child_alloc, x_align, y_align, x_fill, y_fill, flags); } }