From b38a096aee2e7486ced567adbad3177d276a8e5a Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 4 Aug 2013 01:25:26 +0200 Subject: [PATCH] GtkHeaderBar: optionally add a close button Add a boolean property that controls whether a window close button will be shown in the header bar or not. Doing this in the toolkit will ensure consistency of the visual apperance. https://bugzilla.gnome.org/show_bug.cgi?id=702971 --- docs/reference/gtk/gtk3-sections.txt | 2 + gtk/gtkheaderbar.c | 214 ++++++++++++++++++++++++++- gtk/gtkheaderbar.h | 7 + tests/testtitlebar.c | 14 +- 4 files changed, 223 insertions(+), 14 deletions(-) diff --git a/docs/reference/gtk/gtk3-sections.txt b/docs/reference/gtk/gtk3-sections.txt index d5555a2cc3..886f4d9e21 100644 --- a/docs/reference/gtk/gtk3-sections.txt +++ b/docs/reference/gtk/gtk3-sections.txt @@ -7667,6 +7667,8 @@ gtk_header_bar_set_custom_title gtk_header_bar_get_custom_title gtk_header_bar_pack_start gtk_header_bar_pack_end +gtk_header_bar_set_show_close_button +gtk_header_bar_get_show_close_button GTK_TYPE_HEADER_BAR diff --git a/gtk/gtkheaderbar.c b/gtk/gtkheaderbar.c index a803ddab1f..9aa55df70c 100644 --- a/gtk/gtkheaderbar.c +++ b/gtk/gtkheaderbar.c @@ -56,6 +56,8 @@ struct _GtkHeaderBarPrivate GtkWidget *label_box; GtkWidget *label_sizing_box; GtkWidget *custom_title; + GtkWidget *close_button; + GtkWidget *separator; gint spacing; gint hpadding; gint vpadding; @@ -77,7 +79,8 @@ enum { PROP_CUSTOM_TITLE, PROP_SPACING, PROP_HPADDING, - PROP_VPADDING + PROP_VPADDING, + PROP_SHOW_CLOSE_BUTTON }; enum { @@ -202,6 +205,57 @@ _gtk_header_bar_create_title_box (const char *title, return label_box; } +static void +close_button_clicked (GtkButton *button, gpointer data) +{ + GtkWidget *toplevel; + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button)); + gtk_window_close (GTK_WINDOW (toplevel)); +} + +static void +add_close_button (GtkHeaderBar *bar) +{ + GtkHeaderBarPrivate *priv; + GtkWidget *button; + GIcon *icon; + GtkWidget *image; + GtkWidget *separator; + + priv = gtk_header_bar_get_instance_private (bar); + + button = gtk_button_new (); + icon = g_themed_icon_new ("window-close-symbolic"); + image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_BUTTON); + g_object_unref (icon); + gtk_container_add (GTK_CONTAINER (button), image); + gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE); + g_signal_connect (button, "clicked", + G_CALLBACK (close_button_clicked), NULL); + gtk_widget_show_all (button); + gtk_widget_set_parent (button, GTK_WIDGET (bar)); + + separator = gtk_separator_new (GTK_ORIENTATION_VERTICAL); + gtk_widget_show (separator); + gtk_widget_set_parent (separator, GTK_WIDGET (bar)); + + priv->separator = separator; + priv->close_button = button; +} + +static void +remove_close_button (GtkHeaderBar *bar) +{ + GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar); + + gtk_widget_unparent (priv->separator); + gtk_widget_unparent (priv->close_button); + + priv->separator = NULL; + priv->close_button = NULL; +} + static void construct_label_box (GtkHeaderBar *bar) { @@ -230,6 +284,8 @@ gtk_header_bar_init (GtkHeaderBar *bar) priv->title = NULL; priv->subtitle = NULL; priv->custom_title = NULL; + priv->close_button = NULL; + priv->separator = NULL; priv->children = NULL; priv->spacing = DEFAULT_SPACING; priv->hpadding = DEFAULT_HPADDING; @@ -328,6 +384,15 @@ gtk_header_bar_get_size (GtkWidget *widget, nvis_children += 1; } + if (priv->close_button != NULL) + { + if (add_child_size (priv->close_button, orientation, &minimum, &natural)) + nvis_children += 1; + + if (add_child_size (priv->separator, orientation, &minimum, &natural)) + nvis_children += 1; + } + if (nvis_children > 0 && orientation == GTK_ORIENTATION_HORIZONTAL) { minimum += nvis_children * priv->spacing; @@ -408,6 +473,19 @@ gtk_header_bar_compute_size_for_orientation (GtkWidget *widget, required_natural += child_natural; } + if (priv->close_button != NULL) + { + gtk_widget_get_preferred_width (priv->close_button, + &child_size, &child_natural); + required_size += child_size; + required_natural += child_natural; + + gtk_widget_get_preferred_width (priv->separator, + &child_size, &child_natural); + required_size += child_size; + required_natural += child_natural; + } + if (nvis_children > 0) { required_size += nvis_children * priv->spacing; @@ -526,6 +604,19 @@ gtk_header_bar_compute_size_for_opposing_orientation (GtkWidget *widget, computed_natural = MAX (computed_natural, child_natural); } + if (priv->close_button != NULL) + { + gtk_widget_get_preferred_height (priv->close_button, + &child_minimum, &child_natural); + computed_minimum = MAX (computed_minimum, child_minimum); + computed_natural = MAX (computed_natural, child_natural); + + gtk_widget_get_preferred_height (priv->separator, + &child_minimum, &child_natural); + computed_minimum = MAX (computed_minimum, child_minimum); + computed_natural = MAX (computed_natural, child_natural); + } + get_css_padding_and_border (widget, &css_borders); computed_minimum += 2 * priv->vpadding + css_borders.top + css_borders.bottom; @@ -583,6 +674,9 @@ gtk_header_bar_size_allocate (GtkWidget *widget, gint nvis_children; gint title_minimum_size; gint title_natural_size; + gint close_button_width; + gint separator_width; + gint close_width; gint side[2]; GList *l; gint i; @@ -636,6 +730,23 @@ gtk_header_bar_size_allocate (GtkWidget *widget, } width -= title_natural_size; + close_button_width = separator_width = close_width = 0; + if (priv->close_button != NULL) + { + gint min, nat; + gtk_widget_get_preferred_width_for_height (priv->close_button, + height, + &min, &nat); + close_button_width = nat; + + gtk_widget_get_preferred_width_for_height (priv->separator, + height, + &min, &nat); + separator_width = nat; + close_width = close_button_width + separator_width + 2 * priv->spacing; + } + width -= close_width; + width = gtk_distribute_natural_allocation (MAX (0, width), nvis_children, sizes); side[0] = side[1] = 0; @@ -646,7 +757,7 @@ gtk_header_bar_size_allocate (GtkWidget *widget, if (packing == GTK_PACK_START) x = allocation->x + priv->hpadding + css_borders.left; else - x = allocation->x + allocation->width - priv->hpadding - css_borders.right; + x = allocation->x + allocation->width - close_width - priv->hpadding - css_borders.right; if (packing == GTK_PACK_START) { @@ -700,6 +811,8 @@ gtk_header_bar_size_allocate (GtkWidget *widget, } } + side[GTK_PACK_END] += close_width; + child_allocation.y = allocation->y + priv->vpadding + css_borders.top; child_allocation.height = height; @@ -727,6 +840,23 @@ gtk_header_bar_size_allocate (GtkWidget *widget, gtk_widget_size_allocate (priv->custom_title, &child_allocation); else gtk_widget_size_allocate (priv->label_box, &child_allocation); + + if (priv->close_button) + { + if (direction == GTK_TEXT_DIR_RTL) + child_allocation.x = allocation->x + priv->hpadding + css_borders.left; + else + child_allocation.x = allocation->x + allocation->width - priv->hpadding - css_borders.right - close_button_width; + child_allocation.width = close_button_width; + gtk_widget_size_allocate (priv->close_button, &child_allocation); + + if (direction == GTK_TEXT_DIR_RTL) + child_allocation.x = allocation->x + priv->hpadding + css_borders.left + close_button_width + priv->spacing; + else + child_allocation.x = allocation->x + allocation->width - priv->hpadding - css_borders.right - close_button_width - priv->spacing - separator_width; + child_allocation.width = separator_width; + gtk_widget_size_allocate (priv->separator, &child_allocation); + } } /** @@ -976,6 +1106,10 @@ gtk_header_bar_get_property (GObject *object, g_value_set_int (value, priv->vpadding); break; + case PROP_SHOW_CLOSE_BUTTON: + g_value_set_boolean (value, gtk_header_bar_get_show_close_button (bar)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1020,6 +1154,10 @@ gtk_header_bar_set_property (GObject *object, gtk_widget_queue_resize (GTK_WIDGET (bar)); break; + case PROP_SHOW_CLOSE_BUTTON: + gtk_header_bar_set_show_close_button (bar, g_value_get_boolean (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1120,6 +1258,12 @@ gtk_header_bar_forall (GtkContainer *container, if (include_internals && priv->label_box != NULL) (* callback) (priv->label_box, callback_data); + if (include_internals && priv->close_button != NULL) + (* callback) (priv->close_button, callback_data); + + if (include_internals && priv->separator != NULL) + (* callback) (priv->separator, callback_data); + children = priv->children; while (children) { @@ -1362,6 +1506,14 @@ gtk_header_bar_class_init (GtkHeaderBarClass *class) DEFAULT_VPADDING, GTK_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_SHOW_CLOSE_BUTTON, + g_param_spec_boolean ("show-close-button", + P_("Show Close button"), + P_("Whether to show a window close button"), + FALSE, + GTK_PARAM_READWRITE)); + gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_FILLER); } @@ -1433,3 +1585,61 @@ gtk_header_bar_new (void) { return GTK_WIDGET (g_object_new (GTK_TYPE_HEADER_BAR, NULL)); } + +/** + * gtk_header_bar_get_show_close_button: + * @bar: a #GtkHeaderBar + * + * Returns whether this header bar shows a window close + * button. + * + * Returns: %TRUE if a window close button is shown + * + * Since: 3.10 + */ +gboolean +gtk_header_bar_get_show_close_button (GtkHeaderBar *bar) +{ + GtkHeaderBarPrivate *priv; + + g_return_val_if_fail (GTK_IS_HEADER_BAR (bar), FALSE); + + priv = gtk_header_bar_get_instance_private (bar); + + return priv->close_button != NULL; +} + +/** + * gtk_header_bar_set_show_close_button: + * @bar: a #GtkHeaderBar + * @setting: %TRUE to show a window close button + * + * Sets whether this header bar shows a window close + * button. + * + * Since: 3.10 + */ +void +gtk_header_bar_set_show_close_button (GtkHeaderBar *bar, + gboolean setting) +{ + GtkHeaderBarPrivate *priv; + + g_return_if_fail (GTK_IS_HEADER_BAR (bar)); + + priv = gtk_header_bar_get_instance_private (bar); + + setting = setting != FALSE; + + if ((priv->close_button != NULL) == setting) + return; + + if (setting) + add_close_button (bar); + else + remove_close_button (bar); + + gtk_widget_queue_resize (GTK_WIDGET (bar)); + + g_object_notify (G_OBJECT (bar), "show-close-button"); +} diff --git a/gtk/gtkheaderbar.h b/gtk/gtkheaderbar.h index 0437ba0251..0e4f5e20ba 100644 --- a/gtk/gtkheaderbar.h +++ b/gtk/gtkheaderbar.h @@ -83,6 +83,13 @@ GDK_AVAILABLE_IN_3_10 void gtk_header_bar_pack_end (GtkHeaderBar *bar, GtkWidget *child); +GDK_AVAILABLE_IN_3_10 +gboolean gtk_header_bar_get_show_close_button (GtkHeaderBar *bar); + +GDK_AVAILABLE_IN_3_10 +void gtk_header_bar_set_show_close_button (GtkHeaderBar *bar, + gboolean setting); + G_END_DECLS #endif /* __GTK_HEADER_BAR_H__ */ diff --git a/tests/testtitlebar.c b/tests/testtitlebar.c index e20c3940dd..107f705a5d 100644 --- a/tests/testtitlebar.c +++ b/tests/testtitlebar.c @@ -17,7 +17,9 @@ main (int argc, char *argv[]) gtk_window_set_default_size (GTK_WINDOW (window), 600, 400); header = gtk_header_bar_new (); + gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (header), TRUE); gtk_style_context_add_class (gtk_widget_get_style_context (header), "titlebar"); + title = gtk_label_new (NULL); gtk_label_set_markup (GTK_LABEL (title), "Welcome to Facebook - Log in, sign up or learn more"); gtk_label_set_ellipsize (GTK_LABEL (title), PANGO_ELLIPSIZE_END); @@ -32,18 +34,6 @@ main (int argc, char *argv[]) gtk_container_add (GTK_CONTAINER (button), image); gtk_header_bar_pack_end (GTK_HEADER_BAR (header), button); - gtk_header_bar_pack_end (GTK_HEADER_BAR (header), gtk_separator_new (GTK_ORIENTATION_VERTICAL)); - - button = gtk_button_new (); - icon = g_themed_icon_new ("window-close-symbolic"); - image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_BUTTON); - g_object_unref (icon); - gtk_container_add (GTK_CONTAINER (button), image); - gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE); - g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_window_close), window); - - gtk_header_bar_pack_end (GTK_HEADER_BAR (header), button); - box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_style_context_add_class (gtk_widget_get_style_context (box), "linked"); button = gtk_button_new ();