[gtk+] GtkGrid: make attaching more flexible



commit ef4690d511e051a75ec948fab00035e3f5007939
Author: Matthias Clasen <mclasen redhat com>
Date:   Fri Sep 2 20:04:06 2011 -0400

    GtkGrid: make attaching more flexible
    
    Allow to attach children at either end of row/column 0.
    Proposed by Alex Larsson.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=657793

 gtk/gtkgrid.c         |  189 +++++++++++++++++++++++++-------------
 gtk/tests/Makefile.am |    4 +
 gtk/tests/grid.c      |  238 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 366 insertions(+), 65 deletions(-)
---
diff --git a/gtk/gtkgrid.c b/gtk/gtkgrid.c
index cfb21f4..df6d74d 100644
--- a/gtk/gtkgrid.c
+++ b/gtk/gtkgrid.c
@@ -363,41 +363,93 @@ gtk_grid_init (GtkGrid *grid)
   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)
+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));
+}
+
+/* Find the position 'touching' existing
+ * children. @orientation and @max determine
+ * from which direction to approach (horizontal
+ * + max = right, vertical + !max = top, etc).
+ * @op_pos, @op_span determine the rows/columns
+ * in which the touching has to happen.
+ */
+static gint
+find_attach_position (GtkGrid         *grid,
+                      GtkOrientation   orientation,
+                      gint             op_pos,
+                      gint             op_span,
+                      gboolean         max)
 {
-  GtkGrid *grid = GTK_GRID (container);
   GtkGridPrivate *priv = grid->priv;
   GtkGridChild *grid_child;
   GtkGridChildAttach *attach;
   GtkGridChildAttach *opposite;
   GList *list;
   gint pos;
+  gboolean hit;
+
+  if (max)
+    pos = -G_MAXINT;
+  else
+    pos = G_MAXINT;
+
+  hit = FALSE;
 
-  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];
+      attach = &grid_child->attach[orientation];
+      opposite = &grid_child->attach[1 - orientation];
+
+      /* check if the ranges overlap */
+      if (opposite->pos <= op_pos + op_span && op_pos <= opposite->pos + opposite->span)
+        {
+          hit = TRUE;
 
-      if (opposite->pos <= 0 && opposite->pos + opposite->span > 0)
-        pos = MAX (pos, attach->pos + attach->span);
+          if (max)
+            pos = MAX (pos, attach->pos + attach->span);
+          else
+            pos = MIN (pos, attach->pos);
+        }
      }
 
-  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
-    grid_attach (grid, child, pos, 0, 1, 1);
-  else
-    grid_attach (grid, child, 0, pos, 1, 1);
+  if (!hit)
+    pos = 0;
+
+  return pos;
+}
+
+static void
+gtk_grid_add (GtkContainer *container,
+              GtkWidget    *child)
+{
+  GtkGrid *grid = GTK_GRID (container);
+  GtkGridPrivate *priv = grid->priv;
+  gint pos[2] = { 0, 0 };
+
+  pos[priv->orientation] = find_attach_position (grid, priv->orientation, 0, 1, TRUE);
+  grid_attach (grid, child, pos[0], pos[1], 1, 1);
 }
 
 static void
