[gtk+/icon-browser-fuzzy-search] A quick attampt at using fuzzy search in the icon browser



commit cc58b0e20414357e4b0384704bccec175f186b8d
Author: Matthias Clasen <mclasen redhat com>
Date:   Sat Jun 3 00:20:46 2017 -0400

    A quick attampt at using fuzzy search in the icon browser
    
    This is using the fuzzy search code from libdazzle.

 demos/icon-browser/Makefile.am      |    8 +++-
 demos/icon-browser/iconbrowserwin.c |   97 +++++++++++++++++++++++++++++++++--
 demos/icon-browser/main.c           |   76 +++++++++++++++++++++++++++
 demos/icon-browser/window.ui        |    5 ++-
 4 files changed, 180 insertions(+), 6 deletions(-)
---
diff --git a/demos/icon-browser/Makefile.am b/demos/icon-browser/Makefile.am
index 6de3fe7..1837198 100644
--- a/demos/icon-browser/Makefile.am
+++ b/demos/icon-browser/Makefile.am
@@ -21,6 +21,11 @@ gtk3_icon_browser_SOURCES = \
        iconbrowserapp.c iconbrowserapp.h \
        iconbrowserwin.c iconbrowserwin.h \
        iconstore.c iconstore.h \
+       fuzzy/dzl-fuzzy-index-builder.c fuzzy/dzl-fuzzy-index-builder.h \
+       fuzzy/dzl-fuzzy-index.c fuzzy/dzl-fuzzy-index.h \
+       fuzzy/dzl-fuzzy-util.c fuzzy/dzl-fuzzy-util.h \
+       fuzzy/dzl-fuzzy-index-cursor.c fuzzy/dzl-fuzzy-index-cursor.h \
+       fuzzy/dzl-fuzzy-index-match.c fuzzy/dzl-fuzzy-index-match.h \
        resources.c
 
 BUILT_SOURCES = \
@@ -34,7 +39,8 @@ EXTRA_DIST = \
        menus.ui \
        iconbrowser.gresource.xml \
        window.ui \
-       icon.list
+       icon.list \
+       icon.index
 
 # ------------------- MSVC Build Items ----------------
 MSVCPROJS = gtk3-icon-browser
diff --git a/demos/icon-browser/iconbrowserwin.c b/demos/icon-browser/iconbrowserwin.c
index 7e33622..3062db6 100644
--- a/demos/icon-browser/iconbrowserwin.c
+++ b/demos/icon-browser/iconbrowserwin.c
@@ -3,6 +3,8 @@
 #include "iconbrowserwin.h"
 #include "iconstore.h"
 #include <gtk/gtk.h>
+#include <fuzzy/dzl-fuzzy-index.h>
+#include <fuzzy/dzl-fuzzy-index-match.h>
 
 typedef struct
 {
@@ -32,6 +34,7 @@ struct _IconBrowserWindow
   gboolean symbolic;
   GtkWidget *symbolic_radio;
   GtkTreeModelFilter *filter_model;
+  GtkTreeModelSort *sort_model;
   GtkWidget *details;
 
   GtkListStore *store;
@@ -47,6 +50,9 @@ struct _IconBrowserWindow
   GtkWidget *image4;
   GtkWidget *image5;
   GtkWidget *description;
+
+  DzlFuzzyIndex *index;
+  GHashTable *visible;
 };
 
 struct _IconBrowserWindowClass
@@ -57,6 +63,31 @@ struct _IconBrowserWindowClass
 G_DEFINE_TYPE(IconBrowserWindow, icon_browser_window, GTK_TYPE_APPLICATION_WINDOW);
 
 static void
+query_cb (GObject *object,
+          GAsyncResult *result,
+          gpointer data)
+{
+  IconBrowserWindow *win = data;
+  GListModel *model;
+  GError *error = NULL;
+  int i;
+
+  model = dzl_fuzzy_index_query_finish (win->index, result, &error);
+  g_print ("%d matches found.\n", g_list_model_get_n_items (model));
+
+  g_hash_table_remove_all (win->visible);
+  for (i = 0; i < g_list_model_get_n_items (model); i++)
+    {
+      DzlFuzzyIndexMatch *match = g_list_model_get_item (model, i);
+      GVariant *document = dzl_fuzzy_index_match_get_document (match);
+      char *str = g_variant_dup_string (document, NULL);
+      g_hash_table_insert (win->visible, str, GINT_TO_POINTER (i + 1));
+    }
+  g_object_unref (model);
+  gtk_tree_model_filter_refilter (win->filter_model);
+}
+
+static void
 search_text_changed (GtkEntry *entry, IconBrowserWindow *win)
 {
   const gchar *text;
@@ -66,7 +97,7 @@ search_text_changed (GtkEntry *entry, IconBrowserWindow *win)
   if (text[0] == '\0')
     return;
 
-  gtk_tree_model_filter_refilter (win->filter_model);
+  dzl_fuzzy_index_query_async (win->index, text, 0, NULL, query_cb, win);
 }
 
 static GdkPixbuf *
