[geary/wip/765516-gtk-widget-conversation-viewer: 167/169] Manage search term highlighting for messages appended to a conversation.
- From: Michael Gratton <mjog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/wip/765516-gtk-widget-conversation-viewer: 167/169] Manage search term highlighting for messages appended to a conversation.
- Date: Mon, 12 Sep 2016 13:26:24 +0000 (UTC)
commit d713fa74576d36830fc11d0c6ba3f53687df27ff
Author: Michael James Gratton <mike vee net>
Date: Mon Sep 12 20:51:37 2016 +1000
Manage search term highlighting for messages appended to a conversation.
* src/client/conversation-viewer/conversation-listbox.vala
(ConversationListBox): Cache search terms and current location so new
messages can have search applied to them as well. Add
search_matches_found to communicate back to the ConversationViewer that
the search maches have changed asynchronously.
* src/client/conversation-viewer/conversation-viewer.vala
(ConversationViewer): Hook up to search_matches_found when loading a
new conversation. Manage find text/prev button state from there.
.../conversation-viewer/conversation-listbox.vala | 77 ++++++++++++++------
.../conversation-viewer/conversation-viewer.vala | 46 +++++++++---
2 files changed, 88 insertions(+), 35 deletions(-)
---
diff --git a/src/client/conversation-viewer/conversation-listbox.vala
b/src/client/conversation-viewer/conversation-listbox.vala
index 0f053e0..87c1aad 100644
--- a/src/client/conversation-viewer/conversation-listbox.vala
+++ b/src/client/conversation-viewer/conversation-listbox.vala
@@ -149,12 +149,16 @@ public class ConversationListBox : Gtk.ListBox {
/** Conversation being displayed. */
public Geary.App.Conversation conversation { get; private set; }
- // Contacts for the account this conversation exists in
- private Geary.ContactStore contact_store;
+ // Folder from which the conversation was loaded
+ internal Geary.Folder location { get; private set; }
+ // Used to load messages in conversation.
private Geary.App.EmailStore email_store;
// Contacts for the account this conversation exists in
+ private Geary.ContactStore contact_store;
+
+ // Contacts for the account this conversation exists in
private Geary.AccountInformation account_info;
// Was this conversation loaded from the drafts folder?
@@ -173,6 +177,9 @@ public class ConversationListBox : Gtk.ListBox {
// Last visible row in the list, if any
private EmailRow? last_email_row = null;
+ // Cached search terms to apply to new messages
+ private Gee.Set<string>? ordered_search_terms = null;
+
/** Fired when an email view is added to the conversation list. */
public signal void email_added(ConversationEmail email);
@@ -184,18 +191,24 @@ public class ConversationListBox : Gtk.ListBox {
public signal void mark_emails(Gee.Collection<Geary.EmailIdentifier> emails,
Geary.EmailFlags? flags_to_add, Geary.EmailFlags? flags_to_remove);
+ /** Fired when an email that matches the current search terms is found. */
+ public signal void search_matches_found();
+
+
/**
* Constructs a new conversation list box instance.
*/
public ConversationListBox(Geary.App.Conversation conversation,
- Geary.ContactStore contact_store,
+ Geary.Folder location,
Geary.App.EmailStore? email_store,
+ Geary.ContactStore contact_store,
Geary.AccountInformation account_info,
bool is_draft_folder,
Gtk.Adjustment adjustment) {
this.conversation = conversation;
- this.contact_store = contact_store;
+ this.location = location;
this.email_store = email_store;
+ this.contact_store = contact_store;
this.account_info = account_info;
this.is_draft_folder = is_draft_folder;
@@ -434,7 +447,8 @@ public class ConversationListBox : Gtk.ListBox {
/**
* Loads search term matches for this list's emails.
*/
- public async void load_search_terms(Geary.SearchFolder search) {
+ public async void load_search_terms() {
+ Geary.SearchFolder search = (Geary.SearchFolder) this.location;
Geary.SearchQuery? query = search.search_query;
if (query != null) {
@@ -487,38 +501,26 @@ public class ConversationListBox : Gtk.ListBox {
*
* Returns true if any were found, else returns false.
*/
- public bool highlight_search_terms(Gee.Set<string> search_matches) {
+ public void highlight_search_terms(Gee.Set<string> search_matches) {
// Webkit's highlighting is ... weird. In order to actually
// see all the highlighting you're applying, it seems
// necessary to start with the shortest string and work up.
// If you don't, it seems that shorter strings will overwrite
// longer ones, and you're left with incomplete highlighting.
- Gee.ArrayList<string> ordered_matches = new Gee.ArrayList<string>();
+ Gee.TreeSet<string> ordered_matches =
+ new Gee.TreeSet<string>((a, b) => a.length - b.length);
ordered_matches.add_all(search_matches);
- ordered_matches.sort((a, b) => a.length - b.length);
-
- bool any_found = false;
+ this.ordered_search_terms = ordered_matches;
this.foreach((child) => {
- EmailRow row = (EmailRow) child;
- bool email_found = false;
- row.view.message_view_iterator().foreach((msg_view) => {
- if (msg_view.highlight_search_terms(search_matches) > 0) {
- email_found = true;
- }
- return true;
- });
- row.is_search_match = email_found;
- if (email_found) {
- any_found = true;
- }
+ apply_search_terms((EmailRow) child);
});
- return any_found;
}
/**
* Removes search term highlighting from all messages.
*/
public void unmark_search_terms() {
+ this.ordered_search_terms = null;
this.foreach((child) => {
EmailRow row = (EmailRow) child;
if (row.is_search_match) {
@@ -619,6 +621,12 @@ public class ConversationListBox : Gtk.ListBox {
email.is_flagged().is_certain()) {
row.expand();
}
+
+ // Apply any existing search terms to the new row
+ if (this.ordered_search_terms != null) {
+ apply_search_terms(row);
+ }
+
return row;
}
@@ -688,7 +696,30 @@ public class ConversationListBox : Gtk.ListBox {
return full_emails;
}
+
+ private void apply_search_terms(EmailRow row) {
+ if (row.view.message_bodies_loaded) {
+ apply_search_terms_impl(row);
+ } else {
+ row.view.notify["message-bodies-loaded"].connect(() => {
+ apply_search_terms_impl(row);
+ });
+ }
+ }
+ private inline void apply_search_terms_impl(EmailRow row) {
+ bool found = false;
+ row.view.message_view_iterator().foreach((view) => {
+ if (view.highlight_search_terms(this.ordered_search_terms) > 0) {
+ found = true;
+ return false;
+ }
+ return true;
+ });
+ row.is_search_match = found;
+ if (found) {
+ search_matches_found();
+ }
}
/**
diff --git a/src/client/conversation-viewer/conversation-viewer.vala
b/src/client/conversation-viewer/conversation-viewer.vala
index a1d99b7..0866206 100644
--- a/src/client/conversation-viewer/conversation-viewer.vala
+++ b/src/client/conversation-viewer/conversation-viewer.vala
@@ -189,8 +189,9 @@ public class ConversationViewer : Gtk.Stack {
Geary.Account account = location.account;
ConversationListBox new_list = new ConversationListBox(
conversation,
- account.get_contact_store(),
+ location,
new Geary.App.EmailStore(account),
+ account.get_contact_store(),
account.information,
location.special_folder_type == Geary.SpecialFolderType.DRAFTS,
conversation_scroller.get_vadjustment()
@@ -202,13 +203,27 @@ public class ConversationViewer : Gtk.Stack {
this.conversation_added(new_list);
yield new_list.load_conversation();
+ // Also set up find infrastructure early so matching emails
+ // are expanded and highlighted as they are added.
+ this.conversation_find_next.set_sensitive(false);
+ this.conversation_find_prev.set_sensitive(false);
+ new_list.search_matches_found.connect(() => {
+ this.conversation_find_next.set_sensitive(true);
+ this.conversation_find_prev.set_sensitive(true);
+ });
+ Gee.Set<string>? find_terms = get_find_search_terms();
+ if (find_terms != null) {
+ new_list.highlight_search_terms(find_terms);
+ }
remove_current_list();
add_new_list(new_list);
set_visible_child(this.conversation_page);
this.conversation_timeout_id = 0;
- if (location is Geary.SearchFolder) {
- yield new_list.load_search_terms((Geary.SearchFolder) location);
+ // Highlight matching terms from the search if it exists, but
+ // don't clobber any find terms.
+ if (find_terms == null && location is Geary.SearchFolder) {
+ yield new_list.load_search_terms();
}
}
@@ -255,6 +270,16 @@ public class ConversationViewer : Gtk.Stack {
}
base.set_visible_child(widget);
}
+
+ private Gee.Set<string>? get_find_search_terms() {
+ Gee.Set<string>? terms = null;
+ string search = this.conversation_find_entry.get_text().strip();
+ if (search.length > 0) {
+ terms = new Gee.HashSet<string>();
+ terms.add(search);
+ }
+ return terms;
+ }
[GtkCallback]
private void on_find_search_started(Object obj, ParamSpec param) {
@@ -275,15 +300,14 @@ public class ConversationViewer : Gtk.Stack {
[GtkCallback]
private void on_find_search_changed(Gtk.SearchEntry entry) {
- string search = entry.get_text().strip();
- bool have_matches = false;
+ this.conversation_find_next.set_sensitive(false);
+ this.conversation_find_prev.set_sensitive(false);
if (this.current_list != null) {
- if (search.length > 0) {
+ Gee.Set<string>? terms = get_find_search_terms();
+ if (terms != null) {
// Have a search string
- Gee.Set<string> search_matches = new Gee.HashSet<string>();
- search_matches.add(search);
- have_matches =
- this.current_list.highlight_search_terms(search_matches);
+ this.current_list.highlight_search_terms(terms);
+ // XXX scroll to first match
} else {
// Have no search string
// if (location is Geary.SearchFolder) {
@@ -296,8 +320,6 @@ public class ConversationViewer : Gtk.Stack {
// }
}
}
- this.conversation_find_next.set_sensitive(have_matches);
- this.conversation_find_prev.set_sensitive(have_matches);
}
[GtkCallback]
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]