diff options
author | Matthias Clasen <mclasen@redhat.com> | 2019-12-24 15:32:59 -0500 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2020-05-30 22:27:54 -0400 |
commit | d17f224e71d91bf31bef9102397b25f71431f2b6 (patch) | |
tree | afc6008f1df9604fac499a8009dc58e01db75582 | |
parent | b73be6e344191b184e1b4a158104372789d1cba0 (diff) | |
download | gtk+-d17f224e71d91bf31bef9102397b25f71431f2b6.tar.gz |
Add search supportcolumnview-work
Add a filter to GtkListBase, and move the selection
to the first matching item whenever the filter changes.
This is meant to be used with single selection and
a string filter that is hooked up to a search entry.
-rw-r--r-- | docs/reference/gtk/gtk4-sections.txt | 9 | ||||
-rw-r--r-- | gtk/gtkcolumnview.c | 84 | ||||
-rw-r--r-- | gtk/gtkcolumnview.h | 10 | ||||
-rw-r--r-- | gtk/gtkgridview.c | 83 | ||||
-rw-r--r-- | gtk/gtkgridview.h | 11 | ||||
-rw-r--r-- | gtk/gtklistbase.c | 257 | ||||
-rw-r--r-- | gtk/gtklistbaseprivate.h | 6 | ||||
-rw-r--r-- | gtk/gtklistview.c | 84 | ||||
-rw-r--r-- | gtk/gtklistview.h | 11 |
9 files changed, 535 insertions, 20 deletions
diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index 3b51ccde9a..b7db31d99b 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -481,6 +481,9 @@ gtk_list_view_set_single_click_activate gtk_list_view_get_single_click_activate gtk_list_view_set_enable_rubberband gtk_list_view_get_enable_rubberband +gtk_list_view_set_search_filter +gtk_list_view_get_search_filter +gtk_list_view_next_match <SUBSECTION Standard> GTK_LIST_VIEW GTK_LIST_VIEW_CLASS @@ -511,6 +514,9 @@ gtk_column_view_set_single_click_activate gtk_column_view_get_single_click_activate gtk_column_view_set_enable_rubberband gtk_column_view_get_enable_rubberband +gtk_column_view_set_search_filter +gtk_column_view_get_search_filter +gtk_column_view_next_match <SUBSECTION Standard> GTK_COLUMN_VIEW GTK_COLUMN_VIEW_CLASS @@ -565,6 +571,9 @@ gtk_grid_view_set_single_click_activate gtk_grid_view_get_single_click_activate gtk_grid_view_set_enable_rubberband gtk_grid_view_get_enable_rubberband +gtk_grid_view_set_search_filter +gtk_grid_view_get_search_filter +gtk_grid_view_next_match <SUBSECTION Standard> GTK_GRID_VIEW GTK_GRID_VIEW_CLASS diff --git a/gtk/gtkcolumnview.c b/gtk/gtkcolumnview.c index dde08aebf1..c2e976517b 100644 --- a/gtk/gtkcolumnview.c +++ b/gtk/gtkcolumnview.c @@ -40,6 +40,7 @@ #include "gtkeventcontrollermotion.h" #include "gtkdragsource.h" #include "gtkeventcontrollerkey.h" +#include "gtklistbaseprivate.h" /** * SECTION:gtkcolumnview @@ -104,6 +105,7 @@ enum PROP_VSCROLL_POLICY, PROP_SINGLE_CLICK_ACTIVATE, PROP_ENABLE_RUBBERBAND, + PROP_SEARCH_FILTER, N_PROPS }; @@ -399,6 +401,10 @@ gtk_column_view_get_property (GObject *object, g_value_set_boolean (value, gtk_column_view_get_enable_rubberband (self)); break; + case PROP_SEARCH_FILTER: + g_value_set_object (value, gtk_column_view_get_search_filter (self)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -474,6 +480,10 @@ gtk_column_view_set_property (GObject *object, gtk_column_view_set_enable_rubberband (self, g_value_get_boolean (value)); break; + case PROP_SEARCH_FILTER: + gtk_column_view_set_search_filter (self, g_value_get_object (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -582,6 +592,18 @@ gtk_column_view_class_init (GtkColumnViewClass *klass) FALSE, G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); + /** + * GtkColumnView:search-filter: + * + * Filter used for search + */ + properties[PROP_SEARCH_FILTER] = + g_param_spec_object ("search-filter", + P_("Search filter"), + P_("Filter used for searching"), + GTK_TYPE_FILTER, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (gobject_class, N_PROPS, properties); /** @@ -1389,3 +1411,65 @@ gtk_column_view_get_enable_rubberband (GtkColumnView *self) return gtk_list_view_get_enable_rubberband (self->listview); } + +/** + * gtk_column_view_set_search_filter: + * @self: a #GtkColumnView + * @filter: (nullable): the filter to use for search, or %NULL + * + * Sets a search filter. + * + * The selection will be moved to first item matching the + * filter whenever the filter changes. + * + * This can be used with single selection and a string + * filter that is connected to a search entry. + */ +void +gtk_column_view_set_search_filter (GtkColumnView *self, + GtkFilter *filter) +{ + g_return_if_fail (GTK_IS_COLUMN_VIEW (self)); + g_return_if_fail (filter == NULL || GTK_IS_FILTER (filter)); + + if (filter == gtk_list_view_get_search_filter (GTK_LIST_VIEW (self->listview))) + return; + + gtk_list_view_set_search_filter (GTK_LIST_VIEW (self->listview), filter); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SEARCH_FILTER]); +} + +/** + * gtk_column_view_get_search_filter: + * @self: a #GtkColumnView + * + * Gets the search filter that was set with + * gtk_column_view_set_search_filter(). + * + * Returns: (transfer none): The search filter of @self + */ +GtkFilter * +gtk_column_view_get_search_filter (GtkColumnView *self) +{ + g_return_val_if_fail (GTK_IS_COLUMN_VIEW (self), NULL); + + return gtk_list_view_get_search_filter (GTK_LIST_VIEW (self->listview)); +} + +/** + * gtk_column_view_select_next_match: + * @self: a #GtkColumnView + * @forward: whether to move forward or back + * + * Moves the selection to the next item matching the + * search filter set with gtk_column_view_set_search_filter(). + */ +void +gtk_column_view_select_next_match (GtkColumnView *self, + gboolean forward) +{ + g_return_if_fail (GTK_IS_COLUMN_VIEW (self)); + + gtk_list_view_select_next_match (GTK_LIST_VIEW (self->listview), forward); +} diff --git a/gtk/gtkcolumnview.h b/gtk/gtkcolumnview.h index c4ada4a02a..32520b707c 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 @@ -97,6 +98,15 @@ 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_search_filter (GtkColumnView *self, + GtkFilter *filter); +GDK_AVAILABLE_IN_ALL +GtkFilter * gtk_column_view_get_search_filter (GtkColumnView *self); + +GDK_AVAILABLE_IN_ALL +void gtk_column_view_select_next_match (GtkColumnView *self, + gboolean forward); G_END_DECLS diff --git a/gtk/gtkgridview.c b/gtk/gtkgridview.c index 01feba2c16..ad862d2abe 100644 --- a/gtk/gtkgridview.c +++ b/gtk/gtkgridview.c @@ -92,6 +92,7 @@ enum PROP_MODEL, PROP_SINGLE_CLICK_ACTIVATE, PROP_ENABLE_RUBBERBAND, + PROP_SEARCH_FILTER, N_PROPS }; @@ -919,6 +920,10 @@ gtk_grid_view_get_property (GObject *object, g_value_set_boolean (value, gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (self))); break; + case PROP_SEARCH_FILTER: + g_value_set_object (value, gtk_grid_view_get_search_filter (self)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -959,6 +964,10 @@ gtk_grid_view_set_property (GObject *object, gtk_grid_view_set_enable_rubberband (self, g_value_get_boolean (value)); break; + case PROP_SEARCH_FILTER: + gtk_grid_view_set_search_filter (self, g_value_get_object (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -1083,6 +1092,18 @@ gtk_grid_view_class_init (GtkGridViewClass *klass) FALSE, G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); + /** + * GtkGridView:search-filter: + * + * Filter used for search + */ + properties[PROP_SEARCH_FILTER] = + g_param_spec_object ("search-filter", + P_("Search filter"), + P_("Filter used for searching"), + GTK_TYPE_FILTER, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (gobject_class, N_PROPS, properties); /** @@ -1428,3 +1449,65 @@ gtk_grid_view_get_enable_rubberband (GtkGridView *self) return gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (self)); } + +/** + * gtk_grid_view_set_search_filter: + * @self: a #GtkGridView + * @filter: (nullable): the filter to use for search, or %NULL + * + * Sets a search filter. + * + * The selection will be moved to first item matching the + * filter whenever the filter changes. + * + * This can be used with single selection and a string + * filter that is connected to a search entry. + */ +void +gtk_grid_view_set_search_filter (GtkGridView *self, + GtkFilter *filter) +{ + g_return_if_fail (GTK_IS_GRID_VIEW (self)); + g_return_if_fail (filter == NULL || GTK_IS_FILTER (filter)); + + if (filter == gtk_list_base_get_search_filter (GTK_LIST_BASE (self))) + return; + + gtk_list_base_set_search_filter (GTK_LIST_BASE (self), filter); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SEARCH_FILTER]); +} + +/** + * gtk_grid_view_get_search_filter: + * @self: a #GtkGridView + * + * Gets the search filter that was set with + * gtk_grid_view_set_search_filter(). + * + * Returns: (transfer none): The search filter of @self + */ +GtkFilter * +gtk_grid_view_get_search_filter (GtkGridView *self) +{ + g_return_val_if_fail (GTK_IS_GRID_VIEW (self), NULL); + + return gtk_list_base_get_search_filter (GTK_LIST_BASE (self)); +} + +/** + * gtk_grid_view_select_next_match: + * @self: a #GtkGridView + * @forward: whether to move forward or back + * + * Moves the selection to the next item matching the + * search filter set with gtk_grid_view_set_search_filter(). + */ +void +gtk_grid_view_select_next_match (GtkGridView *self, + gboolean forward) +{ + g_return_if_fail (GTK_IS_GRID_VIEW (self)); + + gtk_list_base_select_next_match (GTK_LIST_BASE (self), forward); +} diff --git a/gtk/gtkgridview.h b/gtk/gtkgridview.h index 6fb035ce12..05cf4d4e2c 100644 --- a/gtk/gtkgridview.h +++ b/gtk/gtkgridview.h @@ -25,6 +25,7 @@ #endif #include <gtk/gtklistbase.h> +#include <gtk/gtkfilter.h> G_BEGIN_DECLS @@ -85,6 +86,16 @@ void gtk_grid_view_set_single_click_activate (GtkGridView GDK_AVAILABLE_IN_ALL gboolean gtk_grid_view_get_single_click_activate (GtkGridView *self); +GDK_AVAILABLE_IN_ALL +void gtk_grid_view_set_search_filter (GtkGridView *self, + GtkFilter *filter); +GDK_AVAILABLE_IN_ALL +GtkFilter * gtk_grid_view_get_search_filter (GtkGridView *self); + +GDK_AVAILABLE_IN_ALL +void gtk_grid_view_select_next_match (GtkGridView *self, + gboolean forward); + G_END_DECLS diff --git a/gtk/gtklistbase.c b/gtk/gtklistbase.c index cf0e4e2912..5daf6e83f8 100644 --- a/gtk/gtklistbase.c +++ b/gtk/gtklistbase.c @@ -72,6 +72,8 @@ struct _GtkListBasePrivate guint autoscroll_id; double autoscroll_delta_x; double autoscroll_delta_y; + + GtkFilter *search_filter; }; enum @@ -545,6 +547,8 @@ gtk_list_base_focus (GtkWidget *widget, } } +static void gtk_list_base_clear_search_filter (GtkListBase *self); + static void gtk_list_base_dispose (GObject *object) { @@ -573,6 +577,8 @@ gtk_list_base_dispose (GObject *object) g_clear_object (&priv->model); + gtk_list_base_clear_search_filter (self); + G_OBJECT_CLASS (gtk_list_base_parent_class)->dispose (object); } @@ -786,26 +792,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, @@ -816,7 +814,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, @@ -830,13 +828,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 @@ -885,6 +902,17 @@ gtk_list_base_unselect_all (GtkWidget *widget, gtk_selection_model_unselect_all (selection_model); } +static void +gtk_list_base_next_match_action (GtkWidget *widget, + const char *action_name, + GVariant *parameter) +{ + gboolean forward; + + g_variant_get (parameter, "(b)", &forward); + gtk_list_base_select_next_match (GTK_LIST_BASE (widget), forward); +} + static gboolean gtk_list_base_move_cursor_to_start (GtkWidget *widget, GVariant *args, @@ -1199,6 +1227,11 @@ gtk_list_base_class_init (GtkListBaseClass *klass) NULL, gtk_list_base_unselect_all); + gtk_widget_class_install_action (widget_class, + "list.next-match", + "(b)", + gtk_list_base_next_match_action); + gtk_list_base_add_move_binding (widget_class, GDK_KEY_Up, GTK_ORIENTATION_VERTICAL, -1); gtk_list_base_add_move_binding (widget_class, GDK_KEY_KP_Up, GTK_ORIENTATION_VERTICAL, -1); gtk_list_base_add_move_binding (widget_class, GDK_KEY_Down, GTK_ORIENTATION_VERTICAL, 1); @@ -1221,6 +1254,9 @@ gtk_list_base_class_init (GtkListBaseClass *klass) gtk_widget_class_add_binding_action (widget_class, GDK_KEY_slash, GDK_CONTROL_MASK, "list.select-all", NULL); gtk_widget_class_add_binding_action (widget_class, GDK_KEY_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "list.unselect-all", NULL); gtk_widget_class_add_binding_action (widget_class, GDK_KEY_backslash, GDK_CONTROL_MASK, "list.unselect-all", NULL); + + gtk_widget_class_add_binding_action (widget_class, GDK_KEY_g, GDK_CONTROL_MASK, "list.next-match", "(b)", TRUE); + gtk_widget_class_add_binding_action (widget_class, GDK_KEY_g, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "list.next-match", "(b)", FALSE); } static void gtk_list_base_update_rubberband_selection (GtkListBase *self); @@ -1906,3 +1942,184 @@ 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_search_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 (model == NULL) + return; + + if (gtk_filter_get_strictness (priv->search_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->search_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->search_filter, position, TRUE); + break; + + default: + g_assert_not_reached (); + } + + if (position == GTK_INVALID_LIST_POSITION) + gtk_selection_model_unselect_all (model); + else + gtk_list_base_grab_focus_on_item (self, position, TRUE, FALSE, FALSE); + } +} + +static void +gtk_list_base_clear_search_filter (GtkListBase *self) +{ + GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self); + + if (priv->search_filter == NULL) + return; + + g_signal_handlers_disconnect_by_func (priv->search_filter, + gtk_list_base_search_filter_changed_cb, + self); + + g_clear_object (&priv->search_filter); +} + +void +gtk_list_base_set_search_filter (GtkListBase *self, + GtkFilter *filter) +{ + GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self); + + if (priv->search_filter == filter) + return; + + gtk_list_base_clear_search_filter (self); + + if (filter) + { + priv->search_filter = g_object_ref (filter); + g_signal_connect (priv->search_filter, "changed", + G_CALLBACK (gtk_list_base_search_filter_changed_cb), self); + gtk_list_base_search_filter_changed_cb (priv->search_filter, GTK_FILTER_CHANGE_DIFFERENT, self); + } + + gtk_widget_action_set_enabled (GTK_WIDGET (self), "list.next-match", filter != NULL); +} + +GtkFilter * +gtk_list_base_get_search_filter (GtkListBase *self) +{ + GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self); + + return priv->search_filter; +} + +gboolean +gtk_list_base_select_next_match (GtkListBase *self, + gboolean forward) +{ + GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self); + GtkSelectionModel *model = gtk_list_item_manager_get_model (priv->item_manager); + guint position; + + if (!priv->search_filter) + return FALSE; + + position = find_first_selected (model); + if (position == GTK_INVALID_LIST_POSITION) + return FALSE; + + if (forward) + position = position + 1; + else if (position > 0) + position = position - 1; + else + return FALSE; + + position = find_next_match (G_LIST_MODEL (model), priv->search_filter, position, forward); + if (position == GTK_INVALID_LIST_POSITION) + { + gtk_widget_error_bell (GTK_WIDGET (self)); + return FALSE; + } + + gtk_list_base_grab_focus_on_item (self, position, TRUE, FALSE, FALSE); + return TRUE; +} diff --git a/gtk/gtklistbaseprivate.h b/gtk/gtklistbaseprivate.h index 73d20151c8..3485ea9da6 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_search_filter (GtkListBase *self, + GtkFilter *filter); +GtkFilter * gtk_list_base_get_search_filter (GtkListBase *self); +gboolean gtk_list_base_select_next_match (GtkListBase *self, + gboolean forward); #endif /* __GTK_LIST_BASE_PRIVATE_H__ */ diff --git a/gtk/gtklistview.c b/gtk/gtklistview.c index ea757c1f41..18288c9c06 100644 --- a/gtk/gtklistview.c +++ b/gtk/gtklistview.c @@ -86,6 +86,7 @@ enum PROP_SHOW_SEPARATORS, PROP_SINGLE_CLICK_ACTIVATE, PROP_ENABLE_RUBBERBAND, + PROP_SEARCH_FILTER, N_PROPS }; @@ -648,6 +649,10 @@ gtk_list_view_get_property (GObject *object, g_value_set_boolean (value, gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (self))); break; + case PROP_SEARCH_FILTER: + g_value_set_object (value, gtk_list_view_get_search_filter (self)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -684,6 +689,10 @@ gtk_list_view_set_property (GObject *object, gtk_list_view_set_enable_rubberband (self, g_value_get_boolean (value)); break; + case PROP_SEARCH_FILTER: + gtk_list_view_set_search_filter (self, g_value_get_object (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -792,6 +801,18 @@ gtk_list_view_class_init (GtkListViewClass *klass) FALSE, G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); + /** + * GtkListView:search-filter: + * + * Filter used for search + */ + properties[PROP_SEARCH_FILTER] = + g_param_spec_object ("search-filter", + P_("Search filter"), + P_("Filter used for searching"), + GTK_TYPE_FILTER, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (gobject_class, N_PROPS, properties); /** @@ -1091,3 +1112,66 @@ gtk_list_view_get_enable_rubberband (GtkListView *self) return gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (self)); } + +/** + * gtk_list_view_set_search_filter: + * @self: a #GtkListView + * @filter: (nullable): the filter ot use for search, or %NULL + * + * Sets a search filter. + * + * The selection will be moved to first item matching the + * filter whenever the filter changes. + * + * This can be used with single selection and a string + * filter that is connected to a search entry. + */ +void +gtk_list_view_set_search_filter (GtkListView *self, + GtkFilter *filter) +{ + g_return_if_fail (GTK_IS_LIST_VIEW (self)); + g_return_if_fail (filter == NULL || GTK_IS_FILTER (filter)); + + if (filter == gtk_list_base_get_search_filter (GTK_LIST_BASE (self))) + return; + + gtk_list_base_set_search_filter (GTK_LIST_BASE (self), filter); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SEARCH_FILTER]); +} + +/** + * gtk_list_view_get_search_filter: + * @self: a #GtkListView + * + * Gets the search filter that was set with + * gtk_list_view_set_search_filter(). + * + * Returns: (transfer none): The search filter of @self + */ +GtkFilter * +gtk_list_view_get_search_filter (GtkListView *self) +{ + g_return_val_if_fail (GTK_IS_LIST_VIEW (self), NULL); + + return gtk_list_base_get_search_filter (GTK_LIST_BASE (self)); +} + +/** + * gtk_list_view_select_next_match: + * @self: a #GtkListView + * @forward: whether to move forward or back + * + * Moves the selection to the next item matching the + * search filter set with gtk_list_view_set_search_filter(). + */ +void +gtk_list_view_select_next_match (GtkListView *self, + gboolean forward) +{ + g_return_if_fail (GTK_IS_LIST_VIEW (self)); + + gtk_list_base_select_next_match (GTK_LIST_BASE (self), forward); +} + diff --git a/gtk/gtklistview.h b/gtk/gtklistview.h index afcfb6225f..e2713b894c 100644 --- a/gtk/gtklistview.h +++ b/gtk/gtklistview.h @@ -25,6 +25,7 @@ #endif #include <gtk/gtklistbase.h> +#include <gtk/gtkfilter.h> G_BEGIN_DECLS @@ -81,6 +82,16 @@ void gtk_list_view_set_enable_rubberband (GtkListView GDK_AVAILABLE_IN_ALL gboolean gtk_list_view_get_enable_rubberband (GtkListView *self); +GDK_AVAILABLE_IN_ALL +void gtk_list_view_set_search_filter (GtkListView *self, + GtkFilter *filter); +GDK_AVAILABLE_IN_ALL +GtkFilter * gtk_list_view_get_search_filter (GtkListView *self); + +GDK_AVAILABLE_IN_ALL +void gtk_list_view_select_next_match (GtkListView *self, + gboolean forward); + G_END_DECLS #endif /* __GTK_LIST_VIEW_H__ */ |