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
This commit is contained in:
Matthias Clasen 2011-09-02 20:04:06 -04:00
parent f5e758bd60
commit ef4690d511
3 changed files with 375 additions and 74 deletions

View File

@ -363,12 +363,82 @@ 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
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)
{
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;
for (list = priv->children; list; list = list->next)
{
grid_child = list->data;
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 (max)
pos = MAX (pos, attach->pos + attach->span);
else
pos = MIN (pos, attach->pos);
}
}
if (!hit)
pos = 0;
return pos;
}
static void
gtk_grid_add (GtkContainer *container,
@ -376,28 +446,10 @@ gtk_grid_add (GtkContainer *container,
{
GtkGrid *grid = GTK_GRID (container);
GtkGridPrivate *priv = grid->priv;
GtkGridChild *grid_child;
GtkGridChildAttach *attach;
GtkGridChildAttach *opposite;
GList *list;
gint pos;
gint pos[2] = { 0, 0 };
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);
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);
switch (side)
if (sibling)
{
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_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 ();
}
}
else
{
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);

View File

@ -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

238
gtk/tests/grid.c Normal file
View File

@ -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();
}