* 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 <http://www.gnu.org/licenses/>.
*/
/*
/**
* SECTION:gtkbbox
- * @Short_description: Base class for GtkHButtonBox and GtkVButtonBox
+ * @Short_description: A container for arranging buttons
* @Title: GtkButtonBox
- * @See_also: #GtkVButtonBox, #GtkHButtonBox
*
- * The primary purpose of this class is to keep track of the various properties
- * of #GtkHButtonBox and #GtkVButtonBox widgets.
- *
- * gtk_button_box_get_child_size() retrieves the minimum width and height
- * for widgets in a given button box.
- *
- * The internal padding of buttons can be retrieved and changed per button box
- * using gtk_button_box_get_child_ipadding() and
- * gtk_button_box_set_child_ipadding() respectively.
- *
- * gtk_button_box_get_spacing() and gtk_button_box_set_spacing() retrieve and
- * change default number of pixels between buttons, respectively.
+ * A button box should be used to provide a consistent layout of buttons
+ * throughout your application. The layout/spacing can be altered by the
+ * programmer, or if desired, by the user to alter the 'feel' of a
+ * program to a small degree.
*
* gtk_button_box_get_layout() and gtk_button_box_set_layout() retrieve and
* alter the method used to spread the buttons in a button box across the
* container, respectively.
*
* The main purpose of GtkButtonBox is to make sure the children have all the
- * same size. Therefore it ignores the homogeneous property which it inherited
- * from GtkBox, and always behaves as if homogeneous was %TRUE.
+ * same size. GtkButtonBox gives all children the same size, but it does allow
+ * 'outliers' to keep their own larger size. To force all children to be
+ * strictly the same size without exceptions, you can set the
+ * #GtkButtonBox:homogeneous property to %TRUE.
+ *
+ * To excempt individual children from homogeneous sizing regardless of their
+ * 'outlier' status, you can set the #GtkButtonBox:non-homogeneous child
+ * property.
*/
#include "config.h"
+
#include "gtkbbox.h"
+
+#include "gtkboxprivate.h"
#include "gtkorientable.h"
+#include "gtktypebuiltins.h"
#include "gtkprivate.h"
+#include "gtksizerequest.h"
+
#include "gtkintl.h"
-struct _GtkButtonBoxPriv
+struct _GtkButtonBoxPrivate
{
GtkButtonBoxStyle layout_style;
};
enum {
CHILD_PROP_0,
- CHILD_PROP_SECONDARY
+ CHILD_PROP_SECONDARY,
+ CHILD_PROP_NONHOMOGENEOUS
};
#define GTK_BOX_SECONDARY_CHILD "gtk-box-secondary-child"
+#define GTK_BOX_NON_HOMOGENEOUS "gtk-box-non-homogeneous"
static void gtk_button_box_set_property (GObject *object,
guint prop_id,
guint prop_id,
GValue *value,
GParamSpec *pspec);
-static void gtk_button_box_size_request (GtkWidget *widget,
- GtkRequisition *requisition);
+static void gtk_button_box_get_preferred_width (GtkWidget *widget,
+ gint *minimum,
+ gint *natural);
+static void gtk_button_box_get_preferred_height (GtkWidget *widget,
+ gint *minimum,
+ gint *natural);
+static void gtk_button_box_get_preferred_width_for_height (GtkWidget *widget,
+ gint height,
+ gint *minimum,
+ gint *natural);
+static void gtk_button_box_get_preferred_height_for_width (GtkWidget *widget,
+ gint width,
+ gint *minimum,
+ gint *natural);
+
static void gtk_button_box_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
static void gtk_button_box_remove (GtkContainer *container,
gobject_class->set_property = gtk_button_box_set_property;
gobject_class->get_property = gtk_button_box_get_property;
- widget_class->size_request = gtk_button_box_size_request;
+ widget_class->get_preferred_width = gtk_button_box_get_preferred_width;
+ widget_class->get_preferred_height = gtk_button_box_get_preferred_height;
+ widget_class->get_preferred_width_for_height = gtk_button_box_get_preferred_width_for_height;
+ widget_class->get_preferred_height_for_width = gtk_button_box_get_preferred_height_for_width;
widget_class->size_allocate = gtk_button_box_size_allocate;
container_class->remove = gtk_button_box_remove;
container_class->set_child_property = gtk_button_box_set_child_property;
container_class->get_child_property = gtk_button_box_get_child_property;
+ gtk_container_class_handle_border_width (container_class);
/* FIXME we need to override the "spacing" property on GtkBox once
* libgobject allows that.
PROP_LAYOUT_STYLE,
g_param_spec_enum ("layout-style",
P_("Layout style"),
- P_("How to layout the buttons in the box. Possible values are spread, edge, start and end"),
+ P_("How to lay out the buttons in the box. Possible values are: spread, edge, start and end"),
GTK_TYPE_BUTTON_BOX_STYLE,
DEFAULT_LAYOUT_STYLE,
GTK_PARAM_READWRITE));
FALSE,
GTK_PARAM_READWRITE));
- g_type_class_add_private (class, sizeof (GtkButtonBoxPriv));
+ gtk_container_class_install_child_property (container_class,
+ CHILD_PROP_NONHOMOGENEOUS,
+ g_param_spec_boolean ("non-homogeneous",
+ P_("Non-Homogeneous"),
+ P_("If TRUE, the child will not be subject to homogeneous sizing"),
+ FALSE,
+ GTK_PARAM_READWRITE));
+
+ g_type_class_add_private (class, sizeof (GtkButtonBoxPrivate));
}
static void
gtk_button_box_init (GtkButtonBox *button_box)
{
- GtkButtonBoxPriv *priv;
+ GtkButtonBoxPrivate *priv;
button_box->priv = G_TYPE_INSTANCE_GET_PRIVATE (button_box,
GTK_TYPE_BUTTON_BOX,
- GtkButtonBoxPriv);
+ GtkButtonBoxPrivate);
priv = button_box->priv;
gtk_box_set_spacing (GTK_BOX (button_box), 0);
GValue *value,
GParamSpec *pspec)
{
- GtkButtonBoxPriv *priv = GTK_BUTTON_BOX (object)->priv;
+ GtkButtonBoxPrivate *priv = GTK_BUTTON_BOX (object)->priv;
switch (prop_id)
{
gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (container), child,
g_value_get_boolean (value));
break;
+ case CHILD_PROP_NONHOMOGENEOUS:
+ gtk_button_box_set_child_non_homogeneous (GTK_BUTTON_BOX (container), child,
+ g_value_get_boolean (value));
+ break;
default:
GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
break;
gtk_button_box_get_child_secondary (GTK_BUTTON_BOX (container),
child));
break;
+ case CHILD_PROP_NONHOMOGENEOUS:
+ g_value_set_boolean (value,
+ gtk_button_box_get_child_non_homogeneous (GTK_BUTTON_BOX (container),
+ child));
+ break;
default:
GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
break;
gtk_button_box_remove (GtkContainer *container,
GtkWidget *widget)
{
- /* clear is_secondary flag in case the widget
+ /* clear is_secondary and nonhomogeneous flag in case the widget
* is added to another container
*/
- gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (container),
- widget,
- FALSE);
+ gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (container), widget, FALSE);
+ gtk_button_box_set_child_non_homogeneous (GTK_BUTTON_BOX (container), widget, FALSE);
GTK_CONTAINER_CLASS (gtk_button_box_parent_class)->remove (container, widget);
}
gtk_button_box_set_layout (GtkButtonBox *widget,
GtkButtonBoxStyle layout_style)
{
- GtkButtonBoxPriv *priv;
+ GtkButtonBoxPrivate *priv;
g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
*
* Retrieves the method being used to arrange the buttons in a button box.
*
- * Returns: the method used to layout buttons in @widget.
+ * Returns: the method used to lay out buttons in @widget.
*/
GtkButtonBoxStyle
gtk_button_box_get_layout (GtkButtonBox *widget)
}
/**
- * gtk_button_box_set_child_secondary
+ * gtk_button_box_set_child_secondary:
* @widget: a #GtkButtonBox
* @child: a child of @widget
* @is_secondary: if %TRUE, the @child appears in a secondary group of the
{
g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
g_return_if_fail (GTK_IS_WIDGET (child));
- g_return_if_fail (child->parent == GTK_WIDGET (widget));
+ g_return_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (widget));
g_object_set_data (G_OBJECT (child),
GTK_BOX_SECONDARY_CHILD,
gint **widths,
gint **heights)
{
- GtkButtonBoxPriv *priv;
GtkButtonBox *bbox;
GList *children, *list;
gint nchildren;
g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
bbox = GTK_BUTTON_BOX (widget);
- priv = bbox->priv;
homogeneous = gtk_box_get_homogeneous (GTK_BOX (widget));
if (gtk_widget_get_visible (child))
{
nchildren += 1;
- gtk_widget_size_request (child, &child_requisition);
+ gtk_widget_get_preferred_size (child,
+ &child_requisition, NULL);
avg_w += child_requisition.width + ipad_w;
avg_h += child_requisition.height + ipad_h;
}
}
- avg_w /= nchildren;
- avg_h /= nchildren;
+ avg_w /= MAX (nchildren, 1);
+ avg_h /= MAX (nchildren, 1);
*widths = g_new (gint, nchildren);
*heights = g_new (gint, nchildren);
{
GtkWidget *child;
gboolean is_secondary;
+ gboolean non_homogeneous;
child = children->data;
children = children->next;
if (gtk_widget_get_visible (child))
{
is_secondary = gtk_button_box_get_child_secondary (bbox, child);
+ non_homogeneous = gtk_button_box_get_child_non_homogeneous (bbox, child);
+
if (is_secondary)
nsecondaries++;
- gtk_widget_get_child_requisition (child, &child_requisition);
+ gtk_widget_get_preferred_size (child, &child_requisition, NULL);
if (homogeneous ||
- (child_requisition.width + ipad_w < avg_w * 1.5)) /* &&
- child_requisition.width + ipad_w > avg_w / 1.5)) */
+ (!non_homogeneous && (child_requisition.width + ipad_w < avg_w * 1.5)))
{
(*widths)[i] = -1;
if (child_requisition.width + ipad_w > needed_width)
}
if (homogeneous ||
- (child_requisition.height + ipad_h < avg_h * 1.5)) /* &&
- child_requisition.height + ipad_h > avg_h / 1.5)) */
+ (!non_homogeneous && (child_requisition.height + ipad_h < avg_h * 1.5)))
{
(*heights)[i] = -1;
if (child_requisition.height + ipad_h > needed_height)
gtk_button_box_size_request (GtkWidget *widget,
GtkRequisition *requisition)
{
- GtkButtonBoxPriv *priv;
+ GtkButtonBoxPrivate *priv;
GtkButtonBox *bbox;
gint nvis_children;
gint max_size;
gint total_size;
gint spacing;
- guint border_width;
GtkOrientation orientation;
gint *widths;
gint *heights;
else
requisition->width = max_size;
}
+}
+
+static void
+gtk_button_box_get_preferred_width (GtkWidget *widget,
+ gint *minimum,
+ gint *natural)
+{
+ GtkRequisition requisition;
+
+ gtk_button_box_size_request (widget, &requisition);
+
+ *minimum = *natural = requisition.width;
+}
+
+static void
+gtk_button_box_get_preferred_height (GtkWidget *widget,
+ gint *minimum,
+ gint *natural)
+{
+ GtkRequisition requisition;
+
+ gtk_button_box_size_request (widget, &requisition);
+
+ *minimum = *natural = requisition.height;
+}
- border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
- requisition->width += border_width * 2;
- requisition->height += border_width * 2;
+static void
+gtk_button_box_get_preferred_width_for_height (GtkWidget *widget,
+ gint height,
+ gint *minimum,
+ gint *natural)
+{
+ gtk_button_box_get_preferred_width (widget, minimum, natural);
+}
+
+static void
+gtk_button_box_get_preferred_height_for_width (GtkWidget *widget,
+ gint width,
+ gint *minimum,
+ gint *natural)
+{
+ gtk_button_box_get_preferred_height (widget, minimum, natural);
}
static void
gtk_button_box_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
- GtkButtonBoxPriv *priv;
+ GtkButtonBoxPrivate *priv;
GtkButtonBox *bbox;
GList *children, *list;
GtkAllocation child_allocation;
gint height = 0;
gint childspacing = 0;
gint spacing;
- guint border_width;
GtkOrientation orientation;
gint ipad_x, ipad_y;
gint *widths;
bbox = GTK_BUTTON_BOX (widget);
priv = bbox->priv;
- border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));
spacing = gtk_box_get_spacing (GTK_BOX (widget));
+
gtk_widget_style_get (widget,
"child-internal-pad-x", &ipad_x,
"child-internal-pad-y", &ipad_y,
}
total_size = primary_size + secondary_size;
- widget->allocation = *allocation;
+ gtk_widget_set_allocation (widget, allocation);
if (orientation == GTK_ORIENTATION_HORIZONTAL)
- width = allocation->width - border_width*2;
+ width = allocation->width;
else
- height = allocation->height - border_width*2;
+ height = allocation->height;
switch (priv->layout_style)
{
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
childspacing = (width - total_size) / (nvis_children + 1);
- x = allocation->x + border_width + childspacing;
+ x = allocation->x + childspacing;
secondary_x = x + primary_size + n_primaries * childspacing;
}
else
{
childspacing = (height - total_size) / (nvis_children + 1);
- y = allocation->y + border_width + childspacing;
+ y = allocation->y + childspacing;
secondary_y = y + primary_size + n_primaries * childspacing;
}
if (nvis_children >= 2)
{
childspacing = (width - total_size) / (nvis_children - 1);
- x = allocation->x + border_width;
+ x = allocation->x;
secondary_x = x + primary_size + n_primaries * childspacing;
}
- else
+ else if (nvis_children == 1)
{
- /* one or zero children, just center */
+ /* one child, just center */
childspacing = width;
x = secondary_x = allocation->x
+ (allocation->width - widths[0]) / 2;
}
+ else
+ {
+ /* zero children, meh */
+ childspacing = width;
+ x = secondary_x = allocation->x + allocation->width / 2;
+ }
}
else
{
if (nvis_children >= 2)
{
childspacing = (height - total_size) / (nvis_children - 1);
- y = allocation->y + border_width;
+ y = allocation->y;
secondary_y = y + primary_size + n_primaries * childspacing;
}
- else
+ else if (nvis_children == 1)
{
- /* one or zero children, just center */
+ /* one child, just center */
childspacing = height;
y = secondary_y = allocation->y
- + (allocation->height - heights[0]) / 2;
+ + (allocation->height - heights[0]) / 2;
+ }
+ else
+ {
+ /* zero children, meh */
+ childspacing = height;
+ y = secondary_y = allocation->y + allocation->height / 2;
}
}
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
childspacing = spacing;
- x = allocation->x + border_width;
+ x = allocation->x;
secondary_x = allocation->x + allocation->width
- - secondary_size - spacing * (n_secondaries - 1) - border_width;
+ - secondary_size - spacing * (n_secondaries - 1);
}
else
{
childspacing = spacing;
- y = allocation->y + border_width;
+ y = allocation->y;
secondary_y = allocation->y + allocation->height
- - secondary_size - spacing * (n_secondaries - 1) - border_width;
+ - secondary_size - spacing * (n_secondaries - 1);
}
break;
{
childspacing = spacing;
x = allocation->x + allocation->width
- - primary_size - spacing * (n_primaries - 1) - border_width;
- secondary_x = allocation->x + border_width;
+ - primary_size - spacing * (n_primaries - 1);
+ secondary_x = allocation->x;
}
else
{
childspacing = spacing;
y = allocation->y + allocation->height
- - primary_size - spacing * (n_primaries - 1) - border_width;
- secondary_y = allocation->y + border_width;
+ - primary_size - spacing * (n_primaries - 1);
+ secondary_y = allocation->y;
}
break;
(allocation->width
- (primary_size + spacing * (n_primaries - 1))) / 2
+ (secondary_size + n_secondaries * spacing) / 2;
- secondary_x = allocation->x + border_width;
+ secondary_x = allocation->x;
}
else
{
(allocation->height
- (primary_size + spacing * (n_primaries - 1))) / 2
+ (secondary_size + n_secondaries * spacing) / 2;
- secondary_y = allocation->y + border_width;
+ secondary_y = allocation->y;
}
break;
"orientation", orientation,
NULL);
}
+
+/**
+ * gtk_button_box_get_child_non_homogeneous:
+ * @widget: a #GtkButtonBox
+ * @child: a child of @widget
+ *
+ * Returns whether the child is exempted from homogenous
+ * sizing.
+ *
+ * Returns: %TRUE if the child is not subject to homogenous sizing
+ *
+ * Since: 3.2
+ */
+gboolean
+gtk_button_box_get_child_non_homogeneous (GtkButtonBox *widget,
+ GtkWidget *child)
+{
+ g_return_val_if_fail (GTK_IS_BUTTON_BOX (widget), FALSE);
+ g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
+
+ return (g_object_get_data (G_OBJECT (child), GTK_BOX_NON_HOMOGENEOUS) != NULL);
+}
+
+/**
+ * gtk_button_box_set_child_non_homogeneous:
+ * @widget: a #GtkButtonBox
+ * @child: a child of @widget
+ * @non_homogeneous: the new value
+ *
+ * Sets whether the child is exempted from homogeous sizing.
+ *
+ * Since: 3.2
+ */
+void
+gtk_button_box_set_child_non_homogeneous (GtkButtonBox *widget,
+ GtkWidget *child,
+ gboolean non_homogeneous)
+{
+ g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
+ g_return_if_fail (GTK_IS_WIDGET (child));
+ g_return_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (widget));
+
+ g_object_set_data (G_OBJECT (child),
+ GTK_BOX_NON_HOMOGENEOUS,
+ non_homogeneous ? GINT_TO_POINTER (1) : NULL);
+ gtk_widget_child_notify (child, "non-homogeneous");
+
+ if (gtk_widget_get_visible (GTK_WIDGET (widget)) &&
+ gtk_widget_get_visible (child))
+ gtk_widget_queue_resize (child);
+}