From a1f74d24ff11869f108dc42016a648ce45999b70 Mon Sep 17 00:00:00 2001 From: Adrien Plazas Date: Thu, 16 Jan 2020 13:28:13 +0100 Subject: [PATCH 01/19] Drop saving the paned width This is needed to replace GtkPaned by HdyLeaflet. This breaks syncing the size of the headerbar with the rest of the window, it will be fixed in a later commit. --- desktop/org.gnome.Geary.gschema.xml | 24 ------------------- .../application-configuration.vala | 22 ----------------- .../application/application-main-window.vala | 17 ------------- src/client/components/main-toolbar.vala | 9 ------- 4 files changed, 72 deletions(-) diff --git a/desktop/org.gnome.Geary.gschema.xml b/desktop/org.gnome.Geary.gschema.xml index bbbadc36..9850dffd 100644 --- a/desktop/org.gnome.Geary.gschema.xml +++ b/desktop/org.gnome.Geary.gschema.xml @@ -21,24 +21,6 @@ The last recorded height of the application window. - - 100 - Position of folder list pane - Position of the folder list Paned grabber. - - - - -1 - Position of folder list pane when horizontal - Position of the folder list Paned grabber in the horizontal orientation. - - - - 200 - Position of folder list pane when vertical - Position of the folder list Paned grabber in the vertical orientation. - - true Orientation of the folder list pane @@ -51,12 +33,6 @@ True if the formatting toolbar in the composer is shown. - - 250 - Position of message list pane - Position of the message list Paned grabber. - - true Autoselect next message diff --git a/src/client/application/application-configuration.vala b/src/client/application/application-configuration.vala index 00c359c9..51a11bbf 100644 --- a/src/client/application/application-configuration.vala +++ b/src/client/application/application-configuration.vala @@ -20,11 +20,7 @@ public class Application.Configuration : Geary.BaseObject { public const string CONVERSATION_VIEWER_ZOOM_KEY = "conversation-viewer-zoom"; public const string DISPLAY_PREVIEW_KEY = "display-preview"; public const string FOLDER_LIST_PANE_HORIZONTAL_KEY = "folder-list-pane-horizontal"; - public const string FOLDER_LIST_PANE_POSITION_HORIZONTAL_KEY = "folder-list-pane-position-horizontal"; - public const string FOLDER_LIST_PANE_POSITION_KEY = "folder-list-pane-position"; - public const string FOLDER_LIST_PANE_POSITION_VERTICAL_KEY = "folder-list-pane-position-vertical"; public const string FORMATTING_TOOLBAR_VISIBLE = "formatting-toolbar-visible"; - public const string MESSAGES_PANE_POSITION_KEY = "messages-pane-position"; public const string OPTIONAL_PLUGINS = "optional-plugins"; public const string SEARCH_STRATEGY_KEY = "search-strategy"; public const string SINGLE_KEY_SHORTCUTS = "single-key-shortcuts"; @@ -90,19 +86,6 @@ public class Application.Configuration : Geary.BaseObject { get { return settings.get_boolean(WINDOW_MAXIMIZE_KEY); } } - public int folder_list_pane_position_old { - get { return settings.get_int(FOLDER_LIST_PANE_POSITION_KEY); } - } - - public int folder_list_pane_position_horizontal { - get { return settings.get_int(FOLDER_LIST_PANE_POSITION_HORIZONTAL_KEY); } - set { settings.set_int(FOLDER_LIST_PANE_POSITION_HORIZONTAL_KEY, value); } - } - - public int folder_list_pane_position_vertical { - get { return settings.get_int(FOLDER_LIST_PANE_POSITION_VERTICAL_KEY); } - } - public bool folder_list_pane_horizontal { get { return settings.get_boolean(FOLDER_LIST_PANE_HORIZONTAL_KEY); } } @@ -112,11 +95,6 @@ public class Application.Configuration : Geary.BaseObject { set { settings.set_boolean(FORMATTING_TOOLBAR_VISIBLE, value); } } - public int messages_pane_position { - get { return settings.get_int(MESSAGES_PANE_POSITION_KEY); } - set { settings.set_int(MESSAGES_PANE_POSITION_KEY, value); } - } - public bool autoselect { get { return settings.get_boolean(AUTOSELECT_KEY); } } diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala index 47749019..48f8bae6 100644 --- a/src/client/application/application-main-window.vala +++ b/src/client/application/application-main-window.vala @@ -1130,15 +1130,10 @@ public class Application.MainWindow : // This code both loads AND saves the pane positions with live updating. This is more // resilient against crashes because the value in dconf changes *immediately*, and // stays saved in the event of a crash. - config.bind(Configuration.MESSAGES_PANE_POSITION_KEY, this.conversations_paned, "position"); config.bind(Configuration.WINDOW_WIDTH_KEY, this, "window-width"); config.bind(Configuration.WINDOW_HEIGHT_KEY, this, "window-height"); config.bind(Configuration.WINDOW_MAXIMIZE_KEY, this, "window-maximized"); // Update to layout - if (config.folder_list_pane_position_horizontal == -1) { - config.folder_list_pane_position_horizontal = config.folder_list_pane_position_old; - config.messages_pane_position += config.folder_list_pane_position_old; - } config.settings.changed[ Configuration.FOLDER_LIST_PANE_HORIZONTAL_KEY ].connect(on_change_orientation); @@ -1655,23 +1650,11 @@ public class Application.MainWindow : this.folder_paned.orientation = horizontal ? Gtk.Orientation.HORIZONTAL : Gtk.Orientation.VERTICAL; - int folder_list_width = - this.application.config.folder_list_pane_position_horizontal; if (horizontal) { - if (!initial) - this.conversations_paned.position += folder_list_width; this.folder_box.pack_start(status_bar, false, false); } else { - if (!initial) - this.conversations_paned.position -= folder_list_width; this.conversation_list_box.pack_start(status_bar, false, false); } - - this.application.config.bind( - horizontal - ? Configuration.FOLDER_LIST_PANE_POSITION_HORIZONTAL_KEY - : Configuration.FOLDER_LIST_PANE_POSITION_VERTICAL_KEY, - this.folder_paned, "position"); } private void update_headerbar() { diff --git a/src/client/components/main-toolbar.vala b/src/client/components/main-toolbar.vala index 31f87df6..aa263253 100644 --- a/src/client/components/main-toolbar.vala +++ b/src/client/components/main-toolbar.vala @@ -59,15 +59,6 @@ public class MainToolbar : Gtk.Box { public MainToolbar(Application.Configuration config) { - // Sync headerbar width with left pane - config.bind( - Application.Configuration.MESSAGES_PANE_POSITION_KEY, - this, - "left-pane-width", - SettingsBindFlags.GET - ); - this.bind_property("left-pane-width", this.folder_header, "width-request", BindingFlags.SYNC_CREATE); - if (config.desktop_environment != UNITY) { this.bind_property("account", this.folder_header, "title", BindingFlags.SYNC_CREATE); this.bind_property("folder", this.folder_header, "subtitle", BindingFlags.SYNC_CREATE); From fb311e1c822fb41782c7ff07525c77732a52a971 Mon Sep 17 00:00:00 2001 From: Adrien Plazas Date: Thu, 16 Jan 2020 13:17:58 +0100 Subject: [PATCH 02/19] Drop the 2-panes mode This won't be needed to save horizontal space when using HdyLeaflet, and it would make porting to it harder. --- desktop/org.gnome.Geary.gschema.xml | 6 ----- .../application-configuration.vala | 5 ---- .../application/application-main-window.vala | 27 ++----------------- .../components-preferences-window.vala | 16 ----------- 4 files changed, 2 insertions(+), 52 deletions(-) diff --git a/desktop/org.gnome.Geary.gschema.xml b/desktop/org.gnome.Geary.gschema.xml index 9850dffd..89354dc2 100644 --- a/desktop/org.gnome.Geary.gschema.xml +++ b/desktop/org.gnome.Geary.gschema.xml @@ -21,12 +21,6 @@ The last recorded height of the application window. - - true - Orientation of the folder list pane - True if the folder list Paned is in the horizontal orientation. - - false Show/hide formatting toolbar diff --git a/src/client/application/application-configuration.vala b/src/client/application/application-configuration.vala index 51a11bbf..48542df6 100644 --- a/src/client/application/application-configuration.vala +++ b/src/client/application/application-configuration.vala @@ -19,7 +19,6 @@ public class Application.Configuration : Geary.BaseObject { public const string COMPOSE_AS_HTML_KEY = "compose-as-html"; public const string CONVERSATION_VIEWER_ZOOM_KEY = "conversation-viewer-zoom"; public const string DISPLAY_PREVIEW_KEY = "display-preview"; - public const string FOLDER_LIST_PANE_HORIZONTAL_KEY = "folder-list-pane-horizontal"; public const string FORMATTING_TOOLBAR_VISIBLE = "formatting-toolbar-visible"; public const string OPTIONAL_PLUGINS = "optional-plugins"; public const string SEARCH_STRATEGY_KEY = "search-strategy"; @@ -86,10 +85,6 @@ public class Application.Configuration : Geary.BaseObject { get { return settings.get_boolean(WINDOW_MAXIMIZE_KEY); } } - public bool folder_list_pane_horizontal { - get { return settings.get_boolean(FOLDER_LIST_PANE_HORIZONTAL_KEY); } - } - public bool formatting_toolbar_visible { get { return settings.get_boolean(FORMATTING_TOOLBAR_VISIBLE); } set { settings.set_boolean(FORMATTING_TOOLBAR_VISIBLE, value); } diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala index 48f8bae6..0bec6614 100644 --- a/src/client/application/application-main-window.vala +++ b/src/client/application/application-main-window.vala @@ -511,7 +511,8 @@ public class Application.MainWindow : }); setup_layout(application.config); - on_change_orientation(); + this.folder_paned.orientation = Gtk.Orientation.HORIZONTAL; + this.folder_box.pack_start(status_bar, false, false); update_command_actions(); update_conversation_actions(NONE); @@ -1133,10 +1134,6 @@ public class Application.MainWindow : config.bind(Configuration.WINDOW_WIDTH_KEY, this, "window-width"); config.bind(Configuration.WINDOW_HEIGHT_KEY, this, "window-height"); config.bind(Configuration.WINDOW_MAXIMIZE_KEY, this, "window-maximized"); - // Update to layout - config.settings.changed[ - Configuration.FOLDER_LIST_PANE_HORIZONTAL_KEY - ].connect(on_change_orientation); } private void restore_saved_window_state() { @@ -1637,26 +1634,6 @@ public class Application.MainWindow : } } - private void on_change_orientation() { - bool horizontal = this.application.config.folder_list_pane_horizontal; - bool initial = true; - - if (this.status_bar.parent != null) { - this.status_bar.parent.remove(status_bar); - initial = false; - } - - GLib.Settings.unbind(this.folder_paned, "position"); - this.folder_paned.orientation = horizontal ? Gtk.Orientation.HORIZONTAL : - Gtk.Orientation.VERTICAL; - - if (horizontal) { - this.folder_box.pack_start(status_bar, false, false); - } else { - this.conversation_list_box.pack_start(status_bar, false, false); - } - } - private void update_headerbar() { update_title(); if (this.selected_folder != null) { diff --git a/src/client/components/components-preferences-window.vala b/src/client/components/components-preferences-window.vala index b1bdfd7b..ea978a3a 100644 --- a/src/client/components/components-preferences-window.vala +++ b/src/client/components/components-preferences-window.vala @@ -136,16 +136,6 @@ public class Components.PreferencesWindow : Hdy.PreferencesWindow { display_preview_row.activatable_widget = display_preview; display_preview_row.add(display_preview); - var three_pane_view = new Gtk.Switch(); - three_pane_view.valign = CENTER; - - var three_pane_view_row = new Hdy.ActionRow(); - /// Translators: Preferences label - three_pane_view_row.title = _("Use _three pane view"); - three_pane_view_row.use_underline = true; - three_pane_view_row.activatable_widget = three_pane_view; - three_pane_view_row.add(three_pane_view); - var single_key_shortucts = new Gtk.Switch(); single_key_shortucts.valign = CENTER; @@ -180,7 +170,6 @@ public class Components.PreferencesWindow : Hdy.PreferencesWindow { //group.description = _("General application preferences"); group.add(autoselect_row); group.add(display_preview_row); - group.add(three_pane_view_row); group.add(single_key_shortucts_row); group.add(startup_notifications_row); @@ -210,11 +199,6 @@ public class Components.PreferencesWindow : Hdy.PreferencesWindow { display_preview, "state" ); - config.bind( - Application.Configuration.FOLDER_LIST_PANE_HORIZONTAL_KEY, - three_pane_view, - "state" - ); config.bind( Application.Configuration.SINGLE_KEY_SHORTCUTS, single_key_shortucts, From 850efb72380b3e2adc9f29af45a16bc1bd6e884b Mon Sep 17 00:00:00 2001 From: Adrien Plazas Date: Fri, 17 Jan 2020 18:51:39 +0100 Subject: [PATCH 03/19] application-main-window: Move the conversations searchbar Move it at above the conversations but not above the folders. This is needed to properly split the 3 panes. --- .../application/application-main-window.vala | 4 +- ui/application-main-window.ui | 127 ++++++++---------- 2 files changed, 55 insertions(+), 76 deletions(-) diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala index 0bec6614..43289e33 100644 --- a/src/client/application/application-main-window.vala +++ b/src/client/application/application-main-window.vala @@ -317,8 +317,6 @@ public class Application.MainWindow : [GtkChild] private Gtk.Box main_layout; [GtkChild] - private Gtk.Box search_bar_box; - [GtkChild] private Gtk.Paned folder_paned; [GtkChild] private Gtk.Paned conversations_paned; @@ -1210,7 +1208,7 @@ public class Application.MainWindow : // Search bar this.search_bar = new SearchBar(this.application.engine); this.search_bar.search_text_changed.connect(on_search); - this.search_bar_box.pack_start(this.search_bar, false, false, 0); + this.conversation_list_box.pack_start(this.search_bar, false, false, 0); // Folder list this.folder_list.folder_selected.connect(on_folder_selected); diff --git a/ui/application-main-window.ui b/ui/application-main-window.ui index f429a5ee..4a948294 100644 --- a/ui/application-main-window.ui +++ b/ui/application-main-window.ui @@ -28,100 +28,81 @@ True True - + True - False - vertical + True - + True - True + False + vertical - + True False - vertical + 0 + in - + + 100 True - False - 0 - in - - - 100 - True - True - never - - - + True + never - - True - True - 0 - - - - - False - False - - - - - True - False - vertical - - - True - False - 0 - in - - - 250 - True - True - - - - - - True - True - end - 0 - - True - False + True + True + 0 - - True - True - end - 0 + False + False + + + + + True + False + vertical + + + True + False + 0 + in + + + 250 + True + True + + + + + + True + True + end + 0 + + + + + True + False From 94ab7e5ac6b282e07edf8966da5c69e4fdba0cc8 Mon Sep 17 00:00:00 2001 From: Adrien Plazas Date: Sat, 18 Jan 2020 09:16:32 +0100 Subject: [PATCH 04/19] main-toolbar: Split the folder header Split it into a folder header and a conversations header. This is needed to properly split the 3 panes. Fixes https://gitlab.gnome.org/GNOME/geary/issues/442. --- src/client/components/main-toolbar.vala | 12 ++- ui/main-toolbar.ui | 108 +++++++++++++++--------- 2 files changed, 77 insertions(+), 43 deletions(-) diff --git a/src/client/components/main-toolbar.vala b/src/client/components/main-toolbar.vala index aa263253..4ee02079 100644 --- a/src/client/components/main-toolbar.vala +++ b/src/client/components/main-toolbar.vala @@ -28,10 +28,14 @@ public class MainToolbar : Gtk.Box { [GtkChild] private Gtk.HeaderBar folder_header; [GtkChild] - private Gtk.ToggleButton search_conversations_button; - [GtkChild] private Gtk.MenuButton main_menu_button; + // Conversations header elements + [GtkChild] + private Gtk.HeaderBar conversations_header; + [GtkChild] + private Gtk.ToggleButton search_conversations_button; + // Conversation header elements [GtkChild] private Gtk.HeaderBar conversation_header; @@ -60,8 +64,8 @@ public class MainToolbar : Gtk.Box { public MainToolbar(Application.Configuration config) { if (config.desktop_environment != UNITY) { - this.bind_property("account", this.folder_header, "title", BindingFlags.SYNC_CREATE); - this.bind_property("folder", this.folder_header, "subtitle", BindingFlags.SYNC_CREATE); + this.bind_property("account", this.conversations_header, "title", BindingFlags.SYNC_CREATE); + this.bind_property("folder", this.conversations_header, "subtitle", BindingFlags.SYNC_CREATE); } // Assemble the main/mark menus diff --git a/ui/main-toolbar.ui b/ui/main-toolbar.ui index 874f5b4b..732a01b9 100644 --- a/ui/main-toolbar.ui +++ b/ui/main-toolbar.ui @@ -11,68 +11,97 @@ True False - + True False - - + True - True - False - False - Compose Message - app.compose - True + False + + Mail - + True - False - text-editor-symbolic + True + False + False + True + + + True + False + open-menu-symbolic + + + + end + 1 + - + True - True - False - False - True - - - True - False - open-menu-symbolic - - + False + vertical + - end + False + True 1 - + True - True - False - False - Toggle search bar - True + False + - + True - False - preferences-system-search-symbolic + True + False + False + Compose Message + app.compose + True + + + True + False + text-editor-symbolic + + + + + True + True + False + False + Toggle search bar + True + + + True + False + preferences-system-search-symbolic + + + + + end + 3 + + - - end - 3 - @@ -82,7 +111,7 @@ - + True False vertical @@ -342,6 +371,7 @@ + From 7d4a61d431e072dbcacda2d089b587fe52257da6 Mon Sep 17 00:00:00 2001 From: Adrien Plazas Date: Thu, 16 Jan 2020 13:50:03 +0100 Subject: [PATCH 05/19] main-toolbar: Add add_to_size_groups() This will be used to sync requests of the panes. --- src/client/components/main-toolbar.vala | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/client/components/main-toolbar.vala b/src/client/components/main-toolbar.vala index 4ee02079..6ea472a8 100644 --- a/src/client/components/main-toolbar.vala +++ b/src/client/components/main-toolbar.vala @@ -30,12 +30,18 @@ public class MainToolbar : Gtk.Box { [GtkChild] private Gtk.MenuButton main_menu_button; + [GtkChild] + private Gtk.Separator folder_separator; + // Conversations header elements [GtkChild] private Gtk.HeaderBar conversations_header; [GtkChild] private Gtk.ToggleButton search_conversations_button; + [GtkChild] + private Gtk.Separator conversations_separator; + // Conversation header elements [GtkChild] private Gtk.HeaderBar conversation_header; @@ -105,6 +111,18 @@ public class MainToolbar : Gtk.Box { update_conversation_buttons(); } + public void add_to_size_groups(Gtk.SizeGroup folder_group, + Gtk.SizeGroup folder_separator_group, + Gtk.SizeGroup conversations_group, + Gtk.SizeGroup conversations_separator_group, + Gtk.SizeGroup conversation_group) { + folder_group.add_widget(folder_header); + folder_separator_group.add_widget(folder_separator); + conversations_group.add_widget(conversations_header); + conversations_separator_group.add_widget(conversations_separator); + conversation_group.add_widget(conversation_header); + } + // Updates tooltip text depending on number of conversations selected. private void update_conversation_buttons() { this.mark_message_button.tooltip_text = ngettext( From edfb1a2cf371ab743cbf804cbc4d34b7e4cfb8c6 Mon Sep 17 00:00:00 2001 From: Adrien Plazas Date: Thu, 16 Jan 2020 13:52:03 +0100 Subject: [PATCH 06/19] application-main-window: Sync the pane size request Bind each pane's elements minimum size requests via size groups. --- .../application/application-main-window.vala | 17 +++++++++++++++ ui/application-main-window.ui | 21 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala index 43289e33..4c6d1038 100644 --- a/src/client/application/application-main-window.vala +++ b/src/client/application/application-main-window.vala @@ -328,6 +328,17 @@ public class Application.MainWindow : private Gtk.Box conversation_list_box; [GtkChild] private Gtk.ScrolledWindow conversation_list_scrolled; + [GtkChild] + private Gtk.SizeGroup folder_size_group; + [GtkChild] + private Gtk.SizeGroup folder_separator_size_group; + [GtkChild] + private Gtk.SizeGroup conversations_size_group; + [GtkChild] + private Gtk.SizeGroup conversations_separator_size_group; + [GtkChild] + private Gtk.SizeGroup conversation_size_group; + [GtkChild] private Gtk.Overlay overlay; @@ -1239,9 +1250,15 @@ public class Application.MainWindow : ); this.conversations_paned.pack2(this.conversation_viewer, true, false); + this.conversation_size_group.add_widget(this.conversation_viewer); // Main toolbar this.main_toolbar = new MainToolbar(config); + this.main_toolbar.add_to_size_groups(this.folder_size_group, + this.folder_separator_size_group, + this.conversations_size_group, + this.conversations_separator_size_group, + this.conversation_size_group); this.main_toolbar.move_folder_menu.folder_selected.connect(on_move_conversation); this.main_toolbar.copy_folder_menu.folder_selected.connect(on_copy_conversation); this.main_toolbar.bind_property("search-open", this.search_bar, "search-mode-enabled", diff --git a/ui/application-main-window.ui b/ui/application-main-window.ui index 4a948294..872f31bd 100644 --- a/ui/application-main-window.ui +++ b/ui/application-main-window.ui @@ -138,4 +138,25 @@ + + horizontal + + + + + + horizontal + + + horizontal + + + + + + horizontal + + + horizontal + From 12a7101ba55bb646161e9997485036744121f542 Mon Sep 17 00:00:00 2001 From: Adrien Plazas Date: Thu, 24 Oct 2019 14:36:04 +0200 Subject: [PATCH 07/19] Use leaflets in the UI There is no way to navigate into the app, but it's a start. v2: replace expand with child vexpand --- .../application/application-main-window.vala | 8 +-- src/client/components/main-toolbar.vala | 5 +- .../conversation-viewer.vala | 4 ++ ui/application-main-window.ui | 52 ++++++++++++++----- ui/geary.css | 11 ++++ ui/main-toolbar.ui | 33 +++++++----- 6 files changed, 81 insertions(+), 32 deletions(-) diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala index 4c6d1038..b56079cf 100644 --- a/src/client/application/application-main-window.vala +++ b/src/client/application/application-main-window.vala @@ -317,9 +317,9 @@ public class Application.MainWindow : [GtkChild] private Gtk.Box main_layout; [GtkChild] - private Gtk.Paned folder_paned; + private Hdy.Leaflet main_leaflet; [GtkChild] - private Gtk.Paned conversations_paned; + private Hdy.Leaflet conversations_leaflet; [GtkChild] private Gtk.Box folder_box; [GtkChild] @@ -520,7 +520,6 @@ public class Application.MainWindow : }); setup_layout(application.config); - this.folder_paned.orientation = Gtk.Orientation.HORIZONTAL; this.folder_box.pack_start(status_bar, false, false); update_command_actions(); @@ -1249,8 +1248,9 @@ public class Application.MainWindow : on_conversation_view_added ); - this.conversations_paned.pack2(this.conversation_viewer, true, false); + this.conversation_viewer.hexpand = true; this.conversation_size_group.add_widget(this.conversation_viewer); + this.main_leaflet.add_with_properties(this.conversation_viewer, "name", "conversation", null); // Main toolbar this.main_toolbar = new MainToolbar(config); diff --git a/src/client/components/main-toolbar.vala b/src/client/components/main-toolbar.vala index 6ea472a8..286546d5 100644 --- a/src/client/components/main-toolbar.vala +++ b/src/client/components/main-toolbar.vala @@ -6,7 +6,7 @@ // Draws the main toolbar. [GtkTemplate (ui = "/org/gnome/Geary/main-toolbar.ui")] -public class MainToolbar : Gtk.Box { +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 @@ -97,7 +97,8 @@ public class MainToolbar : Gtk.Box { public void set_conversation_header(Gtk.HeaderBar header) { conversation_header.hide(); this.header_group.add_gtk_header_bar(header); - pack_start(header, true, true); + header.hexpand = true; + add(header); } public void remove_conversation_header(Gtk.HeaderBar header) { diff --git a/src/client/conversation-viewer/conversation-viewer.vala b/src/client/conversation-viewer/conversation-viewer.vala index f04a1d26..eec5f6a4 100644 --- a/src/client/conversation-viewer/conversation-viewer.vala +++ b/src/client/conversation-viewer/conversation-viewer.vala @@ -68,6 +68,10 @@ public class ConversationViewer : Gtk.Stack, Geary.BaseInterface { public signal void conversation_removed(ConversationListBox list); + static construct { + set_css_name("geary-conversation-viewer"); + } + /** * Constructs a new conversation view instance. */ diff --git a/ui/application-main-window.ui b/ui/application-main-window.ui index 872f31bd..f7bb884e 100644 --- a/ui/application-main-window.ui +++ b/ui/application-main-window.ui @@ -24,13 +24,18 @@ False vertical - + True True + True + over - + True True + True + True + over True @@ -40,6 +45,7 @@ True False + True 0 in @@ -55,15 +61,26 @@ - True True 0 - False - False + folder + + + + + True + False + vertical + + + + False @@ -97,22 +114,27 @@ - True - False + conversations + + + conversations + + + + + True + False + vertical - False - False + False - - - True @@ -146,6 +168,9 @@ horizontal + + + horizontal @@ -155,6 +180,9 @@ horizontal + + + horizontal diff --git a/ui/geary.css b/ui/geary.css index 0ddfab30..2d1d48c3 100644 --- a/ui/geary.css +++ b/ui/geary.css @@ -12,12 +12,23 @@ border-left-width: 0; border-top-width: 0; border-right-width: 0; + min-width: 300px; } .geary-conversation-frame > border { border-left-width: 0; border-top-width: 0; border-right-width: 0; + min-width: 360px; } + +treeview.sidebar { + border: none; +} + +geary-conversation-viewer { + min-width: 360px; +} + /* For 3-pane mode only */ .geary-sidebar-pane-separator.vertical .conversation-frame > border { border-bottom-width: 0; diff --git a/ui/main-toolbar.ui b/ui/main-toolbar.ui index 732a01b9..441cd146 100644 --- a/ui/main-toolbar.ui +++ b/ui/main-toolbar.ui @@ -7,13 +7,18 @@ False mail-archive-symbolic - - + + + + + + + + From 1f2896e9a7ba02c0a909c91bb551e596d2d72230 Mon Sep 17 00:00:00 2001 From: Julian Sparber Date: Thu, 1 Oct 2020 10:40:41 +0200 Subject: [PATCH 14/19] composer: Switch leaflet to composer when folded --- src/client/application/application-main-window.vala | 2 ++ src/client/components/main-toolbar.vala | 13 +++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala index 97960660..c2b0954b 100644 --- a/src/client/application/application-main-window.vala +++ b/src/client/application/application-main-window.vala @@ -916,6 +916,8 @@ public class Application.MainWindow : } else { this.conversation_viewer.do_compose(composer); } + // Show the correct leaflet + this.main_leaflet.set_visible_child_name("conversation"); } } diff --git a/src/client/components/main-toolbar.vala b/src/client/components/main-toolbar.vala index c7bdab8f..0ecb599a 100644 --- a/src/client/components/main-toolbar.vala +++ b/src/client/components/main-toolbar.vala @@ -64,6 +64,8 @@ public class MainToolbar : Hdy.Leaflet { [GtkChild] private Hdy.HeaderGroup header_group; + Gtk.SizeGroup conversation_group; + private bool show_trash_button = true; // Load these at construction time @@ -98,16 +100,22 @@ public class MainToolbar : Hdy.Leaflet { } public void set_conversation_header(Gtk.HeaderBar header) { - conversation_header.hide(); + remove(conversation_header); this.header_group.add_gtk_header_bar(header); header.hexpand = true; + conversation_group.remove_widget(conversation_header); + conversation_group.add_widget(header); add(header); + child_set(header, "name", "conversation", null); } public void remove_conversation_header(Gtk.HeaderBar header) { remove(header); this.header_group.remove_gtk_header_bar(header); - conversation_header.show(); + conversation_group.remove_widget(header); + conversation_group.add_widget(conversation_header); + add(conversation_header); + child_set(conversation_header, "name", "conversation", null); } public void update_trash_button(bool show_trash) { @@ -125,6 +133,7 @@ public class MainToolbar : Hdy.Leaflet { conversations_group.add_widget(conversations_header); conversations_separator_group.add_widget(conversations_separator); conversation_group.add_widget(conversation_header); + this.conversation_group = conversation_group; } public void add_to_swipe_groups(Hdy.SwipeGroup conversations_group, From 0b743ab0d396cbbaad5aebaebed235ecd0f1a564 Mon Sep 17 00:00:00 2001 From: Julian Sparber Date: Tue, 6 Oct 2020 13:34:30 +0200 Subject: [PATCH 15/19] conversation-list: use shift+activate to open conversation in new window Open on shift+double click and shift+space/enter the selected conversation in a new window. --- .../application/application-main-window.vala | 14 ++--- .../conversation-list-view.vala | 61 ++++++++++++++++--- 2 files changed, 58 insertions(+), 17 deletions(-) diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala index c2b0954b..01b7b9c6 100644 --- a/src/client/application/application-main-window.vala +++ b/src/client/application/application-main-window.vala @@ -2143,17 +2143,16 @@ public class Application.MainWindow : focus_next_pane(); } - private void on_conversation_activated(Geary.App.Conversation activated) { - if (main_leaflet.folded) { - focus_next_pane(); - } - /* TODO: find correct UX for opening the conversation in a new window - if (this.selected_folder != null) { + private void on_conversation_activated(Geary.App.Conversation activated, bool single) { + if (single) { + if (main_leaflet.folded) + focus_next_pane(); + } else if (this.selected_folder != null) { if (this.selected_folder.used_as != DRAFTS) { this.application.new_window.begin( this.selected_folder, this.conversation_list_view.copy_selected() - ); + ); } else { // TODO: Determine how to map between conversations // and drafts correctly. @@ -2166,7 +2165,6 @@ public class Application.MainWindow : ); } } - */ } private void on_find_in_conversation_action() { diff --git a/src/client/conversation-list/conversation-list-view.vala b/src/client/conversation-list/conversation-list-view.vala index dcced90d..0baa66b4 100644 --- a/src/client/conversation-list/conversation-list-view.vala +++ b/src/client/conversation-list/conversation-list-view.vala @@ -17,6 +17,7 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface { private Geary.Scheduler.Scheduled? scheduled_update_visible_conversations = null; private Gee.Set selected = new Gee.HashSet(); private Geary.IdleManager selection_update; + private Gtk.GestureMultiPress gesture; // Determines if the next folder scan should avoid selecting a // conversation when autoselect is enabled @@ -26,7 +27,7 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface { public signal void conversations_selected(Gee.Set selected); // Signal for when a conversation has been double-clicked, or selected and enter is pressed. - public signal void conversation_activated(Geary.App.Conversation activated); + public signal void conversation_activated(Geary.App.Conversation activated, bool single = false); public virtual signal void load_more() { enable_load_more = false; @@ -42,7 +43,6 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface { base_ref(); set_show_expanders(false); set_headers_visible(false); - set_activate_on_single_click(true); this.config = config; @@ -53,11 +53,13 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface { Gtk.TreeSelection selection = get_selection(); selection.set_mode(Gtk.SelectionMode.MULTIPLE); style_updated.connect(on_style_changed); - row_activated.connect(on_row_activated); notify["vadjustment"].connect(on_vadjustment_changed); + key_press_event.connect(on_key_press); button_press_event.connect(on_button_press); + gesture = new Gtk.GestureMultiPress(this); + gesture.pressed.connect(on_gesture_pressed); // Set up drag and drop. Gtk.drag_source_set(this, Gdk.ModifierType.BUTTON1_MASK, FolderList.Tree.TARGET_ENTRY_LIST, @@ -270,6 +272,53 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface { return parent.get_vadjustment(); } + private void on_gesture_pressed(int n_press, double x, double y) { + if (gesture.get_current_button() != Gdk.BUTTON_PRIMARY) + return; + + Gtk.TreePath? path; + get_path_at_pos((int) x, (int) y, out path, null, null, null); + + // If the user clicked in an empty area, do nothing. + if (path == null) + return; + + Geary.App.Conversation? c = get_model().get_conversation_at_path(path); + if (c == null) + return; + + Gdk.Event event = gesture.get_last_event(gesture.get_current_sequence()); + Gdk.ModifierType modifiers = Gtk.accelerator_get_default_mod_mask(); + + Gdk.ModifierType state_mask; + event.get_state(out state_mask); + + if ((state_mask & modifiers) == 0 && n_press == 1) { + conversation_activated(c, true); + } else if ((state_mask & modifiers) == Gdk.ModifierType.SHIFT_MASK && n_press == 2) { + conversation_activated(c); + } + } + + private bool on_key_press(Gdk.EventKey event) { + if (this.selected.size != 1) + return false; + + Geary.App.Conversation? c = this.selected.to_array()[0]; + if (c == null) + return false; + + Gdk.ModifierType modifiers = Gtk.accelerator_get_default_mod_mask(); + + if (event.keyval == Gdk.Key.Return || + event.keyval == Gdk.Key.ISO_Enter || + event.keyval == Gdk.Key.KP_Enter || + event.keyval == Gdk.Key.space || + event.keyval == Gdk.Key.KP_Space) + conversation_activated(c, !((event.state & modifiers) == Gdk.ModifierType.SHIFT_MASK)); + return false; + } + private bool on_button_press(Gdk.EventButton event) { // Get the coordinates on the cell as well as the clicked path. int cell_x; @@ -576,12 +625,6 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface { return false; } - private void on_row_activated(Gtk.TreePath path) { - Geary.App.Conversation? c = get_model().get_conversation_at_path(path); - if (c != null) - conversation_activated(c); - } - // Enable/disable hover effect on all selected cells. private void set_hover_selected(bool hover) { ConversationListCellRenderer.set_hover_selected(hover); From d6c546e2d555d20e1dd259117822b4d4c8b7152c Mon Sep 17 00:00:00 2001 From: Julian Sparber Date: Tue, 6 Oct 2020 17:31:49 +0200 Subject: [PATCH 16/19] composer: close the composer when navigating back --- src/client/application/application-main-window.vala | 11 +++++++++++ src/client/composer/composer-widget.vala | 5 +++++ ui/application-main-window.ui | 2 ++ 3 files changed, 18 insertions(+) diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala index 01b7b9c6..e1e55d0e 100644 --- a/src/client/application/application-main-window.vala +++ b/src/client/application/application-main-window.vala @@ -1955,6 +1955,17 @@ public class Application.MainWindow : return Gdk.EVENT_STOP; } + [GtkCallback] + private void on_main_leaflet_visible_child_changed() { + if (main_leaflet.child_transition_running) + return; + + if (main_leaflet.visible_child_name == "conversations" && main_leaflet.folded) + if (this.conversation_viewer.current_composer != null) { + this.conversation_viewer.current_composer.activate_close_action(); + } + } + private void on_offline_infobar_response() { this.info_bars.remove(this.offline_infobar); } diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala index 37e93fb4..17430021 100644 --- a/src/client/composer/composer-widget.vala +++ b/src/client/composer/composer-widget.vala @@ -1361,6 +1361,11 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { } } + /* Activate the close action */ + public void activate_close_action() { + this.actions.activate_action(ACTION_CLOSE, null); + } + internal void set_mode(PresentationMode new_mode) { this.current_mode = new_mode; this.header.set_mode(new_mode); diff --git a/ui/application-main-window.ui b/ui/application-main-window.ui index fe66491b..cbaacbcf 100644 --- a/ui/application-main-window.ui +++ b/ui/application-main-window.ui @@ -29,6 +29,8 @@ True True over + + True From f8f223da46716a0a888ef5a5a0840fb69dbc7067 Mon Sep 17 00:00:00 2001 From: Julian Sparber Date: Wed, 7 Oct 2020 10:36:50 +0200 Subject: [PATCH 17/19] main-window: Block forward navigation when viewer is empty Empty viewer includes: - no selected conversation - no conversation in folder - selected more then one conversation --- src/client/application/application-main-window.vala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala index e1e55d0e..3b7c29e7 100644 --- a/src/client/application/application-main-window.vala +++ b/src/client/application/application-main-window.vala @@ -1811,8 +1811,11 @@ public class Application.MainWindow : conversations_leaflet.navigate(Hdy.NavigationDirection.FORWARD); focus = this.conversation_list_view; } else { - main_leaflet.navigate(Hdy.NavigationDirection.FORWARD); - focus = this.conversation_viewer.visible_child; + if (this.main_toolbar.selected_conversations == 1 && + this.selected_folder.properties.email_total > 0) { + main_leaflet.navigate(Hdy.NavigationDirection.FORWARD); + focus = this.conversation_viewer.visible_child; + } } } } else if (focus != null) { From 98017cfb390dd13dd5688711f8164e1a5e9bdcdf Mon Sep 17 00:00:00 2001 From: Julian Sparber Date: Thu, 8 Oct 2020 14:38:31 +0200 Subject: [PATCH 18/19] main-toolbar: Create object containing conversation actions This creates a new object that contain the 4 groups of actions that used to be in the conversation-viewer headerbar. This allows the widgets to be moved to differen locations, e.g. to an action bar that will be added in a later commit. --- po/POTFILES.in | 2 + .../application/application-main-window.vala | 61 +++-- .../components-conversation-actions.vala | 132 ++++++++++ src/client/components/main-toolbar.vala | 96 +------- src/client/meson.build | 1 + ui/application-main-window.ui | 2 +- ui/components-conversation-actions.ui | 221 +++++++++++++++++ ui/main-toolbar.ui | 230 ------------------ ui/org.gnome.Geary.gresource.xml | 1 + 9 files changed, 405 insertions(+), 341 deletions(-) create mode 100644 src/client/components/components-conversation-actions.vala create mode 100644 ui/components-conversation-actions.ui diff --git a/po/POTFILES.in b/po/POTFILES.in index 09c3c970..7ef4e050 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -39,6 +39,7 @@ src/client/application/secret-mediator.vala src/client/client-action.vala src/client/components/client-web-view.vala src/client/components/components-attachment-pane.vala +src/client/components/components-conversation-actions.vala src/client/components/components-entry-undo.vala src/client/components/components-in-app-notification.vala src/client/components/components-info-bar-stack.vala @@ -455,6 +456,7 @@ ui/composer-widget.ui ui/components-attachment-pane.ui ui/components-attachment-pane-menus.ui ui/components-attachment-view.ui +ui/components-conversation-actions.ui ui/components-in-app-notification.ui ui/components-inspector-error-view.ui ui/components-inspector-log-view.ui diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala index 3b7c29e7..20fc3758 100644 --- a/src/client/application/application-main-window.vala +++ b/src/client/application/application-main-window.vala @@ -284,6 +284,9 @@ public class Application.MainWindow : public ConversationListView conversation_list_view { get; private set; } public ConversationViewer conversation_viewer { get; private set; } + // Actions in the Conversation HeaderBar or ActionBar + private Components.ConversationActions conversation_actions; + public Components.InfoBarStack conversation_list_info_bars { get; private set; default = new Components.InfoBarStack(PRIORITY_QUEUE); } @@ -692,10 +695,10 @@ public class Application.MainWindow : // selected model. if (this.selected_folder != null) { - this.main_toolbar.copy_folder_menu.enable_disable_folder( + this.conversation_actions.copy_folder_menu.enable_disable_folder( this.selected_folder, true ); - this.main_toolbar.move_folder_menu.enable_disable_folder( + this.conversation_actions.move_folder_menu.enable_disable_folder( this.selected_folder, true ); @@ -740,9 +743,10 @@ public class Application.MainWindow : update_conversation_actions(NONE); update_title(); - this.main_toolbar.update_trash_button( + this.conversation_actions.update_trash_button( !this.is_shift_down && this.selected_folder_supports_trash ); + this.conversation_viewer.show_loading(); this.previous_selection_was_interactive = is_interactive; @@ -779,10 +783,10 @@ public class Application.MainWindow : this.conversation_list_view.set_model(conversations_model); // disable copy/move to the new folder - this.main_toolbar.copy_folder_menu.enable_disable_folder( + this.conversation_actions.copy_folder_menu.enable_disable_folder( to_select, false ); - this.main_toolbar.move_folder_menu.enable_disable_folder( + this.conversation_actions.move_folder_menu.enable_disable_folder( to_select, false ); @@ -1090,8 +1094,8 @@ public class Application.MainWindow : foreach (var context in to_add) { this.folder_list.add_folder(context); if (context.folder.account == this.selected_account) { - this.main_toolbar.copy_folder_menu.add_folder(context.folder); - this.main_toolbar.move_folder_menu.add_folder(context.folder); + this.conversation_actions.copy_folder_menu.add_folder(context.folder); + this.conversation_actions.move_folder_menu.add_folder(context.folder); } context.folder.use_changed.connect(on_use_changed); } @@ -1111,8 +1115,8 @@ public class Application.MainWindow : folder.use_changed.disconnect(on_use_changed); if (folder.account == this.selected_account) { - this.main_toolbar.copy_folder_menu.remove_folder(folder); - this.main_toolbar.move_folder_menu.remove_folder(folder); + this.conversation_actions.copy_folder_menu.remove_folder(folder); + this.conversation_actions.move_folder_menu.remove_folder(folder); } this.folder_list.remove_folder(context); } @@ -1228,6 +1232,7 @@ public class Application.MainWindow : this.search_bar.search_text_changed.connect(on_search); this.conversation_list_box.pack_start(this.search_bar, false, false, 0); + // Folder list this.folder_list.folder_selected.connect(on_folder_selected); this.folder_list.move_conversation.connect(on_move_conversation); @@ -1261,6 +1266,16 @@ public class Application.MainWindow : this.conversation_size_group.add_widget(this.conversation_viewer); this.main_leaflet.add_with_properties(this.conversation_viewer, "name", "conversation", null); + + // Setup conversation actions + this.conversation_actions = new Components.ConversationActions(); + this.conversation_actions.move_folder_menu.folder_selected.connect(on_move_conversation); + this.conversation_actions.copy_folder_menu.folder_selected.connect(on_copy_conversation); + this.conversation_actions.bind_property("find-open", + this.conversation_viewer.conversation_find_bar, + "search-mode-enabled", + BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); + // Main toolbar this.main_toolbar = new MainToolbar(config); this.main_toolbar.add_to_size_groups(this.folder_size_group, @@ -1270,12 +1285,8 @@ public class Application.MainWindow : this.conversation_size_group); this.main_toolbar.add_to_swipe_groups(this.conversations_swipe_group, this.conversation_swipe_group); - this.main_toolbar.move_folder_menu.folder_selected.connect(on_move_conversation); - this.main_toolbar.copy_folder_menu.folder_selected.connect(on_copy_conversation); this.main_toolbar.bind_property("search-open", this.search_bar, "search-mode-enabled", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); - this.main_toolbar.bind_property("find-open", this.conversation_viewer.conversation_find_bar, - "search-mode-enabled", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); if (config.desktop_environment == UNITY) { this.main_toolbar.show_close_button = false; this.main_layout.pack_start(main_toolbar, false, true, 0); @@ -1286,6 +1297,8 @@ public class Application.MainWindow : set_titlebar(titlebar); } + this.main_toolbar.add_conversation_actions(this.conversation_actions); + this.main_layout.pack_start(this.info_bars, false, true, 0); // Status bar @@ -1462,8 +1475,8 @@ public class Application.MainWindow : private void select_account(Geary.Account? account) { if (this.selected_account != account) { if (this.selected_account != null) { - this.main_toolbar.copy_folder_menu.clear(); - this.main_toolbar.move_folder_menu.clear(); + this.conversation_actions.copy_folder_menu.clear(); + this.conversation_actions.move_folder_menu.clear(); } this.selected_account = account; @@ -1471,8 +1484,8 @@ public class Application.MainWindow : if (account != null) { foreach (Geary.Folder folder in account.list_folders()) { - this.main_toolbar.copy_folder_menu.add_folder(folder); - this.main_toolbar.move_folder_menu.add_folder(folder); + this.conversation_actions.copy_folder_menu.add_folder(folder); + this.conversation_actions.move_folder_menu.add_folder(folder); } } @@ -1494,7 +1507,7 @@ public class Application.MainWindow : // setting it again. this.conversation_list_view.select_conversations(to_select); - this.main_toolbar.selected_conversations = to_select.size; + this.conversation_actions.selected_conversations = to_select.size; if (this.selected_folder != null && !this.has_composer) { switch(to_select.size) { case 0: @@ -1707,13 +1720,13 @@ public class Application.MainWindow : bool move_enabled = ( sensitive && (selected_folder is Geary.FolderSupport.Move) ); - this.main_toolbar.move_message_button.set_sensitive(move_enabled); + this.conversation_actions.move_message_button.set_sensitive(move_enabled); get_window_action(ACTION_SHOW_MOVE_MENU).set_enabled(move_enabled); bool copy_enabled = ( sensitive && (selected_folder is Geary.FolderSupport.Copy) ); - this.main_toolbar.copy_message_button.set_sensitive(copy_enabled); + this.conversation_actions.copy_message_button.set_sensitive(copy_enabled); get_window_action(ACTION_SHOW_COPY_MENU).set_enabled(move_enabled); get_window_action(ACTION_ARCHIVE_CONVERSATION).set_enabled( @@ -1782,7 +1795,7 @@ public class Application.MainWindow : private void set_shift_key_down(bool down) { this.is_shift_down = down; - this.main_toolbar.update_trash_button( + this.conversation_actions.update_trash_button( !down && this.selected_folder_supports_trash ); } @@ -1811,7 +1824,7 @@ public class Application.MainWindow : conversations_leaflet.navigate(Hdy.NavigationDirection.FORWARD); focus = this.conversation_list_view; } else { - if (this.main_toolbar.selected_conversations == 1 && + if (this.conversation_actions.selected_conversations == 1 && this.selected_folder.properties.email_total > 0) { main_leaflet.navigate(Hdy.NavigationDirection.FORWARD); focus = this.conversation_viewer.visible_child; @@ -2215,11 +2228,11 @@ public class Application.MainWindow : } private void on_show_copy_menu() { - this.main_toolbar.copy_message_button.clicked(); + this.conversation_actions.copy_message_button.clicked(); } private void on_show_move_menu() { - this.main_toolbar.move_message_button.clicked(); + this.conversation_actions.move_message_button.clicked(); } private void on_conversation_up() { diff --git a/src/client/components/components-conversation-actions.vala b/src/client/components/components-conversation-actions.vala new file mode 100644 index 00000000..a8cdaed9 --- /dev/null +++ b/src/client/components/components-conversation-actions.vala @@ -0,0 +1,132 @@ +/* Copyright 2017 Software Freedom Conservancy Inc. + * + * This software is licensed under the GNU Lesser General Public License + * (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 + */ +public class Components.ConversationActions : GLib.Object { + public Gtk.Widget? owner { get; private set; } + // Copy and Move popovers + 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 Gtk.Box reply_forward_buttons { get; private set; } + + public Gtk.Box archive_trash_delete_buttons { get; private set; } + private Gtk.Button archive_button; + private Gtk.Button trash_delete_button; + + public Gtk.ToggleButton find_button { get; private set; } + + private bool show_trash_button = true; + + // Load these at construction time + 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"); + + this.find_button = (Gtk.ToggleButton) builder.get_object("find_button"); + + 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; + } + + /** 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; + } + + private void remove_parent (Gtk.Widget widget) { + if (widget.parent != null) + widget.parent.remove(widget); + } + + public void update_trash_button(bool show_trash) { + this.show_trash_button = show_trash; + update_conversation_buttons(); + } + + /** Updates tooltip text depending on number of conversations selected. */ + private void update_conversation_buttons() { + this.mark_message_button.tooltip_text = ngettext( + "Mark conversation", + "Mark conversations", + this.selected_conversations + ); + this.copy_message_button.tooltip_text = ngettext( + "Add label to conversation", + "Add label to conversations", + this.selected_conversations + ); + this.move_message_button.tooltip_text = ngettext( + "Move conversation", + "Move conversations", + this.selected_conversations + ); + this.archive_button.tooltip_text = ngettext( + "Archive conversation", + "Archive conversations", + this.selected_conversations + ); + + if (this.show_trash_button) { + this.trash_delete_button.action_name = Action.Window.prefix( + Application.MainWindow.ACTION_TRASH_CONVERSATION + ); + this.trash_delete_button.image = trash_image; + this.trash_delete_button.tooltip_text = ngettext( + "Move conversation to Trash", + "Move conversations to Trash", + this.selected_conversations + ); + } else { + this.trash_delete_button.action_name = Action.Window.prefix( + Application.MainWindow.ACTION_DELETE_CONVERSATION + ); + this.trash_delete_button.image = delete_image; + this.trash_delete_button.tooltip_text = ngettext( + "Delete conversation", + "Delete conversations", + this.selected_conversations + ); + } + } +} diff --git a/src/client/components/main-toolbar.vala b/src/client/components/main-toolbar.vala index 0ecb599a..6458b7fb 100644 --- a/src/client/components/main-toolbar.vala +++ b/src/client/components/main-toolbar.vala @@ -14,15 +14,8 @@ public class MainToolbar : Hdy.Leaflet { public string folder { get; set; } // Close button settings public bool show_close_button { get; set; default = true; } - // Search and find bar + // Search bar public bool search_open { get; set; default = false; } - public bool find_open { get; set; default = false; } - // Copy and Move popovers - 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; } - [GtkChild] private Hdy.Leaflet conversations_leaflet; @@ -48,31 +41,12 @@ public class MainToolbar : Hdy.Leaflet { // Conversation header elements [GtkChild] private Gtk.HeaderBar conversation_header; - [GtkChild] - private Gtk.MenuButton mark_message_button; - [GtkChild] - public Gtk.MenuButton copy_message_button; - [GtkChild] - public Gtk.MenuButton move_message_button; - [GtkChild] - private Gtk.Button archive_button; - [GtkChild] - private Gtk.Button trash_delete_button; - [GtkChild] - private Gtk.ToggleButton find_button; [GtkChild] private Hdy.HeaderGroup header_group; Gtk.SizeGroup conversation_group; - private bool show_trash_button = true; - - // Load these at construction time - 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 MainToolbar(Application.Configuration config) { if (config.desktop_environment != UNITY) { this.bind_property("account", this.conversations_header, "title", BindingFlags.SYNC_CREATE); @@ -82,21 +56,22 @@ public class MainToolbar : Hdy.Leaflet { // Assemble the main/mark menus Gtk.Builder builder = new Gtk.Builder.from_resource("/org/gnome/Geary/main-toolbar-menus.ui"); MenuModel main_menu = (MenuModel) builder.get_object("main_menu"); - MenuModel mark_menu = (MenuModel) builder.get_object("mark_message_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); + } - // Setup conversation header elements - 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; + public void add_conversation_actions(Components.ConversationActions actions) { + if (actions.owner == this) + return; - this.bind_property("find-open", this.find_button, "active", - BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); + actions.take_ownership(this); + conversation_header.pack_start(actions.mark_copy_move_buttons); + conversation_header.pack_start(actions.reply_forward_buttons); + conversation_header.pack_end(actions.find_button); + conversation_header.pack_end(actions.archive_trash_delete_buttons); } public void set_conversation_header(Gtk.HeaderBar header) { @@ -118,11 +93,6 @@ public class MainToolbar : Hdy.Leaflet { child_set(conversation_header, "name", "conversation", null); } - public void update_trash_button(bool show_trash) { - this.show_trash_button = show_trash; - update_conversation_buttons(); - } - public void add_to_size_groups(Gtk.SizeGroup folder_group, Gtk.SizeGroup folder_separator_group, Gtk.SizeGroup conversations_group, @@ -141,50 +111,4 @@ public class MainToolbar : Hdy.Leaflet { conversations_group.add_swipeable(this.conversations_leaflet); conversation_group.add_swipeable(this); } - - // Updates tooltip text depending on number of conversations selected. - private void update_conversation_buttons() { - this.mark_message_button.tooltip_text = ngettext( - "Mark conversation", - "Mark conversations", - this.selected_conversations - ); - this.copy_message_button.tooltip_text = ngettext( - "Add label to conversation", - "Add label to conversations", - this.selected_conversations - ); - this.move_message_button.tooltip_text = ngettext( - "Move conversation", - "Move conversations", - this.selected_conversations - ); - this.archive_button.tooltip_text = ngettext( - "Archive conversation", - "Archive conversations", - this.selected_conversations - ); - - if (this.show_trash_button) { - this.trash_delete_button.action_name = Action.Window.prefix( - Application.MainWindow.ACTION_TRASH_CONVERSATION - ); - this.trash_delete_button.image = trash_image; - this.trash_delete_button.tooltip_text = ngettext( - "Move conversation to Trash", - "Move conversations to Trash", - this.selected_conversations - ); - } else { - this.trash_delete_button.action_name = Action.Window.prefix( - Application.MainWindow.ACTION_DELETE_CONVERSATION - ); - this.trash_delete_button.image = delete_image; - this.trash_delete_button.tooltip_text = ngettext( - "Delete conversation", - "Delete conversations", - this.selected_conversations - ); - } - } } diff --git a/src/client/meson.build b/src/client/meson.build index 088f4e47..ed0d6b33 100644 --- a/src/client/meson.build +++ b/src/client/meson.build @@ -48,6 +48,7 @@ client_vala_sources = files( 'components/client-web-view.vala', 'components/components-attachment-pane.vala', + 'components/components-conversation-actions.vala', 'components/components-entry-undo.vala', 'components/components-info-bar-stack.vala', 'components/components-info-bar.vala', diff --git a/ui/application-main-window.ui b/ui/application-main-window.ui index cbaacbcf..547b063f 100644 --- a/ui/application-main-window.ui +++ b/ui/application-main-window.ui @@ -111,7 +111,7 @@ True True end - 0 + 1 diff --git a/ui/components-conversation-actions.ui b/ui/components-conversation-actions.ui new file mode 100644 index 00000000..b554deda --- /dev/null +++ b/ui/components-conversation-actions.ui @@ -0,0 +1,221 @@ + + + + + + True + False + + + True + True + False + False + True + + + True + False + marker-symbolic + + + + + False + True + 0 + + + + + True + True + False + False + True + + + True + False + tag-symbolic + + + + + False + True + 1 + + + + + True + True + False + False + True + + + True + False + folder-symbolic + + + + + False + True + 2 + + + + + + True + False + + + True + True + False + False + Reply + win.reply-conversation + True + + + True + False + mail-reply-sender-symbolic + + + + + False + True + 0 + + + + + True + True + False + False + Reply All + win.reply-all-conversation + True + + + True + False + mail-reply-all-symbolic + + + + + False + True + 1 + + + + + True + True + False + False + Forward + win.forward-conversation + True + + + True + False + mail-forward-symbolic + + + + + False + True + 2 + + + + + + True + False + mail-archive-symbolic + + + True + False + + + _Archive + True + True + False + False + win.archive-conversation + archive_image + True + True + + + False + True + 0 + + + + + True + True + False + False + win.trash-conversation + True + + + True + False + user-trash-symbolic + + + + + False + True + 1 + + + + + + True + True + False + False + Toggle find bar + True + + + True + False + preferences-system-search-symbolic + + + + diff --git a/ui/main-toolbar.ui b/ui/main-toolbar.ui index 5d58af6f..64f888ee 100644 --- a/ui/main-toolbar.ui +++ b/ui/main-toolbar.ui @@ -189,236 +189,6 @@ - - - True - False - - - True - True - False - False - Reply - win.reply-conversation - True - - - True - False - mail-reply-sender-symbolic - - - - - False - True - 0 - - - - - True - True - False - False - Reply All - win.reply-all-conversation - True - - - True - False - mail-reply-all-symbolic - - - - - False - True - 1 - - - - - True - True - False - False - Forward - win.forward-conversation - True - - - True - False - mail-forward-symbolic - - - - - False - True - 2 - - - - - - - - True - False - - - True - True - False - False - True - - - True - False - marker-symbolic - - - - - False - True - 0 - - - - - True - True - False - False - True - - - True - False - tag-symbolic - - - - - False - True - 1 - - - - - True - True - False - False - True - - - True - False - folder-symbolic - - - - - False - True - 2 - - - - - - 1 - - - - - True - True - False - False - Toggle find bar - True - - - True - False - preferences-system-search-symbolic - - - - - end - 2 - - - - - True - False - - - _Archive - True - True - False - False - win.archive-conversation - archive_image - True - True - - - False - True - 0 - - - - - True - True - False - False - win.trash-conversation - True - - - True - False - user-trash-symbolic - - - - - False - True - 1 - - - - - - end - 4 - - conversation diff --git a/ui/org.gnome.Geary.gresource.xml b/ui/org.gnome.Geary.gresource.xml index 0cdca875..481bbff4 100644 --- a/ui/org.gnome.Geary.gresource.xml +++ b/ui/org.gnome.Geary.gresource.xml @@ -14,6 +14,7 @@ components-attachment-pane.ui components-attachment-pane-menus.ui components-attachment-view.ui + components-conversation-actions.ui components-in-app-notification.ui components-inspector.ui components-inspector-error-view.ui From 70a40893a3f41483fbc607512d311f4c0c44c79a Mon Sep 17 00:00:00 2001 From: Julian Sparber Date: Thu, 8 Oct 2020 14:50:58 +0200 Subject: [PATCH 19/19] action-bar: Add an action bar to the conversations list (2-panel) This moves the actions from the headerbar to the action bar at the bottom of the conversations list when multiple conversations are selected. This changes is needed so that the user can still interact with the conversations when folded. This also hides the actions from the Headerbar and action bar when no conversation is selected. --- po/POTFILES.in | 2 + .../application/application-main-window.vala | 19 +++++++++ .../components-conversation-action-bar.vala | 39 +++++++++++++++++++ src/client/meson.build | 1 + ui/components-conversation-action-bar.ui | 22 +++++++++++ ui/org.gnome.Geary.gresource.xml | 1 + 6 files changed, 84 insertions(+) create mode 100644 src/client/components/components-conversation-action-bar.vala create mode 100644 ui/components-conversation-action-bar.ui diff --git a/po/POTFILES.in b/po/POTFILES.in index 7ef4e050..68e3ca34 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -40,6 +40,7 @@ src/client/client-action.vala src/client/components/client-web-view.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 @@ -457,6 +458,7 @@ 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-in-app-notification.ui ui/components-inspector-error-view.ui ui/components-inspector-log-view.ui diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala index 20fc3758..0e6a89e1 100644 --- a/src/client/application/application-main-window.vala +++ b/src/client/application/application-main-window.vala @@ -351,6 +351,8 @@ public class Application.MainWindow : [GtkChild] private Gtk.Overlay overlay; + private Components.ConversationActionBar action_bar; + private Components.InfoBarStack info_bars = new Components.InfoBarStack(SINGLE); @@ -1308,6 +1310,12 @@ public class Application.MainWindow : this.spinner.set_progress_monitor(progress_monitor); this.status_bar.add(this.spinner); this.status_bar.show_all(); + + // Action bar + this.action_bar = new Components.ConversationActionBar(); + this.conversation_list_box.add_with_properties(action_bar, + "pack-type", Gtk.PackType.END, + "position", 0); } /** {@inheritDoc} */ @@ -1740,6 +1748,17 @@ public class Application.MainWindow : ); this.update_context_dependent_actions.begin(sensitive); + switch (count) { + case NONE: + conversation_actions.take_ownership(null); + break; + case SINGLE: + this.main_toolbar.add_conversation_actions(this.conversation_actions); + break; + case MULTIPLE: + this.action_bar.add_conversation_actions(this.conversation_actions); + break; + } } private async void update_context_dependent_actions(bool sensitive) { diff --git a/src/client/components/components-conversation-action-bar.vala b/src/client/components/components-conversation-action-bar.vala new file mode 100644 index 00000000..cb574521 --- /dev/null +++ b/src/client/components/components-conversation-action-bar.vala @@ -0,0 +1,39 @@ +/* + * 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 { + private ulong owner_notify; + + [GtkChild] + private Gtk.Box action_box; + + public ConversationActionBar() { + } + + /** + * 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); + } + }); + } +} diff --git a/src/client/meson.build b/src/client/meson.build index ed0d6b33..c0eb0c16 100644 --- a/src/client/meson.build +++ b/src/client/meson.build @@ -49,6 +49,7 @@ client_vala_sources = files( 'components/client-web-view.vala', 'components/components-attachment-pane.vala', 'components/components-conversation-actions.vala', + 'components/components-conversation-action-bar.vala', 'components/components-entry-undo.vala', 'components/components-info-bar-stack.vala', 'components/components-info-bar.vala', diff --git a/ui/components-conversation-action-bar.ui b/ui/components-conversation-action-bar.ui new file mode 100644 index 00000000..ae49683f --- /dev/null +++ b/ui/components-conversation-action-bar.ui @@ -0,0 +1,22 @@ + + + + + True + False + mail-archive-symbolic + + + diff --git a/ui/org.gnome.Geary.gresource.xml b/ui/org.gnome.Geary.gresource.xml index 481bbff4..e064d331 100644 --- a/ui/org.gnome.Geary.gresource.xml +++ b/ui/org.gnome.Geary.gresource.xml @@ -14,6 +14,7 @@ components-attachment-pane.ui components-attachment-pane-menus.ui components-attachment-view.ui + components-conversation-action-bar.ui components-conversation-actions.ui components-in-app-notification.ui components-inspector.ui