padding cleanup



Hi,

Matthias's mention of padding props got me thinking about how this
could be mopped up (based shamelessly on what we did in HippoCanvas
and then BigBox)

I'm attaching a patch that I haven't even tried to compile (my jhbuild
setup is kinda hosed, don't ask) illustrating what I might like to see
if starting from scratch.

In brief it adds to GtkWidget:

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

Here are some opinions about the current APIs:

* GtkBox would not need "fill" or "padding" flags on children if
GtkWidget just had those built-in
* GtkTable would not need fill or x/y padding either
* "fill" and "padding" are universally useful child properties, not
specific to any layout manager, so should be on Widget
* xalign, yalign as floats are near-useless; 0.0, 0.5, and 1.0 cover
99.9% of cases
* xscale, yscale as floats are also near-useless; 0.0, 1.0 cover 99.9%
* xpad, ypad are annoying because you have to set two sides at once
* having to create a GtkAlignment and pack a widget in it is clunky,
vs. just setting a property
* GtkAlignment-wrapping is sort of a clunky model for a UI builder,
vs. setting props
* GtkAlignment is generally redundant with at least some of the
properties on the layout container it's inside
* the way GtkMisc works is clunky (subclass has to do all the work),
no methods on parent "add stuff outside my main content to request" or
"subtract stuff outside my main content from request"
* same clunkiness leads to wonky "get_child_padding_delta()" hack in GtkBin
* often one wishes a widget was a GtkMisc and it isn't.
* containers can't be a GtkMisc.
* GtkMisc and GtkAlignment should have the same props, but they don't.
xypad vs. leftrighttopbottom-padding is odd and illogical
* redundant padding props make a mess of code where you are looking at
some whitespace and part of it is in one spot and part in another
(e.g. set xpad on a label, then pack the label into a box with
padding)
* "fill" mode should be mutually exclusive with center/start/end
alignment, rather than an orthogonal setting

So I'd love to see 4.x remove: GtkMisc, GtkAlignment, fill/padding
props in all layout containers. In 3.x it'd be nice if all the random
pads and aligns and scales were consolidated in GtkWidget so people
could start ignoring the cruft.

Two judgment calls / discussion areas I can think of.

1. if just taking a patch like the attached, which adds GtkMisc-style
stuff to GtkWidget, all subclasses of GtkWidget would have to be
modified to use the new "add padding stuff to my request" and "remove
padding stuff from my allocation" calls. Hopefully this is not a
necessary tedium - some way to make porting widgets easy, or
unnecessary, would be better.

- one approach: put padding stuff in the size_request / size_allocate
*wrappers* in the same way that size groups and set_size_request are
handled. all widgets just work without modifications, is the big plus
of this.

- another approach: somehow add a separate set of size request and
size allocate methods that requested/allocated the inside-padding area
instead of the including-padding area. Then GtkWidget would
default-implement the current size request and size allocate by
invoking the inside-padding versions, if present. The "separate set of
methods" could be:
  . add a new interface, like GtkSizeRequest but with allocate also,
and GtkWidget's default implementations do GTK_IS_THIS_NEW_INTERFACE()
  . add new virtual methods in GtkWidgetClass and check != NULL
 In this approach, to port a widget you'd just change its
request/allocate implementations over to override different (but
identical-parameters) superclass methods.

- yet another approach: add a set of convenience functions that take
the old-style functions as argument, so your get_width()
implementation would call:
     return gtk_widget_get_width_with_padding(widget, real_get_width_func);
  where real_get_width_func would be the previous, old-style get_width(). eh.

- final approach is "punt," just require using the "protected" APIs
like gtk_widget_get_padded_width() in the attached patch, which is
still nicer than subclassing GtkMisc where you have to manually
compute align and pad. But it's the same basic idea as how you
subclass GtkMisc.

2. supporting superclasses deriving from GtkWidget that want to add
more "padding area" stuff to their subclasses on top of what GtkWidget
already does. GtkMisc would be an example - maybe the only example?
I'm not sure if this is useful, but to make implementation of GtkMisc
cleaner, one could remove knowledge of GtkMisc from its subclasses,
and make GtkMisc padding/alignment "additive" with the
built-in-to-Widget ones. So if you set both GtkMisc xpad and GtkWidget
padding-left the sum of the two would be used.

- one approach: virtualize "add padding stuff to my request" and
"remove padding stuff from my allocation" calls. GtkMisc overrides
these, chains up to GtkWidget, then adds its own stuff. GtkLabel now
just calls GtkWidget methods instead of manually peeking at GtkMisc
align/pad.

- another approach: don't solve this problem. for back compat, just
map GtkMisc to the new GtkWidget properties, so xpad/ypad just set
padding-{left,right,top,bottom} and xalign/yalign are rounded to
0.0,0.5,1.0 and then set the corresponding GtkWidget prop. Don't
provide any standard mechanism for a superclass like GtkMisc to be
implemented. (_containers_ that added stuff outside a child would
still be totally possible, but there wouldn't be a standard way to do
a base class that added stuff around a center area provided by a
subclass, as with GtkMisc)


Anyhow. For me the big question is, does adding yet another way to
pad/align put GTK on the path to cleaning this up, or add yet another
way to the mess ...

With the GtkWidget feature, one _could_ just ignore Misc/Alignment,
and just use gtk_container_add on GtkBox to avoid its fill/padding
params. So you could write cleaned-up application code, in theory.

Some bikeshed questions that are likely irrelevant for now:
 - is it called padding or border (border is nice perhaps since it
contrasts with all the existing stuff called padding)
 - GtkAlignment has top-padding and this sketch patch has padding-top

Havoc
diff --git a/gtk/gtkenums.h b/gtk/gtkenums.h
index 3523dc3..8218534 100644
--- a/gtk/gtkenums.h
+++ b/gtk/gtkenums.h
@@ -35,6 +35,37 @@
 
 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;
+
 /* Anchor types */
 typedef enum
 {
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index d490415..e0d3a41 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;
@@ -240,6 +247,31 @@ struct _GtkStateData
   guint		use_forall : 1;
 };
 
+/* 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.
+ */
+typedef struct {
+  guint16 left;
+  guint16 right;
+  guint16 top;
+  guint16 bottom;
+} GtkBorder16;
+
+typedef struct _GtkWidgetPadding GtkWidgetPadding;
+
+struct _GtkWidgetPadding
+{
+  guint   h_align : 4;
+  guint   v_align : 4;
+
+  GtkBorder16 padding;
+};
+
 /* --- prototypes --- */
 static void	gtk_widget_class_init		(GtkWidgetClass     *klass);
 static void	gtk_widget_base_class_finalize	(GtkWidgetClass     *klass);
@@ -393,6 +425,7 @@ static GQuark		quark_mnemonic_labels = 0;
 static GQuark		quark_tooltip_markup = 0;
 static GQuark		quark_has_tooltip = 0;
 static GQuark		quark_tooltip_window = 0;
+static GQuark		quark_padding_info = 0;
 GParamSpecPool         *_gtk_widget_child_property_pool = NULL;
 GObjectNotifyContext   *_gtk_widget_child_property_notify_context = NULL;
 
@@ -490,6 +523,7 @@ gtk_widget_class_init (GtkWidgetClass *klass)
   quark_tooltip_markup = g_quark_from_static_string ("gtk-tooltip-markup");
   quark_has_tooltip = g_quark_from_static_string ("gtk-has-tooltip");
   quark_tooltip_window = g_quark_from_static_string ("gtk-tooltip-window");
+  quark_padding_info = g_quark_from_static_string ("gtk-padding-info");
 
   style_property_spec_pool = g_param_spec_pool_new (FALSE);
   _gtk_widget_child_property_pool = g_param_spec_pool_new (TRUE);
@@ -800,6 +834,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
+   *
+   * Some widgets may not support this property. Others may not distinguish #GTK_ALIGN_FILL
+   * from #GTK_ALIGN_CENTER because they have no logical way to fill up extra space.
+   *
+   * 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
+   *
+   * Some widgets may not support this property. Others may not distinguish #GTK_ALIGN_FILL
+   * from #GTK_ALIGN_CENTER because they have no logical way to fill up extra space.
+   *
+   * 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.
+   *
+   * Some widgets may not support this property.
+   *
+   * 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.
+   *
+   * Some widgets may not support this property.
+   *
+   * 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.
+   *
+   * Some widgets may not support this property.
+   *
+   * 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.
+   *
+   * Some widgets may not support this property.
+   *
+   * 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.
+   *
+   * Some widgets may not support this property.
+   *
+   * 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.
    */
@@ -2734,6 +2902,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;
@@ -2843,6 +3037,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:
+      {
+        GtkWidgetPadding *padding = g_object_get_qdata (widget, quark_padding_info);
+        if (padding == NULL)
+          {
+            g_value_set_int (value, 0);
+          }
+        else
+          {
+            g_value_set_int (value, MAX (MAX (padding->padding.left,
+                                              padding->padding.right),
+                                         MAX (padding->padding.top,
+                                              padding->padding.bottom)));
+          }
+      }
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -10867,7 +11095,469 @@ gtk_widget_size_request_init (GtkSizeRequestIface *iface)
   iface->get_height_for_width = gtk_widget_real_get_height_for_width;  
 }
  
