From e376a577b6bfcdd9bdc0cc6ca283d99199a0197a Mon Sep 17 00:00:00 2001 From: bobufa Date: Wed, 25 Jul 2018 20:41:51 +0200 Subject: improve sidebar UI - only display messages that are content items - only display messages for active accounts - "fix" textview issue - add empty states (no search, no results) --- main/src/ui/global_search.vala | 129 ++++++++++++++++++++++++----------------- 1 file changed, 75 insertions(+), 54 deletions(-) (limited to 'main/src/ui/global_search.vala') diff --git a/main/src/ui/global_search.vala b/main/src/ui/global_search.vala index cadee9c1..8bd13e6f 100644 --- a/main/src/ui/global_search.vala +++ b/main/src/ui/global_search.vala @@ -1,3 +1,4 @@ +using Gee; using Gtk; using Pango; @@ -7,6 +8,8 @@ namespace Dino.Ui { [GtkTemplate (ui = "/im/dino/Dino/global_search.ui")] class GlobalSearch : Box { + public signal void selected_item(MessageItem item); + private StreamInteractor stream_interactor; private string search = ""; private int loaded_results = -1; @@ -16,6 +19,7 @@ class GlobalSearch : Box { [GtkChild] public Label entry_number_label; [GtkChild] public ScrolledWindow results_scrolled; [GtkChild] public Box results_box; + [GtkChild] public Stack results_empty_stack; public GlobalSearch init(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; @@ -27,7 +31,7 @@ class GlobalSearch : Box { results_scrolled.vadjustment.notify["value"].connect(() => { if (results_scrolled.vadjustment.upper - (results_scrolled.vadjustment.value + results_scrolled.vadjustment.page_size) < 100) { if (!reloading_mutex.trylock()) return; - Gee.List new_messages = stream_interactor.get_module(SearchProcessor.IDENTITY).match_messages(search, loaded_results); + Gee.List new_messages = stream_interactor.get_module(SearchProcessor.IDENTITY).match_messages(search, loaded_results); if (new_messages.size == 0) { reloading_mutex.unlock(); return; @@ -51,37 +55,47 @@ class GlobalSearch : Box { clear_search(); this.search = search; - int match_count = stream_interactor.get_module(SearchProcessor.IDENTITY).count_match_messages(search); - entry_number_label.label = "" + _("%i search results").printf(match_count) + ""; - Gee.List messages = stream_interactor.get_module(SearchProcessor.IDENTITY).match_messages(search); - loaded_results += messages.size; - append_messages(messages); + if (get_keywords(search).is_empty) { + results_empty_stack.set_visible_child_name("empty"); + return; + } + + Gee.List messages = stream_interactor.get_module(SearchProcessor.IDENTITY).match_messages(search); + if (messages.size == 0) { + results_empty_stack.set_visible_child_name("no-result"); + } else { + results_empty_stack.set_visible_child_name("results"); + + int match_count = messages.size < 10 ? messages.size : stream_interactor.get_module(SearchProcessor.IDENTITY).count_match_messages(search); + entry_number_label.label = "" + _("%i search results").printf(match_count) + ""; + loaded_results += messages.size; + append_messages(messages); + } } - private void append_messages(Gee.List messages) { - foreach (Message message in messages) { - if (message.from == null) { - print("wtf null\n"); - continue; - } - Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_for_message(message); - Gee.List before_message = stream_interactor.get_module(MessageStorage.IDENTITY).get_messages_before_message(conversation, message.local_time, message.id, 1); - Gee.List after_message = stream_interactor.get_module(MessageStorage.IDENTITY).get_messages_after_message(conversation, message.local_time, message.id, 1); + private void append_messages(Gee.List messages) { + foreach (MessageItem item in messages) { + Gee.List before_message = stream_interactor.get_module(MessageStorage.IDENTITY).get_messages_before_message(item.conversation, item.message.local_time, item.message.id, 1); + Gee.List after_message = stream_interactor.get_module(MessageStorage.IDENTITY).get_messages_after_message(item.conversation, item.message.local_time, item.message.id, 1); Box context_box = new Box(Orientation.VERTICAL, 5) { visible=true }; if (before_message != null && before_message.size > 0) { context_box.add(get_context_message_widget(before_message.first())); } - context_box.add(get_match_message_widget(message)); + + Widget match_widget = get_match_message_widget(item); + Util.force_alloc_width(match_widget, results_empty_stack.get_allocated_width() - results_box.margin * 2); + context_box.add(match_widget); + if (after_message != null && after_message.size > 0) { context_box.add(get_context_message_widget(after_message.first())); } - Label date_label = new Label(ConversationSummary.DefaultSkeletonHeader.get_relative_time(message.time)) { xalign=0, visible=true }; + Label date_label = new Label(ConversationSummary.DefaultSkeletonHeader.get_relative_time(item.display_time)) { xalign=0, visible=true }; date_label.get_style_context().add_class("dim-label"); - string display_name = Util.get_conversation_display_name(stream_interactor, conversation); - string title = message.type_ == Message.Type.GROUPCHAT ? _("In %s").printf(display_name) : _("With %s").printf(display_name); + string display_name = Util.get_conversation_display_name(stream_interactor, item.conversation); + string title = item.message.type_ == Message.Type.GROUPCHAT ? _("In %s").printf(display_name) : _("With %s").printf(display_name); Box header_box = new Box(Orientation.HORIZONTAL, 10) { margin_left=7, visible=true }; header_box.add(new Label(@"$(Markup.escape_text(title))") { ellipsize=EllipsizeMode.END, xalign=0, use_markup=true, visible=true }); header_box.add(date_label); @@ -94,21 +108,12 @@ class GlobalSearch : Box { } } - // Workaround GTK TextView issues - private void force_alloc_width(Widget widget, int width) { - Allocation alloc = Allocation(); - widget.get_preferred_width(out alloc.width, null); - widget.get_preferred_height(out alloc.height, null); - alloc.width = width; - widget.size_allocate(alloc); - } - - private Widget get_match_message_widget(Message message) { - Grid grid = get_skeleton(message); + private Widget get_match_message_widget(MessageItem item) { + Grid grid = get_skeleton(item); grid.margin_top = 3; grid.margin_bottom = 3; - string text = message.body.replace("\n", "").replace("\r", ""); + string text = item.message.body.replace("\n", "").replace("\r", ""); if (text.length > 200) { int index = text.index_of(search); if (index + search.length <= 100) { @@ -123,52 +128,68 @@ class GlobalSearch : Box { tv.buffer.text = text; TextTag link_tag = tv.buffer.create_tag("hit", background: "yellow"); - Regex url_regex = new Regex(search.down()); - MatchInfo match_info; - url_regex.match(text.down(), 0, out match_info); - for (; match_info.matches(); match_info.next()) { - int start; - int end; - match_info.fetch_pos(0, out start, out end); - start = text[0:start].char_count(); - end = text[0:end].char_count(); - TextIter start_iter; - TextIter end_iter; - tv.buffer.get_iter_at_offset(out start_iter, start); - tv.buffer.get_iter_at_offset(out end_iter, end); - tv.buffer.apply_tag(link_tag, start_iter, end_iter); + Gee.List keywords = get_keywords(Regex.escape_string(search.down())); + foreach (string keyword in keywords) { + Regex url_regex = new Regex(keyword.down()); + MatchInfo match_info; + url_regex.match(text.down(), 0, out match_info); + for (; match_info.matches(); match_info.next()) { + int start; + int end; + match_info.fetch_pos(0, out start, out end); + start = text[0:start].char_count(); + end = text[0:end].char_count(); + TextIter start_iter; + TextIter end_iter; + tv.buffer.get_iter_at_offset(out start_iter, start); + tv.buffer.get_iter_at_offset(out end_iter, end); + tv.buffer.apply_tag(link_tag, start_iter, end_iter); + } } - grid.attach(tv, 1, 1, 1, 1); - // force_alloc_width(tv, this.width_request); + grid.attach(tv, 1, 1, 1, 1); Button button = new Button() { relief=ReliefStyle.NONE, visible=true }; + button.clicked.connect(() => { + selected_item(item); + }); button.add(grid); return button; } - private Grid get_context_message_widget(Message message) { - Grid grid = get_skeleton(message); + private Grid get_context_message_widget(MessageItem item) { + Grid grid = get_skeleton(item); grid.margin_left = 7; - Label label = new Label(message.body.replace("\n", "").replace("\r", "")) { ellipsize=EllipsizeMode.MIDDLE, xalign=0, visible=true }; + Label label = new Label(item.message.body.replace("\n", "").replace("\r", "")) { ellipsize=EllipsizeMode.MIDDLE, xalign=0, visible=true }; grid.attach(label, 1, 1, 1, 1); grid.opacity = 0.55; return grid; } - private Grid get_skeleton(Message message) { + private Grid get_skeleton(MessageItem item) { AvatarImage image = new AvatarImage() { height=32, width=32, margin_right=7, valign=Align.START, visible=true, allow_gray = false }; - image.set_jid(stream_interactor, message.from, message.account); + image.set_jid(stream_interactor, item.jid, item.message.account); Grid grid = new Grid() { row_homogeneous=false, visible=true }; grid.attach(image, 0, 0, 1, 2); - string display_name = Util.get_display_name(stream_interactor, message.from, message.account); - string color = Util.get_name_hex_color(stream_interactor, message.account, message.from, false); // TODO Util.is_dark_theme(name_label) + string display_name = Util.get_display_name(stream_interactor, item.jid, item.message.account); + string color = Util.get_name_hex_color(stream_interactor, item.message.account, item.jid, false); // TODO Util.is_dark_theme(name_label) Label name_label = new Label("") { use_markup=true, xalign=0, visible=true }; name_label.label = @"$display_name"; grid.attach(name_label, 1, 0, 1, 1); return grid; } + + private static Gee.List get_keywords(string search_string) { + Gee.List ret = new ArrayList(); + foreach (string search in search_string.split(" ")) { + bool is_filter = search.has_prefix("from:") || search.has_prefix("in:") || search.has_prefix("with:"); + if (!is_filter && search != "") { + ret.add(search); + } + } + return ret; + } } } -- cgit v1.2.3-54-g00ecf