@@ -301,11 +332,9 @@ icon_visible_func (GtkTreeModel *model,
   gchar *name;
   gint column;
   gboolean search;
-  const gchar *search_text;
   gboolean visible;
 
   search = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (win->search));
-  search_text = gtk_entry_get_text (GTK_ENTRY (win->searchentry));
 
   if (win->symbolic)
     column = ICON_STORE_SYMBOLIC_NAME_COLUMN;
@@ -319,7 +348,7 @@ icon_visible_func (GtkTreeModel *model,
   if (!name)
     visible = FALSE;
   else if (search)
-    visible = strstr (name, search_text) != NULL;
+    visible = g_hash_table_lookup (win->visible, name) != NULL;
   else
     visible = win->current_context != NULL && g_strcmp0 (context, win->current_context->id) == 0;
 
@@ -355,6 +384,8 @@ search_mode_toggled (GObject *searchbar, GParamSpec *pspec, IconBrowserWindow *w
 {
   if (gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (searchbar)))
     gtk_list_box_unselect_all (GTK_LIST_BOX (win->context_list));
+
+  gtk_tree_model_filter_refilter (win->filter_model);
 }
 
 static void
@@ -391,12 +422,56 @@ setup_image_dnd (GtkWidget *image)
   g_signal_connect (parent, "drag-data-get", G_CALLBACK (get_image_data), NULL);
 }
 
+static gint
+sort_func (GtkTreeModel *model,
+           GtkTreeIter  *a,
+           GtkTreeIter  *b,
+           gpointer      data)
+{
+  IconBrowserWindow *win = data;
+  char *aname = NULL;
+  char *bname = NULL;
+  int column;
+  int apos, bpos;
+  gboolean search;
+  int ret;
+
+  search = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (win->search));
+
+  if (win->symbolic)
+    column = ICON_STORE_SYMBOLIC_NAME_COLUMN;
+  else
+    column = ICON_STORE_NAME_COLUMN;
+
+  gtk_tree_model_get (model, a, column, &aname, -1);
+  gtk_tree_model_get (model, b, column, &bname, -1);
+
+  if (!aname || !bname)
+    ret = 0;
+  else if (search)
+    {
+      apos = GPOINTER_TO_INT (g_hash_table_lookup (win->visible, aname));
+      bpos = GPOINTER_TO_INT (g_hash_table_lookup (win->visible, bname));
+
+      ret = apos - bpos;
+    }
+  else
+    ret = strcmp (aname, bname);
+
+  g_free (aname);
+  g_free (bname);
+
+  return ret;
+}
+
 static void
 icon_browser_window_init (IconBrowserWindow *win)
 {
   GtkTargetList *list;
   GtkTargetEntry *targets;
   gint n_targets;
+  GFile *file;
+  GError *error = NULL;
 
   gtk_widget_init_template (GTK_WIDGET (win));
 
@@ -421,6 +496,7 @@ icon_browser_window_init (IconBrowserWindow *win)
   win->contexts = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, context_free);
 
   gtk_tree_model_filter_set_visible_func (win->filter_model, icon_visible_func, win, NULL);
+  gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (win->sort_model), sort_func, win, NULL);
   gtk_window_set_transient_for (GTK_WINDOW (win->details), GTK_WINDOW (win));
 
   g_signal_connect (win->searchbar, "notify::search-mode-enabled",
@@ -429,6 +505,18 @@ icon_browser_window_init (IconBrowserWindow *win)
   symbolic_toggled (GTK_TOGGLE_BUTTON (win->symbolic_radio), win);
 
   populate (win);
+
+  win->index = dzl_fuzzy_index_new ();
+  file = g_file_new_for_path ("icon.index");
+  if (!dzl_fuzzy_index_load_file (win->index, file, NULL, &error))
+    {
+      g_printerr ("Failed to load index: %s\n", error->message);
+      g_error_free (error);
+    }
+
+  g_object_unref (file);
+
+  win->visible = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 }
 
 static void