- 
+
+static const GtkWidgetPadding default_padding = {
+  GTK_ALIGN_FILL,
+  GTK_ALIGN_FILL,
+  { 0, 0, 0, 0 }
+};
+
+static const GtkWidgetPadding*
+_gtk_widget_get_padding (GtkWidget *widget)
+{
+  const GtkWidgetPadding *padding;
+
+  padding = g_object_get_qdata (widget, quark_padding_info);
+  if (padding == NULL)
+    {
+      return &default_padding;
+    }
+  else
+    {
+      return padding;
+    }
+}
+
+static void
+free_padding (void *data)
+{
+  g_slice_free (GtkWidgetPadding, data);
+}
+
+static GtkWidgetPadding*
+_gtk_widget_get_padding_writable (GtkWidget *widget)
+{
+  GtkWidgetPadding *padding;
+
+  padding = g_object_get_qdata (widget, quark_padding_info);
+  if (padding == NULL)
+    {
+      padding = g_slice_new (GtkWidgetPadding);
+      *padding = *default_padding;
+
+      g_object_set_qdata_full (widget, quark_padding_info,
+                               padding, free_padding);
+    }
+  return padding;
+}
+
+GtkAlign
+gtk_wigdet_get_h_align (GtkWidget *widget)
+{
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), default_padding.h_align);
+  return _gtk_widget_get_padding (widget)->h_align;
+}
+
+void
+gtk_wigdet_set_h_align (GtkWidget *widget,
+                        GtkAlign   align)
+{
+  GtkWidgetPadding *padding;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  padding = _gtk_widget_get_padding_writable (widget);
+
+  if (padding->h_align == align)
+    return;
+
+  padding->h_align = align;
+  gtk_widget_queue_resize (widget);
+  g_object_notify (G_OBJECT (widget), "h-align");
+}
+
+GtkAlign
+gtk_wigdet_get_v_align (GtkWidget *widget)
+{
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), default_padding.v_align);
+  return _gtk_widget_get_padding (widget)->v_align;
+}
+
+void
+gtk_wigdet_set_v_align (GtkWidget *widget,
+                        GtkAlign   align)
+{
+  GtkWidgetPadding *padding;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  padding = _gtk_widget_get_padding_writable (widget);
+
+  if (padding->v_align == align)
+    return;
+
+  padding->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_padding (widget)->padding.left;
+}
+
+void
+gtk_widget_set_padding_left (GtkWidget *widget,
+                             int        padding)
+{
+  GtkWidgetPadding *padding;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (padding <= G_MAXINT16);
+
+  padding = _gtk_widget_get_padding_writable (widget);
+
+  if (padding->padding.left == padding)
+    return;
+
+  padding->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_padding (widget)->padding.right;
+}
+
+void
+gtk_widget_set_padding_right (GtkWidget *widget,
+                              int        padding)
+{
+  GtkWidgetPadding *padding;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (padding <= G_MAXINT16);
+
+  padding = _gtk_widget_get_padding_writable (widget);
+
+  if (padding->padding.right == padding)
+    return;
+
+  padding->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_padding (widget)->padding.top;
+}
+
+void
+gtk_widget_set_padding_top (GtkWidget *widget,
+                            int        padding)
+{
+  GtkWidgetPadding *padding;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (padding <= G_MAXINT16);
+
+  padding = _gtk_widget_get_padding_writable (widget);
+
+  if (padding->padding.top == padding)
+    return;
+
+  padding->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_padding (widget)->padding.bottom;
+}
+
+void
+gtk_widget_set_padding_bottom (GtkWidget *widget,
+                               int        padding)
+{
+  GtkWidgetPadding *padding;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (padding <= G_MAXINT16);
+
+  padding = _gtk_widget_get_padding_writable (widget);
+
+  if (padding->padding.bottom == padding)
+    return;
+
+  padding->padding.bottom = padding;
+  gtk_widget_queue_resize (widget);
+  g_object_notify (G_OBJECT (widget), "padding-bottom");
+}
+
+/* This function MUST NOT use request/allocation state of the widget
+ * since it may be called in order to compute request/allocation state
+ * of the widget.
+ */
+static void
+get_span_inside_padding_horizontal (GtkWidget              *widget,
+                                    const GtkWidgetPadding *padding,
+                                    int                     allocated_outside_width,
+                                    int                     natural_inside_width,
+                                    int                    *x_inside_p,
+                                    int                    *width_inside_p)
+{
+  int inside_allocated;
+  int x, width;
+
+  inside_allocated = allocated_box_width - padding->padding.left - padding->padding.right;
+
+  switch (padding->h_align)
+    {
+    case GTK_ALIGN_FILL:
+      x = padding->padding.left;
+      width = inside_allocated;
+      break;
+    case HIPPO_ALIGNMENT_START:
+      x = padding->padding.left;
+      width = natural_inside_width;
+      break;
+    case HIPPO_ALIGNMENT_END:
+      x = allocated_outside_width - padding->padding.right - natural_inside_width;
+      width = natural_inside_width;
+      break;
+    case HIPPO_ALIGNMENT_CENTER:
+      x = padding->padding.left + (inside_allocated - natural_inside_width) / 2;
+      width = natural_inside_width;
+      break;
+    }
+
+  if (x_inside_p)
+    *x_inside_p = x;
+
+  if (width_inside_p)
+    *width_inside_p = width;
+}
+
+/* This function MUST NOT use request/allocation state of the widget
+ * since it may be called in order to compute request/allocation state
+ * of the widget.
+ */
+static void
+get_span_inside_padding_vertical (GtkWidget              *widget,
+                                  const GtkWidgetPadding *padding,
+                                  int                     allocated_outside_height,
+                                  int                     natural_inside_height,
+                                  int                    *y_inside_p,
+                                  int                    *height_inside_p)
+{
+  int inside_allocated;
+  int y, height;
+
+  inside_allocated = allocated_box_height - padding->padding.top - padding->padding.bottom;
+
+  switch (padding->h_align)
+    {
+    case GTK_ALIGN_FILL:
+      x = padding->padding.top;
+      height = inside_allocated;
+      break;
+    case HIPPO_ALIGNMENT_START:
+      x = padding->padding.top;
+      height = natural_inside_height;
+      break;
+    case HIPPO_ALIGNMENT_END:
+      x = allocated_outside_height - padding->padding.bottom - natural_inside_height;
+      height = natural_inside_height;
+      break;
+    case HIPPO_ALIGNMENT_CENTER:
+      x = padding->padding.top + (inside_allocated - natural_inside_height) / 2;
+      height = natural_inside_height;
+      break;
+    }
+
+  if (x_inside_p)
+    *x_inside_p = x;
+
+  if (height_inside_p)
+    *height_inside_p = height;
+}
+
+/**
+ * gtk_widget_get_area_inside_padding:
+ * @widget: a #GtkWidget
+ * @full_allocation: the whole allocation received in size_allocate
+ * @natural_width: the width your "non-background" content naturally has
+ * @natural_height: the height your "non-background" content naturally has
+ *
+ * Use this function inside your size allocate implementation to
+ * remove standard padding and alignment space provided by the
+ * #GtkWidget base class and give you only the allocation that your
+ * widget should render itself to.
+ *
+ * For purposes of this function, padding includes not only hardcoded
+ * padding (see gtk_widget_set_padding_left() etc.)  but also padding
+ * dynamically added due to alignment (see gtk_widget_set_h_align()).
+ *
+ * If your widget has a background, the background should be
+ * drawn to the entire allocation including the padding area.
+ * However, the "content" part of the widget such as text, images,
+ * controls, etc.  should only be drawn inside the padding area.
+ *
+ * This function will not use request/allocation state of the widget
+ * since it may be called in order to compute request/allocation state
+ * of the widget. For example it would not look at the widget's
+ * current request or allocation.
+ *
+ * If you don't know the width and the height at the same time (for
+ * example, you are doing height-for-width or width-for-height) then
+ * you may need to use gtk_widget_get_horizontal_inside_padding() and
+ * gtk_widget_get_vertical_inside_padding() rather than this function.
+ *
+ * Since: 3.0
+ */
+void
+gtk_widget_get_area_inside_padding (GtkWidget           *widget,
+                                    const GtkAllocation *full_allocation,
+                                    int                  natural_width,
+                                    int                  natural_height,
+                                    GtkAllocation       *content_allocation)
+{
+  const GtkWidgetPadding *padding;
+  int x, y, w, h;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (full_allocation != NULL);
+  g_return_if_fail (content_allocation != NULL);
+
+  padding = _gtk_widget_get_padding (widget);
+
+  get_span_inside_padding_horizontal (widget,
+                                      padding,
+                                      full_allocation->width,
+                                      natural_width,
+                                      &x, &w);
+  get_span_inside_padding_vertical (widget,
+                                    padding,
+                                    full_allocation->height,
+                                    natural_height,
+                                    &y, &h);
+
+  *content_allocation = *full_allocation;
+  content_allocation->x += x;
+  content_allocation->y += y;
+  content_allocation->width = w;
+  content_allocation->height = h;
+}
+
+/* This could be virtualized to allow superclasses to add
+ * even more padding-parameters to subclasses (a la GtkMisc),
+ * Classes could chain up to get superclass padding and then
+ * add more stuff.
+ */
+void
+gtk_widget_get_horizontal_inside_padding (GtkWidget           *widget,
+                                          int                  allocated_x,
+                                          int                  allocated_width,
+                                          int                  natural_width,
+                                          int                 *padded_x_p,
+                                          int                 *padded_width_p)
+{
+  const GtkWidgetPadding *padding;
+  int x, w;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  padding = _gtk_widget_get_padding (widget);
+
+  get_span_inside_padding_horizontal (widget,
+                                      padding,
+                                      allocated_width,
+                                      natural_width,
+                                      &x, &w);
+
+  if (padded_x_p)
+    *padded_x_p = allocated_x + x;
+  if (padded_width_p)
+    *padded_width_p = w;
+}
+
+/* This could be virtualized to allow superclasses to add
+ * even more padding-parameters to subclasses (a la GtkMisc),
+ * Classes could chain up to get superclass padding and then
+ * add more stuff.
+ */
+void
+gtk_widget_get_vertical_inside_padding (GtkWidget           *widget,
+                                        int                  allocated_y,
+                                        int                  allocated_height,
+                                        int                  natural_height,
+                                        int                 *padded_y_p,
+                                        int                 *padded_height_p)
+{
+  const GtkWidgetPadding *padding;
+  int y, h;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  padding = _gtk_widget_get_padding (widget);
+
+  get_span_inside_padding_vertical (widget,
+                                    padding,
+                                    allocated_width,
+                                    natural_width,
+                                    &y, &h);
+
+  if (padded_y_p)
+    *padded_y_p = allocated_y + y;
+  if (padded_height_p)
+    *padded_height_p = h;
+}
+
+/* FIXME docs, use this to implement size request */
+/* This could be virtualized to allow superclasses to add
+ * even more padding-parameters to subclasses (a la GtkMisc),
+ * Classes could chain up to get superclass padding and then
+ * add more stuff.
+ */
+void
+gtk_widget_get_padded_width (GtkWidget           *widget,
+                             int                  base_width,
+                             int                 *padded_width_p)
+{
+  const GtkWidgetPadding *padding;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  padding = _gtk_widget_get_padding (widget);
+
+  *padded_width_p = base_width + padding->padding.left + padding->padding.right;
+}
+
+/* FIXME docs, use this to implement size request */
+/* This could be virtualized to allow superclasses to add
+ * even more padding-parameters to subclasses (a la GtkMisc),
+ * Classes could chain up to get superclass padding and then
+ * add more stuff.
+ */
+void
+gtk_widget_get_padded_height (GtkWidget           *widget,
+                              int                  base_height,
+                              int                 *padded_height_p)
+{
+  const GtkWidgetPadding *padding;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  padding = _gtk_widget_get_padding (widget);
+
+  *padded_height_p = base_height + padding->padding.top + padding->padding.bottom;
+}
+
 /**
  * gtk_widget_get_clipboard:
  * @widget: a #GtkWidget
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index 0bae2c6..086d4bf 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -770,6 +770,53 @@ void             gtk_widget_set_support_multidevice (GtkWidget      *widget,
 /* Accessibility support */
 AtkObject*       gtk_widget_get_accessible               (GtkWidget          *widget);
 
