diff options
author | Matthias Clasen <mclasen@redhat.com> | 2013-03-10 15:15:45 -0400 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2013-03-17 12:12:53 -0400 |
commit | 3f9455301e7637b2bc1cca33897c38daf1a71e5b (patch) | |
tree | 328b0946960e6d28ac0ddc2f8d82d58814dcb93b | |
parent | 55a98da4d44117a34cc733c27120e114f7bd7260 (diff) | |
download | gtk+-3f9455301e7637b2bc1cca33897c38daf1a71e5b.tar.gz |
Add GtkHeaderBar
This widget has been developed as GdHeaderBar in libgd.
The copy here has been renamed and changed to use GTK+
internals.
-rw-r--r-- | docs/reference/gtk/gtk-docs.sgml | 1 | ||||
-rw-r--r-- | docs/reference/gtk/gtk3-sections.txt | 24 | ||||
-rw-r--r-- | gtk/Makefile.am | 2 | ||||
-rw-r--r-- | gtk/gtk.h | 1 | ||||
-rw-r--r-- | gtk/gtk.symbols | 7 | ||||
-rw-r--r-- | gtk/gtkheaderbar.c | 1202 | ||||
-rw-r--r-- | gtk/gtkheaderbar.h | 84 |
7 files changed, 1321 insertions, 0 deletions
diff --git a/docs/reference/gtk/gtk-docs.sgml b/docs/reference/gtk/gtk-docs.sgml index ebebd1c008..df6af83c7b 100644 --- a/docs/reference/gtk/gtk-docs.sgml +++ b/docs/reference/gtk/gtk-docs.sgml @@ -235,6 +235,7 @@ <xi:include href="xml/gtknotebook.xml" /> <xi:include href="xml/gtkexpander.xml" /> <xi:include href="xml/gtkoverlay.xml" /> + <xi:include href="xml/gtkheaderbar.xml" /> <xi:include href="xml/gtkorientable.xml" /> </chapter> diff --git a/docs/reference/gtk/gtk3-sections.txt b/docs/reference/gtk/gtk3-sections.txt index 5db1c72c3b..26a6ce6034 100644 --- a/docs/reference/gtk/gtk3-sections.txt +++ b/docs/reference/gtk/gtk3-sections.txt @@ -7494,3 +7494,27 @@ GTK_COLOR_CHOOSER_DIALOG_GET_CLASS GtkColorChooserDialogPrivate gtk_color_chooser_dialog_get_type </SECTION> + +<SECTION> +<FILE>gtkheaderbar</FILE> +<TITLE>GtkHeaderBar</TITLE> +GtkHeaderBar +gtk_header_bar_new +gtk_header_bar_set_title +gtk_header_bar_get_title +gtk_header_bar_set_custom_title +gtk_header_bar_get_custom_title +gtk_header_bar_pack_start +gtk_header_bar_pack_end + +<SUBSECTION Standard> +GTK_TYPE_HEADER_BAR +GTK_HEADER_BAR +GTK_HEADER_BAR_CLASS +GTK_IS_HEADER_BAR +GTK_IS_HEADER_BAR_CLASS +GTK_HEADER_BAR_GET_CLASS + +<SUBSECTION Private> +gtk_header_bar_get_type +</SECTION> diff --git a/gtk/Makefile.am b/gtk/Makefile.am index e164c90c95..4efdd3ff8a 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -257,6 +257,7 @@ gtk_public_h_sources = \ gtkfontchooserwidget.h \ gtkframe.h \ gtkgrid.h \ + gtkheaderbar.h \ gtkiconfactory.h \ gtkicontheme.h \ gtkiconview.h \ @@ -731,6 +732,7 @@ gtk_base_c_sources = \ gtkfontchooserwidget.c \ gtkframe.c \ gtkgrid.c \ + gtkheaderbar.c \ gtkhsla.c \ gtkiconcache.c \ gtkiconcachevalidator.c \ @@ -108,6 +108,7 @@ #include <gtk/gtkfontchooserwidget.h> #include <gtk/gtkframe.h> #include <gtk/gtkgrid.h> +#include <gtk/gtkheaderbar.h> #include <gtk/gtkiconfactory.h> #include <gtk/gtkicontheme.h> #include <gtk/gtkiconview.h> diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols index 030a06c5bf..4e2a326b7c 100644 --- a/gtk/gtk.symbols +++ b/gtk/gtk.symbols @@ -1222,6 +1222,13 @@ gtk_hbox_get_type gtk_hbox_new gtk_hbutton_box_get_type gtk_hbutton_box_new +gtk_header_bar_new +gtk_header_bar_pack_start +gtk_header_bar_pack_end +gtk_header_bar_set_title +gtk_header_bar_get_title +gtk_header_bar_set_custom_title +gtk_header_bar_get_custom_title gtk_hpaned_get_type gtk_hpaned_new gtk_hscale_get_type diff --git a/gtk/gtkheaderbar.c b/gtk/gtkheaderbar.c new file mode 100644 index 0000000000..3058c9e6f6 --- /dev/null +++ b/gtk/gtkheaderbar.c @@ -0,0 +1,1202 @@ +/* + * Copyright (c) 2013 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" + +#include "gtkheaderbar.h" +#include "gtkintl.h" +#include "gtkprivate.h" +#include "gtktypebuiltins.h" +#include "gtkwidgetprivate.h" +#include "gtkcontainerprivate.h" +#include "a11y/gtkcontaineraccessible.h" + +#include <string.h> + +/** + * SECTION:gtkheaderbar + * @Short_description: A box with a centered child + * @Title: GtkHeaderBar + * @See_also: #GtkBox + * + * GtkHeaderBar is similar to a horizontal #GtkBox, it allows + * to place children at the start or the end. In addition, + * it allows a title to be displayed. The title will be + * centered wrt to the widget of the box, even if the children + * at either side take up different amounts of space. + */ + +#define DEFAULT_SPACING 6 +#define DEFAULT_HPADDING 6 +#define DEFAULT_VPADDING 6 + +struct _GtkHeaderBarPrivate +{ + gchar *title; + GtkWidget *label; + GtkWidget *custom_title; + gint spacing; + gint hpadding; + gint vpadding; + + GList *children; +}; + +typedef struct _Child Child; +struct _Child +{ + GtkWidget *widget; + GtkPackType pack_type; +}; + +enum { + PROP_0, + PROP_TITLE, + PROP_CUSTOM_TITLE, + PROP_SPACING, + PROP_HPADDING, + PROP_VPADDING +}; + +enum { + CHILD_PROP_0, + CHILD_PROP_PACK_TYPE, + CHILD_PROP_POSITION +}; + +static void gtk_header_buildable_init (GtkBuildableIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GtkHeaderBar, gtk_header_bar, GTK_TYPE_CONTAINER, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_header_buildable_init)); + +static void +boldify_label (GtkWidget *label) +{ + PangoAttrList *attrs; + attrs = pango_attr_list_new (); + pango_attr_list_insert (attrs, pango_attr_weight_new (PANGO_WEIGHT_BOLD)); + gtk_label_set_attributes (GTK_LABEL (label), attrs); + pango_attr_list_unref (attrs); +} + +static void +get_css_padding_and_border (GtkWidget *widget, + GtkBorder *border) +{ + GtkStyleContext *context; + GtkStateFlags state; + GtkBorder tmp; + + context = gtk_widget_get_style_context (widget); + state = gtk_widget_get_state_flags (widget); + + gtk_style_context_get_padding (context, state, border); + gtk_style_context_get_border (context, state, &tmp); + border->top += tmp.top; + border->right += tmp.right; + border->bottom += tmp.bottom; + border->left += tmp.left; +} + +static void +gtk_header_bar_init (GtkHeaderBar *bar) +{ + GtkStyleContext *context; + GtkHeaderBarPrivate *priv; + + priv = G_TYPE_INSTANCE_GET_PRIVATE (bar, GTK_TYPE_HEADER_BAR, GtkHeaderBarPrivate); + bar->priv = priv; + + gtk_widget_set_has_window (GTK_WIDGET (bar), FALSE); + gtk_widget_set_redraw_on_allocate (GTK_WIDGET (bar), FALSE); + + priv->label = gtk_label_new (""); + boldify_label (priv->label); + gtk_widget_set_parent (priv->label, GTK_WIDGET (bar)); + gtk_widget_set_valign (priv->label, GTK_ALIGN_CENTER); + gtk_label_set_line_wrap (GTK_LABEL (priv->label), FALSE); + gtk_label_set_single_line_mode (GTK_LABEL (priv->label), TRUE); + gtk_label_set_ellipsize (GTK_LABEL (priv->label), PANGO_ELLIPSIZE_END); + gtk_widget_show (priv->label); + + priv->title = NULL; + priv->custom_title = NULL; + priv->children = NULL; + priv->spacing = DEFAULT_SPACING; + priv->hpadding = DEFAULT_HPADDING; + priv->vpadding = DEFAULT_VPADDING; + + context = gtk_widget_get_style_context (GTK_WIDGET (bar)); + gtk_style_context_add_class (context, "header-bar"); + gtk_style_context_add_class (context, GTK_STYLE_CLASS_HORIZONTAL); +} + +static gint +count_visible_children (GtkHeaderBar *bar) +{ + GList *l; + Child *child; + gint n; + + n = 0; + for (l = bar->priv->children; l; l = l->next) + { + child = l->data; + if (gtk_widget_get_visible (child->widget)) + n++; + } + + return n; +} + +static gboolean +add_child_size (GtkWidget *child, + GtkOrientation orientation, + gint *minimum, + gint *natural) +{ + gint child_minimum, child_natural; + + if (!gtk_widget_get_visible (child)) + return FALSE; + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + gtk_widget_get_preferred_width (child, &child_minimum, &child_natural); + else + gtk_widget_get_preferred_height (child, &child_minimum, &child_natural); + + if (GTK_ORIENTATION_HORIZONTAL == orientation) + { + *minimum += child_minimum; + *natural += child_natural; + } + else + { + *minimum = MAX (*minimum, child_minimum); + *natural = MAX (*natural, child_natural); + } + + return TRUE; +} + +static void +gtk_header_bar_get_size (GtkWidget *widget, + GtkOrientation orientation, + gint *minimum_size, + gint *natural_size) +{ + GtkHeaderBar *bar = GTK_HEADER_BAR (widget); + GtkHeaderBarPrivate *priv = bar->priv; + GList *l; + gint nvis_children; + gint minimum, natural; + GtkBorder css_borders; + + minimum = natural = 0; + nvis_children = 0; + + for (l = priv->children; l; l = l->next) + { + Child *child = l->data; + + if (add_child_size (child->widget, orientation, &minimum, &natural)) + nvis_children += 1; + } + + if (add_child_size (priv->label, orientation, &minimum, &natural)) + nvis_children += 1; + + if (priv->custom_title) + { + if (add_child_size (priv->custom_title, orientation, &minimum, &natural)) + nvis_children += 1; + } + + if (nvis_children > 0 && orientation == GTK_ORIENTATION_HORIZONTAL) + { + minimum += nvis_children * priv->spacing; + natural += nvis_children * priv->spacing; + } + + get_css_padding_and_border (widget, &css_borders); + + if (GTK_ORIENTATION_HORIZONTAL == orientation) + { + minimum += 2 * priv->hpadding + css_borders.left + css_borders.right; + natural += 2 * priv->hpadding + css_borders.left + css_borders.right; + } + else + { + minimum += 2 * priv->vpadding + css_borders.top + css_borders.bottom; + natural += 2 * priv->vpadding + css_borders.top + css_borders.bottom; + } + + if (minimum_size) + *minimum_size = minimum; + + if (natural_size) + *natural_size = natural; +} + +static void +gtk_header_bar_compute_size_for_orientation (GtkWidget *widget, + gint avail_size, + gint *minimum_size, + gint *natural_size) +{ + GtkHeaderBar *bar = GTK_HEADER_BAR (widget); + GtkHeaderBarPrivate *priv = bar->priv; + GList *children; + gint required_size = 0; + gint required_natural = 0; + gint child_size; + gint child_natural; + gint nvis_children; + GtkBorder css_borders; + + avail_size -= 2 * priv->vpadding; + + nvis_children = 0; + + for (children = priv->children; children != NULL; children = children->next) + { + Child *child = children->data; + + if (gtk_widget_get_visible (child->widget)) + { + gtk_widget_get_preferred_width_for_height (child->widget, + avail_size, &child_size, &child_natural); + + required_size += child_size; + required_natural += child_natural; + + nvis_children += 1; + } + } + + /* FIXME label size */ + + if (nvis_children > 0) + { + required_size += nvis_children * priv->spacing; + required_natural += nvis_children * priv->spacing; + } + + get_css_padding_and_border (widget, &css_borders); + + required_size += 2 * priv->hpadding + css_borders.left + css_borders.right; + required_natural += 2 * priv->hpadding + css_borders.left + css_borders.right; + + if (minimum_size) + *minimum_size = required_size; + + if (natural_size) + *natural_size = required_natural; +} + +static void +gtk_header_bar_compute_size_for_opposing_orientation (GtkWidget *widget, + gint avail_size, + gint *minimum_size, + gint *natural_size) +{ + GtkHeaderBar *bar = GTK_HEADER_BAR (widget); + GtkHeaderBarPrivate *priv = bar->priv; + Child *child; + GList *children; + gint nvis_children; + gint computed_minimum = 0; + gint computed_natural = 0; + GtkRequestedSize *sizes; + GtkPackType packing; + gint size; + gint i; + gint child_size; + gint child_minimum; + gint child_natural; + GtkBorder css_borders; + + nvis_children = count_visible_children (bar); + + if (nvis_children <= 0) + return; + + sizes = g_newa (GtkRequestedSize, nvis_children); + size = avail_size - 2 * priv->hpadding; + + /* Retrieve desired size for visible children */ + for (i = 0, children = priv->children; children; children = children->next) + { + child = children->data; + + if (gtk_widget_get_visible (child->widget)) + { + gtk_widget_get_preferred_width (child->widget, + &sizes[i].minimum_size, + &sizes[i].natural_size); + + size -= sizes[i].minimum_size; + sizes[i].data = child; + i += 1; + } + } + + /* Bring children up to size first */ + size = gtk_distribute_natural_allocation (MAX (0, size), nvis_children, sizes); + + /* Allocate child positions. */ + for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing) + { + for (i = 0, children = priv->children; children; children = children->next) + { + child = children->data; + + /* If widget is not visible, skip it. */ + if (!gtk_widget_get_visible (child->widget)) + continue; + + /* If widget is packed differently skip it, but still increment i, + * since widget is visible and will be handled in next loop + * iteration. + */ + if (child->pack_type != packing) + { + i++; + continue; + } + + child_size = sizes[i].minimum_size; + + gtk_widget_get_preferred_height_for_width (child->widget, + child_size, &child_minimum, &child_natural); + + computed_minimum = MAX (computed_minimum, child_minimum); + computed_natural = MAX (computed_natural, child_natural); + } + i += 1; + } + + get_css_padding_and_border (widget, &css_borders); + + computed_minimum += 2 * priv->vpadding + css_borders.top + css_borders.bottom; + computed_natural += 2 * priv->vpadding + css_borders.top + css_borders.bottom; + + if (minimum_size) + *minimum_size = computed_minimum; + + if (natural_size) + *natural_size = computed_natural; +} + +static void +gtk_header_bar_get_preferred_width (GtkWidget *widget, + gint *minimum_size, + gint *natural_size) +{ + gtk_header_bar_get_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size); +} + +static void +gtk_header_bar_get_preferred_height (GtkWidget *widget, + gint *minimum_size, + gint *natural_size) +{ + gtk_header_bar_get_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size); +} + +static void +gtk_header_bar_get_preferred_width_for_height (GtkWidget *widget, + gint height, + gint *minimum_width, + gint *natural_width) +{ + gtk_header_bar_compute_size_for_orientation (widget, height, minimum_width, natural_width); +} + +static void +gtk_header_bar_get_preferred_height_for_width (GtkWidget *widget, + gint width, + gint *minimum_height, + gint *natural_height) +{ + gtk_header_bar_compute_size_for_opposing_orientation (widget, width, minimum_height, natural_height); +} + +static void +gtk_header_bar_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkHeaderBar *bar = GTK_HEADER_BAR (widget); + GtkHeaderBarPrivate *priv = bar->priv; + GtkRequestedSize *sizes; + gint width, height; + gint nvis_children; + gint title_minimum_size; + gint title_natural_size; + gint side[2]; + GList *l; + gint i; + Child *child; + GtkPackType packing; + GtkAllocation child_allocation; + gint x; + gint child_size; + GtkTextDirection direction; + GtkBorder css_borders; + + gtk_widget_set_allocation (widget, allocation); + + direction = gtk_widget_get_direction (widget); + nvis_children = count_visible_children (bar); + sizes = g_newa (GtkRequestedSize, nvis_children); + + get_css_padding_and_border (widget, &css_borders); + width = allocation->width - nvis_children * priv->spacing - + 2 * priv->hpadding - css_borders.left - css_borders.right; + height = allocation->height - 2 * priv->vpadding - css_borders.top - css_borders.bottom; + + i = 0; + for (l = priv->children; l; l = l->next) + { + child = l->data; + if (!gtk_widget_get_visible (child->widget)) + continue; + + gtk_widget_get_preferred_width_for_height (child->widget, + height, + &sizes[i].minimum_size, + &sizes[i].natural_size); + width -= sizes[i].minimum_size; + i++; + } + + if (priv->custom_title) + { + gtk_widget_get_preferred_width_for_height (priv->custom_title, + height, + &title_minimum_size, + &title_natural_size); + } + else + { + gtk_widget_get_preferred_width_for_height (priv->label, + height, + &title_minimum_size, + &title_natural_size); + } + width -= title_natural_size; + + width = gtk_distribute_natural_allocation (MAX (0, width), nvis_children, sizes); + + side[0] = side[1] = 0; + for (packing = GTK_PACK_START; packing <= GTK_PACK_END; packing++) + { + child_allocation.y = allocation->y + priv->vpadding; + child_allocation.height = height; + if (packing == GTK_PACK_START) + x = allocation->x + priv->hpadding; + else + x = allocation->x + allocation->width - priv->hpadding; + + if (packing == GTK_PACK_START) + { + l = priv->children; + i = 0; + } + else + { + l = g_list_last (priv->children); + i = nvis_children - 1; + } + + for (; l != NULL; (packing == GTK_PACK_START) ? (l = l->next) : (l = l->prev)) + { + child = l->data; + if (!gtk_widget_get_visible (child->widget)) + continue; + + if (child->pack_type != packing) + goto next; + + child_size = sizes[i].minimum_size; + + child_allocation.width = child_size; + + if (packing == GTK_PACK_START) + { + child_allocation.x = x; + x += child_size; + x += priv->spacing; + } + else + { + x -= child_size; + child_allocation.x = x; + x -= priv->spacing; + } + + side[packing] += child_size + priv->spacing; + + if (direction == GTK_TEXT_DIR_RTL) + child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width; + + gtk_widget_size_allocate (child->widget, &child_allocation); + + next: + if (packing == GTK_PACK_START) + i++; + else + i--; + } + } + + child_allocation.y = allocation->y + priv->vpadding; + child_allocation.height = height; + + width = MAX (side[0], side[1]); + + if (allocation->width - 2 * width >= title_natural_size) + child_size = MIN (title_natural_size, allocation->width - 2 * width); + else if (allocation->width - side[0] - side[1] >= title_natural_size) + child_size = MIN (title_natural_size, allocation->width - side[0] - side[1]); + else + child_size = allocation->width - side[0] - side[1]; + + child_allocation.x = allocation->x + (allocation->width - child_size) / 2; + child_allocation.width = child_size; + + if (allocation->x + side[0] > child_allocation.x) + child_allocation.x = allocation->x + side[0]; + else if (allocation->x + allocation->width - side[1] < child_allocation.x + child_allocation.width) + child_allocation.x = allocation->x + allocation->width - side[1] - child_allocation.width; + + if (direction == GTK_TEXT_DIR_RTL) + child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width; + + if (priv->custom_title) + gtk_widget_size_allocate (priv->custom_title, &child_allocation); + else + gtk_widget_size_allocate (priv->label, &child_allocation); +} + +/** + * gtk_header_bar_set_title: + * @bar: a #GtkHeaderBar + * @title: (allow-none): a title + * + * Sets the title of the #GtkHeaderBar. The title should help a user + * identify the current view. A good title should not include the + * application name. + * + * Since: 3.10 + */ +void +gtk_header_bar_set_title (GtkHeaderBar *bar, + const gchar *title) +{ + GtkHeaderBarPrivate *priv; + char *new_title; + + g_return_if_fail (GTK_IS_HEADER_BAR (bar)); + + priv = bar->priv; + + new_title = g_strdup (title); + g_free (priv->title); + priv->title = new_title; + + gtk_label_set_label (GTK_LABEL (priv->label), priv->title); + gtk_widget_queue_resize (GTK_WIDGET (bar)); + + g_object_notify (G_OBJECT (bar), "title"); +} + +/** + * gtk_header_bar_get_title: + * @bar: a #GtkHeaderBar + * + * Retrieves the title of the header. See gtk_header_bar_set_title(). + * + * Return value: the title of the header, or %NULL if none has + * been set explicitely. The returned string is owned by the widget + * and must not be modified or freed. + * + * Since: 3.10 + */ +const gchar * +gtk_header_bar_get_title (GtkHeaderBar *bar) +{ + g_return_val_if_fail (GTK_IS_HEADER_BAR (bar), NULL); + + return bar->priv->title; +} + +/** + * gtk_header_bar_set_custom_title: + * @bar: a #GtkHeaderBar + * @title_widget: (allow-none): a custom widget to use for a title + * + * Sets a custom title for the #GtkHeaderBar. The title should help a + * user identify the current view. This supercedes any title set by + * gtk_header_bar_set_title(). You should set the custom title to %NULL, + * for the header title label to be visible again. + * + * Since: 3.10 + */ +void +gtk_header_bar_set_custom_title (GtkHeaderBar *bar, + GtkWidget *title_widget) +{ + GtkHeaderBarPrivate *priv; + + g_return_if_fail (GTK_IS_HEADER_BAR (bar)); + if (title_widget) + g_return_if_fail (GTK_IS_WIDGET (title_widget)); + + priv = bar->priv; + + /* No need to do anything if the custom widget stays the same */ + if (priv->custom_title == title_widget) + return; + + if (priv->custom_title) + { + GtkWidget *custom = priv->custom_title; + + priv->custom_title = NULL; + gtk_widget_unparent (custom); + g_object_unref (custom); + } + + if (title_widget) + { + priv->custom_title = g_object_ref (title_widget); + + gtk_widget_hide (priv->label); + + gtk_widget_set_parent (priv->custom_title, GTK_WIDGET (bar)); + gtk_widget_set_valign (priv->custom_title, GTK_ALIGN_CENTER); + + gtk_widget_show (title_widget); + } + else + { + gtk_widget_show (priv->label); + } + + gtk_widget_queue_resize (GTK_WIDGET (bar)); + + g_object_notify (G_OBJECT (bar), "custom-title"); +} + +/** + * gtk_header_bar_get_custom_title: + * @bar: a #GtkHeaderBar + * + * Retrieves the custom title widget of the header. See + * gtk_header_bar_set_custom_title(). + * + * Return value: (transfer none): the custom title widget of the header, or %NULL if + * none has been set explicitely. + * + * Since: 3.10 + */ +GtkWidget * +gtk_header_bar_get_custom_title (GtkHeaderBar *bar) +{ + g_return_val_if_fail (GTK_IS_HEADER_BAR (bar), NULL); + + return bar->priv->custom_title; +} + +static void +gtk_header_bar_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkHeaderBar *bar = GTK_HEADER_BAR (object); + GtkHeaderBarPrivate *priv = bar->priv; + + switch (prop_id) + { + case PROP_TITLE: + g_value_set_string (value, priv->title); + break; + + case PROP_CUSTOM_TITLE: + g_value_set_object (value, priv->custom_title); + break; + + case PROP_SPACING: + g_value_set_int (value, priv->spacing); + break; + + case PROP_HPADDING: + g_value_set_int (value, priv->hpadding); + break; + + case PROP_VPADDING: + g_value_set_int (value, priv->vpadding); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_header_bar_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkHeaderBar *bar = GTK_HEADER_BAR (object); + GtkHeaderBarPrivate *priv = bar->priv; + + switch (prop_id) + { + case PROP_TITLE: + gtk_header_bar_set_title (bar, g_value_get_string (value)); + break; + + case PROP_CUSTOM_TITLE: + gtk_header_bar_set_custom_title (bar, g_value_get_object (value)); + break; + + case PROP_SPACING: + priv->spacing = g_value_get_int (value); + gtk_widget_queue_resize (GTK_WIDGET (bar)); + break; + + case PROP_HPADDING: + priv->hpadding = g_value_get_int (value); + gtk_widget_queue_resize (GTK_WIDGET (bar)); + break; + + case PROP_VPADDING: + priv->vpadding = g_value_get_int (value); + gtk_widget_queue_resize (GTK_WIDGET (bar)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_header_bar_pack (GtkHeaderBar *bar, + GtkWidget *widget, + GtkPackType pack_type) +{ + Child *child; + + g_return_if_fail (gtk_widget_get_parent (widget) == NULL); + + child = g_new (Child, 1); + child->widget = widget; + child->pack_type = pack_type; + + bar->priv->children = g_list_append (bar->priv->children, child); + + gtk_widget_freeze_child_notify (widget); + gtk_widget_set_parent (widget, GTK_WIDGET (bar)); + gtk_widget_child_notify (widget, "pack-type"); + gtk_widget_child_notify (widget, "position"); + gtk_widget_thaw_child_notify (widget); +} + +static void +gtk_header_bar_add (GtkContainer *container, + GtkWidget *child) +{ + gtk_header_bar_pack (GTK_HEADER_BAR (container), child, GTK_PACK_START); +} + +static GList * +find_child_link (GtkHeaderBar *bar, + GtkWidget *widget) +{ + GList *l; + Child *child; + + for (l = bar->priv->children; l; l = l->next) + { + child = l->data; + if (child->widget == widget) + return l; + } + + return NULL; +} + +static void +gtk_header_bar_remove (GtkContainer *container, + GtkWidget *widget) +{ + GtkHeaderBar *bar = GTK_HEADER_BAR (container); + GList *l; + Child *child; + + l = find_child_link (bar, widget); + if (l) + { + child = l->data; + gtk_widget_unparent (child->widget); + bar->priv->children = g_list_remove_link (bar->priv->children, l); + g_free (child); + gtk_widget_queue_resize (GTK_WIDGET (container)); + } +} + +static void +gtk_header_bar_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data) +{ + GtkHeaderBar *bar = GTK_HEADER_BAR (container); + GtkHeaderBarPrivate *priv = bar->priv; + Child *child; + GList *children; + + children = priv->children; + while (children) + { + child = children->data; + children = children->next; + if (child->pack_type == GTK_PACK_START) + (* callback) (child->widget, callback_data); + } + + if (include_internals) + { + if (priv->custom_title) + (* callback) (priv->custom_title, callback_data); + else + (* callback) (priv->label, callback_data); + } + + children = priv->children; + while (children) + { + child = children->data; + children = children->next; + if (child->pack_type == GTK_PACK_END) + (* callback) (child->widget, callback_data); + } +} + +static GType +gtk_header_bar_child_type (GtkContainer *container) +{ + return GTK_TYPE_WIDGET; +} + +static void +gtk_header_bar_get_child_property (GtkContainer *container, + GtkWidget *widget, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GList *l; + Child *child; + + l = find_child_link (GTK_HEADER_BAR (container), widget); + if (l == NULL) + { + g_param_value_set_default (pspec, value); + return; + } + + child = l->data; + + switch (property_id) + { + case CHILD_PROP_PACK_TYPE: + g_value_set_enum (value, child->pack_type); + break; + + case CHILD_PROP_POSITION: + g_value_set_int (value, g_list_position (GTK_HEADER_BAR (container)->priv->children, l)); + break; + + default: + GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec); + break; + } +} + +static void +gtk_header_bar_set_child_property (GtkContainer *container, + GtkWidget *widget, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GList *l; + Child *child; + + l = find_child_link (GTK_HEADER_BAR (container), widget); + child = l->data; + + switch (property_id) + { + case CHILD_PROP_PACK_TYPE: + child->pack_type = g_value_get_enum (value); + break; + default: + GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec); + break; + } +} + +static GtkWidgetPath * +gtk_header_bar_get_path_for_child (GtkContainer *container, + GtkWidget *child) +{ + GtkHeaderBar *bar = GTK_HEADER_BAR (container); + GtkWidgetPath *path, *sibling_path; + GList *list, *children; + + path = _gtk_widget_create_path (GTK_WIDGET (container)); + + if (gtk_widget_get_visible (child)) + { + gint i, position; + + sibling_path = gtk_widget_path_new (); + + /* get_all_children works in reverse (!) visible order */ + children = _gtk_container_get_all_children (container); + if (gtk_widget_get_direction (GTK_WIDGET (bar)) == GTK_TEXT_DIR_LTR) + children = g_list_reverse (children); + + position = -1; + i = 0; + for (list = children; list; list = list->next) + { + if (!gtk_widget_get_visible (list->data)) + continue; + + gtk_widget_path_append_for_widget (sibling_path, list->data); + + if (list->data == child) + position = i; + i++; + } + g_list_free (children); + + if (position >= 0) + gtk_widget_path_append_with_siblings (path, sibling_path, position); + else + gtk_widget_path_append_for_widget (path, child); + + gtk_widget_path_unref (sibling_path); + } + else + gtk_widget_path_append_for_widget (path, child); + + return path; +} + +static gint +gtk_header_bar_draw (GtkWidget *widget, + cairo_t *cr) +{ + GtkStyleContext *context; + + context = gtk_widget_get_style_context (widget); + + gtk_render_background (context, cr, 0, 0, + gtk_widget_get_allocated_width (widget), + gtk_widget_get_allocated_height (widget)); + gtk_render_frame (context, cr, 0, 0, + gtk_widget_get_allocated_width (widget), + gtk_widget_get_allocated_height (widget)); + + + GTK_WIDGET_CLASS (gtk_header_bar_parent_class)->draw (widget, cr); + + return TRUE; +} + +static void +gtk_header_bar_class_init (GtkHeaderBarClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); + GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class); + + object_class->get_property = gtk_header_bar_get_property; + object_class->set_property = gtk_header_bar_set_property; + + widget_class->size_allocate = gtk_header_bar_size_allocate; + widget_class->get_preferred_width = gtk_header_bar_get_preferred_width; + widget_class->get_preferred_height = gtk_header_bar_get_preferred_height; + widget_class->get_preferred_height_for_width = gtk_header_bar_get_preferred_height_for_width; + widget_class->get_preferred_width_for_height = gtk_header_bar_get_preferred_width_for_height; + widget_class->draw = gtk_header_bar_draw; + + container_class->add = gtk_header_bar_add; + container_class->remove = gtk_header_bar_remove; + container_class->forall = gtk_header_bar_forall; + container_class->child_type = gtk_header_bar_child_type; + container_class->set_child_property = gtk_header_bar_set_child_property; + container_class->get_child_property = gtk_header_bar_get_child_property; + container_class->get_path_for_child = gtk_header_bar_get_path_for_child; + gtk_container_class_handle_border_width (container_class); + + gtk_container_class_install_child_property (container_class, + CHILD_PROP_PACK_TYPE, + g_param_spec_enum ("pack-type", + P_("Pack type"), + P_("A GtkPackType indicating whether the child is packed with reference to the start or end of the parent"), + GTK_TYPE_PACK_TYPE, GTK_PACK_START, + GTK_PARAM_READWRITE)); + gtk_container_class_install_child_property (container_class, + CHILD_PROP_POSITION, + g_param_spec_int ("position", + P_("Position"), + P_("The index of the child in the parent"), + -1, G_MAXINT, 0, + GTK_PARAM_READABLE)); + + g_object_class_install_property (object_class, + PROP_TITLE, + g_param_spec_string ("title", + P_("Title"), + P_("The title to display"), + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_CUSTOM_TITLE, + g_param_spec_object ("custom-title", + P_("Custom Title"), + P_("Custom title widget to display"), + GTK_TYPE_WIDGET, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, + PROP_SPACING, + g_param_spec_int ("spacing", + P_("Spacing"), + P_("The amount of space between children"), + 0, G_MAXINT, + DEFAULT_SPACING, + GTK_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_HPADDING, + g_param_spec_int ("hpadding", + P_("Horizontal padding"), + P_("The amount of space to the left and right of children"), + 0, G_MAXINT, + DEFAULT_HPADDING, + GTK_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_VPADDING, + g_param_spec_int ("vpadding", + P_("Vertical padding"), + P_("The amount of space to the above and below children"), + 0, G_MAXINT, + DEFAULT_VPADDING, + GTK_PARAM_READWRITE)); + + g_type_class_add_private (object_class, sizeof (GtkHeaderBarPrivate)); + + gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_FILLER); +} + +static void +gtk_header_buildable_add_child (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type) +{ + if (type && strcmp (type, "title") == 0) + gtk_header_bar_set_custom_title (GTK_HEADER_BAR (buildable), GTK_WIDGET (child)); + else if (!type) + gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child)); + else + GTK_BUILDER_WARN_INVALID_CHILD_TYPE (GTK_HEADER_BAR (buildable), type); +} + +static void +gtk_header_buildable_init (GtkBuildableIface *iface) +{ + iface->add_child = gtk_header_buildable_add_child; +} + +/** + * gtk_header_bar_pack_start: + * @bar: A #GtkHeaderBar + * @child: the #GtkWidget to be added to @bar + * + * Adds @child to @box, packed with reference to the + * start of the @box. + * + * Since: 3.10 + */ +void +gtk_header_bar_pack_start (GtkHeaderBar *bar, + GtkWidget *child) +{ + gtk_header_bar_pack (bar, child, GTK_PACK_START); +} + +/** + * gtk_header_bar_pack_end: + * @bar: A #GtkHeaderBar + * @child: the #GtkWidget to be added to @bar + * + * Adds @child to @box, packed with reference to the + * end of the @box. + * + * Since: 3.10 + */ +void +gtk_header_bar_pack_end (GtkHeaderBar *bar, + GtkWidget *child) +{ + gtk_header_bar_pack (bar, child, GTK_PACK_END); +} + +/** + * gtk_header_bar_new: + * + * Creates a new #GtkHeaderBar widget. + * + * Returns: a new #GtkHeaderBar + * + * Since: 3.10 + */ +GtkWidget * +gtk_header_bar_new (void) +{ + return GTK_WIDGET (g_object_new (GTK_TYPE_HEADER_BAR, NULL)); +} diff --git a/gtk/gtkheaderbar.h b/gtk/gtkheaderbar.h new file mode 100644 index 0000000000..c59c447e7d --- /dev/null +++ b/gtk/gtkheaderbar.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2013 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __GTK_HEADER_BAR_H__ +#define __GTK_HEADER_BAR_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only <gtk/gtk.h> can be included directly." +#endif + +#include <gtk/gtkcontainer.h> + +G_BEGIN_DECLS + +#define GTK_TYPE_HEADER_BAR (gtk_header_bar_get_type ()) +#define GTK_HEADER_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_HEADER_BAR, GtkHeaderBar)) +#define GTK_HEADER_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_HEADER_BAR, GtkHeaderBarClass)) +#define GTK_IS_HEADER_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_HEADER_BAR)) +#define GTK_IS_HEADER_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_HEADER_BAR)) +#define GTK_HEADER_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_HEADER_BAR, GtkHeaderBarClass)) + +typedef struct _GtkHeaderBar GtkHeaderBar; +typedef struct _GtkHeaderBarPrivate GtkHeaderBarPrivate; +typedef struct _GtkHeaderBarClass GtkHeaderBarClass; + +struct _GtkHeaderBar +{ + GtkContainer container; + + /*< private >*/ + GtkHeaderBarPrivate *priv; +}; + +struct _GtkHeaderBarClass +{ + GtkContainerClass parent_class; + + /* Padding for future expansion */ + void (*_gtk_reserved1) (void); + void (*_gtk_reserved2) (void); + void (*_gtk_reserved3) (void); + void (*_gtk_reserved4) (void); +}; + +GDK_AVAILABLE_IN_3_10 +GType gtk_header_bar_get_type (void) G_GNUC_CONST; +GDK_AVAILABLE_IN_3_10 +GtkWidget *gtk_header_bar_new (void); +GDK_AVAILABLE_IN_3_10 +void gtk_header_bar_set_title (GtkHeaderBar *bar, + const gchar *title); +GDK_AVAILABLE_IN_3_10 +const gchar *gtk_header_bar_get_title (GtkHeaderBar *bar); +GDK_AVAILABLE_IN_3_10 +void gtk_header_bar_set_custom_title (GtkHeaderBar *bar, + GtkWidget *title_widget); +GDK_AVAILABLE_IN_3_10 +GtkWidget *gtk_header_bar_get_custom_title (GtkHeaderBar *bar); +GDK_AVAILABLE_IN_3_10 +void gtk_header_bar_pack_start (GtkHeaderBar *bar, + GtkWidget *child); +GDK_AVAILABLE_IN_3_10 +void gtk_header_bar_pack_end (GtkHeaderBar *bar, + GtkWidget *child); + +G_END_DECLS + +#endif /* __GTK_HEADER_BAR_H__ */ |