diff --git a/gtk/gtkenums.h b/gtk/gtkenums.h index 019fb0531c..71ea365259 100644 --- a/gtk/gtkenums.h +++ b/gtk/gtkenums.h @@ -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 { diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 874be7eb78..fa383f315f 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -278,7 +278,14 @@ enum { PROP_TOOLTIP_MARKUP, PROP_TOOLTIP_TEXT, PROP_WINDOW, - PROP_DOUBLE_BUFFERED + PROP_DOUBLE_BUFFERED, + PROP_H_ALIGN, + PROP_V_ALIGN, + PROP_MARGIN_LEFT, + PROP_MARGIN_RIGHT, + PROP_MARGIN_TOP, + PROP_MARGIN_BOTTOM, + PROP_MARGIN }; typedef struct _GtkStateData GtkStateData; @@ -863,6 +870,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:margin-left + * + * Margin on left side of widget. + * + * This property adds margin outside of the widget's normal size + * request, the margin 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_MARGIN_LEFT, + g_param_spec_int ("margin-left", + P_("Margin on Left"), + P_("Pixels of extra space on the left side"), + 0, + G_MAXINT16, + 0, + GTK_PARAM_READWRITE)); + + /** + * GtkWidget:margin-right + * + * Margin on right side of widget. + * + * This property adds margin outside of the widget's normal size + * request, the margin 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_MARGIN_RIGHT, + g_param_spec_int ("margin-right", + P_("Margin on Right"), + P_("Pixels of extra space on the right side"), + 0, + G_MAXINT16, + 0, + GTK_PARAM_READWRITE)); + + /** + * GtkWidget:margin-top + * + * Margin on top side of widget. + * + * This property adds margin outside of the widget's normal size + * request, the margin 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_MARGIN_TOP, + g_param_spec_int ("margin-top", + P_("Margin on Top"), + P_("Pixels of extra space on the top side"), + 0, + G_MAXINT16, + 0, + GTK_PARAM_READWRITE)); + + /** + * GtkWidget:margin-bottom + * + * Margin on bottom side of widget. + * + * This property adds margin outside of the widget's normal size + * request, the margin 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_MARGIN_BOTTOM, + g_param_spec_int ("margin-bottom", + P_("Margin on Bottom"), + P_("Pixels of extra space on the bottom side"), + 0, + G_MAXINT16, + 0, + GTK_PARAM_READWRITE)); + + /** + * GtkWidget:margin + * + * Sets all four sides' margin at once. If read, returns max + * margin on any side. + * + * Since: 3.0 + */ + g_object_class_install_property (gobject_class, + PROP_MARGIN, + g_param_spec_int ("margin", + P_("All Margins"), + 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. @@ -2800,6 +2941,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_MARGIN_LEFT: + gtk_widget_set_margin_left (widget, g_value_get_int (value)); + break; + case PROP_MARGIN_RIGHT: + gtk_widget_set_margin_right (widget, g_value_get_int (value)); + break; + case PROP_MARGIN_TOP: + gtk_widget_set_margin_top (widget, g_value_get_int (value)); + break; + case PROP_MARGIN_BOTTOM: + gtk_widget_set_margin_bottom (widget, g_value_get_int (value)); + break; + case PROP_MARGIN: + g_object_freeze_notify (G_OBJECT (widget)); + gtk_widget_set_margin_left (widget, g_value_get_int (value)); + gtk_widget_set_margin_right (widget, g_value_get_int (value)); + gtk_widget_set_margin_top (widget, g_value_get_int (value)); + gtk_widget_set_margin_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; @@ -2910,6 +3077,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_MARGIN_LEFT: + g_value_set_int (value, gtk_widget_get_margin_left (widget)); + break; + case PROP_MARGIN_RIGHT: + g_value_set_int (value, gtk_widget_get_margin_right (widget)); + break; + case PROP_MARGIN_TOP: + g_value_set_int (value, gtk_widget_get_margin_top (widget)); + break; + case PROP_MARGIN_BOTTOM: + g_value_set_int (value, gtk_widget_get_margin_bottom (widget)); + break; + case PROP_MARGIN: + { + 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->margin.left, + aux_info->margin.right), + MAX (aux_info->margin.top, + aux_info->margin.bottom))); + } + } + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -4308,14 +4509,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->margin.left, + aux_info->margin.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->margin.top, + aux_info->margin.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 @@ -8028,6 +8335,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 margin from the + * #GtkWidget properties margin-left, margin-right, margin-top, and + * margin-bottom, but it does include pretty much all other padding + * or border properties set by any subclass of #GtkWidget. **/ void gtk_widget_set_size_request (GtkWidget *widget, @@ -9129,6 +9441,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->margin.left + aux_info->margin.right); + *natural_size += (aux_info->margin.left + aux_info->margin.right); + } + else + { + *minimum_size += (aux_info->margin.top + aux_info->margin.bottom); + *natural_size += (aux_info->margin.top + aux_info->margin.bottom); + } } /** @@ -9634,7 +9957,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 } }; /* @@ -11156,8 +11482,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_margin_left (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), 0); + + return _gtk_widget_get_aux_info_or_defaults (widget)->margin.left; +} + +void +gtk_widget_set_margin_left (GtkWidget *widget, + int margin) +{ + GtkWidgetAuxInfo *aux_info; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (margin <= G_MAXINT16); + + aux_info = _gtk_widget_get_aux_info (widget, TRUE); + + if (aux_info->margin.left == margin) + return; + + aux_info->margin.left = margin; + gtk_widget_queue_resize (widget); + g_object_notify (G_OBJECT (widget), "margin-left"); +} + +int +gtk_widget_get_margin_right (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), 0); + + return _gtk_widget_get_aux_info_or_defaults (widget)->margin.right; +} + +void +gtk_widget_set_margin_right (GtkWidget *widget, + int margin) +{ + GtkWidgetAuxInfo *aux_info; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (margin <= G_MAXINT16); + + aux_info = _gtk_widget_get_aux_info (widget, TRUE); + + if (aux_info->margin.right == margin) + return; + + aux_info->margin.right = margin; + gtk_widget_queue_resize (widget); + g_object_notify (G_OBJECT (widget), "margin-right"); +} + +int +gtk_widget_get_margin_top (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), 0); + + return _gtk_widget_get_aux_info_or_defaults (widget)->margin.top; +} + +void +gtk_widget_set_margin_top (GtkWidget *widget, + int margin) +{ + GtkWidgetAuxInfo *aux_info; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (margin <= G_MAXINT16); + + aux_info = _gtk_widget_get_aux_info (widget, TRUE); + + if (aux_info->margin.top == margin) + return; + + aux_info->margin.top = margin; + gtk_widget_queue_resize (widget); + g_object_notify (G_OBJECT (widget), "margin-top"); +} + +int +gtk_widget_get_margin_bottom (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), 0); + + return _gtk_widget_get_aux_info_or_defaults (widget)->margin.bottom; +} + +void +gtk_widget_set_margin_bottom (GtkWidget *widget, + int margin) +{ + GtkWidgetAuxInfo *aux_info; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (margin <= G_MAXINT16); + + aux_info = _gtk_widget_get_aux_info (widget, TRUE); + + if (aux_info->margin.bottom == margin) + return; + + aux_info->margin.bottom = margin; + gtk_widget_queue_resize (widget); + g_object_notify (G_OBJECT (widget), "margin-bottom"); +} + /** * gtk_widget_get_clipboard: * @widget: a #GtkWidget diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h index 66d3f2737c..8efdcd6ce7 100644 --- a/gtk/gtkwidget.h +++ b/gtk/gtkwidget.h @@ -502,6 +502,11 @@ struct _GtkWidgetAuxInfo { gint width; gint height; + + guint h_align : 4; + guint v_align : 4; + + GtkBorder margin; }; struct _GtkWidgetShapeInfo @@ -730,6 +735,28 @@ void gtk_widget_set_support_multidevice (GtkWidget *widget, /* Accessibility support */ AtkObject* gtk_widget_get_accessible (GtkWidget *widget); + +/* Margin 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_margin_left (GtkWidget *widget); +void gtk_widget_set_margin_left (GtkWidget *widget, + int margin); +int gtk_widget_get_margin_right (GtkWidget *widget); +void gtk_widget_set_margin_right (GtkWidget *widget, + int margin); +int gtk_widget_get_margin_top (GtkWidget *widget); +void gtk_widget_set_margin_top (GtkWidget *widget, + int margin); +int gtk_widget_get_margin_bottom (GtkWidget *widget); +void gtk_widget_set_margin_bottom (GtkWidget *widget, + int margin); + + /* 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