+
+/* Padding and alignment */
+GtkAlign gtk_wigdet_get_h_align                   (GtkWidget           *widget);
+void     gtk_wigdet_set_h_align                   (GtkWidget           *widget,
+                                                   GtkAlign             align);
+GtkAlign gtk_wigdet_get_v_align                   (GtkWidget           *widget);
+void     gtk_wigdet_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);
+
+/* Padding and alignment "protected" API for widget implementations */
+void     gtk_widget_get_area_inside_padding       (GtkWidget           *widget,
+                                                   const GtkAllocation *full_allocation,
+                                                   int                  natural_width,
+                                                   int                  natural_height,
+                                                   GtkAllocation       *content_allocation);
+void     gtk_widget_get_horizontal_inside_padding (GtkWidget           *widget,
+                                                   int                  allocated_x,
+                                                   int                  allocated_width,
+                                                   int                  natural_width,
+                                                   int                 *padded_x_p,
+                                                   int                 *padded_width_p);
+void     gtk_widget_get_vertical_inside_padding   (GtkWidget           *widget,
+                                                   int                  allocated_y,
+                                                   int                  allocated_height,
+                                                   int                  natural_height,
+                                                   int                 *padded_y_p,
+                                                   int                 *padded_height_p);
+void     gtk_widget_get_padded_width              (GtkWidget           *widget,
+                                                   int                  base_width,
+                                                   int                 *padded_width_p);
+void     gtk_widget_get_padded_height             (GtkWidget           *widget,
+                                                   int                  base_height,
+                                                   int                 *padded_height_p);
+
+
 /* 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]