summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Otte <otte@redhat.com>2022-02-14 03:18:36 +0100
committerBenjamin Otte <otte@redhat.com>2023-04-11 13:37:32 +0200
commit74c55de53df2af26bd3dd97ec4ee5dbbf68476a4 (patch)
tree6443cf34f4e99f912e8037985b412381067bab92
parent65a631a56165621cd6df6375337e2c079c71b1f2 (diff)
downloadgtk+-74c55de53df2af26bd3dd97ec4ee5dbbf68476a4.tar.gz
Add GtkSectionModel
Prototyping the interface to be used for sections in listview, so people can review and play with it.
-rw-r--r--gtk/gtk.h1
-rw-r--r--gtk/gtksectionmodel.c182
-rw-r--r--gtk/gtksectionmodel.h72
-rw-r--r--gtk/meson.build2
4 files changed, 257 insertions, 0 deletions
diff --git a/gtk/gtk.h b/gtk/gtk.h
index a3973437f3..009c5353b7 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -225,6 +225,7 @@
#include <gtk/gtkscrolledwindow.h>
#include <gtk/gtksearchbar.h>
#include <gtk/gtksearchentry.h>
+#include <gtk/gtksectionmodel.h>
#include <gtk/gtkselectionfiltermodel.h>
#include <gtk/gtkselectionmodel.h>
#include <gtk/gtkseparator.h>
diff --git a/gtk/gtksectionmodel.c b/gtk/gtksectionmodel.c
new file mode 100644
index 0000000000..a3763a1c64
--- /dev/null
+++ b/gtk/gtksectionmodel.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright © 2022 Benjamin Otte
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte@gnome.org>
+ */
+
+#include "config.h"
+
+#include "gtksectionmodel.h"
+
+#include "gtkmarshalers.h"
+
+/**
+ * GtkSectionModel:
+ *
+ * `GtkSectionModel` is an interface that adds support for section to list models.
+ *
+ * This support is then used by widgets using list models to be able to group their
+ * items into sections.
+ *
+ * Many GTK list models support sections inherently, or they pass through the sections
+ * of a model they are wrapping.
+ *
+ * A `GtkSectionModel` groups successive items into so-called sections. List widgets
+ * like `GtkListView` then allow displaying section headers for these sections.
+ *
+ * When the section groupings of a model changes, the model will emit the
+ * [signal@Gtk.SectionModel::sections-changed] signal by calling the
+ * [method@Gtk.SectionModel.sections_changed] function. All sections in the given range
+ * now need to be queried again.
+ * The [signal@Gio.ListModel::items-changed] signal has the same effect, all sections in
+ * that range are invalidated, too.
+ *
+ * Since: 4.12
+ */
+
+G_DEFINE_INTERFACE (GtkSectionModel, gtk_section_model, G_TYPE_LIST_MODEL)
+
+enum {
+ SECTIONS_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+gtk_section_model_default_get_section (GtkSectionModel *self,
+ guint position,
+ guint *out_start,
+ guint *out_end)
+{
+ guint n_items = g_list_model_get_n_items (G_LIST_MODEL (self));
+
+ if (position >= n_items)
+ {
+ *out_start = n_items;
+ *out_end = G_MAXUINT;
+ }
+
+ *out_start = 0;
+ *out_end = n_items;
+}
+
+static void
+gtk_section_model_default_init (GtkSectionModelInterface *iface)
+{
+ iface->get_section = gtk_section_model_default_get_section;
+
+ /**
+ * GtkSectionModel::sections-changed
+ * @model: a `GtkSectionModel`
+ * @position: The first item that may have changed
+ * @n_items: number of items with changes
+ *
+ * Emitted when the start-of-section state of some of the items in @model changes.
+ *
+ * Note that this signal does not specify the new section state of the
+ * items, they need to be queried manually. It is also not necessary for
+ * a model to change the section state of any of the items in the section
+ * model, though it would be rather useless to emit such a signal.
+ *
+ * The [signal@Gio.ListModel::items-changed] implies the effect of the
+ * [signal@Gtk.SectionModel::section-changed] signal for all the items
+ * it covers.
+ *
+ * Since: 4.12
+ */
+ signals[SECTIONS_CHANGED] =
+ g_signal_new ("sections-changed",
+ GTK_TYPE_SECTION_MODEL,
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ _gtk_marshal_VOID__UINT_UINT,
+ G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+ g_signal_set_va_marshaller (signals[SECTIONS_CHANGED],
+ GTK_TYPE_SECTION_MODEL,
+ _gtk_marshal_VOID__UINT_UINTv);
+}
+
+/**
+ * gtk_section_model_get_section:
+ * @self: a `GtkSectionModel`
+ * @position: the position of the item to query
+ * @out_start: (out caller-allocates): the position of the first
+ * item in the section
+ * @out_end: (out caller-allocates): the position of the first
+ * item not part of the section anymore.
+ *
+ * Query the section that covers the given position. The number of
+ * items in the section can be computed by `out_end - out_start`.
+ *
+ * If the position is larger than the number of items, a single
+ * range from n_items to G_MAXUINT will be returned.
+ *
+ * Since: 4.12
+ */
+void
+gtk_section_model_get_section (GtkSectionModel *self,
+ guint position,
+ guint *out_start,
+ guint *out_end)
+{
+ GtkSectionModelInterface *iface;
+
+ g_return_if_fail (GTK_IS_SECTION_MODEL (self));
+ g_return_if_fail (out_start != NULL);
+ g_return_if_fail (out_end != NULL);
+
+ iface = GTK_SECTION_MODEL_GET_IFACE (self);
+ iface->get_section (self, position, out_start, out_end);
+
+ g_warn_if_fail (*out_start < *out_end);
+}
+
+/**
+ * gtk_section_model_section_changed:
+ * @self: a `GtkSectionModel`
+ * @position: the first changed item
+ * @n_items: the number of changed items
+ *
+ * This function emits the [signal@Gtk.SectionModel::section-changed]
+ * signal to notify about changes to sections. It must cover all
+ * positions that used to be a section start or that are now a section
+ * start. It does not have to cover all positions for which the section
+ * has changed.
+ *
+ * The [signal@Gio.ListModel::items-changed] implies the effect of the
+ * [signal@Gtk.SectionModel::section-changed] signal for all the items
+ * it covers.
+ *
+ * It is recommended that when changes to the items cause section changes
+ * in a larger range, that the larger range is included in the emission
+ * of the [signal@Gio.ListModel::items-changed] instead of emitting
+ * two signals.
+ *
+ * Since: 4.12
+ */
+void
+gtk_section_model_sections_changed (GtkSectionModel *self,
+ guint position,
+ guint n_items)
+{
+ g_return_if_fail (GTK_IS_SECTION_MODEL (self));
+ g_return_if_fail (n_items > 0);
+ g_return_if_fail (position + n_items <= g_list_model_get_n_items (G_LIST_MODEL (self)));
+
+ g_signal_emit (self, signals[SECTIONS_CHANGED], 0, position, n_items);
+}
diff --git a/gtk/gtksectionmodel.h b/gtk/gtksectionmodel.h
new file mode 100644
index 0000000000..a242b42c20
--- /dev/null
+++ b/gtk/gtksectionmodel.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright © 2022 Benjamin Otte
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte@gnome.org>
+ */
+
+#pragma once
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gtk/gtktypes.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SECTION_MODEL (gtk_section_model_get_type ())
+
+GDK_AVAILABLE_IN_4_12
+G_DECLARE_INTERFACE (GtkSectionModel, gtk_section_model, GTK, SECTION_MODEL, GListModel)
+
+/**
+ * GtkSectionModelInterface:
+ * @get_section: Return the section that covers the given position. If
+ * the position is outside the number of items, returns a single range from
+ * n_items to G_MAXUINT
+ *
+ * The list of virtual functions for the `GtkSectionModel` interface.
+ * No function must be implemented, but unless `GtkSectionModel::get_section()`
+ * is implemented, the whole model will just be a single section.
+ *
+ * Since: 4.12
+ */
+struct _GtkSectionModelInterface
+{
+ /*< private >*/
+ GTypeInterface g_iface;
+
+ /*< public >*/
+ void (* get_section) (GtkSectionModel *self,
+ guint position,
+ guint *out_start,
+ guint *out_end);
+};
+
+GDK_AVAILABLE_IN_4_12
+void gtk_section_model_get_section (GtkSectionModel *self,
+ guint position,
+ guint *out_start,
+ guint *out_end);
+
+/* for implementations only */
+GDK_AVAILABLE_IN_4_12
+void gtk_section_model_sections_changed (GtkSectionModel *self,
+ guint position,
+ guint n_items);
+
+G_END_DECLS
+
diff --git a/gtk/meson.build b/gtk/meson.build
index b3975fb3fc..1ea9b37e25 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -333,6 +333,7 @@ gtk_public_sources = files([
'gtkscrolledwindow.c',
'gtksearchbar.c',
'gtksearchentry.c',
+ 'gtksectionmodel.c',
'gtkselectionfiltermodel.c',
'gtkselectionmodel.c',
'gtkseparator.c',
@@ -565,6 +566,7 @@ gtk_public_headers = files([
'gtkscrolledwindow.h',
'gtksearchbar.h',
'gtksearchentry.h',
+ 'gtksectionmodel.h',
'gtkselectionfiltermodel.h',
'gtkselectionmodel.h',
'gtkseparator.h',