forked from AuroraMiddleware/gtk
selectionmodels: Add set_model() support
Now that we don't care about item types anymore, we can make the child models settable. We try to retain the selection, even when the model changes.
This commit is contained in:
parent
5080730728
commit
795d3122cc
@ -422,6 +422,7 @@ gtk_selection_model_get_type
|
||||
GtkNoSelection
|
||||
gtk_no_selection_new
|
||||
gtk_no_selection_get_model
|
||||
gtk_no_selection_set_model
|
||||
<SUBSECTION Private>
|
||||
gtk_no_selection_get_type
|
||||
</SECTION>
|
||||
@ -433,6 +434,7 @@ GtkSingleSelection
|
||||
GTK_INVALID_LIST_POSITION
|
||||
gtk_single_selection_new
|
||||
gtk_single_selection_get_model
|
||||
gtk_single_selection_set_model
|
||||
gtk_single_selection_get_selected
|
||||
gtk_single_selection_set_selected
|
||||
gtk_single_selection_get_selected_item
|
||||
@ -450,6 +452,7 @@ gtk_single_selection_get_type
|
||||
GtkMultiSelection
|
||||
gtk_multi_selection_new
|
||||
gtk_multi_selection_get_model
|
||||
gtk_multi_selection_set_model
|
||||
<SUBSECTION Private>
|
||||
gtk_multi_selection_get_type
|
||||
</SECTION>
|
||||
|
@ -70,6 +70,9 @@ gtk_multi_selection_get_n_items (GListModel *list)
|
||||
{
|
||||
GtkMultiSelection *self = GTK_MULTI_SELECTION (list);
|
||||
|
||||
if (self->model == NULL)
|
||||
return 0;
|
||||
|
||||
return g_list_model_get_n_items (self->model);
|
||||
}
|
||||
|
||||
@ -79,6 +82,9 @@ gtk_multi_selection_get_item (GListModel *list,
|
||||
{
|
||||
GtkMultiSelection *self = GTK_MULTI_SELECTION (list);
|
||||
|
||||
if (self->model == NULL)
|
||||
return NULL;
|
||||
|
||||
return g_list_model_get_item (self->model, position);
|
||||
}
|
||||
|
||||
@ -172,7 +178,7 @@ gtk_multi_selection_set_selection (GtkSelectionModel *model,
|
||||
max = gtk_bitset_get_maximum (changes);
|
||||
|
||||
/* sanity check */
|
||||
n_items = g_list_model_get_n_items (self->model);
|
||||
n_items = self->model ? g_list_model_get_n_items (self->model) : 0;
|
||||
if (max >= n_items)
|
||||
{
|
||||
gtk_bitset_remove_range_closed (changes, n_items, max);
|
||||
@ -289,12 +295,7 @@ gtk_multi_selection_set_property (GObject *object,
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_MODEL:
|
||||
self->model = g_value_dup_object (value);
|
||||
g_warn_if_fail (self->model != NULL);
|
||||
g_signal_connect (self->model,
|
||||
"items-changed",
|
||||
G_CALLBACK (gtk_multi_selection_items_changed_cb),
|
||||
self);
|
||||
gtk_multi_selection_set_model (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -355,7 +356,7 @@ gtk_multi_selection_class_init (GtkMultiSelectionClass *klass)
|
||||
P_("Model"),
|
||||
P_("List managed by this selection"),
|
||||
G_TYPE_LIST_MODEL,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
@ -400,3 +401,49 @@ gtk_multi_selection_get_model (GtkMultiSelection *self)
|
||||
|
||||
return self->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_multi_selection_set_model:
|
||||
* @self: a #GtkMultiSelection
|
||||
* @model: (allow-none): A #GListModel to wrap
|
||||
*
|
||||
* Sets the model that @self should wrap. If @model is %NULL, @self
|
||||
* will be empty.
|
||||
**/
|
||||
void
|
||||
gtk_multi_selection_set_model (GtkMultiSelection *self,
|
||||
GListModel *model)
|
||||
{
|
||||
guint n_items_before;
|
||||
|
||||
g_return_if_fail (GTK_IS_MULTI_SELECTION (self));
|
||||
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
|
||||
|
||||
if (self->model == model)
|
||||
return;
|
||||
|
||||
n_items_before = self->model ? g_list_model_get_n_items (self->model) : 0;
|
||||
gtk_multi_selection_clear_model (self);
|
||||
|
||||
if (model)
|
||||
{
|
||||
self->model = g_object_ref (model);
|
||||
g_signal_connect (self->model,
|
||||
"items-changed",
|
||||
G_CALLBACK (gtk_multi_selection_items_changed_cb),
|
||||
self);
|
||||
gtk_multi_selection_items_changed_cb (self->model,
|
||||
0,
|
||||
n_items_before,
|
||||
g_list_model_get_n_items (model),
|
||||
self);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_bitset_remove_all (self->selected);
|
||||
g_hash_table_remove_all (self->items);
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items_before, 0);
|
||||
}
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
|
||||
}
|
||||
|
@ -35,6 +35,9 @@ GListModel * gtk_multi_selection_new (GListModel *mo
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_multi_selection_get_model (GtkMultiSelection *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_multi_selection_set_model (GtkMultiSelection *self,
|
||||
GListModel *model);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
@ -68,15 +68,21 @@ gtk_no_selection_get_n_items (GListModel *list)
|
||||
{
|
||||
GtkNoSelection *self = GTK_NO_SELECTION (list);
|
||||
|
||||
if (self->model == NULL)
|
||||
return 0;
|
||||
|
||||
return g_list_model_get_n_items (self->model);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
gtk_no_selection_get_item (GListModel *list,
|
||||
guint position)
|
||||
guint position)
|
||||
{
|
||||
GtkNoSelection *self = GTK_NO_SELECTION (list);
|
||||
|
||||
if (self->model == NULL)
|
||||
return NULL;
|
||||
|
||||
return g_list_model_get_item (self->model, position);
|
||||
}
|
||||
|
||||
@ -130,9 +136,9 @@ gtk_no_selection_clear_model (GtkNoSelection *self)
|
||||
|
||||
static void
|
||||
gtk_no_selection_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
|
||||
{
|
||||
GtkNoSelection *self = GTK_NO_SELECTION (object);
|
||||
@ -140,10 +146,7 @@ gtk_no_selection_set_property (GObject *object,
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_MODEL:
|
||||
gtk_no_selection_clear_model (self);
|
||||
self->model = g_value_dup_object (value);
|
||||
g_signal_connect_swapped (self->model, "items-changed",
|
||||
G_CALLBACK (g_list_model_items_changed), self);
|
||||
gtk_no_selection_set_model (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -201,7 +204,7 @@ gtk_no_selection_class_init (GtkNoSelectionClass *klass)
|
||||
P_("The model"),
|
||||
P_("The model being managed"),
|
||||
G_TYPE_LIST_MODEL,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
@ -245,3 +248,40 @@ gtk_no_selection_get_model (GtkNoSelection *self)
|
||||
return self->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_no_selection_set_model:
|
||||
* @self: a #GtkNoSelection
|
||||
* @model: (allow-none): A #GListModel to wrap
|
||||
*
|
||||
* Sets the model that @self should wrap. If @model is %NULL, this
|
||||
* model will be empty.
|
||||
**/
|
||||
void
|
||||
gtk_no_selection_set_model (GtkNoSelection *self,
|
||||
GListModel *model)
|
||||
{
|
||||
guint n_items_before;
|
||||
|
||||
g_return_if_fail (GTK_IS_NO_SELECTION (self));
|
||||
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
|
||||
|
||||
if (self->model == model)
|
||||
return;
|
||||
|
||||
n_items_before = self->model ? g_list_model_get_n_items (self->model) : 0;
|
||||
gtk_no_selection_clear_model (self);
|
||||
|
||||
if (model)
|
||||
{
|
||||
self->model = g_object_ref (model);
|
||||
g_signal_connect_swapped (self->model, "items-changed",
|
||||
G_CALLBACK (g_list_model_items_changed), self);
|
||||
}
|
||||
|
||||
g_list_model_items_changed (G_LIST_MODEL (self),
|
||||
0,
|
||||
n_items_before,
|
||||
model ? g_list_model_get_n_items (self->model) : 0);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
|
||||
}
|
||||
|
@ -34,6 +34,9 @@ GtkNoSelection * gtk_no_selection_new (GListModel
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_no_selection_get_model (GtkNoSelection *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_no_selection_set_model (GtkNoSelection *self,
|
||||
GListModel *model);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
@ -80,6 +80,9 @@ gtk_single_selection_get_n_items (GListModel *list)
|
||||
{
|
||||
GtkSingleSelection *self = GTK_SINGLE_SELECTION (list);
|
||||
|
||||
if (self->model == NULL)
|
||||
return 0;
|
||||
|
||||
return g_list_model_get_n_items (self->model);
|
||||
}
|
||||
|
||||
@ -89,6 +92,9 @@ gtk_single_selection_get_item (GListModel *list,
|
||||
{
|
||||
GtkSingleSelection *self = GTK_SINGLE_SELECTION (list);
|
||||
|
||||
if (self->model == NULL)
|
||||
return NULL;
|
||||
|
||||
return g_list_model_get_item (self->model, position);
|
||||
}
|
||||
|
||||
@ -305,12 +311,7 @@ gtk_single_selection_set_property (GObject *object,
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
gtk_single_selection_clear_model (self);
|
||||
self->model = g_value_dup_object (value);
|
||||
g_signal_connect (self->model, "items-changed",
|
||||
G_CALLBACK (gtk_single_selection_items_changed_cb), self);
|
||||
if (self->autoselect)
|
||||
gtk_single_selection_set_selected (self, 0);
|
||||
gtk_single_selection_set_model (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_SELECTED:
|
||||
@ -438,7 +439,7 @@ gtk_single_selection_class_init (GtkSingleSelectionClass *klass)
|
||||
P_("The model"),
|
||||
P_("The model being managed"),
|
||||
G_TYPE_LIST_MODEL,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
@ -484,6 +485,62 @@ gtk_single_selection_get_model (GtkSingleSelection *self)
|
||||
return self->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_single_selection_set_model:
|
||||
* @self: a #GtkSingleSelection
|
||||
* @model: (allow-none): A #GListModel to wrap
|
||||
*
|
||||
* Sets the model that @self should wrap. If @model is %NULL, @self
|
||||
* will be empty.
|
||||
**/
|
||||
void
|
||||
gtk_single_selection_set_model (GtkSingleSelection *self,
|
||||
GListModel *model)
|
||||
{
|
||||
guint n_items_before;
|
||||
|
||||
g_return_if_fail (GTK_IS_SINGLE_SELECTION (self));
|
||||
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
|
||||
|
||||
if (self->model == model)
|
||||
return;
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (self));
|
||||
|
||||
n_items_before = self->model ? g_list_model_get_n_items (self->model) : 0;
|
||||
gtk_single_selection_clear_model (self);
|
||||
|
||||
if (model)
|
||||
{
|
||||
self->model = g_object_ref (model);
|
||||
g_signal_connect (self->model, "items-changed",
|
||||
G_CALLBACK (gtk_single_selection_items_changed_cb), self);
|
||||
gtk_single_selection_items_changed_cb (self->model,
|
||||
0,
|
||||
n_items_before,
|
||||
g_list_model_get_n_items (model),
|
||||
self);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (self->selected != GTK_INVALID_LIST_POSITION)
|
||||
{
|
||||
self->selected = GTK_INVALID_LIST_POSITION;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED]);
|
||||
}
|
||||
if (self->selected_item)
|
||||
{
|
||||
g_clear_object (&self->selected_item);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED_ITEM]);
|
||||
}
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items_before, 0);
|
||||
}
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (self));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_single_selection_get_selected:
|
||||
* @self: a #GtkSingleSelection
|
||||
|
@ -35,22 +35,25 @@ GtkSingleSelection * gtk_single_selection_new (GListModel
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_single_selection_get_model (GtkSingleSelection *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint gtk_single_selection_get_selected (GtkSingleSelection *self);
|
||||
void gtk_single_selection_set_model (GtkSingleSelection *self,
|
||||
GListModel *model);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_single_selection_set_selected (GtkSingleSelection *self,
|
||||
guint position);
|
||||
guint gtk_single_selection_get_selected (GtkSingleSelection *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gpointer gtk_single_selection_get_selected_item (GtkSingleSelection *self);
|
||||
void gtk_single_selection_set_selected (GtkSingleSelection *self,
|
||||
guint position);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_single_selection_get_autoselect (GtkSingleSelection *self);
|
||||
gpointer gtk_single_selection_get_selected_item (GtkSingleSelection *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_single_selection_set_autoselect (GtkSingleSelection *self,
|
||||
gboolean autoselect);
|
||||
gboolean gtk_single_selection_get_autoselect (GtkSingleSelection *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_single_selection_get_can_unselect (GtkSingleSelection *self);
|
||||
void gtk_single_selection_set_autoselect (GtkSingleSelection *self,
|
||||
gboolean autoselect);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_single_selection_set_can_unselect (GtkSingleSelection *self,
|
||||
gboolean can_unselect);
|
||||
gboolean gtk_single_selection_get_can_unselect (GtkSingleSelection *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_single_selection_set_can_unselect (GtkSingleSelection *self,
|
||||
gboolean can_unselect);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
@ -613,6 +613,59 @@ test_selection_filter (void)
|
||||
g_object_unref (store);
|
||||
g_object_unref (selection);
|
||||
}
|
||||
|
||||
static void
|
||||
test_set_model (void)
|
||||
{
|
||||
GtkSelectionModel *selection;
|
||||
GListStore *store;
|
||||
GListModel *m1, *m2;
|
||||
gboolean ret;
|
||||
|
||||
store = new_store (1, 5, 1);
|
||||
m1 = G_LIST_MODEL (store);
|
||||
m2 = G_LIST_MODEL (gtk_slice_list_model_new (m1, 0, 3));
|
||||
selection = new_model (store);
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
ret = gtk_selection_model_select_range (selection, 1, 3, FALSE);
|
||||
g_assert_true (ret);
|
||||
assert_selection (selection, "2 3 4");
|
||||
assert_selection_changes (selection, "1:3");
|
||||
|
||||
/* we retain the selected item across model changes */
|
||||
gtk_multi_selection_set_model (GTK_MULTI_SELECTION (selection), m2);
|
||||
assert_changes (selection, "0-5+3");
|
||||
assert_selection (selection, "2 3");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
gtk_multi_selection_set_model (GTK_MULTI_SELECTION (selection), NULL);
|
||||
assert_changes (selection, "0-3");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
gtk_multi_selection_set_model (GTK_MULTI_SELECTION (selection), m2);
|
||||
assert_changes (selection, "0+3");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
ret = gtk_selection_model_select_all (selection);
|
||||
g_assert_true (ret);
|
||||
assert_selection (selection, "1 2 3");
|
||||
assert_selection_changes (selection, "0:3");
|
||||
|
||||
/* we retain no selected item across model changes */
|
||||
gtk_multi_selection_set_model (GTK_MULTI_SELECTION (selection), m1);
|
||||
assert_changes (selection, "0-3+5");
|
||||
assert_selection (selection, "1 2 3");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
g_object_unref (m2);
|
||||
g_object_unref (m1);
|
||||
g_object_unref (selection);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
@ -633,6 +686,7 @@ main (int argc, char *argv[])
|
||||
g_test_add_func ("/multiselection/readd", test_readd);
|
||||
g_test_add_func ("/multiselection/set_selection", test_set_selection);
|
||||
g_test_add_func ("/multiselection/selection-filter", test_selection_filter);
|
||||
g_test_add_func ("/multiselection/set-model", test_set_model);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
@ -644,6 +644,57 @@ test_query_range (void)
|
||||
g_object_unref (selection);
|
||||
}
|
||||
|
||||
static void
|
||||
test_set_model (void)
|
||||
{
|
||||
GtkSelectionModel *selection;
|
||||
GListStore *store;
|
||||
GListModel *m1, *m2;
|
||||
|
||||
store = new_store (1, 5, 1);
|
||||
m1 = G_LIST_MODEL (store);
|
||||
m2 = G_LIST_MODEL (gtk_slice_list_model_new (m1, 0, 3));
|
||||
selection = new_model (store, TRUE, TRUE);
|
||||
assert_selection (selection, "1");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
/* we retain the selected item across model changes */
|
||||
gtk_single_selection_set_model (GTK_SINGLE_SELECTION (selection), m2);
|
||||
assert_changes (selection, "0-5+3");
|
||||
assert_selection (selection, "1");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
gtk_single_selection_set_model (GTK_SINGLE_SELECTION (selection), NULL);
|
||||
assert_changes (selection, "0-3");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
gtk_single_selection_set_autoselect (GTK_SINGLE_SELECTION (selection), FALSE);
|
||||
gtk_single_selection_set_model (GTK_SINGLE_SELECTION (selection), m2);
|
||||
assert_changes (selection, "0+3");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
/* we retain no selected item across model changes */
|
||||
gtk_single_selection_set_model (GTK_SINGLE_SELECTION (selection), m1);
|
||||
assert_changes (selection, "0-3+5");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
gtk_single_selection_set_selected (GTK_SINGLE_SELECTION (selection), 4);
|
||||
assert_selection (selection, "5");
|
||||
assert_selection_changes (selection, "4:1");
|
||||
|
||||
gtk_single_selection_set_model (GTK_SINGLE_SELECTION (selection), m2);
|
||||
assert_changes (selection, "0-5+3");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
g_object_unref (m2);
|
||||
g_object_unref (m1);
|
||||
g_object_unref (selection);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
@ -662,6 +713,7 @@ main (int argc, char *argv[])
|
||||
g_test_add_func ("/singleselection/persistence", test_persistence);
|
||||
g_test_add_func ("/singleselection/query-range", test_query_range);
|
||||
g_test_add_func ("/singleselection/changes", test_changes);
|
||||
g_test_add_func ("/singleselection/set-model", test_set_model);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user