[gnome-todo/gbsneto/task-model: 5/14] task-list-popover: Add filtering to task list names
- From: Georges Basile Stavracas Neto <gbsneto src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-todo/gbsneto/task-model: 5/14] task-list-popover: Add filtering to task list names
- Date: Wed, 12 Sep 2018 11:55:19 +0000 (UTC)
commit 955546be6ae828ca129e8afc5aa8b96f1fb7399e
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date: Sun Sep 9 15:59:17 2018 -0300
task-list-popover: Add filtering to task list names
This is helpful when one has many tasklists, and wants to
just type the name of it and Enter and be happy.
There are a few quirks with forcus still, but it's certainly
helpful already!
data/ui/task-list-popover.ui | 4 +-
src/gtd-utils.c | 86 ++++++++++++++++++++++++++++++
src/gtd-utils.h | 2 +
src/task-list-view/gtd-task-list-popover.c | 61 ++++++++++++++++++++-
4 files changed, 151 insertions(+), 2 deletions(-)
---
diff --git a/data/ui/task-list-popover.ui b/data/ui/task-list-popover.ui
index 2a2f3f6..1c21981 100644
--- a/data/ui/task-list-popover.ui
+++ b/data/ui/task-list-popover.ui
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtdTaskListPopover" parent="GtkPopover">
+ <signal name="closed" handler="on_popover_closed_cb" object="GtdTaskListPopover" swapped="no" />
<child>
<object class="GtkBox">
<property name="margin">18</property>
@@ -10,7 +11,8 @@
<!-- Search entry -->
<child>
<object class="GtkSearchEntry" id="search_entry">
- <property name="visible">false</property>
+ <signal name="activate" handler="on_search_entry_activated_cb" object="GtdTaskListPopover"
swapped="no" />
+ <signal name="search-changed" handler="on_search_entry_search_changed_cb"
object="GtdTaskListPopover" swapped="no" />
</object>
</child>
diff --git a/src/gtd-utils.c b/src/gtd-utils.c
index 47f3e3b..294355f 100644
--- a/src/gtd-utils.c
+++ b/src/gtd-utils.c
@@ -25,6 +25,92 @@
#include <string.h>
+
+/* Combining diacritical mark?
+ * Basic range: [0x0300,0x036F]
+ * Supplement: [0x1DC0,0x1DFF]
+ * For Symbols: [0x20D0,0x20FF]
+ * Half marks: [0xFE20,0xFE2F]
+ */
+#define IS_CDM_UCS4(c) (((c) >= 0x0300 && (c) <= 0x036F) || \
+ ((c) >= 0x1DC0 && (c) <= 0x1DFF) || \
+ ((c) >= 0x20D0 && (c) <= 0x20FF) || \
+ ((c) >= 0xFE20 && (c) <= 0xFE2F))
+
+#define IS_SOFT_HYPHEN(c) ((c) == 0x00AD)
+
+
+/* Copied from tracker/src/libtracker-fts/tracker-parser-glib.c under the GPL
+ * And then from gnome-shell/src/shell-util.c
+ *
+ * Originally written by Aleksander Morgado <aleksander gnu org>
+ */
+gchar*
+gtd_normalize_casefold_and_unaccent (const gchar *str)
+{
+ g_autofree gchar *normalized = NULL;
+ gchar *tmp;
+ gint i = 0;
+ gint j = 0;
+ gint ilen;
+
+ if (str == NULL)
+ return NULL;
+
+ normalized = g_utf8_normalize (str, -1, G_NORMALIZE_NFKD);
+ tmp = g_utf8_casefold (normalized, -1);
+
+ ilen = strlen (tmp);
+
+ while (i < ilen)
+ {
+ gunichar unichar;
+ gchar *next_utf8;
+ gint utf8_len;
+
+ /* Get next character of the word as UCS4 */
+ unichar = g_utf8_get_char_validated (&tmp[i], -1);
+
+ /* Invalid UTF-8 character or end of original string. */
+ if (unichar == (gunichar) -1 ||
+ unichar == (gunichar) -2)
+ {
+ break;
+ }
+
+ /* Find next UTF-8 character */
+ next_utf8 = g_utf8_next_char (&tmp[i]);
+ utf8_len = next_utf8 - &tmp[i];
+
+ if (IS_CDM_UCS4 (unichar) || IS_SOFT_HYPHEN (unichar))
+ {
+ /* If the given unichar is a combining diacritical mark,
+ * just update the original index, not the output one */
+ i += utf8_len;
+ continue;
+ }
+
+ /* If already found a previous combining
+ * diacritical mark, indexes are different so
+ * need to copy characters. As output and input
+ * buffers may overlap, need to use memmove
+ * instead of memcpy */
+ if (i != j)
+ {
+ memmove (&tmp[j], &tmp[i], utf8_len);
+ }
+
+ /* Update both indexes */
+ i += utf8_len;
+ j += utf8_len;
+ }
+
+ /* Force proper string end */
+ tmp[j] = '\0';
+
+ return tmp;
+}
+
GdkContentFormats*
_gtd_get_content_formats (void)
{
diff --git a/src/gtd-utils.h b/src/gtd-utils.h
index 6e82588..5d60f03 100644
--- a/src/gtd-utils.h
+++ b/src/gtd-utils.h
@@ -24,6 +24,8 @@
G_BEGIN_DECLS
+gchar* gtd_normalize_casefold_and_unaccent (const gchar *str);
+
gchar* gtd_str_replace (const gchar *source,
const gchar *search,
const gchar *replacement);
diff --git a/src/task-list-view/gtd-task-list-popover.c b/src/task-list-view/gtd-task-list-popover.c
index 3ddbf6f..0bf4d82 100644
--- a/src/task-list-view/gtd-task-list-popover.c
+++ b/src/task-list-view/gtd-task-list-popover.c
@@ -20,6 +20,8 @@
#define G_LOG_DOMAIN "GtdTaskListPopover"
+#include "contrib/gtd-list-model-filter.h"
+#include "gtd-debug.h"
#include "gtd-manager.h"
#include "gtd-provider.h"
#include "gtd-task-list.h"
@@ -30,8 +32,11 @@ struct _GtdTaskListPopover
{
GtkPopover parent;
+ GtdListModelFilter *filter_model;
+
GtkSizeGroup *sizegroup;
GtkListBox *listbox;
+ GtkEntry *search_entry;
GtdTaskList *selected_list;
GtdManager *manager;
@@ -73,6 +78,24 @@ set_selected_tasklist (GtdTaskListPopover *self,
* Callbacks
*/
+static gboolean
+filter_listbox_cb (GObject *object,
+ gpointer user_data)
+{
+ GtdTaskListPopover *self;
+ g_autofree gchar *normalized_list_name = NULL;
+ g_autofree gchar *normalized_search_query = NULL;
+ GtdTaskList *list;
+
+ self = (GtdTaskListPopover*) user_data;
+ list = (GtdTaskList*) object;
+
+ normalized_search_query = gtd_normalize_casefold_and_unaccent (gtk_entry_get_text (self->search_entry));
+ normalized_list_name = gtd_normalize_casefold_and_unaccent (gtd_task_list_get_name (list));
+
+ return g_strrstr (normalized_list_name, normalized_search_query) != NULL;
+}
+
static GtkWidget*
create_list_row_cb (gpointer item,
gpointer data)
@@ -145,6 +168,32 @@ on_listbox_row_activated_cb (GtkListBox *listbox,
set_selected_tasklist (self, list);
gtk_popover_popdown (GTK_POPOVER (self));
+ gtk_entry_set_text (self->search_entry, "");
+}
+
+static void
+on_popover_closed_cb (GtkPopover *popover,
+ GtdTaskListPopover *self)
+{
+ gtk_entry_set_text (self->search_entry, "");
+}
+
+static void
+on_search_entry_activated_cb (GtkEntry *entry,
+ GtdTaskListPopover *self)
+{
+ g_autoptr (GtdTaskList) list = g_list_model_get_item (G_LIST_MODEL (self->filter_model), 0);
+
+ set_selected_tasklist (self, list);
+ gtk_popover_popdown (GTK_POPOVER (self));
+ gtk_entry_set_text (self->search_entry, "");
+}
+
+static void
+on_search_entry_search_changed_cb (GtkEntry *search_entry,
+ GtdTaskListPopover *self)
+{
+ gtd_list_model_filter_invalidate (self->filter_model);
}
@@ -179,9 +228,13 @@ gtd_task_list_popover_class_init (GtdTaskListPopoverClass *klass)
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/todo/ui/task-list-popover.ui");
gtk_widget_class_bind_template_child (widget_class, GtdTaskListPopover, listbox);
+ gtk_widget_class_bind_template_child (widget_class, GtdTaskListPopover, search_entry);
gtk_widget_class_bind_template_child (widget_class, GtdTaskListPopover, sizegroup);
gtk_widget_class_bind_template_callback (widget_class, on_listbox_row_activated_cb);
+ gtk_widget_class_bind_template_callback (widget_class, on_popover_closed_cb);
+ gtk_widget_class_bind_template_callback (widget_class, on_search_entry_activated_cb);
+ gtk_widget_class_bind_template_callback (widget_class, on_search_entry_search_changed_cb);
}
static void
@@ -189,10 +242,16 @@ gtd_task_list_popover_init (GtdTaskListPopover *self)
{
GtdManager *manager = gtd_manager_get_default ();
+ self->filter_model = gtd_list_model_filter_new (gtd_manager_get_task_lists_model (manager));
+ gtd_list_model_filter_set_filter_func (self->filter_model,
+ filter_listbox_cb,
+ self,
+ NULL);
+
gtk_widget_init_template (GTK_WIDGET (self));
gtk_list_box_bind_model (self->listbox,
- gtd_manager_get_task_lists_model (manager),
+ G_LIST_MODEL (self->filter_model),
create_list_row_cb,
self,
NULL);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]