Commit dca845d8 authored by Michael Gratton's avatar Michael Gratton 🤞

Reimplement in-conversation find.

* src/client/application/geary-controller.vala (GearyController): Remove
  ACTION_FIND_NEXT_IN_CONVERSATION and
  ACTION_FIND_PREVIOUS_IN_CONVERSATION arctions and callbacks since they
  will be taken care of by the search entry & search bar buttons, and
  remove from accelerators.ui. Add ACTION_TOGGLE_FIND action to handle
  toggling find bar in the same way as the search bar.

* src/client/components/main-toolbar.vala (MainToolbar): Add new button
  and infrastrcuture for toggling the find bar.

* src/client/conversation-viewer/conversation-viewer.vala
  (ConversationViewer): Convert ::conversation_page to be grid, add new
  ::conversation_scroller property for the scrollbar, update call
  sites. Add props for accessing find widgets, remove old find methods
  and add callbacks for handling find start, change, etc.

* src/client/conversation-viewer/conversation-email.vala,
  src/client/conversation-viewer/conversation-message.vala: Add methods
  for accessing selected text for find.

* src/client/conversation-viewer/conversation-listbox.vala
  (ConversationListBox::highlight_search_terms): Updated to return a flag
  specifiying whether any search results were found, and to
  expand/collapse messsages depending on whether they have any.

* src/client/conversation-viewer/conversation-message.vala
  (ConversationMessage::highlight_search_terms): Keep track of how many
  results were found, and return that.

* ui/conversation-viewer.ui: Convert conversation_page to be a grid, add
  a search bar and search widgets to it, and move conversation
  ScrolledWindow to it.
