static void st_table_get_preferred_height (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { 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; DimensionData *rows; /* 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); /* use min_widths to help allocation of height-for-width widgets */ st_table_calculate_dimensions (ST_TABLE (self), for_width, -1); rows = &g_array_index (priv->rows, DimensionData, 0); /* start off with row spacing */ total_min_height = (priv->visible_rows - 1) * (float)(priv->row_spacing); total_pref_height = total_min_height; for (i = 0; i < priv->n_rows; i++) { total_min_height += rows[i].min_size; total_pref_height += rows[i].pref_size; } 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 void st_table_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { 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; DimensionData *columns; if (priv->n_cols < 1) { *min_width_p = 0; *natural_width_p = 0; return; } /* use min_widths to help allocation of height-for-width widgets */ st_table_calculate_dimensions (ST_TABLE (self), -1, for_height); columns = &g_array_index (priv->columns, DimensionData, 0); /* start off with row spacing */ total_min_width = (priv->visible_cols - 1) * (float)(priv->col_spacing); total_pref_width = total_min_width; for (i = 0; i < priv->n_cols; i++) { total_min_width += columns[i].min_size; total_pref_width += columns[i].pref_size; } /* 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_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { StTablePrivate *priv = ST_TABLE (gobject)->priv; switch (prop_id) { case PROP_HOMOGENEOUS: g_value_set_boolean (value, priv->homogeneous); break; case PROP_COL_COUNT: g_value_set_int (value, priv->n_cols); break; case PROP_ROW_COUNT: g_value_set_int (value, priv->n_rows); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } }
/** * st_table_get_column_count: * @table: A #StTable * * Retrieve the current number of columns in @table * * Returns: the number of columns */ gint st_table_get_column_count (StTable *table) { g_return_val_if_fail (ST_IS_TABLE (table), -1); return ST_TABLE (table)->priv->n_cols; }
static void st_table_finalize (GObject *gobject) { StTablePrivate *priv = ST_TABLE (gobject)->priv; g_array_free (priv->columns, TRUE); g_array_free (priv->rows, TRUE); G_OBJECT_CLASS (st_table_parent_class)->finalize (gobject); }
static void st_table_finalize (GObject *gobject) { StTablePrivate *priv = ST_TABLE (gobject)->priv; g_array_free (priv->min_widths, TRUE); g_array_free (priv->pref_widths, TRUE); g_array_free (priv->min_heights, TRUE); g_array_free (priv->pref_heights, TRUE); g_array_free (priv->is_expand_col, TRUE); g_array_free (priv->is_expand_row, TRUE); g_array_free (priv->col_widths, TRUE); g_array_free (priv->row_heights, TRUE); G_OBJECT_CLASS (st_table_parent_class)->finalize (gobject); }
static void st_table_style_changed (StWidget *self) { StTablePrivate *priv = ST_TABLE (self)->priv; StThemeNode *theme_node = st_widget_get_theme_node (self); int old_row_spacing = priv->row_spacing; int old_col_spacing = priv->col_spacing; double row_spacing, col_spacing; row_spacing = st_theme_node_get_length (theme_node, "spacing-rows"); priv->row_spacing = (int)(row_spacing + 0.5); col_spacing = st_theme_node_get_length (theme_node, "spacing-columns"); priv->col_spacing = (int)(col_spacing + 0.5); if (priv->row_spacing != old_row_spacing || priv->col_spacing != old_col_spacing) clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); ST_WIDGET_CLASS (st_table_parent_class)->style_changed (self); }
static void st_table_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags) { StTablePrivate *priv = ST_TABLE (self)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); ClutterActorBox content_box; clutter_actor_set_allocation (self, box, flags); if (priv->n_cols < 1 || priv->n_rows < 1) return; st_theme_node_get_content_box (theme_node, box, &content_box); if (priv->homogeneous) st_table_homogeneous_allocate (self, &content_box, flags); else st_table_preferred_allocate (self, &content_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)); }
/* * ClutterContainer Implementation */ static void st_table_actor_removed (ClutterContainer *container, ClutterActor *actor) { StTablePrivate *priv = ST_TABLE (container)->priv; GList *list, *children; gint n_rows = 0; gint n_cols = 0; /* Calculate and update the number of rows / columns */ children = st_container_get_children_list (ST_CONTAINER (container)); for (list = children; list; list = list->next) { ClutterActor *child = CLUTTER_ACTOR (list->data); 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)); }
static void st_table_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { StTable *table = ST_TABLE (gobject); switch (prop_id) { case PROP_HOMOGENEOUS: if (table->priv->homogeneous != g_value_get_boolean (value)) { table->priv->homogeneous = g_value_get_boolean (value); clutter_actor_queue_relayout ((ClutterActor *) gobject); } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } }
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 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 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_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 void st_table_homogeneous_allocate (ClutterActor *self, const ClutterActorBox *content_box, gboolean flags) { GList *list, *children; gfloat col_width, row_height; gint row_spacing, col_spacing; StTablePrivate *priv = ST_TABLE (self)->priv; gboolean ltr = st_widget_get_direction (ST_WIDGET (self)) == ST_TEXT_DIRECTION_LTR; 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); children = st_container_get_children_list (ST_CONTAINER (self)); for (list = children; list; list = list->next) { gint row, col, row_span, col_span; StTableChild *meta; ClutterActor *child; ClutterActorBox childbox; StAlign x_align, y_align; gboolean x_fill, y_fill; child = CLUTTER_ACTOR (list->data); 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; x_align = meta->x_align; y_align = meta->y_align; x_fill = meta->x_fill; y_fill = meta->y_fill; 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)); _st_allocate_fill (ST_WIDGET (self), child, &childbox, x_align, y_align, x_fill, y_fill); clutter_actor_allocate (child, &childbox, flags); } }
static void st_table_calculate_row_heights (StTable *table, gint for_height) { StTablePrivate *priv = ST_TABLE (table)->priv; GList *list, *children; gint i; DimensionData *rows, *columns; 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 */ children = st_container_get_children_list (ST_CONTAINER (table)); for (list = children; list; list = list->next) { StTableChild *meta; ClutterActor *child; DimensionData *row; gfloat c_min, c_pref; child = CLUTTER_ACTOR (list->data); meta = (StTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (table), child); if (!meta->allocate_hidden && !CLUTTER_ACTOR_IS_VISIBLE (child)) continue; 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++; } _st_actor_get_preferred_height (child, columns[meta->col].final_size, meta->x_fill, &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 */ for (list = children; list; list = list->next) { StTableChild *meta; ClutterActor *child; gfloat c_min, c_pref; gfloat min_height, pref_height; gint start_row, end_row; gint n_expand; child = CLUTTER_ACTOR (list->data); meta = (StTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (table), child); if (!meta->allocate_hidden && !CLUTTER_ACTOR_IS_VISIBLE (child)) continue; if (meta->row_span < 2) continue; start_row = meta->row; end_row = meta->row + meta->row_span - 1; _st_actor_get_preferred_height (child, columns[meta->col].final_size, meta->x_fill, &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) rows[i].final_size = rows[i].pref_size + (extra_height / n_expand); else if (!n_expand) 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) { if (rows[i].expand || !n_expand) { rows[i].final_size++; remaining--; } i++; } } } }
static void table_child_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { StTableChild *child = ST_TABLE_CHILD (gobject); StTable *table = ST_TABLE (CLUTTER_CHILD_META(gobject)->container); switch (prop_id) { case CHILD_PROP_COL: child->col = g_value_get_int (value); _st_table_update_row_col (table, -1, child->col); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_ROW: child->row = g_value_get_int (value); _st_table_update_row_col (table, child->row, -1); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_COL_SPAN: child->col_span = g_value_get_int (value); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_ROW_SPAN: child->row_span = g_value_get_int (value); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_X_EXPAND: child->x_expand = g_value_get_boolean (value); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_Y_EXPAND: child->y_expand = g_value_get_boolean (value); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_X_ALIGN: child->x_align = g_value_get_enum (value); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_Y_ALIGN: child->y_align = g_value_get_enum (value); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_X_FILL: child->x_fill = g_value_get_boolean (value); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_Y_FILL: child->y_fill = g_value_get_boolean (value); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_ALLOCATE_HIDDEN: child->allocate_hidden = g_value_get_boolean (value); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } }
static void st_table_preferred_allocate (ClutterActor *self, const ClutterActorBox *content_box, gboolean flags) { GList *list, *children; gint row_spacing, col_spacing; gint i; StTable *table; StTablePrivate *priv; gboolean ltr; DimensionData *rows, *columns; table = ST_TABLE (self); priv = ST_TABLE (self)->priv; col_spacing = (priv->col_spacing); row_spacing = (priv->row_spacing); st_table_calculate_dimensions (table, content_box->x2 - content_box->x1, content_box->y2 - content_box->y1); ltr = (st_widget_get_direction (ST_WIDGET (self)) == ST_TEXT_DIRECTION_LTR); rows = &g_array_index (priv->rows, DimensionData, 0); columns = &g_array_index (priv->columns, DimensionData, 0); children = st_container_get_children_list (ST_CONTAINER (self)); for (list = children; list; list = list->next) { gint row, col, row_span, col_span; gint col_width, row_height; StTableChild *meta; ClutterActor *child; ClutterActorBox childbox; gint child_x, child_y; StAlign x_align, y_align; gboolean x_fill, y_fill; child = CLUTTER_ACTOR (list->data); 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; x_align = meta->x_align; y_align = meta->y_align; x_fill = meta->x_fill; y_fill = meta->y_fill; /* 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 ("StTable: the child at %d,%d's col-span, %d, exceeds number of columns, %d", col, row, col_span, priv->n_cols); if (row + row_span > priv->n_rows) g_warning ("StTable: the child at %d,%d's row-span, %d, exceeds number of rows, %d", col, row, row_span, priv->n_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 */ if (ltr) { child_x = (int) content_box->x1 + col_spacing * col; for (i = 0; i < col; i++) child_x += columns[i].final_size; } else { child_x = (int) content_box->x2 - col_spacing * col; for (i = 0; i < col; i++) child_x -= columns[i].final_size; } /* calculate child y */ child_y = (int) content_box->y1 + row_spacing * row; for (i = 0; i < row; i++) child_y += rows[i].final_size; /* 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); _st_allocate_fill (ST_WIDGET (self), child, &childbox, x_align, y_align, x_fill, y_fill); clutter_actor_allocate (child, &childbox, flags); } }