Add search support
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.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
Reference in New Issue
Block a user