[gtk/wip/otte/listview: 13/20] Add GtkSectionModel




commit 980c24eca8ecbaaef9ebe3472c2e928a4ceb45cd
Author: Benjamin Otte <otte redhat com>
Date:   Mon Feb 14 03:18:36 2022 +0100

    Add GtkSectionModel
    
    Prototyping the interface to be used for sections in listview, so people
    can review and play with it.

 gtk/gtk.h             |   1 +
 gtk/gtksectionmodel.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++
 gtk/gtksectionmodel.h |  74 ++++++++++++++++++++
 gtk/meson.build       |   2 +
 4 files changed, 260 insertions(+)
---
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 0d4521e4ac..8afb5b6195 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -213,6 +213,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..e9bb11c68f
--- /dev/null
+++ b/gtk/gtksectionmodel.c
@@ -0,0 +1,183 @@
+/*
+ * 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 "gtkintl.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.8
+ */
+
+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.8
+   */
+  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:
+ * @model: 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.8
+ */
+void
+gtk_section_model_get_section (GtkSectionModel *model,
+                               guint            position,
+                               guint           *out_start,
+                               guint           *out_end)
+{
+  GtkSectionModelInterface *iface;
+
+  g_return_if_fail (GTK_IS_SECTION_MODEL (model));
+  g_return_if_fail (out_start != NULL);
+  g_return_if_fail (out_end != NULL);
+
+  iface = GTK_SECTION_MODEL_GET_IFACE (model);
+  iface->get_section (model, position, out_start, out_end);
+
+  g_warn_if_fail (*out_start < *out_end);
+}
+
+/**
+ * gtk_section_model_section_changed:
+ * @model: 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.8
+ */
+void
+gtk_section_model_sections_changed (GtkSectionModel *model,
+                                    guint            position,
+                                    guint            n_items)
+{
+  g_return_if_fail (GTK_IS_SECTION_MODEL (model));
+  g_return_if_fail (n_items > 0);
+  g_return_if_fail (position + n_items <= g_list_model_get_n_items (G_LIST_MODEL (model)));
+
+  g_signal_emit (model, signals[SECTIONS_CHANGED], 0, position, n_items);
+}
diff --git a/gtk/gtksectionmodel.h b/gtk/gtksectionmodel.h
new file mode 100644
index 0000000000..adf16a5a0b
--- /dev/null
+++ b/gtk/gtksectionmodel.h
@@ -0,0 +1,74 @@
+/*
+ * 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>
+ */
+
+#ifndef __GTK_SECTION_MODEL_H__
+#define __GTK_SECTION_MODEL_H__
+
+#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_8
+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.8
+ */
+struct _GtkSectionModelInterface
+{
+  /*< private >*/
+  GTypeInterface g_iface;
+
+  /*< public >*/
+  void                  (* get_section)                         (GtkSectionModel      *model,
+                                                                 guint                 position,
+                                                                 guint                *out_start,
+                                                                 guint                *out_end);
+};
+
+GDK_AVAILABLE_IN_4_8
+void                    gtk_section_model_get_section           (GtkSectionModel      *self,
+                                                                 guint                 position,
+                                                                 guint                *out_start,
+                                                                 guint                *out_end);
+
+/* for implementations only */
+GDK_AVAILABLE_IN_4_8
+void                    gtk_section_model_sections_changed      (GtkSectionModel      *model,
+                                                                 guint                 position,
+                                                                 guint                 n_items);
+
+G_END_DECLS
+
+#endif /* __GTK_SECTION_MODEL_H__ */
diff --git a/gtk/meson.build b/gtk/meson.build
index 9e01201522..c0d12f94c8 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -354,6 +354,7 @@ gtk_public_sources = files([
   'gtkscrolledwindow.c',
   'gtksearchbar.c',
   'gtksearchentry.c',
+  'gtksectionmodel.c',
   'gtkselectionfiltermodel.c',
   'gtkselectionmodel.c',
   'gtkseparator.c',
@@ -628,6 +629,7 @@ gtk_public_headers = files([
   'gtkscrolledwindow.h',
   'gtksearchbar.h',
   'gtksearchentry.h',
+  'gtksectionmodel.h',
   'gtkselectionfiltermodel.h',
   'gtkselectionmodel.h',
   'gtkseparator.h',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]