/** * _st_allocate_fill: * @parent: the parent #StWidget * @child: the child (not necessarily an #StWidget) * @childbox: total space that could be allocated to @child * @x_alignment: horizontal alignment within @childbox * @y_alignment: vertical alignment within @childbox * @x_fill: whether or not to fill @childbox horizontally * @y_fill: whether or not to fill @childbox vertically * * Given @childbox, containing the initial allocation of @child, this * adjusts the horizontal allocation if @x_fill is %FALSE, and the * vertical allocation if @y_fill is %FALSE, by: * * - reducing the allocation if it is larger than @child's natural * size. * * - adjusting the position of the child within the allocation * according to @x_alignment/@y_alignment (and flipping * @x_alignment if @parent has %ST_TEXT_DIRECTION_RTL) * * If @x_fill and @y_fill are both %TRUE, or if @child's natural size * is larger than the initial allocation in @childbox, then @childbox * will be unchanged. * * If you are allocating children with _st_allocate_fill(), you should * determine their preferred sizes using * _st_actor_get_preferred_width() and * _st_actor_get_preferred_height(), not with the corresponding * Clutter methods. */ void _st_allocate_fill (StWidget *parent, ClutterActor *child, ClutterActorBox *childbox, StAlign x_alignment, StAlign y_alignment, gboolean x_fill, gboolean y_fill) { gfloat natural_width, natural_height; gfloat min_width, min_height; gfloat child_width, child_height; gfloat available_width, available_height; ClutterRequestMode request; gdouble x_align, y_align; available_width = childbox->x2 - childbox->x1; available_height = childbox->y2 - childbox->y1; if (available_width < 0) { available_width = 0; childbox->x2 = childbox->x1; } if (available_height < 0) { available_height = 0; childbox->y2 = childbox->y1; } /* If we are filling both horizontally and vertically then we don't * need to do anything else. */ if (x_fill && y_fill) return; _st_get_align_factors (parent, x_alignment, y_alignment, &x_align, &y_align); /* The following is based on clutter_actor_get_preferred_size(), but * modified to cope with the fact that the available size may be * less than the preferred size. */ request = clutter_actor_get_request_mode (child); if (request == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH) { clutter_actor_get_preferred_width (child, -1, &min_width, &natural_width); child_width = CLAMP (natural_width, min_width, available_width); clutter_actor_get_preferred_height (child, child_width, &min_height, &natural_height); child_height = CLAMP (natural_height, min_height, available_height); } else { clutter_actor_get_preferred_height (child, -1, &min_height, &natural_height); child_height = CLAMP (natural_height, min_height, available_height); clutter_actor_get_preferred_width (child, child_height, &min_width, &natural_width); child_width = CLAMP (natural_width, min_width, available_width); } if (!x_fill) { childbox->x1 += (int)((available_width - child_width) * x_align); childbox->x2 = childbox->x1 + (int) child_width; } if (!y_fill) { childbox->y1 += (int)((available_height - child_height) * y_align); childbox->y2 = childbox->y1 + (int) child_height; } }
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_column_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p) { GList *c; MxPadding padding; gfloat min_width, nat_width; gfloat min_header, nat_header; gfloat min_placeholder, nat_placeholder; MexColumn *self = MEX_COLUMN (actor); MexColumnPrivate *priv = self->priv; clutter_actor_get_preferred_width (priv->header, -1, &min_header, &nat_header); if (priv->adjustment) for_height = -1; else if (for_height >= 0) { gfloat height; clutter_actor_get_preferred_height (priv->header, -1, NULL, &height); for_height = MAX (0, for_height - height); } if (priv->n_items < 1) { if (priv->placeholder_actor) { clutter_actor_get_preferred_width (priv->placeholder_actor, for_height, &min_placeholder, &nat_placeholder); min_width = MAX (min_header, min_placeholder); nat_width = MAX (min_header, nat_placeholder); } else { min_width = min_header; nat_width = nat_header; } } else { min_width = nat_width = 0; for_height /= (gfloat)priv->n_items; for (c = priv->children; c; c = c->next) { gfloat child_min_width, child_nat_width; ClutterActor *child = c->data; clutter_actor_get_preferred_width (child, for_height, &child_min_width, &child_nat_width); if (child_min_width > min_width) min_width = child_min_width; if (child_nat_width > nat_width) nat_width = child_nat_width; } if (min_header > min_width) min_width = min_header; if (min_header > nat_width) nat_width = min_header; } mx_widget_get_padding (MX_WIDGET (actor), &padding); if (min_width_p) *min_width_p = min_width + padding.left + padding.right; if (nat_width_p) *nat_width_p = nat_width + padding.left + padding.right; }
static void mex_column_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { ClutterActorBox child_box; MxPadding padding; GList *c; MexColumn *column = MEX_COLUMN (actor); MexColumnPrivate *priv = column->priv; CLUTTER_ACTOR_CLASS (mex_column_parent_class)->allocate (actor, box, flags); mx_widget_get_padding (MX_WIDGET (actor), &padding); child_box.x1 = padding.left; child_box.x2 = box->x2 - box->x1 - padding.right; child_box.y1 = padding.top; child_box.y2 = box->y2 - box->y1 - padding.bottom; if (priv->n_items) { /* Calculate child height multiplier */ gfloat width, pref_height, avail_height, ratio, remainder; if (!priv->adjustment) { /* Find out the height available for each actor as a ratio of * their preferred height. */ clutter_actor_get_preferred_height (actor, box->x2 - box->x1, NULL, &pref_height); pref_height -= padding.top + padding.bottom; avail_height = child_box.y2 - child_box.y1; ratio = avail_height / pref_height; } else ratio = 1; /* Allocate children */ remainder = 0; width = child_box.x2 - child_box.x1; for (c = priv->children; c; c = c->next) { gfloat min_height, nat_height, height; ClutterActor *child = c->data; clutter_actor_get_preferred_height (child, width, &min_height, &nat_height); /* Calculate the allocatable height and keep an accumulator so * when we round to a pixel, we don't end up with a lot of * empty space at the end of the actor. */ height = MAX (min_height, nat_height / ratio); remainder += (height - (gint)height); height = (gint)height; while (remainder >= 1.f) { height += 1.f; remainder -= 1.f; } /* Allocate the child */ child_box.y2 = child_box.y1 + height; clutter_actor_allocate (child, &child_box, flags); /* Set the top position of the next child box */ child_box.y1 = child_box.y2; } } /* Make sure the adjustment reflects the column's allocation */ if (priv->adjustment) { gdouble page_size = box->y2 - box->y1 - padding.top - padding.bottom; mx_adjustment_set_values (priv->adjustment, mx_adjustment_get_value (priv->adjustment), 0.0, child_box.y2 - padding.top, 1.0, page_size, page_size); } }
static void scroll_bar_allocate_children (StScrollBar *bar, const ClutterActorBox *box, ClutterAllocationFlags flags) { StScrollBarPrivate *priv = bar->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (bar)); ClutterActorBox content_box, bw_box, fw_box, trough_box; gfloat bw_stepper_size, fw_stepper_size, min_size, natural_size; st_theme_node_get_content_box (theme_node, box, &content_box); if (priv->vertical) { gfloat width = content_box.x2 - content_box.x1; clutter_actor_get_preferred_height (priv->bw_stepper, width, &min_size, &natural_size); bw_stepper_size = MAX (min_size, natural_size); /* Backward stepper */ bw_box.x1 = content_box.x1; bw_box.y1 = content_box.y1; bw_box.x2 = content_box.x2; bw_box.y2 = bw_box.y1 + bw_stepper_size; clutter_actor_allocate (priv->bw_stepper, &bw_box, flags); clutter_actor_get_preferred_height (priv->fw_stepper, width, &min_size, &natural_size); fw_stepper_size = MAX (min_size, natural_size); /* Forward stepper */ fw_box.x1 = content_box.x1; fw_box.y1 = content_box.y2 - fw_stepper_size; fw_box.x2 = content_box.x2; fw_box.y2 = content_box.y2; clutter_actor_allocate (priv->fw_stepper, &fw_box, flags); /* Trough */ trough_box.x1 = content_box.x1; trough_box.y1 = content_box.y1 + bw_stepper_size; trough_box.x2 = content_box.x2; trough_box.y2 = content_box.y2 - fw_stepper_size; clutter_actor_allocate (priv->trough, &trough_box, flags); } else { gfloat height = content_box.y2 - content_box.y1; clutter_actor_get_preferred_width (priv->bw_stepper, height, &min_size, &natural_size); bw_stepper_size = MAX (min_size, natural_size); /* Backward stepper */ bw_box.x1 = content_box.x1; bw_box.y1 = content_box.y1; bw_box.x2 = bw_box.x1 + bw_stepper_size; bw_box.y2 = content_box.y2; clutter_actor_allocate (priv->bw_stepper, &bw_box, flags); clutter_actor_get_preferred_width (priv->fw_stepper, height, &min_size, &natural_size); fw_stepper_size = MAX (min_size, natural_size); /* Forward stepper */ fw_box.x1 = content_box.x2 - fw_stepper_size; fw_box.y1 = content_box.y1; fw_box.x2 = content_box.x2; fw_box.y2 = content_box.y2; clutter_actor_allocate (priv->fw_stepper, &fw_box, flags); /* Trough */ trough_box.x1 = content_box.x1 + bw_stepper_size; trough_box.y1 = content_box.y1; trough_box.x2 = content_box.x2 - fw_stepper_size; trough_box.y2 = content_box.y2; clutter_actor_allocate (priv->trough, &trough_box, flags); } if (priv->adjustment) { float handle_size, position, avail_size, stepper_size; gdouble value, lower, upper, page_size, increment, min_size, max_size; ClutterActorBox handle_box = { 0, }; stepper_size = bw_stepper_size + fw_stepper_size; st_adjustment_get_values (priv->adjustment, &value, &lower, &upper, NULL, NULL, &page_size); if ((upper == lower) || (page_size >= (upper - lower))) increment = 1.0; else increment = page_size / (upper - lower); min_size = 32.; st_theme_node_lookup_length (theme_node, "min-size", FALSE, &min_size); max_size = G_MAXINT16; st_theme_node_lookup_length (theme_node, "max-size", FALSE, &max_size); if (upper - lower - page_size <= 0) position = 0; else position = (value - lower) / (upper - lower - page_size); if (priv->vertical) { avail_size = content_box.y2 - content_box.y1 - stepper_size; handle_size = increment * avail_size; handle_size = CLAMP (handle_size, min_size, max_size); handle_box.x1 = content_box.x1; handle_box.y1 = bw_box.y2 + position * (avail_size - handle_size); handle_box.x2 = content_box.x2; handle_box.y2 = handle_box.y1 + handle_size; } else { avail_size = content_box.x2 - content_box.x1 - stepper_size; handle_size = increment * avail_size; handle_size = CLAMP (handle_size, min_size, max_size); handle_box.x1 = bw_box.x2 + position * (avail_size - handle_size); handle_box.y1 = content_box.y1; handle_box.x2 = handle_box.x1 + handle_size; handle_box.y2 = content_box.y2; } /* snap to pixel */ handle_box.x1 = (int) handle_box.x1; handle_box.y1 = (int) handle_box.y1; handle_box.x2 = (int) handle_box.x2; handle_box.y2 = (int) handle_box.y2; clutter_actor_allocate (priv->handle, &handle_box, flags); } }
static void clutter_flow_layout_get_preferred_height (ClutterLayoutManager *manager, ClutterContainer *container, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p) { ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->priv; gint n_columns, line_item_count, line_count; gfloat total_min_height, total_natural_height; gfloat line_min_height, line_natural_height; gfloat max_min_height, max_natural_height; ClutterActor *actor, *child; ClutterActorIter iter; gfloat item_x; n_columns = get_columns (CLUTTER_FLOW_LAYOUT (manager), for_width); total_min_height = 0; total_natural_height = 0; line_min_height = 0; line_natural_height = 0; line_item_count = 0; line_count = 0; item_x = 0; actor = CLUTTER_ACTOR (container); /* clear the line height arrays */ if (priv->line_min != NULL) g_array_free (priv->line_min, TRUE); if (priv->line_natural != NULL) g_array_free (priv->line_natural, TRUE); priv->line_min = g_array_sized_new (FALSE, FALSE, sizeof (gfloat), 16); priv->line_natural = g_array_sized_new (FALSE, FALSE, sizeof (gfloat), 16); if (clutter_actor_get_n_children (actor) != 0) line_count = 1; max_min_height = max_natural_height = 0; clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_next (&iter, &child)) { gfloat child_min, child_natural; gfloat new_x, item_width; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; if (priv->orientation == CLUTTER_FLOW_HORIZONTAL && for_width > 0) { if (line_item_count == n_columns) { total_min_height += line_min_height; total_natural_height += line_natural_height; g_array_append_val (priv->line_min, line_min_height); g_array_append_val (priv->line_natural, line_natural_height); line_min_height = line_natural_height = 0; line_item_count = 0; line_count += 1; item_x = 0; } new_x = ((line_item_count + 1) * (for_width + priv->col_spacing)) / n_columns; item_width = new_x - item_x - priv->col_spacing; clutter_actor_get_preferred_height (child, item_width, &child_min, &child_natural); line_min_height = MAX (line_min_height, child_min); line_natural_height = MAX (line_natural_height, child_natural); item_x = new_x; line_item_count += 1; max_min_height = MAX (max_min_height, line_min_height); max_natural_height = MAX (max_natural_height, line_natural_height); } else { clutter_actor_get_preferred_height (child, for_width, &child_min, &child_natural); max_min_height = MAX (max_min_height, child_min); max_natural_height = MAX (max_natural_height, child_natural); total_min_height += max_min_height; total_natural_height += max_natural_height; line_count += 1; } } priv->row_height = max_natural_height; if (priv->max_row_height > 0 && priv->row_height > priv->max_row_height) priv->row_height = MAX (priv->max_row_height, max_min_height); if (priv->row_height < priv->min_row_height) priv->row_height = priv->min_row_height; if (priv->orientation == CLUTTER_FLOW_HORIZONTAL && for_width > 0) { /* if we have a non-full row we need to add it */ if (line_item_count > 0) { total_min_height += line_min_height; total_natural_height += line_natural_height; g_array_append_val (priv->line_min, line_min_height); g_array_append_val (priv->line_natural, line_natural_height); } priv->line_count = line_count; if (priv->line_count > 0) { gfloat total_spacing; total_spacing = priv->row_spacing * (priv->line_count - 1); total_min_height += total_spacing; total_natural_height += total_spacing; } } else { g_array_append_val (priv->line_min, line_min_height); g_array_append_val (priv->line_natural, line_natural_height); priv->line_count = line_count; if (priv->line_count > 0) { gfloat total_spacing; total_spacing = priv->col_spacing * priv->line_count; total_min_height += total_spacing; total_natural_height += total_spacing; } } CLUTTER_NOTE (LAYOUT, "Flow[h]: %d lines (%d per line): w [ %.2f, %.2f ] for h %.2f", n_columns, priv->line_count, total_min_height, total_natural_height, for_width); priv->req_width = for_width; if (min_height_p) *min_height_p = max_min_height; if (nat_height_p) *nat_height_p = total_natural_height; }
int main(int argc, char *argv[]) { ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff }; ClutterColor actor_color = { 0xff, 0xff, 0xcc, 0xff }; clutter_init (&argc, &argv); /* Get the stage and set its size and color: */ ClutterActor *stage = clutter_stage_get_default (); clutter_actor_set_size (stage, 800, 200); clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); /* Add a non-editable text actor to the stage: */ ClutterActor *text = clutter_text_new (); /* Setup text properties */ clutter_text_set_color (CLUTTER_TEXT (text), &actor_color); clutter_text_set_text (CLUTTER_TEXT (text), "Non-editable text: Wizard imps and sweat sock pimps, interstellar mongrel nymphs."); clutter_text_set_font_name (CLUTTER_TEXT (text), "Sans 12"); clutter_text_set_editable (CLUTTER_TEXT (text), FALSE); clutter_text_set_line_wrap (CLUTTER_TEXT (text), FALSE); /* Discover the preferred height and use that height: */ float min_height = 0; float natural_height = 0; clutter_actor_get_preferred_height (text, 750, &min_height, &natural_height); clutter_actor_set_size (text, 750, natural_height); clutter_actor_set_position (text, 5, 5); clutter_container_add_actor (CLUTTER_CONTAINER (stage), text); clutter_actor_show (text); /* Add a multi-line editable text actor to the stage: */ text = clutter_text_new (); /* Setup text properties */ clutter_text_set_color (CLUTTER_TEXT (text), &actor_color); clutter_text_set_text (CLUTTER_TEXT (text), "Editable text: And as I sat there brooding on the old, unknown world, I thought of " "Gatsby's wonder when he first picked out the green light at the end of " "Daisy's dock. He had come a long way to this blue lawn and his dream " "must have seemed so close that he could hardly fail to grasp it. He did " "not know that it was already behind him, somewhere back in that vast " "obscurity beyond the city, where the dark fields of the republic rolled " "on under the night."); clutter_text_set_font_name (CLUTTER_TEXT (text), "Sans 12"); clutter_text_set_editable (CLUTTER_TEXT (text), TRUE); clutter_text_set_line_wrap (CLUTTER_TEXT (text), TRUE); /* Discover the preferred height and use that height: */ min_height = 0; natural_height = 0; clutter_actor_get_preferred_height (text, 750, &min_height, &natural_height); clutter_actor_set_size (text, 750, natural_height); clutter_actor_set_position (text, 5, 50); clutter_container_add_actor (CLUTTER_CONTAINER (stage), text); clutter_actor_show (text); /* Set focus to handle key presses on the stage: */ clutter_stage_set_key_focus (CLUTTER_STAGE (stage), text); /* Show the stage: */ clutter_actor_show (stage); /* Start the main loop, so we can respond to events: */ clutter_main (); return EXIT_SUCCESS; }
static void st_scroll_view_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { ClutterActorBox content_box, child_box; ClutterActorClass *parent_parent_class; gfloat avail_width, avail_height, sb_width, sb_height; gboolean hscrollbar_visible, vscrollbar_visible; StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); /* Chain up to the parent's parent class * * We do this because we do not want StBin to allocate the child, as we * give it a different allocation later, depending on whether the scrollbars * are visible */ parent_parent_class = g_type_class_peek_parent (st_scroll_view_parent_class); CLUTTER_ACTOR_CLASS (parent_parent_class)-> allocate (actor, box, flags); st_theme_node_get_content_box (theme_node, box, &content_box); avail_width = content_box.x2 - content_box.x1; avail_height = content_box.y2 - content_box.y1; if (clutter_actor_get_request_mode (actor) == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH) { sb_width = get_scrollbar_width (ST_SCROLL_VIEW (actor), -1); sb_height = get_scrollbar_height (ST_SCROLL_VIEW (actor), sb_width); } else { sb_height = get_scrollbar_height (ST_SCROLL_VIEW (actor), -1); sb_width = get_scrollbar_width (ST_SCROLL_VIEW (actor), sb_height); } /* Determine what scrollbars are visible. The basic idea of the * handling of an automatic scrollbars is that we start off with the * assumption that we don't need any scrollbars, see if that works, * and if not add horizontal and vertical scrollbars until we are no * longer overflowing. */ if (priv->child) { gfloat child_min_width; gfloat child_min_height; clutter_actor_get_preferred_width (priv->child, -1, &child_min_width, NULL); if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC) { if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC) { /* Pass one, try without a vertical scrollbar */ clutter_actor_get_preferred_height (priv->child, avail_width, &child_min_height, NULL); vscrollbar_visible = child_min_height > avail_height; hscrollbar_visible = child_min_width > avail_width - (vscrollbar_visible ? sb_width : 0); vscrollbar_visible = child_min_height > avail_height - (hscrollbar_visible ? sb_height : 0); /* Pass two - if we needed a vertical scrollbar, get a new preferred height */ if (vscrollbar_visible) { clutter_actor_get_preferred_height (priv->child, MAX (avail_width - sb_width, 0), &child_min_height, NULL); hscrollbar_visible = child_min_width > avail_width - sb_width; } } else { hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER; /* try without a vertical scrollbar */ clutter_actor_get_preferred_height (priv->child, avail_width, &child_min_height, NULL); vscrollbar_visible = child_min_height > avail_height - (hscrollbar_visible ? sb_height : 0); } } else { vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER; if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC) hscrollbar_visible = child_min_width > avail_height - (vscrollbar_visible ? 0 : sb_width); else hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER; } } else { hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER; vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER; } /* Whether or not we show the scrollbars, if the scrollbars are visible * actors, we need to give them some allocation, so we unconditionally * give them the "right" allocation; that might overlap the child when * the scrollbars are not visible, but it doesn't matter because we * don't include them in pick or paint. */ /* Vertical scrollbar */ if (CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll)) { if (st_widget_get_direction (ST_WIDGET (actor)) == ST_TEXT_DIRECTION_RTL) { child_box.x1 = content_box.x1; child_box.x2 = content_box.x1 + sb_width; } else { child_box.x1 = content_box.x2 - sb_width; child_box.x2 = content_box.x2; } child_box.y1 = content_box.y1; child_box.y2 = content_box.y2 - (hscrollbar_visible ? sb_height : 0); clutter_actor_allocate (priv->vscroll, &child_box, flags); } /* Horizontal scrollbar */ if (CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll)) { if (st_widget_get_direction (ST_WIDGET (actor)) == ST_TEXT_DIRECTION_RTL) { child_box.x1 = content_box.x1 + (vscrollbar_visible ? sb_width : 0); child_box.x2 = content_box.x2; } else { child_box.x1 = content_box.x1; child_box.x2 = content_box.x2 - (vscrollbar_visible ? sb_width : 0); } child_box.y1 = content_box.y2 - sb_height; child_box.y2 = content_box.y2; clutter_actor_allocate (priv->hscroll, &child_box, flags); } /* Now fold visibility into the scrollbar sizes to simplify the rest * of the computations. */ if (!hscrollbar_visible) sb_height = 0; if (!vscrollbar_visible) sb_width = 0; /* Child */ if (st_widget_get_direction (ST_WIDGET (actor)) == ST_TEXT_DIRECTION_RTL) { child_box.x1 = content_box.x1 + sb_width; child_box.x2 = content_box.x2; } else { child_box.x1 = content_box.x1; child_box.x2 = content_box.x2 - sb_width; } child_box.y1 = content_box.y1; child_box.y2 = content_box.y2 - sb_height; if (priv->child) clutter_actor_allocate (priv->child, &child_box, flags); if (priv->hscrollbar_visible != hscrollbar_visible) { g_object_freeze_notify (G_OBJECT (actor)); priv->hscrollbar_visible = hscrollbar_visible; g_object_notify (G_OBJECT (actor), "hscrollbar-visible"); g_object_thaw_notify (G_OBJECT (actor)); } if (priv->vscrollbar_visible != vscrollbar_visible) { g_object_freeze_notify (G_OBJECT (actor)); priv->vscrollbar_visible = vscrollbar_visible; g_object_notify (G_OBJECT (actor), "vscrollbar-visible"); g_object_thaw_notify (G_OBJECT (actor)); } }
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 mx_menu_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { gint i; MxPadding padding; ClutterActorBox child_box; MxMenuPrivate *priv = MX_MENU (actor)->priv; gfloat available_h = box->y2-box->y1; /* * first of all, we have to check if the allocated height is our * natural height... */ gfloat menu_nat_h; mx_menu_get_preferred_height (actor, box->x2-box->x1, NULL, &menu_nat_h); if (available_h < menu_nat_h) { /* * ...if not, we have to draw 2 buttons in order to let the * user be able to navigate in the whole menu. */ priv->scrolling_mode = TRUE; } else { priv->scrolling_mode = FALSE; } /* Allocate children */ mx_widget_get_padding (MX_WIDGET (actor), &padding); child_box.x1 = padding.left; child_box.y1 = padding.top; child_box.x2 = box->x2 - box->x1 - padding.right; gfloat down_but_height; clutter_actor_get_preferred_height (priv->down_button, child_box.x2 - child_box.x1, NULL, &down_but_height); if (priv->scrolling_mode) { clutter_actor_get_preferred_height (priv->up_button, child_box.x2 - child_box.x1, NULL, &child_box.y2); child_box.y2 += child_box.y1; clutter_actor_allocate (priv->up_button, &child_box, flags); child_box.y1 = child_box.y2 + 1; available_h -= down_but_height; } for (i = priv->id_offset; i < priv->children->len; i++) { gfloat natural_height; MxMenuChild *child = &g_array_index (priv->children, MxMenuChild, i); clutter_actor_get_preferred_height (CLUTTER_ACTOR (child->box), child_box.x2 - child_box.x1, NULL, &natural_height); child_box.y2 = child_box.y1 + natural_height; if (child_box.y2 >= available_h) { priv->last_shown_id = i-1; break; } clutter_actor_allocate (CLUTTER_ACTOR (child->box), &child_box, flags); child_box.y1 = child_box.y2 + 1; } if (priv->children->len == i) { priv->last_shown_id = i-1; } if (priv->scrolling_mode) { child_box.y2 = child_box.y1 + down_but_height; clutter_actor_allocate (priv->down_button, &child_box, flags); } /* Chain up and allocate background */ CLUTTER_ACTOR_CLASS (mx_menu_parent_class)->allocate (actor, box, flags); }
static void mx_tooltip_update_position (MxTooltip *tooltip) { MxTooltipPrivate *priv = tooltip->priv; ClutterGeometry tip_area = *tooltip->priv->tip_area; gfloat tooltip_w, tooltip_h, tooltip_x, tooltip_y, abs_x, abs_y; ClutterActor *stage, *parent; gfloat stage_w, stage_h, parent_w, parent_h; MxWindow *window; /* If there's no stage, bail out - there's nothing we can do */ stage = clutter_actor_get_stage ((ClutterActor *) tooltip); if (!stage) return; /* find out the stage's size to keep the tooltip on-screen */ clutter_actor_get_size (stage, &stage_w, &stage_h); parent = clutter_actor_get_parent ((ClutterActor *) tooltip); clutter_actor_get_transformed_position (parent, &abs_x, &abs_y); clutter_actor_get_size (parent, &parent_w, &parent_h); /* ensure the tooltip with is not fixed size */ clutter_actor_set_size ((ClutterActor*) tooltip, -1, -1); /* if no area set, just position ourselves top left */ if (!priv->tip_area) { clutter_actor_set_position ((ClutterActor*) tooltip, abs_x, abs_y); return; } /* check if we're in a window and if there's rotation */ window = mx_window_get_for_stage (CLUTTER_STAGE (stage)); if (window) { MxWindowRotation rotation; gfloat old_x; g_object_get (G_OBJECT (window), "window-rotation", &rotation, NULL); if (rotation == MX_WINDOW_ROTATION_90 || rotation == MX_WINDOW_ROTATION_270) { /* swap stage width and height */ old_x = stage_w; stage_w = stage_h; stage_h = old_x; /* swap tip area width and height */ old_x = tip_area.width; tip_area.width = tip_area.height; tip_area.height = old_x; } switch (rotation) { case MX_WINDOW_ROTATION_90: /* absolute position */ old_x = abs_x; abs_x = abs_y; abs_y = stage_h - old_x; /* tip area */ old_x = tip_area.x; tip_area.x = tip_area.y; tip_area.y = stage_h - old_x - tip_area.height; break; case MX_WINDOW_ROTATION_180: tip_area.x = stage_w - tip_area.x - tip_area.width; tip_area.y = stage_h - tip_area.y - tip_area.height; abs_x = stage_w - abs_x; abs_y = stage_h - abs_y; break; case MX_WINDOW_ROTATION_270: /* absolute position */ old_x = abs_x; abs_x = stage_w - abs_y; abs_y = old_x; /* tip area */ old_x = tip_area.x; tip_area.x = stage_w - tip_area.y - tip_area.width; tip_area.y = old_x; break; default: break; } } /* we need to have a style in case there are padding values to take into * account when calculating width/height */ mx_stylable_style_changed (MX_STYLABLE (tooltip), MX_STYLE_CHANGED_FORCE); /* find out the tooltip's size */ clutter_actor_get_size ((ClutterActor*) tooltip, &tooltip_w, &tooltip_h); /* attempt to place the tooltip */ /* This special-cases the 4 window rotations, as doing this with * arbitrary rotations would massively complicate the code for * little benefit. */ priv->actor_below = FALSE; tooltip_x = (int)(tip_area.x + (tip_area.width / 2) - (tooltip_w / 2)); tooltip_y = (int)(tip_area.y + tip_area.height); /* Keep on the screen vertically */ if (tooltip_y + tooltip_h > stage_h) { priv->actor_below = TRUE; /* re-query size as may have changed */ clutter_actor_get_preferred_height ((ClutterActor*) tooltip, -1, NULL, &tooltip_h); tooltip_y = MAX (0, tip_area.y - tooltip_h); } /* Keep on the screen horizontally */ if (tooltip_w > stage_w) { tooltip_x = 0; clutter_actor_set_width ((ClutterActor*) tooltip, stage_w); } else if (tooltip_x < 0) tooltip_x = 0; else if (tooltip_x + tooltip_w > stage_w) tooltip_x = (int)(stage_w) - tooltip_w; gfloat pos_x, pos_y; pos_x = tooltip_x - abs_x; pos_y = tooltip_y - abs_y; /* calculate the arrow offset */ priv->arrow_offset = tip_area.x + tip_area.width / 2 - tooltip_x; clutter_actor_set_position ((ClutterActor*) tooltip, pos_x, pos_y); }
static void mx_table_calculate_row_heights (MxTable *table, gint for_height) { MxTablePrivate *priv = MX_TABLE (table)->priv; gint i; DimensionData *rows, *columns; MxPadding padding; ClutterActorIter iter; ClutterActor *child; mx_widget_get_padding (MX_WIDGET (table), &padding); /* take padding off available height */ for_height -= (int)(padding.top + padding.bottom); g_array_set_size (priv->rows, 0); g_array_set_size (priv->rows, priv->n_rows); rows = &g_array_index (priv->rows, DimensionData, 0); columns = &g_array_index (priv->columns, DimensionData, 0); /* Reset the visible rows */ priv->visible_rows = 0; for (i = 0; i < priv->n_rows; i++) rows[i].is_visible = FALSE; /* STAGE ONE: calculate row heights for non-spanned children */ clutter_actor_iter_init (&iter, CLUTTER_ACTOR (table)); while (clutter_actor_iter_next (&iter, &child)) { MxTableChild *meta; DimensionData *row; gfloat c_min, c_pref; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; meta = (MxTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (table), child); if (meta->row_span > 1) continue; row = &rows[meta->row]; /* If this child is visible, then its row is visible */ if (!row->is_visible) { row->is_visible = TRUE; priv->visible_rows++; } 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->final_size = row->pref_size = MAX (row->pref_size, c_pref); row->expand = MAX (row->expand, meta->y_expand); } /* STAGE TWO: take spanning children into account */ clutter_actor_iter_init (&iter, CLUTTER_ACTOR (table)); while (clutter_actor_iter_next (&iter, &child)) { MxTableChild *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 = (MxTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (table), 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 this actor is visible, then all the rows is spans are visible */ if (!rows[i].is_visible) { rows[i].is_visible = TRUE; priv->visible_rows++; } rows[i].expand = MAX (rows[i].expand, meta->y_expand); } 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 that 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; 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 < 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 < priv->n_rows; i++) { rows[i].final_size = rows[i].min_size; } return; } if (for_height == pref_height) { /* perfect! */ for (i = 0; i < 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 < 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 % priv->n_rows; for (i = 0; i < priv->n_rows; i++) { if (rows[i].expand) { if (n_expand) { rows[i].final_size = rows[i].pref_size + (extra_height / n_expand); } else { rows[i].final_size = rows[i].pref_size + (extra_height / priv->n_rows); } } 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 mex_column_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p) { GList *c; gfloat min_height, nat_height; MxPadding padding; MexColumn *self = MEX_COLUMN (actor); MexColumnPrivate *priv = self->priv; mx_widget_get_padding (MX_WIDGET (actor), &padding); if (for_width >= 0) for_width = MAX (0, for_width - padding.left - padding.right); clutter_actor_get_preferred_height (priv->header, for_width, NULL, &min_height); nat_height = min_height; if (priv->n_items < 1) { gfloat min_placeholder, nat_placeholder; clutter_actor_get_preferred_height (priv->placeholder_actor, for_width, &min_placeholder, &nat_placeholder); min_height += min_placeholder; nat_height += nat_placeholder; } else { gfloat child_min_height, child_nat_height; for (c = priv->children; c; c = c->next) { ClutterActor *child = c->data; clutter_actor_get_preferred_height (child, for_width, &child_min_height, &child_nat_height); min_height += child_min_height; nat_height += child_nat_height; if (priv->adjustment) break; } } if (min_height_p) *min_height_p = min_height + padding.top + padding.bottom; if (nat_height_p) *nat_height_p += nat_height + padding.top + padding.bottom; }
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_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); }
static void st_scroll_view_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); gboolean account_for_hscrollbar = FALSE; gfloat min_height = 0, natural_height; gfloat child_min_height, child_natural_height; gfloat child_min_width; gfloat sb_width; if (!priv->child) return; st_theme_node_adjust_for_width (theme_node, &for_width); clutter_actor_get_preferred_width (priv->child, -1, &child_min_width, NULL); if (min_height_p) *min_height_p = 0; sb_width = get_scrollbar_width (ST_SCROLL_VIEW (actor), -1); switch (priv->vscrollbar_policy) { case GTK_POLICY_NEVER: break; case GTK_POLICY_ALWAYS: case GTK_POLICY_AUTOMATIC: /* We've requested space for the scrollbar, subtract it back out */ for_width -= sb_width; break; } switch (priv->hscrollbar_policy) { case GTK_POLICY_NEVER: account_for_hscrollbar = FALSE; break; case GTK_POLICY_ALWAYS: account_for_hscrollbar = TRUE; break; case GTK_POLICY_AUTOMATIC: account_for_hscrollbar = for_width < child_min_width; break; } clutter_actor_get_preferred_height (priv->child, for_width, &child_min_height, &child_natural_height); natural_height = child_natural_height; switch (priv->vscrollbar_policy) { case GTK_POLICY_NEVER: min_height = child_min_height; break; case GTK_POLICY_ALWAYS: case GTK_POLICY_AUTOMATIC: /* Should theoretically use the min height of the vscrollbar, * but that's not cleanly defined at the moment */ min_height = 0; break; } if (account_for_hscrollbar) { float sb_height = get_scrollbar_height (ST_SCROLL_VIEW (actor), for_width); min_height += sb_height; natural_height += sb_height; } if (min_height_p) *min_height_p = min_height; if (natural_height_p) *natural_height_p = natural_height; st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p); }
static void penge_calendar_pane_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { PengeCalendarPanePrivate *priv = GET_PRIVATE (actor); gfloat events_nat_h, tasks_nat_h, events_header_nat_h, tasks_header_nat_h; gfloat width, height, remaining_height, last_y; gfloat tasks_available_h, events_available_h; ClutterActorBox child_box; MxPadding padding = { 0, }; if (CLUTTER_ACTOR_CLASS (penge_calendar_pane_parent_class)->allocate) CLUTTER_ACTOR_CLASS (penge_calendar_pane_parent_class)->allocate (actor, box, flags); mx_widget_get_padding (MX_WIDGET (actor), &padding); width = box->x2 - box->x1; height = box->y2 - box->y1; clutter_actor_get_preferred_height (CLUTTER_ACTOR (priv->events_header_table), width, NULL, &events_header_nat_h); clutter_actor_get_preferred_height (CLUTTER_ACTOR (priv->events_pane), width, NULL, &events_nat_h); clutter_actor_get_preferred_height (CLUTTER_ACTOR (priv->tasks_header_table), width, NULL, &tasks_header_nat_h); clutter_actor_get_preferred_height (CLUTTER_ACTOR (priv->tasks_pane), width, NULL, &tasks_nat_h); child_box.x1 = padding.left; child_box.x2 = width - padding.right; child_box.y1 = padding.top; child_box.y2 = child_box.y1 + events_header_nat_h; last_y = child_box.y2; clutter_actor_allocate (CLUTTER_ACTOR (priv->events_header_table), &child_box, flags); remaining_height = height - padding.top - padding.bottom - events_header_nat_h - tasks_header_nat_h; if (tasks_nat_h <= (remaining_height / 2.0)) { tasks_available_h = tasks_nat_h; } else { tasks_available_h = (remaining_height / 2.0); } events_available_h = remaining_height - tasks_available_h; if (events_available_h > events_nat_h) { tasks_available_h += (tasks_available_h - events_nat_h); events_available_h = events_nat_h; } child_box.x1 = padding.left; child_box.x2 = width - padding.right; child_box.y1 = last_y; child_box.y2 = child_box.y1 + (int)events_available_h; last_y = child_box.y2; clutter_actor_allocate (CLUTTER_ACTOR (priv->events_pane), &child_box, flags); child_box.x1 = padding.left; child_box.x2 = width - padding.right; child_box.y1 = last_y; child_box.y2 = child_box.y1 + tasks_header_nat_h; last_y = child_box.y2; clutter_actor_allocate (CLUTTER_ACTOR (priv->tasks_header_table), &child_box, flags); child_box.x1 = padding.left; child_box.x2 = width - padding.right; child_box.y1 = last_y; child_box.y2 = child_box.y1 + (int)tasks_available_h; clutter_actor_allocate (CLUTTER_ACTOR (priv->tasks_pane), &child_box, flags); }
static void st_entry_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { StEntryPrivate *priv = ST_ENTRY_PRIV (actor); StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); ClutterActorClass *parent_class; ClutterActorBox content_box, child_box, icon_box; gfloat icon_w, icon_h; gfloat entry_h, min_h, pref_h, avail_h; parent_class = CLUTTER_ACTOR_CLASS (st_entry_parent_class); parent_class->allocate (actor, box, flags); st_theme_node_get_content_box (theme_node, box, &content_box); avail_h = content_box.y2 - content_box.y1; child_box.x1 = content_box.x1; child_box.x2 = content_box.x2; if (priv->primary_icon) { clutter_actor_get_preferred_width (priv->primary_icon, -1, NULL, &icon_w); clutter_actor_get_preferred_height (priv->primary_icon, -1, NULL, &icon_h); icon_box.x1 = content_box.x1; icon_box.x2 = icon_box.x1 + icon_w; icon_box.y1 = (int) (content_box.y1 + avail_h / 2 - icon_h / 2); icon_box.y2 = icon_box.y1 + icon_h; clutter_actor_allocate (priv->primary_icon, &icon_box, flags); /* reduce the size for the entry */ child_box.x1 += icon_w + priv->spacing; } if (priv->secondary_icon) { clutter_actor_get_preferred_width (priv->secondary_icon, -1, NULL, &icon_w); clutter_actor_get_preferred_height (priv->secondary_icon, -1, NULL, &icon_h); icon_box.x2 = content_box.x2; icon_box.x1 = icon_box.x2 - icon_w; icon_box.y1 = (int) (content_box.y1 + avail_h / 2 - icon_h / 2); icon_box.y2 = icon_box.y1 + icon_h; clutter_actor_allocate (priv->secondary_icon, &icon_box, flags); /* reduce the size for the entry */ child_box.x2 -= icon_w - priv->spacing; } clutter_actor_get_preferred_height (priv->entry, child_box.x2 - child_box.x1, &min_h, &pref_h); entry_h = CLAMP (pref_h, min_h, avail_h); child_box.y1 = (int) (content_box.y1 + avail_h / 2 - entry_h / 2); child_box.y2 = child_box.y1 + entry_h; clutter_actor_allocate (priv->entry, &child_box, flags); }
/* Allocate position and size of actor and its children */ static void _xfdashboard_viewpad_allocate(ClutterActor *self, const ClutterActorBox *inBox, ClutterAllocationFlags inFlags) { XfdashboardViewpadPrivate *priv=XFDASHBOARD_VIEWPAD(self)->priv; ClutterActorClass *actorClass=CLUTTER_ACTOR_CLASS(xfdashboard_viewpad_parent_class); gfloat viewWidth, viewHeight; gfloat vScrollbarWidth, vScrollbarHeight; gfloat hScrollbarWidth, hScrollbarHeight; gboolean hScrollbarVisible, vScrollbarVisible; ClutterActorBox *box; gfloat x, y, w, h; /* Chain up to store the allocation of the actor */ if(actorClass->allocate) actorClass->allocate(self, inBox, inFlags); /* Initialize largest possible allocation for view and determine * real size of view to show. The real size is used to determine * scroll bar visibility if policy is automatic */ viewWidth=clutter_actor_box_get_width(inBox); viewHeight=clutter_actor_box_get_height(inBox); /* Determine visibility of scroll bars */ hScrollbarVisible=FALSE; if(priv->hScrollbarPolicy==XFDASHBOARD_POLICY_ALWAYS || (priv->hScrollbarPolicy==XFDASHBOARD_POLICY_AUTOMATIC && xfdashboard_scrollbar_get_range(XFDASHBOARD_SCROLLBAR(priv->hScrollbar))>viewWidth)) { hScrollbarVisible=TRUE; } if(xfdashboard_view_get_fit_mode(XFDASHBOARD_VIEW(priv->activeView))==XFDASHBOARD_FIT_MODE_HORIZONTAL || xfdashboard_view_get_fit_mode(XFDASHBOARD_VIEW(priv->activeView))==XFDASHBOARD_FIT_MODE_BOTH) { hScrollbarVisible=FALSE; } vScrollbarVisible=FALSE; if(priv->vScrollbarPolicy==XFDASHBOARD_POLICY_ALWAYS || (priv->vScrollbarPolicy==XFDASHBOARD_POLICY_AUTOMATIC && xfdashboard_scrollbar_get_range(XFDASHBOARD_SCROLLBAR(priv->vScrollbar))>viewHeight)) { vScrollbarVisible=TRUE; } if(xfdashboard_view_get_fit_mode(XFDASHBOARD_VIEW(priv->activeView))==XFDASHBOARD_FIT_MODE_VERTICAL || xfdashboard_view_get_fit_mode(XFDASHBOARD_VIEW(priv->activeView))==XFDASHBOARD_FIT_MODE_BOTH) { vScrollbarVisible=FALSE; } /* Set allocation for visible scroll bars */ vScrollbarWidth=0.0f; vScrollbarHeight=viewHeight; clutter_actor_get_preferred_width(priv->vScrollbar, -1, NULL, &vScrollbarWidth); hScrollbarWidth=viewWidth; hScrollbarHeight=0.0f; clutter_actor_get_preferred_height(priv->hScrollbar, -1, NULL, &hScrollbarHeight); if(hScrollbarVisible && vScrollbarVisible) { vScrollbarHeight-=hScrollbarHeight; hScrollbarWidth-=vScrollbarWidth; } if(vScrollbarVisible==FALSE) box=clutter_actor_box_new(0, 0, 0, 0); else box=clutter_actor_box_new(viewWidth-vScrollbarWidth, 0, viewWidth, vScrollbarHeight); clutter_actor_allocate(priv->vScrollbar, box, inFlags); clutter_actor_box_free(box); if(hScrollbarVisible==FALSE) box=clutter_actor_box_new(0, 0, 0, 0); else box=clutter_actor_box_new(0, viewHeight-hScrollbarHeight, hScrollbarWidth, viewHeight); clutter_actor_allocate(priv->hScrollbar, box, inFlags); clutter_actor_box_free(box); /* Reduce allocation for view by any visible scroll bar * and set allocation and clipping of view */ if(priv->activeView) { /* Set allocation */ if(vScrollbarVisible) viewWidth-=vScrollbarWidth; if(hScrollbarVisible) viewHeight-=hScrollbarHeight; x=y=0.0f; if(clutter_actor_has_clip(CLUTTER_ACTOR(priv->activeView))) { clutter_actor_get_clip(CLUTTER_ACTOR(priv->activeView), &x, &y, NULL, NULL); } switch(xfdashboard_view_get_fit_mode(XFDASHBOARD_VIEW(priv->activeView))) { case XFDASHBOARD_FIT_MODE_BOTH: w=viewWidth; h=viewHeight; break; case XFDASHBOARD_FIT_MODE_HORIZONTAL: w=viewWidth; clutter_actor_get_preferred_height(CLUTTER_ACTOR(priv->activeView), w, NULL, &h); break; case XFDASHBOARD_FIT_MODE_VERTICAL: h=viewHeight; clutter_actor_get_preferred_width(CLUTTER_ACTOR(priv->activeView), h, NULL, &w); break; default: clutter_actor_get_preferred_size(CLUTTER_ACTOR(priv->activeView), NULL, NULL, &w, &h); break; } box=clutter_actor_box_new(0, 0, w, h); clutter_actor_allocate(CLUTTER_ACTOR(priv->activeView), box, inFlags); clutter_actor_box_free(box); clutter_actor_set_clip(CLUTTER_ACTOR(priv->activeView), x, y, viewWidth, viewHeight); } /* Only set value if it changes */ if(priv->hScrollbarVisible!=hScrollbarVisible) { /* Set new value */ priv->hScrollbarVisible=hScrollbarVisible; /* Notify about property change */ g_object_notify_by_pspec(G_OBJECT(self), XfdashboardViewpadProperties[PROP_HSCROLLBAR_VISIBLE]); } if(priv->vScrollbarVisible!=vScrollbarVisible) { /* Set new value */ priv->vScrollbarVisible=vScrollbarVisible; /* Notify about property change */ g_object_notify_by_pspec(G_OBJECT(self), XfdashboardViewpadProperties[PROP_VSCROLLBAR_VISIBLE]); } }
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 clutter_flow_layout_allocate (ClutterLayoutManager *manager, ClutterContainer *container, const ClutterActorBox *allocation, ClutterAllocationFlags flags) { ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->priv; GList *l, *children = clutter_container_get_children (container); gfloat avail_width, avail_height; gfloat item_x, item_y; gint line_item_count; gint items_per_line; gint line_index; if (children == NULL) return; clutter_actor_box_get_size (allocation, &avail_width, &avail_height); items_per_line = compute_lines (CLUTTER_FLOW_LAYOUT (manager), avail_width, avail_height); item_x = item_y = 0; line_item_count = 0; line_index = 0; for (l = children; l != NULL; l = l->next) { ClutterActor *child = l->data; ClutterActorBox child_alloc; gfloat item_width, item_height; gfloat new_x, new_y; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; new_x = new_y = 0; if (priv->orientation == CLUTTER_FLOW_HORIZONTAL) { if (line_item_count == items_per_line && line_item_count > 0) { 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 = 0; } new_x = ((line_item_count + 1) * (avail_width + priv->col_spacing)) / items_per_line; item_width = new_x - item_x - priv->col_spacing; item_height = g_array_index (priv->line_natural, gfloat, line_index); if (!priv->is_homogeneous) { gfloat child_min, child_natural; clutter_actor_get_preferred_width (child, item_height, &child_min, &child_natural); item_width = MIN (item_width, child_natural); clutter_actor_get_preferred_height (child, item_width, &child_min, &child_natural); item_height = MIN (item_height, child_natural); } } else { if (line_item_count == items_per_line && line_item_count > 0) { 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 = 0; } new_y = ((line_item_count + 1) * (avail_height + priv->row_spacing)) / items_per_line; item_height = new_y - item_y - priv->row_spacing; item_width = g_array_index (priv->line_natural, gfloat, line_index); if (!priv->is_homogeneous) { gfloat child_min, child_natural; clutter_actor_get_preferred_width (child, item_height, &child_min, &child_natural); item_width = MIN (item_width, child_natural); 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; } g_list_free (children); }
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; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; new_x = new_y = 0; if (priv->orientation == CLUTTER_FLOW_HORIZONTAL) { if (line_item_count == items_per_line && line_item_count > 0) { 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; } 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; item_height = g_array_index (priv->line_natural, gfloat, line_index); if (!priv->is_homogeneous) { gfloat child_min, child_natural; clutter_actor_get_preferred_width (child, item_height, &child_min, &child_natural); item_width = MIN (item_width, child_natural); clutter_actor_get_preferred_height (child, item_width, &child_min, &child_natural); item_height = MIN (item_height, child_natural); } } else { if (line_item_count == items_per_line && line_item_count > 0) { 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; } 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; item_width = g_array_index (priv->line_natural, gfloat, line_index); if (!priv->is_homogeneous) { gfloat child_min, child_natural; clutter_actor_get_preferred_width (child, item_height, &child_min, &child_natural); item_width = MIN (item_width, child_natural); 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_flow_layout_get_preferred_width (ClutterLayoutManager *manager, ClutterContainer *container, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p) { ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->priv; gint n_rows, line_item_count, line_count; gfloat total_min_width, total_natural_width; gfloat line_min_width, line_natural_width; gfloat max_min_width, max_natural_width; ClutterActor *actor, *child; ClutterActorIter iter; gfloat item_y; n_rows = get_rows (CLUTTER_FLOW_LAYOUT (manager), for_height); total_min_width = 0; total_natural_width = 0; line_min_width = 0; line_natural_width = 0; line_item_count = 0; line_count = 0; item_y = 0; actor = CLUTTER_ACTOR (container); /* clear the line width arrays */ if (priv->line_min != NULL) g_array_free (priv->line_min, TRUE); if (priv->line_natural != NULL) g_array_free (priv->line_natural, TRUE); priv->line_min = g_array_sized_new (FALSE, FALSE, sizeof (gfloat), 16); priv->line_natural = g_array_sized_new (FALSE, FALSE, sizeof (gfloat), 16); if (clutter_actor_get_n_children (actor) != 0) line_count = 1; max_min_width = max_natural_width = 0; clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_next (&iter, &child)) { gfloat child_min, child_natural; gfloat new_y, item_height; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; if (priv->orientation == CLUTTER_FLOW_VERTICAL && for_height > 0) { clutter_actor_get_preferred_height (child, -1, &child_min, &child_natural); if ((priv->snap_to_grid && line_item_count == n_rows) || (!priv->snap_to_grid && item_y + child_natural > for_height)) { total_min_width += line_min_width; total_natural_width += line_natural_width; g_array_append_val (priv->line_min, line_min_width); g_array_append_val (priv->line_natural, line_natural_width); line_min_width = line_natural_width = 0; line_item_count = 0; line_count += 1; item_y = 0; } if (priv->snap_to_grid) { new_y = ((line_item_count + 1) * (for_height + priv->row_spacing)) / n_rows; item_height = new_y - item_y - priv->row_spacing; } else { new_y = item_y + child_natural + priv->row_spacing; item_height = child_natural; } clutter_actor_get_preferred_width (child, item_height, &child_min, &child_natural); line_min_width = MAX (line_min_width, child_min); line_natural_width = MAX (line_natural_width, child_natural); item_y = new_y; line_item_count += 1; max_min_width = MAX (max_min_width, line_min_width); max_natural_width = MAX (max_natural_width, line_natural_width); } else { clutter_actor_get_preferred_width (child, for_height, &child_min, &child_natural); max_min_width = MAX (max_min_width, child_min); max_natural_width = MAX (max_natural_width, child_natural); total_min_width += max_min_width; total_natural_width += max_natural_width; line_count += 1; } } priv->col_width = max_natural_width; if (priv->max_col_width > 0 && priv->col_width > priv->max_col_width) priv->col_width = MAX (priv->max_col_width, max_min_width); if (priv->col_width < priv->min_col_width) priv->col_width = priv->min_col_width; if (priv->orientation == CLUTTER_FLOW_VERTICAL && for_height > 0) { /* if we have a non-full row we need to add it */ if (line_item_count > 0) { total_min_width += line_min_width; total_natural_width += line_natural_width; g_array_append_val (priv->line_min, line_min_width); g_array_append_val (priv->line_natural, line_natural_width); } priv->line_count = line_count; if (priv->line_count > 0) { gfloat total_spacing; total_spacing = priv->col_spacing * (priv->line_count - 1); total_min_width += total_spacing; total_natural_width += total_spacing; } } else { g_array_append_val (priv->line_min, line_min_width); g_array_append_val (priv->line_natural, line_natural_width); priv->line_count = line_count; if (priv->line_count > 0) { gfloat total_spacing; total_spacing = priv->col_spacing * (priv->line_count - 1); total_min_width += total_spacing; total_natural_width += total_spacing; } } CLUTTER_NOTE (LAYOUT, "Flow[w]: %d lines (%d per line): w [ %.2f, %.2f ] for h %.2f", n_rows, priv->line_count, total_min_width, total_natural_width, for_height); priv->req_height = for_height; if (min_width_p) *min_width_p = max_min_width; if (nat_width_p) *nat_width_p = total_natural_width; }