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.
This commit is contained in:
parent
1874be9424
commit
dca845d840
9 changed files with 280 additions and 67 deletions
|
|
@ -35,8 +35,6 @@ public class GearyController : Geary.BaseObject {
|
|||
public const string ACTION_EMPTY_TRASH = "GearyEmptyTrash";
|
||||
public const string ACTION_UNDO = "GearyUndo";
|
||||
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_OUT = "GearyZoomOut";
|
||||
public const string ACTION_ZOOM_NORMAL = "GearyZoomNormal";
|
||||
|
|
@ -51,7 +49,8 @@ public class GearyController : Geary.BaseObject {
|
|||
public const string ACTION_SEARCH = "GearySearch";
|
||||
public const string ACTION_CONVERSATION_LIST = "GearyConversationList";
|
||||
public const string ACTION_TOGGLE_SEARCH = "GearyToggleSearch";
|
||||
|
||||
public const string ACTION_TOGGLE_FIND = "GearyToggleFind";
|
||||
|
||||
public const string PROP_CURRENT_CONVERSATION ="current-conversations";
|
||||
|
||||
public const int MIN_CONVERSATION_COUNT = 50;
|
||||
|
|
@ -474,15 +473,7 @@ public class GearyController : Geary.BaseObject {
|
|||
null, on_find_in_conversation_action };
|
||||
entries += 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,
|
||||
ARCHIVE_MESSAGE_LABEL, "A", null, on_archive_message };
|
||||
archive_message.tooltip = ARCHIVE_MESSAGE_TOOLTIP_SINGLE;
|
||||
|
|
@ -546,6 +537,11 @@ public class GearyController : Geary.BaseObject {
|
|||
_("Toggle search bar"), null };
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -1298,6 +1294,9 @@ public class GearyController : Geary.BaseObject {
|
|||
private void on_folder_selected(Geary.Folder? folder) {
|
||||
debug("Folder %s selected", folder != null ? folder.to_string() : "(null)");
|
||||
this.main_window.conversation_viewer.show_loading();
|
||||
GearyApplication.instance.get_action(
|
||||
ACTION_FIND_IN_CONVERSATION
|
||||
).set_sensitive(false);
|
||||
enable_message_buttons(false);
|
||||
|
||||
// If the folder is being unset, clear the message list and exit here.
|
||||
|
|
@ -1507,6 +1506,9 @@ public class GearyController : Geary.BaseObject {
|
|||
|
||||
private void on_conversations_selected(Gee.Set<Geary.App.Conversation> selected) {
|
||||
this.selected_conversations = selected;
|
||||
GearyApplication.instance.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) {
|
||||
|
|
@ -1527,6 +1529,9 @@ public class GearyController : Geary.BaseObject {
|
|||
try {
|
||||
viewer.load_conversation.end(ret);
|
||||
enable_message_buttons(!is_search);
|
||||
GearyApplication.instance.get_action(
|
||||
ACTION_FIND_IN_CONVERSATION
|
||||
).set_sensitive(true);
|
||||
} catch (Error err) {
|
||||
debug("Unable to load conversation: %s",
|
||||
err.message);
|
||||
|
|
@ -2272,11 +2277,14 @@ public class GearyController : Geary.BaseObject {
|
|||
if (widget.state == ComposerWidget.ComposerState.NEW ||
|
||||
widget.state == ComposerWidget.ComposerState.PANED) {
|
||||
main_window.conversation_viewer.do_compose(widget);
|
||||
GearyApplication.instance.get_action(
|
||||
ACTION_FIND_IN_CONVERSATION
|
||||
).set_sensitive(false);
|
||||
} else {
|
||||
ComposerEmbed embed = new ComposerEmbed(
|
||||
referred,
|
||||
widget,
|
||||
main_window.conversation_viewer.conversation_page
|
||||
main_window.conversation_viewer.conversation_scroller
|
||||
);
|
||||
if (conversation_view != null) {
|
||||
conversation_view.add_embedded_composer(embed);
|
||||
|
|
@ -2414,19 +2422,11 @@ public class GearyController : Geary.BaseObject {
|
|||
private void on_forward_message_action() {
|
||||
create_reply_forward_widget(ComposerWidget.ComposeType.FORWARD, null);
|
||||
}
|
||||
|
||||
|
||||
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() {
|
||||
archive_or_delete_selection_async.begin(true, false, cancellable_folder,
|
||||
on_archive_or_delete_selection_finished);
|
||||
|
|
|
|||
|
|
@ -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_right { get; private set; default = true; }
|
||||
public bool search_open { get; set; default = false; }
|
||||
public bool find_open { get; set; default = false; }
|
||||
public int left_pane_width { get; set; }
|
||||
|
||||
private PillHeaderbar folder_header;
|
||||
|
|
@ -106,17 +107,27 @@ public class MainToolbar : Gtk.Box {
|
|||
insert.add(conversation_header.create_popover_button("folder-symbolic", move_folder_menu,
|
||||
GearyController.ACTION_MOVE_MENU));
|
||||
conversation_header.add_start(conversation_header.create_pill_buttons(insert));
|
||||
|
||||
|
||||
// Archive, undo, find
|
||||
insert.clear();
|
||||
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));
|
||||
Gtk.Box archive_trash_delete = conversation_header.create_pill_buttons(insert);
|
||||
|
||||
|
||||
insert.clear();
|
||||
insert.add(conversation_header.create_toolbar_button(null, GearyController.ACTION_UNDO,
|
||||
false));
|
||||
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(archive_trash_delete);
|
||||
|
||||
|
|
|
|||
|
|
@ -90,6 +90,8 @@ public class MainWindow : Gtk.ApplicationWindow {
|
|||
main_toolbar = new MainToolbar();
|
||||
main_toolbar.bind_property("search-open", search_bar, "search-mode-enabled",
|
||||
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;
|
||||
set_titlebar(main_toolbar);
|
||||
|
||||
|
|
|
|||
|
|
@ -550,6 +550,15 @@ public class ConversationEmail : Gtk.Box {
|
|||
: 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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -456,8 +456,10 @@ public class ConversationListBox : Gtk.ListBox {
|
|||
|
||||
/**
|
||||
* 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
|
||||
// see all the highlighting you're applying, it seems
|
||||
// necessary to start with the shortest string and work up.
|
||||
|
|
@ -467,10 +469,24 @@ public class ConversationListBox : Gtk.ListBox {
|
|||
ordered_matches.add_all(search_matches);
|
||||
ordered_matches.sort((a, b) => a.length - b.length);
|
||||
|
||||
message_view_iterator().foreach((msg_view) => {
|
||||
msg_view.highlight_search_terms(search_matches);
|
||||
return true;
|
||||
bool any_found = false;
|
||||
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;
|
||||
});
|
||||
if (email_found) {
|
||||
row.expand();
|
||||
any_found = true;
|
||||
} else {
|
||||
row.collapse();
|
||||
}
|
||||
});
|
||||
return any_found;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -465,8 +465,10 @@ public class ConversationMessage : Gtk.Grid {
|
|||
|
||||
/**
|
||||
* 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
|
||||
|
||||
// Remove existing highlights.
|
||||
|
|
@ -481,11 +483,13 @@ public class ConversationMessage : Gtk.Grid {
|
|||
ordered_matches.add_all(search_matches);
|
||||
ordered_matches.sort((a, b) => a.length - b.length);
|
||||
|
||||
uint found = 0;
|
||||
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);
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -538,6 +542,28 @@ public class ConversationMessage : Gtk.Grid {
|
|||
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) {
|
||||
SimpleAction action = new SimpleAction(name, type);
|
||||
action.set_enabled(enabled);
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ public class ConversationViewer : Gtk.Stack {
|
|||
[GtkChild]
|
||||
private Gtk.Grid no_conversations_page;
|
||||
[GtkChild]
|
||||
internal Gtk.ScrolledWindow conversation_page;
|
||||
private Gtk.Grid conversation_page;
|
||||
[GtkChild]
|
||||
private Gtk.Grid multiple_conversations_page;
|
||||
[GtkChild]
|
||||
|
|
@ -63,7 +63,20 @@ public class ConversationViewer : Gtk.Stack {
|
|||
[GtkChild]
|
||||
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.
|
||||
private Geary.State.MachineDescriptor search_machine_desc = new Geary.State.MachineDescriptor(
|
||||
|
|
@ -131,6 +144,12 @@ public class ConversationViewer : Gtk.Stack {
|
|||
fsm = new Geary.State.Machine(search_machine_desc, mappings, null);
|
||||
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.no_show_all = true;
|
||||
//conversation_find_bar.close.connect(() => { fsm.issue(SearchEvent.CLOSE_FIND_BAR); });
|
||||
|
|
@ -158,25 +177,7 @@ public class ConversationViewer : Gtk.Stack {
|
|||
}
|
||||
});
|
||||
this.composer_page.add(box);
|
||||
set_visible_child(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);
|
||||
set_visible_child(this.composer_page);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -243,7 +244,7 @@ public class ConversationViewer : Gtk.Stack {
|
|||
new Geary.App.EmailStore(account),
|
||||
account.information,
|
||||
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
|
||||
|
|
@ -283,12 +284,12 @@ public class ConversationViewer : Gtk.Stack {
|
|||
viewport.show();
|
||||
viewport.add(list);
|
||||
|
||||
this.conversation_page.add(viewport);
|
||||
this.conversation_scroller.add(viewport);
|
||||
}
|
||||
|
||||
// Remove any existing conversation list, cancelling its loading
|
||||
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) {
|
||||
scrolled_child.destroy();
|
||||
}
|
||||
|
|
@ -314,10 +315,10 @@ public class ConversationViewer : Gtk.Stack {
|
|||
|
||||
// Find bar opened.
|
||||
private uint on_open_find_bar(uint state, uint event, void *user, Object? object) {
|
||||
if (!conversation_find_bar.visible)
|
||||
conversation_find_bar.show();
|
||||
//if (!conversation_find_bar.visible)
|
||||
// conversation_find_bar.show();
|
||||
|
||||
conversation_find_bar.focus_entry();
|
||||
//conversation_find_bar.focus_entry();
|
||||
//web_view.allow_collapsing(false);
|
||||
|
||||
return SearchState.FIND;
|
||||
|
|
@ -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 @@
|
|||
<accelerator action="GearyCopyMenuButton" />
|
||||
<accelerator action="GearyDeleteMessage" />
|
||||
<accelerator action="GearyFindInConversation" />
|
||||
<accelerator action="GearyFindNextInConversation" />
|
||||
<accelerator action="GearyFindPreviousInConversation" />
|
||||
<accelerator action="GearyForwardMessage" />
|
||||
<accelerator action="GearyMoveMenuButton" />
|
||||
<accelerator action="GearyNewMessage" />
|
||||
|
|
|
|||
|
|
@ -32,13 +32,105 @@
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="conversation_page">
|
||||
<object class="GtkGrid" id="conversation_page">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">never</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="can_focus">False</property>
|
||||
<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>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue