aboutsummaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorbobufa <bobufa@users.noreply.github.com>2018-08-01 15:20:56 +0200
committerbobufa <bobufa@users.noreply.github.com>2018-08-13 22:39:18 +0200
commitc0844bdea428c10949339960bd16ea5e2a335fb8 (patch)
treef2e1e3f0257a7d0ef41f052a31eb143f613fb8d6 /main
parentb0c94641fcf85ed55976e894fea970331ae22892 (diff)
downloaddino-c0844bdea428c10949339960bd16ea5e2a335fb8.tar.gz
dino-c0844bdea428c10949339960bd16ea5e2a335fb8.zip
add suggestions/auto-complete for search filters
Diffstat (limited to 'main')
-rw-r--r--main/CMakeLists.txt1
-rw-r--r--main/data/global_search.ui249
-rw-r--r--main/data/search_autocomplete.ui24
-rw-r--r--main/data/theme.css19
-rw-r--r--main/data/unified_main_content.ui4
-rw-r--r--main/src/ui/global_search.vala68
6 files changed, 240 insertions, 125 deletions
diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt
index 1af08217..49b1a9fc 100644
--- a/main/CMakeLists.txt
+++ b/main/CMakeLists.txt
@@ -44,6 +44,7 @@ set(RESOURCE_LIST
menu_encryption.ui
occupant_list.ui
occupant_list_item.ui
+ search_autocomplete.ui
settings_dialog.ui
unified_main_content.ui
unified_window_placeholder.ui
diff --git a/main/data/global_search.ui b/main/data/global_search.ui
index 3c4597c1..44abf6de 100644
--- a/main/data/global_search.ui
+++ b/main/data/global_search.ui
@@ -1,144 +1,167 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <template class="DinoUiGlobalSearch" parent="GtkBox">
- <property name="orientation">vertical</property>
+ <template class="DinoUiGlobalSearch" parent="GtkOverlay">
<property name="visible">True</property>
<child>
- <object class="GtkSearchEntry" id="search_entry">
- <property name="visible">True</property>
- <property name="margin">12</property>
- </object>
- </child>
- <child>
- <object class="GtkStack" id="results_empty_stack">
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
<property name="visible">True</property>
<child>
- <object class="GtkBox">
- <property name="orientation">vertical</property>
- <property name="spacing">10</property>
- <property name="valign">center</property>
+ <object class="GtkSearchEntry" id="search_entry">
<property name="visible">True</property>
- <child>
- <object class="GtkImage">
- <property name="visible">True</property>
- <property name="icon-name">system-search-symbolic</property>
- <property name="icon-size">4</property>
- <property name="pixel-size">72</property>
- <style>
- <class name="dim-label"/>
- </style>
- </object>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="label" translatable="yes">No active search</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="visible">True</property>
- <attributes>
- <attribute name="weight" value="PANGO_WEIGHT_BOLD"/>
- <attribute name="scale" value="1.3"/>
- </attributes>
- <style>
- <class name="dim-label"/>
- </style>
- </object>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="label" translatable="yes">Type to start a search</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="visible">True</property>
- <style>
- <class name="dim-label"/>
- </style>
- </object>
- </child>
+ <property name="margin">12</property>
</object>
- <packing>
- <property name="name">empty</property>
- </packing>
</child>
<child>
- <object class="GtkBox">
- <property name="orientation">vertical</property>
- <property name="spacing">10</property>
- <property name="valign">center</property>
+ <object class="GtkStack" id="results_empty_stack">
<property name="visible">True</property>
<child>
- <object class="GtkImage">
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="spacing">10</property>
+ <property name="valign">center</property>
<property name="visible">True</property>
- <property name="icon-name">face-uncertain-symbolic</property>
- <property name="icon-size">4</property>
- <property name="pixel-size">72</property>
- <style>
- <class name="dim-label"/>
- </style>
- </object>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="label" translatable="yes">No matching messages</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="visible">True</property>
- <attributes>
- <attribute name="weight" value="PANGO_WEIGHT_BOLD"/>
- <attribute name="scale" value="1.3"/>
- </attributes>
- <style>
- <class name="dim-label"/>
- </style>
- </object>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="label" translatable="yes">Check the spelling or try to remove filters</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="visible">True</property>
- <style>
- <class name="dim-label"/>
- </style>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon-name">system-search-symbolic</property>
+ <property name="icon-size">4</property>
+ <property name="pixel-size">72</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">No active search</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="visible">True</property>
+ <attributes>
+ <attribute name="weight" value="PANGO_WEIGHT_BOLD"/>
+ <attribute name="scale" value="1.3"/>
+ </attributes>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">Type to start a search</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="visible">True</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
</object>
+ <packing>
+ <property name="name">empty</property>
+ </packing>
</child>
- </object>
- <packing>
- <property name="name">no-result</property>
- </packing>
- </child>z
- <child>
- <object class="GtkBox">
- <property name="orientation">vertical</property>
- <property name="visible">True</property>
<child>
- <object class="GtkLabel" id="entry_number_label">
- <property name="xalign">0</property>
- <property name="use-markup">True</property>
- <property name="margin-left">17</property>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="spacing">10</property>
+ <property name="valign">center</property>
<property name="visible">True</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon-name">face-uncertain-symbolic</property>
+ <property name="icon-size">4</property>
+ <property name="pixel-size">72</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">No matching messages</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="visible">True</property>
+ <attributes>
+ <attribute name="weight" value="PANGO_WEIGHT_BOLD"/>
+ <attribute name="scale" value="1.3"/>
+ </attributes>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">Check the spelling or try to remove filters</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="visible">True</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
</object>
+ <packing>
+ <property name="name">no-result</property>
+ </packing>
</child>
<child>
- <object class="GtkScrolledWindow" id="results_scrolled">
- <property name="hscrollbar-policy">never</property>
- <property name="expand">True</property>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
<property name="visible">True</property>
<child>
- <object class="GtkBox" id="results_box">
- <property name="orientation">vertical</property>
- <property name="spacing">25</property>
- <property name="margin">10</property>
+ <object class="GtkLabel" id="entry_number_label">
+ <property name="xalign">0</property>
+ <property name="use-markup">True</property>
+ <property name="margin-left">17</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="results_scrolled">
+ <property name="hscrollbar-policy">never</property>
+ <property name="expand">True</property>
<property name="visible">True</property>
+ <child>
+ <object class="GtkBox" id="results_box">
+ <property name="orientation">vertical</property>
+ <property name="spacing">25</property>
+ <property name="margin">10</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
</object>
</child>
</object>
+ <packing>
+ <property name="name">results</property>
+ </packing>
</child>
</object>
- <packing>
- <property name="name">results</property>
- </packing>
+ </child>
+ </object>
+ </child>
+ <child type="overlay">
+ <object class="GtkFrame" id="auto_complete_overlay">
+ <property name="visible">True</property>
+ <property name="margin-top">42</property>
+ <property name="margin-left">12</property>
+ <property name="margin-right">12</property>
+ <property name="valign">start</property>
+ <style>
+ <class name="auto-complete"/>
+ </style>
+ <child>
+ <object class="GtkListBox" id="auto_complete_list">
+ <property name="visible">True</property>
+ <property name="selection-mode">browse</property>
+ </object>
</child>
</object>
</child>
diff --git a/main/data/search_autocomplete.ui b/main/data/search_autocomplete.ui
new file mode 100644
index 00000000..94ec5d7f
--- /dev/null
+++ b/main/data/search_autocomplete.ui
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <object class="GtkBox" id="root">
+ <property name="orientation">horizontal</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="DinoUiAvatarImage" id="image">
+ <property name="margin">4</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="height">24</property>
+ <property name="width">24</property>
+ <property name="visible">True</property>
+ <property name="allow_gray">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label">
+ <property name="visible">True</property>
+ <property name="ellipsize">end</property>
+ </object>
+ </child>
+ </object>
+</interface> \ No newline at end of file
diff --git a/main/data/theme.css b/main/data/theme.css
index ce195924..226689b3 100644
--- a/main/data/theme.css
+++ b/main/data/theme.css
@@ -33,28 +33,31 @@ window.dino-main .dino-conversation textview, window.dino-main .dino-conversatio
background: transparent;
}
-window.dino-main .dino-sidebar frame {
+window.dino-main .dino-sidebar > frame {
background: @insensitive_bg_color;
border-left: 1px solid @borders;
border-bottom: 1px solid @borders;
}
-window.dino-main .dino-sidebar frame.collapsed {
+window.dino-main .dino-sidebar > frame.collapsed {
border-bottom: 1px solid @borders;
}
+window.dino-main .dino-sidebar frame.auto-complete {
+ background: @theme_base_color;
+}
+
+window.dino-main .dino-sidebar frame.auto-complete list > row {
+ transition: none;
+}
+
window.dino-main .dino-sidebar textview,
window.dino-main .dino-sidebar textview text {
background-color: transparent;
}
-
window.dino-main .dino-chatinput frame box {
- background: @theme_base_color;
-}
-
-window.dino-main .dino-chatinput frame box:backdrop {
- background: @theme_unfocused_base_color;
+ background: transparent;
}
window.dino-main button.dino-chatinput-button {
diff --git a/main/data/unified_main_content.ui b/main/data/unified_main_content.ui
index d5897b1a..b2f3a891 100644
--- a/main/data/unified_main_content.ui
+++ b/main/data/unified_main_content.ui
@@ -44,13 +44,13 @@
<property name="valign">end</property>
<property name="transition-type">crossfade</property>
<property name="visible">True</property>
+ <property name="margin-end">30</property>
+ <property name="margin-bottom">70</property>
<child>
<object class="GtkButton" id="goto_end_button">
<property name="vexpand">False</property>
<property name="halign">end</property>
<property name="valign">end</property>
- <property name="margin-end">70</property>
- <property name="margin-bottom">100</property>
<property name="visible">True</property>
<style>
<class name="circular"/>
diff --git a/main/src/ui/global_search.vala b/main/src/ui/global_search.vala
index 8bd13e6f..eadf142c 100644
--- a/main/src/ui/global_search.vala
+++ b/main/src/ui/global_search.vala
@@ -7,9 +7,8 @@ using Dino.Entities;
namespace Dino.Ui {
[GtkTemplate (ui = "/im/dino/Dino/global_search.ui")]
-class GlobalSearch : Box {
+class GlobalSearch : Overlay {
public signal void selected_item(MessageItem item);
-
private StreamInteractor stream_interactor;
private string search = "";
private int loaded_results = -1;
@@ -20,6 +19,8 @@ class GlobalSearch : Box {
[GtkChild] public ScrolledWindow results_scrolled;
[GtkChild] public Box results_box;
[GtkChild] public Stack results_empty_stack;
+ [GtkChild] public Frame auto_complete_overlay;
+ [GtkChild] public ListBox auto_complete_list;
public GlobalSearch init(StreamInteractor stream_interactor) {
this.stream_interactor = stream_interactor;
@@ -27,6 +28,8 @@ class GlobalSearch : Box {
search_entry.search_changed.connect(() => {
set_search(search_entry.text);
});
+ search_entry.notify["text"].connect_after(() => { update_auto_complete(); });
+ search_entry.notify["cursor-position"].connect_after(() => { update_auto_complete(); });
results_scrolled.vadjustment.notify["value"].connect(() => {
if (results_scrolled.vadjustment.upper - (results_scrolled.vadjustment.value + results_scrolled.vadjustment.page_size) < 100) {
@@ -44,9 +47,70 @@ class GlobalSearch : Box {
reloading_mutex.trylock();
reloading_mutex.unlock();
});
+
+ event.connect((event) => {
+ if (auto_complete_overlay.visible) {
+ if (event.type == Gdk.EventType.KEY_PRESS && event.key.keyval == Gdk.Key.Up) {
+ var row = auto_complete_list.get_selected_row();
+ var index = row == null ? -1 : row.get_index() - 1;
+ if (index == -1) index = (int)auto_complete_list.get_children().length() - 1;
+ auto_complete_list.select_row(auto_complete_list.get_row_at_index(index));
+ return true;
+ }
+ if (event.type == Gdk.EventType.KEY_PRESS && event.key.keyval == Gdk.Key.Down) {
+ var row = auto_complete_list.get_selected_row();
+ var index = row == null ? 0 : row.get_index() + 1;
+ if (index == auto_complete_list.get_children().length()) index = 0;
+ auto_complete_list.select_row(auto_complete_list.get_row_at_index(index));
+ return true;
+ }
+ if (event.type == Gdk.EventType.KEY_PRESS && event.key.keyval == Gdk.Key.Tab ||
+ event.type == Gdk.EventType.KEY_RELEASE && event.key.keyval == Gdk.Key.Return) {
+ auto_complete_list.get_selected_row().activate();
+ return true;
+ }
+ }
+ // TODO: Handle cursor movement in results
+ // TODO: Direct all keystrokes to text input
+ return false;
+ });
+
return this;
}
+ private void update_auto_complete() {
+ Gee.List<SearchSuggestion> suggestions = stream_interactor.get_module(SearchProcessor.IDENTITY).suggest_auto_complete(search_entry.text, search_entry.cursor_position);
+ auto_complete_overlay.visible = suggestions.size > 0;
+ if (suggestions.size > 0) {
+ auto_complete_list.@foreach((widget) => auto_complete_list.remove(widget));
+ foreach(SearchSuggestion suggestion in suggestions) {
+ Builder builder = new Builder.from_resource("/im/dino/Dino/search_autocomplete.ui");
+ AvatarImage avatar = (AvatarImage)builder.get_object("image");
+ avatar.set_jid(stream_interactor, suggestion.jid, suggestion.account);
+ Label label = (Label)builder.get_object("label");
+ string display_name = Util.get_display_name(stream_interactor, suggestion.jid, suggestion.account);
+ if (display_name != suggestion.jid.to_string()) {
+ label.set_markup(@"$display_name <span font_weight='light' fgalpha='80%'>$(suggestion.jid)</span>");
+ } else {
+ label.label = display_name;
+ }
+ ListBoxRow row = new ListBoxRow() { visible = true, can_focus = false };
+ row.add((Widget)builder.get_object("root"));
+ row.activate.connect(() => {
+ handle_suggestion(suggestion);
+ });
+ auto_complete_list.add(row);
+ }
+ auto_complete_list.select_row(auto_complete_list.get_row_at_index(0));
+ }
+ }
+
+ private void handle_suggestion(SearchSuggestion suggestion) {
+ search_entry.move_cursor(MovementStep.LOGICAL_POSITIONS, suggestion.start_index - search_entry.cursor_position, false);
+ search_entry.delete_from_cursor(DeleteType.CHARS, suggestion.end_index - suggestion.start_index);
+ search_entry.insert_at_cursor(suggestion.completion + " ");
+ }
+
private void clear_search() {
results_box.@foreach((widget) => { widget.destroy(); });
}