diff --git a/gtk/Makefile.am b/gtk/Makefile.am index f66e20b4c5..df660307e8 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -576,7 +576,8 @@ gtk_private_h_sources = \ gtkwindowprivate.h \ gtktreemenu.h \ gdkpixbufutilsprivate.h \ - gtkgizmoprivate.h + gtkgizmoprivate.h \ + gtkcenterboxprivate.h # GTK+ C sources to build the library from gtk_base_c_sources = \ @@ -939,7 +940,8 @@ gtk_base_c_sources = \ gtkwin32draw.c \ gtkwin32theme.c \ gdkpixbufutils.c \ - gtkgizmo.c + gtkgizmo.c \ + gtkcenterbox.c if USE_QUARTZ gtk_base_c_sources += \ diff --git a/gtk/gtkactionbar.c b/gtk/gtkactionbar.c index bc06fbd623..85506eaefd 100644 --- a/gtk/gtkactionbar.c +++ b/gtk/gtkactionbar.c @@ -30,6 +30,7 @@ #include "gtkwidgetprivate.h" #include "gtkcontainerprivate.h" #include "gtkprivate.h" +#include "gtkcenterboxprivate.h" #include @@ -55,8 +56,11 @@ struct _GtkActionBarPrivate { - GtkWidget *box; + GtkWidget *center_box; + GtkWidget *start_box; + GtkWidget *end_box; GtkWidget *revealer; + GtkCssGadget *gadget; }; @@ -85,7 +89,8 @@ gtk_action_bar_add (GtkContainer *container, { GtkActionBarPrivate *priv = gtk_action_bar_get_instance_private (GTK_ACTION_BAR (container)); - gtk_container_add (GTK_CONTAINER (priv->box), child); + /* Default for pack-type is start */ + gtk_container_add (GTK_CONTAINER (priv->start_box), child); } static void @@ -94,7 +99,15 @@ gtk_action_bar_remove (GtkContainer *container, { GtkActionBarPrivate *priv = gtk_action_bar_get_instance_private (GTK_ACTION_BAR (container)); - gtk_container_remove (GTK_CONTAINER (priv->box), child); + if (gtk_widget_get_parent (child) == priv->start_box) + gtk_container_remove (GTK_CONTAINER (priv->start_box), child); + else if (gtk_widget_get_parent (child) == priv->end_box) + gtk_container_remove (GTK_CONTAINER (priv->end_box), child); + else if (child == gtk_center_box_get_center_widget (GTK_CENTER_BOX (priv->center_box))) + gtk_center_box_set_center_widget (GTK_CENTER_BOX (priv->center_box), NULL); + else + g_warning ("Can't remove non-child %s %p from GtkActionBar %p", + G_OBJECT_TYPE_NAME (child), child, container); } static void @@ -106,9 +119,20 @@ gtk_action_bar_forall (GtkContainer *container, GtkActionBarPrivate *priv = gtk_action_bar_get_instance_private (GTK_ACTION_BAR (container)); if (include_internals) - (* callback) (priv->revealer, callback_data); - else if (priv->box) - gtk_container_forall (GTK_CONTAINER (priv->box), callback, callback_data); + { + (*callback) (priv->revealer, callback_data); + } + else + { + if (priv->start_box != NULL) + gtk_container_forall (GTK_CONTAINER (priv->start_box), callback, callback_data); + + if (gtk_center_box_get_center_widget (GTK_CENTER_BOX (priv->center_box)) != NULL) + (*callback) (gtk_center_box_get_center_widget (GTK_CENTER_BOX (priv->center_box)), callback_data); + + if (priv->end_box != NULL) + gtk_container_forall (GTK_CONTAINER (priv->end_box), callback, callback_data); + } } static void @@ -116,9 +140,10 @@ gtk_action_bar_finalize (GObject *object) { GtkActionBarPrivate *priv = gtk_action_bar_get_instance_private (GTK_ACTION_BAR (object)); - g_clear_object (&priv->gadget); gtk_widget_unparent (priv->revealer); + g_clear_object (&priv->gadget); + G_OBJECT_CLASS (gtk_action_bar_parent_class)->finalize (object); } @@ -137,13 +162,47 @@ gtk_action_bar_get_child_property (GtkContainer *container, { GtkActionBarPrivate *priv = gtk_action_bar_get_instance_private (GTK_ACTION_BAR (container)); - if (child == priv->revealer) - g_param_value_set_default (pspec, value); - else - gtk_container_child_get_property (GTK_CONTAINER (priv->box), - child, - pspec->name, - value); + switch (property_id) + { + case CHILD_PROP_PACK_TYPE: + if (gtk_widget_get_parent (child) == priv->start_box) + g_value_set_enum (value, GTK_PACK_START); + else if (gtk_widget_get_parent (child) == priv->end_box) + g_value_set_enum (value, GTK_PACK_END); + else /* Center widget */ + g_value_set_enum (value, GTK_PACK_START); + + break; + + case CHILD_PROP_POSITION: + if (gtk_widget_get_parent (child) == priv->start_box) + { + int n; + gtk_container_child_get (GTK_CONTAINER (priv->start_box), + child, + "position", &n, + NULL); + g_value_set_int (value, n); + } + else if (gtk_widget_get_parent (child) == priv->end_box) + { + int n; + gtk_container_child_get (GTK_CONTAINER (priv->end_box), + child, + "position", &n, + NULL); + g_value_set_int (value, n); + } + else /* Center widget */ + { + g_value_set_int (value, 0); + } + break; + + default: + GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec); + break; + } } static void @@ -155,11 +214,61 @@ gtk_action_bar_set_child_property (GtkContainer *container, { GtkActionBarPrivate *priv = gtk_action_bar_get_instance_private (GTK_ACTION_BAR (container)); - if (child != priv->revealer) - gtk_container_child_set_property (GTK_CONTAINER (priv->box), - child, - pspec->name, - value); + switch (property_id) + { + case CHILD_PROP_PACK_TYPE: + if (gtk_widget_get_parent (child) == priv->start_box) + { + if (g_value_get_enum (value) == GTK_PACK_END) + { + g_object_ref (child); + gtk_container_remove (GTK_CONTAINER (priv->start_box), child); + gtk_box_pack_end (GTK_BOX (priv->end_box), child); + g_object_unref (child); + } + } + else if (gtk_widget_get_parent (child) == priv->end_box) + { + if (g_value_get_enum (value) == GTK_PACK_START) + { + g_object_ref (child); + gtk_container_remove (GTK_CONTAINER (priv->end_box), child); + gtk_container_add (GTK_CONTAINER (priv->start_box), child); + g_object_unref (child); + } + } + else + { + /* Ignore the center widget */ + } + + break; + + case CHILD_PROP_POSITION: + if (gtk_widget_get_parent (child) == priv->start_box) + { + gtk_container_child_set (GTK_CONTAINER (priv->start_box), + child, + "position", g_value_get_int (value), + NULL); + } + else if (gtk_widget_get_parent (child) == priv->end_box) + { + gtk_container_child_set (GTK_CONTAINER (priv->end_box), + child, + "position", g_value_get_int (value), + NULL); + } + else + { + /* Ignore center widget */ + } + break; + + default: + GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec); + break; + } } static gboolean @@ -171,7 +280,10 @@ gtk_action_bar_render (GtkCssGadget *gadget, int height, gpointer data) { - GTK_WIDGET_CLASS (gtk_action_bar_parent_class)->snapshot (gtk_css_gadget_get_owner (gadget), snapshot); + GtkActionBar *self = GTK_ACTION_BAR (gtk_css_gadget_get_owner (gadget)); + GtkActionBarPrivate *priv = gtk_action_bar_get_instance_private (GTK_ACTION_BAR (self)); + + gtk_widget_snapshot_child (GTK_WIDGET (self), priv->revealer, snapshot); return FALSE; } @@ -196,8 +308,7 @@ gtk_action_bar_allocate (GtkCssGadget *gadget, GtkActionBarPrivate *priv = gtk_action_bar_get_instance_private (GTK_ACTION_BAR (widget)); gtk_widget_size_allocate (priv->revealer, (GtkAllocation *)allocation); - - gtk_container_get_children_clip (GTK_CONTAINER (widget), out_clip); + gtk_widget_get_clip (priv->revealer, out_clip); } static void @@ -290,6 +401,22 @@ gtk_action_bar_get_property (GObject *object, } } +static void +gtk_action_bar_destroy (GtkWidget *widget) +{ + GtkActionBar *self = GTK_ACTION_BAR (widget); + GtkActionBarPrivate *priv = gtk_action_bar_get_instance_private (self); + + gtk_center_box_set_start_widget (GTK_CENTER_BOX (priv->center_box), NULL); + gtk_center_box_set_center_widget (GTK_CENTER_BOX (priv->center_box), NULL); + gtk_center_box_set_end_widget (GTK_CENTER_BOX (priv->center_box), NULL); + + priv->start_box = NULL; + priv->end_box = NULL; + + GTK_WIDGET_CLASS (gtk_action_bar_parent_class)->destroy (widget); +} + static void gtk_action_bar_class_init (GtkActionBarClass *klass) { @@ -308,6 +435,7 @@ gtk_action_bar_class_init (GtkActionBarClass *klass) widget_class->snapshot = gtk_action_bar_snapshot; widget_class->size_allocate = gtk_action_bar_size_allocate; widget_class->measure = gtk_action_bar_measure_; + widget_class->destroy = gtk_action_bar_destroy; container_class->add = gtk_action_bar_add; container_class->remove = gtk_action_bar_remove; @@ -354,12 +482,18 @@ gtk_action_bar_init (GtkActionBar *action_bar) priv->revealer = gtk_revealer_new (); gtk_widget_set_parent (priv->revealer, widget); - priv->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - gtk_container_add (GTK_CONTAINER (priv->revealer), priv->box); - gtk_revealer_set_reveal_child (GTK_REVEALER (priv->revealer), TRUE); gtk_revealer_set_transition_type (GTK_REVEALER (priv->revealer), GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP); + priv->start_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + priv->end_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + + priv->center_box = gtk_center_box_new (); + gtk_center_box_set_start_widget (GTK_CENTER_BOX (priv->center_box), priv->start_box); + gtk_center_box_set_end_widget (GTK_CENTER_BOX (priv->center_box), priv->end_box); + + gtk_container_add (GTK_CONTAINER (priv->revealer), priv->center_box); + widget_node = gtk_widget_get_css_node (GTK_WIDGET (action_bar)); priv->gadget = gtk_css_custom_gadget_new_for_node (widget_node, GTK_WIDGET (action_bar), @@ -377,10 +511,9 @@ gtk_action_bar_buildable_add_child (GtkBuildable *buildable, const gchar *type) { GtkActionBar *action_bar = GTK_ACTION_BAR (buildable); - GtkActionBarPrivate *priv = gtk_action_bar_get_instance_private (action_bar); if (type && strcmp (type, "center") == 0) - gtk_box_set_center_widget (GTK_BOX (priv->box), GTK_WIDGET (child)); + gtk_action_bar_set_center_widget (action_bar, GTK_WIDGET (child)); else if (!type) gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child)); else @@ -412,7 +545,7 @@ gtk_action_bar_pack_start (GtkActionBar *action_bar, { GtkActionBarPrivate *priv = gtk_action_bar_get_instance_private (action_bar); - gtk_box_pack_start (GTK_BOX (priv->box), child); + gtk_container_add (GTK_CONTAINER (priv->start_box), child); } /** @@ -431,7 +564,7 @@ gtk_action_bar_pack_end (GtkActionBar *action_bar, { GtkActionBarPrivate *priv = gtk_action_bar_get_instance_private (action_bar); - gtk_box_pack_end (GTK_BOX (priv->box), child); + gtk_box_pack_end (GTK_BOX (priv->end_box), child); } /** @@ -449,7 +582,7 @@ gtk_action_bar_set_center_widget (GtkActionBar *action_bar, { GtkActionBarPrivate *priv = gtk_action_bar_get_instance_private (action_bar); - gtk_box_set_center_widget (GTK_BOX (priv->box), center_widget); + gtk_center_box_set_center_widget (GTK_CENTER_BOX (priv->center_box), center_widget); } /** @@ -469,7 +602,7 @@ gtk_action_bar_get_center_widget (GtkActionBar *action_bar) g_return_val_if_fail (GTK_IS_ACTION_BAR (action_bar), NULL); - return gtk_box_get_center_widget (GTK_BOX (priv->box)); + return gtk_center_box_get_center_widget (GTK_CENTER_BOX (priv->center_box)); } /** diff --git a/gtk/gtkcenterbox.c b/gtk/gtkcenterbox.c new file mode 100644 index 0000000000..316126c2ac --- /dev/null +++ b/gtk/gtkcenterbox.c @@ -0,0 +1,226 @@ + +#include "gtkcenterboxprivate.h" + +G_DEFINE_TYPE (GtkCenterBox, gtk_center_box, GTK_TYPE_WIDGET); + + +static void +gtk_center_box_measure (GtkWidget *widget, + GtkOrientation orientation, + int for_size, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline) +{ + GtkCenterBox *self = GTK_CENTER_BOX (widget); + int min, nat, min_baseline, nat_baseline; + + gtk_widget_measure (self->start_widget, + orientation, + for_size, + minimum, natural, + minimum_baseline, natural_baseline); + + if (self->center_widget) + { + gtk_widget_measure (self->center_widget, + orientation, + for_size, + &min, &nat, + &min_baseline, &nat_baseline); + + /* XXX How are baselines even handled? */ + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + *minimum = *minimum + min; + *natural = *natural + nat; + } + else /* GTK_ORIENTATION_VERTICAL */ + { + *minimum = MAX (*minimum, min); + *natural = MAX (*minimum, nat); + } + } + + gtk_widget_measure (self->end_widget, + orientation, + for_size, + &min, &nat, + &min_baseline, &nat_baseline); + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + *minimum = *minimum + min; + *natural = *natural + nat; + } + else /* GTK_ORIENTATION_VERTICAL */ + { + *minimum = MAX (*minimum, min); + *natural = MAX (*minimum, nat); + } + +} + +static void +gtk_center_box_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkCenterBox *self = GTK_CENTER_BOX (widget); + GtkAllocation child_allocation; + GtkAllocation clip = *allocation; + GtkAllocation child_clip; + int start_size, end_size; + int min, nat; + + GTK_WIDGET_CLASS (gtk_center_box_parent_class)->size_allocate (widget, allocation); + + + // TODO: Allocate natural sizes if possible? + + /* Start Box */ + gtk_widget_measure (self->start_widget, GTK_ORIENTATION_HORIZONTAL, + allocation->height, + &min, &nat, NULL, NULL); + child_allocation.x = allocation->x; + child_allocation.y = allocation->y; + child_allocation.width = min; + child_allocation.height = allocation->height; + + gtk_widget_size_allocate (self->start_widget, &child_allocation); + gtk_widget_get_clip (self->start_widget, &child_clip); + gdk_rectangle_union (&clip, &clip, &child_clip); + start_size = child_allocation.width; + + + /* End Box */ + gtk_widget_measure (self->end_widget, GTK_ORIENTATION_HORIZONTAL, + allocation->height, + &min, &nat, NULL, NULL); + child_allocation.x = allocation->x + allocation->width - min; + child_allocation.width = min; + + gtk_widget_size_allocate (self->end_widget, &child_allocation); + gtk_widget_get_clip (self->end_widget, &child_clip); + gdk_rectangle_union (&clip, &clip, &child_clip); + end_size = child_allocation.width; + + /* Center Widget */ + if (self->center_widget) + { + gtk_widget_measure (self->center_widget, GTK_ORIENTATION_HORIZONTAL, + allocation->height, + &min, &nat, NULL, NULL); + + child_allocation.x = (allocation->width / 2) - (min / 2); + + /* Push in from start/end */ + if (start_size > child_allocation.x) + child_allocation.x = start_size; + else if (allocation->width - end_size < child_allocation.x + min) + child_allocation.x = allocation->width - min - end_size; + + child_allocation.x += allocation->x; + child_allocation.width = min; + gtk_widget_size_allocate (self->center_widget, &child_allocation); + gtk_widget_get_clip (self->center_widget, &child_clip); + gdk_rectangle_union (&clip, &clip, &child_clip); + } + + gtk_widget_set_clip (widget, &clip); +} + +static void +gtk_center_box_snapshot (GtkWidget *widget, + GtkSnapshot *snapshot) +{ + GtkCenterBox *self = GTK_CENTER_BOX (widget); + + gtk_widget_snapshot_child (widget, self->start_widget, snapshot); + + if (self->center_widget) + gtk_widget_snapshot_child (widget, self->center_widget, snapshot); + + gtk_widget_snapshot_child (widget, self->end_widget, snapshot); +} + +static void +gtk_center_box_class_init (GtkCenterBoxClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + widget_class->measure = gtk_center_box_measure; + widget_class->size_allocate = gtk_center_box_size_allocate; + widget_class->snapshot = gtk_center_box_snapshot; +} + +static void +gtk_center_box_init (GtkCenterBox *self) +{ + gtk_widget_set_has_window (GTK_WIDGET (self), FALSE); + + self->start_widget = NULL; + self->center_widget = NULL; + self->end_widget = NULL; +} + +GtkWidget * +gtk_center_box_new (void) +{ + return GTK_WIDGET (g_object_new (GTK_TYPE_CENTER_BOX, NULL)); +} + +void +gtk_center_box_set_start_widget (GtkCenterBox *self, + GtkWidget *child) +{ + if (self->start_widget) + gtk_widget_unparent (self->start_widget); + + self->start_widget = child; + if (child) + gtk_widget_set_parent (child, GTK_WIDGET (self)); +} + +void +gtk_center_box_set_center_widget (GtkCenterBox *self, + GtkWidget *child) +{ + if (self->center_widget) + gtk_widget_unparent (self->center_widget); + + self->center_widget = child; + if (child) + gtk_widget_set_parent (child, GTK_WIDGET (self)); +} + +void +gtk_center_box_set_end_widget (GtkCenterBox *self, + GtkWidget *child) +{ + if (self->end_widget) + gtk_widget_unparent (self->end_widget); + + self->end_widget = child; + if (child) + gtk_widget_set_parent (child, GTK_WIDGET (self)); +} + +GtkWidget * +gtk_center_box_get_start_widget (GtkCenterBox *self) +{ + return self->start_widget; +} + +GtkWidget * +gtk_center_box_get_center_widget (GtkCenterBox *self) +{ + return self->center_widget; +} + +GtkWidget * +gtk_center_box_get_end_widget (GtkCenterBox *self) +{ + return self->end_widget; +} diff --git a/gtk/gtkcenterboxprivate.h b/gtk/gtkcenterboxprivate.h new file mode 100644 index 0000000000..445d462b83 --- /dev/null +++ b/gtk/gtkcenterboxprivate.h @@ -0,0 +1,43 @@ + +#ifndef __GTK_CENTER_BOX_H__ +#define __GTK_CENTER_BOX_H__ + +#include "gtkwidget.h" + +#define GTK_TYPE_CENTER_BOX (gtk_center_box_get_type ()) +#define GTK_CENTER_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CENTER_BOX, GtkCenterBox)) +#define GTK_CENTER_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CENTER_BOX, GtkCenterBoxClass)) +#define GTK_IS_CENTER_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CENTER_BOX)) +#define GTK_IS_CENTER_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CENTER_BOX)) +#define GTK_CENTER_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CENTER_BOX, GtkCenterBoxClass)) + +typedef struct _GtkCenterBox GtkCenterBox; +typedef struct _GtkCenterBoxClass GtkCenterBoxClass; + +struct _GtkCenterBox +{ + GtkWidget parent_instance; + + GtkWidget *start_widget; + GtkWidget *center_widget; + GtkWidget *end_widget; +}; + +struct _GtkCenterBoxClass +{ + GtkWidgetClass parent_class; +}; + +GType gtk_center_box_get_type (void) G_GNUC_CONST; + +GtkWidget *gtk_center_box_new (void); +void gtk_center_box_set_start_widget (GtkCenterBox *self, GtkWidget *child); +void gtk_center_box_set_center_widget (GtkCenterBox *self, GtkWidget *child); +void gtk_center_box_set_end_widget (GtkCenterBox *self, GtkWidget *child); + +GtkWidget * gtk_center_box_get_start_widget (GtkCenterBox *self); +GtkWidget * gtk_center_box_get_center_widget (GtkCenterBox *self); +GtkWidget * gtk_center_box_get_end_widget (GtkCenterBox *self); + + +#endif diff --git a/tests/testactionbar.c b/tests/testactionbar.c index 00ff9c58ce..bd34bef129 100644 --- a/tests/testactionbar.c +++ b/tests/testactionbar.c @@ -46,6 +46,10 @@ create_widgets (GtkActionBar *bar, GtkPackType type; child = l->data; + + if (child == gtk_action_bar_get_center_widget (bar)) + continue; + gtk_container_child_get (GTK_CONTAINER (bar), child, "pack-type", &type, NULL); if (type == pack_type) gtk_container_remove (GTK_CONTAINER (bar), child);