native-layout: Introduce GtkExtendedLayout interface.

This commit is contained in:
Johannes Schmmid 2009-12-02 09:48:42 +01:00 committed by Johannes Schmid
parent a8ec02c930
commit e08d04b561
16 changed files with 1387 additions and 435 deletions

View File

@ -202,6 +202,7 @@ gtk_public_h_sources = \
gtkenums.h \
gtkeventbox.h \
gtkexpander.h \
gtkextendedlayout.h \
gtkfilechooser.h \
gtkfilechooserbutton.h \
gtkfilechooserdialog.h \
@ -455,6 +456,7 @@ gtk_base_c_sources = \
gtkentrycompletion.c \
gtkeventbox.c \
gtkexpander.c \
gtkextendedlayout.c \
gtkfilechooser.c \
gtkfilechooserbutton.c \
gtkfilechooserdefault.c \

View File

@ -83,6 +83,7 @@
#include <gtk/gtkenums.h>
#include <gtk/gtkeventbox.h>
#include <gtk/gtkexpander.h>
#include <gtk/gtkextendedlayout.h>
#include <gtk/gtkfixed.h>
#include <gtk/gtkfilechooser.h>
#include <gtk/gtkfilechooserbutton.h>

View File

@ -1500,6 +1500,24 @@ gtk_expander_set_use_underline
#endif
#endif
#if IN_HEADER(__GTK_EXTENDED_LAYOUT_H__)
#if IN_FILE(__GTK_EXTENDED_LAYOUT_C__)
gtk_extended_layout_get_type G_GNUC_CONST
gtk_extended_layout_get_desired_size
gtk_extended_layout_get_height_for_width
gtk_extended_layout_get_width_for_height
#endif
#endif
#if IN_HEADER(__GTK_EXTENDED_LAYOUT_H__)
#if IN_FILE(__GTK_EXTENDED_LAYOUT_C__)
gtk_extended_layout_get_type G_GNUC_CONST
gtk_extended_layout_get_desired_size
gtk_extended_layout_get_height_for_width
gtk_extended_layout_get_width_for_height
#endif
#endif
#if IN_HEADER(__GTK_FILE_CHOOSER_H__)
#if IN_FILE(__GTK_FILE_CHOOSER_C__)
gtk_file_chooser_add_filter
@ -2248,6 +2266,7 @@ gtk_label_get_type G_GNUC_CONST
gtk_label_get_use_markup
gtk_label_get_use_underline
gtk_label_get_width_chars
gtk_label_get_full_size
gtk_label_new
gtk_label_new_with_mnemonic
gtk_label_select_region
@ -2271,6 +2290,7 @@ gtk_label_set_use_markup
gtk_label_set_use_underline
gtk_label_set_width_chars
gtk_label_get_current_uri
gtk_label_set_full_size
gtk_label_set_track_visited_links
gtk_label_get_track_visited_links
#endif
@ -5200,6 +5220,9 @@ gtk_widget_show_all
gtk_widget_show_now
gtk_widget_size_allocate
gtk_widget_size_request
gtk_widget_get_desired_size
gtk_widget_get_height_for_width
gtk_widget_get_width_for_height
gtk_widget_style_get G_GNUC_NULL_TERMINATED
gtk_widget_style_get_property
gtk_widget_style_get_valist

View File

