Fix messages being selected after move/etc and autoselect is disabled.
Bug 773054 * src/client/conversation-list/conversation-list-view.vala: Disable all selection when the user is causing the selection will change. This prevents the GtkTreeView impl from selecting the next row after rows are removed as a result of user action, but not for some other reason, i.e. the server removing a message. * src/client/application/geary-controller.vala: Call new ConversationListView::set_changing_selection as needed when moving messages. * src/client/conversation-list/conversation-list-store, src/engine/app/app-conversation-monitor.vala: Rename conversation_removed signal to conversations_removed and change its param to be a collection of conversations, so we can re-enable ConversationListView selection only after all conversations have been removed. Update call sites.
This commit is contained in:
parent
0e5df2c9be
commit
310daa8ad0
4 changed files with 68 additions and 31 deletions
|
|
@ -1442,7 +1442,7 @@ public class GearyController : Geary.BaseObject {
|
|||
current_conversations.seed_completed.connect(on_conversation_count_changed);
|
||||
current_conversations.scan_completed.connect(on_conversation_count_changed);
|
||||
current_conversations.conversations_added.connect(on_conversation_count_changed);
|
||||
current_conversations.conversation_removed.connect(on_conversation_count_changed);
|
||||
current_conversations.conversations_removed.connect(on_conversation_count_changed);
|
||||
|
||||
if (!current_conversations.is_monitoring)
|
||||
yield current_conversations.start_monitoring_async(conversation_cancellable);
|
||||
|
|
@ -1960,12 +1960,19 @@ public class GearyController : Geary.BaseObject {
|
|||
Gee.List<Geary.EmailIdentifier> ids = get_selected_email_ids(false);
|
||||
if (ids.size == 0)
|
||||
return;
|
||||
|
||||
|
||||
this.main_window.conversation_list_view.set_changing_selection(true);
|
||||
|
||||
Geary.FolderSupport.Move? supports_move = current_folder as Geary.FolderSupport.Move;
|
||||
if (supports_move != null)
|
||||
move_conversation_async.begin(supports_move, ids, destination.path, cancellable_folder);
|
||||
move_conversation_async.begin(
|
||||
supports_move, ids, destination.path, cancellable_folder,
|
||||
(obj, ret) => {
|
||||
move_conversation_async.end(ret);
|
||||
this.main_window.conversation_list_view.set_changing_selection(false);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private async void move_conversation_async(Geary.FolderSupport.Move source_folder,
|
||||
Gee.List<Geary.EmailIdentifier> ids, Geary.FolderPath destination, Cancellable? cancellable) {
|
||||
try {
|
||||
|
|
@ -2572,9 +2579,12 @@ public class GearyController : Geary.BaseObject {
|
|||
last_deleted_conversation = selected_conversations.size > 0
|
||||
? Geary.traverse<Geary.App.Conversation>(selected_conversations).first() : null;
|
||||
|
||||
// Return focus to the conversation list from the clicked toolbar button.
|
||||
main_window.conversation_list_view.grab_focus();
|
||||
|
||||
// Return focus to the conversation list from the clicked
|
||||
// toolbar button.
|
||||
this.main_window.conversation_list_view.grab_focus();
|
||||
|
||||
this.main_window.conversation_list_view.set_changing_selection(true);
|
||||
|
||||
Gee.List<Geary.EmailIdentifier> ids = get_selected_email_ids(false);
|
||||
if (archive) {
|
||||
debug("Archiving selected messages");
|
||||
|
|
@ -2629,6 +2639,7 @@ public class GearyController : Geary.BaseObject {
|
|||
} catch (Error e) {
|
||||
debug("Unable to archive/trash/delete messages: %s", e.message);
|
||||
}
|
||||
this.main_window.conversation_list_view.set_changing_selection(false);
|
||||
}
|
||||
|
||||
private void save_revokable(Geary.Revokable? new_revokable, string? description) {
|
||||
|
|
|
|||
|
|
@ -92,8 +92,8 @@ public class ConversationListStore : Gtk.ListStore {
|
|||
private uint update_id = 0;
|
||||
|
||||
public signal void conversations_added_began();
|
||||
|
||||
public signal void conversations_added_finished();
|
||||
public signal void conversations_removed(bool start);
|
||||
|
||||
public ConversationListStore(Geary.App.ConversationMonitor conversations) {
|
||||
set_column_types(Column.get_types());
|
||||
|
|
@ -112,7 +112,7 @@ public class ConversationListStore : Gtk.ListStore {
|
|||
|
||||
conversations.scan_completed.connect(on_scan_completed);
|
||||
conversations.conversations_added.connect(on_conversations_added);
|
||||
conversations.conversation_removed.connect(on_conversation_removed);
|
||||
conversations.conversations_removed.connect(on_conversations_removed);
|
||||
conversations.conversation_appended.connect(on_conversation_appended);
|
||||
conversations.conversation_trimmed.connect(on_conversation_trimmed);
|
||||
conversations.email_flags_changed.connect(on_email_flags_changed);
|
||||
|
|
@ -428,10 +428,13 @@ public class ConversationListStore : Gtk.ListStore {
|
|||
conversations_added_finished();
|
||||
}
|
||||
|
||||
private void on_conversation_removed(Geary.App.Conversation conversation) {
|
||||
remove_conversation(conversation);
|
||||
private void on_conversations_removed(Gee.Collection<Geary.App.Conversation> conversations) {
|
||||
conversations_removed(true);
|
||||
foreach (Geary.App.Conversation removed in conversations)
|
||||
remove_conversation(removed);
|
||||
conversations_removed(false);
|
||||
}
|
||||
|
||||
|
||||
private void on_conversation_appended(Geary.App.Conversation conversation) {
|
||||
if (has_conversation(conversation)) {
|
||||
refresh_conversation(conversation);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ public class ConversationListView : Gtk.TreeView {
|
|||
private Gtk.Menu? context_menu = null;
|
||||
private Gee.Set<Geary.App.Conversation> selected = new Gee.HashSet<Geary.App.Conversation>();
|
||||
private uint selection_changed_id = 0;
|
||||
private bool suppress_selection = false;
|
||||
|
||||
public signal void conversations_selected(Gee.Set<Geary.App.Conversation> selected);
|
||||
|
||||
|
|
@ -83,6 +84,7 @@ public class ConversationListView : Gtk.TreeView {
|
|||
old_store.conversations_added_began.disconnect(
|
||||
on_conversations_added_began
|
||||
);
|
||||
old_store.conversations_removed.disconnect(on_conversations_removed);
|
||||
old_store.row_inserted.disconnect(on_rows_changed);
|
||||
old_store.rows_reordered.disconnect(on_rows_changed);
|
||||
old_store.row_changed.disconnect(on_rows_changed);
|
||||
|
|
@ -102,6 +104,7 @@ public class ConversationListView : Gtk.TreeView {
|
|||
new_store.conversations_added_finished.connect(
|
||||
on_conversations_added_finished
|
||||
);
|
||||
new_store.conversations_removed.connect(on_conversations_removed);
|
||||
}
|
||||
|
||||
// Disconnect the selection handler since we don't want to
|
||||
|
|
@ -113,12 +116,27 @@ public class ConversationListView : Gtk.TreeView {
|
|||
selection.changed.connect(on_selection_changed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies an action is currently changing the view's selection.
|
||||
*/
|
||||
public void set_changing_selection(bool is_changing) {
|
||||
// Make sure that when not autoselecting, and if the user is
|
||||
// causing selected rows to be removed, the next row is not
|
||||
// automatically selected by GtkTreeView
|
||||
if (is_changing) {
|
||||
this.suppress_selection =
|
||||
!GearyApplication.instance.config.autoselect;
|
||||
} else {
|
||||
// If no longer changing, always re-enable selection
|
||||
get_selection().set_mode(Gtk.SelectionMode.MULTIPLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void on_conversation_monitor_changed() {
|
||||
if (conversation_monitor != null) {
|
||||
conversation_monitor.scan_started.disconnect(on_scan_started);
|
||||
conversation_monitor.scan_completed.disconnect(on_scan_completed);
|
||||
conversation_monitor.seed_completed.disconnect(on_seed_completed);
|
||||
conversation_monitor.conversation_removed.disconnect(on_conversation_removed);
|
||||
}
|
||||
|
||||
conversation_monitor = GearyApplication.instance.controller.current_conversations;
|
||||
|
|
@ -127,7 +145,6 @@ public class ConversationListView : Gtk.TreeView {
|
|||
conversation_monitor.scan_started.connect(on_scan_started);
|
||||
conversation_monitor.scan_completed.connect(on_scan_completed);
|
||||
conversation_monitor.seed_completed.connect(on_seed_completed);
|
||||
conversation_monitor.conversation_removed.connect(on_conversation_removed);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -157,11 +174,6 @@ public class ConversationListView : Gtk.TreeView {
|
|||
}
|
||||
}
|
||||
|
||||
private void on_conversation_removed(Geary.App.Conversation conversation) {
|
||||
if (!GearyApplication.instance.config.autoselect)
|
||||
get_selection().unselect_all();
|
||||
}
|
||||
|
||||
private void on_conversations_added_began() {
|
||||
Gtk.Adjustment? adjustment = get_adjustment();
|
||||
// If we were at the top, we want to stay there after conversations are added.
|
||||
|
|
@ -184,7 +196,19 @@ public class ConversationListView : Gtk.TreeView {
|
|||
|
||||
adjustment.set_value(0);
|
||||
}
|
||||
|
||||
|
||||
private void on_conversations_removed(bool start) {
|
||||
if (!GearyApplication.instance.config.autoselect) {
|
||||
Gtk.SelectionMode mode = start
|
||||
// Stop GtkTreeView from automatically selecting the
|
||||
// next row after the removed rows
|
||||
? Gtk.SelectionMode.NONE
|
||||
// Allow the user to make selections again
|
||||
: Gtk.SelectionMode.MULTIPLE;
|
||||
get_selection().set_mode(mode);
|
||||
}
|
||||
}
|
||||
|
||||
private Gtk.Adjustment? get_adjustment() {
|
||||
Gtk.ScrolledWindow? parent = get_parent() as Gtk.ScrolledWindow;
|
||||
if (parent == null) {
|
||||
|
|
|
|||
|
|
@ -112,15 +112,15 @@ public class Geary.App.ConversationMonitor : BaseObject {
|
|||
/**
|
||||
* "conversations-removed" is fired when all the email in a Conversation has been removed.
|
||||
* It's possible this will be called without a signal alerting that it's emails have been
|
||||
* removed, i.e. a "conversation-removed" signal may fire with no accompanying
|
||||
* removed, i.e. a "conversations-removed" signal may fire with no accompanying
|
||||
* "conversation-trimmed".
|
||||
*
|
||||
* Note that this can only occur when monitoring is enabled. There is (currently) no
|
||||
* user call to manually remove email from Conversations.
|
||||
*/
|
||||
public virtual signal void conversation_removed(Conversation conversation) {
|
||||
Logging.debug(Logging.Flag.CONVERSATIONS, "[%s] ConversationMonitor::conversation_removed",
|
||||
folder.to_string());
|
||||
public virtual signal void conversations_removed(Gee.Collection<Conversation> conversations) {
|
||||
Logging.debug(Logging.Flag.CONVERSATIONS, "[%s] ConversationMonitor::conversations_removed %d",
|
||||
folder.to_string(), conversations.size);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -216,8 +216,8 @@ public class Geary.App.ConversationMonitor : BaseObject {
|
|||
conversations_added(conversations);
|
||||
}
|
||||
|
||||
protected virtual void notify_conversation_removed(Conversation conversation) {
|
||||
conversation_removed(conversation);
|
||||
protected virtual void notify_conversations_removed(Gee.Collection<Conversation> conversations) {
|
||||
conversations_removed(conversations);
|
||||
}
|
||||
|
||||
protected virtual void notify_conversation_appended(Conversation conversation,
|
||||
|
|
@ -583,9 +583,8 @@ public class Geary.App.ConversationMonitor : BaseObject {
|
|||
// fall-through
|
||||
}
|
||||
|
||||
if (removed_due_to_merge != null) {
|
||||
foreach (Conversation conversation in removed_due_to_merge)
|
||||
notify_conversation_removed(conversation);
|
||||
if (removed_due_to_merge != null && removed_due_to_merge.size > 0) {
|
||||
notify_conversations_removed(removed_due_to_merge);
|
||||
}
|
||||
|
||||
if (added != null && added.size > 0)
|
||||
|
|
@ -637,8 +636,8 @@ public class Geary.App.ConversationMonitor : BaseObject {
|
|||
foreach (Conversation conversation in trimmed.get_keys())
|
||||
notify_conversation_trimmed(conversation, trimmed.get(conversation));
|
||||
|
||||
foreach (Conversation conversation in removed)
|
||||
notify_conversation_removed(conversation);
|
||||
if (removed.size > 0)
|
||||
notify_conversations_removed(removed);
|
||||
|
||||
// For any still-existing conversations that we've trimmed messages
|
||||
// from, do a search for any messages that should still be there due to
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue