static void update_row_col (ClutterTableLayout *layout, ClutterContainer *container) { ClutterTableLayoutPrivate *priv = layout->priv; ClutterLayoutManager *manager = CLUTTER_LAYOUT_MANAGER (layout); ClutterActor *actor, *child; gint n_cols, n_rows; n_cols = n_rows = 0; if (container == NULL) goto out; actor = CLUTTER_ACTOR (container); for (child = clutter_actor_get_first_child (actor); child != NULL; child = clutter_actor_get_next_sibling (child)) { ClutterTableChild *meta; meta = CLUTTER_TABLE_CHILD (clutter_layout_manager_get_child_meta (manager, container, child)); n_cols = MAX (n_cols, meta->col + meta->col_span); n_rows = MAX (n_rows, meta->row + meta->row_span); } out: priv->n_cols = n_cols; priv->n_rows = n_rows; }
static gboolean move_actors (ClutterActor *actor, ClutterEvent *event, gpointer user_data) { State *state = user_data; ClutterActor *child; /* do nothing if the animator is already running */ if (clutter_timeline_is_playing (clutter_animator_get_timeline (state->animator))) return TRUE; /* remove all keys from the animator */ clutter_animator_remove_key (state->animator, NULL, NULL, -1); /* add keys for all actors in the group */ for (child = clutter_actor_get_first_child (state->group); child != NULL; child = clutter_actor_get_next_sibling (child)) { add_keys_for_actor (child, state->animator); } /* start the animation */ clutter_animator_start (state->animator); return TRUE; }
static gboolean shell_generic_container_get_paint_volume (ClutterActor *self, ClutterPaintVolume *volume) { ClutterActorBox paint_box, alloc_box; StThemeNode *theme_node; ClutterVertex origin; /* Setting the paint volume does not make sense when we don't have any allocation */ if (!clutter_actor_has_allocation (self)) return FALSE; theme_node = st_widget_get_theme_node (ST_WIDGET (self)); clutter_actor_get_allocation_box (self, &alloc_box); st_theme_node_get_paint_box (theme_node, &alloc_box, &paint_box); origin.x = paint_box.x1 - alloc_box.x1; origin.y = paint_box.y1 - alloc_box.y1; origin.z = 0.0f; clutter_paint_volume_set_origin (volume, &origin); clutter_paint_volume_set_width (volume, paint_box.x2 - paint_box.x1); clutter_paint_volume_set_height (volume, paint_box.y2 - paint_box.y1); if (!clutter_actor_get_clip_to_allocation (self)) { ClutterActor *child; /* Based on ClutterGroup/ClutterBox; include the children's * paint volumes, since they may paint outside our allocation. */ for (child = clutter_actor_get_first_child (self); child != NULL; child = clutter_actor_get_next_sibling (child)) { const ClutterPaintVolume *child_volume; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; if (shell_generic_container_get_skip_paint (SHELL_GENERIC_CONTAINER (self), child)) continue; child_volume = clutter_actor_get_transformed_paint_volume (child, self); if (!child_volume) return FALSE; clutter_paint_volume_union (volume, child_volume); } } return TRUE; }
static void st_box_layout_paint (ClutterActor *actor) { StBoxLayout *self = ST_BOX_LAYOUT (actor); StBoxLayoutPrivate *priv = self->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); gdouble x, y; ClutterActorBox allocation_box; ClutterActorBox content_box; ClutterActor *child; get_border_paint_offsets (self, &x, &y); if (x != 0 || y != 0) { cogl_push_matrix (); cogl_translate ((int)x, (int)y, 0); } st_widget_paint_background (ST_WIDGET (actor)); if (x != 0 || y != 0) { cogl_pop_matrix (); } if (clutter_actor_get_n_children (actor) == 0) return; clutter_actor_get_allocation_box (actor, &allocation_box); st_theme_node_get_content_box (theme_node, &allocation_box, &content_box); content_box.x1 += x; content_box.y1 += y; content_box.x2 += x; content_box.y2 += y; /* The content area forms the viewport into the scrolled contents, while * the borders and background stay in place; after drawing the borders and * background, we clip to the content area */ if (priv->hadjustment || priv->vadjustment) cogl_clip_push_rectangle ((int)content_box.x1, (int)content_box.y1, (int)content_box.x2, (int)content_box.y2); for (child = clutter_actor_get_first_child (actor); child != NULL; child = clutter_actor_get_next_sibling (child)) clutter_actor_paint (child); if (priv->hadjustment || priv->vadjustment) cogl_clip_pop (); }
static void st_box_layout_pick (ClutterActor *actor, const ClutterColor *color) { StBoxLayout *self = ST_BOX_LAYOUT (actor); StBoxLayoutPrivate *priv = self->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); gdouble x, y; ClutterActorBox allocation_box; ClutterActorBox content_box; ClutterActor *child; get_border_paint_offsets (self, &x, &y); if (x != 0 || y != 0) { cogl_push_matrix (); cogl_translate ((int)x, (int)y, 0); } CLUTTER_ACTOR_CLASS (st_box_layout_parent_class)->pick (actor, color); if (x != 0 || y != 0) { cogl_pop_matrix (); } if (clutter_actor_get_n_children (actor) == 0) return; clutter_actor_get_allocation_box (actor, &allocation_box); st_theme_node_get_content_box (theme_node, &allocation_box, &content_box); content_box.x1 += x; content_box.y1 += y; content_box.x2 += x; content_box.y2 += y; if (priv->hadjustment || priv->vadjustment) cogl_clip_push_rectangle ((int)content_box.x1, (int)content_box.y1, (int)content_box.x2, (int)content_box.y2); for (child = clutter_actor_get_first_child (actor); child != NULL; child = clutter_actor_get_next_sibling (child)) clutter_actor_paint (child); if (priv->hadjustment || priv->vadjustment) cogl_clip_pop (); }
static void clutter_fixed_layout_allocate (ClutterLayoutManager *manager, ClutterContainer *container, const ClutterActorBox *allocation, ClutterAllocationFlags flags) { ClutterActor *child; for (child = clutter_actor_get_first_child (CLUTTER_ACTOR (container)); child != NULL; child = clutter_actor_get_next_sibling (child)) { clutter_actor_allocate_preferred_size (child, flags); } }
static void shell_stack_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); gboolean first = TRUE; float min = 0, natural = 0; ClutterActor *child; st_theme_node_adjust_for_height (theme_node, &for_height); for (child = clutter_actor_get_first_child (actor); child != NULL; child = clutter_actor_get_next_sibling (child)) { float child_min, child_natural; clutter_actor_get_preferred_width (child, for_height, &child_min, &child_natural); if (first) { first = FALSE; min = child_min; natural = child_natural; } else { if (child_min > min) min = child_min; if (child_natural > natural) natural = child_natural; } } if (min_width_p) *min_width_p = min; if (natural_width_p) *natural_width_p = natural; st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p); }
static void cinnamon_generic_container_paint (ClutterActor *actor) { CinnamonGenericContainer *self = (CinnamonGenericContainer*) actor; ClutterActor *child; st_widget_paint_background (ST_WIDGET (actor)); for (child = clutter_actor_get_first_child (actor); child != NULL; child = clutter_actor_get_next_sibling (child)) { if (g_hash_table_lookup (self->priv->skip_paint, child)) continue; clutter_actor_paint (child); } }
static GList * cinnamon_generic_container_get_focus_chain (StWidget *widget) { CinnamonGenericContainer *self = CINNAMON_GENERIC_CONTAINER (widget); ClutterActor *child; GList *focus_chain; focus_chain = NULL; for (child = clutter_actor_get_first_child (CLUTTER_ACTOR (self)); child != NULL; child = clutter_actor_get_next_sibling (child)) { if (clutter_actor_is_visible (child) && !cinnamon_generic_container_get_skip_paint (self, child)) focus_chain = g_list_prepend (focus_chain, child); } return g_list_reverse (focus_chain); }
static void cinnamon_generic_container_pick (ClutterActor *actor, const ClutterColor *color) { CinnamonGenericContainer *self = (CinnamonGenericContainer*) actor; ClutterActor *child; CLUTTER_ACTOR_CLASS (cinnamon_generic_container_parent_class)->pick (actor, color); for (child = clutter_actor_get_first_child (actor); child != NULL; child = clutter_actor_get_next_sibling (child)) { if (g_hash_table_lookup (self->priv->skip_paint, child)) continue; clutter_actor_paint (child); } }
static GList * shell_generic_container_get_focus_chain (StWidget *widget) { ShellGenericContainer *self = SHELL_GENERIC_CONTAINER (widget); ClutterActor *child; GList *focus_chain; focus_chain = NULL; for (child = clutter_actor_get_first_child (CLUTTER_ACTOR (self)); child != NULL; child = clutter_actor_get_next_sibling (child)) { if (CLUTTER_ACTOR_IS_VISIBLE (child) && !shell_generic_container_get_skip_paint (self, child)) focus_chain = g_list_prepend (focus_chain, child); } return g_list_reverse (focus_chain); }
static void clutter_fixed_layout_get_preferred_height (ClutterLayoutManager *manager, ClutterContainer *container, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p) { ClutterActor *actor, *child; gdouble min_bottom; gdouble natural_bottom; min_bottom = 0; natural_bottom = 0; actor = CLUTTER_ACTOR (container); for (child = clutter_actor_get_first_child (actor); child != NULL; child = clutter_actor_get_next_sibling (child)) { gfloat child_y, child_min, child_natural; child_y = clutter_actor_get_y (child); clutter_actor_get_preferred_size (child, NULL, &child_min, NULL, &child_natural); if (child_y + child_min > min_bottom) min_bottom = child_y + child_min; if (child_y + child_natural > natural_bottom) natural_bottom = child_y + child_natural; } if (min_height_p) *min_height_p = min_bottom; if (nat_height_p) *nat_height_p = natural_bottom; }
static void clutter_fixed_layout_get_preferred_width (ClutterLayoutManager *manager, ClutterContainer *container, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p) { ClutterActor *actor, *child; gdouble min_right; gdouble natural_right; min_right = 0; natural_right = 0; actor = CLUTTER_ACTOR (container); for (child = clutter_actor_get_first_child (actor); child != NULL; child = clutter_actor_get_next_sibling (child)) { gfloat child_x, child_min, child_natural; child_x = clutter_actor_get_x (child); clutter_actor_get_preferred_size (child, &child_min, NULL, &child_natural, NULL); if (child_x + child_min > min_right) min_right = child_x + child_min; if (child_x + child_natural > natural_right) natural_right = child_x + child_natural; } if (min_width_p) *min_width_p = min_right; if (nat_width_p) *nat_width_p = natural_right; }
static void shell_stack_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags) { StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); ClutterActorBox content_box; ClutterActor *child; clutter_actor_set_allocation (self, box, flags); st_theme_node_get_content_box (theme_node, box, &content_box); for (child = clutter_actor_get_first_child (self); child != NULL; child = clutter_actor_get_next_sibling (child)) { ClutterActorBox child_box = content_box; clutter_actor_allocate (child, &child_box, flags); } }
/* * ClutterContainer Implementation */ static void st_table_actor_removed (ClutterContainer *container, ClutterActor *actor) { StTablePrivate *priv = ST_TABLE (container)->priv; gint n_rows = 0; gint n_cols = 0; ClutterActor *child; /* Calculate and update the number of rows / columns */ for (child = clutter_actor_get_first_child (CLUTTER_ACTOR (container)); child != NULL; child = clutter_actor_get_next_sibling (child)) { StTableChild *meta; if (child == actor) continue; meta = (StTableChild *) clutter_container_get_child_meta (container, child); n_rows = MAX (n_rows, meta->row + 1); n_cols = MAX (n_cols, meta->col + 1); } g_object_freeze_notify (G_OBJECT (container)); if (priv->n_rows != n_rows) { priv->n_rows = n_rows; g_object_notify (G_OBJECT (container), "row-count"); } if (priv->n_cols != n_cols) { priv->n_cols = n_cols; g_object_notify (G_OBJECT (container), "column-count"); } g_object_thaw_notify (G_OBJECT (container)); }
/* Find requested selection target depending of current selection */ static ClutterActor* _xfdashboard_workspace_selector_focusable_find_selection(XfdashboardFocusable *inFocusable, ClutterActor *inSelection, XfdashboardSelectionTarget inDirection) { XfdashboardWorkspaceSelector *self; XfdashboardWorkspaceSelectorPrivate *priv; XfdashboardLiveWorkspace *selection; ClutterActor *newSelection; g_return_val_if_fail(XFDASHBOARD_IS_FOCUSABLE(inFocusable), NULL); g_return_val_if_fail(XFDASHBOARD_IS_WORKSPACE_SELECTOR(inFocusable), NULL); g_return_val_if_fail(!inSelection || XFDASHBOARD_IS_LIVE_WORKSPACE(inSelection), NULL); self=XFDASHBOARD_WORKSPACE_SELECTOR(inFocusable); priv=self->priv; newSelection=NULL; selection=NULL; /* Find actor for current active workspace which is also the current selection */ if(priv->activeWorkspace) { selection=_xfdashboard_workspace_selector_find_actor_for_workspace(self, priv->activeWorkspace); } if(!selection) return(NULL); /* If there is nothing selected return currently determined actor which is * the current active workspace. */ if(!inSelection) { g_debug("No selection at %s, so select first child %s for direction %u", G_OBJECT_TYPE_NAME(self), selection ? G_OBJECT_TYPE_NAME(selection) : "<nil>", inDirection); return(CLUTTER_ACTOR(selection)); } /* Check that selection is a child of this actor otherwise return NULL */ if(!xfdashboard_actor_contains_child_deep(CLUTTER_ACTOR(self), inSelection)) { ClutterActor *parent; parent=clutter_actor_get_parent(inSelection); g_warning(_("Cannot lookup selection target at %s because %s is a child of %s"), G_OBJECT_TYPE_NAME(self), G_OBJECT_TYPE_NAME(inSelection), parent ? G_OBJECT_TYPE_NAME(parent) : "<nil>"); return(NULL); } /* Find target selection */ switch(inDirection) { case XFDASHBOARD_SELECTION_TARGET_LEFT: if(priv->orientation==CLUTTER_ORIENTATION_HORIZONTAL) { newSelection=clutter_actor_get_previous_sibling(CLUTTER_ACTOR(selection)); } break; case XFDASHBOARD_SELECTION_TARGET_UP: if(priv->orientation==CLUTTER_ORIENTATION_VERTICAL) { newSelection=clutter_actor_get_previous_sibling(CLUTTER_ACTOR(selection)); } break; case XFDASHBOARD_SELECTION_TARGET_RIGHT: if(priv->orientation==CLUTTER_ORIENTATION_HORIZONTAL) { newSelection=clutter_actor_get_next_sibling(CLUTTER_ACTOR(selection)); } break; case XFDASHBOARD_SELECTION_TARGET_DOWN: if(priv->orientation==CLUTTER_ORIENTATION_VERTICAL) { newSelection=clutter_actor_get_next_sibling(CLUTTER_ACTOR(selection)); } break; case XFDASHBOARD_SELECTION_TARGET_FIRST: case XFDASHBOARD_SELECTION_TARGET_PAGE_UP: case XFDASHBOARD_SELECTION_TARGET_PAGE_LEFT: if(inDirection==XFDASHBOARD_SELECTION_TARGET_FIRST || (inDirection==XFDASHBOARD_SELECTION_TARGET_PAGE_UP && priv->orientation==CLUTTER_ORIENTATION_VERTICAL) || (inDirection==XFDASHBOARD_SELECTION_TARGET_PAGE_LEFT && priv->orientation==CLUTTER_ORIENTATION_HORIZONTAL)) { newSelection=clutter_actor_get_first_child(CLUTTER_ACTOR(self)); } break; case XFDASHBOARD_SELECTION_TARGET_LAST: case XFDASHBOARD_SELECTION_TARGET_PAGE_DOWN: case XFDASHBOARD_SELECTION_TARGET_PAGE_RIGHT: if(inDirection==XFDASHBOARD_SELECTION_TARGET_LAST || (inDirection==XFDASHBOARD_SELECTION_TARGET_PAGE_DOWN && priv->orientation==CLUTTER_ORIENTATION_VERTICAL) || (inDirection==XFDASHBOARD_SELECTION_TARGET_PAGE_RIGHT && priv->orientation==CLUTTER_ORIENTATION_HORIZONTAL)) { newSelection=clutter_actor_get_last_child(CLUTTER_ACTOR(self)); } break; case XFDASHBOARD_SELECTION_TARGET_NEXT: newSelection=clutter_actor_get_next_sibling(CLUTTER_ACTOR(selection)); if(!newSelection) newSelection=clutter_actor_get_previous_sibling(CLUTTER_ACTOR(selection)); break; default: { gchar *valueName; valueName=xfdashboard_get_enum_value_name(XFDASHBOARD_TYPE_SELECTION_TARGET, inDirection); g_critical(_("Focusable object %s does not handle selection direction of type %s."), G_OBJECT_TYPE_NAME(self), valueName); g_free(valueName); } break; } /* If new selection could be found override current selection with it */ if(newSelection && XFDASHBOARD_IS_LIVE_WORKSPACE(newSelection)) { selection=XFDASHBOARD_LIVE_WORKSPACE(newSelection); } /* Return new selection found */ g_debug("Selecting %s at %s for current selection %s in direction %u", selection ? G_OBJECT_TYPE_NAME(selection) : "<nil>", G_OBJECT_TYPE_NAME(self), inSelection ? G_OBJECT_TYPE_NAME(inSelection) : "<nil>", inDirection); return(CLUTTER_ACTOR(selection)); }
static void get_content_preferred_height (StBoxLayout *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { StBoxLayoutPrivate *priv = self->priv; gint n_children = 0; gint n_fixed = 0; gfloat min_height, natural_height; ClutterActor *child; min_height = 0; natural_height = 0; for (child = clutter_actor_get_first_child (CLUTTER_ACTOR (self)); child != NULL; child = clutter_actor_get_next_sibling (child)) { gfloat child_min = 0, child_nat = 0; gboolean child_fill = FALSE; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; n_children++; if (clutter_actor_get_fixed_position_set (child)) { n_fixed++; continue; } if (priv->is_vertical) { clutter_container_child_get ((ClutterContainer*) self, child, "x-fill", &child_fill, NULL); } _st_actor_get_preferred_height (child, (priv->is_vertical) ? for_width : -1, child_fill, &child_min, &child_nat); if (!priv->is_vertical) { min_height = MAX (child_min, min_height); natural_height = MAX (child_nat, natural_height); } else { min_height += child_min; natural_height += child_nat; } } if (priv->is_vertical && (n_children - n_fixed) > 1) { min_height += priv->spacing * (n_children - n_fixed - 1); natural_height += priv->spacing * (n_children - n_fixed - 1); } if (min_height_p) *min_height_p = min_height; if (natural_height_p) *natural_height_p = natural_height; }
static void st_table_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { gint *min_widths, *pref_widths; gfloat total_min_width, total_pref_width; StTablePrivate *priv = ST_TABLE (self)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); gint i; ClutterActor *child; if (priv->n_cols < 1) { *min_width_p = 0; *natural_width_p = 0; return; } /* Setting size to zero and then what we want it to be causes a clear if * clear flag is set (which it should be.) */ g_array_set_size (priv->min_widths, 0); g_array_set_size (priv->pref_widths, 0); g_array_set_size (priv->min_widths, priv->n_cols); g_array_set_size (priv->pref_widths, priv->n_cols); min_widths = (gint *) priv->min_widths->data; pref_widths = (gint *) priv->pref_widths->data; /* calculate minimum row widths */ for (child = clutter_actor_get_first_child (self); child != NULL; child = clutter_actor_get_next_sibling (child)) { gint col, col_span; gfloat w_min, w_pref; StTableChild *meta; meta = (StTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (self), child); if (!meta->allocate_hidden && !CLUTTER_ACTOR_IS_VISIBLE (child)) continue; /* get child properties */ col = meta->col; col_span = meta->col_span; _st_actor_get_preferred_width (child, -1, meta->y_fill, &w_min, &w_pref); if (col_span == 1 && w_min > min_widths[col]) min_widths[col] = w_min; if (col_span == 1 && w_pref > pref_widths[col]) pref_widths[col] = w_pref; } total_min_width = (priv->n_cols - 1) * (float) priv->col_spacing; total_pref_width = total_min_width; for (i = 0; i < priv->n_cols; i++) { total_min_width += min_widths[i]; total_pref_width += pref_widths[i]; } /* If we were requested width-for-height, then we reported minimum/natural * heights based on our natural width. If we were allocated less than our * natural width, then we need more height. So in the width-for-height * case we need to disable shrinking. */ if (for_height >= 0) total_min_width = total_pref_width; if (min_width_p) *min_width_p = total_min_width; if (natural_width_p) *natural_width_p = total_pref_width; st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p); }
static void st_table_preferred_allocate (ClutterActor *self, const ClutterActorBox *content_box, gboolean flags) { gint row_spacing, col_spacing; gint i; gint *col_widths, *row_heights; StTable *table; StTablePrivate *priv; gboolean ltr; ClutterActor *child; table = ST_TABLE (self); priv = ST_TABLE (self)->priv; col_spacing = (priv->col_spacing); row_spacing = (priv->row_spacing); col_widths = st_table_calculate_col_widths (table, (int) (content_box->x2 - content_box->x1)); row_heights = st_table_calculate_row_heights (table, (int) (content_box->y2 - content_box->y1), col_widths); ltr = (clutter_actor_get_text_direction (self) == CLUTTER_TEXT_DIRECTION_LTR); for (child = clutter_actor_get_first_child (self); child != NULL; child = clutter_actor_get_next_sibling (child)) { gint row, col, row_span, col_span; gint col_width, row_height; StTableChild *meta; ClutterActorBox childbox; gint child_x, child_y; gdouble x_align_f, y_align_f; meta = (StTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (self), child); if (!meta->allocate_hidden && !CLUTTER_ACTOR_IS_VISIBLE (child)) continue; /* get child properties */ col = meta->col; row = meta->row; row_span = meta->row_span; col_span = meta->col_span; _st_get_align_factors (meta->x_align, meta->y_align, &x_align_f, &y_align_f); /* initialise the width and height */ col_width = col_widths[col]; row_height = row_heights[row]; /* Add the widths of the spanned columns: * * First check that we have a non-zero span. Then we loop over each of * the columns that we're spanning but we stop short if we go past the * number of columns in the table. This is necessary to avoid accessing * uninitialised memory. We add the spacing in here too since we only * want to add as much spacing as times we successfully span. */ if (col + col_span > priv->n_cols) g_warning ("StTable: col-span exceeds number of columns"); #if 0 if (row + row_span > priv->n_rows) g_warning ("StTable: row-span exceeds number of rows"); #endif if (col_span > 1) { for (i = col + 1; i < col + col_span && i < priv->n_cols; i++) { col_width += col_widths[i]; col_width += col_spacing; } } /* add the height of the spanned rows */ if (row_span > 1) { for (i = row + 1; i < row + row_span && i < priv->n_rows; i++) { row_height += row_heights[i]; row_height += row_spacing; } } /* calculate child x */ if (ltr) { child_x = (int) content_box->x1 + col_spacing * col; for (i = 0; i < col; i++) child_x += col_widths[i]; } else { child_x = (int) content_box->x2 - col_spacing * col; for (i = 0; i < col; i++) child_x -= col_widths[i]; } /* calculate child y */ child_y = (int) content_box->y1 + row_spacing * row; for (i = 0; i < row; i++) child_y += row_heights[i]; /* set up childbox */ if (ltr) { childbox.x1 = (float) child_x; childbox.x2 = (float) MAX (0, child_x + col_width); } else { childbox.x2 = (float) child_x; childbox.x1 = (float) MAX (0, child_x - col_width); } childbox.y1 = (float) child_y; childbox.y2 = (float) MAX (0, child_y + row_height); clutter_actor_allocate_align_fill (child, &childbox, x_align_f, y_align_f, meta->x_fill, meta->y_fill, flags); } }
static void clutter_table_layout_allocate (ClutterLayoutManager *layout, ClutterContainer *container, const ClutterActorBox *box, ClutterAllocationFlags flags) { ClutterTableLayout *self = CLUTTER_TABLE_LAYOUT (layout); ClutterTableLayoutPrivate *priv = self->priv; ClutterActor *actor, *child; gint row_spacing, col_spacing; gint i; DimensionData *rows, *columns; update_row_col (self, container); if (priv->n_cols < 1 || priv->n_rows < 1) return; actor = CLUTTER_ACTOR (container); if (clutter_actor_get_n_children (actor) == 0) return; col_spacing = (priv->col_spacing); row_spacing = (priv->row_spacing); calculate_table_dimensions (self, container, box->x2 - box->x1, box->y2 - box->y1); rows = (DimensionData *) (void *) priv->rows->data; columns = (DimensionData *) (void *) priv->columns->data; for (child = clutter_actor_get_first_child (actor); child != NULL; child = clutter_actor_get_next_sibling (child)) { gint row, col, row_span, col_span; gint col_width, row_height; ClutterTableChild *meta; ClutterActorBox childbox; gint child_x, child_y; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; meta = CLUTTER_TABLE_CHILD (clutter_layout_manager_get_child_meta (layout, container, child)); /* get child properties */ col = meta->col; row = meta->row; row_span = meta->row_span; col_span = meta->col_span; /* initialise the width and height */ col_width = columns[col].final_size; row_height = rows[row].final_size; /* Add the widths of the spanned columns: * * First check that we have a non-zero span. Then we loop over each of * the columns that we're spanning but we stop short if we go past the * number of columns in the table. This is necessary to avoid accessing * uninitialised memory. We add the spacing in here too since we only * want to add as much spacing as times we successfully span. */ if (col + col_span > priv->n_cols) g_warning (G_STRLOC ": column-span exceeds number of columns"); if (row + row_span > priv->n_rows) g_warning (G_STRLOC ": row-span exceeds number of rows"); if (col_span > 1) { for (i = col + 1; i < col + col_span && i < priv->n_cols; i++) { col_width += columns[i].final_size; col_width += col_spacing; } } /* add the height of the spanned rows */ if (row_span > 1) { for (i = row + 1; i < row + row_span && i < priv->n_rows; i++) { row_height += rows[i].final_size; row_height += row_spacing; } } /* calculate child x */ child_x = clutter_actor_box_get_x (box); for (i = 0; i < col; i++) { if (columns[i].visible) { child_x += columns[i].final_size; child_x += col_spacing; } } /* calculate child y */ child_y = clutter_actor_box_get_y (box); for (i = 0; i < row; i++) { if (rows[i].visible) { child_y += rows[i].final_size; child_y += row_spacing; } } /* set up childbox */ childbox.x1 = (float) child_x; childbox.x2 = (float) MAX (0, child_x + col_width); childbox.y1 = (float) child_y; childbox.y2 = (float) MAX (0, child_y + row_height); clutter_actor_allocate (child, &childbox, flags); } }
static gint * st_table_calculate_col_widths (StTable *table, gint for_width) { gint total_min_width, i; StTablePrivate *priv = table->priv; gboolean *is_expand_col; gint extra_col_width, n_expanded_cols = 0, expanded_cols = 0; gint *pref_widths, *min_widths; ClutterActor *child; g_array_set_size (priv->is_expand_col, 0); g_array_set_size (priv->is_expand_col, priv->n_cols); is_expand_col = (gboolean *) priv->is_expand_col->data; g_array_set_size (priv->pref_widths, 0); g_array_set_size (priv->pref_widths, priv->n_cols); pref_widths = (gint *) priv->pref_widths->data; g_array_set_size (priv->min_widths, 0); g_array_set_size (priv->min_widths, priv->n_cols); min_widths = (gint *) priv->min_widths->data; for (child = clutter_actor_get_first_child (CLUTTER_ACTOR (table)); child != NULL; child = clutter_actor_get_next_sibling (child)) { gint col; gfloat w_min, w_pref; gboolean x_expand; StTableChild *meta; gint col_span; meta = (StTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (table), child); if (!meta->allocate_hidden && !CLUTTER_ACTOR_IS_VISIBLE (child)) continue; /* get child properties */ col = meta->col; x_expand = meta->x_expand; col_span = meta->col_span; if (x_expand) is_expand_col[col] = TRUE; _st_actor_get_preferred_width (child, -1, meta->y_fill, &w_min, &w_pref); if (col_span == 1 && w_pref > pref_widths[col]) { pref_widths[col] = w_pref; } if (col_span == 1 && w_min > min_widths[col]) { min_widths[col] = w_min; } } total_min_width = priv->col_spacing * (priv->n_cols - 1); for (i = 0; i < priv->n_cols; i++) total_min_width += pref_widths[i]; /* calculate the remaining space and distribute it evenly onto all rows/cols * with the x/y expand property set. */ for (i = 0; i < priv->n_cols; i++) if (is_expand_col[i]) { expanded_cols += pref_widths[i]; n_expanded_cols++; } /* for_width - total_min_width */ extra_col_width = for_width - total_min_width; if (extra_col_width) for (i = 0; i < priv->n_cols; i++) if (is_expand_col[i]) { if (extra_col_width < 0) { pref_widths[i] = MAX (min_widths[i], pref_widths[i] + (extra_col_width * (pref_widths[i] / (float) expanded_cols))); /* if we reached the minimum width for this column, we need to * stop counting it as expanded */ if (pref_widths[i] == min_widths[i]) { /* restart calculations :-( */ expanded_cols -= pref_widths[i]; is_expand_col[i] = 0; n_expanded_cols--; i = -1; } } else pref_widths[i] += extra_col_width / n_expanded_cols; } return pref_widths; }
static void st_table_homogeneous_allocate (ClutterActor *self, const ClutterActorBox *content_box, gboolean flags) { gfloat col_width, row_height; gint row_spacing, col_spacing; StTablePrivate *priv = ST_TABLE (self)->priv; gboolean ltr = clutter_actor_get_text_direction (self) == CLUTTER_TEXT_DIRECTION_LTR; ClutterActor *child; col_spacing = priv->col_spacing; row_spacing = priv->row_spacing; col_width = (int) ((content_box->x2 - content_box->x1 - (col_spacing * (priv->n_cols - 1))) / priv->n_cols + 0.5); row_height = (int) ((content_box->y2 - content_box->y1 - (row_spacing * (priv->n_rows - 1))) / priv->n_rows + 0.5); for (child = clutter_actor_get_first_child (self); child != NULL; child = clutter_actor_get_next_sibling (child)) { gint row, col, row_span, col_span; StTableChild *meta; ClutterActorBox childbox; gdouble x_align_f, y_align_f; meta = (StTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (self), child); if (!meta->allocate_hidden && !CLUTTER_ACTOR_IS_VISIBLE (child)) continue; /* get child properties */ col = meta->col; row = meta->row; row_span = meta->row_span; col_span = meta->col_span; _st_get_align_factors (meta->x_align, meta->y_align, &x_align_f, &y_align_f); if (ltr) { childbox.x1 = content_box->x1 + (col_width + col_spacing) * col; childbox.x2 = childbox.x1 + (col_width * col_span) + (col_spacing * (col_span - 1)); } else { childbox.x2 = content_box->x2 - (col_width + col_spacing) * col; childbox.x1 = childbox.x2 - (col_width * col_span) - (col_spacing * (col_span - 1)); } childbox.y1 = content_box->y1 + (row_height + row_spacing) * row; childbox.y2 = childbox.y1 + (row_height * row_span) + (row_spacing * (row_span - 1)); clutter_actor_allocate_align_fill (child, &childbox, x_align_f, y_align_f, meta->x_fill, meta->y_fill, flags); } }
static BoxChildShrink * compute_shrinks (StBoxLayout *self, gfloat for_length, gfloat total_shrink) { StBoxLayoutPrivate *priv = self->priv; int n_children = clutter_actor_get_n_children (CLUTTER_ACTOR (self)); BoxChildShrink *shrinks = g_new0 (BoxChildShrink, n_children); gfloat shrink_so_far; gfloat base_shrink = 0; /* the "= 0" is just to make gcc happy */ int n_shrink_children; ClutterActor *child; int i = 0; /* The effect that we want is that all the children get an equal chance * to expand from their minimum size up to the natural size. Or to put * it a different way, we want to start by shrinking only the child that * can shrink most, then shrink that and the next most shrinkable child, * to the point where we are shrinking everything. */ /* Find the amount of possible shrink for each child */ int n_possible_shrink_children = 0; for (child = clutter_actor_get_first_child (CLUTTER_ACTOR (self)); child != NULL; child = clutter_actor_get_next_sibling (child)) { gfloat child_min, child_nat; gboolean child_fill; gboolean fixed; fixed = clutter_actor_get_fixed_position_set (child); shrinks[i].child_index = i; if (CLUTTER_ACTOR_IS_VISIBLE (child) && !fixed) { if (priv->is_vertical) { clutter_container_child_get ((ClutterContainer*) self, child, "x-fill", &child_fill, NULL); _st_actor_get_preferred_height (child, for_length, child_fill, &child_min, &child_nat); } else { clutter_container_child_get ((ClutterContainer*) self, child, "y-fill", &child_fill, NULL); _st_actor_get_preferred_width (child, for_length, child_fill, &child_min, &child_nat); } shrinks[i].shrink_amount = MAX (0., child_nat - child_min); n_possible_shrink_children++; } else { shrinks[i].shrink_amount = -1.; } i++; } /* We want to process children starting from the child with the maximum available * shrink, so sort in this order; !visible children end up at the end */ qsort (shrinks, n_children, sizeof (BoxChildShrink), compare_by_shrink_amount); /* +--+ * | | * | | +-- * | | | | * | | | | +-+ * --+--+-+-+-+-+---------- * | | | | | | +-+ +-+ * | | | | | | | | | | * --+--+-+-+-+-+-+-+------ * * We are trying to find the correct position for the upper line the "water mark" * so that total of the portion of the bars above the line is equal to the total * amount we want to shrink. */ /* Start by moving the line downward, top-of-bar by top-of-bar */ shrink_so_far = 0; for (n_shrink_children = 1; n_shrink_children <= n_possible_shrink_children; n_shrink_children++) { if (n_shrink_children < n_possible_shrink_children) base_shrink = shrinks[n_shrink_children].shrink_amount; else base_shrink = 0; shrink_so_far += n_shrink_children * (shrinks[n_shrink_children - 1].shrink_amount - base_shrink); if (shrink_so_far >= total_shrink || n_shrink_children == n_possible_shrink_children) break; } /* OK, we found enough shrinkage, move it back upwards to the right position */ base_shrink += (shrink_so_far - total_shrink) / n_shrink_children; if (base_shrink < 0) /* can't shrink that much, probably round-off error */ base_shrink = 0; /* Assign the portion above the base shrink line to the shrink_amount */ for (i = 0; i < n_shrink_children; i++) shrinks[i].shrink_amount -= base_shrink; for (; i < n_children; i++) shrinks[i].shrink_amount = 0; /* And sort back to their original order */ qsort (shrinks, n_children, sizeof (BoxChildShrink), compare_by_child_index); return shrinks; }
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 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 st_table_get_preferred_height (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { gint *min_heights, *pref_heights; gfloat total_min_height, total_pref_height; StTablePrivate *priv = ST_TABLE (self)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); gint i; gint *min_widths; ClutterActor *child; /* We only support height-for-width allocation. So if we are called * width-for-height, calculate heights based on our natural width */ if (for_width < 0) { float natural_width; clutter_actor_get_preferred_width (self, -1, NULL, &natural_width); for_width = natural_width; } if (priv->n_rows < 1) { *min_height_p = 0; *natural_height_p = 0; return; } st_theme_node_adjust_for_width (theme_node, &for_width); /* Setting size to zero and then what we want it to be causes a clear if * clear flag is set (which it should be.) */ g_array_set_size (priv->min_heights, 0); g_array_set_size (priv->pref_heights, 0); g_array_set_size (priv->min_heights, priv->n_rows); g_array_set_size (priv->pref_heights, priv->n_rows); /* use min_widths to help allocation of height-for-width widgets */ min_widths = st_table_calculate_col_widths (ST_TABLE (self), for_width); min_heights = (gint *) priv->min_heights->data; pref_heights = (gint *) priv->pref_heights->data; /* calculate minimum row heights */ for (child = clutter_actor_get_first_child (self); child != NULL; child = clutter_actor_get_next_sibling (child)) { gint row, col, col_span, cell_width, row_span; gfloat min, pref; StTableChild *meta; meta = (StTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (self), child); if (!meta->allocate_hidden && !CLUTTER_ACTOR_IS_VISIBLE (child)) continue; /* get child properties */ row = meta->row; col = meta->col; col_span = meta->col_span; row_span = meta->row_span; cell_width = 0; for (i = 0; i < col_span && col + i < priv->n_cols; i++) cell_width += min_widths[col + i]; _st_actor_get_preferred_height (child, (float) cell_width, meta->x_fill, &min, &pref); if (row_span == 1 && min > min_heights[row]) min_heights[row] = min; if (row_span == 1 && pref > pref_heights[row]) pref_heights[row] = pref; } /* start off with row spacing */ total_min_height = (priv->n_rows - 1) * (float) (priv->row_spacing); total_pref_height = total_min_height; for (i = 0; i < priv->n_rows; i++) { total_min_height += min_heights[i]; total_pref_height += pref_heights[i]; } if (min_height_p) *min_height_p = total_min_height; if (natural_height_p) *natural_height_p = total_pref_height; st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p); }
static gint * st_table_calculate_row_heights (StTable *table, gint for_height, gint * col_widths) { StTablePrivate *priv = ST_TABLE (table)->priv; gint *is_expand_row, *min_heights, *pref_heights, *row_heights, extra_row_height; gint i, total_min_height; gint expanded_rows = 0; gint n_expanded_rows = 0; ClutterActor *child; g_array_set_size (priv->row_heights, 0); g_array_set_size (priv->row_heights, priv->n_rows); row_heights = (gboolean *) priv->row_heights->data; g_array_set_size (priv->is_expand_row, 0); g_array_set_size (priv->is_expand_row, priv->n_rows); is_expand_row = (gboolean *) priv->is_expand_row->data; g_array_set_size (priv->min_heights, 0); g_array_set_size (priv->min_heights, priv->n_rows); min_heights = (gboolean *) priv->min_heights->data; g_array_set_size (priv->pref_heights, 0); g_array_set_size (priv->pref_heights, priv->n_rows); pref_heights = (gboolean *) priv->pref_heights->data; for (child = clutter_actor_get_first_child (CLUTTER_ACTOR (table)); child != NULL; child = clutter_actor_get_next_sibling (child)) { gint row, col, cell_width; gfloat h_min, h_pref; gboolean y_expand; StTableChild *meta; gint col_span, row_span; meta = (StTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (table), child); if (!meta->allocate_hidden && !CLUTTER_ACTOR_IS_VISIBLE (child)) continue; /* get child properties */ col = meta->col; row = meta->row; y_expand = meta->y_expand; col_span = meta->col_span; row_span = meta->row_span; if (y_expand) is_expand_row[row] = TRUE; /* calculate the cell width by including any spanned columns */ cell_width = 0; for (i = 0; i < col_span && col + i < priv->n_cols; i++) cell_width += (float)(col_widths[col + i]); if (!meta->x_fill) { gfloat width; _st_actor_get_preferred_width (child, -1, meta->y_fill, NULL, &width); cell_width = MIN (cell_width, width); } _st_actor_get_preferred_height (child, cell_width, meta->x_fill, &h_min, &h_pref); if (row_span == 1 && h_pref > pref_heights[row]) { pref_heights[row] = (int)(h_pref); } if (row_span == 1 && h_min > min_heights[row]) { min_heights[row] = (int)(h_min); } } total_min_height = 0; // priv->row_spacing * (priv->n_rows - 1); for (i = 0; i < priv->n_rows; i++) total_min_height += pref_heights[i]; /* calculate the remaining space and distribute it evenly onto all rows/cols * with the x/y expand property set. */ for (i = 0; i < priv->n_rows; i++) if (is_expand_row[i]) { expanded_rows += pref_heights[i]; n_expanded_rows++; } /* extra row height = for height - row spacings - total_min_height */ for_height -= (priv->row_spacing * (priv->n_rows - 1)); extra_row_height = for_height - total_min_height; if (extra_row_height < 0) { gint *skip = g_slice_alloc0 (sizeof (gint) * priv->n_rows); gint total_shrink_height; /* If we need to shrink rows, we need to do multiple passes. * * We start by assuming all rows can shrink. All rows are sized * proportional to their height in the total table size. If a row would be * sized smaller than its minimum size, we mark it as non-shrinkable, and * reduce extra_row_height by the amount it has been shrunk. The amount * it has been shrunk by is the difference between the preferred and * minimum height, since all rows start at their preferred height. We * also then reduce the total table size (stored in total_shrink_height) by the height * of the row we are going to be skipping. * */ /* We start by assuming all rows can shrink */ total_shrink_height = total_min_height; for (i = 0; i < priv->n_rows; i++) { if (!skip[i]) { gint tmp; /* Calculate the height of the row by starting with the preferred * height and taking away the extra row height proportional to * the preferred row height over the rows that are being shrunk */ tmp = pref_heights[i] + (extra_row_height * (pref_heights[i] / (float) total_shrink_height)); if (tmp < min_heights[i]) { /* This was a row we *were* set to shrink, but we now find it would have * been shrunk too much. We remove it from the list of rows to shrink and * adjust extra_row_height and total_shrink_height appropriately */ skip[i] = TRUE; row_heights[i] = min_heights[i]; /* Reduce extra_row_height by the amount we have reduced this * actor by */ extra_row_height += (pref_heights[i] - min_heights[i]); /* now take off the row from the total shrink height */ total_shrink_height -= pref_heights[i]; /* restart the loop */ i = -1; } else { skip[i] = FALSE; row_heights[i] = tmp; } } } g_slice_free1 (sizeof (gint) * priv->n_rows, skip); } else { for (i = 0; i < priv->n_rows; i++) { if (is_expand_row[i]) row_heights[i] = pref_heights[i] + (extra_row_height / n_expanded_rows); else row_heights[i] = pref_heights[i]; } } return row_heights; }
static void st_box_layout_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (actor)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); ClutterActorBox content_box; gfloat avail_width, avail_height, min_width, natural_width, min_height, natural_height; gfloat position, next_position; gint n_expand_children = 0, i; gfloat expand_amount, shrink_amount; BoxChildShrink *shrinks = NULL; // Home-made logical xor gboolean flip = (!(st_widget_get_direction (ST_WIDGET (actor)) == ST_TEXT_DIRECTION_RTL) != !priv->is_align_end); gboolean reverse_order = (!priv->is_align_end != !priv->is_pack_start); ClutterActor *child; clutter_actor_set_allocation (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; get_content_preferred_width (ST_BOX_LAYOUT (actor), avail_height, &min_width, &natural_width); get_content_preferred_height (ST_BOX_LAYOUT (actor), MAX (avail_width, min_width), &min_height, &natural_height); /* update adjustments for scrolling */ if (priv->vadjustment) { gdouble prev_value; g_object_set (G_OBJECT (priv->vadjustment), "lower", 0.0, "upper", MAX (min_height, avail_height), "page-size", avail_height, "step-increment", avail_height / 6, "page-increment", avail_height - avail_height / 6, NULL); prev_value = st_adjustment_get_value (priv->vadjustment); st_adjustment_set_value (priv->vadjustment, prev_value); } if (priv->hadjustment) { gdouble prev_value; g_object_set (G_OBJECT (priv->hadjustment), "lower", 0.0, "upper", MAX (min_width, avail_width), "page-size", avail_width, "step-increment", avail_width / 6, "page-increment", avail_width - avail_width / 6, NULL); prev_value = st_adjustment_get_value (priv->hadjustment); st_adjustment_set_value (priv->hadjustment, prev_value); } if (avail_height < min_height) { avail_height = min_height; content_box.y2 = content_box.y1 + avail_height; } if (avail_width < min_width) { avail_width = min_width; content_box.x2 = content_box.x1 + avail_width; } if (priv->is_vertical) { expand_amount = MAX (0, avail_height - natural_height); shrink_amount = MAX (0, natural_height - avail_height); } else { expand_amount = MAX (0, avail_width - natural_width); shrink_amount = MAX (0, natural_width - avail_width); } if (expand_amount > 0) { /* count the number of children with expand set to TRUE */ n_expand_children = 0; for (child = clutter_actor_get_first_child (actor); child != NULL; child = clutter_actor_get_next_sibling (child)) { gboolean expand; if (!CLUTTER_ACTOR_IS_VISIBLE (child) || clutter_actor_get_fixed_position_set (child)) continue; clutter_container_child_get ((ClutterContainer *) actor, child, "expand", &expand, NULL); if (expand) n_expand_children++; } if (n_expand_children == 0) expand_amount = 0; } else if (shrink_amount > 0) { shrinks = compute_shrinks (ST_BOX_LAYOUT (actor), priv->is_vertical ? avail_width : avail_height, shrink_amount); } if (priv->is_vertical) { if (flip) { position = content_box.y2; } else { position = content_box.y1; } } else { if (flip) { position = content_box.x2; } else { position = content_box.x1; } } if (reverse_order) { child = clutter_actor_get_last_child (actor); i = clutter_actor_get_n_children (actor) - 1; } else { child = clutter_actor_get_first_child (actor); i = 0; } gfloat init_padding = (avail_width/2) - (natural_width/2); while (child != NULL) { ClutterActorBox child_box; gfloat child_min, child_nat, child_allocated; gboolean xfill, yfill, expand, fixed; StAlign xalign, yalign; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) goto next_child; fixed = clutter_actor_get_fixed_position_set (child); if (fixed) { clutter_actor_allocate_preferred_size (child, flags); goto next_child; } clutter_container_child_get ((ClutterContainer*) actor, child, "x-fill", &xfill, "y-fill", &yfill, "x-align", &xalign, "y-align", &yalign, "expand", &expand, NULL); if (priv->is_vertical) { _st_actor_get_preferred_height (child, avail_width, xfill, &child_min, &child_nat); } else { _st_actor_get_preferred_width (child, avail_height, yfill, &child_min, &child_nat); } child_allocated = child_nat; if (expand_amount > 0 && expand) child_allocated += expand_amount / n_expand_children; else if (shrink_amount > 0) child_allocated -= shrinks[i].shrink_amount; if (flip) { next_position = position - child_allocated; } else { next_position = position + child_allocated; } if (priv->is_vertical) { if (flip) { child_box.y1 = (int)(0.5 + next_position); child_box.y2 = (int)(0.5 + position); } else { child_box.y1 = (int)(0.5 + position); child_box.y2 = (int)(0.5 + next_position); } child_box.x1 = content_box.x1; child_box.x2 = content_box.x2; _st_allocate_fill (ST_WIDGET (actor), child, &child_box, xalign, yalign, xfill, yfill); clutter_actor_allocate (child, &child_box, flags); } else { if (flip) { child_box.x1 = (int)(0.5 + next_position); child_box.x2 = (int)(0.5 + position); } else { child_box.x1 = (int)(0.5 + position); child_box.x2 = (int)(0.5 + next_position); } child_box.y1 = content_box.y1; child_box.y2 = content_box.y2; _st_allocate_fill (ST_WIDGET (actor), child, &child_box, xalign, yalign, xfill, yfill); clutter_actor_allocate (child, &child_box, flags); } if (flip) position = next_position - priv->spacing; else position = next_position + priv->spacing; next_child: if (reverse_order) { child = clutter_actor_get_previous_sibling (child); i--; } else { child = clutter_actor_get_next_sibling (child); i++; } } if (shrinks) g_free (shrinks); }