diff --git a/data/glade/libhandy.xml b/data/glade/libhandy.xml
index f9ffd7e04228684f77bc9f555c5a9d1129f14a22..f5d8abe4ab1bd13b1590ba0f8d9c657dc47648e3 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 f2d3866942616cbea5376a9787ad7599c71a685c..e474d6054b869244fbf9a0950651ddb46a7ee011 100644
--- a/doc/handy-docs.xml
+++ b/doc/handy-docs.xml
@@ -42,6 +42,7 @@
+
diff --git a/examples/example-window.ui b/examples/example-window.ui
index 050c07e741e2a5dbd4a575ad113362390032d88e..7e57f4a12b7c6437bd7dd0c5f47d5601d613dd7a 100644
--- a/examples/example-window.ui
+++ b/examples/example-window.ui
@@ -25,65 +25,70 @@
576
-
diff --git a/examples/style.css b/examples/style.css
index 6c244f39baa24706c2ab0854243f4e99542de2be..be0921ca884c7b301a54dd0f26394a9167e1dae5 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;
+}
diff --git a/src/handy.h b/src/handy.h
index 285210e1708e8f319f78aeb57d4882d22c674a3f..97c67720691cc67ac8ea6fe011e3b9f4ddd14c2b 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 0000000000000000000000000000000000000000..7fe531597c097bca8b34b939a402f4cbc233059a
--- /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 0000000000000000000000000000000000000000..b73bc7d013deeb0e164c4ed60f236cfae386fec2
--- /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 e750f1e2b6a928bfeaa46eefac238f4851b08188..ca4b89f82cd26b351a1d9c5b58858dc0fe44d170 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)