Add padding and alignment properties to GtkWidget

h-align = START,END,CENTER,FILL
v-align = START,END,CENTER,FILL
padding-left,right,top,bottom,all-sides

These should obsolete all such similar properties on
layout containers, GtkMisc, GtkAlignment, GtkContainer::border-width

Padding is outside the size request.
If padding were not outside the set_size_request() it would not work the
same way as container-supplied (child property) padding.

Conceptually set_size_request() forces the value from the subclass
(the original unadjusted request) and then we go on to adjust
the request further by adding the padding.

To reflect this, we'll probably rename padding to margin
(and squash that back with this patch).
This commit is contained in:
Havoc Pennington
2010-09-05 12:21:27 -04:00
parent 86e475b843
commit c440a6760e
3 changed files with 560 additions and 8 deletions

View File

@@ -35,6 +35,36 @@
G_BEGIN_DECLS
/**
* GtkAlign:
*
* @GTK_ALIGN_FILL: stretch to fill all space if possible, center if
* no meaningful way to stretch
* @GTK_ALIGN_START: snap to left or top side, leaving space on right
* or bottom
* @GTK_ALIGN_END: snap to right or bottom side, leaving space on left
* or top
* @GTK_ALIGN_CENTER: center natural width of widget inside the
* allocation
*
* Controls how a widget deals with extra space in a single (x or y)
* dimension.
*
* Alignment only matters if the widget receives a "too large"
* allocation, for example if you packed the widget with the "expand"
* flag inside a #GtkBox, then the widget might get extra space. If
* you have for example a 16x16 icon inside a 32x32 space, the icon
* could be scaled and stretched, it could be centered, or it could be
* positioned to one side of the space.
*/
typedef enum
{
GTK_ALIGN_FILL,
GTK_ALIGN_START,
GTK_ALIGN_END,
GTK_ALIGN_CENTER
} GtkAlign;
/* Arrow placement */
typedef enum
{

View File

@@ -227,7 +227,14 @@ enum {
PROP_TOOLTIP_MARKUP,
PROP_TOOLTIP_TEXT,
PROP_WINDOW,
PROP_DOUBLE_BUFFERED
PROP_DOUBLE_BUFFERED,
PROP_H_ALIGN,
PROP_V_ALIGN,
PROP_PADDING_LEFT,
PROP_PADDING_RIGHT,
PROP_PADDING_TOP,
PROP_PADDING_BOTTOM,
PROP_PADDING_ALL_SIDES
};
typedef struct _GtkStateData GtkStateData;
@@ -812,6 +819,140 @@ gtk_widget_class_init (GtkWidgetClass *klass)
TRUE,
GTK_PARAM_READWRITE));
/**
* GtkWidget:h-align
*
* How to distribute horizontal space if widget gets extra space, see #GtkAlign
*
* Since: 3.0
*/
g_object_class_install_property (gobject_class,
PROP_H_ALIGN,
g_param_spec_enum ("h-align",
P_("Horizontal Alignment"),
P_("How to position in extra horizontal space"),
GTK_TYPE_ALIGN,
GTK_ALIGN_FILL,
GTK_PARAM_READWRITE));
/**
* GtkWidget:v-align
*
* How to distribute vertical space if widget gets extra space, see #GtkAlign
*
* Since: 3.0
*/
g_object_class_install_property (gobject_class,
PROP_V_ALIGN,
g_param_spec_enum ("v-align",
P_("Vertical Alignment"),
P_("How to position in extra vertical space"),
GTK_TYPE_ALIGN,
GTK_ALIGN_FILL,
GTK_PARAM_READWRITE));
/**
* GtkWidget:padding-left
*
* Padding on left side of widget.
*
* This property adds padding outside of the widget's normal size
* request, the padding will be added in addition to the size from
* gtk_widget_set_size_request() for example.
*
* Since: 3.0
*/
g_object_class_install_property (gobject_class,
PROP_PADDING_LEFT,
g_param_spec_int ("padding-left",
P_("Padding on Left"),
P_("Pixels of extra space on the left side"),
0,
G_MAXINT16,
0,
GTK_PARAM_READWRITE));
/**
* GtkWidget:padding-right
*
* Padding on right side of widget.
*
* This property adds padding outside of the widget's normal size
* request, the padding will be added in addition to the size from
* gtk_widget_set_size_request() for example.
*
* Since: 3.0
*/
g_object_class_install_property (gobject_class,
PROP_PADDING_RIGHT,
g_param_spec_int ("padding-right",
P_("Padding on Right"),
P_("Pixels of extra space on the right side"),
0,
G_MAXINT16,
0,
GTK_PARAM_READWRITE));
/**
* GtkWidget:padding-top
*
* Padding on top side of widget.
*
* This property adds padding outside of the widget's normal size
* request, the padding will be added in addition to the size from
* gtk_widget_set_size_request() for example.
*
* Since: 3.0
*/
g_object_class_install_property (gobject_class,
PROP_PADDING_TOP,
g_param_spec_int ("padding-top",
P_("Padding on Top"),
P_("Pixels of extra space on the top side"),
0,
G_MAXINT16,
0,
GTK_PARAM_READWRITE));
/**
* GtkWidget:padding-bottom
*
* Padding on bottom side of widget.
*
* This property adds padding outside of the widget's normal size
* request, the padding will be added in addition to the size from
* gtk_widget_set_size_request() for example.
*
* Since: 3.0
*/
g_object_class_install_property (gobject_class,
PROP_PADDING_BOTTOM,
g_param_spec_int ("padding-bottom",
P_("Padding on Bottom"),
P_("Pixels of extra space on the bottom side"),
0,
G_MAXINT16,
0,
GTK_PARAM_READWRITE));
/**
* GtkWidget:padding-all-sides
*
* Sets all four sides' padding at once. If read, returns max
* padding on any side.
*
* Since: 3.0
*/
g_object_class_install_property (gobject_class,
PROP_PADDING_ALL_SIDES,
g_param_spec_int ("padding-all-sides",
P_("All Paddings"),
P_("Pixels of extra space on all four sides"),
0,
G_MAXINT16,
0,
GTK_PARAM_READWRITE));
/**
* GtkWidget::show:
* @widget: the object which received the signal.
@@ -2747,6 +2888,32 @@ gtk_widget_set_property (GObject *object,
case PROP_DOUBLE_BUFFERED:
gtk_widget_set_double_buffered (widget, g_value_get_boolean (value));
break;
case PROP_H_ALIGN:
gtk_widget_set_h_align (widget, g_value_get_enum (value));
break;
case PROP_V_ALIGN:
gtk_widget_set_v_align (widget, g_value_get_enum (value));
break;
case PROP_PADDING_LEFT:
gtk_widget_set_padding_left (widget, g_value_get_int (value));
break;
case PROP_PADDING_RIGHT:
gtk_widget_set_padding_right (widget, g_value_get_int (value));
break;
case PROP_PADDING_TOP:
gtk_widget_set_padding_top (widget, g_value_get_int (value));
break;
case PROP_PADDING_BOTTOM:
gtk_widget_set_padding_bottom (widget, g_value_get_int (value));
break;
case PROP_PADDING_ALL_SIDES:
g_object_freeze_notify (G_OBJECT (widget));
gtk_widget_set_padding_left (widget, g_value_get_int (value));
gtk_widget_set_padding_right (widget, g_value_get_int (value));
gtk_widget_set_padding_top (widget, g_value_get_int (value));
gtk_widget_set_padding_bottom (widget, g_value_get_int (value));
g_object_thaw_notify (G_OBJECT (widget));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -2856,6 +3023,40 @@ gtk_widget_get_property (GObject *object,
case PROP_DOUBLE_BUFFERED:
g_value_set_boolean (value, gtk_widget_get_double_buffered (widget));
break;
case PROP_H_ALIGN:
g_value_set_enum (value, gtk_widget_get_h_align (widget));
break;
case PROP_V_ALIGN:
g_value_set_enum (value, gtk_widget_get_v_align (widget));
break;
case PROP_PADDING_LEFT:
g_value_set_int (value, gtk_widget_get_padding_left (widget));
break;
case PROP_PADDING_RIGHT:
g_value_set_int (value, gtk_widget_get_padding_right (widget));
break;
case PROP_PADDING_TOP:
g_value_set_int (value, gtk_widget_get_padding_top (widget));
break;
case PROP_PADDING_BOTTOM:
g_value_set_int (value, gtk_widget_get_padding_bottom (widget));
break;
case PROP_PADDING_ALL_SIDES:
{
GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (widget, FALSE);
if (aux_info == NULL)
{
g_value_set_int (value, 0);
}
else
{
g_value_set_int (value, MAX (MAX (aux_info->padding.left,
aux_info->padding.right),
MAX (aux_info->padding.top,
aux_info->padding.bottom)));
}
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -4222,14 +4423,120 @@ gtk_widget_real_size_allocate (GtkWidget *widget,
}
}
static void
get_span_inside_border (GtkWidget *widget,
GtkAlign align,
int start_pad,
int end_pad,
int allocated_outside_size,
int natural_inside_size,
int *coord_inside_p,
int *size_inside_p)
{
int inside_allocated;
int content_size;
int coord, size;
inside_allocated = allocated_outside_size - start_pad - end_pad;
content_size = natural_inside_size;
if (content_size > inside_allocated)
{
/* didn't get full natural size */
content_size = inside_allocated;
}
coord = size = 0; /* silence compiler */
switch (align)
{
case GTK_ALIGN_FILL:
coord = start_pad;
size = inside_allocated;
break;
case GTK_ALIGN_START:
coord = start_pad;
size = content_size;
break;
case GTK_ALIGN_END:
coord = allocated_outside_size - end_pad - content_size;
size = content_size;
break;
case GTK_ALIGN_CENTER:
coord = start_pad + (inside_allocated - content_size) / 2;
size = content_size;
break;
}
if (coord_inside_p)
*coord_inside_p = coord;
if (size_inside_p)
*size_inside_p = size;
}
static void
get_span_inside_border_horizontal (GtkWidget *widget,
const GtkWidgetAuxInfo *aux_info,
int allocated_outside_width,
int natural_inside_width,
int *x_inside_p,
int *width_inside_p)
{
get_span_inside_border (widget,
aux_info->h_align,
aux_info->padding.left,
aux_info->padding.right,
allocated_outside_width,
natural_inside_width,
x_inside_p,
width_inside_p);
}
static void
get_span_inside_border_vertical (GtkWidget *widget,
const GtkWidgetAuxInfo *aux_info,
int allocated_outside_height,
int natural_inside_height,
int *y_inside_p,
int *height_inside_p)
{
get_span_inside_border (widget,
aux_info->v_align,
aux_info->padding.top,
aux_info->padding.bottom,
allocated_outside_height,
natural_inside_height,
y_inside_p,
height_inside_p);
}
static void
gtk_widget_real_adjust_size_allocation (GtkWidget *widget,
GtkAllocation *allocation)
{
/* We have no adjustments by default for now, but we have this empty
* function here so subclasses can chain up in case we do add
* something.
*/
const GtkWidgetAuxInfo *aux_info;
GtkRequisition min, natural;
int x, y, w, h;
aux_info = _gtk_widget_get_aux_info_or_defaults (widget);
gtk_size_request_get_size (GTK_SIZE_REQUEST (widget), &min, &natural);
get_span_inside_border_horizontal (widget,
aux_info,
allocation->width,
natural.width,
&x, &w);
get_span_inside_border_vertical (widget,
aux_info,
allocation->height,
natural.height,
&y, &h);
allocation->x += x;
allocation->y += y;
allocation->width = w;
allocation->height = h;
}
static gboolean
@@ -7889,6 +8196,11 @@ gtk_widget_set_usize_internal (GtkWidget *widget,
*
* Widgets can't actually be allocated a size less than 1 by 1, but
* you can pass 0,0 to this function to mean "as small as possible."
*
* The size request set here does not include any padding from the
* #GtkWidget properties padding-left, padding-right, padding-top, and
* padding-bottom, but it does include pretty much all other padding
* properties set by any subclass of #GtkWidget.
**/
void
gtk_widget_set_size_request (GtkWidget *widget,
@@ -8967,6 +9279,17 @@ gtk_widget_real_adjust_size_request (GtkWidget *widget,
* in gtksizerequest.c when calling their size request vfuncs.
*/
*natural_size = MAX (*natural_size, *minimum_size);
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
*minimum_size += (aux_info->padding.left + aux_info->padding.right);
*natural_size += (aux_info->padding.left + aux_info->padding.right);
}
else
{
*minimum_size += (aux_info->padding.top + aux_info->padding.bottom);
*natural_size += (aux_info->padding.top + aux_info->padding.bottom);
}
}
/**
@@ -9447,7 +9770,10 @@ gtk_widget_propagate_state (GtkWidget *widget,
}
static const GtkWidgetAuxInfo default_aux_info = {
-1, -1
-1, -1,
GTK_ALIGN_FILL,
GTK_ALIGN_FILL,
{ 0, 0, 0, 0 }
};
/*
@@ -10956,8 +11282,165 @@ gtk_widget_size_request_init (GtkSizeRequestIface *iface)
iface->get_width_for_height = gtk_widget_real_get_width_for_height;
iface->get_height_for_width = gtk_widget_real_get_height_for_width;
}
GtkAlign
gtk_widget_get_h_align (GtkWidget *widget)
{
g_return_val_if_fail (GTK_IS_WIDGET (widget), GTK_ALIGN_FILL);
return _gtk_widget_get_aux_info_or_defaults (widget)->h_align;
}
void
gtk_widget_set_h_align (GtkWidget *widget,
GtkAlign align)
{
GtkWidgetAuxInfo *aux_info;
g_return_if_fail (GTK_IS_WIDGET (widget));
aux_info = _gtk_widget_get_aux_info (widget, TRUE);
if (aux_info->h_align == align)
return;
aux_info->h_align = align;
gtk_widget_queue_resize (widget);
g_object_notify (G_OBJECT (widget), "h-align");
}
GtkAlign
gtk_widget_get_v_align (GtkWidget *widget)
{
g_return_val_if_fail (GTK_IS_WIDGET (widget), GTK_ALIGN_FILL);
return _gtk_widget_get_aux_info_or_defaults (widget)->v_align;
}
void
gtk_widget_set_v_align (GtkWidget *widget,
GtkAlign align)
{
GtkWidgetAuxInfo *aux_info;
g_return_if_fail (GTK_IS_WIDGET (widget));
aux_info = _gtk_widget_get_aux_info (widget, TRUE);
if (aux_info->v_align == align)
return;
aux_info->v_align = align;
gtk_widget_queue_resize (widget);
g_object_notify (G_OBJECT (widget), "v-align");
}
int
gtk_widget_get_padding_left (GtkWidget *widget)
{
g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
return _gtk_widget_get_aux_info_or_defaults (widget)->padding.left;
}
void
gtk_widget_set_padding_left (GtkWidget *widget,
int padding)
{
GtkWidgetAuxInfo *aux_info;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (padding <= G_MAXINT16);
aux_info = _gtk_widget_get_aux_info (widget, TRUE);
if (aux_info->padding.left == padding)
return;
aux_info->padding.left = padding;
gtk_widget_queue_resize (widget);
g_object_notify (G_OBJECT (widget), "padding-left");
}
int
gtk_widget_get_padding_right (GtkWidget *widget)
{
g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
return _gtk_widget_get_aux_info_or_defaults (widget)->padding.right;
}
void
gtk_widget_set_padding_right (GtkWidget *widget,
int padding)
{
GtkWidgetAuxInfo *aux_info;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (padding <= G_MAXINT16);
aux_info = _gtk_widget_get_aux_info (widget, TRUE);
if (aux_info->padding.right == padding)
return;
aux_info->padding.right = padding;
gtk_widget_queue_resize (widget);
g_object_notify (G_OBJECT (widget), "padding-right");
}
int
gtk_widget_get_padding_top (GtkWidget *widget)
{
g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
return _gtk_widget_get_aux_info_or_defaults (widget)->padding.top;
}
void
gtk_widget_set_padding_top (GtkWidget *widget,
int padding)
{
GtkWidgetAuxInfo *aux_info;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (padding <= G_MAXINT16);
aux_info = _gtk_widget_get_aux_info (widget, TRUE);
if (aux_info->padding.top == padding)
return;
aux_info->padding.top = padding;
gtk_widget_queue_resize (widget);
g_object_notify (G_OBJECT (widget), "padding-top");
}
int
gtk_widget_get_padding_bottom (GtkWidget *widget)
{
g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
return _gtk_widget_get_aux_info_or_defaults (widget)->padding.bottom;
}
void
gtk_widget_set_padding_bottom (GtkWidget *widget,
int padding)
{
GtkWidgetAuxInfo *aux_info;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (padding <= G_MAXINT16);
aux_info = _gtk_widget_get_aux_info (widget, TRUE);
if (aux_info->padding.bottom == padding)
return;
aux_info->padding.bottom = padding;
gtk_widget_queue_resize (widget);
g_object_notify (G_OBJECT (widget), "padding-bottom");
}
/**
* gtk_widget_get_clipboard:
* @widget: a #GtkWidget

View File

@@ -551,6 +551,24 @@ struct _GtkWidgetAuxInfo
{
gint width;
gint height;
guint h_align : 4;
guint v_align : 4;
/* FIXME GtkBorder uses 32-bit ints for each side which is kinda
* bloated; 255 (or even 128) already exceeds 99.99% of actual padding
* settings, int16 would exceed 100%, int32 is nuts. GtkBox uses
* uint16.
*
* Just fix GtkBorder itself? The main danger is probably going signed
* to unsigned, rather than 32 to 16.
*/
struct {
guint16 left;
guint16 right;
guint16 top;
guint16 bottom;
} padding;
};
struct _GtkWidgetShapeInfo
@@ -779,6 +797,27 @@ void gtk_widget_set_support_multidevice (GtkWidget *widget,
/* Accessibility support */
AtkObject* gtk_widget_get_accessible (GtkWidget *widget);
/* Padding and alignment */
GtkAlign gtk_widget_get_h_align (GtkWidget *widget);
void gtk_widget_set_h_align (GtkWidget *widget,
GtkAlign align);
GtkAlign gtk_widget_get_v_align (GtkWidget *widget);
void gtk_widget_set_v_align (GtkWidget *widget,
GtkAlign align);
int gtk_widget_get_padding_left (GtkWidget *widget);
void gtk_widget_set_padding_left (GtkWidget *widget,
int padding);
int gtk_widget_get_padding_right (GtkWidget *widget);
void gtk_widget_set_padding_right (GtkWidget *widget,
int padding);
int gtk_widget_get_padding_top (GtkWidget *widget);
void gtk_widget_set_padding_top (GtkWidget *widget,
int padding);
int gtk_widget_get_padding_bottom (GtkWidget *widget);
void gtk_widget_set_padding_bottom (GtkWidget *widget,
int padding);
/* The following functions must not be called on an already
* realized widget. Because it is possible that somebody
* can call get_colormap() or get_visual() and save the