[geary/wip/730682-refine-convo-list: 169/175] Hook up selection/activation handling for new conversation list widget.
- From: Michael Gratton <mjog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/wip/730682-refine-convo-list: 169/175] Hook up selection/activation handling for new conversation list widget.
- Date: Mon, 4 Dec 2017 14:17:01 +0000 (UTC)
commit 1db34cf1e8c33cba191bfe6f37d51bfdb128c463
Author: Michael James Gratton <mike vee net>
Date: Sat Oct 14 17:18:37 2017 +1100
Hook up selection/activation handling for new conversation list widget.
This also moves selection management code from GearyController to
MainWindow in prep for that glorious future where multiple main windows
are supported.
* src/client/conversation-list/conversation-list.vala (ListBox): Add
conversation_selection_changed and conversation_activated signals,
ensure they are fired as needed.
* src/client/components/main-window.vala (MainWindow): Move selection
management code over from GearyController and adapt for new
ConversationList widget. Update call sites.
src/client/application/geary-controller.vala | 108 +++++---------------
src/client/components/main-window.vala | 73 +++++++++++++
.../conversation-list/conversation-list-view.vala | 4 +-
.../conversation-list/conversation-list.vala | 30 +++++-
4 files changed, 126 insertions(+), 89 deletions(-)
---
diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala
index 08fd326..abbcaab 100644
--- a/src/client/application/geary-controller.vala
+++ b/src/client/application/geary-controller.vala
@@ -49,7 +49,6 @@ public class GearyController : Geary.BaseObject {
// Properties
public const string PROP_CURRENT_CONVERSATION ="current-conversations";
- public const string PROP_SELECTED_CONVERSATIONS ="selected-conversations";
public const int MIN_CONVERSATION_COUNT = 50;
@@ -83,7 +82,6 @@ public class GearyController : Geary.BaseObject {
private Gee.HashMap<Geary.Account, Cancellable> inbox_cancellables
= new Gee.HashMap<Geary.Account, Cancellable>();
private ContactListStoreCache contact_list_store_cache = new ContactListStoreCache();
- private Gee.Set<Geary.App.Conversation> selected_conversations = new
Gee.HashSet<Geary.App.Conversation>();
private Geary.App.Conversation? last_deleted_conversation = null;
private Gee.LinkedList<ComposerWidget> composer_widgets = new Gee.LinkedList<ComposerWidget>();
private NewMessagesMonitor? new_messages_monitor = null;
@@ -230,8 +228,6 @@ public class GearyController : Geary.BaseObject {
Geary.Engine.instance.untrusted_host.connect(on_untrusted_host);
// Connect to various UI signals.
- main_window.conversation_list_view.conversations_selected.connect(on_conversations_selected);
- main_window.conversation_list_view.conversation_activated.connect(on_conversation_activated);
main_window.conversation_list_view.load_more.connect(on_load_more);
main_window.conversation_list_view.mark_conversations.connect(on_mark_conversations);
main_window.conversation_list_view.visible_conversations_changed.connect(on_visible_conversations_changed);
@@ -304,8 +300,6 @@ public class GearyController : Geary.BaseObject {
Geary.Engine.instance.untrusted_host.disconnect(on_untrusted_host);
// Disconnect from various UI signals.
- main_window.conversation_list_view.conversations_selected.disconnect(on_conversations_selected);
- main_window.conversation_list_view.conversation_activated.disconnect(on_conversation_activated);
main_window.conversation_list_view.load_more.disconnect(on_load_more);
main_window.conversation_list_view.mark_conversations.disconnect(on_mark_conversations);
main_window.conversation_list_view.visible_conversations_changed.disconnect(on_visible_conversations_changed);
@@ -1230,7 +1224,8 @@ public class GearyController : Geary.BaseObject {
// Update widgets and such to match capabilities of the current folder ... sensitivity is handled
// by other utility methods
private void update_ui() {
- main_window.main_toolbar.selected_conversations = this.selected_conversations.size;
+ main_window.main_toolbar.selected_conversations =
+ this.main_window.get_selected_conversations().size;
main_window.main_toolbar.show_trash_button = current_folder_supports_trash() ||
!(current_folder is Geary.FolderSupport.Remove);
}
@@ -1448,59 +1443,6 @@ public class GearyController : Geary.BaseObject {
}
}
- private void on_conversations_selected(Gee.Set<Geary.App.Conversation> selected) {
- this.selected_conversations = selected;
- get_window_action(ACTION_FIND_IN_CONVERSATION).set_enabled(false);
- ConversationViewer viewer = this.main_window.conversation_viewer;
- if (this.current_folder != null && !viewer.is_composer_visible) {
- switch(selected.size) {
- case 0:
- enable_message_buttons(false);
- viewer.show_none_selected();
- break;
-
- case 1:
- // Cancel existing avatar loads before loading new
- // convo since that will start loading more avatars
- viewer.load_conversation.begin(
- Geary.Collection.get_first(selected),
- this.current_folder,
- (obj, ret) => {
- try {
- viewer.load_conversation.end(ret);
- enable_message_buttons(true);
- get_window_action(ACTION_FIND_IN_CONVERSATION).set_enabled(true);
- } catch (Error err) {
- debug("Unable to load conversation: %s",
- err.message);
- }
- }
- );
- break;
-
- default:
- enable_multiple_message_buttons();
- viewer.show_multiple_selected();
- break;
- }
- }
- }
-
- private void on_conversation_activated(Geary.App.Conversation activated) {
- // Currently activating a conversation is only available for drafts folders.
- if (current_folder == null || current_folder.special_folder_type !=
- Geary.SpecialFolderType.DRAFTS)
- return;
-
- // TODO: Determine how to map between conversations and drafts correctly.
- Geary.Email draft = activated.get_latest_recv_email(
- Geary.App.Conversation.Location.IN_FOLDER
- );
- create_compose_widget(
- ComposerWidget.ComposeType.NEW_MESSAGE, draft, null, null, true
- );
- }
-
private void on_special_folder_type_changed(Geary.Folder folder, Geary.SpecialFolderType old_type,
Geary.SpecialFolderType new_type) {
main_window.folder_list.remove_folder(folder);
@@ -1699,7 +1641,7 @@ public class GearyController : Geary.BaseObject {
private Gee.ArrayList<Geary.EmailIdentifier> get_selected_email_ids(bool latest_sent_only) {
Gee.ArrayList<Geary.EmailIdentifier> ids = new Gee.ArrayList<Geary.EmailIdentifier>();
- foreach (Geary.App.Conversation conversation in selected_conversations)
+ foreach (Geary.App.Conversation conversation in this.main_window.get_selected_conversations())
get_conversation_email_ids(conversation, latest_sent_only, ids);
return ids;
}
@@ -1717,7 +1659,7 @@ public class GearyController : Geary.BaseObject {
bool read_selected = false;
bool starred_selected = false;
bool unstarred_selected = false;
- foreach (Geary.App.Conversation conversation in selected_conversations) {
+ foreach (Geary.App.Conversation conversation in this.main_window.get_selected_conversations()) {
if (conversation.is_unread())
unread_selected = true;
@@ -1886,9 +1828,6 @@ public class GearyController : Geary.BaseObject {
private void on_move_conversation(Geary.Folder destination) {
// Nothing to do if nothing selected.
- if (selected_conversations == null || selected_conversations.size == 0)
- return;
-
Gee.List<Geary.EmailIdentifier> ids = get_selected_email_ids(false);
if (ids.size == 0)
return;
@@ -2137,12 +2076,6 @@ public class GearyController : Geary.BaseObject {
}
}
- private void create_compose_widget(ComposerWidget.ComposeType compose_type,
- Geary.Email? referred = null, string? quote = null, string? mailto = null,
- bool is_draft = false) {
- create_compose_widget_async.begin(compose_type, referred, quote, mailto, is_draft);
- }
-
/**
* Creates a composer widget. Depending on the arguments, this can be inline in the
* conversation or as a new window.
@@ -2152,12 +2085,18 @@ public class GearyController : Geary.BaseObject {
* @param mailto - A "mailto:"-link
* @param is_draft - Whether we're starting from a draft (true) or a new mail (false)
*/
+ internal void create_compose_widget(ComposerWidget.ComposeType compose_type,
+ Geary.Email? referred = null, string? quote = null, string? mailto = null,
+ bool is_draft = false) {
+ create_compose_widget_async.begin(compose_type, referred, quote, mailto, is_draft);
+ }
+
private async void create_compose_widget_async(ComposerWidget.ComposeType compose_type,
Geary.Email? referred = null, string? quote = null, string? mailto = null,
bool is_draft = false) {
if (current_account == null)
return;
-
+
bool inline;
if (!should_create_new_composer(compose_type, referred, quote, is_draft, out inline))
return;
@@ -2453,6 +2392,10 @@ public class GearyController : Geary.BaseObject {
return;
}
+ Gee.Set<Geary.App.Conversation> selected_conversations =
this.main_window.get_selected_conversations();
+ if (selected_conversations.size == 0)
+ return;
+
last_deleted_conversation = selected_conversations.size > 0
? Geary.traverse<Geary.App.Conversation>(selected_conversations).first() : null;
@@ -2669,7 +2612,8 @@ public class GearyController : Geary.BaseObject {
// Disables all single-message buttons and enables all multi-message buttons.
public void enable_multiple_message_buttons() {
- main_window.main_toolbar.selected_conversations = this.selected_conversations.size;
+ main_window.main_toolbar.selected_conversations =
+ this.main_window.get_selected_conversations().size;
// Single message only buttons.
get_window_action(ACTION_REPLY_TO_MESSAGE).set_enabled(false);
@@ -2688,7 +2632,8 @@ public class GearyController : Geary.BaseObject {
// Enables or disables the message buttons on the toolbar.
public void enable_message_buttons(bool sensitive) {
- main_window.main_toolbar.selected_conversations = this.selected_conversations.size;
+ main_window.main_toolbar.selected_conversations =
+ this.main_window.get_selected_conversations().size;
// No reply/forward in drafts folder.
bool respond_sensitive = sensitive;
@@ -2730,8 +2675,12 @@ public class GearyController : Geary.BaseObject {
if (selected_operations != null)
supported_operations.add_all(selected_operations.get_values());
- get_window_action(ACTION_SHOW_MARK_MENU).set_enabled(sensitive && (typeof(Geary.FolderSupport.Mark)
in supported_operations));
- get_window_action(ACTION_COPY_MENU).set_enabled(sensitive &&
(supported_operations.contains(typeof(Geary.FolderSupport.Copy))));
+ get_window_action(ACTION_SHOW_MARK_MENU).set_enabled(
+ sensitive && (typeof(Geary.FolderSupport.Mark) in supported_operations)
+ );
+ get_window_action(ACTION_COPY_MENU).set_enabled(
+ sensitive && (supported_operations.contains(typeof(Geary.FolderSupport.Copy)))
+ );
}
// Returns a list of composer windows for an account, or null if none.
@@ -2784,13 +2733,6 @@ public class GearyController : Geary.BaseObject {
search_text_changed(main_window.search_bar.search_text);
}
- /**
- * Returns a read-only set of currently selected conversations.
- */
- public Gee.Set<Geary.App.Conversation> get_selected_conversations() {
- return selected_conversations.read_only_view;
- }
-
// Find the first inbox we know about and switch to it.
private void switch_to_first_inbox() {
try {
diff --git a/src/client/components/main-window.vala b/src/client/components/main-window.vala
index ef12a33..14c3326 100644
--- a/src/client/components/main-window.vala
+++ b/src/client/components/main-window.vala
@@ -33,6 +33,7 @@ public class MainWindow : Gtk.ApplicationWindow {
public ConversationList conversation_list { get; private set; }
public ConversationViewer conversation_viewer { get; private set; default = new ConversationViewer(); }
public StatusBar status_bar { get; private set; default = new StatusBar(); }
+ private Gee.Set<Geary.App.Conversation> selected_conversations = new
Gee.HashSet<Geary.App.Conversation>();
private MonitoredSpinner spinner = new MonitoredSpinner();
[GtkChild]
private Gtk.Box main_layout;
@@ -66,6 +67,8 @@ public class MainWindow : Gtk.ApplicationWindow {
Object(application: application);
this.conversation_list = new ConversationList(application.config);
+ this.conversation_list.conversation_selection_changed.connect(on_conversation_selection_changed);
+ this.conversation_list.conversation_activated.connect(on_conversation_activated);
load_config(application.config);
restore_saved_window_state();
@@ -81,6 +84,18 @@ public class MainWindow : Gtk.ApplicationWindow {
on_change_orientation();
}
+ ~MainWindow() {
+ this.conversation_list.conversation_selection_changed.disconnect(on_conversation_selection_changed);
+ this.conversation_list.conversation_activated.disconnect(on_conversation_activated);
+ }
+
+ /**
+ * Returns a read-only set of currently selected conversations.
+ */
+ public Gee.Set<Geary.App.Conversation> get_selected_conversations() {
+ return this.selected_conversations.read_only_view;
+ }
+
public void show_infobar(MainWindowInfoBar info_bar) {
this.info_bar_container.add(info_bar);
this.info_bar_frame.show();
@@ -415,6 +430,64 @@ public class MainWindow : Gtk.ApplicationWindow {
}
}
+ private inline SimpleAction get_action(string name) {
+ return (SimpleAction) lookup_action(name);
+ }
+
+ private void on_conversation_selection_changed(Gee.Set<Geary.App.Conversation> selection) {
+ this.selected_conversations = selection;
+ SimpleAction find_action = get_action(
+ GearyController.ACTION_FIND_IN_CONVERSATION
+ );
+ find_action.set_enabled(false);
+ if (this.current_folder != null && !this.conversation_viewer.is_composer_visible) {
+ switch(selection.size) {
+ case 0:
+ this.application.controller.enable_message_buttons(false);
+ this.conversation_viewer.show_none_selected();
+ break;
+
+ case 1:
+ // Cancel existing avatar loads before loading new
+ // convo since that will start loading more avatars
+ this.conversation_viewer.load_conversation.begin(
+ Geary.Collection.get_first(selection),
+ this.current_folder,
+ (obj, ret) => {
+ try {
+ this.conversation_viewer.load_conversation.end(ret);
+ this.application.controller.enable_message_buttons(true);
+ find_action.set_enabled(true);
+ } catch (Error err) {
+ debug("Unable to load conversation: %s",
+ err.message);
+ }
+ }
+ );
+ break;
+
+ default:
+ this.application.controller.enable_multiple_message_buttons();
+ this.conversation_viewer.show_multiple_selected();
+ break;
+ }
+ }
+ }
+
+ private void on_conversation_activated(Geary.App.Conversation activated) {
+ // Currently activating a conversation is only available for drafts folders.
+ if (this.current_folder != null &&
+ this.current_folder.special_folder_type == Geary.SpecialFolderType.DRAFTS) {
+ // TODO: Determine how to map between conversations and drafts correctly.
+ Geary.Email draft = activated.get_latest_recv_email(
+ Geary.App.Conversation.Location.IN_FOLDER
+ );
+ this.application.controller.create_compose_widget(
+ ComposerWidget.ComposeType.NEW_MESSAGE, draft, null, null, true
+ );
+ }
+ }
+
[GtkCallback]
private bool on_key_release_event(Gdk.EventKey event) {
check_shift_event(event);
diff --git a/src/client/conversation-list/conversation-list-view.vala
b/src/client/conversation-list/conversation-list-view.vala
index abacb34..db34478 100644
--- a/src/client/conversation-list/conversation-list-view.vala
+++ b/src/client/conversation-list/conversation-list-view.vala
@@ -248,8 +248,8 @@ public class ConversationListView : Gtk.TreeView {
// all selected conversations; otherwise, it just applies to this one.
Geary.App.Conversation conversation = get_model().get_conversation_at_path(path);
Gee.Collection<Geary.App.Conversation> to_mark;
- if (GearyApplication.instance.controller.get_selected_conversations().contains(conversation))
- to_mark = GearyApplication.instance.controller.get_selected_conversations();
+ if
(GearyApplication.instance.controller.main_window.get_selected_conversations().contains(conversation))
+ to_mark = GearyApplication.instance.controller.main_window.get_selected_conversations();
else
to_mark = Geary.iterate<Geary.App.Conversation>(conversation).to_array_list();
diff --git a/src/client/conversation-list/conversation-list.vala
b/src/client/conversation-list/conversation-list.vala
index 20edc8d..6ef40f5 100644
--- a/src/client/conversation-list/conversation-list.vala
+++ b/src/client/conversation-list/conversation-list.vala
@@ -16,21 +16,43 @@ public class ConversationList : Gtk.ListBox {
private Configuration config;
+ private ConversationListModel? model = null;
+
+
+ /** Fired when a user changes the list's selection. */
+ public signal void conversation_selection_changed(Gee.Set<Geary.App.Conversation> selection);
+
+ /** Fired when a user activates a row in the list. */
+ public signal void conversation_activated(Geary.App.Conversation activated);
+
public ConversationList(Configuration config) {
this.config = config;
get_style_context().add_class(CLASS);
set_activate_on_single_click(true);
- set_selection_mode(Gtk.SelectionMode.MULTIPLE);
+ set_selection_mode(Gtk.SelectionMode.SINGLE);
+
+ this.row_activated.connect((row) => {
+ uint activated = row.get_index();
+ this.conversation_activated(this.model.get_conversation(activated));
+ });
+ this.selected_rows_changed.connect(() => {
+ Gee.HashSet<Geary.App.Conversation> new_selection =
+ new Gee.HashSet<Geary.App.Conversation>();
+ foreach (Gtk.ListBoxRow row in get_selected_rows()) {
+ uint selected = row.get_index();
+ new_selection.add(this.model.get_conversation(selected));
+ }
+ this.conversation_selection_changed(new_selection);
+ });
}
public void set_model(Geary.App.ConversationMonitor monitor) {
+ this.model = new ConversationListModel(monitor);
Geary.Folder displayed = monitor.folder;
Gee.List<Geary.RFC822.MailboxAddress> account_addresses =
displayed.account.information.get_all_mailboxes();
bool use_to = (displayed != null) && displayed.special_folder_type.is_outgoing();
- bind_model(
- new ConversationListModel(monitor),
- (convo) => {
+ bind_model(this.model, (convo) => {
return new ConversationListItem(convo as Geary.App.Conversation,
account_addresses,
use_to,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]