[evolution] EShell: Use a header bar as title bar
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution] EShell: Use a header bar as title bar
- Date: Thu, 21 Jul 2022 11:49:06 +0000 (UTC)
commit 38a531c503b54c3d457baff1c2ebab46848f11cc
Author: Cédric Bellegarde <cedric bellegarde adishatz org>
Date: Mon Jun 13 17:21:23 2022 +0200
EShell: Use a header bar as title bar
- Move common actions from toolbar to headerbar
- Add EHeaderBarButton to mimic and replace EMenuToolButton
- Add EShellHeaderBar for "New button" handling
data/ui/evolution-calendars.ui | 6 +-
data/ui/evolution-mail-reader.ui | 21 +-
.../evolution-shell/evolution-shell-docs.sgml.in | 5 +
.../evolution-util/evolution-util-docs.sgml.in | 3 +-
src/e-util/CMakeLists.txt | 6 +-
src/e-util/e-headerbar-button.c | 484 +++++++++++++++++++++
src/e-util/e-headerbar-button.h | 68 +++
src/e-util/e-menu-tool-action.c | 57 ---
src/e-util/e-menu-tool-action.h | 73 ----
src/e-util/e-menu-tool-button.c | 276 ------------
src/e-util/e-menu-tool-button.h | 78 ----
src/e-util/e-util.h | 3 +-
src/mail/e-mail-reader.c | 176 ++++++--
src/mail/e-mail-reader.h | 3 +
src/modules/calendar/e-cal-shell-view-private.c | 37 ++
src/modules/calendar/e-cal-shell-view-private.h | 1 +
src/modules/mail/e-mail-shell-view-private.c | 113 +++--
src/modules/mail/e-mail-shell-view-private.h | 5 +-
src/shell/CMakeLists.txt | 2 +
src/shell/e-shell-headerbar.c | 310 +++++++++++++
src/shell/e-shell-headerbar.h | 59 +++
src/shell/e-shell-window-private.c | 54 ++-
src/shell/e-shell-window-private.h | 5 +-
src/shell/e-shell-window.c | 92 +---
24 files changed, 1214 insertions(+), 723 deletions(-)
---
diff --git a/data/ui/evolution-calendars.ui b/data/ui/evolution-calendars.ui
index fa68e3b84d..7aa1ca3159 100644
--- a/data/ui/evolution-calendars.ui
+++ b/data/ui/evolution-calendars.ui
@@ -1,4 +1,4 @@
-<ui evolution-ui-version="1.0">
+<ui evolution-ui-version="1.1">
<menubar name='main-menu'>
<menu action='file-menu'>
<placeholder name='file-actions'>
@@ -59,10 +59,6 @@
<toolitem action='calendar-print'/>
<toolitem action='delete-selection'/>
<separator/>
- <toolitem action='calendar-go-back'/>
- <toolitem action='calendar-go-today'/>
- <toolitem action='calendar-go-forward'/>
- <separator/>
<toolitem action='calendar-jump-to'/>
<separator/>
<toolitem action='calendar-view-day'/>
diff --git a/data/ui/evolution-mail-reader.ui b/data/ui/evolution-mail-reader.ui
index 07659a37f9..f135d7c355 100644
--- a/data/ui/evolution-mail-reader.ui
+++ b/data/ui/evolution-mail-reader.ui
@@ -1,4 +1,4 @@
-<ui evolution-ui-version="1.1">
+<ui evolution-ui-version="1.2">
<accelerator action='mail-smart-backward'/>
<accelerator action='mail-smart-forward'/>
@@ -137,25 +137,6 @@
</menubar>
<toolbar name='main-toolbar'>
<placeholder name='mail-toolbar-common'>
- <toolitem action='mail-reply-sender'/>
- <toolitem action='mail-reply-group'>
- <menu action='mail-reply-group-menu'>
- <menuitem action='mail-reply-all'/>
- <menuitem action='mail-reply-list'/>
- <menu action='mail-reply-template'/>
- <menuitem action='mail-reply-alternative'/>
- </menu>
- </toolitem>
- <toolitem action='mail-forward'>
- <menu action='mail-forward-as-menu'>
- <menuitem action='mail-forward-attached-full'/>
- <menuitem action='mail-forward-inline-full'/>
- <menuitem action='mail-forward-quoted-full'/>
- <separator/>
- <menuitem action='mail-redirect'/>
- </menu>
- </toolitem>
- <separator/>
<toolitem action='mail-print'/>
<toolitem action='mail-delete'/>
<toolitem action='mail-mark-junk'/>
diff --git a/docs/reference/evolution-shell/evolution-shell-docs.sgml.in
b/docs/reference/evolution-shell/evolution-shell-docs.sgml.in
index cd7e054639..760c105bac 100644
--- a/docs/reference/evolution-shell/evolution-shell-docs.sgml.in
+++ b/docs/reference/evolution-shell/evolution-shell-docs.sgml.in
@@ -24,6 +24,7 @@
<xi:include href="xml/e-shell-taskbar.xml"/>
<xi:include href="xml/e-shell-searchbar.xml"/>
<xi:include href="xml/e-shell-switcher.xml"/>
+ <xi:include href="xml/e-shell-headerbar.xml"/>
<xi:include href="xml/e-shell-utils.xml"/>
</chapter>
@@ -41,6 +42,10 @@
<title>Index</title>
<xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
</index>
+ <index id="api-index-3-46" role="3.46">
+ <title>Index of new symbols in 3.46</title>
+ <xi:include href="xml/api-index-3.46.xml"><xi:fallback /></xi:include>
+ </index>
<index id="api-index-3-42" role="3.42">
<title>Index of new symbols in 3.42</title>
<xi:include href="xml/api-index-3.42.xml"><xi:fallback /></xi:include>
diff --git a/docs/reference/evolution-util/evolution-util-docs.sgml.in
b/docs/reference/evolution-util/evolution-util-docs.sgml.in
index 9b6386d0f2..5b9898aedd 100644
--- a/docs/reference/evolution-util/evolution-util-docs.sgml.in
+++ b/docs/reference/evolution-util/evolution-util-docs.sgml.in
@@ -277,14 +277,13 @@
<xi:include href="xml/e-ellipsized-combo-box-text.xml"/>
<xi:include href="xml/e-file-utils.xml"/>
<xi:include href="xml/e-focus-tracker.xml"/>
+ <xi:include href="xml/e-headerbar-button.xml"/>
<xi:include href="xml/e-image-chooser.xml"/>
<xi:include href="xml/e-import-assistant.xml"/>
<xi:include href="xml/e-interval-chooser.xml"/>
<xi:include href="xml/e-mail-identity-combo-box.xml"/>
<xi:include href="xml/e-map.xml"/>
<xi:include href="xml/e-markdown-editor.xml"/>
- <xi:include href="xml/e-menu-tool-action.xml"/>
- <xi:include href="xml/e-menu-tool-button.xml"/>
<xi:include href="xml/e-mktemp.xml"/>
<xi:include href="xml/e-month-widget.xml"/>
<xi:include href="xml/e-name-selector-dialog.xml"/>
diff --git a/src/e-util/CMakeLists.txt b/src/e-util/CMakeLists.txt
index 46588be512..1b8a3dd1da 100644
--- a/src/e-util/CMakeLists.txt
+++ b/src/e-util/CMakeLists.txt
@@ -172,8 +172,7 @@ set(SOURCES
e-markdown-editor.c
e-markdown-utils.c
e-marshal.c
- e-menu-tool-action.c
- e-menu-tool-button.c
+ e-headerbar-button.c
e-misc-utils.c
e-mktemp.c
e-month-widget.c
@@ -450,8 +449,7 @@ set(HEADERS
e-map.h
e-markdown-editor.h
e-markdown-utils.h
- e-menu-tool-action.h
- e-menu-tool-button.h
+ e-headerbar-button.h
e-misc-utils.h
e-mktemp.h
e-month-widget.h
diff --git a/src/e-util/e-headerbar-button.c b/src/e-util/e-headerbar-button.c
new file mode 100644
index 0000000000..c443af1c93
--- /dev/null
+++ b/src/e-util/e-headerbar-button.c
@@ -0,0 +1,484 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * SPDX-FileCopyrightText: (C) 1999-2008 Novell, Inc. (www.novell.com)
+ * SPDX-FileCopyrightText: (C) 2022 Cédric Bellegarde <cedric bellegarde adishatz org>
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+/* EHeaderBarButton is a collection of buttons (one main button and additionnal actions)
+ * with an optional dropdown menu.
+ *
+ * You can use e_header_bar_button_new() to get a new #EHeaderBarButton. If you do not provide
+ * an action, prefered action will be used. See e_header_bar_button_take_menu().
+ */
+
+#include "evolution-config.h"
+
+#include "e-headerbar-button.h"
+#include "e-misc-utils.h"
+
+#include <glib/gi18n.h>
+
+struct _EHeaderBarButtonPrivate {
+ GtkWidget *button;
+ GtkWidget *dropdown_button;
+
+ GtkAction *action;
+ gchar *label;
+ gchar *prefer_item;
+};
+
+enum {
+ PROP_0,
+ PROP_PREFER_ITEM,
+ PROP_LABEL,
+ PROP_ACTION
+};
+
+G_DEFINE_TYPE_WITH_CODE (EHeaderBarButton, e_header_bar_button, GTK_TYPE_BOX,
+ G_ADD_PRIVATE (EHeaderBarButton))
+
+static GtkAction *
+header_bar_button_get_prefer_action (EHeaderBarButton *header_bar_button)
+{
+ GtkMenu *menu;
+ GList *children;
+ GtkAction *action = NULL;
+ GList *link;
+ const gchar *prefer_item;
+
+ if (header_bar_button->priv->dropdown_button == NULL)
+ return NULL;
+
+ menu = gtk_menu_button_get_popup ( GTK_MENU_BUTTON (header_bar_button->priv->dropdown_button));
+ g_return_val_if_fail (menu != NULL, NULL);
+
+ children = gtk_container_get_children (GTK_CONTAINER (menu));
+ g_return_val_if_fail (children != NULL, NULL);
+
+ prefer_item = header_bar_button->priv->prefer_item;
+
+ for (link = children; link != NULL; link = g_list_next (link)) {
+ GtkWidget *child;
+ const gchar *name;
+
+ child = GTK_WIDGET (link->data);
+
+ if (!GTK_IS_MENU_ITEM (child))
+ continue;
+
+ action = gtk_activatable_get_related_action (
+ GTK_ACTIVATABLE (child));
+
+ if (action == NULL)
+ continue;
+
+ name = gtk_action_get_name (action);
+
+ if (prefer_item == NULL ||
+ *prefer_item == '\0' ||
+ g_strcmp0 (name, prefer_item) == 0) {
+ break;
+ }
+ }
+
+ g_list_free (children);
+
+ return action;
+}
+
+static void
+header_bar_button_update_button_for_action (GtkButton *button,
+ GtkAction *action)
+{
+ GtkWidget *image;
+ GtkStyleContext *style_context;
+ const gchar *tooltip;
+ const gchar *icon_name;
+
+ g_return_if_fail (button != NULL);
+ g_return_if_fail (action != NULL);
+
+ tooltip = gtk_action_get_tooltip (action);
+ gtk_widget_set_tooltip_text (GTK_WIDGET (button), tooltip);
+
+ icon_name = gtk_action_get_icon_name (action);
+ if (icon_name == NULL) {
+ GIcon *icon = gtk_action_get_gicon (action);
+ image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_BUTTON);
+ } else {
+ image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_BUTTON);
+ }
+ gtk_widget_set_margin_end (image, 2);
+ gtk_button_set_image (GTK_BUTTON (button), image);
+ gtk_widget_show (image);
+
+ /* Force text button class to fix some themes */
+ style_context = gtk_widget_get_style_context (GTK_WIDGET (button));
+ gtk_style_context_add_class (style_context, "text-button");
+}
+
+static void
+header_bar_button_update_button (EHeaderBarButton *header_bar_button)
+{
+ GtkAction *action;
+
+ if (header_bar_button->priv->action == NULL)
+ action = header_bar_button_get_prefer_action (header_bar_button);
+ else
+ action = header_bar_button->priv->action;
+
+ if (action != NULL) {
+ header_bar_button_update_button_for_action (
+ GTK_BUTTON (header_bar_button->priv->button),
+ action);
+ }
+}
+
+static void
+header_bar_button_clicked (EHeaderBarButton *header_bar_button)
+{
+ GtkAction *action;
+
+ if (header_bar_button->priv->action == NULL)
+ action = header_bar_button_get_prefer_action (header_bar_button);
+ else
+ action = header_bar_button->priv->action;
+
+ if (action != NULL)
+ gtk_action_activate (action);
+}
+
+static void
+header_bar_button_set_prefer_item (EHeaderBarButton *self,
+ const gchar *prefer_item)
+{
+ g_return_if_fail (E_IS_HEADER_BAR_BUTTON (self));
+
+ if (g_strcmp0 (self->priv->prefer_item, prefer_item) == 0)
+ return;
+
+ g_free (self->priv->prefer_item);
+
+ self->priv->prefer_item = g_strdup (prefer_item);
+ header_bar_button_update_button (self);
+}
+
+static void
+header_bar_button_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EHeaderBarButton *header_bar_button = E_HEADER_BAR_BUTTON (object);
+
+ switch (property_id) {
+ case PROP_PREFER_ITEM:
+ header_bar_button_set_prefer_item (
+ header_bar_button,
+ g_value_get_string (value));
+ return;
+ case PROP_LABEL:
+ if (header_bar_button->priv->label == NULL)
+ header_bar_button->priv->label = g_value_dup_string (value);
+ return;
+ case PROP_ACTION:
+ header_bar_button->priv->action = g_value_get_object (value);
+ if (header_bar_button->priv->action != NULL)
+ g_object_ref (header_bar_button->priv->action);
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+header_bar_button_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EHeaderBarButton *header_bar_button = E_HEADER_BAR_BUTTON (object);
+
+ switch (property_id) {
+ case PROP_PREFER_ITEM:
+ g_value_set_string (
+ value, header_bar_button->priv->prefer_item);
+ return;
+ case PROP_LABEL:
+ g_value_set_string (value, header_bar_button->priv->label);
+ return;
+ case PROP_ACTION:
+ g_value_set_object (value, header_bar_button->priv->action);
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+header_bar_button_constructed (GObject *object)
+{
+ EHeaderBarButton *header_bar_button = E_HEADER_BAR_BUTTON (object);
+ GtkStyleContext *style_context;
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_header_bar_button_parent_class)->constructed (object);
+
+ if (GTK_IS_TOGGLE_ACTION (header_bar_button->priv->action)) {
+ header_bar_button->priv->button = gtk_toggle_button_new_with_label (
+ header_bar_button->priv->label);
+ } else {
+ header_bar_button->priv->button = gtk_button_new_with_label (
+ header_bar_button->priv->label);
+ }
+ gtk_widget_show (header_bar_button->priv->button);
+
+ gtk_box_pack_start (
+ GTK_BOX (header_bar_button), header_bar_button->priv->button,
+ FALSE, FALSE, 0);
+
+ if (header_bar_button->priv->action != NULL) {
+ header_bar_button_update_button_for_action (
+ GTK_BUTTON (header_bar_button->priv->button),
+ header_bar_button->priv->action);
+
+ e_binding_bind_property (
+ header_bar_button->priv->action, "sensitive",
+ header_bar_button, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ if (GTK_IS_TOGGLE_ACTION (header_bar_button->priv->action))
+ e_binding_bind_property (
+ header_bar_button->priv->action, "active",
+ header_bar_button->priv->button, "active",
+ G_BINDING_SYNC_CREATE);
+ }
+
+ /* TODO: GTK4 port: do not use linked buttons
+ * https://developer.gnome.org/hig/patterns/containers/header-bars.html#button-grouping */
+ style_context = gtk_widget_get_style_context (GTK_WIDGET (header_bar_button));
+ gtk_style_context_add_class (style_context, "linked");
+
+ g_signal_connect_swapped (
+ header_bar_button->priv->button, "clicked",
+ G_CALLBACK (header_bar_button_clicked), header_bar_button);
+}
+
+static void
+header_bar_button_finalize (GObject *object)
+{
+ EHeaderBarButton *header_bar_button = E_HEADER_BAR_BUTTON (object);
+
+ g_free (header_bar_button->priv->prefer_item);
+ g_free (header_bar_button->priv->label);
+
+ if (header_bar_button->priv->action != NULL)
+ g_object_unref (header_bar_button->priv->action);
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_header_bar_button_parent_class)->finalize (object);
+}
+
+static void
+e_header_bar_button_class_init (EHeaderBarButtonClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = header_bar_button_set_property;
+ object_class->get_property = header_bar_button_get_property;
+ object_class->constructed = header_bar_button_constructed;
+ object_class->finalize = header_bar_button_finalize;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_PREFER_ITEM,
+ g_param_spec_string (
+ "prefer-item",
+ "Prefer Item",
+ "Name of an item to show instead of the first",
+ NULL,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_LABEL,
+ g_param_spec_string (
+ "label",
+ "Label",
+ "Button label",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_ACTION,
+ g_param_spec_object (
+ "action",
+ "Action",
+ "Button action",
+ GTK_TYPE_ACTION,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+e_header_bar_button_init (EHeaderBarButton *self)
+{
+ self->priv = e_header_bar_button_get_instance_private (self);
+
+ self->priv->dropdown_button = NULL;
+ self->priv->prefer_item = NULL;
+ self->priv->label = NULL;
+}
+
+/**
+ * e_header_bar_button_new:
+ * @action: An action overriding menu default action
+ *
+ * Creates a new #EHeaderBarButton labeled with @label.
+ * If action is %NULL, button will use default preferred menu action if available,
+ * see e_header_bar_button_take_menu().
+ *
+ * Returns: (transfer full): a new #EHeaderBarButton
+ *
+ * Since: 3.46
+ **/
+GtkWidget*
+e_header_bar_button_new (const gchar *label,
+ GtkAction *action)
+{
+ return g_object_new (E_TYPE_HEADER_BAR_BUTTON,
+ "label", label,
+ "action", action, NULL);
+}
+
+/**
+ * e_header_bar_button_add_action:
+ * @header_bar_button: #EHeaderBarButton
+ * @label: The text you want the #GtkButton to hold.
+ * @action: #GtkButton related action
+ *
+ * Adds a new button with a related action.
+ *
+ * Since: 3.46
+ **/
+void
+e_header_bar_button_add_action (EHeaderBarButton *header_bar_button,
+ const gchar *label,
+ GtkAction *action)
+{
+ GtkWidget *button;
+
+ g_return_if_fail (E_IS_HEADER_BAR_BUTTON (header_bar_button));
+ g_return_if_fail (GTK_IS_ACTION (action));
+
+ if (GTK_IS_TOGGLE_ACTION (action))
+ button = gtk_toggle_button_new_with_label (label);
+ else
+ button = gtk_button_new_with_label (label);
+
+ gtk_widget_show (button);
+ gtk_box_pack_start (GTK_BOX (header_bar_button), button, FALSE, FALSE, 0);
+
+ e_binding_bind_property (
+ action, "sensitive",
+ button, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ if (GTK_IS_TOGGLE_ACTION (action))
+ e_binding_bind_property (
+ action, "active",
+ button, "active",
+ G_BINDING_SYNC_CREATE);
+
+ g_signal_connect_swapped (
+ button, "clicked",
+ G_CALLBACK (gtk_action_activate), action);
+
+ header_bar_button_update_button_for_action (GTK_BUTTON (button), action);
+}
+
+/**
+ * e_header_bar_button_take_menu:
+ * @header_bar_button: #EHeaderBarButton
+ * @menu: (transfer full) (nullable): A #GtkMenu, or %NULL to unset and disable the dropdown button.
+ *
+ * Sets the #GtkMenu that will be popped up when the @menu_button is clicked, or
+ * %NULL to dissociate any existing menu and disable the dropdown button.
+ *
+ * If current #EHeaderBarButton action is %NULL, clicking the button will fire
+ * the preferred item, if set, or the first menu item otherwise.
+ *
+ * Since: 3.46
+ **/
+void
+e_header_bar_button_take_menu (EHeaderBarButton *header_bar_button,
+ GtkWidget *menu)
+{
+ g_return_if_fail (E_IS_HEADER_BAR_BUTTON (header_bar_button));
+
+ if (!GTK_IS_MENU (menu)) {
+ if (header_bar_button->priv->dropdown_button != NULL)
+ gtk_widget_hide (header_bar_button->priv->dropdown_button);
+ return;
+ }
+
+ if (header_bar_button->priv->dropdown_button == NULL) {
+ header_bar_button->priv->dropdown_button = gtk_menu_button_new ();
+ gtk_box_pack_end (
+ GTK_BOX (header_bar_button), header_bar_button->priv->dropdown_button,
+ FALSE, FALSE, 0);
+ }
+
+ gtk_menu_button_set_popup (GTK_MENU_BUTTON (header_bar_button->priv->dropdown_button), menu);
+
+ header_bar_button_update_button (header_bar_button);
+
+ gtk_widget_show (header_bar_button->priv->dropdown_button);
+}
+
+/**
+ * e_header_bar_button_css_add_class:
+ * @header_bar_button: #EHeaderBarButton
+ * @class_name: a CSS class name
+ *
+ * Adds a CSS class to #EHeaderBarButton main button
+ *
+ * Since: 3.46
+ **/
+void e_header_bar_button_css_add_class (EHeaderBarButton *header_bar_button,
+ const gchar *class_name)
+{
+ GtkStyleContext *style_context;
+
+ g_return_if_fail (E_IS_HEADER_BAR_BUTTON (header_bar_button));
+
+ style_context = gtk_widget_get_style_context (header_bar_button->priv->button);
+ gtk_style_context_add_class (style_context, class_name);
+}
+
+/**
+ * e_header_bar_button_add_accelerator:
+ * @header_bar_button: #EHeaderBarButton to activate
+ * @accel_group: Accel group for this widget, added to its toplevel.
+ * @accel_key: GDK keyval of the accelerator
+ * @accel_mods: Modifier key combination of the accelerator.
+ * @accel_flags: Flag accelerators, e.g. GTK_ACCEL_VISIBLE
+ *
+ * Installs an accelerator for main action
+ *
+ * Since: 3.46
+ **/
+void e_header_bar_button_add_accelerator (EHeaderBarButton *header_bar_button,
+ GtkAccelGroup* accel_group,
+ guint accel_key,
+ GdkModifierType accel_mods,
+ GtkAccelFlags accel_flags)
+{
+ g_return_if_fail (E_IS_HEADER_BAR_BUTTON (header_bar_button));
+
+ gtk_widget_add_accelerator (
+ GTK_WIDGET (header_bar_button->priv->button), "clicked",
+ accel_group, accel_key, accel_mods, accel_flags);
+}
diff --git a/src/e-util/e-headerbar-button.h b/src/e-util/e-headerbar-button.h
new file mode 100644
index 0000000000..44dcae44d0
--- /dev/null
+++ b/src/e-util/e-headerbar-button.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * SPDX-FileCopyrightText: (C) 1999-2008 Novell, Inc. (www.novell.com)
+ * SPDX-FileCopyrightText: (C) 2022 Cédric Bellegarde <cedric bellegarde adishatz org>
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_HEADER_BAR_BUTTON_H
+#define E_HEADER_BAR_BUTTON_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_HEADER_BAR_BUTTON \
+ (e_header_bar_button_get_type ())
+#define E_HEADER_BAR_BUTTON(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_HEADER_BAR_BUTTON, EHeaderBarButton))
+#define E_HEADER_BAR_BUTTON_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_HEADER_BAR_BUTTON, EHeaderBarButtonClass))
+#define E_IS_HEADER_BAR_BUTTON(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_HEADER_BAR_BUTTON))
+#define E_IS_HEADER_BAR_BUTTON_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_HEADER_BAR_BUTTON))
+#define E_HEADER_BAR_BUTTON_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_HEADER_BAR_BUTTON, EHeaderBarButtonClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHeaderBarButton EHeaderBarButton;
+typedef struct _EHeaderBarButtonClass EHeaderBarButtonClass;
+typedef struct _EHeaderBarButtonPrivate EHeaderBarButtonPrivate;
+
+struct _EHeaderBarButton {
+ GtkBox parent;
+ EHeaderBarButtonPrivate *priv;
+};
+
+struct _EHeaderBarButtonClass {
+ GtkBoxClass parent_class;
+};
+
+GType e_header_bar_button_get_type (void) G_GNUC_CONST;
+GtkWidget* e_header_bar_button_new (const gchar *label,
+ GtkAction *action);
+void e_header_bar_button_add_action (EHeaderBarButton *header_bar_button,
+ const gchar *label,
+ GtkAction *action);
+void e_header_bar_button_take_menu (EHeaderBarButton *header_bar_button,
+ GtkWidget *menu);
+void e_header_bar_button_css_add_class (EHeaderBarButton *header_bar_button,
+ const gchar *class);
+void e_header_bar_button_add_accelerator (EHeaderBarButton* header_bar_button,
+ GtkAccelGroup* accel_group,
+ guint accel_key,
+ GdkModifierType accel_mods,
+ GtkAccelFlags accel_flags);
+G_END_DECLS
+
+#endif /* E_HEADER_BAR_BUTTON_H */
diff --git a/src/e-util/e-util.h b/src/e-util/e-util.h
index d580d14382..4ba1469e37 100644
--- a/src/e-util/e-util.h
+++ b/src/e-util/e-util.h
@@ -155,8 +155,7 @@
#include <e-util/e-map.h>
#include <e-util/e-markdown-editor.h>
#include <e-util/e-markdown-utils.h>
-#include <e-util/e-menu-tool-action.h>
-#include <e-util/e-menu-tool-button.h>
+#include <e-util/e-headerbar-button.h>
#include <e-util/e-misc-utils.h>
#include <e-util/e-mktemp.h>
#include <e-util/e-month-widget.h>
diff --git a/src/mail/e-mail-reader.c b/src/mail/e-mail-reader.c
index f387d5afd8..5b696e8dd3 100644
--- a/src/mail/e-mail-reader.c
+++ b/src/mail/e-mail-reader.c
@@ -29,6 +29,7 @@
#include <X11/XF86keysym.h>
#endif
+#include <shell/e-shell-headerbar.h>
#include <shell/e-shell-utils.h>
#include <libemail-engine/libemail-engine.h>
@@ -2636,6 +2637,13 @@ static GtkActionEntry mail_reader_entries[] = {
N_("Flag the selected messages for follow-up"),
G_CALLBACK (action_mail_flag_for_followup_cb) },
+ { "mail-forward",
+ "mail-forward",
+ N_("_Forward"),
+ "<Control>f",
+ N_("Forward the selected message to someone"),
+ G_CALLBACK (action_mail_forward_cb) },
+
{ "mail-forward-attached",
NULL,
N_("_Attached"),
@@ -2904,6 +2912,13 @@ static GtkActionEntry mail_reader_entries[] = {
N_("Choose reply options for the selected message"),
G_CALLBACK (action_mail_reply_alternative_cb) },
+ { "mail-reply-group",
+ "mail-reply-all",
+ N_("Group Reply"),
+ "<Control>g",
+ N_("Reply to the mailing list, or to all recipients"),
+ G_CALLBACK (action_mail_reply_group_cb) },
+
{ "mail-reply-list",
NULL,
N_("Reply to _List"),
@@ -5158,7 +5173,6 @@ e_mail_reader_init (EMailReader *reader,
gboolean init_actions,
gboolean connect_signals)
{
- EMenuToolAction *menu_tool_action;
GtkActionGroup *action_group;
GtkWidget *message_list;
GtkAction *action;
@@ -5190,44 +5204,6 @@ e_mail_reader_init (EMailReader *reader,
action_group = e_mail_reader_get_action_group (
reader, E_MAIL_READER_ACTION_GROUP_STANDARD);
- /* The "mail-forward" action is special: it uses a GtkMenuToolButton
- * for its toolbar item type. So we have to create it separately. */
-
- menu_tool_action = e_menu_tool_action_new (
- "mail-forward", _("_Forward"),
- _("Forward the selected message to someone"));
-
- gtk_action_set_icon_name (
- GTK_ACTION (menu_tool_action), "mail-forward");
-
- g_signal_connect (
- menu_tool_action, "activate",
- G_CALLBACK (action_mail_forward_cb), reader);
-
- gtk_action_group_add_action_with_accel (
- action_group, GTK_ACTION (menu_tool_action), "<Control>f");
-
- /* Likewise the "mail-reply-group" action. */
-
- menu_tool_action = e_menu_tool_action_new (
- /* For Translators: "Group Reply" will reply either to a mailing list
- * (if possible and if that configuration option is enabled), or else
- * it will reply to all. The word "Group" was chosen because it covers
- * either of those, without too strongly implying one or the other. */
- "mail-reply-group", _("Group Reply"),
- _("Reply to the mailing list, or to all recipients"));
-
- gtk_action_set_icon_name (
- GTK_ACTION (menu_tool_action), "mail-reply-all");
-
- g_signal_connect (
- menu_tool_action, "activate",
- G_CALLBACK (action_mail_reply_group_cb), reader);
-
- gtk_action_group_add_action_with_accel (
- action_group, GTK_ACTION (menu_tool_action), "<Control>g");
-
- /* Add the other actions the normal way. */
gtk_action_group_add_actions (
action_group, mail_reader_entries,
G_N_ELEMENTS (mail_reader_entries), reader);
@@ -5285,11 +5261,9 @@ e_mail_reader_init (EMailReader *reader,
action_name = "mail-forward";
action = e_mail_reader_get_action (reader, action_name);
- gtk_action_set_is_important (action, TRUE);
action_name = "mail-reply-group";
action = e_mail_reader_get_action (reader, action_name);
- gtk_action_set_is_important (action, TRUE);
action_name = "mail-next";
action = e_mail_reader_get_action (reader, action_name);
@@ -5301,11 +5275,9 @@ e_mail_reader_init (EMailReader *reader,
action_name = "mail-reply-all";
action = e_mail_reader_get_action (reader, action_name);
- gtk_action_set_is_important (action, TRUE);
action_name = "mail-reply-sender";
action = e_mail_reader_get_action (reader, action_name);
- gtk_action_set_is_important (action, TRUE);
gtk_action_set_short_label (action, _("Reply"));
action_name = "add-to-address-book";
@@ -6307,3 +6279,121 @@ e_mail_reader_remove_ui (EMailReader *reader)
iface->remove_ui (reader);
}
+
+/**
+ * e_mail_reader_create_reply_menu:
+ * @reader: A #EMailReader
+ *
+ * Get reply menu
+ *
+ * Returns: (transfer full): A new #GtkMenu
+ *
+ * Since: 3.46
+ **/
+GtkWidget *
+e_mail_reader_create_reply_menu (EMailReader *reader)
+{
+ GtkWindow *window;
+ GtkWidget *menu;
+ GtkAction *action;
+ GtkAccelGroup *accel_group;
+ GtkUIManager *ui_manager;
+
+ menu = gtk_menu_new ();
+
+ window = e_mail_reader_get_window (reader);
+ g_return_val_if_fail (window != NULL, menu);
+
+ if (E_IS_SHELL_WINDOW (window))
+ ui_manager = e_shell_window_get_ui_manager (E_SHELL_WINDOW (window));
+ else if (E_IS_MAIL_BROWSER (window))
+ ui_manager = e_mail_browser_get_ui_manager (E_MAIL_BROWSER (window));
+ else
+ return menu;
+
+ accel_group = gtk_ui_manager_get_accel_group (ui_manager);
+
+ action = e_mail_reader_get_action (reader, "mail-reply-all");
+ gtk_action_set_accel_group (action, accel_group);
+ gtk_menu_shell_append (
+ GTK_MENU_SHELL (menu),
+ gtk_action_create_menu_item (action));
+
+ action = e_mail_reader_get_action (reader, "mail-reply-list");
+ gtk_action_set_accel_group (action, accel_group);
+ gtk_menu_shell_append (
+ GTK_MENU_SHELL (menu),
+ gtk_action_create_menu_item (action));
+
+ action = e_mail_reader_get_action (reader, "mail-reply-alternative");
+ gtk_action_set_accel_group (action, accel_group);
+ gtk_menu_shell_append (
+ GTK_MENU_SHELL (menu),
+ gtk_action_create_menu_item (action));
+
+ gtk_widget_show_all (menu);
+
+ return menu;
+}
+
+/**
+ * e_mail_reader_create_forward_menu:
+ * @reader: A #EMailReader
+ *
+ * Get forward menu
+ *
+ * Returns: (transfer full): A new #GtkMenu
+ *
+ * Since: 3.46
+ **/
+GtkWidget *
+e_mail_reader_create_forward_menu (EMailReader *reader)
+{
+ GtkWindow *window;
+ GtkWidget *menu;
+ GtkAction *action;
+ GtkAccelGroup *accel_group;
+ GtkUIManager *ui_manager;
+
+ menu = gtk_menu_new ();
+
+ window = e_mail_reader_get_window (reader);
+ g_return_val_if_fail (window != NULL, menu);
+
+ if (E_IS_SHELL_WINDOW (window))
+ ui_manager = e_shell_window_get_ui_manager (E_SHELL_WINDOW (window));
+ else if (E_IS_MAIL_BROWSER (window))
+ ui_manager = e_mail_browser_get_ui_manager (E_MAIL_BROWSER (window));
+ else
+ return menu;
+
+ accel_group = gtk_ui_manager_get_accel_group (ui_manager);
+
+ action = e_mail_reader_get_action (reader, "mail-forward-attached-full");
+ gtk_action_set_accel_group (action, accel_group);
+ gtk_menu_shell_append (
+ GTK_MENU_SHELL (menu),
+ gtk_action_create_menu_item (action));
+
+ action = e_mail_reader_get_action (reader, "mail-forward-inline-full");
+ gtk_action_set_accel_group (action, accel_group);
+ gtk_menu_shell_append (
+ GTK_MENU_SHELL (menu),
+ gtk_action_create_menu_item (action));
+
+ action = e_mail_reader_get_action (reader, "mail-forward-quoted-full");
+ gtk_action_set_accel_group (action, accel_group);
+ gtk_menu_shell_append (
+ GTK_MENU_SHELL (menu),
+ gtk_action_create_menu_item (action));
+
+ action = e_mail_reader_get_action (reader, "mail-redirect");
+ gtk_action_set_accel_group (action, accel_group);
+ gtk_menu_shell_append (
+ GTK_MENU_SHELL (menu),
+ gtk_action_create_menu_item (action));
+
+ gtk_widget_show_all (menu);
+
+ return menu;
+}
diff --git a/src/mail/e-mail-reader.h b/src/mail/e-mail-reader.h
index ce0ced4927..41ce3946c8 100644
--- a/src/mail/e-mail-reader.h
+++ b/src/mail/e-mail-reader.h
@@ -214,6 +214,9 @@ void e_mail_reader_composer_created (EMailReader *reader,
CamelMimeMessage *message);
void e_mail_reader_reload (EMailReader *reader);
void e_mail_reader_remove_ui (EMailReader *reader);
+GtkWidget * e_mail_reader_create_reply_menu (EMailReader *reader);
+GtkWidget * e_mail_reader_create_forward_menu
+ (EMailReader *reader);
G_END_DECLS
diff --git a/src/modules/calendar/e-cal-shell-view-private.c b/src/modules/calendar/e-cal-shell-view-private.c
index da0142958e..f01e288253 100644
--- a/src/modules/calendar/e-cal-shell-view-private.c
+++ b/src/modules/calendar/e-cal-shell-view-private.c
@@ -309,6 +309,37 @@ cal_shell_view_taskpad_settings_changed_cb (GSettings *settings,
}
}
+static void
+cal_shell_view_update_header_bar (ECalShellView *cal_shell_view)
+{
+ EShellWindow *shell_window;
+ EShellView *shell_view;
+ EShellHeaderBar *shell_headerbar;
+ GtkWidget *widget;
+ GtkAction *action;
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ shell_headerbar = E_SHELL_HEADER_BAR (gtk_window_get_titlebar (GTK_WINDOW (shell_window)));
+
+ e_shell_header_bar_clear (shell_headerbar, "e-cal-shell-view");
+ if (!e_shell_view_is_active (shell_view))
+ return;
+
+ action = ACTION (CALENDAR_GO_BACK);
+ widget = e_header_bar_button_new (NULL, action);
+ gtk_widget_set_name (widget, "e-cal-shell-view-buttons");
+ gtk_widget_show (widget);
+
+ action = ACTION (CALENDAR_GO_TODAY);
+ e_header_bar_button_add_action (E_HEADER_BAR_BUTTON (widget), NULL, action);
+
+ action = ACTION (CALENDAR_GO_FORWARD);
+ e_header_bar_button_add_action (E_HEADER_BAR_BUTTON (widget), NULL, action);
+
+ e_shell_header_bar_pack_end (shell_headerbar, widget);
+}
+
void
e_cal_shell_view_private_init (ECalShellView *cal_shell_view)
{
@@ -399,6 +430,12 @@ e_cal_shell_view_private_constructed (ECalShellView *cal_shell_view)
priv->client_cache = e_shell_get_client_cache (shell);
g_object_ref (priv->client_cache);
+ g_signal_connect_object (
+ cal_shell_view, "toggled",
+ G_CALLBACK (cal_shell_view_update_header_bar),
+ NULL,
+ G_CONNECT_AFTER);
+
handler_id = g_signal_connect (
priv->client_cache, "backend-error",
G_CALLBACK (cal_shell_view_backend_error_cb),
diff --git a/src/modules/calendar/e-cal-shell-view-private.h b/src/modules/calendar/e-cal-shell-view-private.h
index fa8ab91178..8385370217 100644
--- a/src/modules/calendar/e-cal-shell-view-private.h
+++ b/src/modules/calendar/e-cal-shell-view-private.h
@@ -28,6 +28,7 @@
#include <libecal/libecal.h>
+#include <shell/e-shell-headerbar.h>
#include <shell/e-shell-utils.h>
#include <calendar/gui/calendar-config.h>
diff --git a/src/modules/mail/e-mail-shell-view-private.c b/src/modules/mail/e-mail-shell-view-private.c
index ad161670c8..86fd1c70db 100644
--- a/src/modules/mail/e-mail-shell-view-private.c
+++ b/src/modules/mail/e-mail-shell-view-private.c
@@ -1465,40 +1465,28 @@ create_send_receive_submenu (EMailShellView *mail_shell_view)
void
e_mail_shell_view_update_send_receive_menus (EMailShellView *mail_shell_view)
{
- EMailShellViewPrivate *priv;
+ EMailShellContent *mail_shell_content;
EShellWindow *shell_window;
EShellView *shell_view;
+ EMailView *mail_view;
+ EShellHeaderBar *shell_headerbar;
GtkWidget *widget;
+ GtkAction *action;
+ const gchar *action_name;
const gchar *widget_path;
g_return_if_fail (E_IS_MAIL_SHELL_VIEW (mail_shell_view));
- priv = E_MAIL_SHELL_VIEW_GET_PRIVATE (mail_shell_view);
-
shell_view = E_SHELL_VIEW (mail_shell_view);
shell_window = e_shell_view_get_shell_window (shell_view);
+ shell_headerbar = E_SHELL_HEADER_BAR (gtk_window_get_titlebar (GTK_WINDOW (shell_window)));
- if (!e_shell_view_is_active (shell_view)) {
- if (priv->send_receive_tool_item) {
- GtkWidget *toolbar;
-
- toolbar = e_shell_window_get_managed_widget (
- shell_window, "/main-toolbar");
- g_return_if_fail (toolbar != NULL);
-
- gtk_container_remove (
- GTK_CONTAINER (toolbar),
- GTK_WIDGET (priv->send_receive_tool_item));
- gtk_container_remove (
- GTK_CONTAINER (toolbar),
- GTK_WIDGET (priv->send_receive_tool_separator));
-
- priv->send_receive_tool_item = NULL;
- priv->send_receive_tool_separator = NULL;
- }
-
+ e_shell_header_bar_clear (shell_headerbar, "e-mail-shell-view");
+ if (!e_shell_view_is_active (shell_view))
return;
- }
+
+ mail_shell_content = mail_shell_view->priv->mail_shell_content;
+ mail_view = e_mail_shell_content_get_mail_view (mail_shell_content);
widget_path =
"/main-menu/file-menu"
@@ -1509,46 +1497,45 @@ e_mail_shell_view_update_send_receive_menus (EMailShellView *mail_shell_view)
GTK_MENU_ITEM (widget),
create_send_receive_submenu (mail_shell_view));
- if (!priv->send_receive_tool_item) {
- GtkWidget *toolbar;
- GtkToolItem *tool_item;
- gint index;
-
- toolbar = e_shell_window_get_managed_widget (
- shell_window, "/main-toolbar");
- g_return_if_fail (toolbar != NULL);
-
- widget_path =
- "/main-toolbar/toolbar-actions/mail-send-receiver";
- widget = e_shell_window_get_managed_widget (
- shell_window, widget_path);
- g_return_if_fail (widget != NULL);
-
- index = gtk_toolbar_get_item_index (
- GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (widget));
-
- tool_item = gtk_separator_tool_item_new ();
- gtk_toolbar_insert (GTK_TOOLBAR (toolbar), tool_item, index);
- gtk_widget_show (GTK_WIDGET (tool_item));
- priv->send_receive_tool_separator = tool_item;
-
- tool_item = GTK_TOOL_ITEM (
- e_menu_tool_button_new (_("Send / Receive")));
- gtk_tool_item_set_is_important (tool_item, TRUE);
- gtk_toolbar_insert (GTK_TOOLBAR (toolbar), tool_item, index);
- gtk_widget_show (GTK_WIDGET (tool_item));
- priv->send_receive_tool_item = tool_item;
-
- e_binding_bind_property (
- ACTION (MAIL_SEND_RECEIVE), "sensitive",
- tool_item, "sensitive",
- G_BINDING_SYNC_CREATE);
- }
-
- if (priv->send_receive_tool_item)
- gtk_menu_tool_button_set_menu (
- GTK_MENU_TOOL_BUTTON (priv->send_receive_tool_item),
- create_send_receive_submenu (mail_shell_view));
+ widget = e_header_bar_button_new (_("Send / Receive"), ACTION (MAIL_SEND_RECEIVE));
+ gtk_widget_set_name (widget, "e-mail-shell-view-send-receive");
+ e_header_bar_button_take_menu (
+ E_HEADER_BAR_BUTTON (widget),
+ create_send_receive_submenu (mail_shell_view));
+ gtk_widget_show (widget);
+
+ e_shell_header_bar_pack_start (shell_headerbar, widget);
+
+ action_name = "mail-forward";
+ action = e_mail_reader_get_action (E_MAIL_READER (mail_view), action_name);
+ widget = e_header_bar_button_new (_("Forward"), action);
+ gtk_widget_set_name (widget, "e-mail-shell-view-forward");
+ e_header_bar_button_take_menu (
+ E_HEADER_BAR_BUTTON (widget),
+ e_mail_reader_create_forward_menu (E_MAIL_READER (mail_view)));
+ gtk_widget_show (widget);
+
+ e_shell_header_bar_pack_end (shell_headerbar, widget);
+
+ action_name = "mail-reply-group";
+ action = e_mail_reader_get_action (E_MAIL_READER (mail_view), action_name);
+ widget = e_header_bar_button_new (_("Group Reply"), action);
+ gtk_widget_set_name (widget, "e-mail-shell-view-reply-group");
+ gtk_widget_show (widget);
+
+ e_header_bar_button_take_menu (
+ E_HEADER_BAR_BUTTON (widget),
+ e_mail_reader_create_reply_menu (E_MAIL_READER (mail_view)));
+
+ e_shell_header_bar_pack_end (shell_headerbar, widget);
+
+ action_name = "mail-reply-sender";
+ action = e_mail_reader_get_action (E_MAIL_READER (mail_view), action_name);
+ widget = e_header_bar_button_new (_("Reply"), action);
+ gtk_widget_set_name (widget, "e-mail-shell-view-reply-sender");
+ gtk_widget_show (widget);
+
+ e_shell_header_bar_pack_end (shell_headerbar, widget);
}
static void
diff --git a/src/modules/mail/e-mail-shell-view-private.h b/src/modules/mail/e-mail-shell-view-private.h
index 512003654a..6a7a44bd4b 100644
--- a/src/modules/mail/e-mail-shell-view-private.h
+++ b/src/modules/mail/e-mail-shell-view-private.h
@@ -26,6 +26,8 @@
#include <glib/gi18n.h>
#include <camel/camel-search-private.h> /* for camel_search_word */
+#include "shell/e-shell-headerbar.h"
+
#include <mail/e-mail-folder-create-dialog.h>
#include <mail/e-mail-reader.h>
#include <mail/e-mail-reader-utils.h>
@@ -133,9 +135,6 @@ struct _EMailShellViewPrivate {
CamelVeeFolder *search_account_current;
GCancellable *search_account_cancel;
- GtkToolItem *send_receive_tool_item;
- GtkToolItem *send_receive_tool_separator;
-
gboolean vfolder_allow_expunge;
gboolean ignore_folder_popup_selection_done;
diff --git a/src/shell/CMakeLists.txt b/src/shell/CMakeLists.txt
index 9a3307ce0b..101a85dcc8 100644
--- a/src/shell/CMakeLists.txt
+++ b/src/shell/CMakeLists.txt
@@ -19,6 +19,7 @@ set(SOURCES
e-shell-backend.c
e-shell-content.c
e-shell-enumtypes.c
+ e-shell-headerbar.c
e-shell-searchbar.c
e-shell-sidebar.c
e-shell-switcher.c
@@ -40,6 +41,7 @@ set(HEADERS
e-shell-backend.h
e-shell-common.h
e-shell-content.h
+ e-shell-headerbar.h
e-shell-searchbar.h
e-shell-sidebar.h
e-shell-switcher.h
diff --git a/src/shell/e-shell-headerbar.c b/src/shell/e-shell-headerbar.c
new file mode 100644
index 0000000000..feedce68b4
--- /dev/null
+++ b/src/shell/e-shell-headerbar.c
@@ -0,0 +1,310 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * SPDX-FileCopyrightText: (C) 2022 Cédric Bellegarde <cedric bellegarde adishatz org>
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "evolution-config.h"
+
+#include "e-shell-headerbar.h"
+#include "e-shell-window-private.h"
+
+#include <glib/gi18n.h>
+
+struct _EShellHeaderBarPrivate {
+ GWeakRef shell_window;
+
+ GtkWidget *new_button;
+ GtkWidget *start_buttons;
+ GtkWidget *end_buttons;
+
+ gulong prefered_item_notify_id;
+};
+
+enum {
+ PROP_0,
+ PROP_SHELL_WINDOW
+};
+
+G_DEFINE_TYPE_WITH_CODE (EShellHeaderBar, e_shell_header_bar, GTK_TYPE_HEADER_BAR,
+ G_ADD_PRIVATE (EShellHeaderBar))
+
+static void
+shell_header_bar_clear_box (GtkWidget *widget,
+ const gchar *name)
+{
+ GList *children, *iter;
+ const gchar *widget_name;
+
+ children = gtk_container_get_children (GTK_CONTAINER (widget));
+
+ for (iter = children; iter != NULL; iter = g_list_next (iter)) {
+ widget_name = gtk_widget_get_name (iter->data);
+ if (widget_name != NULL && g_str_has_prefix (widget_name, name))
+ gtk_widget_destroy (iter->data);
+ }
+
+ g_list_free (children);
+}
+
+static EShellWindow *
+shell_header_bar_dup_shell_window (EShellHeaderBar *headerbar)
+{
+ g_return_val_if_fail (E_IS_SHELL_HEADER_BAR (headerbar), NULL);
+
+ return g_weak_ref_get (&headerbar->priv->shell_window);
+}
+
+static void
+shell_header_bar_set_shell_window (EShellHeaderBar *headerbar,
+ EShellWindow *shell_window)
+{
+ EShellWindow *priv_shell_window = shell_header_bar_dup_shell_window (headerbar);
+
+ /* This should be always NULL, but for a sake of completeness
+ and no memory leak do unref it here. Also do *not* use
+ g_clear_object(), because the non-NULL is tested below. */
+ if (priv_shell_window)
+ g_object_unref (priv_shell_window);
+
+ g_return_if_fail (E_IS_SHELL_WINDOW (shell_window));
+ g_return_if_fail (priv_shell_window == NULL);
+
+ g_weak_ref_set (
+ &headerbar->priv->shell_window,
+ G_OBJECT (shell_window));
+}
+
+static void
+shell_header_bar_update_new_menu (EShellWindow *shell_window,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ EShellHeaderBar *headerbar = user_data;
+ GtkWidget *menu;
+
+ /* Update the "New" menu button submenu. */
+ menu = e_shell_window_create_new_menu (shell_window);
+ e_header_bar_button_take_menu (E_HEADER_BAR_BUTTON (headerbar->priv->new_button), menu);
+}
+
+
+static void
+shell_header_bar_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_SHELL_WINDOW:
+ shell_header_bar_set_shell_window (
+ E_SHELL_HEADER_BAR (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+shell_header_bar_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_SHELL_WINDOW:
+ g_value_take_object (
+ value, shell_header_bar_dup_shell_window (
+ E_SHELL_HEADER_BAR (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+shell_header_bar_constructed (GObject *object)
+{
+ EShellHeaderBar *headerbar = E_SHELL_HEADER_BAR (object);
+ EShellWindow *shell_window = shell_header_bar_dup_shell_window (headerbar);
+ GtkUIManager *ui_manager = e_shell_window_get_ui_manager (shell_window);
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_shell_header_bar_parent_class)->constructed (object);
+
+ g_return_if_fail (E_IS_SHELL_WINDOW (shell_window));
+
+ e_header_bar_button_add_accelerator (
+ E_HEADER_BAR_BUTTON (headerbar->priv->new_button),
+ gtk_ui_manager_get_accel_group (ui_manager),
+ GDK_KEY_N, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
+
+ headerbar->priv->prefered_item_notify_id = e_signal_connect_notify (
+ shell_window, "notify::active-view",
+ G_CALLBACK (shell_header_bar_update_new_menu), headerbar);
+
+ g_object_unref (shell_window);
+}
+
+static void
+shell_header_bar_dispose (GObject *object)
+{
+ EShellHeaderBar *headerbar = E_SHELL_HEADER_BAR (object);
+
+ if (headerbar->priv->new_button != NULL) {
+ EShellWindow *shell_window = shell_header_bar_dup_shell_window (headerbar);
+
+ if (shell_window) {
+ g_signal_handler_disconnect (
+ shell_window,
+ headerbar->priv->prefered_item_notify_id);
+ g_object_unref (headerbar->priv->new_button);
+
+ g_object_unref (shell_window);
+ }
+
+ headerbar->priv->new_button = NULL;
+ headerbar->priv->prefered_item_notify_id = 0;
+ }
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_shell_header_bar_parent_class)->dispose (object);
+}
+
+static void
+e_shell_header_bar_class_init (EShellHeaderBarClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->set_property = shell_header_bar_set_property;
+ object_class->get_property = shell_header_bar_get_property;
+ object_class->dispose = shell_header_bar_dispose;
+ object_class->constructed = shell_header_bar_constructed;
+
+ /**
+ * EShellHeaderbar:shell-window
+ *
+ * The #EShellWindow to which the headerbar belongs.
+ **/
+ g_object_class_install_property (
+ object_class,
+ PROP_SHELL_WINDOW,
+ g_param_spec_object (
+ "shell-window",
+ "Shell Window",
+ "The window to which the headerbar belongs",
+ E_TYPE_SHELL_WINDOW,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_EXPLICIT_NOTIFY |
+ G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_shell_header_bar_init (EShellHeaderBar *self)
+{
+ GtkWidget *new_button;
+
+ self->priv = e_shell_header_bar_get_instance_private (self);
+ gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (self), TRUE);
+
+ new_button = e_header_bar_button_new (C_("toolbar-button", "New"), NULL);
+ gtk_header_bar_pack_start (GTK_HEADER_BAR (self), new_button);
+ gtk_widget_show (new_button);
+ self->priv->new_button = g_object_ref (new_button);
+
+ self->priv->start_buttons = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+ gtk_header_bar_pack_start (GTK_HEADER_BAR (self), self->priv->start_buttons);
+ gtk_widget_show (self->priv->start_buttons);
+
+ self->priv->end_buttons = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+ gtk_header_bar_pack_end (GTK_HEADER_BAR (self), self->priv->end_buttons);
+ gtk_widget_show (self->priv->end_buttons);
+}
+
+/**
+ * e_shell_header_bar_new:
+ * @shel_window: The #EShellWindow to which the headerbar belongs
+ *
+ * Creates a new #EShellHeaderBar
+ *
+ * Returns: (transfer full): a new #EShellHeaderBar
+ *
+ * Since: 3.46
+ **/
+GtkWidget *
+e_shell_header_bar_new (EShellWindow *shell_window)
+{
+ return g_object_new (E_TYPE_SHELL_HEADER_BAR, "shell-window", shell_window, NULL);
+}
+
+/**
+ * e_shell_header_bar_get_new_button:
+ * @headerbar: a #EShellHeaderBar
+ *
+ * Returns: (transfer none): the 'New' button widget, which is #EHeaderBarButton
+ *
+ * Since: 3.46
+ **/
+GtkWidget *
+e_shell_header_bar_get_new_button (EShellHeaderBar *headerbar)
+{
+ g_return_val_if_fail (E_IS_SHELL_HEADER_BAR (headerbar), NULL);
+
+ return headerbar->priv->new_button;
+}
+
+/**
+ * e_shell_header_bar_pack_start:
+ * @headerbar: an #EShellHeaderBar
+ * @widget: widget to pack
+ *
+ * Adds child to bar, packed with reference to the start of the bar.
+ *
+ * Since: 3.46
+ **/
+void
+e_shell_header_bar_pack_start (EShellHeaderBar *headerbar,
+ GtkWidget *widget)
+{
+ g_return_if_fail (E_IS_SHELL_HEADER_BAR (headerbar));
+
+ gtk_box_pack_start (GTK_BOX (headerbar->priv->start_buttons), widget, FALSE, FALSE, 0);
+}
+
+/**
+ * e_shell_header_bar_pack_end:
+ * @headerbar: an #EShellHeaderBar
+ * @widget: widget to pack
+ *
+ * Adds child to bar, packed with reference to the end of the bar.
+ *
+ * Since: 3.46
+ **/
+void e_shell_header_bar_pack_end (EShellHeaderBar *headerbar,
+ GtkWidget *widget)
+{
+ g_return_if_fail (E_IS_SHELL_HEADER_BAR (headerbar));
+
+ gtk_box_pack_end (GTK_BOX (headerbar->priv->end_buttons), widget, FALSE, FALSE, 0);
+}
+
+/**
+ * e_shell_header_bar_clear:
+ * @headerbar: an #EShellHeaderBar
+ * @name: widget name starts with
+ *
+ * Removes all widgets from the header bar where widget name starts with @name
+ *
+ * Since: 3.46
+ **/
+void e_shell_header_bar_clear (EShellHeaderBar *headerbar,
+ const gchar *name)
+{
+ g_return_if_fail (E_IS_SHELL_HEADER_BAR (headerbar));
+
+ shell_header_bar_clear_box (headerbar->priv->start_buttons, name);
+ shell_header_bar_clear_box (headerbar->priv->end_buttons, name);
+}
diff --git a/src/shell/e-shell-headerbar.h b/src/shell/e-shell-headerbar.h
new file mode 100644
index 0000000000..2aaabf009b
--- /dev/null
+++ b/src/shell/e-shell-headerbar.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * SPDX-FileCopyrightText: (C) 2022 Cédric Bellegarde <cedric bellegarde adishatz org>
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef E_SHELL_HEADER_BAR_H
+#define E_SHELL_HEADER_BAR_H
+
+#include <gtk/gtk.h>
+
+#include <e-util/e-util.h>
+#include <shell/e-shell-window.h>
+
+#define E_TYPE_SHELL_HEADER_BAR \
+ (e_shell_header_bar_get_type ())
+#define E_SHELL_HEADER_BAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_SHELL_HEADER_BAR, EShellHeaderBar))
+#define E_SHELL_HEADER_BAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_SHELL_HEADER_BAR, EShellHeaderBarClass))
+#define E_IS_SHELL_HEADER_BAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_SHELL_HEADER_BAR))
+#define E_IS_SHELL_HEADER_BAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_SHELL_HEADER_BAR))
+#define E_SHELL_HEADER_BAR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_SHELL_HEADER_BAR, EShellHeaderBarClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EShellHeaderBar EShellHeaderBar;
+typedef struct _EShellHeaderBarClass EShellHeaderBarClass;
+typedef struct _EShellHeaderBarPrivate EShellHeaderBarPrivate;
+
+struct _EShellHeaderBar {
+ GtkHeaderBar parent;
+ EShellHeaderBarPrivate *priv;
+};
+
+struct _EShellHeaderBarClass {
+ GtkHeaderBarClass parent_class;
+};
+
+GType e_shell_header_bar_get_type (void);
+GtkWidget * e_shell_header_bar_new (EShellWindow *shell_window);
+GtkWidget * e_shell_header_bar_get_new_button (EShellHeaderBar *headerbar);
+void e_shell_header_bar_pack_start (EShellHeaderBar *headerbar,
+ GtkWidget *widget);
+void e_shell_header_bar_pack_end (EShellHeaderBar *headerbar,
+ GtkWidget *widget);
+void e_shell_header_bar_clear (EShellHeaderBar *headerbar,
+ const gchar *name);
+G_END_DECLS
+
+#endif /* E_SHELL_HEADER_BAR_H */
diff --git a/src/shell/e-shell-window-private.c b/src/shell/e-shell-window-private.c
index 8eb50413f9..2545814b0b 100644
--- a/src/shell/e-shell-window-private.c
+++ b/src/shell/e-shell-window-private.c
@@ -203,6 +203,30 @@ shell_window_construct_taskbar (EShellWindow *shell_window)
return class->construct_taskbar (shell_window);
}
+static gboolean
+shell_window_active_view_to_prefer_item (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ GObject *source_object;
+ EShell *shell;
+ EShellBackend *shell_backend;
+ const gchar *active_view;
+ const gchar *prefer_item;
+
+ active_view = g_value_get_string (source_value);
+
+ source_object = g_binding_get_source (binding);
+ shell = e_shell_window_get_shell (E_SHELL_WINDOW (source_object));
+ shell_backend = e_shell_get_backend_by_name (shell, active_view);
+ prefer_item = e_shell_backend_get_prefer_new_item (shell_backend);
+
+ g_value_set_string (target_value, prefer_item);
+
+ return TRUE;
+}
+
void
e_shell_window_private_init (EShellWindow *shell_window)
{
@@ -435,6 +459,10 @@ e_shell_window_private_constructed (EShellWindow *shell_window)
/* Construct window widgets. */
+ priv->headerbar = e_shell_header_bar_new (shell_window);
+ gtk_window_set_titlebar (window, priv->headerbar);
+ gtk_widget_show (priv->headerbar);
+
widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add (GTK_CONTAINER (shell_window), widget);
gtk_widget_show (widget);
@@ -661,6 +689,26 @@ e_shell_window_private_constructed (EShellWindow *shell_window)
g_signal_connect (shell_window, "key-press-event",
G_CALLBACK (e_shell_window_key_press_event_cb), NULL);
+
+ /* XXX The ECalShellBackend has a hack where it forces the
+ * EMenuButton to update its button image by forcing
+ * a "notify::active-view" signal emission on the window.
+ * This will trigger the property binding, which will set
+ * EMenuButton's "prefer-item" property, which will
+ * invoke header_bar_update_new_menu(), which
+ * will cause EMenuButton to update its button image.
+ *
+ * It's a bit of a Rube Goldberg machine and should be
+ * reworked, but it's just serving one (now documented)
+ * corner case and works for now. */
+ e_binding_bind_property_full (
+ shell_window, "active-view",
+ e_shell_header_bar_get_new_button (E_SHELL_HEADER_BAR (priv->headerbar)),
+ "prefer-item",
+ G_BINDING_SYNC_CREATE,
+ shell_window_active_view_to_prefer_item,
+ (GBindingTransformFunc) NULL,
+ NULL, (GDestroyNotify) NULL);
}
void
@@ -810,7 +858,6 @@ e_shell_window_update_title (EShellWindow *shell_window)
EShellView *shell_view;
const gchar *view_title;
const gchar *view_name;
- gchar *window_title;
g_return_if_fail (E_IS_SHELL_WINDOW (shell_window));
@@ -818,8 +865,5 @@ e_shell_window_update_title (EShellWindow *shell_window)
shell_view = e_shell_window_get_shell_view (shell_window, view_name);
view_title = e_shell_view_get_title (shell_view);
- /* Translators: This is used for the main window title. */
- window_title = g_strdup_printf (_("%s — Evolution"), view_title);
- gtk_window_set_title (GTK_WINDOW (shell_window), window_title);
- g_free (window_title);
+ gtk_window_set_title (GTK_WINDOW (shell_window), view_title);
}
diff --git a/src/shell/e-shell-window-private.h b/src/shell/e-shell-window-private.h
index ae10390521..de80f8e767 100644
--- a/src/shell/e-shell-window-private.h
+++ b/src/shell/e-shell-window-private.h
@@ -30,6 +30,7 @@
#include "shell/e-shell.h"
#include "shell/e-shell-content.h"
+#include "shell/e-shell-headerbar.h"
#include "shell/e-shell-view.h"
#include "shell/e-shell-searchbar.h"
#include "shell/e-shell-switcher.h"
@@ -80,9 +81,7 @@ struct _EShellWindowPrivate {
GtkWidget *switcher;
GtkWidget *tooltip_label;
GtkWidget *status_notebook;
-
- /* Miscellaneous */
- GtkWidget *menubar_box;
+ GtkWidget *headerbar;
/* Shell signal handlers. */
GArray *signal_handler_ids;
diff --git a/src/shell/e-shell-window.c b/src/shell/e-shell-window.c
index e7be30feee..6fe7672ad0 100644
--- a/src/shell/e-shell-window.c
+++ b/src/shell/e-shell-window.c
@@ -108,42 +108,6 @@ shell_window_menubar_update_new_menu (EShellWindow *shell_window)
gtk_widget_show (widget);
}
-static void
-shell_window_toolbar_update_new_menu (GtkMenuToolButton *menu_tool_button,
- GParamSpec *pspec,
- EShellWindow *shell_window)
-{
- GtkWidget *menu;
-
- /* Update the "New" menu tool button submenu. */
- menu = e_shell_window_create_new_menu (shell_window);
- gtk_menu_tool_button_set_menu (menu_tool_button, menu);
-}
-
-static gboolean
-shell_window_active_view_to_prefer_item (GBinding *binding,
- const GValue *source_value,
- GValue *target_value,
- gpointer user_data)
-{
- GObject *source_object;
- EShell *shell;
- EShellBackend *shell_backend;
- const gchar *active_view;
- const gchar *prefer_item;
-
- active_view = g_value_get_string (source_value);
-
- source_object = g_binding_get_source (binding);
- shell = e_shell_window_get_shell (E_SHELL_WINDOW (source_object));
- shell_backend = e_shell_get_backend_by_name (shell, active_view);
- prefer_item = e_shell_backend_get_prefer_new_item (shell_backend);
-
- g_value_set_string (target_value, prefer_item);
-
- return TRUE;
-}
-
static void
shell_window_set_notebook_page (EShellWindow *shell_window,
GParamSpec *pspec,
@@ -473,12 +437,8 @@ shell_window_construct_menubar (EShellWindow *shell_window)
static GtkWidget *
shell_window_construct_toolbar (EShellWindow *shell_window)
{
- GtkUIManager *ui_manager;
GtkWidget *toolbar;
GtkWidget *box;
- GtkToolItem *item;
-
- ui_manager = e_shell_window_get_ui_manager (shell_window);
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_show (box);
@@ -490,69 +450,23 @@ shell_window_construct_toolbar (EShellWindow *shell_window)
toolbar = e_shell_window_get_managed_widget (
shell_window, "/main-toolbar");
+ gtk_toolbar_set_icon_size (GTK_TOOLBAR (toolbar), GTK_ICON_SIZE_BUTTON);
gtk_style_context_add_class (
gtk_widget_get_style_context (toolbar),
GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
- /* XXX Having this separator in the UI definition doesn't work
- * because GtkUIManager is unaware of the "New" button, so
- * it makes the separator invisible. One possibility is to
- * define a GtkAction subclass for which create_tool_item()
- * return an EMenuToolButton. Then both this separator
- * and the "New" button could be added to the UI definition.
- * Tempting, but the "New" button and its dynamically
- * generated menu is already a complex beast, and I'm not
- * convinced having it proxy some new type of GtkAction
- * is worth the extra effort. */
- item = gtk_separator_tool_item_new ();
- gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, 0);
- gtk_widget_show (GTK_WIDGET (item));
-
- /* Translators: a 'New' toolbar button caption which is context sensitive and
- runs one of the actions under File->New menu */
- item = e_menu_tool_button_new (C_("toolbar-button", "New"));
- gtk_tool_item_set_is_important (GTK_TOOL_ITEM (item), TRUE);
- gtk_widget_add_accelerator (
- GTK_WIDGET (item), "clicked",
- gtk_ui_manager_get_accel_group (ui_manager),
- GDK_KEY_N, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
- gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, 0);
- gtk_widget_show (GTK_WIDGET (item));
-
- /* XXX The ECalShellBackend has a hack where it forces the
- * EMenuToolButton to update its button image by forcing
- * a "notify::active-view" signal emission on the window.
- * This will trigger the property binding, which will set
- * EMenuToolButton's "prefer-item" property, which will
- * invoke shell_window_toolbar_update_new_menu(), which
- * will cause EMenuToolButton to update its button image.
- *
- * It's a bit of a Rube Goldberg machine and should be
- * reworked, but it's just serving one (now documented)
- * corner case and works for now. */
- e_binding_bind_property_full (
- shell_window, "active-view",
- item, "prefer-item",
- G_BINDING_SYNC_CREATE,
- shell_window_active_view_to_prefer_item,
- (GBindingTransformFunc) NULL,
- NULL, (GDestroyNotify) NULL);
-
- g_signal_connect (
- item, "notify::prefer-item",
- G_CALLBACK (shell_window_toolbar_update_new_menu),
- shell_window);
-
gtk_box_pack_start (GTK_BOX (box), toolbar, TRUE, TRUE, 0);
toolbar = e_shell_window_get_managed_widget (
shell_window, "/search-toolbar");
gtk_toolbar_set_show_arrow (GTK_TOOLBAR (toolbar), FALSE);
+ gtk_toolbar_set_icon_size (GTK_TOOLBAR (toolbar), GTK_ICON_SIZE_BUTTON);
toolbar = e_shell_window_get_managed_widget (
shell_window, "/close-toolbar");
gtk_toolbar_set_show_arrow (GTK_TOOLBAR (toolbar), FALSE);
+ gtk_toolbar_set_icon_size (GTK_TOOLBAR (toolbar), GTK_ICON_SIZE_BUTTON);
return box;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]