Commit 3af0d628 authored by Michael Gratton's avatar Michael Gratton 🍸
Browse files

Merge branch 'mjog/1193-conversation-buttons-resize-race' into 'mainline'

Duplicate, don't re-parent, conversation actions as responsive layout changes.

Closes #1193

See merge request !691
parents 1de50614 11882a10
Pipeline #274351 passed with stages
in 25 minutes and 1 second
......@@ -29,6 +29,7 @@ variables:
libappstream-glib-devel libgee-devel libhandy1-devel libicu-devel
libpeas-devel libsecret-devel libstemmer-devel libunwind-devel
libxml2-devel libytnef-devel sqlite-devel webkitgtk4-devel
git
FEDORA_TEST_DEPS: glibc-langpack-en gnutls-utils tar Xvfb xz
# Ubuntu packages
......@@ -40,6 +41,7 @@ variables:
libhandy-1-dev libicu-dev libjson-glib-dev libmessaging-menu-dev
libpeas-dev libsecret-1-dev libsqlite3-dev libstemmer-dev
libunwind-dev libwebkit2gtk-4.0-dev libxml2-dev libytnef0-dev
git ca-certificates
UBUNTU_TEST_DEPS: gnutls-bin librsvg2-common locales xauth xvfb
fedora:
......
......@@ -89,7 +89,7 @@ icu_uc = dependency('icu-uc', version: '>=60')
iso_codes = dependency('iso-codes')
javascriptcoregtk = dependency('javascriptcoregtk-4.0', version: '>=' + target_webkit)
json_glib = dependency('json-glib-1.0', version: '>= 1.0')
libhandy = dependency('libhandy-1', version: '>= 1.2')
libhandy = dependency('libhandy-1', version: '>= 1.2.1', required: false)
libmath = cc.find_library('m')
libpeas = dependency('libpeas-1.0', version: '>= 1.24.0')
libpeas_gtk = dependency('libpeas-gtk-1.0', version: '>= 1.24.0')
......@@ -138,6 +138,26 @@ libstemmer = declare_dependency(
],
)
# Required until libhandy 1.2.1 is GA
libhandy_vapi = ''
if not libhandy.found()
libhandy_project = subproject(
'libhandy',
default_options: [
'examples=false',
'package_subdir=geary',
'tests=false',
]
)
libhandy = declare_dependency(
dependencies: [
libhandy_project.get_variable('libhandy_dep'),
libhandy_project.get_variable('libhandy_vapi')
]
)
libhandy_vapi = meson.build_root() / 'subprojects' / 'libhandy' / 'src'
endif
# Optional dependencies
appstream_util = find_program('appstream-util', required: false)
desktop_file_validate = find_program('desktop-file-validate', required: false)
......
......@@ -78,6 +78,24 @@
"/share"
]
},
{
"name": "libhandy",
"buildsystem": "meson",
"config-opts": [
"-Dglade_catalog=disabled"
],
"sources": [
{
"type": "git",
"url": "https://gitlab.gnome.org/GNOME/libhandy.git",
"branch": "master"
}
],
"cleanup": [
"/bin",
"/share"
]
},
{
"name": "libical",
"buildsystem": "cmake-ninja",
......
......@@ -111,6 +111,19 @@ modules:
- /bin
- /share
# Geary dependency
- name: libhandy
buildsystem: meson
config-opts:
- "-Dglade_catalog=disabled"
sources:
- type: git
url: "https://gitlab.gnome.org/GNOME/libhandy.git"
branch: master
cleanup:
- /bin
- /share
# EDS dependency
- name: libical
buildsystem: cmake-ninja
......
......@@ -38,12 +38,12 @@ src/client/application/secret-mediator.vala
src/client/client-action.vala
src/client/components/components-attachment-pane.vala
src/client/components/components-conversation-actions.vala
src/client/components/components-conversation-action-bar.vala
src/client/components/components-entry-undo.vala
src/client/components/components-in-app-notification.vala
src/client/components/components-info-bar-stack.vala
src/client/components/components-info-bar.vala
src/client/components/components-inspector.vala
src/client/components/components-main-toolbar.vala
src/client/components/components-placeholder-pane.vala
src/client/components/components-preferences-window.vala
src/client/components/components-problem-report-info-bar.vala
......@@ -54,7 +54,6 @@ src/client/components/components-web-view.vala
src/client/components/count-badge.vala
src/client/components/folder-popover.vala
src/client/components/icon-factory.vala
src/client/components/main-toolbar.vala
src/client/components/monitored-progress-bar.vala
src/client/components/monitored-spinner.vala
src/client/components/status-bar.vala
......@@ -458,13 +457,13 @@ ui/components-attachment-pane.ui
ui/components-attachment-pane-menus.ui
ui/components-attachment-view.ui
ui/components-conversation-actions.ui
ui/components-conversation-action-bar.ui
ui/components-conversation-header-bar.ui
ui/components-in-app-notification.ui
ui/components-info-bar.ui
ui/components-inspector-error-view.ui
ui/components-inspector-log-view.ui
ui/components-inspector.ui
ui/components-main-toolbar.ui
ui/components-main-toolbar-menus.ui
ui/components-placeholder-pane.ui
ui/conversation-contact-popover.ui
ui/conversation-email.ui
......@@ -476,7 +475,5 @@ ui/conversation-viewer.ui
ui/find_bar.glade
ui/folder-popover.ui
ui/gtk/help-overlay.ui
ui/main-toolbar.ui
ui/main-toolbar-menus.ui
ui/password-dialog.glade
ui/problem-details-dialog.ui
/*
* Copyright © 2016 Software Freedom Conservancy Inc.
* Copyright © 2020 Purism SPC
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
*/
// Draws the conversation action bar.
[GtkTemplate (ui = "/org/gnome/Geary/components-conversation-action-bar.ui")]
public class Components.ConversationActionBar : Gtk.Revealer {
[GtkChild] public unowned Gtk.Box action_box { get; }
private ulong owner_notify;
/**
* This takes ownership of the ConversationActions and places some of
* the buttons into the ActionBar.
*/
public void add_conversation_actions(Components.ConversationActions actions) {
if (actions.owner == this)
return;
actions.take_ownership(this);
action_box.pack_start(actions.mark_copy_move_buttons, false, false);
action_box.pack_end(actions.archive_trash_delete_buttons, false, false);
reveal_child = true;
this.owner_notify = actions.notify["owner"].connect(() => {
if (actions.owner != this) {
reveal_child = false;
actions.disconnect (this.owner_notify);
}
});
}
}
/* Copyright 2017 Software Freedom Conservancy Inc.
/*
* Copyright © 2017 Software Freedom Conservancy Inc.
* Copyright © 2021 Michael Gratton <mike@vee.net>
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
* (version 2.1 or later). See the COPYING file in this distribution.
*/
/**
* Container for actions for a conversation generally placed into the ActionBar or HeaderBar
* The user of the actions needs to take ownership before they can place the actions in a container
* A container of conversation-related actions.
*/
public class Components.ConversationActions : GLib.Object {
public Gtk.Widget? owner { get; private set; }
// Copy and Move popovers
[GtkTemplate (ui = "/org/gnome/Geary/components-conversation-actions.ui")]
public class Components.ConversationActions : Gtk.Box {
public bool show_conversation_actions { get; construct; }
public bool show_response_actions { get; construct; }
public bool pack_justified { get; construct; }
public FolderPopover copy_folder_menu { get; private set; default = new FolderPopover(); }
public FolderPopover move_folder_menu { get; private set; default = new FolderPopover(); }
// How many conversations are selected right now. Should automatically be updated.
public int selected_conversations { get; set; }
public bool find_open { get; set; }
public Gtk.Box mark_copy_move_buttons { get; private set; }
public Gtk.MenuButton mark_message_button { get; private set; }
public Gtk.MenuButton copy_message_button { get; private set; }
public Gtk.MenuButton move_message_button { get; private set; }
public int selected_conversations { get; set; }
public Gtk.Box reply_forward_buttons { get; private set; }
[GtkChild] private unowned Gtk.Box response_buttons { get; }
public Gtk.Box archive_trash_delete_buttons { get; private set; }
private Gtk.Button archive_button;
private Gtk.Button trash_delete_button;
[GtkChild] private unowned Gtk.Box mark_copy_move_buttons { get; }
[GtkChild] private unowned Gtk.MenuButton mark_message_button { get; }
[GtkChild] private unowned Gtk.MenuButton copy_message_button { get; }
[GtkChild] private unowned Gtk.MenuButton move_message_button { get; }
public Gtk.ToggleButton find_button { get; private set; }
[GtkChild] private unowned Gtk.Box archive_trash_delete_buttons { get; }
[GtkChild] private unowned Gtk.Button archive_button;
[GtkChild] private unowned Gtk.Button trash_delete_button;
private bool show_trash_button = true;
......@@ -36,47 +41,49 @@ public class Components.ConversationActions : GLib.Object {
private Gtk.Image trash_image = new Gtk.Image.from_icon_name("user-trash-symbolic", Gtk.IconSize.MENU);
private Gtk.Image delete_image = new Gtk.Image.from_icon_name("edit-delete-symbolic", Gtk.IconSize.MENU);
public ConversationActions() {
Gtk.Builder builder =
new Gtk.Builder.from_resource("/org/gnome/Geary/components-conversation-actions.ui");
// Assemble the mark menus
Gtk.Builder menu_builder =
new Gtk.Builder.from_resource("/org/gnome/Geary/main-toolbar-menus.ui");
MenuModel mark_menu = (MenuModel) menu_builder.get_object("mark_message_menu");
this.mark_copy_move_buttons = (Gtk.Box) builder.get_object("mark_copy_move_buttons");
this.mark_message_button = (Gtk.MenuButton) builder.get_object("mark_message_button");
this.copy_message_button = (Gtk.MenuButton) builder.get_object("copy_message_button");
this.move_message_button = (Gtk.MenuButton) builder.get_object("move_message_button");
this.reply_forward_buttons = (Gtk.Box) builder.get_object("reply_forward_buttons");
this.archive_trash_delete_buttons = (Gtk.Box) builder.get_object("archive_trash_delete_buttons");
this.archive_button = (Gtk.Button) builder.get_object("archive_button");
this.trash_delete_button = (Gtk.Button) builder.get_object("trash_delete_button");
static construct {
set_css_name("components-conversation-actions");
}
this.find_button = (Gtk.ToggleButton) builder.get_object("find_button");
// GObject style constuction to support loading via GTK Builder files
construct {
// Assemble the mark menus
Gtk.Builder menu_builder = new Gtk.Builder.from_resource(
"/org/gnome/Geary/components-main-toolbar-menus.ui"
);
GLib.MenuModel mark_menu = (MenuModel) menu_builder.get_object(
"mark_message_menu"
);
this.bind_property("find-open", this.find_button, "active",
BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
this.notify["selected-conversations"].connect(() => update_conversation_buttons());
this.mark_message_button.popover = new Gtk.Popover.from_model(null, mark_menu);
this.copy_message_button.popover = copy_folder_menu;
this.move_message_button.popover = move_folder_menu;
this.response_buttons.set_visible(this.show_response_actions);
this.mark_copy_move_buttons.set_visible(this.show_conversation_actions);
this.archive_trash_delete_buttons.set_visible(this.show_conversation_actions);
if (this.pack_justified) {
this.archive_trash_delete_buttons.hexpand = true;
this.archive_trash_delete_buttons.halign = END;
}
}
public void set_move_sensitive(bool is_sensitive) {
this.move_message_button.sensitive = is_sensitive;
}
public void show_move_menu() {
this.move_message_button.clicked();
}
/** Sets the new owner and removes the previous owner and parents of the single actions */
public void take_ownership(Gtk.Widget? new_owner) {
remove_parent(mark_copy_move_buttons);
remove_parent(reply_forward_buttons);
remove_parent(archive_trash_delete_buttons);
remove_parent(find_button);
owner = new_owner;
public void set_copy_sensitive(bool is_sensitive) {
this.copy_message_button.sensitive = is_sensitive;
}
private void remove_parent (Gtk.Widget widget) {
if (widget.parent != null)
widget.parent.remove(widget);
public void show_copy_menu() {
this.copy_message_button.clicked();
}
public void update_trash_button(bool show_trash) {
......
/*
* Copyright © 2020 Purism SPC
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
*/
[GtkTemplate (ui = "/org/gnome/Geary/components-conversation-header-bar.ui")]
public class Components.ConversationHeaderBar : Hdy.HeaderBar {
public Components.ConversationActionBar action_bar { get; set; }
public bool folded { get; set; }
private ulong owner_notify;
private Gtk.Widget? reply_forward_buttons;
private Gtk.Widget? archive_trash_delete_buttons;
public ConversationHeaderBar() {
}
public override void size_allocate(Gtk.Allocation allocation) {
update_action_bar();
base.size_allocate(allocation);
}
[GtkCallback]
private void update_action_bar () {
/* Only show the action_bar when the conversation_header is shown */
if (parent == null)
action_bar.reveal_child = false;
else if (reply_forward_buttons != null && archive_trash_delete_buttons != null)
if (action_bar.reveal_child && get_allocated_width() > 600) {
action_bar.reveal_child = false;
remove_action_parent();
pack_start(reply_forward_buttons);
pack_end(archive_trash_delete_buttons);
} else if (!action_bar.reveal_child && get_allocated_width() < 600) {
remove_action_parent();
action_bar.action_box.pack_start(reply_forward_buttons, false, false);
action_bar.action_box.pack_end(archive_trash_delete_buttons, false, false);
action_bar.reveal_child = true;
}
}
private void remove_action_parent() {
if (reply_forward_buttons != null && reply_forward_buttons.parent != null)
reply_forward_buttons.parent.remove(reply_forward_buttons);
if (archive_trash_delete_buttons != null && archive_trash_delete_buttons.parent != null)
archive_trash_delete_buttons.parent.remove(archive_trash_delete_buttons);
}
public void add_conversation_actions(Components.ConversationActions actions) {
if (actions.owner != this) {
actions.take_ownership(this);
pack_start(actions.mark_copy_move_buttons);
pack_end(actions.find_button);
reply_forward_buttons = actions.reply_forward_buttons;
archive_trash_delete_buttons = actions.archive_trash_delete_buttons;
action_bar.reveal_child = get_allocated_width() > 600;
update_action_bar();
this.owner_notify = actions.notify["owner"].connect(() => {
if (actions.owner != this) {
action_bar.reveal_child = false;
reply_forward_buttons = null;
archive_trash_delete_buttons = null;
actions.disconnect (this.owner_notify);
}
});
}
}
}
/* Copyright 2017 Software Freedom Conservancy Inc.
/*
* Copyright © 2017 Software Freedom Conservancy Inc.
* Copyright © 2021 Michael Gratton <mike@vee.net>
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
* (version 2.1 or later). See the COPYING file in this distribution.
*/
// Draws the main toolbar.
[GtkTemplate (ui = "/org/gnome/Geary/main-toolbar.ui")]
public class MainToolbar : Hdy.Leaflet {
// How wide the left pane should be. Auto-synced with our settings
public int left_pane_width { get; set; }
// Used to form the title of the folder header
/**
* The toolbar for the main window.
*
* @see Application.MainWindow
*/
[GtkTemplate (ui = "/org/gnome/Geary/components-main-toolbar.ui")]
public class Components.MainToolbar : Hdy.Leaflet {
public string account { get; set; }
public string folder { get; set; }
// Close button settings
public bool show_close_button { get; set; default = true; }
// Search bar
public bool search_open { get; set; default = false; }
private Components.ConversationActionBar conversation_viewer_action_bar;
public bool find_open { get; set; default = false; }
public ConversationActions shown_actions {
get {
return (ConversationActions) this.actions_squeezer.visible_child;
}
}
[GtkChild] public unowned ConversationActions full_actions;
[GtkChild] public unowned ConversationActions compact_actions;
[GtkChild] private unowned Hdy.Leaflet conversations_leaflet;
......@@ -27,36 +43,47 @@ public class MainToolbar : Hdy.Leaflet {
[GtkChild] private unowned Gtk.Separator folder_separator;
// Conversations header elements
// Conversation list header elements
[GtkChild] private unowned Hdy.HeaderBar conversations_header;
[GtkChild] private unowned Gtk.ToggleButton search_conversations_button;
[GtkChild] private unowned Gtk.ToggleButton search_button;
[GtkChild] private unowned Gtk.Separator conversations_separator;
// Conversation header elements
[GtkChild] private unowned Components.ConversationHeaderBar conversation_header;
// Conversation viewer header elements
[GtkChild] private unowned Hdy.HeaderBar conversation_header;
[GtkChild] private unowned Hdy.Squeezer actions_squeezer;
[GtkChild] private unowned Gtk.ToggleButton find_button;
[GtkChild] private unowned Hdy.HeaderGroup header_group;
Gtk.SizeGroup conversation_group;
private Gtk.SizeGroup conversation_group;
public MainToolbar(Application.Configuration config,
Components.ConversationActionBar action_bar) {
public MainToolbar(Application.Configuration config) {
if (config.desktop_environment != UNITY) {
this.bind_property("account", this.conversations_header, "title", BindingFlags.SYNC_CREATE);
this.bind_property("folder", this.conversations_header, "subtitle", BindingFlags.SYNC_CREATE);
}
this.conversation_viewer_action_bar = action_bar;
this.conversation_header.action_bar = action_bar;
// Assemble the main/mark menus
Gtk.Builder builder = new Gtk.Builder.from_resource("/org/gnome/Geary/main-toolbar-menus.ui");
Gtk.Builder builder = new Gtk.Builder.from_resource("/org/gnome/Geary/components-main-toolbar-menus.ui");
MenuModel main_menu = (MenuModel) builder.get_object("main_menu");
// Setup folder header elements
this.main_menu_button.popover = new Gtk.Popover.from_model(null, main_menu);
this.bind_property("search-open", this.search_conversations_button, "active",
BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
this.bind_property(
"search-open",
this.search_button, "active",
SYNC_CREATE | BIDIRECTIONAL
);
this.bind_property(
"find-open",
this.find_button, "active",
SYNC_CREATE | BIDIRECTIONAL
);
this.actions_squeezer.notify["visible-child"].connect_after(
() => { notify_property("shown-actions"); }
);
}
public void set_conversation_header(Hdy.HeaderBar header) {
......@@ -97,10 +124,6 @@ public class MainToolbar : Hdy.Leaflet {
conversation_group.add_swipeable(this);
}
public void add_conversation_actions(Components.ConversationActions actions) {
conversation_header.add_conversation_actions(actions);
}
public void show_main_menu() {
this.main_menu_button.clicked();
}
......
......@@ -27,14 +27,14 @@ public class Composer.Box : Gtk.Frame, Container {
/** {@inheritDoc} */
internal Widget composer { get; set; }
private MainToolbar main_toolbar { get; private set; }
private Components.MainToolbar main_toolbar { get; private set; }
/** Emitted when the container is closed. */
public signal void vanished();
public Box(Widget composer, MainToolbar main_toolbar) {
public Box(Widget composer, Components.MainToolbar main_toolbar) {
this.composer = composer;
this.composer.set_mode(PANED);
......
......@@ -47,8 +47,6 @@ client_vala_sources = files(
'components/components-attachment-pane.vala',
'components/components-conversation-actions.vala',
'components/components-conversation-action-bar.vala',
'components/components-conversation-header-bar.vala',
'components/components-entry-undo.vala',
'components/components-info-bar-stack.vala',
'components/components-info-bar.vala',
......@@ -57,6 +55,7 @@ client_vala_sources = files(
'components/components-inspector-error-view.vala',
'components/components-inspector-log-view.vala',
'components/components-inspector-system-view.vala',
'components/components-main-toolbar.vala',
'components/components-placeholder-pane.vala',
'components/components-preferences-window.vala',
'components/components-problem-report-info-bar.vala',
......@@ -67,7 +66,6 @@ client_vala_sources = files(
'components/count-badge.vala',
'components/folder-popover.vala',
'components/icon-factory.vala',
'components/main-toolbar.vala',
'components/monitored-progress-bar.vala',
'components/monitored-spinner.vala',
'components/status-bar.vala',
......
......@@ -139,6 +139,9 @@ valadoc_vapi_dirs = [
vapi_dir,
meson.current_build_dir()
]
if libhandy_vapi != ''
valadoc_vapi_dirs += libhandy_vapi
endif
# Hopefully Meson will get baked-in valadoc support, so we don't have
# to resort to these kinds of hacks any more. See
......@@ -147,7 +150,11 @@ valadoc_vapi_dirs = [
valadoc_dep_args = []
foreach dep : valadoc_dependencies
valadoc_dep_args += '--pkg'
valadoc_dep_args += dep.name()
if dep != libhandy
valadoc_dep_args += dep.name()
else
valadoc_dep_args += 'libhandy-1'
endif
endforeach
valadoc_dep_args += [ '--pkg', 'icu-uc' ]
valadoc_dep_args += [ '--pkg', 'libstemmer' ]
......
[wrap-git]
directory = libhandy
url = https://gitlab.gnome.org/GNOME/libhandy.git
revision = 1.2.1
......@@ -18,14 +18,14 @@
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<object class="HdyLeaflet" id="main_leaflet">