diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index 15bdac5bc3..b02457dca8 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -422,6 +422,7 @@ gtk_selection_model_get_type GtkNoSelection gtk_no_selection_new gtk_no_selection_get_model +gtk_no_selection_set_model gtk_no_selection_get_type @@ -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 gtk_multi_selection_get_type diff --git a/gtk/gtkmultiselection.c b/gtk/gtkmultiselection.c index ad6d3857de..4dd93b5234 100644 --- a/gtk/gtkmultiselection.c +++ b/gtk/gtkmultiselection.c @@ -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]); +} diff --git a/gtk/gtkmultiselection.h b/gtk/gtkmultiselection.h index 92074683c0..69c8a69028 100644 --- a/gtk/gtkmultiselection.h +++ b/gtk/gtkmultiselection.h @@ -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 diff --git a/gtk/gtknoselection.c b/gtk/gtknoselection.c index 652b148735..8027d003b2 100644 --- a/gtk/gtknoselection.c +++ b/gtk/gtknoselection.c @@ -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]); +} diff --git a/gtk/gtknoselection.h b/gtk/gtknoselection.h index e6341df409..09f027c9d9 100644 --- a/gtk/gtknoselection.h +++ b/gtk/gtknoselection.h @@ -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 diff --git a/gtk/gtksingleselection.c b/gtk/gtksingleselection.c index 70f0beab27..209f6524e8 100644 --- a/gtk/gtksingleselection.c +++ b/gtk/gtksingleselection.c @@ -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 diff --git a/gtk/gtksingleselection.h b/gtk/gtksingleselection.h index f9bbcae9e9..edfb1fe5c1 100644 --- a/gtk/gtksingleselection.h +++ b/gtk/gtksingleselection.h @@ -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 diff --git a/testsuite/gtk/multiselection.c b/testsuite/gtk/multiselection.c index 1fd32bce71..970a6c4a6a 100644 --- a/testsuite/gtk/multiselection.c +++ b/testsuite/gtk/multiselection.c @@ -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 (); } diff --git a/testsuite/gtk/singleselection.c b/testsuite/gtk/singleselection.c index 81b96e90d0..90356345da 100644 --- a/testsuite/gtk/singleselection.c +++ b/testsuite/gtk/singleselection.c @@ -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 (); }