[gtk+] Add GtkGrid
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+] Add GtkGrid
- Date: Fri, 22 Oct 2010 14:53:41 +0000 (UTC)
commit 8f0ae8e8a3dd3e7dfa1cdb49bbfab61b8339ce9f
Author: Matthias Clasen <mclasen redhat com>
Date: Mon Sep 27 10:44:31 2010 -0400
Add GtkGrid
GtkGrid is a container similar to GtkTable, without legacy
properties and unnecessary restrictions.
It does height-for-width geometry management.
docs/reference/gtk/gtk-docs.sgml | 1 +
docs/reference/gtk/gtk3-sections.txt | 31 +
docs/reference/gtk/gtk3.types | 1 +
gtk/Makefile.am | 2 +
gtk/gtk.h | 1 +
gtk/gtk.symbols | 17 +
gtk/gtkgrid.c | 1676 ++++++++++++++++++++++++++++++++++
gtk/gtkgrid.h | 98 ++
tests/Makefile.am | 6 +
tests/testgrid.c | 264 ++++++
10 files changed, 2097 insertions(+), 0 deletions(-)
---
diff --git a/docs/reference/gtk/gtk-docs.sgml b/docs/reference/gtk/gtk-docs.sgml
index 0c7f7e1..519879e 100644
--- a/docs/reference/gtk/gtk-docs.sgml
+++ b/docs/reference/gtk/gtk-docs.sgml
@@ -203,6 +203,7 @@
<chapter id="LayoutContainers">
<title>Layout Containers</title>
+ <xi:include href="xml/gtkgrid.xml" />
<xi:include href="xml/gtkalignment.xml" />
<xi:include href="xml/gtkaspectframe.xml" />
<xi:include href="xml/gtkbox.xml" />
diff --git a/docs/reference/gtk/gtk3-sections.txt b/docs/reference/gtk/gtk3-sections.txt
index f0ba03b..2ebc6b1 100644
--- a/docs/reference/gtk/gtk3-sections.txt
+++ b/docs/reference/gtk/gtk3-sections.txt
@@ -6350,3 +6350,34 @@ GTK_APPLICATION_GET_CLASS
gtk_application_get_type
GtkApplicationPrivate
</SECTION>
+
+<SECTION>
+<FILE>gtkgrid</FILE>
+<TITLE>GtkGrid</TITLE>
+GtkGrid
+gtk_grid_new
+gtk_grid_attach
+gtk_grid_attach_next_to
+gtk_grid_set_row_homogeneous
+gtk_grid_get_row_homogeneous
+gtk_grid_set_row_spacing
+gtk_grid_get_row_spacing
+gtk_grid_set_column_homogeneous
+gtk_grid_get_column_homogeneous
+gtk_grid_set_column_spacing
+gtk_grid_get_column_spacing
+
+<SUBSECTION Standard>
+GtkGrid
+GtkGridClass
+GTK_TYPE_GRID
+GTK_GRID
+GTK_GRID_CLASS
+GTK_IS_GRID
+GTK_IS_GRID_CLASS
+GTK_GRID_GET_CLASS
+
+<SUBSECTION Private>
+GtkGridPrivate
+gtk_grid_get_type
+</SECTION>
diff --git a/docs/reference/gtk/gtk3.types b/docs/reference/gtk/gtk3.types
index 5cd9d9b..5c02fbe 100644
--- a/docs/reference/gtk/gtk3.types
+++ b/docs/reference/gtk/gtk3.types
@@ -61,6 +61,7 @@ gtk_font_button_get_type
gtk_font_selection_dialog_get_type
gtk_font_selection_get_type
gtk_frame_get_type
+gtk_grid_get_type
gtk_handle_box_get_type
gtk_hbox_get_type
gtk_hbutton_box_get_type
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 5d6892b..0774710 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -209,6 +209,7 @@ gtk_public_h_sources = \
gtkfontbutton.h \
gtkfontsel.h \
gtkframe.h \
+ gtkgrid.h \
gtkhandlebox.h \
gtkhbbox.h \
gtkhbox.h \
@@ -472,6 +473,7 @@ gtk_base_c_sources = \
gtkfontbutton.c \
gtkfontsel.c \
gtkframe.c \
+ gtkgrid.c \
gtkhandlebox.c \
gtkhbbox.c \
gtkhbox.c \
diff --git a/gtk/gtk.h b/gtk/gtk.h
index ea2e207..6e99849 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -93,6 +93,7 @@
#include <gtk/gtkfontbutton.h>
#include <gtk/gtkfontsel.h>
#include <gtk/gtkframe.h>
+#include <gtk/gtkgrid.h>
#include <gtk/gtkhandlebox.h>
#include <gtk/gtkhbbox.h>
#include <gtk/gtkhbox.h>
diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
index a2aeaf4..795cd3c 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -4528,3 +4528,20 @@ gtk_info_bar_set_message_type
gtk_info_bar_get_message_type
#endif
#endif
+
+#if IN_HEADER(__GTK_GRID_H__)
+#if IN_FILE(__GTK_GRID_c__)
+gtk_grid_get_type G_GNUC_CONST
+gtk_grid_new
+gtk_grid_attach
+gtk_grid_attach_next_to
+gtk_grid_set_row_homogeneous
+gtk_grid_get_row_homogeneous
+gtk_grid_set_row_spacing
+gtk_grid_get_row_spacing
+gtk_grid_set_column_homogeneous
+gtk_grid_get_column_homogeneous
+gtk_grid_set_column_spacing
+gtk_grid_get_column_spacing
+#endif
+#endif
diff --git a/gtk/gtkgrid.c b/gtk/gtkgrid.c
new file mode 100644
index 0000000..60d55b8
--- /dev/null
+++ b/gtk/gtkgrid.c
@@ -0,0 +1,1676 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2010 Red Hat, Inc.
+ * Author: Matthias Clasen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gtkgrid.h"
+
+#include "gtkorientable.h"
+#include "gtksizerequest.h"
+#include "gtkprivate.h"
+#include "gtkintl.h"
+
+
+/**
+ * SECTION:gtkgrid
+ * @Short_description: Pack widgets in a rows and columns
+ * @Title: GtkGrid
+ * @See_also: #GtkTable, #GtkHBox, #GtkVBox
+ *
+ * GtkGrid is a container which arranges its child widgets in
+ * rows and columns. It is a very similar to #GtkTable and #GtkBox,
+ * but it consistently uses #GtkWidget's margin and expand properties
+ * instead of custom child properties, and it fully supports
+ * height-for-width geometry management.
+ *
+ * Children are added using gtk_grid_attach(). They can span multiple
+ * rows or columns. It is also possible to add a child next to an
+ * existing child, using gtk_grid_attach_next_to().
+ *
+ * GtkGrid can be used like a #GtkBox by just using gtk_container_add(),
+ * which will place children next to each other in the direction determined
+ * by the #GtkGrid::orientation property.
+ */
+
+typedef struct _GtkGridChild GtkGridChild;
+typedef struct _GtkGridChildAttach GtkGridChildAttach;
+typedef struct _GtkGridLine GtkGridLine;
+typedef struct _GtkGridLines GtkGridLines;
+typedef struct _GtkGridLineData GtkGridLineData;
+typedef struct _GtkGridRequest GtkGridRequest;
+
+struct _GtkGridChildAttach
+{
+ gint pos;
+ gint span;
+};
+
+struct _GtkGridChild
+{
+ GtkWidget *widget;
+ GtkGridChildAttach attach[2];
+};
+
+#define CHILD_LEFT(child) ((child)->attach[GTK_ORIENTATION_HORIZONTAL].pos)
+#define CHILD_WIDTH(child) ((child)->attach[GTK_ORIENTATION_HORIZONTAL].span)
+#define CHILD_TOP(child) ((child)->attach[GTK_ORIENTATION_VERTICAL].pos)
+#define CHILD_HEIGHT(child) ((child)->attach[GTK_ORIENTATION_VERTICAL].span)
+
+/* A GtkGridLineData struct contains row/column specific parts
+ * of the grid.
+ */
+struct _GtkGridLineData
+{
+ gint16 spacing;
+ guint homogeneous : 1;
+};
+
+struct _GtkGridPrivate
+{
+ GList *children;
+
+ GtkOrientation orientation;
+
+ GtkGridLineData linedata[2];
+};
+
+#define ROWS(priv) (&(priv)->linedata[GTK_ORIENTATION_HORIZONTAL])
+#define COLUMNS(priv) (&(priv)->linedata[GTK_ORIENTATION_VERTICAL])
+
+/* A GtkGridLine struct represents a single row or column
+ * during size requests
+ */
+struct _GtkGridLine
+{
+ gint minimum;
+ gint natural;
+ gint position;
+ gint allocation;
+
+ guint need_expand : 1;
+ guint expand : 1;
+ guint empty : 1;
+};
+
+struct _GtkGridLines
+{
+ GtkGridLine *lines;
+ gint min, max;
+};
+
+struct _GtkGridRequest
+{
+ GtkGrid *grid;
+ GtkGridLines lines[2];
+};
+
+
+enum
+{
+ PROP_0,
+ PROP_ORIENTATION,
+ PROP_ROW_SPACING,
+ PROP_COLUMN_SPACING,
+ PROP_ROW_HOMOGENEOUS,
+ PROP_COLUMN_HOMOGENEOUS
+};
+
+enum
+{
+ CHILD_PROP_0,
+ CHILD_PROP_LEFT_ATTACH,
+ CHILD_PROP_TOP_ATTACH,
+ CHILD_PROP_WIDTH,
+ CHILD_PROP_HEIGHT
+};
+
+G_DEFINE_TYPE_WITH_CODE (GtkGrid, gtk_grid, GTK_TYPE_CONTAINER,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
+
+
+static void
+gtk_grid_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkGrid *grid = GTK_GRID (object);
+ GtkGridPrivate *priv = grid->priv;
+
+ switch (prop_id)
+ {
+ case PROP_ORIENTATION:
+ g_value_set_enum (value, priv->orientation);
+ break;
+
+ case PROP_ROW_SPACING:
+ g_value_set_int (value, ROWS (priv)->spacing);
+ break;
+
+ case PROP_COLUMN_SPACING:
+ g_value_set_int (value, COLUMNS (priv)->spacing);
+ break;
+
+ case PROP_ROW_HOMOGENEOUS:
+ g_value_set_boolean (value, ROWS (priv)->homogeneous);
+ break;
+
+ case PROP_COLUMN_HOMOGENEOUS:
+ g_value_set_boolean (value, COLUMNS (priv)->homogeneous);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_grid_set_orientation (GtkGrid *grid,
+ GtkOrientation orientation)
+{
+ GtkGridPrivate *priv = grid->priv;
+ GList *list;
+ GtkGridChild *child;
+ gint left, top, width, height;
+
+ if (priv->orientation != orientation)
+ {
+ priv->orientation = orientation;
+
+ g_object_notify (G_OBJECT (grid), "orientation");
+
+ for (list = priv->children; list; list = list->next)
+ {
+ child = list->data;
+
+ left = CHILD_LEFT (child);
+ top = CHILD_TOP (child);
+ width = CHILD_WIDTH (child);
+ height = CHILD_HEIGHT (child);
+
+ if (orientation == GTK_ORIENTATION_VERTICAL)
+ {
+ CHILD_LEFT (child) = - (top + height);
+ CHILD_TOP (child) = left;
+ CHILD_WIDTH (child) = height;
+ CHILD_HEIGHT (child) = width;
+ }
+ else
+ {
+ CHILD_LEFT (child) = top;
+ CHILD_TOP (child) = - (left + width);
+ CHILD_WIDTH (child) = height;
+ CHILD_HEIGHT (child) = width;
+ }
+ }
+
+ gtk_widget_queue_resize (GTK_WIDGET (grid));
+
+ for (list = priv->children; list; list = list->next)
+ {
+ child = list->data;
+
+ gtk_widget_child_notify (child->widget, "left-attach");
+ gtk_widget_child_notify (child->widget, "top-attach");
+ gtk_widget_child_notify (child->widget, "width");
+ gtk_widget_child_notify (child->widget, "height");
+
+ }
+ }
+}
+
+static void
+gtk_grid_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkGrid *grid = GTK_GRID (object);
+
+ switch (prop_id)
+ {
+ case PROP_ORIENTATION:
+ gtk_grid_set_orientation (grid, g_value_get_enum (value));
+ break;
+
+ case PROP_ROW_SPACING:
+ gtk_grid_set_row_spacing (grid, g_value_get_int (value));
+ break;
+
+ case PROP_COLUMN_SPACING:
+ gtk_grid_set_column_spacing (grid, g_value_get_int (value));
+ break;
+
+ case PROP_ROW_HOMOGENEOUS:
+ gtk_grid_set_row_homogeneous (grid, g_value_get_boolean (value));
+ break;
+
+ case PROP_COLUMN_HOMOGENEOUS:
+ gtk_grid_set_column_homogeneous (grid, g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GtkGridChild *
+find_grid_child (GtkGrid *grid,
+ GtkWidget *widget)
+{
+ GtkGridPrivate *priv = grid->priv;
+ GtkGridChild *child;
+ GList *list;
+
+ for (list = priv->children; list; list = list->next)
+ {
+ child = list->data;
+
+ if (child->widget == widget)
+ return child;
+ }
+
+ return NULL;
+}
+
+static void
+gtk_grid_get_child_property (GtkContainer *container,
+ GtkWidget *child,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkGrid *grid = GTK_GRID (container);
+ GtkGridChild *grid_child;
+
+ grid_child = find_grid_child (grid, child);
+
+ if (grid_child == NULL)
+ {
+ GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
+ return;
+ }
+
+ switch (property_id)
+ {
+ case CHILD_PROP_LEFT_ATTACH:
+ g_value_set_int (value, CHILD_LEFT (grid_child));
+ break;
+
+ case CHILD_PROP_TOP_ATTACH:
+ g_value_set_int (value, CHILD_TOP (grid_child));
+ break;
+
+ case CHILD_PROP_WIDTH:
+ g_value_set_int (value, CHILD_WIDTH (grid_child));
+ break;
+
+ case CHILD_PROP_HEIGHT:
+ g_value_set_int (value, CHILD_HEIGHT (grid_child));
+ break;
+
+ default:
+ GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_grid_set_child_property (GtkContainer *container,
+ GtkWidget *child,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkGrid *grid = GTK_GRID (container);
+ GtkGridChild *grid_child;
+
+ grid_child = find_grid_child (grid, child);
+
+ if (grid_child == NULL)
+ {
+ GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
+ return;
+ }
+
+ switch (property_id)
+ {
+ case CHILD_PROP_LEFT_ATTACH:
+ CHILD_LEFT (grid_child) = g_value_get_int (value);
+ break;
+
+ case CHILD_PROP_TOP_ATTACH:
+ CHILD_TOP (grid_child) = g_value_get_int (value);
+ break;
+
+ case CHILD_PROP_WIDTH:
+ CHILD_WIDTH (grid_child) = g_value_get_int (value);
+ break;
+
+ case CHILD_PROP_HEIGHT:
+ CHILD_HEIGHT (grid_child) = g_value_get_int (value);
+ break;
+
+ default:
+ GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
+ break;
+ }
+
+ if (gtk_widget_get_visible (child) &&
+ gtk_widget_get_visible (GTK_WIDGET (grid)))
+ gtk_widget_queue_resize (child);
+}
+
+static void
+gtk_grid_init (GtkGrid *grid)
+{
+ GtkGridPrivate *priv;
+
+ grid->priv = G_TYPE_INSTANCE_GET_PRIVATE (grid, GTK_TYPE_GRID, GtkGridPrivate);
+ priv = grid->priv;
+
+ gtk_widget_set_has_window (GTK_WIDGET (grid), FALSE);
+ gtk_widget_set_redraw_on_allocate (GTK_WIDGET (grid), FALSE);
+
+ priv->children = NULL;
+ priv->orientation = GTK_ORIENTATION_HORIZONTAL;
+
+ priv->linedata[0].spacing = 0;
+ priv->linedata[1].spacing = 0;
+
+ priv->linedata[0].homogeneous = FALSE;
+ priv->linedata[1].homogeneous = FALSE;
+}
+
+static void grid_attach (GtkGrid *grid,
+ GtkWidget *child,
+ gint left,
+ gint top,
+ gint width,
+ gint height);
+
+static void
+gtk_grid_add (GtkContainer *container,
+ GtkWidget *child)
+{
+ GtkGrid *grid = GTK_GRID (container);
+ GtkGridPrivate *priv = grid->priv;
+ GtkGridChild *grid_child;
+ GtkGridChildAttach *attach;
+ GtkGridChildAttach *opposite;
+ GList *list;
+ gint pos;
+
+ pos = 0;
+ for (list = priv->children; list; list = list->next)
+ {
+ grid_child = list->data;
+
+ attach = &grid_child->attach[priv->orientation];
+ opposite = &grid_child->attach[1 - priv->orientation];
+
+ if (opposite->pos <= 0 && opposite->pos + opposite->span > 0)
+ pos = MAX (pos, attach->pos + attach->span);
+ }
+
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+ grid_attach (grid, child, pos, 0, 1, 1);
+ else
+ grid_attach (grid, child, 0, pos, 1, 1);
+}
+
+static void
+gtk_grid_remove (GtkContainer *container,
+ GtkWidget *child)
+{
+ GtkGrid *grid = GTK_GRID (container);
+ GtkGridPrivate *priv = grid->priv;
+ GtkGridChild *grid_child;
+ GList *list;
+
+ for (list = priv->children; list; list = list->next)
+ {
+ grid_child = list->data;
+
+ if (grid_child->widget == child)
+ {
+ gboolean was_visible = gtk_widget_get_visible (child);
+
+ gtk_widget_unparent (child);
+
+ priv->children = g_list_remove (priv->children, grid_child);
+
+ g_slice_free (GtkGridChild, grid_child);
+
+ if (was_visible && gtk_widget_get_visible (GTK_WIDGET (grid)))
+ gtk_widget_queue_resize (GTK_WIDGET (grid));
+
+ break;
+ }
+ }
+}
+
+static void
+gtk_grid_forall (GtkContainer *container,
+ gboolean include_internals,
+ GtkCallback callback,
+ gpointer callback_data)
+{
+ GtkGrid *grid = GTK_GRID (container);
+ GtkGridPrivate *priv = grid->priv;
+ GtkGridChild *child;
+ GList *list;
+
+ for (list = priv->children; list; list = list->next)
+ {
+ child = list->data;
+
+ (* callback) (child->widget, callback_data);
+ }
+}
+
+static GType
+gtk_grid_child_type (GtkContainer *container)
+{
+ return GTK_TYPE_WIDGET;
+}
+
+static GtkSizeRequestMode
+gtk_grid_get_request_mode (GtkWidget *widget)
+{
+ GtkGridPrivate *priv = GTK_GRID (widget)->priv;
+
+ if (priv->orientation == GTK_ORIENTATION_VERTICAL)
+ return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
+ else
+ return GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT;
+}
+
+/* Calculates the min and max numbers for both orientations.
+ */
+static void
+gtk_grid_request_count_lines (GtkGridRequest *request)
+{
+ GtkGridPrivate *priv = request->grid->priv;
+ GtkGridChild *child;
+ GtkGridChildAttach *attach;
+ GList *list;
+ gint min[2];
+ gint max[2];
+
+ min[0] = min[1] = G_MAXINT;
+ max[0] = max[1] = G_MININT;
+
+ for (list = priv->children; list; list = list->next)
+ {
+ child = list->data;
+ attach = child->attach;
+
+ min[0] = MIN (min[0], attach[0].pos);
+ max[0] = MAX (max[0], attach[0].pos + attach[0].span);
+ min[1] = MIN (min[1], attach[1].pos);
+ max[1] = MAX (max[1], attach[1].pos + attach[1].span);
+ }
+
+ request->lines[0].min = min[0];
+ request->lines[0].max = max[0];
+ request->lines[1].min = min[1];
+ request->lines[1].max = max[1];
+}
+
+/* Sets line sizes to 0 and marks lines as expand
+ * if they have a non-spanning expanding child.
+ */
+static void
+gtk_grid_request_init (GtkGridRequest *request,
+ GtkOrientation orientation)
+{
+ GtkGridPrivate *priv = request->grid->priv;
+ GtkGridChild *child;
+ GtkGridChildAttach *attach;
+ GtkGridLines *lines;
+ GList *list;
+ gint i;
+
+ lines = &request->lines[orientation];
+
+ for (i = 0; i < lines->max - lines->min; i++)
+ {
+ lines->lines[i].minimum = 0;
+ lines->lines[i].natural = 0;
+ lines->lines[i].expand = FALSE;
+ }
+
+ for (list = priv->children; list; list = list->next)
+ {
+ child = list->data;
+
+ attach = &child->attach[orientation];
+ if (attach->span == 1 && gtk_widget_compute_expand (child->widget, orientation))
+ lines->lines[attach->pos - lines->min].expand = TRUE;
+ }
+}
+
+/* Sums allocations for lines spanned by child and their spacing.
+ */
+static gint
+compute_allocation_for_child (GtkGridRequest *request,
+ GtkGridChild *child,
+ GtkOrientation orientation)
+{
+ GtkGridPrivate *priv = request->grid->priv;
+ GtkGridLineData *linedata;
+ GtkGridLines *lines;
+ GtkGridLine *line;
+ GtkGridChildAttach *attach;
+ gint size;
+ gint i;
+
+ linedata = &priv->linedata[orientation];
+ lines = &request->lines[orientation];
+ attach = &child->attach[orientation];
+
+ size = (attach->span - 1) * linedata->spacing;
+ for (i = 0; i < attach->span; i++)
+ {
+ line = &lines->lines[attach->pos - lines->min + i];
+ size += line->allocation;
+ }
+
+ return size;
+}
+
+static void
+compute_request_for_child (GtkGridRequest *request,
+ GtkGridChild *child,
+ GtkOrientation orientation,
+ gboolean contextual,
+ gint *minimum,
+ gint *natural)
+{
+ if (contextual)
+ {
+ gint size;
+
+ size = compute_allocation_for_child (request, child, 1 - orientation);
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ gtk_widget_get_preferred_width_for_height (child->widget,
+ size,
+ minimum, natural);
+ else
+ gtk_widget_get_preferred_height_for_width (child->widget,
+ size,
+ minimum, natural);
+ }
+ else
+ {
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ gtk_widget_get_preferred_width (child->widget, minimum, natural);
+ else
+ gtk_widget_get_preferred_height (child->widget, minimum, natural);
+ }
+}
+
+/* Sets requisition to max. of non-spanning children.
+ * If contextual is TRUE, requires allocations of
+ * lines in the opposite orientation to be set.
+ */
+static void
+gtk_grid_request_non_spanning (GtkGridRequest *request,
+ GtkOrientation orientation,
+ gboolean contextual)
+{
+ GtkGridPrivate *priv = request->grid->priv;
+ GtkGridChild *child;
+ GtkGridChildAttach *attach;
+ GtkGridLines *lines;
+ GtkGridLine *line;
+ GList *list;
+ gint minimum;
+ gint natural;
+
+ lines = &request->lines[orientation];
+
+ for (list = priv->children; list; list = list->next)
+ {
+ child = list->data;
+
+ if (!gtk_widget_get_visible (child->widget))
+ continue;
+
+ attach = &child->attach[orientation];
+ if (attach->span != 1)
+ continue;
+
+ compute_request_for_child (request, child, orientation, contextual, &minimum, &natural);
+
+ line = &lines->lines[attach->pos - lines->min];
+ line->minimum = MAX (line->minimum, minimum);
+ line->natural = MAX (line->natural, natural);
+ }
+}
+
+/* Enforce homogeneous sizes.
+ */
+static void
+gtk_grid_request_homogeneous (GtkGridRequest *request,
+ GtkOrientation orientation)
+{
+ GtkGridPrivate *priv = request->grid->priv;
+ GtkGridLineData *linedata;
+ GtkGridLines *lines;
+ gint minimum, natural;
+ gint i;
+
+ linedata = &priv->linedata[orientation];
+ lines = &request->lines[orientation];
+
+ if (!linedata->homogeneous)
+ return;
+
+ minimum = 0;
+ natural = 0;
+
+ for (i = 0; i < lines->max - lines->min; i++)
+ {
+ minimum = MAX (minimum, lines->lines[i].minimum);
+ natural = MAX (natural, lines->lines[i].natural);
+ }
+
+ for (i = 0; i < lines->max - lines->min; i++)
+ {
+ lines->lines[i].minimum = minimum;
+ lines->lines[i].natural = natural;
+ }
+}
+
+/* Deals with spanning children.
+ * Requires expand fields of lines to be set for
+ * non-spanning children.
+ */
+static void
+gtk_grid_request_spanning (GtkGridRequest *request,
+ GtkOrientation orientation,
+ gboolean contextual)
+{
+ GtkGridPrivate *priv = request->grid->priv;
+ GList *list;
+ GtkGridChild *child;
+ GtkGridChildAttach *attach;
+ GtkGridLineData *linedata;
+ GtkGridLines *lines;
+ GtkGridLine *line;
+ gint minimum;
+ gint natural;
+ gint span_minimum;
+ gint span_natural;
+ gint span_expand;
+ gboolean force_expand;
+ gint extra;
+ gint expand;
+ gint line_extra;
+ gint i;
+
+ linedata = &priv->linedata[orientation];
+ lines = &request->lines[orientation];
+
+ for (list = priv->children; list; list = list->next)
+ {
+ child = list->data;
+
+ if (!gtk_widget_get_visible (child->widget))
+ continue;
+
+ attach = &child->attach[orientation];
+ if (attach->span == 1)
+ continue;
+
+ compute_request_for_child (request, child, orientation, contextual, &minimum, &natural);
+
+ span_minimum = (attach->span - 1) * linedata->spacing;
+ span_natural = (attach->span - 1) * linedata->spacing;
+ span_expand = 0;
+ force_expand = FALSE;
+ for (i = 0; i < attach->span; i++)
+ {
+ line = &lines->lines[attach->pos - lines->min + i];
+ span_minimum += line->minimum;
+ span_natural += line->natural;
+ if (line->expand)
+ span_expand += 1;
+ }
+ if (span_expand == 0)
+ {
+ span_expand = attach->span;
+ force_expand = TRUE;
+ }
+
+ /* If we need to request more space for this child to fill
+ * its requisition, then divide up the needed space amongst the
+ * lines it spans, favoring expandable lines if any.
+ */
+ if (span_minimum < minimum)
+ {
+ extra = minimum - span_minimum;
+ expand = span_expand;
+ for (i = 0; i < attach->span; i++)
+ {
+ line = &lines->lines[attach->pos - lines->min + i];
+ if (force_expand || line->expand)
+ {
+ line_extra = extra / expand;
+ line->minimum += line_extra;
+ extra -= line_extra;
+ expand -= 1;
+ }
+ }
+ }
+
+ if (span_natural < natural)
+ {
+ extra = natural - span_natural;
+ expand = span_expand;
+ for (i = 0; i < attach->span; i++)
+ {
+ line = &lines->lines[attach->pos - lines->min + i];
+ if (force_expand || line->expand)
+ {
+ line_extra = extra / expand;
+ line->natural += line_extra;
+ extra -= line_extra;
+ expand -= 1;
+ }
+ }
+ }
+ }
+}
+
+/* Marks empty and expanding lines and counts them.
+ */
+static void
+gtk_grid_request_compute_expand (GtkGridRequest *request,
+ GtkOrientation orientation,
+ gint *nonempty_lines,
+ gint *expand_lines)
+{
+ GtkGridPrivate *priv = request->grid->priv;
+ GtkGridChild *child;
+ GtkGridChildAttach *attach;
+ GList *list;
+ gint i;
+ GtkGridLines *lines;
+ GtkGridLine *line;
+ gboolean has_expand;
+ gint expand;
+ gint empty;
+
+ lines = &request->lines[orientation];
+
+ for (i = 0; i < lines->max - lines->min; i++)
+ {
+ lines->lines[i].need_expand = FALSE;
+ lines->lines[i].expand = FALSE;
+ lines->lines[i].empty = TRUE;
+ }
+
+ for (list = priv->children; list; list = list->next)
+ {
+ child = list->data;
+
+ if (!gtk_widget_get_visible (child->widget))
+ continue;
+
+ attach = &child->attach[orientation];
+ if (attach->span != 1)
+ continue;
+
+ line = &lines->lines[attach->pos - lines->min];
+ line->empty = FALSE;
+ if (gtk_widget_compute_expand (child->widget, orientation))
+ line->expand = TRUE;
+ }
+
+ for (list = priv->children; list; list = list->next)
+ {
+ child = list->data;
+
+ if (!gtk_widget_get_visible (child->widget))
+ continue;
+
+ attach = &child->attach[orientation];
+ if (attach->span == 1)
+ continue;
+
+ has_expand = FALSE;
+ for (i = 0; i < attach->span; i++)
+ {
+ line = &lines->lines[attach->pos - lines->min + i];
+ line->empty = FALSE;
+ if (line->expand)
+ has_expand = TRUE;
+ }
+
+ if (!has_expand && gtk_widget_compute_expand (child->widget, orientation))
+ {
+ for (i = 0; i < attach->span; i++)
+ {
+ line = &lines->lines[attach->pos - lines->min + i];
+ line->need_expand = TRUE;
+ }
+ }
+ }
+
+ empty = 0;
+ expand = 0;
+ for (i = 0; i < lines->max - lines->min; i++)
+ {
+ line = &lines->lines[i];
+
+ if (line->need_expand)
+ line->expand = TRUE;
+
+ if (line->empty)
+ empty += 1;
+
+ if (line->expand)
+ expand += 1;
+ }
+
+ if (nonempty_lines)
+ *nonempty_lines = lines->max - lines->min - empty;
+
+ if (expand_lines)
+ *expand_lines = expand;
+}
+
+/* Sums the minimum and natural fields of lines and their spacing.
+ */
+static void
+gtk_grid_request_sum (GtkGridRequest *request,
+ GtkOrientation orientation,
+ gint *minimum,
+ gint *natural)
+{
+ GtkGridPrivate *priv = request->grid->priv;
+ GtkGridLineData *linedata;
+ GtkGridLines *lines;
+ gint i;
+ gint min, nat;
+ gint nonempty;
+
+ gtk_grid_request_compute_expand (request, orientation, &nonempty, NULL);
+
+ linedata = &priv->linedata[orientation];
+ lines = &request->lines[orientation];
+
+ min = (nonempty - 1) * linedata->spacing;
+ nat = (nonempty - 1) * linedata->spacing;
+
+ for (i = 0; i < lines->max - lines->min; i++)
+ {
+ min += lines->lines[i].minimum;
+ nat += lines->lines[i].natural;
+ }
+
+ if (minimum)
+ *minimum = min;
+
+ if (natural)
+ *natural = nat;
+}
+
+/* Computes minimum and natural fields of lines.
+ * When contextual is TRUE, requires allocation of
+ * lines in the opposite orientation to be set.
+ */
+static void
+gtk_grid_request_run (GtkGridRequest *request,
+ GtkOrientation orientation,
+ gboolean contextual)
+{
+ gtk_grid_request_init (request, orientation);
+ gtk_grid_request_non_spanning (request, orientation, contextual);
+ gtk_grid_request_homogeneous (request, orientation);
+ gtk_grid_request_spanning (request, orientation, contextual);
+ gtk_grid_request_homogeneous (request, orientation);
+}
+
+/* Requires that the minimum and natural fields of lines
+ * have been set, computes the allocation field of lines
+ * by distributing total_size among lines.
+ */
+static void
+gtk_grid_request_allocate (GtkGridRequest *request,
+ GtkOrientation orientation,
+ gint total_size)
+{
+ GtkGridPrivate *priv = request->grid->priv;
+ GtkGridLineData *linedata;
+ GtkGridLines *lines;
+ GtkGridLine *line;
+ gint nonempty;
+ gint expand;
+ gint i, j;
+ GtkRequestedSize *sizes;
+ gint extra;
+ gint rest;
+ gint size;
+
+ gtk_grid_request_compute_expand (request, orientation, &nonempty, &expand);
+
+ linedata = &priv->linedata[orientation];
+ lines = &request->lines[orientation];
+
+ size = total_size - (nonempty - 1) * linedata->spacing;
+
+ if (linedata->homogeneous)
+ {
+ extra = size / nonempty;
+ rest = size % nonempty;
+
+ for (i = 0; i < lines->max - lines->min; i++)
+ {
+ line = &lines->lines[i];
+ if (line->empty)
+ continue;
+
+ line->allocation = extra;
+ if (rest > 0)
+ {
+ line->allocation += 1;
+ rest -= 1;
+ }
+ }
+ }
+ else
+ {
+ sizes = g_newa (GtkRequestedSize, nonempty);
+
+ j = 0;
+ for (i = 0; i < lines->max - lines->min; i++)
+ {
+ line = &lines->lines[i];
+ if (line->empty)
+ continue;
+
+ size -= line->minimum;
+
+ sizes[j].minimum_size = line->minimum;
+ sizes[j].natural_size = line->natural;
+ sizes[j].data = line;
+ j++;
+ }
+
+ size = gtk_distribute_natural_allocation (MAX (0, size), nonempty, sizes);
+
+ if (expand > 0)
+ {
+ extra = size / expand;
+ rest = size % expand;
+ }
+ else
+ {
+ extra = 0;
+ rest = 0;
+ }
+
+ j = 0;
+ for (i = 0; i < lines->max - lines->min; i++)
+ {
+ line = &lines->lines[i];
+ if (line->empty)
+ continue;
+
+ g_assert (line == sizes[j].data);
+
+ line->allocation = sizes[j].minimum_size;
+ if (line->expand)
+ {
+ line->allocation += extra;
+ if (rest > 0)
+ {
+ line->allocation += 1;
+ rest -= 1;
+ }
+ }
+
+ j++;
+ }
+ }
+}
+
+/* Computes the position fields from allocation and spacing.
+ */
+static void
+gtk_grid_request_position (GtkGridRequest *request,
+ GtkOrientation orientation)
+{
+ GtkGridPrivate *priv = request->grid->priv;
+ GtkGridLineData *linedata;
+ GtkGridLines *lines;
+ GtkGridLine *line;
+ gint position;
+ gint i;
+
+ linedata = &priv->linedata[orientation];
+ lines = &request->lines[orientation];
+
+ position = 0;
+ for (i = 0; i < lines->max - lines->min; i++)
+ {
+ line = &lines->lines[i];
+ if (!line->empty)
+ {
+ line->position = position;
+ position += line->allocation + linedata->spacing;
+ }
+ }
+}
+
+static void
+gtk_grid_get_size (GtkGrid *grid,
+ GtkOrientation orientation,
+ gint *minimum,
+ gint *natural)
+{
+ GtkGridRequest request;
+ GtkGridLines *lines;
+
+ request.grid = grid;
+ gtk_grid_request_count_lines (&request);
+ lines = &request.lines[orientation];
+ lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
+
+ gtk_grid_request_run (&request, orientation, FALSE);
+ gtk_grid_request_sum (&request, orientation, minimum, natural);
+}
+
+static void
+gtk_grid_get_size_for_size (GtkGrid *grid,
+ GtkOrientation orientation,
+ gint size,
+ gint *minimum,
+ gint *natural)
+{
+ GtkGridRequest request;
+ GtkGridLines *lines;
+ gint min_size;
+
+ request.grid = grid;
+ gtk_grid_request_count_lines (&request);
+ lines = &request.lines[0];
+ lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
+ lines = &request.lines[1];
+ lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
+
+ gtk_grid_request_run (&request, 1 - orientation, FALSE);
+ gtk_grid_request_sum (&request, 1 - orientation, &min_size, NULL);
+ gtk_grid_request_allocate (&request, 1 - orientation, MAX (size, min_size));
+
+ gtk_grid_request_run (&request, orientation, TRUE);
+ gtk_grid_request_sum (&request, orientation, minimum, natural);
+}
+
+static void
+gtk_grid_get_preferred_width (GtkWidget *widget,
+ gint *minimum,
+ gint *natural)
+{
+ GtkGrid *grid = GTK_GRID (widget);
+
+ if (gtk_grid_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
+
+ gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_HORIZONTAL, 0, minimum, natural);
+ else
+ gtk_grid_get_size (grid, GTK_ORIENTATION_HORIZONTAL, minimum, natural);
+}
+
+static void
+gtk_grid_get_preferred_height (GtkWidget *widget,
+ gint *minimum,
+ gint *natural)
+{
+ GtkGrid *grid = GTK_GRID (widget);
+
+ if (gtk_grid_get_request_mode (widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
+ gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_VERTICAL, 0, minimum, natural);
+ else
+ gtk_grid_get_size (grid, GTK_ORIENTATION_VERTICAL, minimum, natural);
+}
+
+static void
+gtk_grid_get_preferred_width_for_height (GtkWidget *widget,
+ gint height,
+ gint *minimum,
+ gint *natural)
+{
+ GtkGrid *grid = GTK_GRID (widget);
+
+ if (gtk_grid_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
+ gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_HORIZONTAL, height, minimum, natural);
+ else
+ gtk_grid_get_size (grid, GTK_ORIENTATION_HORIZONTAL, minimum, natural);
+}
+
+static void
+gtk_grid_get_preferred_height_for_width (GtkWidget *widget,
+ gint width,
+ gint *minimum,
+ gint *natural)
+{
+ GtkGrid *grid = GTK_GRID (widget);
+
+ if (gtk_grid_get_request_mode (widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
+ gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_VERTICAL, width, minimum, natural);
+ else
+ gtk_grid_get_size (grid, GTK_ORIENTATION_VERTICAL, minimum, natural);
+}
+
+static void
+allocate_child (GtkGridRequest *request,
+ GtkOrientation orientation,
+ GtkGridChild *child,
+ gint *position,
+ gint *size)
+{
+ GtkGridPrivate *priv = request->grid->priv;
+ GtkGridLineData *linedata;
+ GtkGridLines *lines;
+ GtkGridLine *line;
+ GtkGridChildAttach *attach;
+ gint i;
+
+ linedata = &priv->linedata[orientation];
+ lines = &request->lines[orientation];
+ attach = &child->attach[orientation];
+
+ *position = lines->lines[attach->pos - lines->min].position;
+
+ *size = (attach->span - 1) * linedata->spacing;
+ for (i = 0; i < attach->span; i++)
+ {
+ line = &lines->lines[attach->pos - lines->min + i];
+ *size += line->allocation;
+ }
+}
+
+static void
+gtk_grid_request_allocate_children (GtkGridRequest *request)
+{
+ GtkGridPrivate *priv = request->grid->priv;
+ GList *list;
+ GtkGridChild *child;
+ GtkAllocation allocation;
+ GtkAllocation child_allocation;
+ gint x, y, width, height;
+
+ gtk_widget_get_allocation (GTK_WIDGET (request->grid), &allocation);
+
+ for (list = priv->children; list; list = list->next)
+ {
+ child = list->data;
+
+ if (!gtk_widget_get_visible (child->widget))
+ continue;
+
+ allocate_child (request, GTK_ORIENTATION_HORIZONTAL, child, &x, &width);
+ allocate_child (request, GTK_ORIENTATION_VERTICAL, child, &y, &height);
+
+ child_allocation.x = allocation.x + x;
+ child_allocation.y = allocation.y + y;
+ child_allocation.width = MAX (1, width);
+ child_allocation.height = MAX (1, height);
+
+ gtk_widget_size_allocate (child->widget, &child_allocation);
+ }
+}
+
+#define GET_SIZE(allocation, orientation) (orientation == GTK_ORIENTATION_HORIZONTAL ? allocation->width : allocation->height)
+
+static void
+gtk_grid_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GtkGrid *grid = GTK_GRID (widget);
+ GtkGridPrivate *priv = grid->priv;
+ GtkGridRequest request;
+ GtkGridLines *lines;
+
+ request.grid = grid;
+ gtk_grid_request_count_lines (&request);
+ lines = &request.lines[0];
+ lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
+ lines = &request.lines[1];
+ lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
+
+ gtk_widget_set_allocation (widget, allocation);
+
+ gtk_grid_request_run (&request, 1 - priv->orientation, FALSE);
+ gtk_grid_request_allocate (&request, 1 - priv->orientation, GET_SIZE (allocation, 1 - priv->orientation));
+ gtk_grid_request_run (&request, priv->orientation, TRUE);
+ gtk_grid_request_allocate (&request, priv->orientation, GET_SIZE (allocation, priv->orientation));
+
+ gtk_grid_request_position (&request, 0);
+ gtk_grid_request_position (&request, 1);
+
+ gtk_grid_request_allocate_children (&request);
+}
+
+static void
+gtk_grid_class_init (GtkGridClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+ GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
+
+ object_class->get_property = gtk_grid_get_property;
+ object_class->set_property = gtk_grid_set_property;
+
+ widget_class->size_allocate = gtk_grid_size_allocate;
+ widget_class->get_preferred_width = gtk_grid_get_preferred_width;
+ widget_class->get_preferred_height = gtk_grid_get_preferred_height;
+ widget_class->get_request_mode = gtk_grid_get_request_mode;
+ widget_class->get_preferred_width_for_height = gtk_grid_get_preferred_width_for_height;
+ widget_class->get_preferred_height_for_width = gtk_grid_get_preferred_height_for_width;
+
+ container_class->add = gtk_grid_add;
+ container_class->remove = gtk_grid_remove;
+ container_class->forall = gtk_grid_forall;
+ container_class->child_type = gtk_grid_child_type;
+ container_class->set_child_property = gtk_grid_set_child_property;
+ container_class->get_child_property = gtk_grid_get_child_property;
+ gtk_container_class_handle_border_width (container_class);
+
+ g_object_class_override_property (object_class, PROP_ORIENTATION, "orientation");
+
+ g_object_class_install_property (object_class, PROP_ROW_SPACING,
+ g_param_spec_int ("row-spacing",
+ P_("Row spacing"),
+ P_("The amount of space between two consecutive rows"),
+ 0, G_MAXINT16, 0,
+ GTK_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_COLUMN_SPACING,
+ g_param_spec_int ("column-spacing",
+ P_("Column spacing"),
+ P_("The amount of space between two consecutive columns"),
+ 0, G_MAXINT16, 0,
+ GTK_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_ROW_HOMOGENEOUS,
+ g_param_spec_boolean ("row-homogeneous",
+ P_("Row Homogeneous"),
+ P_("If TRUE, the rows are all the same height"),
+ FALSE,
+ GTK_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_COLUMN_HOMOGENEOUS,
+ g_param_spec_boolean ("column-homogeneous",
+ P_("Column Homogeneous"),
+ P_("If TRUE, the columns are all the same width"),
+ FALSE,
+ GTK_PARAM_READWRITE));
+
+ gtk_container_class_install_child_property (container_class, CHILD_PROP_LEFT_ATTACH,
+ g_param_spec_int ("left-attach",
+ P_("Left attachment"),
+ P_("The column number to attach the left side of the child to"),
+ G_MININT, G_MAXINT, 0,
+ GTK_PARAM_READWRITE));
+
+ gtk_container_class_install_child_property (container_class, CHILD_PROP_TOP_ATTACH,
+ g_param_spec_int ("top-attach",
+ P_("Top attachment"),
+ P_("The row number to attach the top side of a child widget to"),
+ G_MININT, G_MAXINT, 0,
+ GTK_PARAM_READWRITE));
+
+ gtk_container_class_install_child_property (container_class, CHILD_PROP_WIDTH,
+ g_param_spec_int ("width",
+ P_("Width"),
+ P_("The number of columns that a child spans"),
+ 1, G_MAXINT, 1,
+ GTK_PARAM_READWRITE));
+
+ gtk_container_class_install_child_property (container_class, CHILD_PROP_HEIGHT,
+ g_param_spec_int ("height",
+ P_("Height"),
+ P_("The number of rows that a child spans"),
+ 1, G_MAXINT, 1,
+ GTK_PARAM_READWRITE));
+
+ g_type_class_add_private (class, sizeof (GtkGridPrivate));
+}
+
+/**
+ * gtk_grid_new:
+ *
+ * Creates a new grid widget.
+ *
+ * Returns: the new #GtkGrid
+ */
+GtkWidget *
+gtk_grid_new (void)
+{
+ return g_object_new (GTK_TYPE_GRID, NULL);
+}
+
+static void
+grid_attach (GtkGrid *grid,
+ GtkWidget *widget,
+ gint left,
+ gint top,
+ gint width,
+ gint height)
+{
+ GtkGridPrivate *priv = grid->priv;
+ GtkGridChild *child;
+
+ child = g_slice_new (GtkGridChild);
+ child->widget = widget;
+ CHILD_LEFT (child) = left;
+ CHILD_TOP (child) = top;
+ CHILD_WIDTH (child) = width;
+ CHILD_HEIGHT (child) = height;
+
+ priv->children = g_list_prepend (priv->children, child);
+
+ gtk_widget_set_parent (widget, GTK_WIDGET (grid));
+}
+
+/**
+ * gtk_grid_attach:
+ * @grid: a #GtkGrid
+ * @child: the widget to add
+ * @left: the column number to attach the left side of @child to
+ * @top: the row number to attach the top side of @child to
+ * @width: the number of columns that @child will span
+ * @height: the number of rows that @child will span
+ *
+ * Adds a widget to the grid.
+ *
+ * The position of @child is determined by @left and @top. The
+ * number of 'cells' that @child will occupy is determined by
+ * @width and @height.
+ */
+void
+gtk_grid_attach (GtkGrid *grid,
+ GtkWidget *child,
+ gint left,
+ gint top,
+ gint width,
+ gint height)
+{
+ g_return_if_fail (GTK_IS_GRID (grid));
+ g_return_if_fail (GTK_IS_WIDGET (child));
+ g_return_if_fail (gtk_widget_get_parent (child) == NULL);
+ g_return_if_fail (width > 0);
+ g_return_if_fail (height > 0);
+
+ grid_attach (grid, child, left, top, width, height);
+}
+
+/**
+ * gtk_grid_attach_next_to:
+ * @grid: a #GtkGrid
+ * @child: the widget to add
+ * @sibling: the child of @grid that @child will be placed next to
+ * @side: the side of @sibling that @child is positioned next to
+ * @width: the number of columns that @child will span
+ * @height: the number of rows that @child will span
+ *
+ * Adds a widget to the grid.
+ *
+ * The widget is placed next to @sibling, on the side determined by
+ * @side.
+ */
+void
+gtk_grid_attach_next_to (GtkGrid *grid,
+ GtkWidget *child,
+ GtkWidget *sibling,
+ GtkPositionType side,
+ gint width,
+ gint height)
+{
+ GtkGridChild *grid_sibling;
+ gint left, top;
+
+ g_return_if_fail (GTK_IS_GRID (grid));
+ g_return_if_fail (GTK_IS_WIDGET (child));
+ g_return_if_fail (gtk_widget_get_parent (child) == NULL);
+ g_return_if_fail (gtk_widget_get_parent (sibling) == (GtkWidget*)grid);
+ g_return_if_fail (width > 0);
+ g_return_if_fail (height > 0);
+
+ grid_sibling = find_grid_child (grid, sibling);
+
+ switch (side)
+ {
+ case GTK_POS_LEFT:
+ left = CHILD_LEFT (grid_sibling) - width;
+ top = CHILD_TOP (grid_sibling);
+ break;
+ case GTK_POS_RIGHT:
+ left = CHILD_LEFT (grid_sibling) + CHILD_WIDTH (grid_sibling);
+ top = CHILD_TOP (grid_sibling);
+ break;
+ case GTK_POS_TOP:
+ left = CHILD_LEFT (grid_sibling);
+ top = CHILD_TOP (grid_sibling) - height;
+ break;
+ case GTK_POS_BOTTOM:
+ left = CHILD_LEFT (grid_sibling);
+ top = CHILD_TOP (grid_sibling) + CHILD_HEIGHT (grid_sibling);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ grid_attach (grid, child, left, top, width, height);
+}
+
+/**
+ * gtk_grid_set_row_homogeneous:
+ * @grid: a #GtkGrid
+ * @homogeneous: %TRUE to make rows homogeneous
+ *
+ * Sets whether all rows of @grid will have the same height.
+ */
+void
+gtk_grid_set_row_homogeneous (GtkGrid *grid,
+ gboolean homogeneous)
+{
+ GtkGridPrivate *priv;
+ g_return_if_fail (GTK_IS_GRID (grid));
+
+ priv = grid->priv;
+
+ if (ROWS (priv)->homogeneous != homogeneous)
+ {
+ ROWS (priv)->homogeneous = homogeneous;
+
+ if (gtk_widget_get_visible (GTK_WIDGET (grid)))
+ gtk_widget_queue_resize (GTK_WIDGET (grid));
+
+ g_object_notify (G_OBJECT (grid), "row-homogeneous");
+ }
+}
+
+/**
+ * gtk_grid_get_row_homogeneous:
+ * @grid: a #GtkGrid
+ *
+ * Returns whether all rows of @grid have the same height.
+ *
+ * Returns: whether all rows of @grid have the same height.
+ */
+gboolean
+gtk_grid_get_row_homogeneous (GtkGrid *grid)
+{
+ GtkGridPrivate *priv;
+ g_return_val_if_fail (GTK_IS_GRID (grid), FALSE);
+
+ priv = grid->priv;
+
+ return ROWS (priv)->homogeneous;
+}
+
+/**
+ * gtk_grid_set_column_homogeneous:
+ * @grid: a #GtkGrid
+ * @homogeneous: %TRUE to make columns homogeneous
+ *
+ * Sets whether all columns of @grid will have the same width.
+ */
+void
+gtk_grid_set_column_homogeneous (GtkGrid *grid,
+ gboolean homogeneous)
+{
+ GtkGridPrivate *priv;
+ g_return_if_fail (GTK_IS_GRID (grid));
+
+ priv = grid->priv;
+
+ if (COLUMNS (priv)->homogeneous != homogeneous)
+ {
+ COLUMNS (priv)->homogeneous = homogeneous;
+
+ if (gtk_widget_get_visible (GTK_WIDGET (grid)))
+ gtk_widget_queue_resize (GTK_WIDGET (grid));
+
+ g_object_notify (G_OBJECT (grid), "column-homogeneous");
+ }
+}
+
+/**
+ * gtk_grid_get_column_homogeneous:
+ * @grid: a #GtkGrid
+ *
+ * Returns whether all columns of @grid have the same width.
+ *
+ * Returns: whether all columns of @grid have the same width.
+ */
+gboolean
+gtk_grid_get_column_homogeneous (GtkGrid *grid)
+{
+ GtkGridPrivate *priv;
+ g_return_val_if_fail (GTK_IS_GRID (grid), FALSE);
+
+ priv = grid->priv;
+
+ return COLUMNS (priv)->homogeneous;
+}
+
+/**
+ * gtk_grid_set_row_spacing:
+ * @grid: a #GtkGrid
+ * @spacing: the amount of space to insert between rows
+ *
+ * Sets the amount of space between rows of @grid.
+ */
+void
+gtk_grid_set_row_spacing (GtkGrid *grid,
+ guint spacing)
+{
+ GtkGridPrivate *priv;
+ g_return_if_fail (GTK_IS_GRID (grid));
+ g_return_if_fail (spacing <= G_MAXINT16);
+
+ priv = grid->priv;
+
+ if (ROWS (priv)->spacing != spacing)
+ {
+ ROWS (priv)->spacing = spacing;
+
+ if (gtk_widget_get_visible (GTK_WIDGET (grid)))
+ gtk_widget_queue_resize (GTK_WIDGET (grid));
+
+ g_object_notify (G_OBJECT (grid), "row-spacing");
+ }
+}
+
+/**
+ * gtk_grid_get_row_spacing:
+ * @grid: a #GtkGrid
+ *
+ * Returns the amount of space between the rows of @grid.
+ *
+ * Returns: the row spacing of @grid
+ */
+guint
+gtk_grid_get_row_spacing (GtkGrid *grid)
+{
+ GtkGridPrivate *priv;
+ g_return_val_if_fail (GTK_IS_GRID (grid), 0);
+
+ priv = grid->priv;
+
+ return ROWS (priv)->spacing;
+}
+
+/**
+ * gtk_grid_set_column_spacing:
+ * @grid: a #GtkGrid
+ * @spacing: the amount of space to insert between columns
+ *
+ * Sets the amount of space between columns of @grid.
+ */
+void
+gtk_grid_set_column_spacing (GtkGrid *grid,
+ guint spacing)
+{
+ GtkGridPrivate *priv;
+ g_return_if_fail (GTK_IS_GRID (grid));
+ g_return_if_fail (spacing <= G_MAXINT16);
+
+ priv = grid->priv;
+
+ if (COLUMNS (priv)->spacing != spacing)
+ {
+ COLUMNS (priv)->spacing = spacing;
+
+ if (gtk_widget_get_visible (GTK_WIDGET (grid)))
+ gtk_widget_queue_resize (GTK_WIDGET (grid));
+
+ g_object_notify (G_OBJECT (grid), "column-spacing");
+ }
+}
+
+/**
+ * gtk_grid_get_column_spacing:
+ * @grid: a #GtkGrid
+ *
+ * Returns the amount of space between the columns of @grid.
+ *
+ * Returns: the column spacing of @grid
+ */
+guint
+gtk_grid_get_column_spacing (GtkGrid *grid)
+{
+ GtkGridPrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_GRID (grid), 0);
+
+ priv = grid->priv;
+
+ return COLUMNS (priv)->spacing;
+}
diff --git a/gtk/gtkgrid.h b/gtk/gtkgrid.h
new file mode 100644
index 0000000..590d9cd
--- /dev/null
+++ b/gtk/gtkgrid.h
@@ -0,0 +1,98 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2010 Red Hat, Inc.
+ * Author: Matthias Clasen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#ifndef __GTK_GRID_H__
+#define __GTK_GRID_H__
+
+
+#include <gtk/gtkcontainer.h>
+
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_GRID (gtk_grid_get_type ())
+#define GTK_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_GRID, GtkGrid))
+#define GTK_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_GRID, GtkGridClass))
+#define GTK_IS_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_GRID))
+#define GTK_IS_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_GRID))
+#define GTK_GRID_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_GRID, GtkGridClass))
+
+
+typedef struct _GtkGrid GtkGrid;
+typedef struct _GtkGridPrivate GtkGridPrivate;
+typedef struct _GtkGridClass GtkGridClass;
+
+struct _GtkGrid
+{
+ /* <private> */
+ GtkContainer container;
+
+ GtkGridPrivate *priv;
+};
+
+struct _GtkGridClass
+{
+ GtkContainerClass parent_class;
+
+ void (*_gtk_reserved1) (void);
+ void (*_gtk_reserved2) (void);
+ void (*_gtk_reserved3) (void);
+ void (*_gtk_reserved4) (void);
+ void (*_gtk_reserved5) (void);
+ void (*_gtk_reserved6) (void);
+ void (*_gtk_reserved7) (void);
+ void (*_gtk_reserved8) (void);
+};
+
+GType gtk_grid_get_type (void) G_GNUC_CONST;
+GtkWidget* gtk_grid_new (void);
+void gtk_grid_attach (GtkGrid *grid,
+ GtkWidget *child,
+ gint left,
+ gint top,
+ gint width,
+ gint height);
+void gtk_grid_attach_next_to (GtkGrid *grid,
+ GtkWidget *widget,
+ GtkWidget *sibling,
+ GtkPositionType side,
+ gint width,
+ gint height);
+void gtk_grid_set_row_homogeneous (GtkGrid *grid,
+ gboolean homogeneous);
+gboolean gtk_grid_get_row_homogeneous (GtkGrid *grid);
+void gtk_grid_set_row_spacing (GtkGrid *grid,
+ guint spacing);
+guint gtk_grid_get_row_spacing (GtkGrid *grid);
+void gtk_grid_set_column_homogeneous (GtkGrid *grid,
+ gboolean homogeneous);
+gboolean gtk_grid_get_column_homogeneous (GtkGrid *grid);
+void gtk_grid_set_column_spacing (GtkGrid *grid,
+ guint spacing);
+guint gtk_grid_get_column_spacing (GtkGrid *grid);
+
+
+G_END_DECLS
+
+#endif /* __GTK_GRID_H__ */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 455bf4b..6132dc8 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -48,6 +48,7 @@ noinst_PROGRAMS = $(TEST_PROGS) \
testfilechooserbutton \
testframe \
testgeometry \
+ testgrid \
testgtk \
testheightforwidth \
testiconview \
@@ -137,6 +138,7 @@ testfilechooser_DEPENDENCIES = $(TEST_DEPS)
testfilechooserbutton_DEPENDENCIES = $(TEST_DEPS)
testframe_DEPENDENCIES = $(TEST_DEPS)
testgeometry_DEPENDENCIES = $(TEST_DEPS)
+testgrid_DEPENDENCIES = $(TEST_DEPS)
testgtk_DEPENDENCIES = $(TEST_DEPS)
testinput_DEPENDENCIES = $(TEST_DEPS)
testimage_DEPENDENCIES = $(TEST_DEPS)
@@ -202,6 +204,7 @@ testfilechooser_LDADD = $(LDADDS)
testfilechooserbutton_LDADD = $(LDADDS)
testframe_LDADD = $(LDADDS)
testgeometry_LDADD = $(LDADDS)
+testgrid_LDADD = $(LDADDS)
testgtk_LDADD = $(LDADDS)
testheightforwidth_LDADD = $(LDADDS)
testicontheme_LDADD = $(LDADDS)
@@ -266,6 +269,9 @@ testfilechooserbutton_SOURCES = \
prop-editor.c \
testfilechooserbutton.c
+testgrid_SOURCES = \
+ testgrid.c
+
testgtk_SOURCES = \
prop-editor.c \
testgtk.c
diff --git a/tests/testgrid.c b/tests/testgrid.c
new file mode 100644
index 0000000..76d1a8c
--- /dev/null
+++ b/tests/testgrid.c
@@ -0,0 +1,264 @@
+#include <gtk/gtk.h>
+
+static GtkWidget *
+oriented_test_widget (const gchar *label, const gchar *color, gdouble angle)
+{
+ GtkWidget *box;
+ GtkWidget *widget;
+ GdkColor c;
+
+ widget = gtk_label_new (label);
+ gtk_label_set_angle (GTK_LABEL (widget), angle);
+ box = gtk_event_box_new ();
+ gdk_color_parse (color, &c);
+ gtk_widget_modify_bg (box, GTK_STATE_NORMAL, &c);
+ gtk_container_add (GTK_CONTAINER (box), widget);
+
+ return box;
+}
+
+static GtkWidget *
+test_widget (const gchar *label, const gchar *color)
+{
+ return oriented_test_widget (label, color, 0.0);
+}
+
+static GtkOrientation o;
+
+static gboolean
+toggle_orientation (GtkWidget *window, GdkEventButton *event, GtkGrid *grid)
+{
+ o = 1 - o;
+
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (grid), o);
+
+ return FALSE;
+}
+
+static void
+simple_grid (void)
+{
+ GtkWidget *window;
+ GtkWidget *grid;
+ GtkWidget *child;
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (window), "Orientation");
+ grid = gtk_grid_new ();
+ gtk_container_add (GTK_CONTAINER (window), grid);
+ g_signal_connect (window, "button-press-event", G_CALLBACK (toggle_orientation), grid);
+
+ gtk_grid_set_column_spacing (GTK_GRID (grid), 5);
+ gtk_grid_set_row_spacing (GTK_GRID (grid), 5);
+ gtk_container_add (GTK_CONTAINER (grid), test_widget ("1", "red"));
+ gtk_container_add (GTK_CONTAINER (grid), test_widget ("2", "green"));
+ gtk_container_add (GTK_CONTAINER (grid), test_widget ("3", "blue"));
+ child = test_widget ("4", "green");
+ gtk_grid_attach (GTK_GRID (grid), child, 0, 1, 1, 1);
+ gtk_widget_set_vexpand (child, TRUE);
+ gtk_grid_attach_next_to (GTK_GRID (grid), test_widget ("5", "blue"), child, GTK_POS_RIGHT, 2, 1);
+ child = test_widget ("6", "yellow");
+ gtk_grid_attach (GTK_GRID (grid), child, -1, 0, 1, 2);
+ gtk_widget_set_hexpand (child, TRUE);
+
+ gtk_widget_show_all (window);
+}
+
+static void
+text_grid (void)
+{
+ GtkWidget *window;
+ GtkWidget *grid;
+ GtkWidget *paned1;
+ GtkWidget *box;
+ GtkWidget *label;
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (window), "Height-for-Width");
+ paned1 = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
+ gtk_container_add (GTK_CONTAINER (window), paned1);
+
+ box = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 0);
+ gtk_paned_pack1 (GTK_PANED (paned1), box, TRUE, FALSE);
+ gtk_paned_pack2 (GTK_PANED (paned1), gtk_label_new ("Space"), TRUE, FALSE);
+
+ grid = gtk_grid_new ();
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (grid), GTK_ORIENTATION_VERTICAL);
+ gtk_container_add (GTK_CONTAINER (box), gtk_label_new ("Above"));
+ gtk_container_add (GTK_CONTAINER (box), gtk_separator_new (GTK_ORIENTATION_HORIZONTAL));
+ gtk_container_add (GTK_CONTAINER (box), grid);
+ gtk_container_add (GTK_CONTAINER (box), gtk_separator_new (GTK_ORIENTATION_HORIZONTAL));
+ gtk_container_add (GTK_CONTAINER (box), gtk_label_new ("Below"));
+
+ label = gtk_label_new ("Some text that may wrap if it has to");
+ gtk_label_set_width_chars (GTK_LABEL (label), 10);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
+
+ gtk_grid_attach (GTK_GRID (grid), test_widget ("1", "red"), 1, 0, 1, 1);
+ gtk_grid_attach (GTK_GRID (grid), test_widget ("2", "blue"), 0, 1, 1, 1);
+
+ label = gtk_label_new ("Some text that may wrap if it has to");
+ gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+ gtk_label_set_width_chars (GTK_LABEL (label), 10);
+ gtk_grid_attach (GTK_GRID (grid), label, 1, 1, 1, 1);
+
+ gtk_widget_show_all (window);
+}
+
+static void
+box_comparison (void)
+{
+ GtkWidget *window;
+ GtkWidget *vbox;
+ GtkWidget *box;
+ GtkWidget *label;
+ GtkWidget *grid;
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (window), "Grid vs. Box");
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 5);
+ gtk_container_add (GTK_CONTAINER (window), vbox);
+
+ gtk_container_add (GTK_CONTAINER (vbox), gtk_label_new ("Above"));
+ gtk_container_add (GTK_CONTAINER (vbox), gtk_separator_new (GTK_ORIENTATION_HORIZONTAL));
+
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0, FALSE);
+ gtk_container_add (GTK_CONTAINER (vbox), box);
+
+ gtk_box_pack_start (GTK_BOX (box), test_widget ("1", "white"), FALSE, FALSE, 0);
+
+ label = gtk_label_new ("Some ellipsizing text");
+ gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+ gtk_label_set_width_chars (GTK_LABEL (label), 10);
+ gtk_box_pack_start (GTK_BOX (box), label, TRUE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX (box), test_widget ("2", "green"), FALSE, FALSE, 0);
+
+ label = gtk_label_new ("Some text that may wrap if needed");
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_label_set_width_chars (GTK_LABEL (label), 10);
+ gtk_box_pack_start (GTK_BOX (box), label, TRUE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX (box), test_widget ("3", "red"), FALSE, FALSE, 0);
+
+ grid = gtk_grid_new ();
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (grid), GTK_ORIENTATION_VERTICAL);
+ gtk_container_add (GTK_CONTAINER (vbox), gtk_separator_new (GTK_ORIENTATION_HORIZONTAL));
+ gtk_container_add (GTK_CONTAINER (vbox), grid);
+
+ gtk_grid_attach (GTK_GRID (grid), test_widget ("1", "white"), 0, 0, 1, 1);
+
+ label = gtk_label_new ("Some ellipsizing text");
+ gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+ gtk_label_set_width_chars (GTK_LABEL (label), 10);
+ gtk_grid_attach (GTK_GRID (grid), label, 1, 0, 1, 1);
+ gtk_widget_set_hexpand (label, TRUE);
+
+ gtk_grid_attach (GTK_GRID (grid), test_widget ("2", "green"), 2, 0, 1, 1);
+
+ label = gtk_label_new ("Some text that may wrap if needed");
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_label_set_width_chars (GTK_LABEL (label), 10);
+ gtk_grid_attach (GTK_GRID (grid), label, 3, 0, 1, 1);
+ gtk_widget_set_hexpand (label, TRUE);
+
+ gtk_grid_attach (GTK_GRID (grid), test_widget ("3", "red"), 4, 0, 1, 1);
+
+ gtk_container_add (GTK_CONTAINER (vbox), gtk_separator_new (GTK_ORIENTATION_HORIZONTAL));
+ gtk_container_add (GTK_CONTAINER (vbox), gtk_label_new ("Below"));
+
+ gtk_widget_show_all (window);
+}
+
+static void
+empty_line (void)
+{
+ GtkWidget *window;
+ GtkWidget *grid;
+ GtkWidget *child;
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (window), "Empty row");
+ grid = gtk_grid_new ();
+ gtk_container_add (GTK_CONTAINER (window), grid);
+
+ gtk_grid_set_row_spacing (GTK_GRID (grid), 10);
+ gtk_grid_set_column_spacing (GTK_GRID (grid), 10);
+
+ child = test_widget ("(0, 0)", "red");
+ gtk_grid_attach (GTK_GRID (grid), child, 0, 0, 1, 1);
+ gtk_widget_set_hexpand (child, TRUE);
+ gtk_widget_set_vexpand (child, TRUE);
+
+ gtk_grid_attach (GTK_GRID (grid), test_widget ("(0, 1)", "blue"), 0, 1, 1, 1);
+
+ gtk_grid_attach (GTK_GRID (grid), test_widget ("(10, 0)", "green"), 10, 0, 1, 1);
+ gtk_grid_attach (GTK_GRID (grid), test_widget ("(10, 1)", "magenta"), 10, 1, 1, 1);
+
+ gtk_widget_show_all (window);
+}
+
+static void
+scrolling (void)
+{
+ GtkWidget *window;
+ GtkWidget *sw;
+ GtkWidget *viewport;
+ GtkWidget *grid;
+ GtkWidget *child;
+ gint i;
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (window), "Scrolling");
+ sw = gtk_scrolled_window_new (NULL, NULL);
+ viewport = gtk_viewport_new (NULL, NULL);
+ grid = gtk_grid_new ();
+
+ gtk_container_add (GTK_CONTAINER (window), sw);
+ gtk_container_add (GTK_CONTAINER (sw), viewport);
+ gtk_container_add (GTK_CONTAINER (viewport), grid);
+
+ child = oriented_test_widget ("#800080", "#800080", -45.0);
+ gtk_grid_attach (GTK_GRID (grid), child, 0, 0, 1, 1);
+ gtk_widget_set_hexpand (child, TRUE);
+ gtk_widget_set_vexpand (child, TRUE);
+
+ for (i = 1; i < 16; i++)
+ {
+ gchar *color;
+ color = g_strdup_printf ("#%02x00%02x", 128 + 8*i, 128 - 8*i);
+ child = test_widget (color, color);
+ gtk_grid_attach (GTK_GRID (grid), child, 0, i, i + 1, 1);
+ gtk_widget_set_hexpand (child, TRUE);
+ g_free (color);
+ }
+
+ for (i = 1; i < 16; i++)
+ {
+ gchar *color;
+ color = g_strdup_printf ("#%02x00%02x", 128 - 8*i, 128 + 8*i);
+ child = oriented_test_widget (color, color, -90.0);
+ gtk_grid_attach (GTK_GRID (grid), child, i, 0, 1, i);
+ gtk_widget_set_vexpand (child, TRUE);
+ g_free (color);
+ }
+
+ gtk_widget_show_all (window);
+}
+
+int
+main (int argc, char *argv[])
+{
+ gtk_init (NULL, NULL);
+
+ simple_grid ();
+ text_grid ();
+ box_comparison ();
+ empty_line ();
+ scrolling ();
+
+ gtk_main ();
+
+ return 0;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]