[sysprof/wip/gtk4-port: 25/125] libsysprof-ui: add EggThreeGrid
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [sysprof/wip/gtk4-port: 25/125] libsysprof-ui: add EggThreeGrid
- Date: Fri, 29 Oct 2021 02:02:30 +0000 (UTC)
commit 464389d26c7e374a190da2fd33849440a50258d7
Author: Christian Hergert <chergert redhat com>
Date: Tue Sep 28 13:38:43 2021 -0700
libsysprof-ui: add EggThreeGrid
So we can drop our use from libdazzle. We'll need to port this to GTK 4
but it shouldn't be too bad.
src/libsysprof-ui/egg-three-grid.c | 833 +++++++++++++++++++++++++++++++++++++
src/libsysprof-ui/egg-three-grid.h | 45 ++
src/libsysprof-ui/meson.build | 1 +
3 files changed, 879 insertions(+)
---
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 <chergert redhat com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "egg-three-grid"
+
+#include "config.h"
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+
+#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 <chergert redhat com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+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',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]