diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 7114da8a13..2b092fc93a 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -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 \ diff --git a/gtk/gtk.h b/gtk/gtk.h index 07952be6c6..2a49a8e0d7 100644 --- a/gtk/gtk.h +++ b/gtk/gtk.h @@ -83,6 +83,7 @@ #include #include #include +#include #include #include #include diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols index 40f3261c66..68e3fe2cbc 100644 --- a/gtk/gtk.symbols +++ b/gtk/gtk.symbols @@ -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 diff --git a/gtk/gtkalignment.c b/gtk/gtkalignment.c index bf3523b179..2d8ccb5421 100644 --- a/gtk/gtkalignment.c +++ b/gtk/gtkalignment.c @@ -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) + diff --git a/gtk/gtkbox.c b/gtk/gtkbox.c index 50c588bb61..f8dc28f4db 100644 --- a/gtk/gtkbox.c +++ b/gtk/gtkbox.c @@ -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); } } } diff --git a/gtk/gtkextendedlayout.c b/gtk/gtkextendedlayout.c new file mode 100644 index 0000000000..b9d35c779d --- /dev/null +++ b/gtk/gtkextendedlayout.c @@ -0,0 +1,146 @@ +/* gtkextendedlayout.c + * Copyright (C) 2007 Openismus GmbH + * + * Author: + * Mathias Hasselmann + * + * 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 +#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" diff --git a/gtk/gtkextendedlayout.h b/gtk/gtkextendedlayout.h new file mode 100644 index 0000000000..9944fedad8 --- /dev/null +++ b/gtk/gtkextendedlayout.h @@ -0,0 +1,74 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2007 Openismus GmbH + * + * Author: + * Mathias Hasselmann + * + * 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 + +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__ */ diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c index c3df84a2dc..8342b8af27 100644 --- a/gtk/gtklabel.c +++ b/gtk/gtklabel.c @@ -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 diff --git a/gtk/gtklabel.h b/gtk/gtklabel.h index d44b142fad..2c270afe3c 100644 --- a/gtk/gtklabel.h +++ b/gtk/gtklabel.h @@ -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); diff --git a/gtk/gtksizegroup.c b/gtk/gtksizegroup.c index 6cd02c0e2e..1911087039 100644 --- a/gtk/gtksizegroup.c +++ b/gtk/gtksizegroup.c @@ -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); } } diff --git a/gtk/gtksizegroup.h b/gtk/gtksizegroup.h index ecd2ceacf6..d82fbd5ca8 100644 --- a/gtk/gtksizegroup.h +++ b/gtk/gtksizegroup.h @@ -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 diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 36bbcb4ee5..d03abfe8ab 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -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 + * #GtkSizeGroups. 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 diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h index 21c58d10c9..7fd86d6663 100644 --- a/gtk/gtkwidget.h +++ b/gtk/gtkwidget.h @@ -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, diff --git a/tests/Makefile.am b/tests/Makefile.am index 8956c65888..09feb72f5f 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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) diff --git a/tests/testellipsise.c b/tests/testellipsise.c index 0783d619da..5b8b46dabb 100644 --- a/tests/testellipsise.c +++ b/tests/testellipsise.c @@ -25,6 +25,21 @@ #include +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 ( + "\342\200\242 requisition:\t%dx%d\n" + "\342\200\242 natural size:\t%dx%d\n" + "\342\200\242 allocation:\t%dx%d", + 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); diff --git a/tests/testextendedlayout.c b/tests/testextendedlayout.c new file mode 100644 index 0000000000..497ccafb82 --- /dev/null +++ b/tests/testextendedlayout.c @@ -0,0 +1,120 @@ +#include +#include + +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; +}