@@ -441,6 +529,7 @@ icon_browser_window_class_init (IconBrowserWindowClass *class)
 
   gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), IconBrowserWindow, context_list);
   gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), IconBrowserWindow, filter_model);
+  gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), IconBrowserWindow, sort_model);
   gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), IconBrowserWindow, symbolic_radio);
   gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), IconBrowserWindow, details);
 
diff --git a/demos/icon-browser/main.c b/demos/icon-browser/main.c
index 7dd5eee..9bd58c4 100644
--- a/demos/icon-browser/main.c
+++ b/demos/icon-browser/main.c
@@ -1,8 +1,84 @@
+#include <string.h>
 #include <gtk/gtk.h>
 #include <iconbrowserapp.h>
+#include <fuzzy/dzl-fuzzy-index-builder.h>
+
+static void
+build_fuzzy_index (void)
+{
+  DzlFuzzyIndexBuilder *builder;
+  GFile *file;
+  GKeyFile *kf;
+  char *data;
+  gsize length;
+  char **groups;
+  int i;
+  GFile *outfile;
+  GError *error = NULL;
+
+  builder = dzl_fuzzy_index_builder_new ();
+  dzl_fuzzy_index_builder_set_case_sensitive (builder, FALSE);
+
+  file = g_file_new_for_uri ("resource:/org/gtk/iconbrowser/gtk/icon.list");
+  g_file_load_contents (file, NULL, &data, &length, NULL, NULL);
+
+  kf = g_key_file_new ();
+  g_key_file_load_from_data (kf, data, length, G_KEY_FILE_NONE, NULL);
+
+  groups = g_key_file_get_groups (kf, &length);
+  for (i = 0; i < length; i++)
+    {
+      const char *context;
+      char **keys;
+      gsize len;
+      int j;
+
+      context = groups[i];
+
+      keys = g_key_file_get_keys (kf, context, &len, NULL);
+      for (j = 0; j < len; j++)
+        {
+          const char *key = keys[j];
+          char *symbolic;
+
+          if (strcmp (key, "Name") == 0 || strcmp (key, "Description") == 0)
+            continue;
+
+          dzl_fuzzy_index_builder_insert (builder, key, g_variant_new_string (key));
+
+          symbolic = g_strconcat (key, "-symbolic", NULL);
+
+          dzl_fuzzy_index_builder_insert (builder, symbolic, g_variant_new_string (symbolic));
+
+          g_free (symbolic);
+        }
+      g_strfreev (keys);
+    }
+  g_strfreev (groups);
+
+  outfile = g_file_new_for_path ("icon.index");
+
+  if (!dzl_fuzzy_index_builder_write (builder, outfile, G_PRIORITY_DEFAULT, NULL, &error))
+    {
+      g_printerr ("%s\n", error->message);
+      g_error_free (error);
+    }
+  else
+    {
+      g_print ("icon.index written\n");
+    }
+
+  g_object_unref (builder);
+}
 
 int
 main (int argc, char *argv[])
 {
+  if (argc == 2 && strcmp (argv[1], "--generate-index") == 0)
+    {
+      build_fuzzy_index ();
+      return 0;
+    }
+
   return g_application_run (G_APPLICATION (icon_browser_app_new ()), argc, argv);
 }
diff --git a/demos/icon-browser/window.ui b/demos/icon-browser/window.ui
index c4e4b87..0eeb8a0 100644
--- a/demos/icon-browser/window.ui
+++ b/demos/icon-browser/window.ui
@@ -3,8 +3,11 @@
   <!-- interface-requires gtk+ 3.8 -->
   <object class="IconStore" id="store">
   </object>
+  <object class="GtkTreeModelSort" id="sort_model">
+    <property name="model">store</property>
+  </object>
   <object class="GtkTreeModelFilter" id="filter_model">
-    <property name="child_model">store</property>
+    <property name="child_model">sort_model</property>
   </object>
   <template class="IconBrowserWindow" parent="GtkApplicationWindow">
     <property name="title" translatable="yes">Icon Browser</property>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]