@ -26,6 +26,7 @@
#include "config.h"
#include "gtkalignment.h"
#include "gtkextendedlayout.h"
#include "gtkprivate.h"
#include "gtkintl.h"
#include "gtkalias.h"
@ -458,7 +459,9 @@ gtk_alignment_size_allocate (GtkWidget *widget,
if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
{
gtk_widget_get_child_requisition (bin->child, &child_requisition);
GtkExtendedLayout *layout = GTK_EXTENDED_LAYOUT (bin->child);
gtk_extended_layout_get_desired_size (layout, NULL, &child_requisition);
border_width = GTK_CONTAINER (alignment)->border_width;
@ -468,14 +471,21 @@ gtk_alignment_size_allocate (GtkWidget *widget,
width = MAX (1, allocation->width - padding_horizontal - 2 * border_width);
height = MAX (1, allocation->height - padding_vertical - 2 * border_width);
if (child_requisition.width > width)
gtk_extended_layout_get_height_for_width (layout, width, NULL,
&child_requisition.height);
else if (child_requisition.height > height)
gtk_extended_layout_get_width_for_height (layout, height, NULL,
&child_requisition.width);
if (width > child_requisition.width)
child_allocation.width = (child_requisition.width *
(1.0 - alignment->xscale) +
width * alignment->xscale);
else
child_allocation.width = width;
if (height > child_requisition.height)
child_allocation.height = (child_requisition.height *
(1.0 - alignment->yscale) +

View File

@ -28,6 +28,7 @@
#include "gtkbox.h"
#include "gtkorientable.h"
#include "gtkextendedlayout.h"
#include "gtkprivate.h"
#include "gtkintl.h"
#include "gtkalias.h"
@ -60,6 +61,27 @@ struct _GtkBoxPrivate
#define GTK_BOX_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_BOX, GtkBoxPrivate))
typedef struct _GtkBoxDesiredSizes GtkBoxDesiredSizes;
typedef struct _GtkBoxSpreading GtkBoxSpreading;
struct _GtkBoxDesiredSizes
{
gint minimum_size;
gint natural_size;
};
struct _GtkBoxSpreading
{
GtkBoxChild *child;
gint index;
};
static void gtk_box_get_desired_size (GtkExtendedLayout *layout,
GtkRequisition *minimum_size,
GtkRequisition *natural_size);
static void gtk_box_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
static void gtk_box_layout_interface_init (GtkExtendedLayoutIface *iface);
static void gtk_box_set_property (GObject *object,
guint prop_id,
@ -70,11 +92,6 @@ static void gtk_box_get_property (GObject *object,
GValue *value,
GParamSpec *pspec);
static void gtk_box_size_request (GtkWidget *widget,
GtkRequisition *requisition);
static void gtk_box_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
static void gtk_box_add (GtkContainer *container,
GtkWidget *widget);
static void gtk_box_remove (GtkContainer *container,
@ -98,7 +115,9 @@ static GType gtk_box_child_type (GtkContainer *container);
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkBox, gtk_box, GTK_TYPE_CONTAINER,
G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
NULL));
NULL)
G_IMPLEMENT_INTERFACE (GTK_TYPE_EXTENDED_LAYOUT,
gtk_box_layout_interface_init));
static void
gtk_box_class_init (GtkBoxClass *class)
@ -110,7 +129,6 @@ gtk_box_class_init (GtkBoxClass *class)
object_class->set_property = gtk_box_set_property;
object_class->get_property = gtk_box_get_property;
widget_class->size_request = gtk_box_size_request;
widget_class->size_allocate = gtk_box_size_allocate;
container_class->add = gtk_box_add;
@ -181,6 +199,12 @@ gtk_box_class_init (GtkBoxClass *class)
g_type_class_add_private (object_class, sizeof (GtkBoxPrivate));
}
static void
gtk_box_layout_interface_init (GtkExtendedLayoutIface *iface)
{
iface->get_desired_size = gtk_box_get_desired_size;
}
static void
gtk_box_init (GtkBox *box)
{
@ -252,78 +276,143 @@ gtk_box_get_property (GObject *object,
}
static void
gtk_box_size_request (GtkWidget *widget,
GtkRequisition *requisition)
gtk_box_get_desired_size (GtkExtendedLayout *layout,
GtkRequisition *minimum_size,
GtkRequisition *natural_size)
{
GtkBox *box = GTK_BOX (widget);
GtkBoxPrivate *private = GTK_BOX_GET_PRIVATE (box);
GtkBoxChild *child;
GtkBox *box;
GtkBoxPrivate *private;
GList *children;
gint nvis_children;
gint width;
gint height;
gint border_width;
box = GTK_BOX (layout);
private = GTK_BOX_GET_PRIVATE (box);
border_width = GTK_CONTAINER (box)->border_width;
minimum_size->width = minimum_size->height = 0;
natural_size->width = natural_size->height = 0;
requisition->width = 0;
requisition->height = 0;
nvis_children = 0;
children = box->children;
while (children)
{
GtkBoxChild *child;
child = children->data;
children = children->next;
if (GTK_WIDGET_VISIBLE (child->widget))
{
GtkRequisition child_requisition;
{
GtkRequisition child_minimum_size;
GtkRequisition child_natural_size;
gtk_widget_size_request (child->widget, &child_requisition);
if (box->homogeneous)
{
width = child_requisition.width + child->padding * 2;
height = child_requisition.height + child->padding * 2;
if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
requisition->width = MAX (requisition->width, width);
else
requisition->height = MAX (requisition->height, height);
}
else
{
if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
requisition->width += child_requisition.width + child->padding * 2;
else
requisition->height += child_requisition.height + child->padding * 2;
}
gtk_widget_get_desired_size (child->widget,
&child_minimum_size,
&child_natural_size);
if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
requisition->height = MAX (requisition->height, child_requisition.height);
{
if (box->homogeneous)
{
gint width;
width = child_minimum_size.width + child->padding * 2;
minimum_size->width = MAX (minimum_size->width, width);
width = child_natural_size.width + child->padding * 2;
natural_size->width = MAX (natural_size->width, width);
}
else
{
minimum_size->width += child_minimum_size.width + child->padding * 2;
natural_size->width += child_natural_size.width + child->padding * 2;
}
minimum_size->height = MAX (minimum_size->height, child_minimum_size.height);
natural_size->height = MAX (natural_size->height, child_natural_size.height);
}
else
requisition->width = MAX (requisition->width, child_requisition.width);
{
if (box->homogeneous)
{
gint height;
nvis_children += 1;
}
height = child_minimum_size.height + child->padding * 2;
minimum_size->height = MAX (minimum_size->height, height);
height = child_natural_size.height + child->padding * 2;
natural_size->height = MAX (natural_size->height, height);
}
else
{
minimum_size->height += child_minimum_size.height + child->padding * 2;
natural_size->height += child_natural_size.height + child->padding * 2;
}
minimum_size->width = MAX (minimum_size->width, child_minimum_size.width);
natural_size->width = MAX (natural_size->width, child_natural_size.width);
}
nvis_children += 1;
}
}
if (nvis_children > 0)
{
if (box->homogeneous)
{
if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
requisition->width *= nvis_children;
else
requisition->height *= nvis_children;
}
if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
requisition->width += (nvis_children - 1) * box->spacing;
{
if (box->homogeneous)
{
minimum_size->width *= nvis_children;
natural_size->width *= nvis_children;
}
minimum_size->width += (nvis_children - 1) * box->spacing;
natural_size->width += (nvis_children - 1) * box->spacing;
}
else
requisition->height += (nvis_children - 1) * box->spacing;
{
if (box->homogeneous)
{
minimum_size->height *= nvis_children;
natural_size->height *= nvis_children;
}
minimum_size->height += (nvis_children - 1) * box->spacing;
natural_size->height += (nvis_children - 1) * box->spacing;
}
}
requisition->width += GTK_CONTAINER (box)->border_width * 2;
requisition->height += GTK_CONTAINER (box)->border_width * 2;
minimum_size->width += border_width * 2;
minimum_size->height += border_width * 2;
natural_size->width += border_width * 2;
natural_size->height += border_width * 2;
}
static gint
gtk_box_compare_gap (gconstpointer p1,
gconstpointer p2,
gpointer data)
{
GtkBoxDesiredSizes *sizes = data;
const GtkBoxSpreading *c1 = p1;
const GtkBoxSpreading *c2 = p2;
const gint d1 = MAX (sizes[c1->index].natural_size -
sizes[c1->index].minimum_size,
0);
const gint d2 = MAX (sizes[c2->index].natural_size -
sizes[c2->index].minimum_size,
0);
gint delta = (d2 - d1);
if (0 == delta)
delta = (c2->index - c1->index);
return delta;
}
static void
@ -334,21 +423,13 @@ gtk_box_size_allocate (GtkWidget *widget,
GtkBoxPrivate *private = GTK_BOX_GET_PRIVATE (box);
GtkBoxChild *child;
GList *children;
GtkAllocation child_allocation;
gint nvis_children = 0;
gint nexpand_children = 0;
gint child_width = 0;
gint child_height = 0;
gint width = 0;
gint height = 0;
gint extra = 0;
gint x = 0;
gint y = 0;
GtkTextDirection direction;
gint nvis_children;
gint nexpand_children;
widget->allocation = *allocation;
direction = gtk_widget_get_direction (widget);
nvis_children = 0;
nexpand_children = 0;
for (children = box->children; children; children = children->next)
{
@ -364,239 +445,221 @@ gtk_box_size_allocate (GtkWidget *widget,
if (nvis_children > 0)
{
if (box->homogeneous)
{
if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
{
width = (allocation->width -
GTK_CONTAINER (box)->border_width * 2 -
(nvis_children - 1) * box->spacing);
extra = width / nvis_children;
}
else
{
height = (allocation->height -
GTK_CONTAINER (box)->border_width * 2 -
(nvis_children - 1) * box->spacing);
extra = height / nvis_children;
}
}
else if (nexpand_children > 0)
{
if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
{
width = (gint) allocation->width - (gint) widget->requisition.width;
extra = width / nexpand_children;
}
else
{
height = (gint) allocation->height - (gint) widget->requisition.height;
extra = height / nexpand_children;
}
}
gint border_width = GTK_CONTAINER (box)->border_width;
GtkTextDirection direction = gtk_widget_get_direction (widget);
GtkAllocation child_allocation;
GtkBoxSpreading *spreading = g_newa (GtkBoxSpreading, nvis_children);
GtkBoxDesiredSizes *sizes = g_newa (GtkBoxDesiredSizes, nvis_children);
GtkPackType packing;
gint size;
gint extra;
gint x, y, i;
gint child_size;
if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
{
x = allocation->x + GTK_CONTAINER (box)->border_width;
child_allocation.y = allocation->y + GTK_CONTAINER (box)->border_width;
child_allocation.height = MAX (1, (gint) allocation->height - (gint) GTK_CONTAINER (box)->border_width * 2);
size = allocation->width - border_width * 2 - (nvis_children - 1) * box->spacing;
else
size = allocation->height - border_width * 2 - (nvis_children - 1) * box->spacing;
if (box->homogeneous)
{
extra = size / nvis_children;
}
else
{
y = allocation->y + GTK_CONTAINER (box)->border_width;
child_allocation.x = allocation->x + GTK_CONTAINER (box)->border_width;
child_allocation.width = MAX (1, (gint) allocation->width - (gint) GTK_CONTAINER (box)->border_width * 2);
{
/* Retrieve desired size for visible children */
i = 0;
children = box->children;
while (children)
{
child = children->data;
children = children->next;
if (GTK_WIDGET_VISIBLE (child->widget))
{
if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
gtk_widget_get_width_for_height (child->widget,
allocation->height,
&sizes[i].minimum_size,
&sizes[i].natural_size);
else
gtk_widget_get_height_for_width (child->widget,
allocation->width,
&sizes[i].minimum_size,
&sizes[i].natural_size);
size -= sizes[i].minimum_size;
spreading[i].index = i;
spreading[i].child = child;
i += 1;
}
}
/* Distribute the container's extra space c_gap. We want to assign
* this space such that the sum of extra space assigned to children
* (c^i_gap) is equal to c_cap. The case that there's not enough
* space for all children to take their natural size needs some
* attention. The goals we want to achieve are:
*
* a) Maximize number of children taking their natural size.
* b) The allocated size of children should be a continuous
* function of c_gap. That is, increasing the container size by
* one pixel should never make drastic changes in the distribution.
* c) If child i takes its natural size and child j doesn't,
* child j should have received at least as much gap as child i.
*
* The following code distributes the additional space by following
* this rules.
*/
/* Sort descending by gap and position. */
g_qsort_with_data (spreading,
nvis_children, sizeof (GtkBoxSpreading),
gtk_box_compare_gap, sizes);
/* Distribute available space.
* This master piece of a loop was conceived by Behdad Esfahbod.
*/
for (i = nvis_children - 1; i >= 0; --i)
{
/* Divide remaining space by number of remaining children.
* Sort order and reducing remaining space by assigned space
* ensures that space is distributed equally.
*/
gint glue = (size + i) / (i + 1);
gint gap = sizes[spreading[i].index].natural_size
- sizes[spreading[i].index].minimum_size;
extra = MIN (glue, gap);
sizes[spreading[i].index].minimum_size += extra;
size -= extra;
}
/* Calculate space which hasn't distributed yet,
* and is available for expanding children.
*/
if (nexpand_children > 0)
extra = size / nexpand_children;
else
extra = 0;
}
children = box->children;
while (children)
{
child = children->data;
children = children->next;
/* Allocate child positions. */
if ((child->pack == GTK_PACK_START) && GTK_WIDGET_VISIBLE (child->widget))
{
if (box->homogeneous)
{
if (nvis_children == 1)
{
child_width = width;
child_height = height;
}
else
{
child_width = extra;
child_height = extra;
}
nvis_children -= 1;
width -= extra;
height -= extra;
}
else
{
GtkRequisition child_requisition;
gtk_widget_get_child_requisition (child->widget, &child_requisition);
child_width = child_requisition.width + child->padding * 2;
child_height = child_requisition.height + child->padding * 2;
if (child->expand)
{
if (nexpand_children == 1)
{
child_width += width;
child_height += height;
}
else
{
child_width += extra;
child_height += extra;
}
nexpand_children -= 1;
width -= extra;
height -= extra;
}
}
if (child->fill)
{
if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
{
child_allocation.width = MAX (1, (gint) child_width - (gint) child->padding * 2);
child_allocation.x = x + child->padding;
}
else
{
child_allocation.height = MAX (1, child_height - (gint)child->padding * 2);
child_allocation.y = y + child->padding;
}
}
else
{
GtkRequisition child_requisition;
gtk_widget_get_child_requisition (child->widget, &child_requisition);
if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
{
child_allocation.width = child_requisition.width;
child_allocation.x = x + (child_width - child_allocation.width) / 2;
}
else
{
child_allocation.height = child_requisition.height;
child_allocation.y = y + (child_height - child_allocation.height) / 2;
}
}
if (direction == GTK_TEXT_DIR_RTL &&
private->orientation == GTK_ORIENTATION_HORIZONTAL)
{
child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
}
gtk_widget_size_allocate (child->widget, &child_allocation);
x += child_width + box->spacing;
y += child_height + box->spacing;
}
}
x = allocation->x + allocation->width - GTK_CONTAINER (box)->border_width;
y = allocation->y + allocation->height - GTK_CONTAINER (box)->border_width;
children = box->children;
while (children)
{
child = children->data;
children = children->next;
if ((child->pack == GTK_PACK_END) && GTK_WIDGET_VISIBLE (child->widget))
{
GtkRequisition child_requisition;
gtk_widget_get_child_requisition (child->widget, &child_requisition);
if (box->homogeneous)
{
if (nvis_children == 1)
{
child_width = width;
child_height = height;
}
else
{
child_width = extra;
child_height = extra;
}
nvis_children -= 1;
width -= extra;
height -= extra;
}
for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
{
if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
{
child_allocation.y = allocation->y + border_width;
child_allocation.height = MAX (1, allocation->height - border_width * 2);
if (packing == GTK_PACK_START)
x = allocation->x + border_width;
else
{
child_width = child_requisition.width + child->padding * 2;
child_height = child_requisition.height + child->padding * 2;
x = allocation->x + allocation->width - border_width;
}
else
{
child_allocation.x = allocation->x + border_width;
child_allocation.width = MAX (1, allocation->width - border_width * 2);
if (packing == GTK_PACK_START)
y = allocation->y + border_width;
else
y = allocation->y + allocation->height - border_width;
}
if (child->expand)
i = 0;
children = box->children;
while (children)
{
child = children->data;
children = children->next;
if (GTK_WIDGET_VISIBLE (child->widget))
{
if (child->pack == packing)
{
if (nexpand_children == 1)
/* Assign the child's size. */
if (box->homogeneous)
{
if (nvis_children == 1)
child_size = size;
else
child_size = extra;
nvis_children -= 1;
size -= extra;
}
else
{
child_size = sizes[i].minimum_size + child->padding * 2;
if (child->expand)
{
if (nexpand_children == 1)
child_size += size;
else
child_size += extra;
nexpand_children -= 1;
size -= extra;
}
}
/* Assign the child's position. */
if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
{
child_width += width;
child_height += height;
}
if (child->fill)
{
child_allocation.width = MAX (1, child_size - child->padding * 2);
child_allocation.x = x + child->padding;
}
else
{
child_allocation.width = sizes[i].minimum_size;
child_allocation.x = x + (child_size - child_allocation.width) / 2;
}
if (direction == GTK_TEXT_DIR_RTL)
child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
if (packing == GTK_PACK_START)
x += child_size + box->spacing;
else
x -= child_size + box->spacing;
}
else
{
child_width += extra;
child_height += extra;
if (child->fill)
{
child_allocation.height = MAX (1, child_size - child->padding * 2);
child_allocation.y = y + child->padding;
}
else
{
child_allocation.height = sizes[i].minimum_size;
child_allocation.y = y + (child_size - child_allocation.height) / 2;
}
if (packing == GTK_PACK_START)
y += child_size + box->spacing;
else
y -= child_size + box->spacing;
}
nexpand_children -= 1;
width -= extra;
height -= extra;
gtk_widget_size_allocate (child->widget, &child_allocation);
}
i += 1;
}
if (child->fill)
{
if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
{
child_allocation.width = MAX (1, (gint)child_width - (gint)child->padding * 2);
child_allocation.x = x + child->padding - child_width;
}
else
{
child_allocation.height = MAX (1, child_height - (gint)child->padding * 2);
child_allocation.y = y + child->padding - child_height;
}
}
else
{
if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
{
child_allocation.width = child_requisition.width;
child_allocation.x = x + (child_width - child_allocation.width) / 2 - child_width;
}
else
{
child_allocation.height = child_requisition.height;
child_allocation.y = y + (child_height - child_allocation.height) / 2 - child_height;
}
}
if (direction == GTK_TEXT_DIR_RTL &&
private->orientation == GTK_ORIENTATION_HORIZONTAL)
{
child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
}
gtk_widget_size_allocate (child->widget, &child_allocation);
x -= (child_width + box->spacing);
y -= (child_height + box->spacing);
}
}
}

146
gtk/gtkextendedlayout.c Normal file
View File

@ -0,0 +1,146 @@
/* gtkextendedlayout.c
* Copyright (C) 2007 Openismus GmbH
*
* Author:
* Mathias Hasselmann <mathias@openismus.com>
*
* 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 <config.h>
#include "gtkextendedlayout.h"
#include "gtkintl.h"
#include "gtkalias.h"
GType
gtk_extended_layout_get_type (void)
{
static GType extended_layout_type = 0;
if (G_UNLIKELY(!extended_layout_type))
extended_layout_type =
g_type_register_static_simple (G_TYPE_INTERFACE, I_("GtkExtendedLayout"),
sizeof (GtkExtendedLayoutIface),
NULL, 0, NULL, 0);
return extended_layout_type;
}
/**
* gtk_extended_layout_get_desired_size:
* @layout: a #GtkExtendedLayout instance
* @minimum_size: location for storing the minimum size, or %NULL
* @natural_size: location for storing the preferred size, or %NULL
*
* Retreives an extended layout item's desired size.
*
* Since: 2.16
*/
void
gtk_extended_layout_get_desired_size (GtkExtendedLayout *layout,
GtkRequisition *minimum_size,
GtkRequisition *natural_size)
{
GtkExtendedLayoutIface *iface;
g_return_if_fail (GTK_IS_EXTENDED_LAYOUT (layout));
g_return_if_fail (NULL != minimum_size || NULL != natural_size);
iface = GTK_EXTENDED_LAYOUT_GET_IFACE (layout);
iface->get_desired_size (layout, minimum_size, natural_size);
}
/**
* gtk_extended_layout_get_width_for_height:
* @layout: a #GtkExtendedLayout instance
* @height: the size which is available for allocation
* @minimum_size: location for storing the minimum size, or %NULL
* @natural_size: location for storing the preferred size, or %NULL
*
* Retreives an extended layout item's desired width if it would given
* the size specified in @height.
*
* Since: 2.16
*/
void
gtk_extended_layout_get_width_for_height (GtkExtendedLayout *layout,
gint height,
gint *minimum_width,
gint *natural_width)
{
GtkExtendedLayoutIface *iface;
g_return_if_fail (GTK_IS_EXTENDED_LAYOUT (layout));
iface = GTK_EXTENDED_LAYOUT_GET_IFACE (layout);
if (iface->get_width_for_height)
iface->get_width_for_height (layout, height, minimum_width, natural_width);
else
{
GtkRequisition minimum_size;
GtkRequisition natural_size;
iface->get_desired_size (layout, &minimum_size, &natural_size);
if (minimum_width)
*minimum_width = minimum_size.width;
if (natural_width)
*natural_width = natural_size.width;
}
}
/**
* gtk_extended_layout_get_height_for_width:
* @layout: a #GtkExtendedLayout instance
* @width: the size which is available for allocation
* @minimum_size: location for storing the minimum size, or %NULL
* @natural_size: location for storing the preferred size, or %NULL
*
* Retreives an extended layout item's desired height if it would given
* the size specified in @width.
*
* Since: 2.16
*/
void
gtk_extended_layout_get_height_for_width (GtkExtendedLayout *layout,
gint width,
gint *minimum_height,
gint *natural_height)
{
GtkExtendedLayoutIface *iface;
g_return_if_fail (GTK_IS_EXTENDED_LAYOUT (layout));
iface = GTK_EXTENDED_LAYOUT_GET_IFACE (layout);
if (iface->get_height_for_width)
iface->get_height_for_width (layout, width, minimum_height, natural_height);
else
{
GtkRequisition minimum_size;
GtkRequisition natural_size;
iface->get_desired_size (layout, &minimum_size, &natural_size);
if (minimum_height)
*minimum_height = minimum_size.height;
if (natural_height)
*natural_height = natural_size.height;
}
}
#define __GTK_EXTENDED_LAYOUT_C__
#include "gtkaliasdef.c"

74
gtk/gtkextendedlayout.h Normal file
View File

@ -0,0 +1,74 @@
/* GTK - The GIMP Toolkit
* Copyright (C) 2007 Openismus GmbH
*
* Author:
* Mathias Hasselmann <mathias@openismus.com>
*
* 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.
*/
#ifndef __GTK_EXTENDED_LAYOUT_H__
#define __GTK_EXTENDED_LAYOUT_H__
#include <gtk/gtkwidget.h>
G_BEGIN_DECLS
#define GTK_TYPE_EXTENDED_LAYOUT (gtk_extended_layout_get_type ())
#define GTK_EXTENDED_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_EXTENDED_LAYOUT, GtkExtendedLayout))
#define GTK_EXTENDED_LAYOUT_CLASS(klass) ((GtkExtendedLayoutIface*)g_type_interface_peek ((klass), GTK_TYPE_EXTENDED_LAYOUT))
#define GTK_IS_EXTENDED_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_EXTENDED_LAYOUT))
#define GTK_EXTENDED_LAYOUT_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GTK_TYPE_EXTENDED_LAYOUT, GtkExtendedLayoutIface))
typedef struct _GtkExtendedLayout GtkExtendedLayout;
typedef struct _GtkExtendedLayoutIface GtkExtendedLayoutIface;
struct _GtkExtendedLayoutIface
{
GTypeInterface g_iface;
/* virtual table */
void (*get_desired_size) (GtkExtendedLayout *layout,
GtkRequisition *minimum_size,
GtkRequisition *natural_size);
void (*get_width_for_height) (GtkExtendedLayout *layout,
gint height,
gint *minimum_width,
gint *natural_width);
void (*get_height_for_width) (GtkExtendedLayout *layout,
gint width,
gint *minimum_height,
gint *natural_height);
};
GType gtk_extended_layout_get_type (void) G_GNUC_CONST;
void gtk_extended_layout_get_desired_size (GtkExtendedLayout *layout,
GtkRequisition *minimum_size,
GtkRequisition *natural_size);
void gtk_extended_layout_get_width_for_height (GtkExtendedLayout *layout,
gint height,
gint *minimum_width,
gint *natural_width);
void gtk_extended_layout_get_height_for_width (GtkExtendedLayout *layout,
gint width,
gint *minimum_height,
gint *natural_height);
G_END_DECLS
#endif /* __GTK_EXTENDED_LAYOUT_H__ */

View File

@ -48,6 +48,7 @@
#include "gtkimage.h"
#include "gtkshow.h"
#include "gtktooltip.h"
#include "gtkextendedlayout.h"
#include "gtkprivate.h"
#include "gtkalias.h"
@ -58,6 +59,7 @@ typedef struct
gint wrap_width;
gint width_chars;
gint max_width_chars;
gboolean full_size;
} GtkLabelPrivate;
/* Notes about the handling of links:
@ -148,7 +150,8 @@ enum {
PROP_SINGLE_LINE_MODE,
PROP_ANGLE,
PROP_MAX_WIDTH_CHARS,
PROP_TRACK_VISITED_LINKS
PROP_TRACK_VISITED_LINKS,
PROP_FULL_SIZE
};
static guint signals[LAST_SIGNAL] = { 0 };
@ -166,8 +169,6 @@ static void gtk_label_get_property (GObject *object,
GParamSpec *pspec);
static void gtk_label_destroy (GtkObject *object);
static void gtk_label_finalize (GObject *object);
static void gtk_label_size_request (GtkWidget *widget,
GtkRequisition *requisition);
static void gtk_label_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
static void gtk_label_state_changed (GtkWidget *widget,
@ -291,13 +292,29 @@ static void gtk_label_get_link_colors (GtkWidget *widget,
static void emit_activate_link (GtkLabel *label,
GtkLabelLink *link);
static void gtk_label_layout_interface_init (GtkExtendedLayoutIface *iface);
static void gtk_label_get_desired_size (GtkExtendedLayout *layout,
GtkRequisition *minimum_size,
GtkRequisition *natural_size);
static void gtk_label_get_width_for_height (GtkExtendedLayout *layout,
gint height,
gint *minimum_width,
gint *natural_width);
static void gtk_label_get_height_for_width (GtkExtendedLayout *layout,
gint width,
gint *minimum_height,
gint *natural_height);
static GQuark quark_angle = 0;
static GtkBuildableIface *buildable_parent_iface = NULL;
G_DEFINE_TYPE_WITH_CODE (GtkLabel, gtk_label, GTK_TYPE_MISC,
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
gtk_label_buildable_interface_init));
gtk_label_buildable_interface_init)
G_IMPLEMENT_INTERFACE (GTK_TYPE_EXTENDED_LAYOUT,
gtk_label_layout_interface_init));
static void
add_move_binding (GtkBindingSet *binding_set,
@ -322,6 +339,14 @@ add_move_binding (GtkBindingSet *binding_set,
G_TYPE_BOOLEAN, TRUE);
}
static void
gtk_label_layout_interface_init (GtkExtendedLayoutIface *iface)
{
iface->get_desired_size = gtk_label_get_desired_size;
iface->get_width_for_height = gtk_label_get_width_for_height;
iface->get_height_for_width = gtk_label_get_height_for_width;
}
static void
gtk_label_class_init (GtkLabelClass *class)
{
@ -338,7 +363,6 @@ gtk_label_class_init (GtkLabelClass *class)
object_class->destroy = gtk_label_destroy;
widget_class->size_request = gtk_label_size_request;
widget_class->size_allocate = gtk_label_size_allocate;
widget_class->state_changed = gtk_label_state_changed;
widget_class->style_set = gtk_label_style_set;
@ -728,6 +752,24 @@ gtk_label_class_init (GtkLabelClass *class)
P_("Whether visited links should be tracked"),
TRUE,
GTK_PARAM_READWRITE));
/**
* GtkLabel:full-size:
*
* Use the entire space the widget got assigned for text wrapping. Overrides
* any #GtkLabel:width-chars, #GtkLabel:max-width-chars and screen size based
* constraints. Requires #GtkLabel:angle to be 0°, 90°, 180° or 270°.
*
* Since: 2.18
**/
g_object_class_install_property (gobject_class,
PROP_FULL_SIZE,
g_param_spec_boolean ("full-size",
P_("Full size"),
P_("Use the entire size of the widget to wrap text"),
FALSE,
GTK_PARAM_READWRITE));
/*
* Key bindings
*/
@ -914,6 +956,9 @@ gtk_label_set_property (GObject *object,
case PROP_TRACK_VISITED_LINKS:
gtk_label_set_track_visited_links (label, g_value_get_boolean (value));
break;
case PROP_FULL_SIZE:
gtk_label_set_full_size (label, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -1000,6 +1045,9 @@ gtk_label_get_property (GObject *object,
case PROP_TRACK_VISITED_LINKS:
g_value_set_boolean (value, gtk_label_get_track_visited_links (label));
break;
case PROP_FULL_SIZE:
g_value_set_int (value, gtk_label_get_full_size (label));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -1119,7 +1167,6 @@ attribute_from_text (GtkBuilder *builder,
value, &val, error))
attribute = pango_attr_gravity_hint_new (g_value_get_enum (&val));
break;
/* PangoAttrString */
case PANGO_ATTR_FAMILY:
attribute = pango_attr_family_new (value);
@ -2891,14 +2938,14 @@ gtk_label_ensure_layout (GtkLabel *label)
PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
gdouble angle = gtk_label_get_angle (label);
if (angle != 0.0 && !label->wrap && !label->ellipsize && !label->select_info)
if (angle != 0.0 && !label->select_info)
{
PangoMatrix matrix = PANGO_MATRIX_INIT;
/* We rotate the standard singleton PangoContext for the widget,
* depending on the fact that it's meant pretty much exclusively
* for our use.
*/
PangoMatrix matrix = PANGO_MATRIX_INIT;
pango_matrix_rotate (&matrix, angle);
pango_context_set_matrix (gtk_widget_get_pango_context (widget), &matrix);
@ -2944,8 +2991,8 @@ gtk_label_ensure_layout (GtkLabel *label)
pango_layout_set_single_paragraph_mode (label->layout, label->single_line_mode);
if (label->ellipsize)
pango_layout_set_width (label->layout,
widget->allocation.width * PANGO_SCALE);
pango_layout_set_width (label->layout,
widget->allocation.width * PANGO_SCALE);
else if (label->wrap)
{
GtkWidgetAuxInfo *aux_info;
@ -2974,12 +3021,12 @@ gtk_label_ensure_layout (GtkLabel *label)
width = MIN (width, wrap_width);
width = MIN (width,
PANGO_SCALE * (gdk_screen_get_width (screen) + 1) / 2);
pango_layout_set_width (label->layout, width);
pango_layout_get_extents (label->layout, NULL, &logical_rect);
width = logical_rect.width;
height = logical_rect.height;
/* Unfortunately, the above may leave us with a very unbalanced looking paragraph,
* so we try short search for a narrower width that leaves us with the same height
*/
@ -3020,17 +3067,33 @@ gtk_label_ensure_layout (GtkLabel *label)
}
}
static void
gtk_label_size_request (GtkWidget *widget,
GtkRequisition *requisition)
static gint
get_single_line_height (GtkWidget *widget,
PangoLayout *layout)
{
GtkLabel *label = GTK_LABEL (widget);
GtkLabelPrivate *priv;
gint width, height;
PangoRectangle logical_rect;
GtkWidgetAuxInfo *aux_info;
PangoContext *context;
PangoFontMetrics *metrics;
gint ascent, descent;
priv = GTK_LABEL_GET_PRIVATE (widget);
context = pango_layout_get_context (layout);
metrics = pango_context_get_metrics (context, widget->style->font_desc,
pango_context_get_language (context));
ascent = pango_font_metrics_get_ascent (metrics);
descent = pango_font_metrics_get_descent (metrics);
pango_font_metrics_unref (metrics);
return PANGO_PIXELS (ascent + descent);
}
static void
gtk_label_get_desired_size (GtkExtendedLayout *layout,
GtkRequisition *minimum_size,
GtkRequisition *natural_size)
{
GtkLabelPrivate *priv = GTK_LABEL_GET_PRIVATE (layout);
GtkLabel *label = GTK_LABEL (layout);
PangoRectangle required_rect;
/*
* If word wrapping is on, then the height requisition can depend
@ -3050,61 +3113,152 @@ gtk_label_size_request (GtkWidget *widget,
gtk_label_ensure_layout (label);
width = label->misc.xpad * 2;
height = label->misc.ypad * 2;
aux_info = _gtk_widget_get_aux_info (widget, FALSE);
if (label->have_transform)
if (minimum_size)
{
PangoRectangle rect;
PangoContext *context = pango_layout_get_context (label->layout);
const PangoMatrix *matrix = pango_context_get_matrix (context);
GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (GTK_WIDGET (label), FALSE);
pango_layout_get_extents (label->layout, NULL, &rect);
pango_matrix_transform_rectangle (matrix, &rect);
pango_extents_to_pixels (&rect, NULL);
requisition->width = width + rect.width;
requisition->height = height + rect.height;
pango_layout_get_extents (label->layout, NULL, &required_rect);
required_rect.x = required_rect.y = 0;
return;
if (label->ellipsize || priv->width_chars > 0 || priv->max_width_chars > 0)
{
/* backup the Pango layout, as get_label_char_width() scrambles it */
PangoLayout *backup = label->layout;
label->layout = pango_layout_copy (label->layout);
required_rect.width = get_label_char_width (label);
g_object_unref (label->layout);
label->layout = backup;
}
if (label->single_line_mode)
required_rect.height = get_single_line_height (GTK_WIDGET (label), label->layout);
if (label->have_transform)
{
PangoContext *context = pango_layout_get_context (label->layout);
const PangoMatrix *matrix = pango_context_get_matrix (context);
pango_matrix_transform_rectangle (matrix, &required_rect);
}
required_rect.width = PANGO_PIXELS_CEIL (required_rect.width);
required_rect.height = PANGO_PIXELS_CEIL (required_rect.height);
if ((label->wrap || label->ellipsize ||
priv->width_chars > 0 || priv->max_width_chars > 0) &&
aux_info && aux_info->width > 0)
required_rect.width = aux_info->width;
minimum_size->width = required_rect.width + label->misc.xpad * 2;
minimum_size->height = required_rect.height + label->misc.ypad * 2;
}
else
pango_layout_get_extents (label->layout, NULL, &logical_rect);
if ((label->wrap || label->ellipsize ||
priv->width_chars > 0 || priv->max_width_chars > 0) &&
aux_info && aux_info->width > 0)
width += aux_info->width;
else if (label->ellipsize || priv->width_chars > 0 || priv->max_width_chars > 0)
if (natural_size)
{
width += PANGO_PIXELS (get_label_char_width (label));
}
else
width += PANGO_PIXELS (logical_rect.width);
PangoLayout *natural_layout = pango_layout_copy (label->layout);
if (label->single_line_mode)
pango_layout_set_width (natural_layout, -1);
pango_layout_set_ellipsize (natural_layout, PANGO_ELLIPSIZE_NONE);
pango_layout_get_extents (natural_layout, NULL, &required_rect);
required_rect.x = required_rect.y = 0;
if (label->single_line_mode)
required_rect.height = get_single_line_height (GTK_WIDGET (label), label->layout);
if (label->have_transform)
{
PangoContext *context = pango_layout_get_context (natural_layout);
const PangoMatrix *matrix = pango_context_get_matrix (context);
pango_matrix_transform_rectangle (matrix, &required_rect);
}
required_rect.width = PANGO_PIXELS_CEIL (required_rect.width);
required_rect.height = PANGO_PIXELS_CEIL (required_rect.height);
natural_size->width = required_rect.width + label->misc.xpad * 2;
natural_size->height = required_rect.height + label->misc.ypad * 2;
g_object_unref (natural_layout);
}
}
static void
get_size_for_allocation (GtkLabel *label,
gint allocation,
gint *minimum_size,
gint *natural_size)
{
PangoLayout *layout;
gtk_label_ensure_layout (label);
layout = pango_layout_copy (label->layout);
pango_layout_set_width (layout, PANGO_SCALE * allocation);
if (minimum_size)
pango_layout_get_pixel_size (layout, NULL, minimum_size);
if (natural_size)
{
PangoContext *context;
PangoFontMetrics *metrics;
gint ascent, descent;
context = pango_layout_get_context (label->layout);
metrics = pango_context_get_metrics (context, widget->style->font_desc,
pango_context_get_language (context));
ascent = pango_font_metrics_get_ascent (metrics);
descent = pango_font_metrics_get_descent (metrics);
pango_font_metrics_unref (metrics);
height += PANGO_PIXELS (ascent + descent);
// pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_NONE);
pango_layout_get_pixel_size (layout, NULL, natural_size);
}
else
height += PANGO_PIXELS (logical_rect.height);
requisition->width = width;
requisition->height = height;
g_object_unref (layout);
}
static void
gtk_label_get_width_for_height (GtkExtendedLayout *layout,
gint height,
gint *minimum_width,
gint *natural_width)
{
GtkLabel *label = GTK_LABEL (layout);
gdouble angle = gtk_label_get_angle (label);
if (90 == angle || 270 == angle)
get_size_for_allocation (label, height, minimum_width, natural_width);
else
{
GtkRequisition minimum_size, natural_size;
gtk_extended_layout_get_desired_size (layout,
minimum_width ? &minimum_size : NULL,
natural_width ? &natural_size : NULL);
if (minimum_width)
*minimum_width = minimum_size.width;
if (natural_width)
*natural_width = natural_size.width;
}
}
static void
gtk_label_get_height_for_width (GtkExtendedLayout *layout,
gint width,
gint *minimum_height,
gint *natural_height)
{
GtkLabel *label = GTK_LABEL (layout);
gdouble angle = gtk_label_get_angle (label);
if (0 == angle || 180 == angle)
get_size_for_allocation (label, width, minimum_height, natural_height);
else
{
GtkRequisition minimum_size, natural_size;
gtk_extended_layout_get_desired_size (layout,
minimum_height ? &minimum_size : NULL,
natural_height ? &natural_size : NULL);
if (minimum_height)
*minimum_height = minimum_size.height;
if (natural_height)
*natural_height = natural_size.height;
}
}
static void
@ -4945,6 +5099,32 @@ gtk_label_set_use_underline (GtkLabel *label,
gtk_label_recalculate (label);
}
gboolean
gtk_label_get_full_size (GtkLabel *label)
{
g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
return GTK_LABEL_GET_PRIVATE (label)->full_size;
}
void
gtk_label_set_full_size (GtkLabel *label,
gboolean setting)
{
GtkLabelPrivate *priv;
g_return_if_fail (GTK_IS_LABEL (label));
priv = GTK_LABEL_GET_PRIVATE (label);
if (priv->full_size != setting)
{
priv->full_size = setting;
g_object_notify (G_OBJECT (label), "full-size");
gtk_label_invalidate_wrap_width (label);
gtk_widget_queue_resize (GTK_WIDGET (label));
}
}
/**
* gtk_label_get_use_underline:
* @label: a #GtkLabel

View File

@ -126,6 +126,9 @@ gboolean gtk_label_get_use_markup (GtkLabel *label);
void gtk_label_set_use_underline (GtkLabel *label,
gboolean setting);
gboolean gtk_label_get_use_underline (GtkLabel *label);
void gtk_label_set_full_size (GtkLabel *label,
gboolean setting);
gboolean gtk_label_get_full_size (GtkLabel *label);
void gtk_label_set_markup_with_mnemonic (GtkLabel *label,
const gchar *str);

View File

@ -25,8 +25,18 @@
#include "gtkprivate.h"
#include "gtksizegroup.h"
#include "gtkbuildable.h"
#include "gtkextendedlayout.h"
#include "gtkalias.h"
#define GTK_SIZE_GROUP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_SIZE_GROUP, GtkSizeGroupPrivate))
typedef struct _GtkSizeGroupPrivate GtkSizeGroupPrivate;
struct _GtkSizeGroupPrivate
{
GtkRequisition natural_size;
};
enum {
PROP_0,
PROP_MODE,
@ -322,6 +332,7 @@ gtk_size_group_class_init (GtkSizeGroupClass *klass)
GTK_PARAM_READWRITE));
initialize_size_group_quarks ();
g_type_class_add_private (klass, sizeof (GtkSizeGroupPrivate));
}
static void
@ -596,25 +607,49 @@ gtk_size_group_get_widgets (GtkSizeGroup *size_group)
return size_group->widgets;
}
static gint
get_base_dimension (GtkWidget *widget,
GtkSizeGroupMode mode)
static void
get_base_dimensions (GtkWidget *widget,
GtkSizeGroupMode mode,
gint *minimum_size,
gint *natural_size)
{
GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (widget, FALSE);
if (mode == GTK_SIZE_GROUP_HORIZONTAL)
{
if (aux_info && aux_info->width > 0)
return aux_info->width;
else
return widget->requisition.width;
if (minimum_size)
{
if (aux_info && aux_info->width > 0)
*minimum_size = aux_info->width;
else
*minimum_size = widget->requisition.width;
}
if (natural_size)
{
if (aux_info)
*natural_size = aux_info->natural_size.width;
else
*natural_size = widget->requisition.width;
}
}
else
{
if (aux_info && aux_info->height > 0)
return aux_info->height;
else
return widget->requisition.height;
if (minimum_size)
{
if (aux_info && aux_info->height > 0)
*minimum_size = aux_info->height;
else
*minimum_size = widget->requisition.height;
}
if (natural_size)
{
if (aux_info)
*natural_size = aux_info->natural_size.height;
else
*natural_size = widget->requisition.height;
}
}
}
@ -623,26 +658,35 @@ do_size_request (GtkWidget *widget)
{
if (GTK_WIDGET_REQUEST_NEEDED (widget))
{
GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (widget, TRUE);
gtk_widget_ensure_style (widget);
GTK_PRIVATE_UNSET_FLAG (widget, GTK_REQUEST_NEEDED);
g_signal_emit_by_name (widget,
"size-request",
&widget->requisition);
gtk_extended_layout_get_desired_size (GTK_EXTENDED_LAYOUT (widget),
&widget->requisition,
&aux_info->natural_size);
g_assert (widget->requisition.width <= aux_info->natural_size.width);
g_assert (widget->requisition.height <= aux_info->natural_size.height);
}
}
static gint
compute_base_dimension (GtkWidget *widget,
GtkSizeGroupMode mode)
static void
compute_base_dimensions (GtkWidget *widget,
GtkSizeGroupMode mode,
gint *minimum_size,
gint *natural_size)
{
do_size_request (widget);
return get_base_dimension (widget, mode);
get_base_dimensions (widget, mode, minimum_size, natural_size);
}
static gint
compute_dimension (GtkWidget *widget,
GtkSizeGroupMode mode)
GtkSizeGroupMode mode,
gint *minimum_size,
gint *natural_size)
{
GSList *widgets = NULL;
GSList *groups = NULL;
@ -658,16 +702,26 @@ compute_dimension (GtkWidget *widget,
if (!groups)
{
result = compute_base_dimension (widget, mode);
compute_base_dimensions (widget, mode, minimum_size, natural_size);
}
else
{
GtkSizeGroup *group = groups->data;
GtkSizeGroupPrivate *priv = GTK_SIZE_GROUP_GET_PRIVATE (group);
gint result_minimum_size = 0;
gint result_natural_size = 0;
if (mode == GTK_SIZE_GROUP_HORIZONTAL && group->have_width)
result = group->requisition.width;
{
result_minimum_size = group->requisition.width;
result_natural_size = priv->natural_size.width;
}
else if (mode == GTK_SIZE_GROUP_VERTICAL && group->have_height)
result = group->requisition.height;
{
result_minimum_size = group->requisition.height;
result_natural_size = priv->natural_size.height;
}
else
{
tmp_list = widgets;
@ -675,13 +729,20 @@ compute_dimension (GtkWidget *widget,
{
GtkWidget *tmp_widget = tmp_list->data;
gint dimension = compute_base_dimension (tmp_widget, mode);
gint tmp_widget_minimum_size;
gint tmp_widget_natural_size;
if (GTK_WIDGET_MAPPED (tmp_widget) || !group->ignore_hidden)
{
if (dimension > result)
result = dimension;
}
compute_base_dimensions (tmp_widget, mode,
&tmp_widget_minimum_size,
&tmp_widget_natural_size);
if (GTK_WIDGET_MAPPED (tmp_widget) || !group->ignore_hidden)
{
if (result_minimum_size < tmp_widget_minimum_size)
result_minimum_size = tmp_widget_minimum_size;
if (result_natural_size < tmp_widget_natural_size)
result_natural_size = tmp_widget_natural_size;
}
tmp_list = tmp_list->next;
}
@ -690,38 +751,45 @@ compute_dimension (GtkWidget *widget,
while (tmp_list)
{
GtkSizeGroup *tmp_group = tmp_list->data;
GtkSizeGroupPrivate *tmp_priv = GTK_SIZE_GROUP_GET_PRIVATE (tmp_group);
if (mode == GTK_SIZE_GROUP_HORIZONTAL)
{
tmp_group->have_width = TRUE;
tmp_group->requisition.width = result;
tmp_group->requisition.width = result_minimum_size;
tmp_priv->natural_size.width = result_natural_size;
}
else
{
tmp_group->have_height = TRUE;
tmp_group->requisition.height = result;
tmp_group->requisition.height = result_minimum_size;
tmp_priv->natural_size.height = result_natural_size;
}
tmp_list = tmp_list->next;
}
}
if (minimum_size)
*minimum_size = result_minimum_size;
if (natural_size)
*natural_size = result_natural_size;
}
g_slist_foreach (widgets, (GFunc)g_object_unref, NULL);
g_slist_free (widgets);
g_slist_free (groups);
return result;
}
static gint
get_dimension (GtkWidget *widget,
GtkSizeGroupMode mode)
static void
get_dimensions (GtkWidget *widget,
GtkSizeGroupMode mode,
gint *minimum_size,
gint *natural_size)
{
GSList *widgets = NULL;
GSList *groups = NULL;
gint result = 0;
add_widget_to_closure (widget, mode, &groups, &widgets);
@ -730,22 +798,31 @@ get_dimension (GtkWidget *widget,
if (!groups)
{
result = get_base_dimension (widget, mode);
get_base_dimensions (widget, mode, minimum_size, natural_size);
}
else
{
GtkSizeGroup *group = groups->data;
GtkSizeGroupPrivate *priv = GTK_SIZE_GROUP_GET_PRIVATE (group);
if (mode == GTK_SIZE_GROUP_HORIZONTAL && group->have_width)
result = group->requisition.width;
{
if (minimum_size)
*minimum_size = group->requisition.width;
if (natural_size)
*natural_size = priv->natural_size.width;
}
else if (mode == GTK_SIZE_GROUP_VERTICAL && group->have_height)
result = group->requisition.height;
{
if (minimum_size)
*minimum_size = group->requisition.height;
if (natural_size)
*natural_size = priv->natural_size.height;
}
}
g_slist_free (widgets);
g_slist_free (groups);
return result;
}
static void
@ -760,11 +837,23 @@ get_fast_child_requisition (GtkWidget *widget,
{
if (aux_info->width > 0)
requisition->width = aux_info->width;
if (aux_info && aux_info->height > 0)
if (aux_info->height > 0)
requisition->height = aux_info->height;
}
}
static void
get_fast_natural_size (GtkWidget *widget,
GtkRequisition *requisition)
{
GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (widget, FALSE);
if (aux_info)
*requisition = aux_info->natural_size;
else
*requisition = widget->requisition;
}
/**
* _gtk_size_group_get_child_requisition:
* @widget: a #GtkWidget
@ -783,8 +872,8 @@ _gtk_size_group_get_child_requisition (GtkWidget *widget,
{
if (get_size_groups (widget))
{
requisition->width = get_dimension (widget, GTK_SIZE_GROUP_HORIZONTAL);
requisition->height = get_dimension (widget, GTK_SIZE_GROUP_VERTICAL);
get_dimensions (widget, GTK_SIZE_GROUP_HORIZONTAL, &requisition->width, NULL);
get_dimensions (widget, GTK_SIZE_GROUP_VERTICAL, &requisition->height, NULL);
/* Only do the full computation if we actually have size groups */
}
@ -794,41 +883,40 @@ _gtk_size_group_get_child_requisition (GtkWidget *widget,
}
/**
* _gtk_size_group_compute_requisition:
* _gtk_size_group_compute_desired_size:
* @widget: a #GtkWidget
* @requisition: location to store computed requisition.
* @minimum_size: location to store computed minimum size
* @natural_size: location to store computed natural size
*
* Compute the requisition of a widget taking into account grouping of
* Compute the desired size of a widget taking into account grouping of
* the widget's requisition with other widgets.
**/
void
_gtk_size_group_compute_requisition (GtkWidget *widget,
GtkRequisition *requisition)
_gtk_size_group_compute_desired_size (GtkWidget *widget,
GtkRequisition *minimum_size,
GtkRequisition *natural_size)
{
gint width;
gint height;
initialize_size_group_quarks ();
if (get_size_groups (widget))
{
/* Only do the full computation if we actually have size groups */
width = compute_dimension (widget, GTK_SIZE_GROUP_HORIZONTAL);
height = compute_dimension (widget, GTK_SIZE_GROUP_VERTICAL);
if (requisition)
{
requisition->width = width;
requisition->height = height;
}
compute_dimension (widget, GTK_SIZE_GROUP_HORIZONTAL,
minimum_size ? &minimum_size->width : NULL,
natural_size ? &natural_size->width : NULL);
compute_dimension (widget, GTK_SIZE_GROUP_VERTICAL,
minimum_size ? &minimum_size->height : NULL,
natural_size ? &natural_size->height : NULL);
}
else
{
do_size_request (widget);
if (requisition)
get_fast_child_requisition (widget, requisition);
if (minimum_size)
get_fast_child_requisition (widget, minimum_size);
if (natural_size)
get_fast_natural_size (widget, natural_size);
}
}

View File

@ -102,8 +102,9 @@ GSList * gtk_size_group_get_widgets (GtkSizeGroup *size_group);
void _gtk_size_group_get_child_requisition (GtkWidget *widget,
GtkRequisition *requisition);
void _gtk_size_group_compute_requisition (GtkWidget *widget,
GtkRequisition *requisition);
void _gtk_size_group_compute_desired_size (GtkWidget *widget,
GtkRequisition *minimum_size,
GtkRequisition *natural_size);
void _gtk_size_group_queue_resize (GtkWidget *widget);
G_END_DECLS

View File

@ -53,6 +53,7 @@
#include "gtkinvisible.h"
#include "gtkbuildable.h"
#include "gtkbuilderprivate.h"
#include "gtkextendedlayout.h"
#include "gtkalias.h"
/**
@ -343,6 +344,11 @@ static void gtk_widget_buildable_custom_finished (GtkBuildable
static void gtk_widget_buildable_parser_finished (GtkBuildable *buildable,
GtkBuilder *builder);
static void gtk_widget_layout_interface_init (GtkExtendedLayoutIface *iface);
static void gtk_widget_real_get_desired_size (GtkExtendedLayout *layout,
GtkRequisition *minimum_size,
GtkRequisition *natural_size);
static void gtk_widget_queue_tooltip_query (GtkWidget *widget);
static void gtk_widget_set_usize_internal (GtkWidget *widget,
@ -418,6 +424,13 @@ gtk_widget_get_type (void)
NULL /* interface data */
};
const GInterfaceInfo layout_info =
{
(GInterfaceInitFunc) gtk_widget_layout_interface_init,
(GInterfaceFinalizeFunc) NULL,
NULL /* interface data */
};
widget_type = g_type_register_static (GTK_TYPE_OBJECT, "GtkWidget",
&widget_info, G_TYPE_FLAG_ABSTRACT);
@ -425,7 +438,8 @@ gtk_widget_get_type (void)
&accessibility_info) ;
g_type_add_interface_static (widget_type, GTK_TYPE_BUILDABLE,
&buildable_info) ;
g_type_add_interface_static (widget_type, GTK_TYPE_EXTENDED_LAYOUT,
&layout_info) ;
}
return widget_type;
@ -3869,10 +3883,11 @@ gtk_widget_size_request (GtkWidget *widget,
#ifdef G_ENABLE_DEBUG
if (requisition == &widget->requisition)
g_warning ("gtk_widget_size_request() called on child widget with request equal\n to widget->requisition. gtk_widget_set_usize() may not work properly.");
g_warning ("gtk_widget_size_request() called on child widget with request equal\n"
"to widget->requisition. gtk_widget_set_usize() may not work properly.");
#endif /* G_ENABLE_DEBUG */
_gtk_size_group_compute_requisition (widget, requisition);
_gtk_size_group_compute_desired_size (widget, requisition, NULL);
}
/**
@ -9116,14 +9131,11 @@ _gtk_widget_get_aux_info (GtkWidget *widget,
aux_info = g_object_get_qdata (G_OBJECT (widget), quark_aux_info);
if (!aux_info && create)
{
aux_info = g_slice_new (GtkWidgetAuxInfo);
aux_info = g_slice_new0 (GtkWidgetAuxInfo);
aux_info->width = -1;
aux_info->height = -1;
aux_info->x = 0;
aux_info->y = 0;
aux_info->x_set = FALSE;
aux_info->y_set = FALSE;
g_object_set_qdata (G_OBJECT (widget), quark_aux_info, aux_info);
}
@ -10559,7 +10571,102 @@ gtk_widget_buildable_custom_finished (GtkBuildable *buildable,
}
}
/*
* GtkExtendedLayout implementation
*/
static void
gtk_widget_real_get_desired_size (GtkExtendedLayout *layout,
GtkRequisition *minimum_size,
GtkRequisition *natural_size)
{
GtkWidget *widget = GTK_WIDGET (layout);
GtkRequisition requisition = widget->requisition;
g_signal_emit (widget, widget_signals[SIZE_REQUEST], 0, &requisition);
if (minimum_size)
*minimum_size = requisition;
if (natural_size)
*natural_size = requisition;
}
/**
* gtk_widget_get_desired_size:
* @widget: a #GtkWidget
* @minimum_size: location for storing the @widget's minimum size, or %NULL
* @natural_size: location for storing the @widget's preferred size, or %NULL
*
* Retreives a widget's desired size, considering restrictions imposed by
* #GtkSizeGroup<!-- -->s. See also: gtk_extended_layout_get_desired_size().
*
* Since: 2.20
*/
void
gtk_widget_get_desired_size (GtkWidget *widget,
GtkRequisition *minimum_size,
GtkRequisition *natural_size)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
_gtk_size_group_compute_desired_size (widget, minimum_size, natural_size);
}
void
gtk_widget_get_height_for_width (GtkWidget *widget,
gint width,
gint *minimum_height,
gint *natural_height)
{
GtkRequisition minimum_size;
GtkRequisition natural_size;
g_return_if_fail (GTK_IS_WIDGET (widget));
#if 0
TODO: integrate height-for-width with size-groups
#else
gtk_widget_get_desired_size (widget,
minimum_height ? &minimum_size : NULL,
natural_height ? &natural_size : NULL);
if (minimum_height)
*minimum_height = minimum_size.height;
if (natural_height)
*natural_height = natural_size.height;
#endif
}
void
gtk_widget_get_width_for_height (GtkWidget *widget,
gint height,
gint *minimum_width,
gint *natural_width)
{
GtkRequisition minimum_size;
GtkRequisition natural_size;
g_return_if_fail (GTK_IS_WIDGET (widget));
#if 0
TODO: integrate width-for-height with size-groups
#else
gtk_widget_get_desired_size (widget,
minimum_width ? &minimum_size : NULL,
natural_width ? &natural_size : NULL);
if (minimum_width)
*minimum_width = minimum_size.width;
if (natural_width)
*natural_width = natural_size.width;
#endif
}
static void
gtk_widget_layout_interface_init (GtkExtendedLayoutIface *iface)
{
iface->get_desired_size = gtk_widget_real_get_desired_size;
}
/**
* gtk_widget_get_clipboard:
* @widget: a #GtkWidget

View File

@ -705,8 +705,11 @@ struct _GtkWidgetAuxInfo
gint y;
gint width;
gint height;
guint x_set : 1;
guint y_set : 1;
GtkRequisition natural_size;
};
struct _GtkWidgetShapeInfo
@ -771,6 +774,17 @@ void gtk_widget_size_request (GtkWidget *widget,
GtkRequisition *requisition);
void gtk_widget_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
void gtk_widget_get_desired_size (GtkWidget *widget,
GtkRequisition *minimum_size,
GtkRequisition *natural_size);
void gtk_widget_get_height_for_width(GtkWidget *widget,
gint width,
gint *minimum_height,
gint *natural_height);
void gtk_widget_get_width_for_height(GtkWidget *widget,
gint height,
gint *minimum_width,
gint *natural_width);
void gtk_widget_get_child_requisition (GtkWidget *widget,
GtkRequisition *requisition);
void gtk_widget_add_accelerator (GtkWidget *widget,

View File

@ -42,6 +42,7 @@ noinst_PROGRAMS = $(TEST_PROGS) \
testellipsise \
testentrycompletion \
testentryicons \
testextendedlayout \
testfilechooser \
testfilechooserbutton \
testframe \
@ -127,6 +128,7 @@ testdnd_DEPENDENCIES = $(TEST_DEPS)
testellipsise_DEPENDENCIES = $(TEST_DEPS)
testentrycompletion_DEPENDENCIES = $(TEST_DEPS)
testentryicons_DEPENDENCIES = $(TEST_DEPS)
testextendedlayout_DEPENDENCIES = $(TEST_DEPS)
testfilechooser_DEPENDENCIES = $(TEST_DEPS)
testfilechooserbutton_DEPENDENCIES = $(TEST_DEPS)
testgtk_DEPENDENCIES = $(TEST_DEPS)
@ -188,6 +190,7 @@ testdnd_LDADD = $(LDADDS)
testellipsise_LDADD = $(LDADDS)
testentrycompletion_LDADD = $(LDADDS)
testentryicons_LDADD = $(LDADDS)
testextendedlayout_LDADD = $(LDADDS)
testfilechooser_LDADD = $(LDADDS)
testfilechooserbutton_LDADD = $(LDADDS)
testgtk_LDADD = $(LDADDS)

View File

@ -25,6 +25,21 @@
#include <gtk/gtk.h>
static void
redraw_event_box (GtkWidget *widget)
{
while (widget)
{
if (GTK_IS_EVENT_BOX (widget))
{
gtk_widget_queue_draw (widget);
break;
}
widget = gtk_widget_get_parent (widget);
}
}
static void
combo_changed_cb (GtkWidget *combo,
gpointer data)
@ -33,33 +48,135 @@ combo_changed_cb (GtkWidget *combo,
gint active;
active = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
gtk_label_set_ellipsize (GTK_LABEL (label), (PangoEllipsizeMode)active);
redraw_event_box (label);
}
static void
scale_changed_cb (GtkRange *range,
gpointer data)
{
double angle = gtk_range_get_value (range);
GtkWidget *label = GTK_WIDGET (data);
gtk_label_set_angle (GTK_LABEL (label), angle);
redraw_event_box (label);
}
static gboolean
ebox_expose_event_cb (GtkWidget *widget,
GdkEventExpose *event,
gpointer data)
{
PangoLayout *layout;
const double dashes[] = { 6, 18 };
GtkRequisition natural_size;
GtkWidget *label = data;
gint x, y, dx, dy;
gchar *markup;
cairo_t *cr;
gtk_widget_translate_coordinates (label, widget, 0, 0, &x, &y);
cr = gdk_cairo_create (widget->window);
cairo_translate (cr, -0.5, -0.5);
cairo_set_line_width (cr, 1);
cairo_rectangle (cr,
x + 0.5 * (label->allocation.width - label->requisition.width),
y + 0.5 * (label->allocation.height - label->requisition.height),
label->requisition.width, label->requisition.height);
cairo_set_source_rgb (cr, 0.8, 0.2, 0.2);
cairo_set_dash (cr, NULL, 0, 0);
cairo_stroke (cr);
cairo_rectangle (cr, x, y, label->allocation.width, label->allocation.height);
cairo_set_source_rgb (cr, 0.2, 0.2, 0.8);
cairo_set_dash (cr, dashes, 2, 0.5);
cairo_stroke (cr);
gtk_extended_layout_get_desired_size (GTK_EXTENDED_LAYOUT (label),
NULL, &natural_size);
cairo_rectangle (cr,
x + 0.5 * (label->allocation.width - natural_size.width),
y + 0.5 * (label->allocation.height - natural_size.height),
natural_size.width, natural_size.height);
cairo_set_source_rgb (cr, 0.2, 0.8, 0.2);
cairo_set_dash (cr, dashes, 2, 12.5);
cairo_stroke (cr);
markup = g_strdup_printf (
"<span color='#c33'>\342\200\242 requisition:\t%dx%d</span>\n"
"<span color='#3c3'>\342\200\242 natural size:\t%dx%d</span>\n"
"<span color='#33c'>\342\200\242 allocation:\t%dx%d</span>",
label->requisition.width, label->requisition.height,
natural_size.width, natural_size.height,
label->allocation.width, label->allocation.height);
layout = gtk_widget_create_pango_layout (widget, NULL);
pango_layout_set_markup (layout, markup, -1);
pango_layout_get_pixel_size (layout, &dx, &dy);
g_free (markup);
cairo_translate (cr, 0, widget->allocation.height - dy - 8);
cairo_set_source_rgba (cr, 1, 1, 1, 0.8);
cairo_rectangle (cr, 0, 0, dx + 12, dy + 8);
cairo_fill (cr);
cairo_translate (cr, 6, 4);
pango_cairo_show_layout (cr, layout);
g_object_unref (layout);
cairo_destroy (cr);
return FALSE;
}
int
main (int argc, char *argv[])
{
GtkWidget *window, *vbox, *hbox, *label, *combo;
GtkWidget *window, *vbox, *label;
GtkWidget *combo, *scale, *align, *ebox;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_container_set_border_width (GTK_CONTAINER (window), 12);
gtk_window_set_default_size (GTK_WINDOW (window), 400, 300);
g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
vbox = gtk_vbox_new (0, FALSE);
vbox = gtk_vbox_new (FALSE, 6);
gtk_container_add (GTK_CONTAINER (window), vbox);
hbox = gtk_hbox_new (0, FALSE);
gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
label = gtk_label_new ("This label may be ellipsized\nto make it fit.");
gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
combo = gtk_combo_box_new_text ();
scale = gtk_hscale_new_with_range (0, 360, 1);
label = gtk_label_new ("This label may be ellipsized\nto make it fit.");
gtk_combo_box_append_text (GTK_COMBO_BOX (combo), "NONE");
gtk_combo_box_append_text (GTK_COMBO_BOX (combo), "START");
gtk_combo_box_append_text (GTK_COMBO_BOX (combo), "MIDDLE");
gtk_combo_box_append_text (GTK_COMBO_BOX (combo), "END");
gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
gtk_container_add (GTK_CONTAINER (align), label);
ebox = gtk_event_box_new ();
gtk_widget_set_app_paintable (ebox, TRUE);
gtk_container_add (GTK_CONTAINER (ebox), align);
gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (vbox), ebox, TRUE, TRUE, 0);
g_object_set_data (G_OBJECT (label), "combo", combo);
g_signal_connect (combo, "changed", G_CALLBACK (combo_changed_cb), label);
g_signal_connect (scale, "value-changed", G_CALLBACK (scale_changed_cb), label);
g_signal_connect_after (ebox, "expose-event", G_CALLBACK (ebox_expose_event_cb), label);
gtk_widget_show_all (window);

120
tests/testextendedlayout.c Normal file
View File

@ -0,0 +1,120 @@
#include <gtk/gtk.h>
#include <math.h>
static void
size_group_toggled_cb (GtkToggleButton *button,
GtkSizeGroup *group)
{
if (gtk_toggle_button_get_active (button))
gtk_size_group_set_mode (group, GTK_SIZE_GROUP_HORIZONTAL);
else
gtk_size_group_set_mode (group, GTK_SIZE_GROUP_NONE);
}
static void
ellipsize_toggled_cb (GtkToggleButton *button,
GtkWidget *vbox)
{
GList *rows, *row_iter, *cells, *cell_iter;
PangoEllipsizeMode mode;
if (gtk_toggle_button_get_active (button))
mode = PANGO_ELLIPSIZE_END;
else
mode = PANGO_ELLIPSIZE_NONE;
rows = gtk_container_get_children (GTK_CONTAINER (vbox));
for (row_iter = rows; row_iter; row_iter = row_iter->next)
{
if (!GTK_IS_CONTAINER (row_iter->data))
break;
cells = gtk_container_get_children (row_iter->data);
for (cell_iter = cells; cell_iter; cell_iter = cell_iter->next)
if (GTK_IS_LABEL (cell_iter->data))
gtk_label_set_ellipsize (cell_iter->data, mode);
g_list_free (cells);
}
g_list_free (rows);
}
int
main (int argc,
char *argv[])
{
GtkWidget *window, *vbox, *button;
GtkSizeGroup *groups[5];
gint x, y;
gtk_init (&argc, &argv);
for (x = 0; x < G_N_ELEMENTS (groups); ++x)
groups[x] = gtk_size_group_new (GTK_SIZE_GROUP_NONE);
vbox = gtk_vbox_new (FALSE, 6);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
for (y = 0; y < 4; ++y)
{
GtkWidget *hbox = gtk_hbox_new (FALSE, 6);
for (x = 0; x < G_N_ELEMENTS (groups); ++x)
{
gchar *text = g_strdup_printf ("Label #%.0f.%d", pow(10, y), x + 1);
GtkWidget *label = gtk_label_new (text);
g_free (text);
text = g_strdup_printf ("label/%d/%d", y, x);
gtk_widget_set_name (label, text);
g_free (text);
if (1 != x)
gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
if (x > 0)
gtk_box_pack_start (GTK_BOX (hbox), gtk_vseparator_new (), FALSE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (hbox), label, 1 == x, TRUE, 0);
gtk_size_group_add_widget (groups[x], label);
}
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
}
gtk_box_pack_start (GTK_BOX (vbox), gtk_hseparator_new (), FALSE, TRUE, 0);
for (x = 0; x < G_N_ELEMENTS (groups); ++x)
{
gchar *text = g_strdup_printf ("Size Group #%d", x + 1);
button = gtk_check_button_new_with_label (text);
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 0);
g_free (text);
g_signal_connect (button, "toggled", G_CALLBACK (size_group_toggled_cb), groups[x]);
}
gtk_box_pack_start (GTK_BOX (vbox), gtk_hseparator_new (), FALSE, TRUE, 0);
button = gtk_check_button_new_with_label ("Ellipsize");
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 0);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
g_signal_connect (button, "toggled",
G_CALLBACK (ellipsize_toggled_cb),
vbox);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_container_add (GTK_CONTAINER (window), vbox);
gtk_widget_show_all (window);
g_signal_connect (window, "destroy",
G_CALLBACK (gtk_main_quit),
NULL);
gtk_main ();
return 0;
}