From b2b847f3657eeac02f18797a6757cb9325d9bc19 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 2 Dec 2019 23:43:14 -0500 Subject: [PATCH] Add GtkSorter This is a helper object for sorting, similar to GtkFilter. --- docs/reference/gtk/gtk4-docs.xml | 3 + docs/reference/gtk/gtk4-sections.txt | 20 +++ docs/reference/gtk/gtk4.types.in | 1 + gtk/gtk.h | 1 + gtk/gtksorter.c | 207 +++++++++++++++++++++++++++ gtk/gtksorter.h | 124 ++++++++++++++++ gtk/meson.build | 2 + 7 files changed, 358 insertions(+) create mode 100644 gtk/gtksorter.c create mode 100644 gtk/gtksorter.h diff --git a/docs/reference/gtk/gtk4-docs.xml b/docs/reference/gtk/gtk4-docs.xml index e9b4ab8c06..18451cfae2 100644 --- a/docs/reference/gtk/gtk4-docs.xml +++ b/docs/reference/gtk/gtk4-docs.xml @@ -60,6 +60,9 @@ +
+ +
diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index df839a3ec4..614527aa0f 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -2421,6 +2421,26 @@ GTK_SLICE_LIST_MODEL_GET_CLASS gtk_slice_list_model_get_type +
+gtksorter +GtkSorter +GtkSorter +GtkSorterOrder +GtkSorterChange +gtk_sorter_compare +gtk_sorter_get_order +gtk_sorter_changed + +GTK_SORTER +GTK_IS_SORTER +GTK_TYPE_SORTER +GTK_SORTER_CLASS +GTK_IS_SORTER_CLASS +GTK_SORTER_GET_CLASS + +gtk_sorter_get_type +
+
gtksortlistmodel GtkSortListModel diff --git a/docs/reference/gtk/gtk4.types.in b/docs/reference/gtk/gtk4.types.in index 0ef0a863f5..3b52d11d51 100644 --- a/docs/reference/gtk/gtk4.types.in +++ b/docs/reference/gtk/gtk4.types.in @@ -185,6 +185,7 @@ gtk_size_group_get_type gtk_slice_list_model_get_type gtk_snapshot_get_type gtk_sort_list_model_get_type +gtk_sorter_get_type gtk_spin_button_get_type gtk_spinner_get_type gtk_stack_get_type diff --git a/gtk/gtk.h b/gtk/gtk.h index 3b03bbbc44..4e0a62bf5b 100644 --- a/gtk/gtk.h +++ b/gtk/gtk.h @@ -214,6 +214,7 @@ #include #include #include +#include #include #include #include diff --git a/gtk/gtksorter.c b/gtk/gtksorter.c new file mode 100644 index 0000000000..22ef6c4644 --- /dev/null +++ b/gtk/gtksorter.c @@ -0,0 +1,207 @@ +/* + * Copyright © 2019 Matthias Clasen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Matthias Clasen + */ + +#include "config.h" + +#include "gtksorter.h" + +#include "gtkintl.h" +#include "gtktypebuiltins.h" + +/** + * SECTION:gtksorter + * @title: GtkSorter + * @Short_description: Sorting items + * @See_also: #GtkSortListModel + * + * #GtkSorter is the way to describe sorting criteria. + * Its primary user is #GtkSortListModel. + * + * The model will use a sorter to determine the order in which its items should appear + * by calling gtk_sorter_compare() for pairs of items. + * + * Sorters may change their sorting behavior through their lifetime. In that case, + * they call gtk_sorter_changed(), which will emit the #GtkSorter::changed signal to + * notify that the sort order is no longer valid and should be updated by calling + * gtk_sorter_compare() again. + * + * GTK provides various pre-made sorter implementations for common sorting operations. + * #GtkColumnView has built-in support for sorting lists via the #GtkColumnViewColumn:sorter + * property, where the user can change the sorting by clicking on list headers. + * + * Of course, in particular for large lists, it is also possible to subclass #GtkSorter + * and provide one's own sorter. + */ + +enum { + CHANGED, + LAST_SIGNAL +}; + +G_DEFINE_TYPE (GtkSorter, gtk_sorter, G_TYPE_OBJECT) + +static guint signals[LAST_SIGNAL] = { 0 }; + +static GtkOrdering +gtk_sorter_default_compare (GtkSorter *self, + gpointer item1, + gpointer item2) +{ + g_critical ("Sorter of type '%s' does not implement GtkSorter::compare", G_OBJECT_TYPE_NAME (self)); + + return GTK_ORDERING_EQUAL; +} + +static GtkSorterOrder +gtk_sorter_default_get_order (GtkSorter *self) +{ + return GTK_SORTER_ORDER_PARTIAL; +} + +static void +gtk_sorter_class_init (GtkSorterClass *class) +{ + class->compare = gtk_sorter_default_compare; + class->get_order = gtk_sorter_default_get_order; + + /** + * GtkSorter::changed: + * @self: The #GtkSorter + * @change: how the sorter changed + * + * This signal is emitted whenever the sorter changed. Users of the sorter + * should then update the sort order again via gtk_sorter_compare(). + * + * #GtkSortListModel handles this signal automatically. + * + * Depending on the @change parameter, it may be possible to update + * the sort order without a full resorting. Refer to the #GtkSorterChange + * documentation for details. + */ + signals[CHANGED] = + g_signal_new (I_("changed"), + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__ENUM, + G_TYPE_NONE, 1, + GTK_TYPE_SORTER_CHANGE); + g_signal_set_va_marshaller (signals[CHANGED], + G_TYPE_FROM_CLASS (class), + g_cclosure_marshal_VOID__ENUMv); +} + +static void +gtk_sorter_init (GtkSorter *self) +{ +} + +/** + * gtk_sorter_compare: + * @self: a #GtkSorter + * @item1: (type GObject) (transfer none): first item to compare + * @item2: (type GObject) (transfer none): second item to compare + * + * Compares two given items according to the sort order implemented + * by the sorter. + * + * Sorters implement a partial order: + * * It is reflexive, ie a = a + * * It is antisymmetric, ie if a < b and b < a, then a = b + * * It is transitive, ie given any 3 items with a ≤ b and b ≤ c, + * then a ≤ c + * + * The sorter may signal it conforms to additional constraints + * via the return value of gtk_sorter_get_order(). + * + * Returns: %GTK_ORDERING_EQUAL if @item1 == @item2, + * %GTK_ORDERING_SMALLER if @item1 < @item2, + * %GTK_ORDERING_LARGER if @item1 > @item2 + */ +GtkOrdering +gtk_sorter_compare (GtkSorter *self, + gpointer item1, + gpointer item2) +{ + GtkOrdering result; + + g_return_val_if_fail (GTK_IS_SORTER (self), GTK_ORDERING_EQUAL); + g_return_val_if_fail (item1 && item2, GTK_ORDERING_EQUAL); + + if (item1 == item2) + return GTK_ORDERING_EQUAL; + + result = GTK_SORTER_GET_CLASS (self)->compare (self, item1, item2); + +#ifdef G_ENABLE_DEBUG + if (result < -1 || result > 1) + { + g_critical ("A sorter of type \"%s\" returned %d, which is not a valid GtkOrdering result.\n" + "Did you forget to call gtk_ordering_from_cmpfunc()?", + G_OBJECT_TYPE_NAME (self), (int) result); + } +#endif + + return result; +} + +/** + * gtk_sorter_get_order: + * @self: a #GtkSorter + * + * Gets the order that @self conforms to. See #GtkSorterOrder for details + * of the possible return values. + * + * This function is intended to allow optimizations. + * + * Returns: The order + **/ +GtkSorterOrder +gtk_sorter_get_order (GtkSorter *self) +{ + g_return_val_if_fail (GTK_IS_SORTER (self), GTK_SORTER_ORDER_PARTIAL); + + return GTK_SORTER_GET_CLASS (self)->get_order (self); +} + +/** + * gtk_sorter_changed: + * @self: a #GtkSorter + * @change: How the sorter changed + * + * Emits the #GtkSorter::changed signal to notify all users of the sorter + * that it has changed. Users of the sorter should then update the sort + * order via gtk_sorter_compare(). + * + * Depending on the @change parameter, it may be possible to update + * the sort order without a full resorting. Refer to the #GtkSorterChange + * documentation for details. + * + * This function is intended for implementors of #GtkSorter subclasses and + * should not be called from other functions. + */ +void +gtk_sorter_changed (GtkSorter *self, + GtkSorterChange change) +{ + g_return_if_fail (GTK_IS_SORTER (self)); + + g_signal_emit (self, signals[CHANGED], 0, change); +} diff --git a/gtk/gtksorter.h b/gtk/gtksorter.h new file mode 100644 index 0000000000..5a997a7c84 --- /dev/null +++ b/gtk/gtksorter.h @@ -0,0 +1,124 @@ +/* + * Copyright © 2019 Matthias Clasen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Matthias Clasen + */ + +#ifndef __GTK_SORTER_H__ +#define __GTK_SORTER_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include + +G_BEGIN_DECLS + +/** + * GtkSorterOrder: + * @GTK_SORTER_ORDER_PARTIAL: A partial order. And #GtkOrdering is possible. + * @GTK_SORTER_ORDER_INVALID: An invalid order. gtk_sorter_compare() will + * always return %GTK_ORDERING_INVALID if both items are unequal. + * @GTK_SORTER_ORDER_NONE: No order, all elements are considered equal. + * gtk_sorter_compare() will only return %GTK_ORDERING_EQUAL or + * %GTK_ORDERING_INVALID. + * @GTK_SORTER_ORDER_TOTAL: A total order. gtk_sorter_compare() will only + * return %GTK_ORDERING_EQUAL if an item is compared with itself. Two + * different items will never cause this value to be returned. + * + * Describes the type of order that a #GtkSorter may describe. + */ +typedef enum { + GTK_SORTER_ORDER_PARTIAL, + GTK_SORTER_ORDER_NONE, + GTK_SORTER_ORDER_TOTAL +} GtkSorterOrder; + +/** + * GtkSorterChange: + * @GTK_SORTER_CHANGE_DIFFERENT: The sorter change cannot be described + * by any of the other enumeration values + * @GTK_SORTER_CHANGE_INVERTED: The sort order was inverted. Comparisons + * that returned %GTK_ORDERING_SMALLER now return %GTK_ORDERING_LARGER + * and vice versa. Other comparisons return the same values as before. + * @GTK_SORTER_CHANGE_LESS_STRICT: The sorter is less strict: Comparisons + * may now return %GTK_ORDERING_EQUAL that did not do so before. + * @GTK_SORTER_CHANGE_MORE_STRICT: The sorter is more strict: Comparisons + * that did return %GTK_ORDERING_EQUAL may not do so anymore. + * + * Describes changes in a sorter in more detail and allows users + * to optimize resorting. + */ +typedef enum { + GTK_SORTER_CHANGE_DIFFERENT, + GTK_SORTER_CHANGE_INVERTED, + GTK_SORTER_CHANGE_LESS_STRICT, + GTK_SORTER_CHANGE_MORE_STRICT +} GtkSorterChange; + +#define GTK_TYPE_SORTER (gtk_sorter_get_type ()) + +GDK_AVAILABLE_IN_ALL +G_DECLARE_DERIVABLE_TYPE (GtkSorter, gtk_sorter, GTK, SORTER, GObject) + +/** + * GtkSorterClass + * @compare: Compare two items. See gtk_sorter_compare() for details. + * @get_order: Get the #GtkSorderOrder that applies to the current sorter. + * If unimplemented, it returns %GTK_SORTER_ORDER_PARTIAL. + * + * The virtual table for #GtkSorter. + */ +struct _GtkSorterClass +{ + GObjectClass parent_class; + + GtkOrdering (* compare) (GtkSorter *self, + gpointer item1, + gpointer item2); + + /* optional */ + GtkSorterOrder (* get_order) (GtkSorter *self); + + /* Padding for future expansion */ + void (*_gtk_reserved1) (void); + void (*_gtk_reserved2) (void); + void (*_gtk_reserved3) (void); + void (*_gtk_reserved4) (void); + void (*_gtk_reserved5) (void); + void (*_gtk_reserved6) (void); + void (*_gtk_reserved7) (void); + void (*_gtk_reserved8) (void); +}; + +GDK_AVAILABLE_IN_ALL +GtkOrdering gtk_sorter_compare (GtkSorter *self, + gpointer item1, + gpointer item2); +GDK_AVAILABLE_IN_ALL +GtkSorterOrder gtk_sorter_get_order (GtkSorter *self); + +/* for sorter implementations */ +GDK_AVAILABLE_IN_ALL +void gtk_sorter_changed (GtkSorter *self, + GtkSorterChange change); + +G_END_DECLS + +#endif /* __GTK_SORTER_H__ */ + diff --git a/gtk/meson.build b/gtk/meson.build index 8b073c58e2..c8157ef2b5 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -346,6 +346,7 @@ gtk_public_sources = files([ 'gtksizerequest.c', 'gtkslicelistmodel.c', 'gtksnapshot.c', + 'gtksorter.c', 'gtksortlistmodel.c', 'gtkspinbutton.c', 'gtkspinner.c', @@ -592,6 +593,7 @@ gtk_public_headers = files([ 'gtksizerequest.h', 'gtkslicelistmodel.h', 'gtksnapshot.h', + 'gtksorter.h', 'gtksortlistmodel.h', 'gtkspinbutton.h', 'gtkspinner.h',