diff options
author | Benjamin Otte <otte@redhat.com> | 2022-02-18 03:21:48 +0100 |
---|---|---|
committer | Benjamin Otte <otte@redhat.com> | 2023-05-09 17:00:39 +0200 |
commit | bd7ee3f3e1b1f812fb76d5f830300f207094c5c0 (patch) | |
tree | a3d09c633120a7055833736aa9ea08ce7757b116 /gtk | |
parent | ff6c5ad710bcd291cb4470ac25e2a3390b49403c (diff) | |
download | gtk+-bd7ee3f3e1b1f812fb76d5f830300f207094c5c0.tar.gz |
sortlistmodel: Implement GtkSectionModel
The get_section() implementation is a slow and steady implementation
that has to be careful to not screw up when an incremental sort is only
partially sorted.
Diffstat (limited to 'gtk')
-rw-r--r-- | gtk/gtksortlistmodel.c | 219 | ||||
-rw-r--r-- | gtk/gtksortlistmodel.h | 6 |
2 files changed, 210 insertions, 15 deletions
diff --git a/gtk/gtksortlistmodel.c b/gtk/gtksortlistmodel.c index c2feb9e250..77128bda1d 100644 --- a/gtk/gtksortlistmodel.c +++ b/gtk/gtksortlistmodel.c @@ -22,7 +22,9 @@ #include "gtksortlistmodel.h" #include "gtkbitset.h" +#include "gtkmultisorter.h" #include "gtkprivate.h" +#include "gtksectionmodel.h" #include "gtksorterprivate.h" #include "timsort/gtktimsortprivate.h" @@ -73,6 +75,13 @@ * If you run into performance issues with `GtkSortListModel`, * it is strongly recommended that you write your own sorting list * model. + * + * `GtkSortListModel` allows sorting the items into sections. It + * implements `GtkSectionModel` and when [property@Gtk.SortListModel:section-sorter] + * is set, it will sort all items with that sorter and items comparing + * equal with it will be put into the same section. + * The [property@Gtk.SortListModel:sorter] will then be used to sort items + * inside their sections. */ enum { @@ -82,6 +91,7 @@ enum { PROP_MODEL, PROP_N_ITEMS, PROP_PENDING, + PROP_SECTION_SORTER, PROP_SORTER, NUM_PROPERTIES }; @@ -92,6 +102,8 @@ struct _GtkSortListModel GListModel *model; GtkSorter *sorter; + GtkSorter *section_sorter; + GtkSorter *real_sorter; gboolean incremental; GtkTimSort sort; /* ongoing sort operation */ @@ -99,6 +111,7 @@ struct _GtkSortListModel guint n_items; GtkSortKeys *sort_keys; + GtkSortKeys *section_sort_keys; /* we assume they are compatible with the sort keys because they're the first element */ gsize key_size; gpointer keys; GtkBitset *missing_keys; @@ -174,8 +187,79 @@ gtk_sort_list_model_model_init (GListModelInterface *iface) iface->get_item = gtk_sort_list_model_get_item; } +static void +gtk_sort_list_model_ensure_key (GtkSortListModel *self, + guint pos) +{ + gpointer item; + + if (!gtk_bitset_contains (self->missing_keys, pos)) + return; + + item = g_list_model_get_item (self->model, pos); + gtk_sort_keys_init_key (self->sort_keys, item, key_from_pos (self, pos)); + g_object_unref (item); + + gtk_bitset_remove (self->missing_keys, pos); +} + +static void +gtk_sort_list_model_get_section (GtkSectionModel *model, + guint position, + guint *out_start, + guint *out_end) +{ + GtkSortListModel *self = GTK_SORT_LIST_MODEL (model); + gpointer *pos, *start, *end; + + if (position >= self->n_items) + { + *out_start = self->n_items; + *out_end = G_MAXUINT; + return; + } + + if (self->section_sort_keys == NULL) + { + *out_start = 0; + *out_end = self->n_items; + return; + } + + pos = &self->positions[position]; + gtk_sort_list_model_ensure_key (self, pos_from_key (self, *pos)); + + for (start = pos; + start > self->positions; + start--) + { + gtk_sort_list_model_ensure_key (self, pos_from_key (self, start[-1])); + if (gtk_sort_keys_compare (self->section_sort_keys, start[-1], *pos) != GTK_ORDERING_EQUAL) + break; + } + + for (end = pos + 1; + end < &self->positions[self->n_items]; + end++) + { + gtk_sort_list_model_ensure_key (self, pos_from_key (self, *end)); + if (gtk_sort_keys_compare (self->section_sort_keys, *end, *pos) != GTK_ORDERING_EQUAL) + break; + } + + *out_start = start - self->positions; + *out_end = end - self->positions; +} + +static void +gtk_sort_list_model_section_model_init (GtkSectionModelInterface *iface) +{ + iface->get_section = gtk_sort_list_model_get_section; +} + G_DEFINE_TYPE_WITH_CODE (GtkSortListModel, gtk_sort_list_model, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_sort_list_model_model_init)) + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_sort_list_model_model_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_SECTION_MODEL, gtk_sort_list_model_section_model_init)) static gboolean gtk_sort_list_model_is_sorting (GtkSortListModel *self) @@ -379,6 +463,7 @@ gtk_sort_list_model_clear_keys (GtkSortListModel *self) g_clear_pointer (&self->missing_keys, gtk_bitset_unref); g_clear_pointer (&self->keys, g_free); g_clear_pointer (&self->sort_keys, gtk_sort_keys_unref); + g_clear_pointer (&self->section_sort_keys, gtk_sort_keys_unref); self->key_size = 0; } @@ -426,9 +511,9 @@ gtk_sort_list_model_clear_items (GtkSortListModel *self, static gboolean gtk_sort_list_model_should_sort (GtkSortListModel *self) { - return self->sorter != NULL && + return self->real_sorter != NULL && self->model != NULL && - gtk_sorter_get_order (self->sorter) != GTK_SORTER_ORDER_NONE; + gtk_sorter_get_order (self->real_sorter) != GTK_SORTER_ORDER_NONE; } static void @@ -436,9 +521,12 @@ gtk_sort_list_model_create_keys (GtkSortListModel *self) { g_assert (self->keys == NULL); g_assert (self->sort_keys == NULL); + g_assert (self->section_sort_keys == NULL); g_assert (self->key_size == 0); - self->sort_keys = gtk_sorter_get_keys (self->sorter); + self->sort_keys = gtk_sorter_get_keys (self->real_sorter); + if (self->section_sorter) + self->section_sort_keys = gtk_sorter_get_keys (self->section_sorter); self->key_size = gtk_sort_keys_get_key_size (self->sort_keys); self->keys = g_malloc_n (self->n_items, self->key_size); self->missing_keys = gtk_bitset_new_range (0, self->n_items); @@ -646,6 +734,10 @@ gtk_sort_list_model_set_property (GObject *object, gtk_sort_list_model_set_model (self, g_value_get_object (value)); break; + case PROP_SECTION_SORTER: + gtk_sort_list_model_set_section_sorter (self, g_value_get_object (value)); + break; + case PROP_SORTER: gtk_sort_list_model_set_sorter (self, g_value_get_object (value)); break; @@ -686,6 +778,10 @@ gtk_sort_list_model_get_property (GObject *object, g_value_set_uint (value, gtk_sort_list_model_get_pending (self)); break; + case PROP_SECTION_SORTER: + g_value_set_object (value, self->section_sorter); + break; + case PROP_SORTER: g_value_set_object (value, self->sorter); break; @@ -763,13 +859,42 @@ gtk_sort_list_model_clear_model (GtkSortListModel *self) } static void -gtk_sort_list_model_clear_sorter (GtkSortListModel *self) +gtk_sort_list_model_clear_real_sorter (GtkSortListModel *self) { - if (self->sorter == NULL) + if (self->real_sorter == NULL) return; - g_signal_handlers_disconnect_by_func (self->sorter, gtk_sort_list_model_sorter_changed_cb, self); - g_clear_object (&self->sorter); + g_signal_handlers_disconnect_by_func (self->real_sorter, gtk_sort_list_model_sorter_changed_cb, self); + g_clear_object (&self->real_sorter); +} + +static void +gtk_sort_list_model_ensure_real_sorter (GtkSortListModel *self) +{ + if (self->sorter) + { + if (self->section_sorter) + { + GtkMultiSorter *multi; + + multi = gtk_multi_sorter_new (); + self->real_sorter = GTK_SORTER (multi); + gtk_multi_sorter_append (multi, g_object_ref (self->section_sorter)); + gtk_multi_sorter_append (multi, g_object_ref (self->sorter)); + } + else + self->real_sorter = g_object_ref (self->sorter); + } + else + { + if (self->section_sorter) + self->real_sorter = g_object_ref (self->section_sorter); + } + + if (self->real_sorter) + g_signal_connect (self->real_sorter, "changed", G_CALLBACK (gtk_sort_list_model_sorter_changed_cb), self); + + gtk_sort_list_model_sorter_changed_cb (self->real_sorter, GTK_SORTER_CHANGE_DIFFERENT, self); } static void @@ -778,7 +903,9 @@ gtk_sort_list_model_dispose (GObject *object) GtkSortListModel *self = GTK_SORT_LIST_MODEL (object); gtk_sort_list_model_clear_model (self); - gtk_sort_list_model_clear_sorter (self); + gtk_sort_list_model_clear_real_sorter (self); + g_clear_object (&self->section_sorter); + g_clear_object (&self->sorter); G_OBJECT_CLASS (gtk_sort_list_model_parent_class)->dispose (object); }; @@ -847,6 +974,18 @@ gtk_sort_list_model_class_init (GtkSortListModelClass *class) GTK_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY); /** + * GtkSortListModel:section-sorter: (attributes org.gtk.Property.get=gtk_sort_list_model_get_section_sorter org.gtk.Property.set=gtk_sort_list_model_set_section_sorter) + * + * The section sorter for this model, if one is set. + * + * Since: 4.12 + */ + properties[PROP_SECTION_SORTER] = + g_param_spec_object ("section-sorter", NULL, NULL, + GTK_TYPE_SORTER, + GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); + + /** * GtkSortListModel:sorter: (attributes org.gtk.Property.get=gtk_sort_list_model_get_sorter org.gtk.Property.set=gtk_sort_list_model_set_sorter) * * The sorter for this model. @@ -972,15 +1111,16 @@ gtk_sort_list_model_set_sorter (GtkSortListModel *self, g_return_if_fail (GTK_IS_SORT_LIST_MODEL (self)); g_return_if_fail (sorter == NULL || GTK_IS_SORTER (sorter)); - gtk_sort_list_model_clear_sorter (self); + if (self->sorter == sorter) + return; + + gtk_sort_list_model_clear_real_sorter (self); + g_clear_object (&self->sorter); if (sorter) - { - self->sorter = g_object_ref (sorter); - g_signal_connect (sorter, "changed", G_CALLBACK (gtk_sort_list_model_sorter_changed_cb), self); - } + self->sorter = g_object_ref (sorter); - gtk_sort_list_model_sorter_changed_cb (sorter, GTK_SORTER_CHANGE_DIFFERENT, self); + gtk_sort_list_model_ensure_real_sorter (self); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SORTER]); } @@ -1002,6 +1142,55 @@ gtk_sort_list_model_get_sorter (GtkSortListModel *self) } /** + * gtk_sort_list_model_set_section_sorter: (attributes org.gtk.Method.set_property=section-sorter) + * @self: a `GtkSortListModel` + * @sorter: (nullable): the `GtkSorter` to sort @model with + * + * Sets a new section sorter on @self. + * + * Since: 4.12 + */ +void +gtk_sort_list_model_set_section_sorter (GtkSortListModel *self, + GtkSorter *sorter) +{ + g_return_if_fail (GTK_IS_SORT_LIST_MODEL (self)); + g_return_if_fail (sorter == NULL || GTK_IS_SORTER (sorter)); + + if (self->section_sorter == sorter) + return; + + gtk_sort_list_model_clear_real_sorter (self); + g_clear_object (&self->section_sorter); + + if (sorter) + self->section_sorter = g_object_ref (sorter); + + gtk_sort_list_model_ensure_real_sorter (self); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SECTION_SORTER]); +} + +/** + * gtk_sort_list_model_get_section_sorter: (attributes org.gtk.Method.get_property=section-sorter) + * @self: a `GtkSortListModel` + * + * Gets the section sorter that is used to sort items of @self into + * sections. + * + * Returns: (nullable) (transfer none): the sorter of #self + * + * Since: 4.12 + */ +GtkSorter * +gtk_sort_list_model_get_section_sorter (GtkSortListModel *self) +{ + g_return_val_if_fail (GTK_IS_SORT_LIST_MODEL (self), NULL); + + return self->section_sorter; +} + +/** * gtk_sort_list_model_set_incremental: (attributes org.gtk.Method.set_property=incremental) * @self: a `GtkSortListModel` * @incremental: %TRUE to sort incrementally diff --git a/gtk/gtksortlistmodel.h b/gtk/gtksortlistmodel.h index 957f5b0019..64bb0d019c 100644 --- a/gtk/gtksortlistmodel.h +++ b/gtk/gtksortlistmodel.h @@ -45,6 +45,12 @@ void gtk_sort_list_model_set_sorter (GtkSortListMode GDK_AVAILABLE_IN_ALL GtkSorter * gtk_sort_list_model_get_sorter (GtkSortListModel *self); +GDK_AVAILABLE_IN_4_12 +void gtk_sort_list_model_set_section_sorter (GtkSortListModel *self, + GtkSorter *sorter); +GDK_AVAILABLE_IN_4_12 +GtkSorter * gtk_sort_list_model_get_section_sorter (GtkSortListModel *self); + GDK_AVAILABLE_IN_ALL void gtk_sort_list_model_set_model (GtkSortListModel *self, GListModel *model); |