[geary/wip/730682-refine-convo-list: 9/15] 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: 9/15] Hook up selection/activation handling for new conversation list widget.
- Date: Tue, 17 Oct 2017 07:51:41 +0000 (UTC)
commit 6615dc3d7b69fc4254019b5d7d04454cc8869179
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 | 96 +++-----------------
src/client/components/main-window.vala | 70 ++++++++++++++
.../conversation-list/conversation-list-view.vala | 4 +-
.../conversation-list/conversation-list.vala | 30 ++++++-
4 files changed, 112 insertions(+), 88 deletions(-)
---
diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala
index b263530..735e2bf 100644
--- a/src/client/application/geary-controller.vala
+++ b/src/client/application/geary-controller.vala
@@ -109,7 +109,6 @@ public class GearyController : Geary.BaseObject {
private Cancellable cancellable_context_dependent_buttons = new Cancellable();
private Gee.HashMap<Geary.Account, Cancellable> inbox_cancellables
= new Gee.HashMap<Geary.Account, Cancellable>();
- 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;
@@ -232,8 +231,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);
@@ -306,8 +303,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);
@@ -1552,63 +1547,6 @@ public class GearyController : Geary.BaseObject {
}
}
- private void on_conversations_selected(Gee.Set<Geary.App.Conversation> selected) {
- this.selected_conversations = selected;
- this.application.get_action(
- ACTION_FIND_IN_CONVERSATION
- ).set_sensitive(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);
- this.application.get_action(
- ACTION_FIND_IN_CONVERSATION
- ).set_sensitive(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);
@@ -1794,7 +1732,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;
}
@@ -1812,7 +1750,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;
@@ -1984,9 +1922,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;
@@ -2241,12 +2176,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.
@@ -2256,12 +2185,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;
@@ -2554,6 +2489,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;
@@ -2854,7 +2793,7 @@ public class GearyController : Geary.BaseObject {
// Updates tooltip text depending on number of conversations selected.
private void update_tooltips() {
- bool single = selected_conversations.size == 1;
+ bool single = this.main_window.get_selected_conversations().size == 1;
this.application.actions.get_action(ACTION_MARK_AS_MENU).tooltip = single ?
MARK_MESSAGE_MENU_TOOLTIP_SINGLE : MARK_MESSAGE_MENU_TOOLTIP_MULTIPLE;
@@ -2921,13 +2860,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 ad5b52e..0d5deb0 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;
@@ -60,6 +61,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();
@@ -77,6 +80,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;
+ }
+
private void load_config(Configuration config) {
// 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
@@ -404,6 +419,61 @@ public class MainWindow : Gtk.ApplicationWindow {
}
}
+ private void on_conversation_selection_changed(Gee.Set<Geary.App.Conversation> selection) {
+ this.selected_conversations = selection;
+ this.application.get_action(
+ GearyController.ACTION_FIND_IN_CONVERSATION
+ ).set_sensitive(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);
+ this.application.get_action(
+ GearyController.ACTION_FIND_IN_CONVERSATION
+ ).set_sensitive(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 bad2744..bbd4b26 100644
--- a/src/client/conversation-list/conversation-list-view.vala
+++ b/src/client/conversation-list/conversation-list-view.vala
@@ -245,8 +245,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]