From 8d98b4de478156def2fab9256ddfbd9ef5ee7122 Mon Sep 17 00:00:00 2001 From: Adrien Plazas Date: Fri, 24 Aug 2018 13:26:08 +0200 Subject: [PATCH 1/3] Add HdyTitleBar This widget present itself as the title bar of a window but is otherwise a simple bin which can receive any widget. This coupled with a transparent headerbar background can work around graphical glitches when animation header bars in a leaflet. --- data/glade/libhandy.xml | 2 + doc/handy-docs.xml | 1 + src/handy.h | 1 + src/hdy-title-bar.c | 222 ++++++++++++++++++++++++++++++++++++++++ src/hdy-title-bar.h | 23 +++++ src/meson.build | 2 + 6 files changed, 251 insertions(+) create mode 100644 src/hdy-title-bar.c create mode 100644 src/hdy-title-bar.h diff --git a/data/glade/libhandy.xml b/data/glade/libhandy.xml index f9ffd7e0..f5d8abe4 100644 --- a/data/glade/libhandy.xml +++ b/data/glade/libhandy.xml @@ -5,6 +5,7 @@ + @@ -12,5 +13,6 @@ + diff --git a/doc/handy-docs.xml b/doc/handy-docs.xml index f2d38669..e474d605 100644 --- a/doc/handy-docs.xml +++ b/doc/handy-docs.xml @@ -42,6 +42,7 @@ + diff --git a/src/handy.h b/src/handy.h index 285210e1..97c67720 100644 --- a/src/handy.h +++ b/src/handy.h @@ -34,6 +34,7 @@ G_BEGIN_DECLS #include "hdy-fold.h" #include "hdy-leaflet.h" #include "hdy-string-utf8.h" +#include "hdy-title-bar.h" #undef _HANDY_INSIDE diff --git a/src/hdy-title-bar.c b/src/hdy-title-bar.c new file mode 100644 index 00000000..7fe53159 --- /dev/null +++ b/src/hdy-title-bar.c @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2018 Purism SPC + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "hdy-title-bar.h" + +#include +#include + +/** + * 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 #HdyTitleBar would do. + */ + +enum { + PROP_0, + PROP_SELECTION_MODE, + LAST_PROP, +}; + +#define HDY_EASE_OUT_TAN_CUBIC 3 + +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 wether 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 + * explicitely 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); +} + +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; + + /** + * 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); + + /* 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 + */ +HdyTitleBar * +hdy_title_bar_new (void) +{ + return g_object_new (HDY_TYPE_TITLE_BAR, NULL); +} diff --git a/src/hdy-title-bar.h b/src/hdy-title-bar.h new file mode 100644 index 00000000..b73bc7d0 --- /dev/null +++ b/src/hdy-title-bar.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2018 Purism SPC + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define HDY_TYPE_TITLE_BAR (hdy_title_bar_get_type()) + +G_DECLARE_FINAL_TYPE (HdyTitleBar, hdy_title_bar, HDY, TITLE_BAR, GtkBin) + +HdyTitleBar *hdy_title_bar_new (void); + +gboolean hdy_title_bar_get_selection_mode (HdyTitleBar *self); +void hdy_title_bar_set_selection_mode (HdyTitleBar *self, + gboolean selection_mode); + +G_END_DECLS diff --git a/src/meson.build b/src/meson.build index e750f1e2..ca4b89f8 100644 --- a/src/meson.build +++ b/src/meson.build @@ -53,6 +53,7 @@ src_headers = [ 'hdy-fold.h', 'hdy-leaflet.h', 'hdy-string-utf8.h', + 'hdy-title-bar.h', ] src_sources = [ @@ -65,6 +66,7 @@ src_sources = [ 'hdy-fold.c', 'hdy-leaflet.c', 'hdy-string-utf8.c', + 'hdy-title-bar.c', ] libhandy_public_headers += files(src_headers) -- GitLab From a4a4cceda834b08805834c30a90f8bd9eb8d6c71 Mon Sep 17 00:00:00 2001 From: Adrien Plazas Date: Fri, 7 Sep 2018 16:33:01 +0200 Subject: [PATCH 2/3] example: Don't draw a background on nested headerbars This is needed to let the titlebar handle rendering its background. --- examples/style.css | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/examples/style.css b/examples/style.css index 6c244f39..be0921ca 100644 --- a/examples/style.css +++ b/examples/style.css @@ -21,3 +21,12 @@ separator.sidebar.selection-mode, .selection-mode separator.sidebar { background-color: mix(@theme_selected_bg_color, #000, 0.2); } + +/* Needed for HdyTitleBar to work. This has been merged upstream but no release + * include it yet, it is likely to be released as part of GTK+ 3.24.1. + */ +headerbar.titlebar headerbar:not(.titlebar) { + background-color: rgba (0, 0, 0, 0); + background-image: none; + box-shadow: none; +} -- GitLab From 662d7a5b241f0591afb00232fc5b34050a79eeaa Mon Sep 17 00:00:00 2001 From: Adrien Plazas Date: Mon, 10 Sep 2018 09:34:40 +0200 Subject: [PATCH 3/3] example: Use HdyTitleBar --- examples/example-window.ui | 95 ++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 45 deletions(-) diff --git a/examples/example-window.ui b/examples/example-window.ui index 050c07e7..7e57f4a1 100644 --- a/examples/example-window.ui +++ b/examples/example-window.ui @@ -25,65 +25,70 @@ 576 - + True - slide - slide - - + True - False - Handy Widget Factory - - - sidebar - - - - - True - False - vertical - - - - - - True - True - True + slide + slide + - + + True False - False - center - True - + Handy Widget Factory + + + sidebar + + + + + True + False + vertical - - - Back - - + + + + + True + True + True - - True + False - go-previous-symbolic - 1 + False + center + True + + + + + Back + + + + + True + False + go-previous-symbolic + 1 + + + + content + - - content - -- GitLab