parent 1874be94
...@@ -35,8 +35,6 @@ public class GearyController : Geary.BaseObject { ...@@ -35,8 +35,6 @@ public class GearyController : Geary.BaseObject {
public const string ACTION_EMPTY_TRASH = "GearyEmptyTrash"; public const string ACTION_EMPTY_TRASH = "GearyEmptyTrash";
public const string ACTION_UNDO = "GearyUndo"; public const string ACTION_UNDO = "GearyUndo";
public const string ACTION_FIND_IN_CONVERSATION = "GearyFindInConversation"; public const string ACTION_FIND_IN_CONVERSATION = "GearyFindInConversation";
public const string ACTION_FIND_NEXT_IN_CONVERSATION = "GearyFindNextInConversation";
public const string ACTION_FIND_PREVIOUS_IN_CONVERSATION = "GearyFindPreviousInConversation";
public const string ACTION_ZOOM_IN = "GearyZoomIn"; public const string ACTION_ZOOM_IN = "GearyZoomIn";
public const string ACTION_ZOOM_OUT = "GearyZoomOut"; public const string ACTION_ZOOM_OUT = "GearyZoomOut";
public const string ACTION_ZOOM_NORMAL = "GearyZoomNormal"; public const string ACTION_ZOOM_NORMAL = "GearyZoomNormal";
...@@ -51,7 +49,8 @@ public class GearyController : Geary.BaseObject { ...@@ -51,7 +49,8 @@ public class GearyController : Geary.BaseObject {
public const string ACTION_SEARCH = "GearySearch"; public const string ACTION_SEARCH = "GearySearch";
public const string ACTION_CONVERSATION_LIST = "GearyConversationList"; public const string ACTION_CONVERSATION_LIST = "GearyConversationList";
public const string ACTION_TOGGLE_SEARCH = "GearyToggleSearch"; public const string ACTION_TOGGLE_SEARCH = "GearyToggleSearch";
public const string ACTION_TOGGLE_FIND = "GearyToggleFind";
public const string PROP_CURRENT_CONVERSATION ="current-conversations"; public const string PROP_CURRENT_CONVERSATION ="current-conversations";
public const int MIN_CONVERSATION_COUNT = 50; public const int MIN_CONVERSATION_COUNT = 50;
...@@ -474,15 +473,7 @@ public class GearyController : Geary.BaseObject { ...@@ -474,15 +473,7 @@ public class GearyController : Geary.BaseObject {
null, on_find_in_conversation_action }; null, on_find_in_conversation_action };
entries += find_in_conversation; entries += find_in_conversation;
add_accelerator("slash", ACTION_FIND_IN_CONVERSATION); add_accelerator("slash", ACTION_FIND_IN_CONVERSATION);
Gtk.ActionEntry find_next_in_conversation = { ACTION_FIND_NEXT_IN_CONVERSATION, null, null,
"<Ctrl>G", null, on_find_next_in_conversation_action };
entries += find_next_in_conversation;
Gtk.ActionEntry find_previous_in_conversation = { ACTION_FIND_PREVIOUS_IN_CONVERSATION,
null, null, "<Shift><Ctrl>G", null, on_find_previous_in_conversation_action };
entries += find_previous_in_conversation;
Gtk.ActionEntry archive_message = { ACTION_ARCHIVE_MESSAGE, ARCHIVE_MESSAGE_ICON_NAME, Gtk.ActionEntry archive_message = { ACTION_ARCHIVE_MESSAGE, ARCHIVE_MESSAGE_ICON_NAME,
ARCHIVE_MESSAGE_LABEL, "A", null, on_archive_message }; ARCHIVE_MESSAGE_LABEL, "A", null, on_archive_message };
archive_message.tooltip = ARCHIVE_MESSAGE_TOOLTIP_SINGLE; archive_message.tooltip = ARCHIVE_MESSAGE_TOOLTIP_SINGLE;
...@@ -546,6 +537,11 @@ public class GearyController : Geary.BaseObject { ...@@ -546,6 +537,11 @@ public class GearyController : Geary.BaseObject {
_("Toggle search bar"), null }; _("Toggle search bar"), null };
entries += toggle_search; entries += toggle_search;
// No callback is connected, since we bind the toggle button to the find bar visibility
Gtk.ActionEntry toggle_find = { ACTION_TOGGLE_FIND, null, null, null,
_("Toggle find bar"), null };
entries += toggle_find;
return entries; return entries;
} }
...@@ -1298,6 +1294,9 @@ public class GearyController : Geary.BaseObject { ...@@ -1298,6 +1294,9 @@ public class GearyController : Geary.BaseObject {
private void on_folder_selected(Geary.Folder? folder) { private void on_folder_selected(Geary.Folder? folder) {
debug("Folder %s selected", folder != null ? folder.to_string() : "(null)"); debug("Folder %s selected", folder != null ? folder.to_string() : "(null)");
this.main_window.conversation_viewer.show_loading(); this.main_window.conversation_viewer.show_loading();
GearyApplication.instance.get_action(
ACTION_FIND_IN_CONVERSATION
).set_sensitive(false);
enable_message_buttons(false); enable_message_buttons(false);
// If the folder is being unset, clear the message list and exit here. // If the folder is being unset, clear the message list and exit here.
...@@ -1507,6 +1506,9 @@ public class GearyController : Geary.BaseObject { ...@@ -1507,6 +1506,9 @@ public class GearyController : Geary.BaseObject {
private void on_conversations_selected(Gee.Set<Geary.App.Conversation> selected) { private void on_conversations_selected(Gee.Set<Geary.App.Conversation> selected) {
this.selected_conversations = selected; this.selected_conversations = selected;
GearyApplication.instance.get_action(
ACTION_FIND_IN_CONVERSATION
).set_sensitive(false);
ConversationViewer viewer = this.main_window.conversation_viewer; ConversationViewer viewer = this.main_window.conversation_viewer;
if (this.current_folder != null && !viewer.is_composer_visible) { if (this.current_folder != null && !viewer.is_composer_visible) {
switch(selected.size) { switch(selected.size) {
...@@ -1527,6 +1529,9 @@ public class GearyController : Geary.BaseObject { ...@@ -1527,6 +1529,9 @@ public class GearyController : Geary.BaseObject {
try { try {
viewer.load_conversation.end(ret); viewer.load_conversation.end(ret);
enable_message_buttons(!is_search); enable_message_buttons(!is_search);
GearyApplication.instance.get_action(
ACTION_FIND_IN_CONVERSATION
).set_sensitive(true);
} catch (Error err) { } catch (Error err) {
debug("Unable to load conversation: %s", debug("Unable to load conversation: %s",
err.message); err.message);
...@@ -2272,11 +2277,14 @@ public class GearyController : Geary.BaseObject { ...@@ -2272,11 +2277,14 @@ public class GearyController : Geary.BaseObject {
if (widget.state == ComposerWidget.ComposerState.NEW || if (widget.state == ComposerWidget.ComposerState.NEW ||
widget.state == ComposerWidget.ComposerState.PANED) { widget.state == ComposerWidget.ComposerState.PANED) {
main_window.conversation_viewer.do_compose(widget); main_window.conversation_viewer.do_compose(widget);
GearyApplication.instance.get_action(
ACTION_FIND_IN_CONVERSATION
).set_sensitive(false);
} else { } else {
ComposerEmbed embed = new ComposerEmbed( ComposerEmbed embed = new ComposerEmbed(
referred, referred,
widget, widget,
main_window.conversation_viewer.conversation_page main_window.conversation_viewer.conversation_scroller
); );
if (conversation_view != null) { if (conversation_view != null) {
conversation_view.add_embedded_composer(embed); conversation_view.add_embedded_composer(embed);
...@@ -2414,19 +2422,11 @@ public class GearyController : Geary.BaseObject { ...@@ -2414,19 +2422,11 @@ public class GearyController : Geary.BaseObject {
private void on_forward_message_action() { private void on_forward_message_action() {
create_reply_forward_widget(ComposerWidget.ComposeType.FORWARD, null); create_reply_forward_widget(ComposerWidget.ComposeType.FORWARD, null);
} }
private void on_find_in_conversation_action() { private void on_find_in_conversation_action() {
main_window.conversation_viewer.show_find_bar(); this.main_window.conversation_viewer.conversation_find_bar.set_search_mode(true);
}
private void on_find_next_in_conversation_action() {
main_window.conversation_viewer.find(true);
} }
private void on_find_previous_in_conversation_action() {
main_window.conversation_viewer.find(false);
}
private void on_archive_message() { private void on_archive_message() {
archive_or_delete_selection_async.begin(true, false, cancellable_folder, archive_or_delete_selection_async.begin(true, false, cancellable_folder,
on_archive_or_delete_selection_finished); on_archive_or_delete_selection_finished);
......
...@@ -14,6 +14,7 @@ public class MainToolbar : Gtk.Box { ...@@ -14,6 +14,7 @@ public class MainToolbar : Gtk.Box {
public bool show_close_button_left { get; private set; default = true; } public bool show_close_button_left { get; private set; default = true; }
public bool show_close_button_right { get; private set; default = true; } public bool show_close_button_right { get; private set; default = true; }
public bool search_open { get; set; default = false; } public bool search_open { get; set; default = false; }
public bool find_open { get; set; default = false; }
public int left_pane_width { get; set; } public int left_pane_width { get; set; }
private PillHeaderbar folder_header; private PillHeaderbar folder_header;
...@@ -106,17 +107,27 @@ public class MainToolbar : Gtk.Box { ...@@ -106,17 +107,27 @@ public class MainToolbar : Gtk.Box {
insert.add(conversation_header.create_popover_button("folder-symbolic", move_folder_menu, insert.add(conversation_header.create_popover_button("folder-symbolic", move_folder_menu,
GearyController.ACTION_MOVE_MENU)); GearyController.ACTION_MOVE_MENU));
conversation_header.add_start(conversation_header.create_pill_buttons(insert)); conversation_header.add_start(conversation_header.create_pill_buttons(insert));
// Archive, undo, find
insert.clear(); insert.clear();
insert.add(archive_button = conversation_header.create_toolbar_button(null, GearyController.ACTION_ARCHIVE_MESSAGE, true)); insert.add(archive_button = conversation_header.create_toolbar_button(null, GearyController.ACTION_ARCHIVE_MESSAGE, true));
insert.add(trash_delete_button = conversation_header.create_toolbar_button(null, GearyController.ACTION_TRASH_MESSAGE, false)); insert.add(trash_delete_button = conversation_header.create_toolbar_button(null, GearyController.ACTION_TRASH_MESSAGE, false));
Gtk.Box archive_trash_delete = conversation_header.create_pill_buttons(insert); Gtk.Box archive_trash_delete = conversation_header.create_pill_buttons(insert);
insert.clear(); insert.clear();
insert.add(conversation_header.create_toolbar_button(null, GearyController.ACTION_UNDO, insert.add(conversation_header.create_toolbar_button(null, GearyController.ACTION_UNDO,
false)); false));
Gtk.Box undo = conversation_header.create_pill_buttons(insert); Gtk.Box undo = conversation_header.create_pill_buttons(insert);
Gtk.Button find_button = folder_header.create_toggle_button(
"preferences-system-search-symbolic", GearyController.ACTION_TOGGLE_FIND);
this.bind_property("find-open", find_button, "active",
BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
insert.clear();
insert.add(find_button);
Gtk.Box find = conversation_header.create_pill_buttons(insert);
conversation_header.add_end(find);
conversation_header.add_end(undo); conversation_header.add_end(undo);
conversation_header.add_end(archive_trash_delete); conversation_header.add_end(archive_trash_delete);
......
...@@ -90,6 +90,8 @@ public class MainWindow : Gtk.ApplicationWindow { ...@@ -90,6 +90,8 @@ public class MainWindow : Gtk.ApplicationWindow {
main_toolbar = new MainToolbar(); main_toolbar = new MainToolbar();
main_toolbar.bind_property("search-open", search_bar, "search-mode-enabled", main_toolbar.bind_property("search-open", search_bar, "search-mode-enabled",
BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
main_toolbar.bind_property("find-open", conversation_viewer.conversation_find_bar, "search-mode-enabled",
BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
main_toolbar.show_close_button = true; main_toolbar.show_close_button = true;
set_titlebar(main_toolbar); set_titlebar(main_toolbar);
......
...@@ -550,6 +550,15 @@ public class ConversationEmail : Gtk.Box { ...@@ -550,6 +550,15 @@ public class ConversationEmail : Gtk.Box {
: null; : null;
} }
/**
* Returns user-selected body text from a message, if any.
*/
public string? get_selection_for_find() {
return (this.body_selection_message != null)
? this.body_selection_message.get_selection_for_find()
: null;
}
/** /**
* Attach an embedded composer to this email view. * Attach an embedded composer to this email view.
*/ */
......
...@@ -456,8 +456,10 @@ public class ConversationListBox : Gtk.ListBox { ...@@ -456,8 +456,10 @@ public class ConversationListBox : Gtk.ListBox {
/** /**
* Applies search term highlighting to all email views. * Applies search term highlighting to all email views.
*
* Returns true if any were found, else returns false.
*/ */
public void highlight_search_terms(Gee.Set<string>? search_matches) { public bool highlight_search_terms(Gee.Set<string> search_matches) {
// Webkit's highlighting is ... weird. In order to actually // Webkit's highlighting is ... weird. In order to actually
// see all the highlighting you're applying, it seems // see all the highlighting you're applying, it seems
// necessary to start with the shortest string and work up. // necessary to start with the shortest string and work up.
...@@ -467,10 +469,24 @@ public class ConversationListBox : Gtk.ListBox { ...@@ -467,10 +469,24 @@ public class ConversationListBox : Gtk.ListBox {
ordered_matches.add_all(search_matches); ordered_matches.add_all(search_matches);
ordered_matches.sort((a, b) => a.length - b.length); ordered_matches.sort((a, b) => a.length - b.length);
message_view_iterator().foreach((msg_view) => { bool any_found = false;
msg_view.highlight_search_terms(search_matches); this.foreach((child) => {
return true; 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;
});
if (email_found) {
row.expand();
any_found = true;
} else {
row.collapse();
}
}); });
return any_found;
} }
/** /**
......
...@@ -465,8 +465,10 @@ public class ConversationMessage : Gtk.Grid { ...@@ -465,8 +465,10 @@ public class ConversationMessage : Gtk.Grid {
/** /**
* Highlights user search terms in the message view. * Highlights user search terms in the message view.
&
* Returns the number of matching search terms.
*/ */
public void highlight_search_terms(Gee.Set<string> search_matches) { public uint highlight_search_terms(Gee.Set<string> search_matches) {
// XXX Need to highlight subject, sender and recipient matches too // XXX Need to highlight subject, sender and recipient matches too
// Remove existing highlights. // Remove existing highlights.
...@@ -481,11 +483,13 @@ public class ConversationMessage : Gtk.Grid { ...@@ -481,11 +483,13 @@ public class ConversationMessage : Gtk.Grid {
ordered_matches.add_all(search_matches); ordered_matches.add_all(search_matches);
ordered_matches.sort((a, b) => a.length - b.length); ordered_matches.sort((a, b) => a.length - b.length);
uint found = 0;
foreach(string match in ordered_matches) { foreach(string match in ordered_matches) {
web_view.mark_text_matches(match, false, 0); found += web_view.mark_text_matches(match, false, 0);
} }
web_view.set_highlight_text_matches(true); web_view.set_highlight_text_matches(true);
return found;
} }
/** /**
...@@ -538,6 +542,28 @@ public class ConversationMessage : Gtk.Grid { ...@@ -538,6 +542,28 @@ public class ConversationMessage : Gtk.Grid {
return quote; return quote;
} }
/**
* Returns the current selection as a string, suitable for find.
*/
internal string? get_selection_for_find() {
string? value = null;
WebKit.DOM.Document document = web_view.get_dom_document();
WebKit.DOM.DOMWindow window = document.get_default_view();
WebKit.DOM.DOMSelection selection = window.get_selection();
if (selection.get_range_count() > 0) {
try {
WebKit.DOM.Range range = selection.get_range_at(0);
value = range.get_text().strip();
if (value.length <= 0)
value = null;
} catch (Error e) {
warning("Could not get selected text from web view: %s", e.message);
}
}
return value;
}
private SimpleAction add_action(string name, bool enabled, VariantType? type = null) { private SimpleAction add_action(string name, bool enabled, VariantType? type = null) {
SimpleAction action = new SimpleAction(name, type); SimpleAction action = new SimpleAction(name, type);
action.set_enabled(enabled); action.set_enabled(enabled);
......
...@@ -53,7 +53,7 @@ public class ConversationViewer : Gtk.Stack { ...@@ -53,7 +53,7 @@ public class ConversationViewer : Gtk.Stack {
[GtkChild] [GtkChild]
private Gtk.Grid no_conversations_page; private Gtk.Grid no_conversations_page;
[GtkChild] [GtkChild]
internal Gtk.ScrolledWindow conversation_page; private Gtk.Grid conversation_page;
[GtkChild] [GtkChild]
private Gtk.Grid multiple_conversations_page; private Gtk.Grid multiple_conversations_page;
[GtkChild] [GtkChild]
...@@ -63,7 +63,20 @@ public class ConversationViewer : Gtk.Stack { ...@@ -63,7 +63,20 @@ public class ConversationViewer : Gtk.Stack {
[GtkChild] [GtkChild]
private Gtk.Grid composer_page; private Gtk.Grid composer_page;
private ConversationFindBar conversation_find_bar; [GtkChild]
internal Gtk.ScrolledWindow conversation_scroller;
[GtkChild]
internal Gtk.SearchBar conversation_find_bar;
[GtkChild]
internal Gtk.SearchEntry conversation_find_entry;
[GtkChild]
private Gtk.Button conversation_find_next;
[GtkChild]
private Gtk.Button conversation_find_prev;
// State machine setup for search/find modes. // State machine setup for search/find modes.
private Geary.State.MachineDescriptor search_machine_desc = new Geary.State.MachineDescriptor( private Geary.State.MachineDescriptor search_machine_desc = new Geary.State.MachineDescriptor(
...@@ -131,6 +144,12 @@ public class ConversationViewer : Gtk.Stack { ...@@ -131,6 +144,12 @@ public class ConversationViewer : Gtk.Stack {
fsm = new Geary.State.Machine(search_machine_desc, mappings, null); fsm = new Geary.State.Machine(search_machine_desc, mappings, null);
fsm.set_logging(false); fsm.set_logging(false);
this.conversation_find_bar.notify["search-mode-enabled"].connect(
on_find_search_started
);
// XXX Do this in Glade when possible.
this.conversation_find_bar.connect_entry(this.conversation_find_entry);
//conversation_find_bar = new ConversationFindBar(web_view); //conversation_find_bar = new ConversationFindBar(web_view);
//conversation_find_bar.no_show_all = true; //conversation_find_bar.no_show_all = true;
//conversation_find_bar.close.connect(() => { fsm.issue(SearchEvent.CLOSE_FIND_BAR); }); //conversation_find_bar.close.connect(() => { fsm.issue(SearchEvent.CLOSE_FIND_BAR); });
...@@ -158,25 +177,7 @@ public class ConversationViewer : Gtk.Stack { ...@@ -158,25 +177,7 @@ public class ConversationViewer : Gtk.Stack {
} }
}); });
this.composer_page.add(box); this.composer_page.add(box);
set_visible_child(composer_page); set_visible_child(this.composer_page);
}
/**
* Shows the in-conversation search UI.
*/
public void show_find_bar() {
fsm.issue(SearchEvent.OPEN_FIND_BAR);
conversation_find_bar.focus_entry();
}
/**
* Displays the next/previous match for an in-conversation search.
*/
public void find(bool forward) {
if (!conversation_find_bar.visible)
show_find_bar();
conversation_find_bar.find(forward);
} }
/** /**
...@@ -243,7 +244,7 @@ public class ConversationViewer : Gtk.Stack { ...@@ -243,7 +244,7 @@ public class ConversationViewer : Gtk.Stack {
new Geary.App.EmailStore(account), new Geary.App.EmailStore(account),
account.information, account.information,
location.special_folder_type == Geary.SpecialFolderType.DRAFTS, location.special_folder_type == Geary.SpecialFolderType.DRAFTS,
conversation_page.get_vadjustment() conversation_scroller.get_vadjustment()
); );
// Need to fire this signal early so the the controller // Need to fire this signal early so the the controller
...@@ -283,12 +284,12 @@ public class ConversationViewer : Gtk.Stack { ...@@ -283,12 +284,12 @@ public class ConversationViewer : Gtk.Stack {
viewport.show(); viewport.show();
viewport.add(list); viewport.add(list);
this.conversation_page.add(viewport); this.conversation_scroller.add(viewport);
} }
// Remove any existing conversation list, cancelling its loading // Remove any existing conversation list, cancelling its loading
private void remove_current_list() { private void remove_current_list() {
Gtk.Widget? scrolled_child = this.conversation_page.get_child(); Gtk.Widget? scrolled_child = this.conversation_scroller.get_child();
if (scrolled_child != null) { if (scrolled_child != null) {
scrolled_child.destroy(); scrolled_child.destroy();
} }
...@@ -314,10 +315,10 @@ public class ConversationViewer : Gtk.Stack { ...@@ -314,10 +315,10 @@ public class ConversationViewer : Gtk.Stack {
// Find bar opened. // Find bar opened.
private uint on_open_find_bar(uint state, uint event, void *user, Object? object) { private uint on_open_find_bar(uint state, uint event, void *user, Object? object) {
if (!conversation_find_bar.visible) //if (!conversation_find_bar.visible)
conversation_find_bar.show(); // conversation_find_bar.show();
conversation_find_bar.focus_entry(); //conversation_find_bar.focus_entry();
//web_view.allow_collapsing(false); //web_view.allow_collapsing(false);
return SearchState.FIND; return SearchState.FIND;
...@@ -336,4 +337,62 @@ public class ConversationViewer : Gtk.Stack { ...@@ -336,4 +337,62 @@ public class ConversationViewer : Gtk.Stack {
// } // }
} }
private void on_find_search_started(Object obj, ParamSpec param) {
if (this.conversation_find_bar.get_search_mode()) {
if (this.current_list != null) {
ConversationEmail? email_view =
this.current_list.get_selection_view();
if (email_view != null) {
string text = email_view.get_selection_for_find();
if (text != null) {
this.conversation_find_entry.set_text(text);
this.conversation_find_entry.select_region(0, -1);
}
}
}
}
}
[GtkCallback]
private void on_find_search_changed(Gtk.SearchEntry entry) {
string search = entry.get_text().strip();
bool have_matches = false;
if (this.current_list != null) {
if (search.length > 0) {
// 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);
} else {
// Have no search string
// if (location is Geary.SearchFolder) {
// // Re-display the search results
// yield this.current_list.load_search_terms(
// (Geary.SearchFolder) location
// );
// } else {
this.current_list.unmark_search_terms();
// }
}
}
this.conversation_find_next.set_sensitive(have_matches);
this.conversation_find_prev.set_sensitive(have_matches);
}
[GtkCallback]
private void on_find_next(Gtk.Widget entry) {
if (this.current_list != null) {
//this.current_list.show_prev_search_term();
}
}
[GtkCallback]
private void on_find_prev(Gtk.Widget entry) {
if (this.current_list != null) {
//this.current_list.show_next_search_term();
}
}
} }
...@@ -4,8 +4,6 @@ ...@@ -4,8 +4,6 @@
<accelerator action="GearyCopyMenuButton" /> <accelerator action="GearyCopyMenuButton" />
<accelerator action="GearyDeleteMessage" /> <accelerator action="GearyDeleteMessage" />
<accelerator action="GearyFindInConversation" /> <accelerator action="GearyFindInConversation" />
<accelerator action="GearyFindNextInConversation" />
<accelerator action="GearyFindPreviousInConversation" />
<accelerator action="GearyForwardMessage" /> <accelerator action="GearyForwardMessage" />
<accelerator action="GearyMoveMenuButton" /> <accelerator action="GearyMoveMenuButton" />
<accelerator action="GearyNewMessage" /> <accelerator action="GearyNewMessage" />
......
...@@ -32,13 +32,105 @@ ...@@ -32,13 +32,105 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkScrolledWindow" id="conversation_page"> <object class="GtkGrid" id="conversation_page">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">False</property>
<property name="hscrollbar_policy">never</property>
<property name="shadow_type">in</property>
<child> <child>
<placeholder/> <object class="GtkSearchBar" id="conversation_find_bar">
<property name="visible">True</property>
<property name="app_paintable">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkSearchEntry" id="conversation_find_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="primary_icon_name">edit-find-symbolic</property>
<property name="primary_icon_activatable">False</property>
<property name="primary_icon_sensitive">False</property>
<property name="placeholder_text" translatable="yes">Find in conversation</property>
<signal name="next-match" handler="on_find_next" swapped="no"/>
<signal name="previous-match" handler="on_find_prev" swapped="no"/>
<signal name="search-changed" handler="on_find_search_changed" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="conversation_find_prev">
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="no_show_all">True</property>
<property name="tooltip_text" translatable="yes">Find the previous occurrence of the search string.</property>
<signal name="clicked" handler="on_find_prev" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">go-up-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="conversation_find_next">
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="no_show_all">True</property>
<property name="tooltip_text" translatable="yes">Find the next occurrence of the search string.</property>
<signal name="activate" handler="on_find_next" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">go-down-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
</packing>
</child>
<style>
<class name="linked"/>
</style>
</object>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="conversation_scroller">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="hscrollbar_policy">never</property>
<property name="shadow_type">in</property>
<child>
<placeholder/>
</child>
</object>
<packing>