@@ -1365,29 +1417,6 @@ 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
@@ -1424,7 +1453,8 @@ gtk_grid_attach (GtkGrid   *grid,
  * 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
+ * @sibling (allow-none): the child of @grid that @child will be placed
+ *     next to, or %NULL to place @child at the beginning or end
  * @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
@@ -1432,7 +1462,9 @@ gtk_grid_attach (GtkGrid   *grid,
  * Adds a widget to the grid.
  *
  * The widget is placed next to @sibling, on the side determined by
- * @side.
+ * @side. When @sibling is %NULL, the widget is placed in row (for
+ * left or right placement) or column 0 (for top or bottom placement),
+ * at the end indicated by @side.
  */
 void
 gtk_grid_attach_next_to (GtkGrid         *grid,
@@ -1448,32 +1480,59 @@ gtk_grid_attach_next_to (GtkGrid         *grid,
   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 (sibling == NULL || 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);
+  if (sibling)
+    {
+      grid_sibling = find_grid_child (grid, sibling);
 
-  switch (side)
+      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 ();
+        }
+    }
+  else
     {
-    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 ();
+      switch (side)
+        {
+        case GTK_POS_LEFT:
+          left = find_attach_position (grid, GTK_ORIENTATION_HORIZONTAL, 0, height, TRUE);
+          top = 0;
+          break;
+        case GTK_POS_RIGHT:
+          left = find_attach_position (grid, GTK_ORIENTATION_HORIZONTAL, 0, height, FALSE);
+          top = 0;
+          break;
+        case GTK_POS_TOP:
+          left = 0;
+          top = find_attach_position (grid, GTK_ORIENTATION_VERTICAL, 0, width, TRUE);
+          break;
+        case GTK_POS_BOTTOM:
+          left = 0;
+          top = find_attach_position (grid, GTK_ORIENTATION_VERTICAL, 0, width, FALSE);
+          break;
+        default:
+          g_assert_not_reached ();
+        }
     }
 
   grid_attach (grid, child, left, top, width, height);
diff --git a/gtk/tests/Makefile.am b/gtk/tests/Makefile.am
index 4b2d344..b4d224c 100644
--- a/gtk/tests/Makefile.am
+++ b/gtk/tests/Makefile.am
@@ -123,6 +123,10 @@ TEST_PROGS			+= entry
 entry_SOURCES			 = entry.c
 entry_LDADD			 = $(progs_ldadd)
 
+TEST_PROGS			+= grid
+grid_SOURCES			 = grid.c
+grid_LDADD			 = $(progs_ldadd)
+
 EXTRA_DIST +=				\
 	file-chooser-test-dir/empty     \
 	file-chooser-test-dir/text.txt
diff --git a/gtk/tests/grid.c b/gtk/tests/grid.c
new file mode 100644
index 0000000..90be1fd
--- /dev/null
+++ b/gtk/tests/grid.c
@@ -0,0 +1,238 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * 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 <gtk/gtk.h>
+
+/* test that attach_next_to picks the places
+ * we expect it to pick, when there is any choice
+ */
+static void
+test_attach (void)
+{
+  GtkGrid *g;
+  GtkWidget *child, *sibling, *z, *A, *B;
+  gint left, top, width, height;
+
+  g = (GtkGrid *)gtk_grid_new ();
+
+  child = gtk_label_new ("a");
+  gtk_grid_attach_next_to (g, child, NULL, GTK_POS_LEFT, 1, 1);
+  gtk_container_child_get (GTK_CONTAINER (g), child,
+                           "left-attach", &left,
+                           "top-attach", &top,
+                           "width", &width,
+                           "height", &height,
+                           NULL);
+  g_assert_cmpint (left,   ==, 0);
+  g_assert_cmpint (top,    ==, 0);
+  g_assert_cmpint (width,  ==, 1);
+  g_assert_cmpint (height, ==, 1);
+
+  sibling = child;
+  child = gtk_label_new ("b");
+  gtk_grid_attach_next_to (g, child, sibling, GTK_POS_RIGHT, 2, 2);
+  gtk_container_child_get (GTK_CONTAINER (g), child,
+                           "left-attach", &left,
+                           "top-attach", &top,
+                           "width", &width,
+                           "height", &height,
+                           NULL);
+  g_assert_cmpint (left,   ==, 1);
+  g_assert_cmpint (top,    ==, 0);
+  g_assert_cmpint (width,  ==, 2);
+  g_assert_cmpint (height, ==, 2);
+
+  /* this one should just be ignored */
+  z = gtk_label_new ("z");
+  gtk_grid_attach (g, z, 4, 4, 1, 1);
+
+  child = gtk_label_new ("c");
+  gtk_grid_attach_next_to (g, child, sibling, GTK_POS_BOTTOM, 3, 1);
+  gtk_container_child_get (GTK_CONTAINER (g), child,
+                           "left-attach", &left,
+                           "top-attach", &top,
+                           "width", &width,
+                           "height", &height,
+                           NULL);
+  g_assert_cmpint (left,   ==, 0);
+  g_assert_cmpint (top,    ==, 1);
+  g_assert_cmpint (width,  ==, 3);
+  g_assert_cmpint (height, ==, 1);
+
+  child = gtk_label_new ("u");
+  gtk_grid_attach_next_to (g, child, z, GTK_POS_LEFT, 2, 1);
+  gtk_container_child_get (GTK_CONTAINER (g), child,
+                           "left-attach", &left,
+                           "top-attach", &top,
+                           "width", &width,
+                           "height", &height,
+                           NULL);
+  g_assert_cmpint (left,   ==, 2);
+  g_assert_cmpint (top,    ==, 4);
+  g_assert_cmpint (width,  ==, 2);
+  g_assert_cmpint (height, ==, 1);
+
+  child = gtk_label_new ("v");
+  gtk_grid_attach_next_to (g, child, z, GTK_POS_RIGHT, 2, 1);
+  gtk_container_child_get (GTK_CONTAINER (g), child,
+                           "left-attach", &left,
+                           "top-attach", &top,
+                           "width", &width,
+                           "height", &height,
+                           NULL);
+  g_assert_cmpint (left,   ==, 5);
+  g_assert_cmpint (top,    ==, 4);
+  g_assert_cmpint (width,  ==, 2);
+  g_assert_cmpint (height, ==, 1);
+
+  child = gtk_label_new ("x");
+  gtk_grid_attach_next_to (g, child, z, GTK_POS_TOP, 1, 2);
+  gtk_container_child_get (GTK_CONTAINER (g), child,
+                           "left-attach", &left,
+                           "top-attach", &top,
+                           "width", &width,
+                           "height", &height,
+                           NULL);
+  g_assert_cmpint (left,   ==, 4);
+  g_assert_cmpint (top,    ==, 2);
+  g_assert_cmpint (width,  ==, 1);
+  g_assert_cmpint (height, ==, 2);
+
+  child = gtk_label_new ("x");
+  gtk_grid_attach_next_to (g, child, z, GTK_POS_TOP, 1, 2);
+  gtk_container_child_get (GTK_CONTAINER (g), child,
+                           "left-attach", &left,
+                           "top-attach", &top,
+                           "width", &width,
+                           "height", &height,
+                           NULL);
+  g_assert_cmpint (left,   ==, 4);
+  g_assert_cmpint (top,    ==, 2);
+  g_assert_cmpint (width,  ==, 1);
+  g_assert_cmpint (height, ==, 2);
+
+  child = gtk_label_new ("y");
+  gtk_grid_attach_next_to (g, child, z, GTK_POS_BOTTOM, 1, 2);
+  gtk_container_child_get (GTK_CONTAINER (g), child,
+                           "left-attach", &left,
+                           "top-attach", &top,
+                           "width", &width,
+                           "height", &height,
+                           NULL);
+  g_assert_cmpint (left,   ==, 4);
+  g_assert_cmpint (top,    ==, 5);
+  g_assert_cmpint (width,  ==, 1);
+  g_assert_cmpint (height, ==, 2);
+
+  A = gtk_label_new ("A");
+  gtk_grid_attach (g, A, 10, 10, 1, 1);
+  B = gtk_label_new ("B");
+  gtk_grid_attach (g, B, 10, 12, 1, 1);
+
+  child  = gtk_label_new ("D");
+  gtk_grid_attach_next_to (g, child, A, GTK_POS_RIGHT, 1, 3);
+  gtk_container_child_get (GTK_CONTAINER (g), child,
+                           "left-attach", &left,
+                           "top-attach", &top,
+                           "width", &width,
+                           "height", &height,
+                           NULL);
+  g_assert_cmpint (left,   ==, 11);
+  g_assert_cmpint (top,    ==, 10);
+  g_assert_cmpint (width,  ==,  1);
+  g_assert_cmpint (height, ==,  3);
+}
+
+static void
+test_add (void)
+{
+  GtkGrid *g;
+  GtkWidget *child;
+  gint left, top, width, height;
+
+  g = (GtkGrid *)gtk_grid_new ();
+
+  gtk_orientable_set_orientation (GTK_ORIENTABLE (g), GTK_ORIENTATION_HORIZONTAL);
+
+  child = gtk_label_new ("a");
+  gtk_container_add (GTK_CONTAINER (g), child);
+  gtk_container_child_get (GTK_CONTAINER (g), child,
+                           "left-attach", &left,
+                           "top-attach", &top,
+                           "width", &width,
+                           "height", &height,
+                           NULL);
+  g_assert_cmpint (left,   ==, 0);
+  g_assert_cmpint (top,    ==, 0);
+  g_assert_cmpint (width,  ==, 1);
+  g_assert_cmpint (height, ==, 1);
+
+  child = gtk_label_new ("b");
+  gtk_container_add (GTK_CONTAINER (g), child);
+  gtk_container_child_get (GTK_CONTAINER (g), child,
+                           "left-attach", &left,
+                           "top-attach", &top,
+                           "width", &width,
+                           "height", &height,
+                           NULL);
+  g_assert_cmpint (left,   ==, 1);
+  g_assert_cmpint (top,    ==, 0);
+  g_assert_cmpint (width,  ==, 1);
+  g_assert_cmpint (height, ==, 1);
+
+  child = gtk_label_new ("c");
+  gtk_container_add (GTK_CONTAINER (g), child);
+  gtk_container_child_get (GTK_CONTAINER (g), child,
+                           "left-attach", &left,
+                           "top-attach", &top,
+                           "width", &width,
+                           "height", &height,
+                           NULL);
+  g_assert_cmpint (left,   ==, 2);
+  g_assert_cmpint (top,    ==, 0);
+  g_assert_cmpint (width,  ==, 1);
+  g_assert_cmpint (height, ==, 1);
+
+  gtk_orientable_set_orientation (GTK_ORIENTABLE (g), GTK_ORIENTATION_VERTICAL);
+
+  child = gtk_label_new ("d");
+  gtk_container_add (GTK_CONTAINER (g), child);
+  gtk_container_child_get (GTK_CONTAINER (g), child,
+                           "left-attach", &left,
+                           "top-attach", &top,
+                           "width", &width,
+                           "height", &height,
+                           NULL);
+  g_assert_cmpint (left,   ==, 0);
+  g_assert_cmpint (top,    ==, 1);
+  g_assert_cmpint (width,  ==, 1);
+  g_assert_cmpint (height, ==, 1);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  gtk_test_init (&argc, &argv);
+
+  g_test_add_func ("/grid/attach", test_attach);
+  g_test_add_func ("/grid/add", test_add);
+
+  return g_test_run();
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]