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
This commit is contained in:
Owen W. Taylor 2010-10-09 22:15:34 -04:00
parent c250a7e673
commit 88cf547029
13 changed files with 463 additions and 239 deletions

View File

@ -382,6 +382,7 @@ gtk_private_h_sources = \
gtkrecentchooserdefault.h \
gtkrecentchooserprivate.h \
gtkrecentchooserutils.h \
gtksizegroup-private.h \
gtksocketprivate.h \
gtktextbtree.h \
gtktextbufferserialize.h\

View File

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

View File

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

View File

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

View File

@ -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 <gtk/gtksizegroup.h>
/**
* 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__ */

View File

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

View File

@ -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__ */

View File

@ -23,7 +23,7 @@
#include <config.h>
#include "gtksizerequest.h"
#include "gtksizegroup.h"
#include "gtksizegroup-private.h"
#include "gtkdebug.h"
#include "gtkprivate.h"
#include "gtkintl.h"

View File

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

View File

@ -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,
&current_width, &current_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
{

View File

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

203
tests/testgeometry.c Normal file
View File

@ -0,0 +1,203 @@
/* testgeometry.c
* Author: Owen Taylor <otaylor@redhat.com>
* 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 <gtk/gtk.h>
#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 ();
}

View File

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