diff --git a/ChangeLog.gtk-extended-layout b/ChangeLog.gtk-extended-layout index c44e07eecf..cd4af71b95 100644 --- a/ChangeLog.gtk-extended-layout +++ b/ChangeLog.gtk-extended-layout @@ -1,3 +1,9 @@ +2007-06-28 Mathias Hasselmann + + * gtk/gtkhbox.c, gtk/gtkhbox.h, gtk/gtk.symbols, + tests/testextendedlayout.c: Initial, buggish implementation + of baseline alignment in GtkHBox. + 2007-06-28 Mathias Hasselmann * gtk/gtkenums.h: Introduce GtkBaselinePolicy. diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols index c72901989f..c143dcb399 100644 --- a/gtk/gtk.symbols +++ b/gtk/gtk.symbols @@ -1671,6 +1671,8 @@ gtk_handle_box_set_snap_edge #if IN_FILE(__GTK_HBOX_C__) gtk_hbox_get_type G_GNUC_CONST gtk_hbox_new +gtk_hbox_get_baseline_policy +gtk_hbox_set_baseline_policy #endif #endif diff --git a/gtk/gtkhbox.c b/gtk/gtkhbox.c index 3140cac67b..ef685131c7 100644 --- a/gtk/gtkhbox.c +++ b/gtk/gtkhbox.c @@ -26,27 +26,66 @@ #include #include "gtkhbox.h" +#include "gtkextendedlayout.h" #include "gtkintl.h" #include "gtkalias.h" +enum { + PROP_0, + PROP_BASELINE_POLICY +}; + +#define GTK_HBOX_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_HBOX, GtkHBoxPrivate)) + +typedef struct _GtkHBoxPrivate GtkHBoxPrivate; + +struct _GtkHBoxPrivate +{ + GtkBaselinePolicy baseline_policy; + gint effective_baseline; + gint *baselines; +}; + +static void gtk_hbox_dispose (GObject *object); +static void gtk_hbox_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_hbox_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + static void gtk_hbox_size_request (GtkWidget *widget, GtkRequisition *requisition); static void gtk_hbox_size_allocate (GtkWidget *widget, GtkAllocation *allocation); - G_DEFINE_TYPE (GtkHBox, gtk_hbox, GTK_TYPE_BOX) static void gtk_hbox_class_init (GtkHBoxClass *class) { - GtkWidgetClass *widget_class; + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); - widget_class = (GtkWidgetClass*) class; + gobject_class->dispose = gtk_hbox_dispose; + gobject_class->set_property = gtk_hbox_set_property; + gobject_class->get_property = gtk_hbox_get_property; widget_class->size_request = gtk_hbox_size_request; widget_class->size_allocate = gtk_hbox_size_allocate; + + g_type_class_add_private (gobject_class, sizeof (GtkHBoxPrivate)); + + g_object_class_install_property (gobject_class, + PROP_BASELINE_POLICY, + g_param_spec_enum ("baseline-policy", + P_("Baseline policy"), + P_("Indicates which baseline of children to use for vertical alignment"), + GTK_TYPE_BASELINE_POLICY, GTK_BASELINE_NONE, + G_PARAM_READWRITE)); } static void @@ -69,21 +108,69 @@ gtk_hbox_new (gboolean homogeneous, } +static void +gtk_hbox_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkHBoxPrivate *priv = GTK_HBOX_GET_PRIVATE (object); + + switch (prop_id) + { + case PROP_BASELINE_POLICY: + priv->baseline_policy = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_hbox_dispose (GObject *object) +{ + GtkHBoxPrivate *priv = GTK_HBOX_GET_PRIVATE (object); + + g_free (priv->baselines); + priv->baselines = NULL; + + G_OBJECT_CLASS (gtk_hbox_parent_class)->dispose (object); +} + +static void +gtk_hbox_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkHBoxPrivate *priv = GTK_HBOX_GET_PRIVATE (object); + + switch (prop_id) + { + case PROP_BASELINE_POLICY: + g_value_set_enum (value, priv->baseline_policy); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + static void gtk_hbox_size_request (GtkWidget *widget, GtkRequisition *requisition) { - GtkBox *box; + GtkBox *box = GTK_BOX (widget); + + gint nvis_children; GtkBoxChild *child; GList *children; - gint nvis_children; - gint width; - box = GTK_BOX (widget); requisition->width = 0; requisition->height = 0; - nvis_children = 0; + nvis_children = 0; children = box->children; while (children) { @@ -91,31 +178,76 @@ gtk_hbox_size_request (GtkWidget *widget, children = children->next; if (GTK_WIDGET_VISIBLE (child->widget)) - { - GtkRequisition child_requisition; - - gtk_widget_size_request (child->widget, &child_requisition); - - if (box->homogeneous) - { - width = child_requisition.width + child->padding * 2; - requisition->width = MAX (requisition->width, width); - } - else - { - requisition->width += child_requisition.width + child->padding * 2; - } - - requisition->height = MAX (requisition->height, child_requisition.height); - - nvis_children += 1; - } + nvis_children += 1; } if (nvis_children > 0) { + GtkHBoxPrivate *priv = GTK_HBOX_GET_PRIVATE (widget); + gint i_child; + + g_free (priv->baselines); + priv->baselines = g_new0 (gint, nvis_children); + + priv->effective_baseline = 0; + if (priv->baseline_policy != GTK_BASELINE_NONE) + { + i_child = 0; + children = box->children; + while (children) + { + child = children->data; + children = children->next; + + if (GTK_WIDGET_VISIBLE (child->widget)) + { + if (GTK_IS_EXTENDED_LAYOUT (child->widget)) + { + priv->baselines[i_child] = gtk_extended_layout_get_single_baseline ( + GTK_EXTENDED_LAYOUT (child->widget), priv->baseline_policy); + + priv->effective_baseline = MAX (priv->effective_baseline, priv->baselines[i_child]); + } + + ++i_child; + } + } + } + + i_child = 0; + children = box->children; + while (children) + { + child = children->data; + children = children->next; + + if (GTK_WIDGET_VISIBLE (child->widget)) + { + GtkRequisition child_requisition; + gint width; + + gtk_widget_size_request (child->widget, &child_requisition); + + if (box->homogeneous) + { + width = child_requisition.width + child->padding * 2; + requisition->width = MAX (requisition->width, width); + } + else + { + requisition->width += child_requisition.width + child->padding * 2; + } + + child_requisition.height += MAX (0, (priv->effective_baseline - priv->baselines[i_child])); + requisition->height = MAX (requisition->height, child_requisition.height); + + ++i_child; + } + } + if (box->homogeneous) requisition->width *= nvis_children; + requisition->width += (nvis_children - 1) * box->spacing; } @@ -128,15 +260,12 @@ gtk_hbox_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { GtkBox *box; - GtkBoxChild *child; - GList *children; - GtkAllocation child_allocation; + gint nvis_children; gint nexpand_children; - gint child_width; - gint width; - gint extra; - gint x; + GtkBoxChild *child; + GList *children; + GtkTextDirection direction; box = GTK_BOX (widget); @@ -163,6 +292,15 @@ gtk_hbox_size_allocate (GtkWidget *widget, if (nvis_children > 0) { + GtkHBoxPrivate *priv = GTK_HBOX_GET_PRIVATE (widget); + GtkAllocation child_allocation; + gint child_width; + gint i_child; + + gint width; + gint extra; + gint x; + if (box->homogeneous) { width = (allocation->width - @@ -304,8 +442,60 @@ gtk_hbox_size_allocate (GtkWidget *widget, x -= (child_width + box->spacing); } } + + i_child = 0; + children = box->children; + while (children) + { + child = children->data; + children = children->next; + + if (GTK_WIDGET_VISIBLE (child->widget)) + { + gint offset; + + offset = MAX (0, (priv->effective_baseline - priv->baselines[i_child])); + + child->widget->allocation.y += offset; + child->widget->allocation.height -= offset; + + ++i_child; + } + } } } +/** + * gtk_hbox_get_baseline_policy: + * @hbox: a #GtkHBox + * + * Returns which baseline of children is used + * to vertically align them. + * + * Return value: the baseline alignment policy of the @hbox. + **/ +GtkBaselinePolicy +gtk_hbox_get_baseline_policy (GtkHBox *hbox) +{ + g_return_val_if_fail (GTK_IS_HBOX (hbox), GTK_BASELINE_NONE); + return GTK_HBOX_GET_PRIVATE (hbox)->baseline_policy; +} + +/** + * gtk_hbox_set_baseline_policy: + * @box: a #GtkBox + * @policy: the baseline alignment policy + * + * Sets the #GtkHBox:baseline-policy property of @vbox, + * which is the policy to vertically align children. + */ +void +gtk_hbox_set_baseline_policy (GtkHBox *hbox, + GtkBaselinePolicy policy) +{ + g_return_if_fail (GTK_IS_HBOX (hbox)); + g_object_set (hbox, "baseline-policy", policy, NULL); +} + #define __GTK_HBOX_C__ #include "gtkaliasdef.c" diff --git a/gtk/gtkhbox.h b/gtk/gtkhbox.h index 5296e21e3c..ed546a00d8 100644 --- a/gtk/gtkhbox.h +++ b/gtk/gtkhbox.h @@ -56,10 +56,13 @@ struct _GtkHBoxClass }; -GType gtk_hbox_get_type (void) G_GNUC_CONST; -GtkWidget* gtk_hbox_new (gboolean homogeneous, - gint spacing); +GType gtk_hbox_get_type (void) G_GNUC_CONST; +GtkWidget* gtk_hbox_new (gboolean homogeneous, + gint spacing); +GtkBaselinePolicy gtk_hbox_get_baseline_policy (GtkHBox *hbox); +void gtk_hbox_set_baseline_policy (GtkHBox *hbox, + GtkBaselinePolicy policy); G_END_DECLS diff --git a/tests/testextendedlayout.c b/tests/testextendedlayout.c index af49276431..8450849ed4 100644 --- a/tests/testextendedlayout.c +++ b/tests/testextendedlayout.c @@ -343,6 +343,13 @@ create_baseline_test (TestSuite *suite) static TestCase* create_baseline_test_bin (TestSuite *suite) { + GtkWidget *bin; + GtkWidget *label; + GtkWidget *table; + GtkWidget *hbox; + + int i, j; + const GType types[] = { GTK_TYPE_ALIGNMENT, GTK_TYPE_BUTTON, @@ -359,16 +366,10 @@ create_baseline_test_bin (TestSuite *suite) NULL }; - GtkWidget *bin; - GtkWidget *label; - GtkWidget *table; - - int i, j; - - TestCase *test = test_case_new (suite, "Baseline Alignment for GtkBin", + TestCase *test = test_case_new (suite, "Baseline Alignment II", gtk_alignment_new (0.5, 0.5, 0.0, 0.0)); - table = gtk_table_new (G_N_ELEMENTS (types) - 1, + table = gtk_table_new (G_N_ELEMENTS (types) + 6, G_N_ELEMENTS (markup), FALSE); @@ -399,6 +400,50 @@ create_baseline_test_bin (TestSuite *suite) } } + gtk_table_attach (GTK_TABLE (table), gtk_hseparator_new (), + 0, G_N_ELEMENTS (markup), + G_N_ELEMENTS (types), G_N_ELEMENTS (types) + 1, + GTK_FILL, GTK_FILL, 0, 0); + + for (i = 0; i < 6; i += 2) + { + hbox = gtk_hbox_new (FALSE, 6); + gtk_hbox_set_baseline_policy (GTK_HBOX (hbox), GTK_BASELINE_FIRST); + + gtk_table_attach (GTK_TABLE (table), hbox, + 0, G_N_ELEMENTS (markup), + G_N_ELEMENTS (types) + i + 1, + G_N_ELEMENTS (types) + i + 2, + GTK_FILL, GTK_FILL, 0, 0); + gtk_table_attach (GTK_TABLE (table), gtk_hseparator_new (), + 0, G_N_ELEMENTS (markup), + G_N_ELEMENTS (types) + i + 2, + G_N_ELEMENTS (types) + i + 3, + GTK_FILL, GTK_FILL, 0, 0); + + for (j = 0; markup[j]; ++j) + { + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label), markup[j]); + + test_case_append_guide (test, label, GUIDE_BASELINE, G_N_ELEMENTS (types)); + + if (0 == j && i >= 2) + { + bin = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); + gtk_alignment_set_padding (GTK_ALIGNMENT (bin), 0, 0, 0, 0); +// i < 3 ? 20 : 0, i > 3 ? 20 : 0, 0, 0); + gtk_container_add (GTK_CONTAINER (bin), label); + + gtk_box_pack_start (GTK_BOX (hbox), bin, FALSE, TRUE, 0); + } + else + { + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0); + } + } + } + return test; }