From 88cf5470290cd03ada031641cb6ee1c90df75e7d Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Sat, 9 Oct 2010 22:15:34 -0400 Subject: [PATCH] Fix handling of the geometry widget The geometry widget feature of gtk_window_set_geometry_hints() has never really worked right because the calculation that GTK+ did to compute the base size of the window only worked when the geometry widget had a larger minimum size than anything else in the window. Setup: * Move the GtkSizeGroup private functions to a new private header gtksizegroup-private.h * Add the possibilty to pass flags to _gtk_size_group_queue_resize(), with the flag GTK_QUEUE_RESIZE_INVALIDATE_ONLY to suppress adding the widget's toplevel to the resize queue. * _gtk_container_resize_invalidate() is added to implement that feature * _gtk_widget_override_size_request()/_gtk_widget_restore_size_request() allow temporarily forcing a large minimum size on the geometry widget without creating resize loops. GtkWindow: * Compute the extra width/height around the geometry widget correctly; print a warning if the computation fails. * Always make the minimum size at least the natural minimum size of the toplevel; GTK+ now fails badly with underallocation. * Always set the base size hint; we were failing to set it properly when the specified minimum size was overriden, but it's harmless to always set it. Tests: * New test 'testgeometry' that replaces the 'gridded geometry' test from testgtk. The new test is roughly similar but creates a bunch of windows showing different possibilities. * The testgtk test is removed. No need to have both. https://bugzilla.gnome.org/show_bug.cgi?id=68668 --- gtk/Makefile.am | 1 + gtk/gtkcontainer.c | 37 ++++++- gtk/gtkcontainer.h | 1 + gtk/gtkprivate.h | 9 ++ gtk/gtksizegroup-private.h | 49 +++++++++ gtk/gtksizegroup.c | 50 +++++---- gtk/gtksizegroup.h | 9 -- gtk/gtksizerequest.c | 2 +- gtk/gtkwidget.c | 85 +++++++++++++--- gtk/gtkwindow.c | 82 ++++++++++----- tests/Makefile.am | 6 ++ tests/testgeometry.c | 203 +++++++++++++++++++++++++++++++++++++ tests/testgtk.c | 168 ------------------------------ 13 files changed, 463 insertions(+), 239 deletions(-) create mode 100644 gtk/gtksizegroup-private.h create mode 100644 tests/testgeometry.c diff --git a/gtk/Makefile.am b/gtk/Makefile.am index fb040fd08..29df0c90e 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -382,6 +382,7 @@ gtk_private_h_sources = \ gtkrecentchooserdefault.h \ gtkrecentchooserprivate.h \ gtkrecentchooserutils.h \ + gtksizegroup-private.h \ gtksocketprivate.h \ gtktextbtree.h \ gtktextbufferserialize.h\ diff --git a/gtk/gtkcontainer.c b/gtk/gtkcontainer.c index 17473e514..75ac3f70a 100644 --- a/gtk/gtkcontainer.c +++ b/gtk/gtkcontainer.c @@ -1609,8 +1609,9 @@ gtk_container_idle_sizer (gpointer data) return FALSE; } -void -_gtk_container_queue_resize (GtkContainer *container) +static void +_gtk_container_queue_resize_internal (GtkContainer *container, + gboolean invalidate_only) { GtkContainerPrivate *priv; GtkContainer *resize_container; @@ -1637,7 +1638,7 @@ _gtk_container_queue_resize (GtkContainer *container) widget = parent; } - if (resize_container) + if (resize_container && !invalidate_only) { if (gtk_widget_get_visible (GTK_WIDGET (resize_container)) && (gtk_widget_is_toplevel (GTK_WIDGET (resize_container)) || @@ -1677,6 +1678,36 @@ _gtk_container_queue_resize (GtkContainer *container) } } +/** + * _gtk_container_queue_resize: + * @container: a #GtkContainer + * + * Determines the "resize container" in the hierarchy above this container + * (typically the toplevel, but other containers can be set as resize + * containers with gtk_container_set_resize_mode()), marks the container + * and all parents up to and including the resize container as needing + * to have sizes recompted, and if necessary adds the resize container + * to the queue of containers that will be resized out at idle. + */ +void +_gtk_container_queue_resize (GtkContainer *container) +{ + _gtk_container_queue_resize_internal (container, FALSE); +} + +/** + * _gtk_container_resize_invalidate: + * @container: a #GtkContainer + * + * Invalidates cached sizes like _gtk_container_queue_resize() but doesn't + * actually queue the resize container for resize. + */ +void +_gtk_container_resize_invalidate (GtkContainer *container) +{ + _gtk_container_queue_resize_internal (container, TRUE); +} + void gtk_container_check_resize (GtkContainer *container) { diff --git a/gtk/gtkcontainer.h b/gtk/gtkcontainer.h index 607baf6f7..57a80aed3 100644 --- a/gtk/gtkcontainer.h +++ b/gtk/gtkcontainer.h @@ -210,6 +210,7 @@ void gtk_container_class_handle_border_width (GtkContainerClass *klass); /* Non-public methods */ void _gtk_container_queue_resize (GtkContainer *container); +void _gtk_container_resize_invalidate (GtkContainer *container); void _gtk_container_clear_resize_widgets (GtkContainer *container); gchar* _gtk_container_child_composite_name (GtkContainer *container, GtkWidget *child); diff --git a/gtk/gtkprivate.h b/gtk/gtkprivate.h index c0f14b4f3..d8fd0c021 100644 --- a/gtk/gtkprivate.h +++ b/gtk/gtkprivate.h @@ -55,6 +55,15 @@ gboolean _gtk_widget_get_height_request_needed (GtkWidget *widget); void _gtk_widget_set_height_request_needed (GtkWidget *widget, gboolean height_request_needed); +void _gtk_widget_override_size_request (GtkWidget *widget, + int width, + int height, + int *old_width, + int *old_height); +void _gtk_widget_restore_size_request (GtkWidget *widget, + int old_width, + int old_height); + #ifdef G_OS_WIN32 const gchar *_gtk_get_datadir (); diff --git a/gtk/gtksizegroup-private.h b/gtk/gtksizegroup-private.h new file mode 100644 index 000000000..3f80d3fd7 --- /dev/null +++ b/gtk/gtksizegroup-private.h @@ -0,0 +1,49 @@ +/* GTK - The GIMP Toolkit + * gtksizegroup-private.h: + * Copyright (C) 2000-2010 Red Hat Software + * + * 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. + */ + +#ifndef __GTK_SIZE_GROUP_PRIVATE_H__ +#define __GTK_SIZE_GROUP_PRIVATE_H__ + +#include + +/** + * GtkQueueResizeFlags: + * @GTK_QUEUE_RESIZE_INVALIDATE_ONLY: invalidate all cached sizes + * as we would normally do when a widget is queued for resize, + * but don't actually add the toplevel resize container to the + * resize queue. Useful if we want to change the size of a widget + * see how that would affect the overall layout, then restore + * the old size. + * + * Flags that affect the operation of queueing a widget for resize. + */ +typedef enum +{ + GTK_QUEUE_RESIZE_INVALIDATE_ONLY = 1 << 0 +} GtkQueueResizeFlags; + +void _gtk_size_group_bump_requisition (GtkWidget *widget, + GtkSizeGroupMode mode, + gint *minimum, + gint *natural); +void _gtk_size_group_queue_resize (GtkWidget *widget, + GtkQueueResizeFlags flags); + +#endif /* __GTK_SIZE_GROUP_PRIVATE_H__ */ diff --git a/gtk/gtksizegroup.c b/gtk/gtksizegroup.c index 1663f9732..62ce9ea7e 100644 --- a/gtk/gtksizegroup.c +++ b/gtk/gtksizegroup.c @@ -23,7 +23,7 @@ #include "gtkcontainer.h" #include "gtkintl.h" #include "gtkprivate.h" -#include "gtksizegroup.h" +#include "gtksizegroup-private.h" #include "gtkbuildable.h" @@ -182,19 +182,27 @@ add_widget_to_closure (GtkWidget *widget, } static void -real_queue_resize (GtkWidget *widget) +real_queue_resize (GtkWidget *widget, + GtkQueueResizeFlags flags) { - GtkWidget *parent; + GtkWidget *container; _gtk_widget_set_alloc_needed (widget, TRUE); _gtk_widget_set_width_request_needed (widget, TRUE); _gtk_widget_set_height_request_needed (widget, TRUE); - parent = gtk_widget_get_parent (widget); - if (parent) - _gtk_container_queue_resize (GTK_CONTAINER (parent)); - else if (gtk_widget_is_toplevel (widget) && GTK_IS_CONTAINER (widget)) - _gtk_container_queue_resize (GTK_CONTAINER (widget)); + container = gtk_widget_get_parent (widget); + if (!container && + gtk_widget_is_toplevel (widget) && GTK_IS_CONTAINER (widget)) + container = widget; + + if (container) + { + if (flags & GTK_QUEUE_RESIZE_INVALIDATE_ONLY) + _gtk_container_resize_invalidate (GTK_CONTAINER (container)); + else + _gtk_container_queue_resize (GTK_CONTAINER (container)); + } } static void @@ -214,8 +222,9 @@ reset_group_sizes (GSList *groups) } static void -queue_resize_on_widget (GtkWidget *widget, - gboolean check_siblings) +queue_resize_on_widget (GtkWidget *widget, + gboolean check_siblings, + GtkQueueResizeFlags flags) { GtkWidget *parent = widget; GSList *tmp_list; @@ -228,7 +237,7 @@ queue_resize_on_widget (GtkWidget *widget, if (widget == parent && !check_siblings) { - real_queue_resize (widget); + real_queue_resize (widget, flags); parent = gtk_widget_get_parent (parent); continue; } @@ -237,7 +246,7 @@ queue_resize_on_widget (GtkWidget *widget, if (!widget_groups) { if (widget == parent) - real_queue_resize (widget); + real_queue_resize (widget, flags); parent = gtk_widget_get_parent (parent); continue; @@ -258,14 +267,14 @@ queue_resize_on_widget (GtkWidget *widget, if (tmp_list->data == parent) { if (widget == parent) - real_queue_resize (parent); + real_queue_resize (parent, flags); } else if (tmp_list->data == widget) { g_warning ("A container and its child are part of this SizeGroup"); } else - queue_resize_on_widget (tmp_list->data, FALSE); + queue_resize_on_widget (tmp_list->data, FALSE, flags); tmp_list = tmp_list->next; } @@ -288,14 +297,14 @@ queue_resize_on_widget (GtkWidget *widget, if (tmp_list->data == parent) { if (widget == parent) - real_queue_resize (parent); + real_queue_resize (parent, flags); } else if (tmp_list->data == widget) { g_warning ("A container and its child are part of this SizeGroup"); } else - queue_resize_on_widget (tmp_list->data, FALSE); + queue_resize_on_widget (tmp_list->data, FALSE, flags); tmp_list = tmp_list->next; } @@ -308,12 +317,12 @@ queue_resize_on_widget (GtkWidget *widget, } static void -queue_resize_on_group (GtkSizeGroup *size_group) +queue_resize_on_group (GtkSizeGroup *size_group) { GtkSizeGroupPrivate *priv = size_group->priv; if (priv->widgets) - queue_resize_on_widget (priv->widgets->data, TRUE); + queue_resize_on_widget (priv->widgets->data, TRUE, 0); } static void @@ -806,11 +815,12 @@ _gtk_size_group_bump_requisition (GtkWidget *widget, * Queue a resize on a widget, and on all other widgets grouped with this widget. **/ void -_gtk_size_group_queue_resize (GtkWidget *widget) +_gtk_size_group_queue_resize (GtkWidget *widget, + GtkQueueResizeFlags flags) { initialize_size_group_quarks (); - queue_resize_on_widget (widget, TRUE); + queue_resize_on_widget (widget, TRUE, flags); } typedef struct { diff --git a/gtk/gtksizegroup.h b/gtk/gtksizegroup.h index d778f0881..ff1b0b10f 100644 --- a/gtk/gtksizegroup.h +++ b/gtk/gtksizegroup.h @@ -92,15 +92,6 @@ void gtk_size_group_remove_widget (GtkSizeGroup *size_group, GtkWidget *widget); GSList * gtk_size_group_get_widgets (GtkSizeGroup *size_group); - - -void _gtk_size_group_bump_requisition (GtkWidget *widget, - GtkSizeGroupMode mode, - gint *minimum, - gint *natural); -void _gtk_size_group_queue_resize (GtkWidget *widget); - - G_END_DECLS #endif /* __GTK_SIZE_GROUP_H__ */ diff --git a/gtk/gtksizerequest.c b/gtk/gtksizerequest.c index a30b2418e..c3d0398fa 100644 --- a/gtk/gtksizerequest.c +++ b/gtk/gtksizerequest.c @@ -23,7 +23,7 @@ #include #include "gtksizerequest.h" -#include "gtksizegroup.h" +#include "gtksizegroup-private.h" #include "gtkdebug.h" #include "gtkprivate.h" #include "gtkintl.h" diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index e3acd7826..779a00380 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -40,7 +40,7 @@ #include "gtkrc.h" #include "gtkselection.h" #include "gtksettings.h" -#include "gtksizegroup.h" +#include "gtksizegroup-private.h" #include "gtkwidget.h" #include "gtkwindow.h" #include "gtkbindings.h" @@ -563,9 +563,10 @@ static void gtk_widget_real_adjust_size_request (GtkWidget static void gtk_widget_real_adjust_size_allocation (GtkWidget *widget, GtkAllocation *allocation); -static void gtk_widget_set_usize_internal (GtkWidget *widget, - gint width, - gint height); +static void gtk_widget_set_usize_internal (GtkWidget *widget, + gint width, + gint height, + GtkQueueResizeFlags flags); static void gtk_widget_add_events_internal (GtkWidget *widget, GdkDevice *device, @@ -3022,10 +3023,10 @@ gtk_widget_set_property (GObject *object, gtk_container_add (GTK_CONTAINER (g_value_get_object (value)), widget); break; case PROP_WIDTH_REQUEST: - gtk_widget_set_usize_internal (widget, g_value_get_int (value), -2); + gtk_widget_set_usize_internal (widget, g_value_get_int (value), -2, 0); break; case PROP_HEIGHT_REQUEST: - gtk_widget_set_usize_internal (widget, -2, g_value_get_int (value)); + gtk_widget_set_usize_internal (widget, -2, g_value_get_int (value), 0); break; case PROP_VISIBLE: gtk_widget_set_visible (widget, g_value_get_boolean (value)); @@ -4207,7 +4208,7 @@ gtk_widget_queue_resize (GtkWidget *widget) if (gtk_widget_get_realized (widget)) gtk_widget_queue_shallow_draw (widget); - _gtk_size_group_queue_resize (widget); + _gtk_size_group_queue_resize (widget, 0); } /** @@ -4224,7 +4225,7 @@ gtk_widget_queue_resize_no_redraw (GtkWidget *widget) { g_return_if_fail (GTK_IS_WIDGET (widget)); - _gtk_size_group_queue_resize (widget); + _gtk_size_group_queue_resize (widget, 0); } /** @@ -8643,9 +8644,10 @@ gtk_widget_error_bell (GtkWidget *widget) } static void -gtk_widget_set_usize_internal (GtkWidget *widget, - gint width, - gint height) +gtk_widget_set_usize_internal (GtkWidget *widget, + gint width, + gint height, + GtkQueueResizeFlags flags) { GtkWidgetAuxInfo *aux_info; gboolean changed = FALSE; @@ -8656,19 +8658,26 @@ gtk_widget_set_usize_internal (GtkWidget *widget, if (width > -2 && aux_info->width != width) { - g_object_notify (G_OBJECT (widget), "width-request"); + if ((flags & GTK_QUEUE_RESIZE_INVALIDATE_ONLY) == 0) + g_object_notify (G_OBJECT (widget), "width-request"); aux_info->width = width; changed = TRUE; } if (height > -2 && aux_info->height != height) { - g_object_notify (G_OBJECT (widget), "height-request"); + if ((flags & GTK_QUEUE_RESIZE_INVALIDATE_ONLY) == 0) + g_object_notify (G_OBJECT (widget), "height-request"); aux_info->height = height; changed = TRUE; } if (gtk_widget_get_visible (widget) && changed) - gtk_widget_queue_resize (widget); + { + if ((flags & GTK_QUEUE_RESIZE_INVALIDATE_ONLY) == 0) + gtk_widget_queue_resize (widget); + else + _gtk_size_group_queue_resize (widget, GTK_QUEUE_RESIZE_INVALIDATE_ONLY); + } g_object_thaw_notify (G_OBJECT (widget)); } @@ -8728,7 +8737,7 @@ gtk_widget_set_size_request (GtkWidget *widget, if (height == 0) height = 1; - gtk_widget_set_usize_internal (widget, width, height); + gtk_widget_set_usize_internal (widget, width, height, 0); } @@ -8764,6 +8773,52 @@ gtk_widget_get_size_request (GtkWidget *widget, *height = aux_info->height; } +/** + * _gtk_widget_override_size_request: + * @widget: a #GtkWidget + * @width: new forced minimum width + * @height: new forced minimum height + * @old_width: location to store previous forced minimum width + * @old_width: location to store previous forced minumum height + * + * Temporarily establishes a forced minimum size for a widget; this + * is used by GtkWindow when calculating the size to add to the + * window's geometry widget. Cached sizes for the widget and its + * parents are invalidated, so that subsequent calls to the size + * negotiation machinery produce the overriden result, but the + * widget is not queued for relayout or redraw. The old size must + * be restored with _gtk_widget_restore_size_request() or things + * will go screwy. + */ +void +_gtk_widget_override_size_request (GtkWidget *widget, + int width, + int height, + int *old_width, + int *old_height) +{ + gtk_widget_get_size_request (widget, old_width, old_height); + gtk_widget_set_usize_internal (widget, width, height, + GTK_QUEUE_RESIZE_INVALIDATE_ONLY); +} + +/** + * _gtk_widget_restore_size_request: + * @widget: a #GtkWidget + * @old_width: saved forced minimum size + * @old_height: saved forced minimum size + * + * Undoes the operation of_gtk_widget_override_size_request(). + */ +void +_gtk_widget_restore_size_request (GtkWidget *widget, + int old_width, + int old_height) +{ + gtk_widget_set_usize_internal (widget, old_width, old_height, + GTK_QUEUE_RESIZE_INVALIDATE_ONLY); +} + /** * gtk_widget_set_events: * @widget: a #GtkWidget diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 4103ef67c..571484eb9 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -47,6 +47,7 @@ #include "gtkicontheme.h" #include "gtkmarshalers.h" #include "gtkplug.h" +#include "gtkprivate.h" #include "gtkbuildable.h" #ifdef GDK_WINDOWING_X11 @@ -7021,28 +7022,52 @@ gtk_window_compute_hints (GtkWindow *window, if (geometry_info && geometry_info->widget) { - GtkRequisition requisition; - GtkRequisition child_requisition; - - /* FIXME: This really isn't right. It gets the min size wrong and forces - * callers to do horrible hacks like set a huge usize on the child requisition - * to get the base size right. We really want to find the answers to: + /* If the geometry widget is set, then the hints really apply to that + * widget. This is pretty much meaningless unless the window layout + * is such that the rest of the window adds fixed size borders to + * the geometry widget. Our job is to figure the size of the borders; + * We do that by asking how big the toplevel would be if the + * geometry widget was *really big*. * - * - If the geometry widget was infinitely big, how much extra space - * would be needed for the stuff around it. + * +----------+ + * |AAAAAAAAA | At small sizes, the minimum sizes of widgets + * |GGGGG B| in the border can confuse things + * |GGGGG B| + * | B| + * +----------+ * - * - If the geometry widget was infinitely small, how big would the - * window still have to be. - * - * Finding these answers would be a bit of a mess here. (Bug #68668) + * +-----------+ + * |AAAAAAAAA | When the geometry widget is large, things are + * |GGGGGGGGGGB| clearer. + * |GGGGGGGGGGB| + * |GGGGGGGGGG | + * +-----------+ */ - gtk_widget_get_preferred_size (geometry_info->widget, - &child_requisition, NULL); +#define TEMPORARY_SIZE 10000 /* 10,000 pixels should be bigger than real widget sizes */ + GtkRequisition requisition; + int current_width, current_height; + _gtk_widget_override_size_request (geometry_info->widget, + TEMPORARY_SIZE, TEMPORARY_SIZE, + ¤t_width, ¤t_height); gtk_widget_get_preferred_size (widget, &requisition, NULL); - extra_width = requisition.width - child_requisition.width; - extra_height = requisition.height - child_requisition.height; + _gtk_widget_restore_size_request (geometry_info->widget, + current_width, current_height); + + extra_width = requisition.width - TEMPORARY_SIZE; + extra_height = requisition.height - TEMPORARY_SIZE; + + if (extra_width < 0 || extra_width < 0) + { + g_warning("Toplevel size doesn't seem to directly depend on the " + "size of the geometry widget from gtk_window_set_geometry_hints(). " + "The geometry widget might not be in the window, or it might not " + "be packed into the window appropriately"); + extra_width = MAX(extra_width, 0); + extra_height = MAX(extra_height, 0); + } +#undef TEMPORARY_SIZE } /* We don't want to set GDK_HINT_POS in here, we just set it @@ -7055,27 +7080,38 @@ gtk_window_compute_hints (GtkWindow *window, new_geometry->base_width += extra_width; new_geometry->base_height += extra_height; } - else if (!(*new_flags & GDK_HINT_MIN_SIZE) && - (*new_flags & GDK_HINT_RESIZE_INC) && - ((extra_width != 0) || (extra_height != 0))) + else { + /* For simplicity, we always set the base hint, even when we + * don't expect it to have any visible effect. + */ *new_flags |= GDK_HINT_BASE_SIZE; - + new_geometry->base_width = extra_width; new_geometry->base_height = extra_height; + + /* As for X, if BASE_SIZE is not set but MIN_SIZE is set, then the + * base size is the minimum size */ + if (*new_flags & GDK_HINT_MIN_SIZE) + { + if (new_geometry->min_width > 0) + new_geometry->base_width += new_geometry->min_width; + if (new_geometry->min_height > 0) + new_geometry->base_height += new_geometry->min_height; + } } - + if (*new_flags & GDK_HINT_MIN_SIZE) { if (new_geometry->min_width < 0) new_geometry->min_width = requisition.width; else - new_geometry->min_width += extra_width; + new_geometry->min_width = MAX (requisition.width, new_geometry->min_width + extra_width); if (new_geometry->min_height < 0) new_geometry->min_height = requisition.height; else - new_geometry->min_height += extra_height; + new_geometry->min_height = MAX (requisition.height, new_geometry->min_height + extra_height); } else { diff --git a/tests/Makefile.am b/tests/Makefile.am index 4fa432121..b607ad53e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -47,6 +47,7 @@ noinst_PROGRAMS = $(TEST_PROGS) \ testfilechooser \ testfilechooserbutton \ testframe \ + testgeometry \ testgtk \ testheightforwidth \ testiconview \ @@ -136,6 +137,7 @@ testerrors_DEPENDENCIES = $(TEST_DEPS) testfilechooser_DEPENDENCIES = $(TEST_DEPS) testfilechooserbutton_DEPENDENCIES = $(TEST_DEPS) testframe_DEPENDENCIES = $(TEST_DEPS) +testgeometry_DEPENDENCIES = $(TEST_DEPS) testgtk_DEPENDENCIES = $(TEST_DEPS) testinput_DEPENDENCIES = $(TEST_DEPS) testimage_DEPENDENCIES = $(TEST_DEPS) @@ -201,6 +203,7 @@ testerrors_LDADD = $(LDADDS) testfilechooser_LDADD = $(LDADDS) testfilechooserbutton_LDADD = $(LDADDS) testframe_LDADD = $(LDADDS) +testgeometry_LDADD = $(LDADDS) testgtk_LDADD = $(LDADDS) testheightforwidth_LDADD = $(LDADDS) testicontheme_LDADD = $(LDADDS) @@ -322,6 +325,9 @@ testbuttons_SOURCES = \ testframe_SOURCES = \ testframe.c +testgeometry_SOURCES = \ + testgeometry.c + testiconview_SOURCES = \ testiconview.c \ prop-editor.c diff --git a/tests/testgeometry.c b/tests/testgeometry.c new file mode 100644 index 000000000..9d3f71407 --- /dev/null +++ b/tests/testgeometry.c @@ -0,0 +1,203 @@ +/* testgeometry.c + * Author: Owen Taylor + * Copyright © 2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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 + +#define GRID_SIZE 20 +#define BORDER 6 + +static int window_count = 0; +const char *geometry_string; + +static void +on_window_destroy (GtkWidget *widget) +{ + window_count--; + if (window_count == 0) + gtk_main_quit(); +} + +static gboolean +on_drawing_area_draw (GtkWidget *drawing_area, + cairo_t *cr, + gpointer data) +{ + int width = gtk_widget_get_allocated_width (drawing_area); + int height = gtk_widget_get_allocated_height (drawing_area); + int x, y; + int border = 0; + GdkWindowHints mask = GPOINTER_TO_UINT(data); + + cairo_set_source_rgb (cr, 1, 1, 1); + cairo_paint (cr); + + if ((mask & GDK_HINT_BASE_SIZE) != 0) + border = BORDER; + + cairo_set_source_rgb (cr, 0, 0, 0); + for (y = 0; y < height - 2 * border; y += GRID_SIZE) + for (x = 0; x < width - 2 * border; x += GRID_SIZE) + if (((x + y) / GRID_SIZE) % 2 == 0) + { + cairo_rectangle (cr, border + x, border + y, GRID_SIZE, GRID_SIZE); + cairo_fill (cr); + } + + if (border > 0) + { + cairo_set_source_rgb (cr, 0, 0, 1); + cairo_save (cr); + cairo_set_line_width (cr, border); + cairo_rectangle (cr, + border / 2., border / 2., width - border, height - border); + cairo_stroke (cr); + } + + return FALSE; +} + +static void +create_window (GdkWindowHints mask) +{ + GtkWidget *window; + GtkWidget *drawing_area; + GtkWidget *table; + GtkWidget *label; + GdkGeometry geometry; + GString *label_text = g_string_new (NULL); + int border = 0; + + if ((mask & GDK_HINT_RESIZE_INC) != 0) + g_string_append (label_text, "Gridded\n"); + if ((mask & GDK_HINT_BASE_SIZE) != 0) + g_string_append (label_text, "Base\n"); + if ((mask & GDK_HINT_MIN_SIZE) != 0) + { + g_string_append (label_text, "Minimum\n"); + if ((mask & GDK_HINT_BASE_SIZE) == 0) + g_string_append (label_text, "(base=min)\n"); + } + if ((mask & GDK_HINT_MAX_SIZE) != 0) + g_string_append (label_text, "Maximum\n"); + + if (label_text->len > 0) + g_string_erase (label_text, label_text->len - 1, 1); + else + g_string_append (label_text, "No Options"); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + g_signal_connect (window, "destroy", + G_CALLBACK (on_window_destroy), NULL); + + table = gtk_table_new (0, 0, FALSE); + gtk_container_set_border_width (GTK_CONTAINER (table), 10); + + label = gtk_label_new (label_text->str); + gtk_table_attach (GTK_TABLE (table), label, + 0, 1, 0, 1, + GTK_EXPAND | GTK_FILL, GTK_FILL, + 0, 0); + + label = gtk_label_new ("A\nB\nC\nD\nE"); + gtk_table_attach (GTK_TABLE (table), label, + 1, 2, 1, 2, + GTK_FILL, GTK_EXPAND | GTK_FILL, + 0, 0); + + drawing_area = gtk_drawing_area_new (); + g_signal_connect (drawing_area, "draw", + G_CALLBACK (on_drawing_area_draw), + GUINT_TO_POINTER (mask)); + gtk_table_attach (GTK_TABLE (table), drawing_area, + 0, 1, 1, 2, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, + 0, 0); + + gtk_container_add (GTK_CONTAINER (window), table); + + if ((mask & GDK_HINT_BASE_SIZE) != 0) + { + border = BORDER; + geometry.base_width = border * 2; + geometry.base_height = border * 2; + } + + if ((mask & GDK_HINT_RESIZE_INC) != 0) + { + geometry.width_inc = GRID_SIZE; + geometry.height_inc = GRID_SIZE; + } + + if ((mask & GDK_HINT_MIN_SIZE) != 0) + { + geometry.min_width = 5 * GRID_SIZE + 2 * border; + geometry.min_height = 5 * GRID_SIZE + 2 * border; + } + + if ((mask & GDK_HINT_MAX_SIZE) != 0) + { + geometry.max_width = 15 * GRID_SIZE + 2 * border; + geometry.max_height = 15 * GRID_SIZE + 2 * border; + } + + /* Contents must be set up before gtk_window_parse_geometry() */ + gtk_widget_show_all (table); + + gtk_window_set_geometry_hints (GTK_WINDOW (window), + drawing_area, + &geometry, + mask); + + if ((mask & GDK_HINT_RESIZE_INC) != 0) + { + if (geometry_string) + gtk_window_parse_geometry (GTK_WINDOW (window), geometry_string); + } + + gtk_widget_show (window); + window_count++; +} + +int +main(int argc, char **argv) +{ + GError *error; + GOptionEntry options[] = { + { "geometry", 'g', 0, G_OPTION_ARG_STRING, &geometry_string, "Window geometry (only for gridded windows)", "GEOMETRY" }, + { NULL } + }; + + if (!gtk_init_with_args (&argc, &argv, "", options, NULL, &error)) + { + g_print ("Failed to parse args: %s\n", error->message); + g_error_free (error); + return 1; + } + + create_window (GDK_HINT_MIN_SIZE); + create_window (GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE); + create_window (GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE); + create_window (GDK_HINT_RESIZE_INC | GDK_HINT_MIN_SIZE); + create_window (GDK_HINT_RESIZE_INC | GDK_HINT_MAX_SIZE); + create_window (GDK_HINT_RESIZE_INC | GDK_HINT_BASE_SIZE); + create_window (GDK_HINT_RESIZE_INC | GDK_HINT_BASE_SIZE | GDK_HINT_MIN_SIZE); + + gtk_main (); +} diff --git a/tests/testgtk.c b/tests/testgtk.c index 89d648b69..17136cfc5 100644 --- a/tests/testgtk.c +++ b/tests/testgtk.c @@ -1644,173 +1644,6 @@ create_statusbar (GtkWidget *widget) gtk_widget_destroy (window); } -/* - * Gridded geometry - */ -#define GRID_SIZE 20 -#define DEFAULT_GEOMETRY "10x10" - -static gboolean -gridded_geometry_draw (GtkWidget *widget, - cairo_t *cr) -{ - GtkStateType state; - GtkStyle *style; - int i, j, width, height; - - style = gtk_widget_get_style (widget); - state = gtk_widget_get_state (widget); - width = gtk_widget_get_allocated_width (widget); - height = gtk_widget_get_allocated_height (widget); - - gdk_cairo_set_source_color (cr, &style->base[state]); - cairo_paint (cr); - - for (i = 0 ; i * GRID_SIZE < width; i++) - for (j = 0 ; j * GRID_SIZE < height; j++) - { - if ((i + j) % 2 == 0) - cairo_rectangle (cr, i * GRID_SIZE, j * GRID_SIZE, GRID_SIZE, GRID_SIZE); - } - - gdk_cairo_set_source_color (cr, &style->text[state]); - cairo_fill (cr); - - return FALSE; -} - -static void -gridded_geometry_subresponse (GtkDialog *dialog, - gint response_id, - gchar *geometry_string) -{ - if (response_id == GTK_RESPONSE_NONE) - { - gtk_widget_destroy (GTK_WIDGET (dialog)); - } - else - { - if (!gtk_window_parse_geometry (GTK_WINDOW (dialog), geometry_string)) - { - g_print ("Can't parse geometry string %s\n", geometry_string); - gtk_window_parse_geometry (GTK_WINDOW (dialog), DEFAULT_GEOMETRY); - } - } -} - -static void -gridded_geometry_response (GtkDialog *dialog, - gint response_id, - GtkEntry *entry) -{ - if (response_id == GTK_RESPONSE_NONE) - { - gtk_widget_destroy (GTK_WIDGET (dialog)); - } - else - { - gchar *geometry_string = g_strdup (gtk_entry_get_text (entry)); - gchar *title = g_strdup_printf ("Gridded window at: %s", geometry_string); - GtkWidget *content_area; - GtkWidget *window; - GtkWidget *drawing_area; - GtkWidget *box; - GdkGeometry geometry; - - window = gtk_dialog_new_with_buttons (title, - NULL, 0, - "Reset", 1, - GTK_STOCK_CLOSE, GTK_RESPONSE_NONE, - NULL); - - gtk_window_set_screen (GTK_WINDOW (window), - gtk_widget_get_screen (GTK_WIDGET (dialog))); - g_free (title); - g_signal_connect (window, "response", - G_CALLBACK (gridded_geometry_subresponse), geometry_string); - - content_area = gtk_dialog_get_content_area (GTK_DIALOG (window)); - - box = gtk_vbox_new (FALSE, 0); - gtk_box_pack_start (GTK_BOX (content_area), box, TRUE, TRUE, 0); - - gtk_container_set_border_width (GTK_CONTAINER (box), 7); - - drawing_area = gtk_drawing_area_new (); - g_signal_connect (drawing_area, "draw", - G_CALLBACK (gridded_geometry_draw), NULL); - gtk_box_pack_start (GTK_BOX (box), drawing_area, TRUE, TRUE, 0); - - /* Gross hack to work around bug 68668... if we set the size request - * large enough, then the current - * - * request_of_window - request_of_geometry_widget - * - * method of getting the base size works more or less works. - */ - gtk_widget_set_size_request (drawing_area, 2000, 2000); - - geometry.base_width = 0; - geometry.base_height = 0; - geometry.min_width = 2 * GRID_SIZE; - geometry.min_height = 2 * GRID_SIZE; - geometry.width_inc = GRID_SIZE; - geometry.height_inc = GRID_SIZE; - - gtk_window_set_geometry_hints (GTK_WINDOW (window), drawing_area, - &geometry, - GDK_HINT_BASE_SIZE | GDK_HINT_MIN_SIZE | GDK_HINT_RESIZE_INC); - - if (!gtk_window_parse_geometry (GTK_WINDOW (window), geometry_string)) - { - g_print ("Can't parse geometry string %s\n", geometry_string); - gtk_window_parse_geometry (GTK_WINDOW (window), DEFAULT_GEOMETRY); - } - - gtk_widget_show_all (window); - } -} - -static void -create_gridded_geometry (GtkWidget *widget) -{ - static GtkWidget *window = NULL; - gpointer window_ptr; - GtkWidget *content_area; - GtkWidget *entry; - GtkWidget *label; - - if (!window) - { - window = gtk_dialog_new_with_buttons ("Gridded Geometry", - NULL, 0, - "Create", 1, - GTK_STOCK_CLOSE, GTK_RESPONSE_NONE, - NULL); - - gtk_window_set_screen (GTK_WINDOW (window), - gtk_widget_get_screen (widget)); - - content_area = gtk_dialog_get_content_area (GTK_DIALOG (window)); - - label = gtk_label_new ("Geometry string:"); - gtk_box_pack_start (GTK_BOX (content_area), label, FALSE, FALSE, 0); - - entry = gtk_entry_new (); - gtk_entry_set_text (GTK_ENTRY (entry), DEFAULT_GEOMETRY); - gtk_box_pack_start (GTK_BOX (content_area), entry, FALSE, FALSE, 0); - - g_signal_connect (window, "response", - G_CALLBACK (gridded_geometry_response), entry); - window_ptr = &window; - g_object_add_weak_pointer (G_OBJECT (window), window_ptr); - - gtk_widget_show_all (window); - } - else - gtk_widget_destroy (window); -} - /* * GtkHandleBox */ @@ -10152,7 +9985,6 @@ struct { { "flipping", create_flipping }, { "focus", create_focus }, { "font selection", create_font_selection }, - { "gridded geometry", create_gridded_geometry }, { "handle box", create_handle_box }, { "image", create_image }, { "key lookup", create_key_lookup }, -- 2.43.2