diff --git a/src/libsysprof-ui/egg-three-grid.c b/src/libsysprof-ui/egg-three-grid.c new file mode 100644 index 00000000..7eeba771 --- /dev/null +++ b/src/libsysprof-ui/egg-three-grid.c @@ -0,0 +1,833 @@ +/* egg-three-grid.c + * + * Copyright (C) 2016 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define G_LOG_DOMAIN "egg-three-grid" + +#include "config.h" + +#define _USE_MATH_DEFINES +#include + +#include "egg-three-grid.h" + +typedef struct +{ + GtkWidget *widget; + EggThreeGridColumn column; + gint row; + gint min_height; + gint nat_height; + gint min_baseline; + gint nat_baseline; +} EggThreeGridChild; + +typedef struct +{ + GPtrArray *children; + GHashTable *row_infos; + guint column_spacing; + guint row_spacing; +} EggThreeGridPrivate; + +typedef struct +{ + gint row; + gint min_above_baseline; + gint min_below_baseline; + gint nat_above_baseline; + gint nat_below_baseline; +} EggThreeGridRowInfo; + +G_DEFINE_TYPE_WITH_PRIVATE (EggThreeGrid, egg_three_grid, GTK_TYPE_CONTAINER) + +enum { + PROP_0, + PROP_COLUMN_SPACING, + PROP_ROW_SPACING, + N_PROPS +}; + +enum { + CHILD_PROP_0, + CHILD_PROP_ROW, + CHILD_PROP_COLUMN, + N_CHILD_PROPS +}; + +static GParamSpec *properties [N_PROPS]; +static GParamSpec *child_properties [N_CHILD_PROPS]; +static EggThreeGridChild dummy; + +static EggThreeGridChild * +egg_three_grid_child_new (void) +{ + return g_slice_new0 (EggThreeGridChild); +} + +static void +egg_three_grid_child_free (gpointer data) +{ + EggThreeGridChild *child = data; + + g_clear_object (&child->widget); + g_slice_free (EggThreeGridChild, child); +} + +static EggThreeGridChild * +egg_three_grid_find_child (EggThreeGrid *self, + GtkWidget *widget) +{ + EggThreeGridPrivate *priv = egg_three_grid_get_instance_private (self); + + g_assert (EGG_IS_THREE_GRID (self)); + g_assert (GTK_IS_WIDGET (widget)); + + for (guint i = 0; i < priv->children->len; i++) + { + EggThreeGridChild *child = g_ptr_array_index (priv->children, i); + + if (child->widget == widget) + return child; + } + + return &dummy; +} + +static void +egg_three_grid_add (GtkContainer *container, + GtkWidget *widget) +{ + EggThreeGrid *self = (EggThreeGrid *)container; + EggThreeGridPrivate *priv = egg_three_grid_get_instance_private (self); + EggThreeGridChild *child; + + g_assert (EGG_IS_THREE_GRID (self)); + g_assert (GTK_IS_WIDGET (widget)); + + child = egg_three_grid_child_new (); + child->widget = g_object_ref_sink (widget); + g_ptr_array_add (priv->children, child); + + gtk_widget_set_parent (widget, GTK_WIDGET (self)); +} + +static void +egg_three_grid_remove (GtkContainer *container, + GtkWidget *widget) +{ + EggThreeGrid *self = (EggThreeGrid *)container; + EggThreeGridPrivate *priv = egg_three_grid_get_instance_private (self); + + g_assert (EGG_IS_THREE_GRID (self)); + g_assert (GTK_IS_WIDGET (widget)); + + for (guint i = 0; i < priv->children->len; i++) + { + EggThreeGridChild *child = g_ptr_array_index (priv->children, i); + + if (child->widget == widget) + { + gtk_widget_unparent (child->widget); + g_ptr_array_remove_index (priv->children, i); + gtk_widget_queue_resize (GTK_WIDGET (self)); + return; + } + } +} + +static GtkSizeRequestMode +egg_three_grid_get_request_mode (GtkWidget *widget) +{ + return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH; +} + +static void +egg_three_grid_get_column_width (EggThreeGrid *self, + EggThreeGridColumn column, + gint *min_width, + gint *nat_width) +{ + EggThreeGridPrivate *priv = egg_three_grid_get_instance_private (self); + gint real_min_width = 0; + gint real_nat_width = 0; + + g_assert (EGG_IS_THREE_GRID (self)); + g_assert (column >= EGG_THREE_GRID_COLUMN_LEFT); + g_assert (column <= EGG_THREE_GRID_COLUMN_RIGHT); + g_assert (min_width != NULL); + g_assert (nat_width != NULL); + + for (guint i = 0; i < priv->children->len; i++) + { + EggThreeGridChild *child = g_ptr_array_index (priv->children, i); + + if (child->column == column) + { + gint child_min_width; + gint child_nat_width; + + gtk_widget_get_preferred_width (child->widget, &child_min_width, &child_nat_width); + + real_min_width = MAX (real_min_width, child_min_width); + real_nat_width = MAX (real_nat_width, child_nat_width); + } + } + + *min_width = real_min_width; + *nat_width = real_nat_width; +} + +static void +egg_three_grid_get_preferred_width (GtkWidget *widget, + gint *min_width, + gint *nat_width) +{ + EggThreeGrid *self = (EggThreeGrid *)widget; + EggThreeGridPrivate *priv = egg_three_grid_get_instance_private (self); + GtkStyleContext *style_context; + GtkBorder margin; + gint min_widths[3]; + gint nat_widths[3]; + gint border_width; + GtkStateFlags state; + + g_assert (EGG_IS_THREE_GRID (self)); + g_assert (min_width != NULL); + g_assert (nat_width != NULL); + + style_context = gtk_widget_get_style_context (widget); + state = gtk_style_context_get_state (style_context); + gtk_style_context_get_margin (style_context, state, &margin); + + for (guint i = 0; i < 3; i++) + egg_three_grid_get_column_width (self, i, &min_widths[i], &nat_widths[i]); + + border_width = gtk_container_get_border_width (GTK_CONTAINER (self)); + + *min_width = MAX (min_widths[0], min_widths[2]) * 2 + min_widths[1] + margin.left + margin.right + (border_width * 2) + (priv->column_spacing * 2); + *nat_width = MAX (nat_widths[0], nat_widths[2]) * 2 + nat_widths[1] + margin.left + margin.right + (border_width * 2) + (priv->column_spacing * 2); +} + +static void +row_info_merge (EggThreeGridRowInfo *row_info, + const EggThreeGridRowInfo *other) +{ + g_assert (row_info); + g_assert (other); + + row_info->min_above_baseline = MAX (row_info->min_above_baseline, other->min_above_baseline); + row_info->min_below_baseline = MAX (row_info->min_below_baseline, other->min_below_baseline); + row_info->nat_above_baseline = MAX (row_info->nat_above_baseline, other->nat_above_baseline); + row_info->nat_below_baseline = MAX (row_info->nat_below_baseline, other->nat_below_baseline); +} + +static void +update_row_info (GHashTable *hashtable, + EggThreeGridChild *child) +{ + GtkBaselinePosition baseline_position = GTK_BASELINE_POSITION_CENTER; + EggThreeGridRowInfo *row_info; + EggThreeGridRowInfo current = { 0 }; + + g_assert (hashtable); + g_assert (child); + + row_info = g_hash_table_lookup (hashtable, GINT_TO_POINTER (child->row)); + + if (row_info == NULL) + { + row_info = g_new0 (EggThreeGridRowInfo, 1); + row_info->row = child->row; + g_hash_table_insert (hashtable, GINT_TO_POINTER (child->row), row_info); + } + + /* + * TODO: + * + * Allow setting baseline position per row. Right now we only support center + * because that is the easiest thing to start with. + */ + + if (child->min_baseline == -1) + { + if (baseline_position == GTK_BASELINE_POSITION_CENTER) + { + current.min_above_baseline = current.min_below_baseline = ceil (child->min_height / 2.0); + current.nat_above_baseline = current.nat_below_baseline = ceil (child->min_height / 2.0); + } + else if (baseline_position == GTK_BASELINE_POSITION_TOP) + { + g_assert_not_reached (); + } + else if (baseline_position == GTK_BASELINE_POSITION_BOTTOM) + { + g_assert_not_reached (); + } + } + else + { + current.min_above_baseline = child->min_baseline; + current.min_below_baseline = child->min_height - child->min_baseline; + current.nat_above_baseline = child->nat_baseline; + current.nat_below_baseline = child->nat_height - child->nat_baseline; + } + + row_info_merge (row_info, ¤t); +} + +static void +egg_three_grid_get_preferred_height_for_width (GtkWidget *widget, + gint width, + gint *min_height, + gint *nat_height) +{ + EggThreeGrid *self = (EggThreeGrid *)widget; + EggThreeGridPrivate *priv = egg_three_grid_get_instance_private (self); + g_autoptr(GHashTable) row_infos = NULL; + EggThreeGridRowInfo *row_info; + GtkStyleContext *style_context; + GHashTableIter iter; + GtkStateFlags state; + GtkBorder margin; + gint real_min_height = 0; + gint real_nat_height = 0; + gint column_min_widths[3]; + gint column_nat_widths[3]; + gint widths[3]; + gint border_width; + gint n_rows; + + g_assert (EGG_IS_THREE_GRID (self)); + g_assert (min_height != NULL); + g_assert (nat_height != NULL); + + border_width = gtk_container_get_border_width (GTK_CONTAINER (self)); + width -= border_width * 2; + width -= priv->column_spacing * 2; + + style_context = gtk_widget_get_style_context (widget); + state = gtk_style_context_get_state (style_context); + gtk_style_context_get_margin (style_context, state, &margin); + width -= margin.left + margin.right; + + egg_three_grid_get_column_width (self, EGG_THREE_GRID_COLUMN_LEFT, &column_min_widths[0], &column_nat_widths[0]); + egg_three_grid_get_column_width (self, EGG_THREE_GRID_COLUMN_CENTER, &column_min_widths[1], &column_nat_widths[1]); + egg_three_grid_get_column_width (self, EGG_THREE_GRID_COLUMN_RIGHT, &column_min_widths[2], &column_nat_widths[2]); + + if ((MAX (column_min_widths[0], column_min_widths[2]) * 2 + column_nat_widths[1]) > width) + { + widths[0] = column_min_widths[0]; + widths[2] = column_min_widths[2]; + widths[1] = width - widths[0] - widths[2]; + } + else + { + /* Handle #1 and #2 */ + widths[1] = column_nat_widths[1]; + widths[0] = (width - widths[1]) / 2; + widths[2] = width - widths[1] - widths[0]; + } + + row_infos = g_hash_table_new_full (NULL, NULL, NULL, g_free); + + for (guint i = 0; i < priv->children->len; i++) + { + EggThreeGridChild *child = g_ptr_array_index (priv->children, i); + + if (!gtk_widget_get_visible (child->widget) || + !gtk_widget_get_child_visible (child->widget)) + continue; + + gtk_widget_get_preferred_height_and_baseline_for_width (child->widget, + widths[child->column], + &child->min_height, + &child->nat_height, + &child->min_baseline, + &child->nat_baseline); + update_row_info (row_infos, child); + } + + g_hash_table_iter_init (&iter, row_infos); + + while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&row_info)) + { +#if 0 + g_print ("Row %d: MIN Above %d Below %d\n", + row_info->row, + row_info->min_above_baseline, row_info->min_below_baseline); + g_print ("Row %d: NAT Above %d Below %d\n", + row_info->row, + row_info->nat_above_baseline, row_info->nat_below_baseline); +#endif + real_min_height += row_info->min_above_baseline + row_info->min_below_baseline; + real_nat_height += row_info->nat_above_baseline + row_info->nat_below_baseline; + } + + real_min_height += border_width * 2; + real_nat_height += border_width * 2; + + real_min_height += margin.top + margin.bottom; + real_nat_height += margin.top + margin.bottom; + + n_rows = g_hash_table_size (row_infos); + + if (n_rows > 1) + { + real_min_height += (n_rows - 1) * priv->row_spacing; + real_nat_height += (n_rows - 1) * priv->row_spacing; + } + + *min_height = real_min_height; + *nat_height = real_nat_height; + +#if 0 + g_print ("%d children in %d rows: %dx%d\n", + priv->children->len, + g_hash_table_size (row_infos), + real_min_height, real_nat_height); +#endif + + g_clear_pointer (&priv->row_infos, g_hash_table_unref); + priv->row_infos = g_steal_pointer (&row_infos); +} + +static gint +sort_by_row (gconstpointer a, + gconstpointer b) +{ + const EggThreeGridRowInfo *info_a = a; + const EggThreeGridRowInfo *info_b = b; + + return info_a->row - info_b->row; +} + +static void +egg_three_grid_size_allocate_children (EggThreeGrid *self, + EggThreeGridColumn column, + gint row, + GtkAllocation *allocation, + gint baseline) +{ + EggThreeGridPrivate *priv = egg_three_grid_get_instance_private (self); + + g_assert (EGG_IS_THREE_GRID (self)); + g_assert (allocation != NULL); + + for (guint i = 0; i < priv->children->len; i++) + { + EggThreeGridChild *child = g_ptr_array_index (priv->children, i); + + if (child->row == row && child->column == column) + { + GtkAllocation copy = *allocation; + gtk_widget_size_allocate_with_baseline (child->widget, ©, baseline); + } + } +} + +static void +egg_three_grid_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + EggThreeGrid *self = (EggThreeGrid *)widget; + EggThreeGridPrivate *priv = egg_three_grid_get_instance_private (self); + g_autofree GtkRequestedSize *rows = NULL; + const GList *iter; + GtkStyleContext *style_context; + GtkAllocation area; + GtkTextDirection dir; + GList *values; + GtkStateFlags state; + GtkBorder margin; + guint i; + guint n_rows; + gint min_height; + gint nat_height; + gint border_width; + gint left_min_width; + gint left_nat_width; + gint center_min_width; + gint center_nat_width; + gint right_min_width; + gint right_nat_width; + gint left; + gint center; + gint right; + + g_assert (EGG_IS_THREE_GRID (self)); + g_assert (allocation != NULL); + + area = *allocation; + + style_context = gtk_widget_get_style_context (widget); + state = gtk_style_context_get_state (style_context); + gtk_style_context_get_margin (style_context, state, &margin); + border_width = gtk_container_get_border_width (GTK_CONTAINER (self)); + + area.x += border_width; + area.y += border_width; + area.width -= border_width * 2; + area.height -= border_width * 2; + + area.x += margin.left; + area.width -= margin.right + margin.left; + area.y += margin.top; + area.height -= margin.top + margin.bottom; + + gtk_widget_set_allocation (widget, &area); + + dir = gtk_widget_get_direction (widget); + + egg_three_grid_get_preferred_height_for_width (widget, allocation->width, &min_height, &nat_height); + + if (min_height > allocation->height) + g_warning ("%s requested a minimum height of %d and got %d", + G_OBJECT_TYPE_NAME (widget), min_height, allocation->height); + + if (priv->row_infos == NULL) + return; + + values = g_hash_table_get_values (priv->row_infos); + values = g_list_sort (values, sort_by_row); + + egg_three_grid_get_column_width (self, EGG_THREE_GRID_COLUMN_LEFT, &left_min_width, &left_nat_width); + egg_three_grid_get_column_width (self, EGG_THREE_GRID_COLUMN_CENTER, ¢er_min_width, ¢er_nat_width); + egg_three_grid_get_column_width (self, EGG_THREE_GRID_COLUMN_RIGHT, &right_min_width, &right_nat_width); + + /* + * Determine how much to give to the center widget first. This is because we will + * just give the rest of the space on the sides to left/right columns and they + * can deal with alignment by using halign. + * + * We can be in one of a couple states: + * + * 1) There is enough room for all columns natural size. + * (We allocate the same to the left and the right). + * 2) There is enough for the natural size of the center + * but for some amount between natural and min sizing + * of the left/right columns. + * 3) There is only minimum size for columns and some + * amount between natural/minimum of the center. + * + * We can handle #1 and #2 with the same logic though. + */ + + if ((MAX (left_min_width, right_min_width) * 2 + center_nat_width + 2 * priv->column_spacing) > area.width) + { + /* Handle #3 */ + left = right = MAX (left_min_width, right_min_width); + center = area.width - left - right - 2 * priv->column_spacing; + } + else + { + /* Handle #1 and #2 */ + center = center_nat_width; + right = left = (area.width - center) / 2 - priv->column_spacing; + } + + n_rows = g_list_length (values); + rows = g_new0 (GtkRequestedSize, n_rows); + + for (iter = values, i = 0; iter != NULL; iter = iter->next, i++) + { + EggThreeGridRowInfo *row_info = iter->data; + + rows[i].data = row_info; + rows[i].minimum_size = row_info->min_above_baseline + row_info->min_below_baseline; + rows[i].natural_size = row_info->nat_above_baseline + row_info->nat_below_baseline; + } + + gtk_distribute_natural_allocation (area.height, n_rows, rows); + + for (i = 0; i < n_rows; i++) + { + GtkRequestedSize *size = &rows[i]; + EggThreeGridRowInfo *row_info = size->data; + GtkAllocation child_alloc; + gint baseline; + + if (row_info->nat_above_baseline + row_info->nat_below_baseline < size->minimum_size) + baseline = row_info->nat_above_baseline; + else + baseline = row_info->min_above_baseline; + + child_alloc.x = area.x; + child_alloc.width = left; + child_alloc.y = area.y; + child_alloc.height = size->minimum_size; + if (dir == GTK_TEXT_DIR_LTR) + egg_three_grid_size_allocate_children (self, EGG_THREE_GRID_COLUMN_LEFT, row_info->row, &child_alloc, baseline); + else + egg_three_grid_size_allocate_children (self, EGG_THREE_GRID_COLUMN_RIGHT, row_info->row, &child_alloc, baseline); + + child_alloc.x = area.x + left + priv->column_spacing; + child_alloc.width = center; + child_alloc.y = area.y; + child_alloc.height = size->minimum_size; + egg_three_grid_size_allocate_children (self, EGG_THREE_GRID_COLUMN_CENTER, row_info->row, &child_alloc, baseline); + + child_alloc.x = area.x + area.width - right; + child_alloc.width = right; + child_alloc.y = area.y; + child_alloc.height = size->minimum_size; + if (dir == GTK_TEXT_DIR_LTR) + egg_three_grid_size_allocate_children (self, EGG_THREE_GRID_COLUMN_RIGHT, row_info->row, &child_alloc, baseline); + else + egg_three_grid_size_allocate_children (self, EGG_THREE_GRID_COLUMN_LEFT, row_info->row, &child_alloc, baseline); + + area.y += child_alloc.height + priv->row_spacing; + area.height -= child_alloc.height + priv->row_spacing; + } + + g_list_free (values); +} + +static void +egg_three_grid_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer user_data) +{ + EggThreeGrid *self = (EggThreeGrid *)container; + EggThreeGridPrivate *priv = egg_three_grid_get_instance_private (self); + + g_assert (GTK_IS_CONTAINER (self)); + g_assert (callback != NULL); + + for (guint i = priv->children->len; i > 0; i--) + { + EggThreeGridChild *child = g_ptr_array_index (priv->children, i - 1); + + callback (child->widget, user_data); + } +} + +static void +egg_three_grid_get_child_property (GtkContainer *container, + GtkWidget *widget, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + EggThreeGrid *self = (EggThreeGrid *)container; + EggThreeGridChild *child = egg_three_grid_find_child (self, widget); + + switch (prop_id) + { + case CHILD_PROP_COLUMN: + g_value_set_enum (value, child->column); + break; + + case CHILD_PROP_ROW: + g_value_set_uint (value, child->row); + break; + + default: + GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, prop_id, pspec); + } +} + +static void +egg_three_grid_set_child_property (GtkContainer *container, + GtkWidget *widget, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + EggThreeGrid *self = (EggThreeGrid *)container; + EggThreeGridChild *child = egg_three_grid_find_child (self, widget); + + switch (prop_id) + { + case CHILD_PROP_COLUMN: + child->column = g_value_get_enum (value); + break; + + case CHILD_PROP_ROW: + child->row = g_value_get_uint (value); + break; + + default: + GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, prop_id, pspec); + } + + gtk_widget_queue_resize (GTK_WIDGET (container)); +} + +static void +egg_three_grid_finalize (GObject *object) +{ + EggThreeGrid *self = (EggThreeGrid *)object; + EggThreeGridPrivate *priv = egg_three_grid_get_instance_private (self); + + g_clear_pointer (&priv->row_infos, g_hash_table_unref); + g_clear_pointer (&priv->children, g_ptr_array_unref); + + G_OBJECT_CLASS (egg_three_grid_parent_class)->finalize (object); +} + +static void +egg_three_grid_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + EggThreeGrid *self = EGG_THREE_GRID (object); + EggThreeGridPrivate *priv = egg_three_grid_get_instance_private (self); + + switch (prop_id) + { + case PROP_COLUMN_SPACING: + g_value_set_uint (value, priv->column_spacing); + break; + + case PROP_ROW_SPACING: + g_value_set_uint (value, priv->row_spacing); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +egg_three_grid_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + EggThreeGrid *self = EGG_THREE_GRID (object); + EggThreeGridPrivate *priv = egg_three_grid_get_instance_private (self); + + switch (prop_id) + { + case PROP_COLUMN_SPACING: + priv->column_spacing = g_value_get_uint (value); + gtk_widget_queue_resize (GTK_WIDGET (self)); + break; + + case PROP_ROW_SPACING: + priv->row_spacing = g_value_get_uint (value); + gtk_widget_queue_resize (GTK_WIDGET (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +egg_three_grid_class_init (EggThreeGridClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); + + object_class->finalize = egg_three_grid_finalize; + object_class->get_property = egg_three_grid_get_property; + object_class->set_property = egg_three_grid_set_property; + + widget_class->get_request_mode = egg_three_grid_get_request_mode; + widget_class->get_preferred_height_for_width = egg_three_grid_get_preferred_height_for_width; + widget_class->get_preferred_width = egg_three_grid_get_preferred_width; + widget_class->size_allocate = egg_three_grid_size_allocate; + + container_class->add = egg_three_grid_add; + container_class->forall = egg_three_grid_forall; + container_class->get_child_property = egg_three_grid_get_child_property; + container_class->remove = egg_three_grid_remove; + container_class->set_child_property = egg_three_grid_set_child_property; + + properties [PROP_COLUMN_SPACING] = + g_param_spec_uint ("column-spacing", + "Column Spacing", + "The amount of spacing to add between columns", + 0, + G_MAXUINT, + 0, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_ROW_SPACING] = + g_param_spec_uint ("row-spacing", + "Row Spacing", + "The amount of spacing to add between rows", + 0, + G_MAXUINT, + 0, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); + + child_properties [CHILD_PROP_COLUMN] = + g_param_spec_enum ("column", + "Column", + "Column", + EGG_TYPE_THREE_GRID_COLUMN, + EGG_THREE_GRID_COLUMN_LEFT, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + child_properties [CHILD_PROP_ROW] = + g_param_spec_uint ("row", + "Row", + "Row", + 0, + G_MAXUINT, + 0, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gtk_container_class_install_child_properties (container_class, N_CHILD_PROPS, child_properties); + + gtk_widget_class_set_css_name (widget_class, "threegrid"); +} + +static void +egg_three_grid_init (EggThreeGrid *self) +{ + EggThreeGridPrivate *priv = egg_three_grid_get_instance_private (self); + + priv->children = g_ptr_array_new_with_free_func (egg_three_grid_child_free); + + gtk_widget_set_has_window (GTK_WIDGET (self), FALSE); +} + +GtkWidget * +egg_three_grid_new (void) +{ + return g_object_new (EGG_TYPE_THREE_GRID, NULL); +} + +GType +egg_three_grid_column_get_type (void) +{ + static GType type_id; + + if (g_once_init_enter (&type_id)) + { + GType _type_id; + static const GEnumValue values[] = { + { EGG_THREE_GRID_COLUMN_LEFT, "EGG_THREE_GRID_COLUMN_LEFT", "left" }, + { EGG_THREE_GRID_COLUMN_CENTER, "EGG_THREE_GRID_COLUMN_CENTER", "center" }, + { EGG_THREE_GRID_COLUMN_RIGHT, "EGG_THREE_GRID_COLUMN_RIGHT", "right" }, + { 0 } + }; + _type_id = g_enum_register_static ("EggThreeGridColumn", values); + g_once_init_leave (&type_id, _type_id); + } + + return type_id; +} diff --git a/src/libsysprof-ui/egg-three-grid.h b/src/libsysprof-ui/egg-three-grid.h new file mode 100644 index 00000000..d1d687f0 --- /dev/null +++ b/src/libsysprof-ui/egg-three-grid.h @@ -0,0 +1,45 @@ +/* egg-three-grid.h + * + * Copyright (C) 2016 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define EGG_TYPE_THREE_GRID (egg_three_grid_get_type()) +#define EGG_TYPE_THREE_GRID_COLUMN (egg_three_grid_column_get_type()) + +G_DECLARE_DERIVABLE_TYPE (EggThreeGrid, egg_three_grid, EGG, THREE_GRID, GtkContainer) + +struct _EggThreeGridClass +{ + GtkContainerClass parent_class; +}; + +typedef enum +{ + EGG_THREE_GRID_COLUMN_LEFT, + EGG_THREE_GRID_COLUMN_CENTER, + EGG_THREE_GRID_COLUMN_RIGHT +} EggThreeGridColumn; + +GType egg_three_grid_column_get_type (void); +GtkWidget *egg_three_grid_new (void); + +G_END_DECLS diff --git a/src/libsysprof-ui/meson.build b/src/libsysprof-ui/meson.build index 406b47ef..c2f1deba 100644 --- a/src/libsysprof-ui/meson.build +++ b/src/libsysprof-ui/meson.build @@ -13,6 +13,7 @@ libsysprof_ui_public_sources = [ ] libsysprof_ui_private_sources = [ + 'egg-three-grid.c', 'pointcache.c', 'rectangles.c', 'sysprof-aid.c',