[gtk+/widget-padding: 10/11] Add padding and alignment properties to GtkWidget



commit c440a6760e90262ce0fb3d6356602709111ffb94
Author: Havoc Pennington <hp pobox com>
Date:   Sun Sep 5 12:21:27 2010 -0400

    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).

 gtk/gtkenums.h  |   30 ++++
 gtk/gtkwidget.c |  499 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 gtk/gtkwidget.h |   39 +++++
 3 files changed, 560 insertions(+), 8 deletions(-)
---
diff --git a/gtk/gtkenums.h b/gtk/gtkenums.h
index 019fb05..71ea365 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 cff9b08..b04f99a 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -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;
@@ -813,6 +820,140 @@ gtk_widget_class_init (GtkWidgetClass *klass)
                                                          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;
@@ -4223,13 +4424,119 @@ 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
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index 8d49485..3f41333 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -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



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]