diff options
Diffstat (limited to 'subprojects/libhandy/src/hdy-title-bar.c')
-rw-r--r-- | subprojects/libhandy/src/hdy-title-bar.c | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/subprojects/libhandy/src/hdy-title-bar.c b/subprojects/libhandy/src/hdy-title-bar.c new file mode 100644 index 0000000..fd5371a --- /dev/null +++ b/subprojects/libhandy/src/hdy-title-bar.c @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2018 Purism SPC + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" +#include "hdy-title-bar.h" + +#include <glib/gi18n-lib.h> + +/** + * SECTION:hdy-title-bar + * @short_description: A simple title bar container. + * @Title: HdyTitleBar + * + * HdyTitleBar is meant to be used as the top-level widget of your window's + * title bar. It will be drawn with the same style as a GtkHeaderBar but it + * won't force a widget layout on you: you can put whatever widget you want in + * it, including a GtkHeaderBar. + * + * HdyTitleBar becomes really useful when you want to animate header bars, like + * an adaptive application using #HdyLeaflet would do. + * + * # CSS nodes + * + * #HdyTitleBar has a single CSS node with name headerbar. + */ + +enum { + PROP_0, + PROP_SELECTION_MODE, + LAST_PROP, +}; + +struct _HdyTitleBar +{ + GtkBin parent_instance; + + gboolean selection_mode; +}; + +G_DEFINE_TYPE (HdyTitleBar, hdy_title_bar, GTK_TYPE_BIN) + +static GParamSpec *props[LAST_PROP]; + +/** + * hdy_title_bar_set_selection_mode: + * @self: a #HdyTitleBar + * @selection_mode: %TRUE to enable the selection mode + * + * Sets whether @self is in selection mode. + */ +void +hdy_title_bar_set_selection_mode (HdyTitleBar *self, + gboolean selection_mode) +{ + GtkStyleContext *context; + + g_return_if_fail (HDY_IS_TITLE_BAR (self)); + + selection_mode = !!selection_mode; + + context = gtk_widget_get_style_context (GTK_WIDGET (self)); + + if (self->selection_mode == selection_mode) + return; + + self->selection_mode = selection_mode; + + if (selection_mode) + gtk_style_context_add_class (context, "selection-mode"); + else + gtk_style_context_remove_class (context, "selection-mode"); + + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_SELECTION_MODE]); +} + +/** + * hdy_title_bar_get_selection_mode: + * @self: a #HdyTitleBar + * + * Returns whether whether @self is in selection mode. + * + * Returns: %TRUE if the title bar is in selection mode + */ +gboolean +hdy_title_bar_get_selection_mode (HdyTitleBar *self) +{ + g_return_val_if_fail (HDY_IS_TITLE_BAR (self), FALSE); + + return self->selection_mode; +} + +static void +style_updated_cb (HdyTitleBar *self) +{ + GtkStyleContext *context; + gboolean selection_mode; + + g_assert (HDY_IS_TITLE_BAR (self)); + + context = gtk_widget_get_style_context (GTK_WIDGET (self)); + selection_mode = gtk_style_context_has_class (context, "selection-mode"); + + if (self->selection_mode == selection_mode) + return; + + self->selection_mode = selection_mode; + + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_SELECTION_MODE]); +} + +static void +hdy_title_bar_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + HdyTitleBar *self = HDY_TITLE_BAR (object); + + switch (prop_id) { + case PROP_SELECTION_MODE: + g_value_set_boolean (value, hdy_title_bar_get_selection_mode (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +hdy_title_bar_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + HdyTitleBar *self = HDY_TITLE_BAR (object); + + switch (prop_id) { + case PROP_SELECTION_MODE: + hdy_title_bar_set_selection_mode (self, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static gboolean +hdy_title_bar_draw (GtkWidget *widget, + cairo_t *cr) +{ + GtkStyleContext *context; + + context = gtk_widget_get_style_context (widget); + /* GtkWidget draws nothing by default so we have to render the background + * explicitly for HdyTitleBar to render the typical titlebar background. + */ + gtk_render_background (context, + cr, + 0, 0, + gtk_widget_get_allocated_width (widget), + gtk_widget_get_allocated_height (widget)); + + return GTK_WIDGET_CLASS (hdy_title_bar_parent_class)->draw (widget, cr); +} + +/* This private method is prefixed by the class name because it will be a + * virtual method in GTK 4. + */ +static void +hdy_title_bar_measure (GtkWidget *widget, + GtkOrientation orientation, + gint for_size, + gint *minimum, + gint *natural, + gint *minimum_baseline, + gint *natural_baseline) +{ + GtkWidget *child; + gint parent_min, parent_nat; + gint css_width, css_height, css_min; + + child = gtk_bin_get_child (GTK_BIN (widget)); + + gtk_style_context_get (gtk_widget_get_style_context (widget), + gtk_widget_get_state_flags (widget), + "min-width", &css_width, + "min-height", &css_height, + NULL); + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + css_min = css_width; + else + css_min = css_height; + + if (child) + if (orientation == GTK_ORIENTATION_HORIZONTAL) + if (for_size != 1) + gtk_widget_get_preferred_width_for_height (child, + MAX (for_size, css_height), + &parent_min, &parent_nat); + else + gtk_widget_get_preferred_width (child, &parent_min, &parent_nat); + else + if (for_size != 1) + gtk_widget_get_preferred_height_for_width (child, + MAX (for_size, css_width), + &parent_min, &parent_nat); + else + gtk_widget_get_preferred_height (child, &parent_min, &parent_nat); + else { + parent_min = 0; + parent_nat = 0; + } + + if (minimum) + *minimum = MAX (parent_min, css_min); + + if (natural) + *natural = MAX (parent_nat, css_min); + + if (minimum_baseline) + *minimum_baseline = -1; + + if (natural_baseline) + *natural_baseline = -1; +} + +static void +hdy_title_bar_get_preferred_width (GtkWidget *widget, + gint *minimum, + gint *natural) +{ + hdy_title_bar_measure (widget, GTK_ORIENTATION_HORIZONTAL, -1, + minimum, natural, NULL, NULL); +} + +static void +hdy_title_bar_get_preferred_width_for_height (GtkWidget *widget, + gint height, + gint *minimum, + gint *natural) +{ + hdy_title_bar_measure (widget, GTK_ORIENTATION_HORIZONTAL, height, + minimum, natural, NULL, NULL); +} + +static void +hdy_title_bar_get_preferred_height (GtkWidget *widget, + gint *minimum, + gint *natural) +{ + hdy_title_bar_measure (widget, GTK_ORIENTATION_VERTICAL, -1, + minimum, natural, NULL, NULL); +} + +static void +hdy_title_bar_get_preferred_height_for_width (GtkWidget *widget, + gint width, + gint *minimum, + gint *natural) +{ + hdy_title_bar_measure (widget, GTK_ORIENTATION_VERTICAL, width, + minimum, natural, NULL, NULL); +} + +static void +hdy_title_bar_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkAllocation clip; + + gtk_render_background_get_clip (gtk_widget_get_style_context (widget), + allocation->x, + allocation->y, + allocation->width, + allocation->height, + &clip); + + GTK_WIDGET_CLASS (hdy_title_bar_parent_class)->size_allocate (widget, allocation); + gtk_widget_set_clip (widget, &clip); +} + +static void +hdy_title_bar_class_init (HdyTitleBarClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); + + object_class->get_property = hdy_title_bar_get_property; + object_class->set_property = hdy_title_bar_set_property; + + widget_class->draw = hdy_title_bar_draw; + widget_class->get_preferred_width = hdy_title_bar_get_preferred_width; + widget_class->get_preferred_width_for_height = hdy_title_bar_get_preferred_width_for_height; + widget_class->get_preferred_height = hdy_title_bar_get_preferred_height; + widget_class->get_preferred_height_for_width = hdy_title_bar_get_preferred_height_for_width; + widget_class->size_allocate = hdy_title_bar_size_allocate; + + /** + * HdyTitleBar:selection_mode: + * + * %TRUE if the title bar is in selection mode. + */ + props[PROP_SELECTION_MODE] = + g_param_spec_boolean ("selection-mode", + _("Selection mode"), + _("Whether or not the title bar is in selection mode"), + FALSE, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); + + g_object_class_install_properties (object_class, LAST_PROP, props); + + gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_TITLE_BAR); + /* Adwaita states it expects a headerbar to be the top-level titlebar widget, + * so style-wise HdyTitleBar pretends to be one as its role is to be the + * top-level titlebar widget. + */ + gtk_widget_class_set_css_name (widget_class, "headerbar"); + gtk_container_class_handle_border_width (container_class); +} + +static void +hdy_title_bar_init (HdyTitleBar *self) +{ + GtkStyleContext *context; + + context = gtk_widget_get_style_context (GTK_WIDGET (self)); + /* Ensure the widget has the titlebar style class. */ + gtk_style_context_add_class (context, "titlebar"); + + g_signal_connect (self, "style-updated", G_CALLBACK (style_updated_cb), NULL); +} + +/** + * hdy_title_bar_new: + * + * Creates a new #HdyTitleBar. + * + * Returns: a new #HdyTitleBar + */ +GtkWidget * +hdy_title_bar_new (void) +{ + return g_object_new (HDY_TYPE_TITLE_BAR, NULL); +} |