X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkbox.c;h=5c8e6e3ff839b4132cf1fb09aa1255ffe07320e2;hb=c6fbdb67f3dc86cf49f2ac5ac8c072f4e586fd29;hp=523b19a349c02e3f5b9da14fd6acba3bbf06684c;hpb=a7465c3479ff720b270b17082ae9cf858d1871b9;p=~andy%2Fgtk diff --git a/gtk/gtkbox.c b/gtk/gtkbox.c index 523b19a34..5c8e6e3ff 100644 --- a/gtk/gtkbox.c +++ b/gtk/gtkbox.c @@ -12,9 +12,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . */ /* @@ -26,38 +24,34 @@ /** * SECTION:gtkbox - * @Short_description: Base class for box containers + * @Short_description: A container box * @Title: GtkBox - * @See_also: #GtkHBox, #GtkVBox, #GtkFrame, #GtkTable, #GtkLayout + * @See_also: #GtkFrame, #GtkGrid, #GtkLayout * - * GtkBox is an widget which encapsulates functionality for a - * particular kind of container, one that organizes a variable number of - * widgets into a rectangular area. GtkBox has a number of derived - * classes, e.g. #GtkHBox and #GtkVBox. + * The GtkBox widget organizes child widgets into a rectangular area. * * The rectangular area of a GtkBox is organized into either a single row - * or a single column of child widgets depending upon whether the box is - * of type #GtkHBox or #GtkVBox, respectively. Thus, all children of a - * GtkBox are allocated one dimension in common, which is the height of a - * row, or the width of a column. + * or a single column of child widgets depending upon the orientation. + * Thus, all children of a GtkBox are allocated one dimension in common, + * which is the height of a row, or the width of a column. * - * GtkBox uses a notion of packing. Packing - * refers to adding widgets with reference to a particular position in a - * #GtkContainer. For a GtkBox, there are two reference positions: the + * GtkBox uses a notion of packing. Packing refers + * to adding widgets with reference to a particular position in a + * #GtkContainer. For a GtkBox, there are two reference positions: the * start and the end of the box. - * For a #GtkVBox, the start is defined as the top of the box and the end is - * defined as the bottom. For a #GtkHBox the start is defined as the - * left side and the end is defined as the right side. + * For a vertical #GtkBox, the start is defined as the top of the box and + * the end is defined as the bottom. For a horizontal #GtkBox the start + * is defined as the left side and the end is defined as the right side. * * Use repeated calls to gtk_box_pack_start() to pack widgets into a - * GtkBox from start to end. Use gtk_box_pack_end() to add widgets from - * end to start. You may intersperse these calls and add widgets from + * GtkBox from start to end. Use gtk_box_pack_end() to add widgets from + * end to start. You may intersperse these calls and add widgets from * both ends of the same GtkBox. * - * Because GtkBox is a #GtkContainer, you may also use - * gtk_container_add() to insert widgets into the box, and they will be - * packed with the default values for #GtkBox:expand and #GtkBox:fill. - * Use gtk_container_remove() to remove widgets from the GtkBox. + * Because GtkBox is a #GtkContainer, you may also use gtk_container_add() + * to insert widgets into the box, and they will be packed with the default + * values for #GtkBox:expand and #GtkBox:fill. Use gtk_container_remove() + * to remove widgets from the GtkBox. * * Use gtk_box_set_homogeneous() to specify whether or not all children * of the GtkBox are forced to get the same amount of space. @@ -74,16 +68,26 @@ * Use gtk_box_set_child_packing() to reset the #GtkBox:expand, * #GtkBox:fill and #GtkBox:padding child properties. * Use gtk_box_query_child_packing() to query these fields. + * + * + * Note that a single-row or single-column #GtkGrid provides exactly + * the same functionality as #GtkBox. + * */ #include "config.h" #include "gtkbox.h" +#include "gtkboxprivate.h" +#include "gtkintl.h" #include "gtkorientable.h" -#include "gtksizerequest.h" -#include "gtktypeutils.h" +#include "gtkorientableprivate.h" #include "gtkprivate.h" -#include "gtkintl.h" +#include "gtktypebuiltins.h" +#include "gtksizerequest.h" +#include "gtkwidgetpath.h" +#include "gtkwidgetprivate.h" +#include "a11y/gtkcontaineraccessible.h" enum { @@ -104,10 +108,9 @@ enum { struct _GtkBoxPrivate { - GtkOrientation orientation; - GList *children; + GtkOrientation orientation; gint16 spacing; guint default_expand : 1; @@ -148,6 +151,8 @@ static void gtk_box_size_allocate (GtkWidget *widget, static void gtk_box_compute_expand (GtkWidget *widget, gboolean *hexpand, gboolean *vexpand); +static void gtk_box_direction_changed (GtkWidget *widget, + GtkTextDirection previous_direction); static void gtk_box_set_property (GObject *object, guint prop_id, @@ -176,9 +181,11 @@ static void gtk_box_get_child_property (GtkContainer *container, GValue *value, GParamSpec *pspec); static GType gtk_box_child_type (GtkContainer *container); +static GtkWidgetPath * gtk_box_get_path_for_child + (GtkContainer *container, + GtkWidget *child); -static GtkSizeRequestMode gtk_box_get_request_mode (GtkWidget *widget); static void gtk_box_get_preferred_width (GtkWidget *widget, gint *minimum_size, gint *natural_size); @@ -194,6 +201,7 @@ static void gtk_box_get_preferred_height_for_width (GtkWidget gint *minimum_height, gint *natural_height); + G_DEFINE_TYPE_WITH_CODE (GtkBox, gtk_box, GTK_TYPE_CONTAINER, G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL)) @@ -209,12 +217,12 @@ gtk_box_class_init (GtkBoxClass *class) object_class->get_property = gtk_box_get_property; widget_class->size_allocate = gtk_box_size_allocate; - widget_class->get_request_mode = gtk_box_get_request_mode; widget_class->get_preferred_width = gtk_box_get_preferred_width; widget_class->get_preferred_height = gtk_box_get_preferred_height; widget_class->get_preferred_height_for_width = gtk_box_get_preferred_height_for_width; widget_class->get_preferred_width_for_height = gtk_box_get_preferred_width_for_height; widget_class->compute_expand = gtk_box_compute_expand; + widget_class->direction_changed = gtk_box_direction_changed; container_class->add = gtk_box_add; container_class->remove = gtk_box_remove; @@ -222,6 +230,7 @@ gtk_box_class_init (GtkBoxClass *class) container_class->child_type = gtk_box_child_type; container_class->set_child_property = gtk_box_set_child_property; container_class->get_child_property = gtk_box_get_child_property; + container_class->get_path_for_child = gtk_box_get_path_for_child; gtk_container_class_handle_border_width (container_class); g_object_class_override_property (object_class, @@ -254,6 +263,10 @@ gtk_box_class_init (GtkBoxClass *class) * Note that the default value for this property is %FALSE for GtkBox, * but #GtkHBox, #GtkVBox and other subclasses use the old default * of %TRUE. + * + * Note that the #GtkWidget:halign, #GtkWidget:valign, #GtkWidget:hexpand + * and #GtkWidget:vexpand properties are the preferred way to influence + * child size allocation in containers. */ gtk_container_class_install_child_property (container_class, CHILD_PROP_EXPAND, @@ -268,16 +281,16 @@ gtk_box_class_init (GtkBoxClass *class) * * Whether the child should receive extra space when the parent grows. * - * Note that the default value for this property is %FALSE for GtkBox, - * but #GtkHBox, #GtkVBox and other subclasses use the old default - * of %TRUE. + * Note that the #GtkWidget:halign, #GtkWidget:valign, #GtkWidget:hexpand + * and #GtkWidget:vexpand properties are the preferred way to influence + * child size allocation in containers. */ gtk_container_class_install_child_property (container_class, CHILD_PROP_FILL, g_param_spec_boolean ("fill", P_("Fill"), P_("Whether extra space given to the child should be allocated to the child or used as padding"), - FALSE, + TRUE, GTK_PARAM_READWRITE)); gtk_container_class_install_child_property (container_class, @@ -290,19 +303,21 @@ gtk_box_class_init (GtkBoxClass *class) gtk_container_class_install_child_property (container_class, CHILD_PROP_PACK_TYPE, g_param_spec_enum ("pack-type", - P_("Pack type"), + P_("Pack type"), P_("A GtkPackType indicating whether the child is packed with reference to the start or end of the parent"), GTK_TYPE_PACK_TYPE, GTK_PACK_START, GTK_PARAM_READWRITE)); gtk_container_class_install_child_property (container_class, CHILD_PROP_POSITION, - g_param_spec_int ("position", - P_("Position"), + g_param_spec_int ("position", + P_("Position"), P_("The index of the child in the parent"), -1, G_MAXINT, 0, GTK_PARAM_READWRITE)); g_type_class_add_private (object_class, sizeof (GtkBoxPrivate)); + + gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_FILLER); } static void @@ -340,6 +355,7 @@ gtk_box_set_property (GObject *object, { case PROP_ORIENTATION: private->orientation = g_value_get_enum (value); + _gtk_orientable_set_style_classes (GTK_ORIENTABLE (box)); gtk_widget_queue_resize (GTK_WIDGET (box)); break; case PROP_SPACING: @@ -823,6 +839,138 @@ gtk_box_get_child_property (GtkContainer *container, } } +typedef struct _CountingData CountingData; +struct _CountingData { + GtkWidget *widget; + gboolean found; + guint before; + guint after; +}; + +static void +count_widget_position (GtkWidget *widget, + gpointer data) +{ + CountingData *count = data; + + if (!gtk_widget_get_visible (widget)) + return; + + if (count->widget == widget) + count->found = TRUE; + else if (count->found) + count->after++; + else + count->before++; +} + +static gint +gtk_box_get_visible_position (GtkBox *box, + GtkWidget *child) +{ + CountingData count = { child, FALSE, 0, 0 }; + + /* foreach iterates in visible order */ + gtk_container_foreach (GTK_CONTAINER (box), + count_widget_position, + &count); + + /* the child wasn't found, it's likely an internal child of some + * subclass, return -1 to indicate that there is no sibling relation + * to the regular box children + */ + if (!count.found) + return -1; + + if (box->priv->orientation == GTK_ORIENTATION_HORIZONTAL && + gtk_widget_get_direction (GTK_WIDGET (box)) == GTK_TEXT_DIR_RTL) + return count.after; + else + return count.before; +} + +static GtkWidgetPath * +gtk_box_get_path_for_child (GtkContainer *container, + GtkWidget *child) +{ + GtkWidgetPath *path, *sibling_path; + GtkBox *box; + GtkBoxPrivate *private; + GList *list, *children; + + box = GTK_BOX (container); + private = box->priv; + + path = _gtk_widget_create_path (GTK_WIDGET (container)); + + if (gtk_widget_get_visible (child)) + { + gint position; + + sibling_path = gtk_widget_path_new (); + + /* get_children works in visible order */ + children = gtk_container_get_children (container); + if (private->orientation == GTK_ORIENTATION_HORIZONTAL && + gtk_widget_get_direction (GTK_WIDGET (box)) == GTK_TEXT_DIR_RTL) + children = g_list_reverse (children); + + for (list = children; list; list = list->next) + { + if (!gtk_widget_get_visible (list->data)) + continue; + + gtk_widget_path_append_for_widget (sibling_path, list->data); + } + + g_list_free (children); + + position = gtk_box_get_visible_position (box, child); + + if (position >= 0) + gtk_widget_path_append_with_siblings (path, sibling_path, position); + else + gtk_widget_path_append_for_widget (path, child); + + gtk_widget_path_unref (sibling_path); + } + else + gtk_widget_path_append_for_widget (path, child); + + return path; +} + +static void +gtk_box_invalidate_order_foreach (GtkWidget *widget) +{ + _gtk_widget_invalidate_style_context (widget, GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_SIBLING_POSITION); +} + +static void +gtk_box_invalidate_order (GtkBox *box) +{ + gtk_container_foreach (GTK_CONTAINER (box), + (GtkCallback) gtk_box_invalidate_order_foreach, + NULL); +} + +static void +gtk_box_direction_changed (GtkWidget *widget, + GtkTextDirection previous_direction) +{ + gtk_box_invalidate_order (GTK_BOX (widget)); +} + +static void +box_child_visibility_notify_cb (GObject *obj, + GParamSpec *pspec, + gpointer user_data) +{ + GtkBox *box = user_data; + + gtk_box_invalidate_order (box); +} + static void gtk_box_pack (GtkBox *box, GtkWidget *child, @@ -849,8 +997,12 @@ gtk_box_pack (GtkBox *box, gtk_widget_freeze_child_notify (child); + gtk_box_invalidate_order (box); gtk_widget_set_parent (child, GTK_WIDGET (box)); - + + g_signal_connect (child, "notify::visible", + G_CALLBACK (box_child_visibility_notify_cb), box); + gtk_widget_child_notify (child, "expand"); gtk_widget_child_notify (child, "fill"); gtk_widget_child_notify (child, "padding"); @@ -859,16 +1011,6 @@ gtk_box_pack (GtkBox *box, gtk_widget_thaw_child_notify (child); } - -static GtkSizeRequestMode -gtk_box_get_request_mode (GtkWidget *widget) -{ - GtkBoxPrivate *private = GTK_BOX (widget)->priv; - - return (private->orientation == GTK_ORIENTATION_VERTICAL) ? - GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH : GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT; -} - static void gtk_box_get_size (GtkWidget *widget, GtkOrientation orientation, @@ -966,7 +1108,7 @@ gtk_box_get_preferred_height (GtkWidget *widget, gtk_box_get_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size); } -static void +static void gtk_box_compute_size_for_opposing_orientation (GtkBox *box, gint avail_size, gint *minimum_size, @@ -996,7 +1138,7 @@ gtk_box_compute_size_for_opposing_orientation (GtkBox *box, for (i = 0, children = private->children; children; children = children->next) { child = children->data; - + if (gtk_widget_get_visible (child->widget)) { if (private->orientation == GTK_ORIENTATION_HORIZONTAL) @@ -1153,7 +1295,7 @@ gtk_box_compute_size_for_orientation (GtkBox *box, gint largest_child = 0, largest_natural = 0; for (children = private->children; children != NULL; - children = children->next, nvis_children++) + children = children->next) { GtkBoxChild *child = children->data; @@ -1179,6 +1321,8 @@ gtk_box_compute_size_for_orientation (GtkBox *box, required_size += child_size; required_natural += child_natural; + + nvis_children += 1; } } @@ -1233,8 +1377,7 @@ gtk_box_get_preferred_height_for_width (GtkWidget *widget, /** * gtk_box_new: - * @orientation: the box' orientation. - * @homogeneous: %TRUE if all children are to be given equal space allocations. + * @orientation: the box's orientation. * @spacing: the number of pixels to place by default between children. * * Creates a new #GtkBox. @@ -1245,13 +1388,11 @@ gtk_box_get_preferred_height_for_width (GtkWidget *widget, **/ GtkWidget* gtk_box_new (GtkOrientation orientation, - gboolean homogeneous, gint spacing) { return g_object_new (GTK_TYPE_BOX, "orientation", orientation, "spacing", spacing, - "homogeneous", homogeneous ? TRUE : FALSE, NULL); } @@ -1265,8 +1406,8 @@ gtk_box_new (GtkOrientation orientation, * @fill: %TRUE if space given to @child by the @expand option is * actually allocated to @child, rather than just padding it. This * parameter has no effect if @expand is set to %FALSE. A child is - * always allocated the full height of a #GtkHBox and the full width - * of a #GtkVBox. This option affects the other dimension + * always allocated the full height of a horizontal #GtkBox and the full width + * of a vertical #GtkBox. This option affects the other dimension * @padding: extra space in pixels to put between this child and its * neighbors, over and above the global amount specified by * #GtkBox:spacing property. If @child is a widget at one of the @@ -1291,22 +1432,22 @@ gtk_box_pack_start (GtkBox *box, * gtk_box_pack_end: * @box: a #GtkBox * @child: the #GtkWidget to be added to @box - * @expand: %TRUE if the new child is to be given extra space allocated - * to @box. The extra space will be divided evenly between all children + * @expand: %TRUE if the new child is to be given extra space allocated + * to @box. The extra space will be divided evenly between all children * of @box that use this option * @fill: %TRUE if space given to @child by the @expand option is * actually allocated to @child, rather than just padding it. This * parameter has no effect if @expand is set to %FALSE. A child is - * always allocated the full height of a #GtkHBox and the full width - * of a #GtkVBox. This option affects the other dimension + * always allocated the full height of a horizontal #GtkBox and the full width + * of a vertical #GtkBox. This option affects the other dimension * @padding: extra space in pixels to put between this child and its * neighbors, over and above the global amount specified by - * #GtkBox:spacing property. If @child is a widget at one of the - * reference ends of @box, then @padding pixels are also put between + * #GtkBox:spacing property. If @child is a widget at one of the + * reference ends of @box, then @padding pixels are also put between * @child and the reference edge of @box * - * Adds @child to @box, packed with reference to the end of @box. - * The @child is packed after (away from end of) any other child + * Adds @child to @box, packed with reference to the end of @box. + * The @child is packed after (away from end of) any other child * packed with reference to the end of @box. */ void @@ -1324,9 +1465,9 @@ gtk_box_pack_end (GtkBox *box, * @box: a #GtkBox * @homogeneous: a boolean value, %TRUE to create equal allotments, * %FALSE for variable allotments - * - * Sets the #GtkBox:homogeneous property of @box, controlling - * whether or not all children of @box are given equal space + * + * Sets the #GtkBox:homogeneous property of @box, controlling + * whether or not all children of @box are given equal space * in the box. */ void @@ -1369,7 +1510,7 @@ gtk_box_get_homogeneous (GtkBox *box) * @box: a #GtkBox * @spacing: the number of pixels to put between children * - * Sets the #GtkBox:spacing property of @box, which is the + * Sets the #GtkBox:spacing property of @box, which is the * number of pixels to place between children of @box. */ void @@ -1396,9 +1537,9 @@ gtk_box_set_spacing (GtkBox *box, /** * gtk_box_get_spacing: * @box: a #GtkBox - * + * * Gets the value set by gtk_box_set_spacing(). - * + * * Return value: spacing between children **/ gint @@ -1438,21 +1579,21 @@ _gtk_box_get_spacing_set (GtkBox *box) * gtk_box_reorder_child: * @box: a #GtkBox * @child: the #GtkWidget to move - * @position: the new position for @child in the list of children - * of @box, starting from 0. If negative, indicates the end of + * @position: the new position for @child in the list of children + * of @box, starting from 0. If negative, indicates the end of * the list * - * Moves @child to a new @position in the list of @box children. + * Moves @child to a new @position in the list of @box children. * The list is the children field of - * #GtkBox-struct, and contains both widgets packed #GTK_PACK_START - * as well as widgets packed #GTK_PACK_END, in the order that these + * #GtkBox-struct, and contains both widgets packed #GTK_PACK_START + * as well as widgets packed #GTK_PACK_END, in the order that these * widgets were added to @box. - * - * A widget's position in the @box children list determines where - * the widget is packed into @box. A child widget at some position - * in the list will be packed just after all other widgets of the + * + * A widget's position in the @box children list determines where + * the widget is packed into @box. A child widget at some position + * in the list will be packed just after all other widgets of the * same packing type that appear earlier in the list. - */ + */ void gtk_box_reorder_child (GtkBox *box, GtkWidget *child, @@ -1498,18 +1639,25 @@ gtk_box_reorder_child (GtkBox *box, gtk_widget_child_notify (child, "position"); if (gtk_widget_get_visible (child) && gtk_widget_get_visible (GTK_WIDGET (box))) - gtk_widget_queue_resize (child); + { + gtk_box_invalidate_order (box); + gtk_widget_queue_resize (child); + } } /** * gtk_box_query_child_packing: * @box: a #GtkBox * @child: the #GtkWidget of the child to query - * @expand: pointer to return location for #GtkBox:expand child property - * @fill: pointer to return location for #GtkBox:fill child property - * @padding: pointer to return location for #GtkBox:padding child property - * @pack_type: pointer to return location for #GtkBox:pack-type child property - * + * @expand: (out): pointer to return location for #GtkBox:expand child + * property + * @fill: (out): pointer to return location for #GtkBox:fill child + * property + * @padding: (out): pointer to return location for #GtkBox:padding + * child property + * @pack_type: (out): pointer to return location for #GtkBox:pack-type + * child property + * * Obtains information about how @child is packed into @box. */ void @@ -1556,7 +1704,7 @@ gtk_box_query_child_packing (GtkBox *box, * gtk_box_set_child_packing: * @box: a #GtkBox * @child: the #GtkWidget of the child to set - * @expand: the new value of the #GtkBox:expand child property + * @expand: the new value of the #GtkBox:expand child property * @fill: the new value of the #GtkBox:fill child property * @padding: the new value of the #GtkBox:padding child property * @pack_type: the new value of the #GtkBox:pack-type child property @@ -1611,11 +1759,14 @@ gtk_box_set_child_packing (GtkBox *box, gtk_widget_child_notify (child, "fill"); child_info->padding = padding; gtk_widget_child_notify (child, "padding"); - if (pack_type == GTK_PACK_END) - child_info->pack = GTK_PACK_END; - else - child_info->pack = GTK_PACK_START; - gtk_widget_child_notify (child, "pack-type"); + if (pack_type != GTK_PACK_END) + pack_type = GTK_PACK_START; + if (child_info->pack != pack_type) + { + child_info->pack = pack_type; + gtk_widget_child_notify (child, "pack-type"); + gtk_box_invalidate_order (box); + } if (gtk_widget_get_visible (child) && gtk_widget_get_visible (GTK_WIDGET (box))) @@ -1644,7 +1795,7 @@ gtk_box_add (GtkContainer *container, gtk_box_pack_start (GTK_BOX (container), widget, priv->default_expand, - priv->default_expand, + TRUE, 0); } @@ -1666,6 +1817,10 @@ gtk_box_remove (GtkContainer *container, { gboolean was_visible; + g_signal_handlers_disconnect_by_func (widget, + box_child_visibility_notify_cb, + box); + was_visible = gtk_widget_get_visible (widget); gtk_widget_unparent (widget); @@ -1677,7 +1832,10 @@ gtk_box_remove (GtkContainer *container, * since that's what is needed by toplevels. */ if (was_visible) - gtk_widget_queue_resize (GTK_WIDGET (container)); + { + gtk_box_invalidate_order (box); + gtk_widget_queue_resize (GTK_WIDGET (container)); + } break; }