[geary/wip/713150-conversations] Improved ConversationListStore row handling
- From: Jim Nelson <jnelson src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/wip/713150-conversations] Improved ConversationListStore row handling
- Date: Fri, 20 Mar 2015 23:03:13 +0000 (UTC)
commit 5b1ab0d2df03687c1791e4616ad399f6e2a183ce
Author: Jim Nelson <jim yorba org>
Date: Fri Mar 20 15:31:08 2015 -0700
Improved ConversationListStore row handling
Old code would re-sort an updated row (cell) in the list by removing
the old one and inserting a new one with the new conversation data.
This caused unsightly flicker and selection problems. This new code
will update the existing row and force a re-sort of the list.
Preview handling is still frightful.
.../conversation-list/conversation-list-store.vala | 61 +++++++++-----------
.../formatted-conversation-data.vala | 53 +++++++++++-------
src/engine/app/app-conversation-monitor.vala | 20 ++++---
3 files changed, 71 insertions(+), 63 deletions(-)
---
diff --git a/src/client/conversation-list/conversation-list-store.vala
b/src/client/conversation-list/conversation-list-store.vala
index 552463a..9594ef8 100644
--- a/src/client/conversation-list/conversation-list-store.vala
+++ b/src/client/conversation-list/conversation-list-store.vala
@@ -113,8 +113,8 @@ public class ConversationListStore : Gtk.ListStore {
conversation_monitor.scan_completed.disconnect(on_scan_completed);
conversation_monitor.conversations_added.disconnect(on_conversations_added);
conversation_monitor.conversation_removed.disconnect(on_conversation_removed);
- conversation_monitor.conversation_appended.disconnect(on_conversation_appended);
- conversation_monitor.conversation_trimmed.disconnect(on_conversation_trimmed);
+ conversation_monitor.conversation_appended.disconnect(on_refresh_conversation);
+ conversation_monitor.conversation_trimmed.disconnect(on_refresh_conversation);
conversation_monitor.email_flags_changed.disconnect(on_email_flags_changed);
conversation_monitor.email_paths_changed.disconnect(on_email_paths_changed);
}
@@ -136,8 +136,8 @@ public class ConversationListStore : Gtk.ListStore {
conversation_monitor.scan_completed.connect(on_scan_completed);
conversation_monitor.conversations_added.connect(on_conversations_added);
conversation_monitor.conversation_removed.connect(on_conversation_removed);
- conversation_monitor.conversation_appended.connect(on_conversation_appended);
- conversation_monitor.conversation_trimmed.connect(on_conversation_trimmed);
+ conversation_monitor.conversation_appended.connect(on_refresh_conversation);
+ conversation_monitor.conversation_trimmed.connect(on_refresh_conversation);
conversation_monitor.email_flags_changed.connect(on_email_flags_changed);
conversation_monitor.email_paths_changed.connect(on_email_paths_changed);
}
@@ -197,7 +197,7 @@ public class ConversationListStore : Gtk.ListStore {
foreach (Geary.Email email in emails) {
Geary.App.Conversation? conversation = conversation_monitor.get_conversation_for_email(email.id);
if (conversation != null)
- set_preview_for_conversation(conversation, email);
+ refresh_conversation(conversation, email);
else
debug("Couldn't find conversation for %s", email.id.to_string());
}
@@ -277,14 +277,6 @@ public class ConversationListStore : Gtk.ListStore {
return message_data == null ? null : message_data.preview;
}
- private void set_preview_for_conversation(Geary.App.Conversation conversation, Geary.Email preview) {
- Gtk.TreeIter iter;
- if (get_iter_for_conversation(conversation, out iter))
- set_row(iter, conversation, preview);
- else
- debug("Unable to find preview for conversation");
- }
-
private void set_row(Gtk.TreeIter iter, Geary.App.Conversation conversation, Geary.Email preview) {
FormattedConversationData conversation_data = new FormattedConversationData(conversation,
preview, conversation_monitor.folder,
@@ -303,29 +295,42 @@ public class ConversationListStore : Gtk.ListStore {
row_map.set(conversation, wrapper);
}
- private void refresh_conversation(Geary.App.Conversation conversation) {
+ private void on_refresh_conversation(Geary.App.Conversation conversation) {
+ refresh_conversation(conversation, null);
+ }
+
+ private void refresh_conversation(Geary.App.Conversation conversation, Geary.Email? preview) {
Gtk.TreeIter iter;
if (!get_iter_for_conversation(conversation, out iter)) {
- // Unknown conversation, attempt to append it.
+ debug("Can't refresh conversation: iter not found, adding...");
+
add_conversation(conversation);
+
return;
}
- Geary.Email? last_email =
conversation.get_latest_recv_email(Geary.App.Conversation.Location.ANYWHERE);
- if (last_email == null) {
- debug("Cannot refresh conversation: last email is null");
+ FormattedConversationData? cell = get_message_data_at_iter(iter);
+ if (cell == null) {
+ debug("Can't refresh conversation: no FormattedConversationData, adding...");
+
+ add_conversation(conversation);
- remove(iter);
return;
}
- set_row(iter, conversation, last_email);
+ assert(cell.conversation == conversation);
+ cell.update_conversation(preview);
+
+ // mark row as changed (which queues a redraw)
Gtk.TreePath? path = get_path(iter);
if (path != null)
row_changed(path, iter);
else
- debug("Cannot refresh conversation: no path for iterator");
+ debug("Error refreshing conversation: no path for iterator, can't report row changed");
+
+ // force a re-sort of the list
+ set_default_sort_func(sort_by_date);
}
private void refresh_flags(Geary.App.Conversation conversation) {
@@ -441,18 +446,6 @@ public class ConversationListStore : Gtk.ListStore {
remove_conversation(conversation);
}
- private void on_conversation_appended(Geary.App.Conversation conversation) {
- if (has_conversation(conversation)) {
- refresh_conversation(conversation);
- } else {
- debug("Unable to append conversation; conversation not present in list store");
- }
- }
-
- private void on_conversation_trimmed(Geary.App.Conversation conversation) {
- refresh_conversation(conversation);
- }
-
private void on_display_preview_changed() {
refresh_previews_async.begin(conversation_monitor);
}
@@ -469,7 +462,7 @@ public class ConversationListStore : Gtk.ListStore {
private void on_email_paths_changed(Geary.App.Conversation conversation,
Gee.Collection<Geary.EmailIdentifier> ids) {
// refresh the conversation because the change in paths can change the sort order (sorting
// depends on dates of email in this folder)
- refresh_conversation(conversation);
+ refresh_conversation(conversation, null);
}
private int sort_by_date(Gtk.TreeModel model, Gtk.TreeIter aiter, Gtk.TreeIter biter) {
diff --git a/src/client/conversation-list/formatted-conversation-data.vala
b/src/client/conversation-list/formatted-conversation-data.vala
index 07e01ae..21b30d4 100644
--- a/src/client/conversation-list/formatted-conversation-data.vala
+++ b/src/client/conversation-list/formatted-conversation-data.vala
@@ -72,6 +72,8 @@ public class FormattedConversationData : Geary.BaseObject {
}
}
+ public Geary.App.Conversation? conversation { get; private set; }
+
public bool is_unread { get; set; }
public bool is_flagged { get; set; }
public string date { get; private set; }
@@ -80,17 +82,14 @@ public class FormattedConversationData : Geary.BaseObject {
public int num_emails { get; set; }
public Geary.Email? preview { get; private set; default = null; }
- private Geary.App.Conversation? conversation = null;
private Gee.List<Geary.RFC822.MailboxAddress>? account_owner_emails = null;
private bool use_to = true;
private CountBadge count_badge = new CountBadge(2);
private ConversationListCellDimensions cell_dimensions;
// Creates a formatted message data from an e-mail.
- public FormattedConversationData(Geary.App.Conversation conversation, Geary.Email preview,
+ public FormattedConversationData(Geary.App.Conversation conversation, Geary.Email? preview,
Geary.Folder folder, Gee.List<Geary.RFC822.MailboxAddress> account_owner_emails) {
- assert(preview.fields.fulfills(ConversationListStore.REQUIRED_FIELDS));
-
this.conversation = conversation;
this.account_owner_emails = account_owner_emails;
use_to = (folder != null) && folder.special_folder_type.is_outgoing();
@@ -99,26 +98,18 @@ public class FormattedConversationData : Geary.BaseObject {
// the GearyController
cell_dimensions = GearyApplication.instance.controller.cell_dimensions;
- // Load preview-related data.
- update_date_string();
- this.subject = EmailUtil.strip_subject_prefixes(preview);
- this.body = Geary.String.reduce_whitespace(preview.get_preview_as_string());
- this.preview = preview;
-
- // Load conversation-related data.
- this.is_unread = conversation.is_unread();
- this.is_flagged = conversation.is_flagged();
- this.num_emails = conversation.get_count();
+ update_conversation(preview);
}
// Creates an example message (used interally for styling calculations.)
public FormattedConversationData.create_example() {
- this.is_unread = false;
- this.is_flagged = false;
- this.date = STYLE_EXAMPLE;
- this.subject = STYLE_EXAMPLE;
- this.body = STYLE_EXAMPLE + "\n" + STYLE_EXAMPLE;
- this.num_emails = 1;
+ conversation = null;
+ is_unread = false;
+ is_flagged = false;
+ date = STYLE_EXAMPLE;
+ subject = STYLE_EXAMPLE;
+ body = STYLE_EXAMPLE + "\n" + STYLE_EXAMPLE;
+ num_emails = 1;
// take a reference to the global ConversationListCellDimensions object, which is held by
// the GearyController
@@ -143,6 +134,28 @@ public class FormattedConversationData : Geary.BaseObject {
return true;
}
+ public void update_conversation(Geary.Email? supplied_preview) {
+ update_date_string();
+
+ // caller can override which preview is shown, otherwise use the latest email in conversation
+ if (supplied_preview != null)
+ preview = supplied_preview;
+ else if (preview == null)
+ preview = conversation.get_latest_recv_email(Geary.App.Conversation.Location.ANYWHERE);
+
+ if (preview != null) {
+ if (preview.fields.fulfills(Geary.Email.Field.SUBJECT))
+ subject = EmailUtil.strip_subject_prefixes(preview);
+
+ if (preview.fields.fulfills(Geary.Email.Field.PREVIEW))
+ body = Geary.String.reduce_whitespace(preview.get_preview_as_string());
+ }
+
+ is_unread = conversation.is_unread();
+ is_flagged = conversation.is_flagged();
+ num_emails = conversation.get_count();
+ }
+
private uint8 gdk_to_rgb(double gdk) {
return (uint8) (gdk.clamp(0.0, 1.0) * 255.0);
}
diff --git a/src/engine/app/app-conversation-monitor.vala b/src/engine/app/app-conversation-monitor.vala
index c4a2547..448d315 100644
--- a/src/engine/app/app-conversation-monitor.vala
+++ b/src/engine/app/app-conversation-monitor.vala
@@ -821,8 +821,9 @@ public class Geary.App.ConversationMonitor : BaseObject {
// if conversation is empty or has no messages in primary folder path, remove it
// entirely
if (conversation.get_count() == 0 || !conversation.any_in_folder_path(folder.path)) {
- // could have been trimmed earlier in the loop
+ // could have been trimmed/updated earlier in the loop
trimmed_conversations.remove_all(conversation);
+ paths_changed.remove_all(conversation);
// strip Conversation from local storage and lookup tables
conversations.remove(conversation);
@@ -836,6 +837,7 @@ public class Geary.App.ConversationMonitor : BaseObject {
// since the email was fully removed from conversation, report as trimmed
trimmed_conversations.set(conversation, email);
} else if (!fully_removed && email != null) {
+ // the path was removed but the email remains
paths_changed.set(conversation, email.id);
}
}
@@ -849,6 +851,14 @@ public class Geary.App.ConversationMonitor : BaseObject {
foreach (Conversation conversation in trimmed_conversations.get_keys())
notify_conversation_trimmed(conversation, trimmed_conversations.get(conversation));
+ if (paths_changed.size > 0) {
+ debug("[%s] Paths changed in %d conversations of %d emails from %s", to_string(),
+ paths_changed.get_keys().size, paths_changed.get_values().size, path.to_string());
+ }
+
+ foreach (Conversation conversation in paths_changed.get_keys())
+ notify_email_paths_changed(conversation, paths_changed.get(conversation));
+
if (removed_conversations.size > 0) {
debug("[%s] Removed %d conversations from %s", to_string(), removed_conversations.size,
path.to_string());
@@ -858,14 +868,6 @@ public class Geary.App.ConversationMonitor : BaseObject {
notify_conversation_removed(conversation);
conversation.clear_owner();
}
-
- if (paths_changed.size > 0) {
- debug("[%s] Paths changed in %d conversations of %d emails from %s", to_string(),
- paths_changed.get_keys().size, paths_changed.get_values().size, path.to_string());
- }
-
- foreach (Conversation conversation in paths_changed.get_keys())
- notify_email_paths_changed(conversation, paths_changed.get(conversation));
}
private void on_account_email_flags_changed(Geary.Folder folder,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]