[gtk/prop-list: 95/96] wip: search support
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/prop-list: 95/96] wip: search support
- Date: Tue, 24 Dec 2019 20:44:54 +0000 (UTC)
commit ed41a8e2c0407aa1aed8c708aabead0df43602a4
Author: Matthias Clasen <mclasen redhat com>
Date: Tue Dec 24 15:32:59 2019 -0500
wip: search support
Add a filter to GtkListBase, and move the selection
to the first matching item. There is still some open
questions around scrolling and focus.
gtk/gtkcolumnview.c | 19 ++++
gtk/gtkcolumnview.h | 8 ++
gtk/gtklistbase.c | 247 +++++++++++++++++++++++++++++++++++++++++++----
gtk/gtklistbaseprivate.h | 6 ++
4 files changed, 260 insertions(+), 20 deletions(-)
---
diff --git a/gtk/gtkcolumnview.c b/gtk/gtkcolumnview.c
index 1c54b4fd71..7056785ba7 100644
--- a/gtk/gtkcolumnview.c
+++ b/gtk/gtkcolumnview.c
@@ -39,6 +39,7 @@
#include "gtkgesturedrag.h"
#include "gtkeventcontrollermotion.h"
#include "gtkeventcontrollerkey.h"
+#include "gtklistbaseprivate.h"
#include "gtkdnd.h"
/**
@@ -1390,3 +1391,21 @@ gtk_column_view_get_enable_rubberband (GtkColumnView *self)
return gtk_list_view_get_enable_rubberband (self->listview);
}
+void
+gtk_column_view_set_selection_filter (GtkColumnView *self,
+ GtkFilter *filter)
+{
+ gtk_list_base_set_selection_filter (GTK_LIST_BASE (self->listview), filter);
+}
+
+void
+gtk_column_view_select_next_match (GtkColumnView *self)
+{
+ gtk_list_base_select_next_match (GTK_LIST_BASE (self->listview));
+}
+
+void
+gtk_column_view_select_previous_match (GtkColumnView *self)
+{
+ gtk_list_base_select_previous_match (GTK_LIST_BASE (self->listview));
+}
diff --git a/gtk/gtkcolumnview.h b/gtk/gtkcolumnview.h
index 8b38986417..fef7d76ef2 100644
--- a/gtk/gtkcolumnview.h
+++ b/gtk/gtkcolumnview.h
@@ -27,6 +27,7 @@
#include <gtk/gtktypes.h>
#include <gtk/gtksortlistmodel.h>
#include <gtk/gtksorter.h>
+#include <gtk/gtkfilter.h>
G_BEGIN_DECLS
@@ -98,6 +99,13 @@ void gtk_column_view_set_enable_rubberband (GtkColumnView
GDK_AVAILABLE_IN_ALL
gboolean gtk_column_view_get_enable_rubberband (GtkColumnView *self);
+GDK_AVAILABLE_IN_ALL
+void gtk_column_view_set_selection_filter (GtkColumnView *self,
+ GtkFilter *filter);
+GDK_AVAILABLE_IN_ALL
+void gtk_column_view_select_next_match (GtkColumnView *self);
+GDK_AVAILABLE_IN_ALL
+void gtk_column_view_select_previous_match (GtkColumnView *self);
G_END_DECLS
diff --git a/gtk/gtklistbase.c b/gtk/gtklistbase.c
index 0a6140225d..a3071aec65 100644
--- a/gtk/gtklistbase.c
+++ b/gtk/gtklistbase.c
@@ -73,6 +73,8 @@ struct _GtkListBasePrivate
guint autoscroll_id;
double autoscroll_delta_x;
double autoscroll_delta_y;
+
+ GtkFilter *selection_filter;
};
enum
@@ -546,6 +548,8 @@ gtk_list_base_focus (GtkWidget *widget,
}
}
+static void gtk_list_base_clear_selection_filter (GtkListBase *self);
+
static void
gtk_list_base_dispose (GObject *object)
{
@@ -574,6 +578,8 @@ gtk_list_base_dispose (GObject *object)
g_clear_object (&priv->model);
+ gtk_list_base_clear_selection_filter (self);
+
G_OBJECT_CLASS (gtk_list_base_parent_class)->dispose (object);
}
@@ -787,26 +793,18 @@ gtk_list_base_update_focus_tracker (GtkListBase *self)
}
}
-static void
-gtk_list_base_scroll_to_item (GtkWidget *widget,
- const char *action_name,
- GVariant *parameter)
+static gboolean
+gtk_list_base_scroll_to_position (GtkListBase *self,
+ guint pos)
{
- GtkListBase *self = GTK_LIST_BASE (widget);
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
int start, end;
double align_along, align_across;
GtkPackType side_along, side_across;
- guint pos;
-
- if (!g_variant_check_format_string (parameter, "u", FALSE))
- return;
-
- g_variant_get (parameter, "u", &pos);
/* figure out primary orientation and if position is valid */
if (!gtk_list_base_get_allocation_along (GTK_LIST_BASE (self), pos, &start, &end))
- return;
+ return FALSE;
end += start;
gtk_list_base_compute_scroll_align (self,
@@ -817,7 +815,7 @@ gtk_list_base_scroll_to_item (GtkWidget *widget,
/* now do the same thing with the other orientation */
if (!gtk_list_base_get_allocation_across (GTK_LIST_BASE (self), pos, &start, &end))
- return;
+ return FALSE;
end += start;
gtk_list_base_compute_scroll_align (self,
@@ -831,13 +829,32 @@ gtk_list_base_scroll_to_item (GtkWidget *widget,
align_across, side_across,
align_along, side_along);
- /* HACK HACK HACK
- *
- * GTK has no way to track the focused child. But we now that when a listitem
- * gets focus, it calls this action. So we update our focus tracker from here
- * because it's the closest we can get to accurate tracking.
- */
- gtk_list_base_update_focus_tracker (self);
+ return TRUE;
+}
+
+static void
+gtk_list_base_scroll_to_item (GtkWidget *widget,
+ const char *action_name,
+ GVariant *parameter)
+{
+ GtkListBase *self = GTK_LIST_BASE (widget);
+ guint pos;
+
+ if (!g_variant_check_format_string (parameter, "u", FALSE))
+ return;
+
+ g_variant_get (parameter, "u", &pos);
+
+ if (gtk_list_base_scroll_to_position (self, pos))
+ {
+ /* HACK HACK HACK
+ *
+ * GTK has no way to track the focused child. But we know that when a listitem
+ * gets focus, it calls this action. So we update our focus tracker from here
+ * because it's the closest we can get to accurate tracking.
+ */
+ gtk_list_base_update_focus_tracker (self);
+ }
}
static void
@@ -1913,3 +1930,193 @@ gtk_list_base_set_model (GtkListBase *self,
return TRUE;
}
+
+static guint
+find_first_selected (GtkSelectionModel *model)
+{
+ guint i, start, n_items;
+ gboolean selected;
+
+ n_items = 0;
+ for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (model)); i += n_items)
+ {
+ gtk_selection_model_query_range (model, i, &start, &n_items, &selected);
+ if (selected)
+ return i;
+ }
+
+ return GTK_INVALID_LIST_POSITION;
+}
+
+static gboolean
+match_item (GListModel *model,
+ GtkFilter *filter,
+ guint position)
+{
+ gpointer item;
+ gboolean result;
+
+ item = g_list_model_get_item (model, position);
+ result = gtk_filter_match (filter, item);
+ g_object_unref (item);
+
+ return result;
+}
+
+static guint
+find_next_match (GListModel *model,
+ GtkFilter *filter,
+ guint start,
+ gboolean forward)
+{
+ guint i;
+
+ if (start == GTK_INVALID_LIST_POSITION)
+ start = 0;
+
+ if (forward)
+ for (i = start; i < g_list_model_get_n_items (model); i++)
+ {
+ if (match_item (model, filter, i))
+ return i;
+ }
+ else
+ for (i = start; ; i--)
+ {
+ if (match_item (model, filter, i))
+ return i;
+ if (i == 0)
+ break;
+ }
+
+ return GTK_INVALID_LIST_POSITION;
+}
+
+static void
+gtk_list_base_selection_filter_changed_cb (GtkFilter *filter,
+ GtkFilterChange change,
+ GtkListBase *self)
+{
+ GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
+ GtkSelectionModel *model = gtk_list_item_manager_get_model (priv->item_manager);
+
+ if (gtk_filter_get_strictness (priv->selection_filter) == GTK_FILTER_MATCH_NONE)
+ gtk_selection_model_unselect_all (model);
+ else
+ {
+ guint position;
+
+ switch (change)
+ {
+ case GTK_FILTER_CHANGE_DIFFERENT:
+ case GTK_FILTER_CHANGE_LESS_STRICT:
+ position = find_next_match (G_LIST_MODEL (model), priv->selection_filter, 0, TRUE);
+ break;
+
+ case GTK_FILTER_CHANGE_MORE_STRICT:
+ position = find_first_selected (model);
+ if (position == GTK_INVALID_LIST_POSITION)
+ position = 0;
+ position = find_next_match (G_LIST_MODEL (model), priv->selection_filter, position, TRUE);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ gtk_list_base_select_item (self, position, FALSE, FALSE);
+ gtk_list_base_scroll_to_position (self, position);
+ }
+}
+
+static void
+gtk_list_base_clear_selection_filter (GtkListBase *self)
+{
+ GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
+
+ if (priv->selection_filter == NULL)
+ return;
+
+ g_signal_handlers_disconnect_by_func (priv->selection_filter,
+ gtk_list_base_selection_filter_changed_cb,
+ self);
+
+ g_clear_object (&priv->selection_filter);
+}
+
+void
+gtk_list_base_set_selection_filter (GtkListBase *self,
+ GtkFilter *filter)
+{
+ GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
+
+ if (priv->selection_filter == filter)
+ return;
+
+ gtk_list_base_clear_selection_filter (self);
+
+ if (filter)
+ {
+ priv->selection_filter = g_object_ref (filter);
+ g_signal_connect (priv->selection_filter, "changed",
+ G_CALLBACK (gtk_list_base_selection_filter_changed_cb), self);
+ gtk_list_base_selection_filter_changed_cb (priv->selection_filter, GTK_FILTER_CHANGE_DIFFERENT, self);
+ }
+}
+
+GtkFilter *
+gtk_list_base_get_selection_filter (GtkListBase *self)
+{
+ GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
+
+ return priv->selection_filter;
+}
+
+gboolean
+gtk_list_base_select_next_match (GtkListBase *self)
+{
+ GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
+ GtkSelectionModel *model = gtk_list_item_manager_get_model (priv->item_manager);
+ guint position;
+
+ if (!priv->selection_filter)
+ return FALSE;
+
+ position = find_first_selected (model);
+ if (position == GTK_INVALID_LIST_POSITION)
+ return FALSE;
+
+ position = find_next_match (G_LIST_MODEL (model), priv->selection_filter, position + 1, TRUE);
+ if (position == GTK_INVALID_LIST_POSITION)
+ return FALSE;
+
+ gtk_list_base_select_item (self, position, FALSE, FALSE);
+ gtk_list_base_scroll_to_position (self, position);
+
+ return TRUE;
+}
+
+gboolean
+gtk_list_base_select_previous_match (GtkListBase *self)
+{
+ GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
+ GtkSelectionModel *model = gtk_list_item_manager_get_model (priv->item_manager);
+ guint position;
+
+ if (!priv->selection_filter)
+ return FALSE;
+
+ position = find_first_selected (model);
+ if (position == GTK_INVALID_LIST_POSITION ||
+ position == 0)
+ return FALSE;
+
+ position = find_next_match (G_LIST_MODEL (model), priv->selection_filter, position - 1, FALSE);
+ if (position == GTK_INVALID_LIST_POSITION)
+ return FALSE;
+
+ gtk_list_base_select_item (self, position, FALSE, FALSE);
+ gtk_list_base_scroll_to_position (self, position);
+
+ return TRUE;
+}
diff --git a/gtk/gtklistbaseprivate.h b/gtk/gtklistbaseprivate.h
index 73d20151c8..ba42e270da 100644
--- a/gtk/gtklistbaseprivate.h
+++ b/gtk/gtklistbaseprivate.h
@@ -23,6 +23,7 @@
#include "gtklistbase.h"
#include "gtklistitemmanagerprivate.h"
+#include "gtkfilter.h"
#include "gtkprivate.h"
struct _GtkListBase
@@ -102,5 +103,10 @@ gboolean gtk_list_base_grab_focus_on_item (GtkListBase
void gtk_list_base_set_enable_rubberband (GtkListBase *self,
gboolean enable);
gboolean gtk_list_base_get_enable_rubberband (GtkListBase *self);
+void gtk_list_base_set_selection_filter (GtkListBase *self,
+ GtkFilter *filter);
+GtkFilter* gtk_list_base_get_selection_filter (GtkListBase *self);
+gboolean gtk_list_base_select_next_match (GtkListBase *self);
+gboolean gtk_list_base_select_previous_match (GtkListBase *self);
#endif /* __GTK_LIST_BASE_PRIVATE_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]