]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkgrid.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gtkgrid.c
index cfb21f41ba54014926e4f6365a2ab799e05ff152..571f8266f2039408d600cfae7876dd4558c1811c 100644 (file)
@@ -13,9 +13,7 @@
  * 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.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "config.h"
@@ -44,7 +42,8 @@
  *
  * 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().
+ * existing child, using gtk_grid_attach_next_to(). The behaviour of
+ * GtkGrid when several children occupy the same grid cell is undefined.
  *
  * 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
@@ -163,11 +162,11 @@ gtk_grid_get_property (GObject    *object,
       break;
 
     case PROP_ROW_SPACING:
-      g_value_set_int (value, ROWS (priv)->spacing);
+      g_value_set_int (value, COLUMNS (priv)->spacing);
       break;
 
     case PROP_COLUMN_SPACING:
-      g_value_set_int (value, COLUMNS (priv)->spacing);
+      g_value_set_int (value, ROWS (priv)->spacing);
       break;
 
     case PROP_ROW_HOMOGENEOUS:
@@ -363,41 +362,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
@@ -720,37 +771,72 @@ gtk_grid_request_spanning (GtkGridRequest *request,
       /* 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.
+       *
+       * When doing homogeneous allocation though, try to keep the
+       * line allocations even, since we're going to force them to
+       * be the same anyway, and we don't want to introduce unnecessary
+       * extra space.
        */
       if (span_minimum < minimum)
         {
-          extra = minimum - span_minimum;
-          expand = span_expand;
-          for (i = 0; i < attach->span; i++)
+          if (linedata->homogeneous)
             {
-              line = &lines->lines[attach->pos - lines->min + i];
-              if (force_expand || line->expand)
+              gint total, m;
+
+              total = minimum - (attach->span - 1) * linedata->spacing;
+              m = total / attach->span + (total % attach->span ? 1 : 0);
+              for (i = 0; i < attach->span; i++)
+                {
+                  line = &lines->lines[attach->pos - lines->min + i];
+                  line->minimum = MAX(line->minimum, m);
+                }
+            }
+          else
+            {
+              extra = minimum - span_minimum;
+              expand = span_expand;
+              for (i = 0; i < attach->span; i++)
                 {
-                  line_extra = extra / expand;
-                  line->minimum += line_extra;
-                  extra -= line_extra;
-                  expand -= 1;
+                  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++)
+          if (linedata->homogeneous)
             {
-              line = &lines->lines[attach->pos - lines->min + i];
-              if (force_expand || line->expand)
+              gint total, n;
+
+              total = natural - (attach->span - 1) * linedata->spacing;
+              n = total / attach->span + (total % attach->span ? 1 : 0);
+              for (i = 0; i < attach->span; i++)
+                {
+                  line = &lines->lines[attach->pos - lines->min + i];
+                  line->natural = MAX(line->natural, n);
+                }
+            }
+          else
+            {
+              extra = natural - span_natural;
+              expand = span_expand;
+              for (i = 0; i < attach->span; i++)
                 {
-                  line_extra = extra / expand;
-                  line->natural += line_extra;
-                  extra -= line_extra;
-                  expand -= 1;
+                  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;
+                    }
                 }
             }
         }
@@ -875,8 +961,13 @@ gtk_grid_request_sum (GtkGridRequest *request,
   linedata = &priv->linedata[orientation];
   lines = &request->lines[orientation];
 
-  min = (nonempty - 1) * linedata->spacing;
-  nat = (nonempty - 1) * linedata->spacing;
+  min = 0;
+  nat = 0;
+  if (nonempty > 0)
+    {
+      min = (nonempty - 1) * linedata->spacing;
+      nat = (nonempty - 1) * linedata->spacing;
+    }
 
   for (i = 0; i < lines->max - lines->min; i++)
     {
@@ -930,6 +1021,9 @@ gtk_grid_request_allocate (GtkGridRequest *request,
 
   gtk_grid_request_compute_expand (request, orientation, &nonempty, &expand);
 
+  if (nonempty == 0)
+    return;
+
   linedata = &priv->linedata[orientation];
   lines = &request->lines[orientation];
 
@@ -1365,29 +1459,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 +1495,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 +1504,12 @@ 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.
+ *
+ * Attaching widgets labeled [1], [2], [3] with @sibling == %NULL and
+ * @side == %GTK_POS_LEFT yields a layout of [3][2][1].
  */
 void
 gtk_grid_attach_next_to (GtkGrid         *grid,
@@ -1448,32 +1525,61 @@ 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, FALSE);
+          left -= width;
+          top = 0;
+          break;
+        case GTK_POS_RIGHT:
+          left = find_attach_position (grid, GTK_ORIENTATION_HORIZONTAL, 0, height, TRUE);
+          top = 0;
+          break;
+        case GTK_POS_TOP:
+          left = 0;
+          top = find_attach_position (grid, GTK_ORIENTATION_VERTICAL, 0, width, FALSE);
+          top -= height;
+          break;
+        case GTK_POS_BOTTOM:
+          left = 0;
+          top = find_attach_position (grid, GTK_ORIENTATION_VERTICAL, 0, width, TRUE);
+          break;
+        default:
+          g_assert_not_reached ();
+        }
     }
 
   grid_attach (grid, child, left, top, width, height);
@@ -1488,7 +1594,7 @@ gtk_grid_attach_next_to (GtkGrid         *grid,
  * Gets the child of @grid whose area covers the grid
  * cell whose upper left corner is at @left, @top.
  *
- * Returns: the child at the given position, or %NULL
+ * Returns: (transfer none): the child at the given position, or %NULL
  *
  * Since: 3.2
  */
@@ -1497,10 +1603,14 @@ gtk_grid_get_child_at (GtkGrid *grid,
                        gint     left,
                        gint     top)
 {
-  GtkGridPrivate *priv = grid->priv;
+  GtkGridPrivate *priv;
   GtkGridChild *child;
   GList *list;
 
+  g_return_val_if_fail (GTK_IS_GRID (grid), NULL);
+
+  priv = grid->priv;
+
   for (list = priv->children; list; list = list->next)
     {
       child = list->data;
@@ -1532,13 +1642,15 @@ void
 gtk_grid_insert_row (GtkGrid *grid,
                      gint     position)
 {
-  GtkGridPrivate *priv = grid->priv;
+  GtkGridPrivate *priv;
   GtkGridChild *child;
   GList *list;
   gint top, height;
 
   g_return_if_fail (GTK_IS_GRID (grid));
 
+  priv = grid->priv;
+
   for (list = priv->children; list; list = list->next)
     {
       child = list->data;
@@ -1576,13 +1688,15 @@ void
 gtk_grid_insert_column (GtkGrid *grid,
                         gint     position)
 {
-  GtkGridPrivate *priv = grid->priv;
+  GtkGridPrivate *priv;
   GtkGridChild *child;
   GList *list;
   gint left, width;
 
   g_return_if_fail (GTK_IS_GRID (grid));
 
+  priv = grid->priv;
+
   for (list = priv->children; list; list = list->next)
     {
       child = list->data;
@@ -1762,9 +1876,9 @@ gtk_grid_set_row_spacing (GtkGrid *grid,
 
   priv = grid->priv;
 
-  if (ROWS (priv)->spacing != spacing)
+  if (COLUMNS (priv)->spacing != spacing)
     {
-      ROWS (priv)->spacing = spacing;
+      COLUMNS (priv)->spacing = spacing;
 
       if (gtk_widget_get_visible (GTK_WIDGET (grid)))
         gtk_widget_queue_resize (GTK_WIDGET (grid));
@@ -1789,7 +1903,7 @@ gtk_grid_get_row_spacing (GtkGrid *grid)
 
   priv = grid->priv;
 
-  return ROWS (priv)->spacing;
+  return COLUMNS (priv)->spacing;
 }
 
 /**
@@ -1809,9 +1923,9 @@ gtk_grid_set_column_spacing (GtkGrid *grid,
 
   priv = grid->priv;
 
-  if (COLUMNS (priv)->spacing != spacing)
+  if (ROWS (priv)->spacing != spacing)
     {
-      COLUMNS (priv)->spacing = spacing;
+      ROWS (priv)->spacing = spacing;
 
       if (gtk_widget_get_visible (GTK_WIDGET (grid)))
         gtk_widget_queue_resize (GTK_WIDGET (grid));
@@ -1837,5 +1951,5 @@ gtk_grid_get_column_spacing (GtkGrid *grid)
 
   priv = grid->priv;
 
-  return COLUMNS (priv)->spacing;
+  return ROWS (priv)->spacing;
 }