[gtk/wip/otte/listview: 4/4] demo: Add a listview sections demo




commit 36c7b2fbd66a13f217ad678897dce8ae3fb0b883
Author: Benjamin Otte <otte redhat com>
Date:   Mon Feb 21 06:54:51 2022 +0100

    demo: Add a listview sections demo
    
    Just display the list of objects from GTK, and order them in some
    made-up categories.

 demos/gtk-demo/demo.gresource.xml      |   1 +
 demos/gtk-demo/listview_filebrowser.c  |  62 +++++++---
 demos/gtk-demo/listview_filebrowser.ui |  18 ++-
 demos/gtk-demo/listview_objects.c      | 220 +++++++++++++++++++++++++++++++++
 demos/gtk-demo/meson.build             |   9 +-
 5 files changed, 289 insertions(+), 21 deletions(-)
---
diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml
index f1caed2b8d..5cb524328f 100644
--- a/demos/gtk-demo/demo.gresource.xml
+++ b/demos/gtk-demo/demo.gresource.xml
@@ -305,6 +305,7 @@
     <file>listview_clocks.c</file>
     <file>listview_filebrowser.c</file>
     <file>listview_minesweeper.c</file>
+    <file>listview_objects.c</file>
     <file>listview_settings.c</file>
     <file>listview_ucd.c</file>
     <file>listview_weather.c</file>
diff --git a/demos/gtk-demo/listview_filebrowser.c b/demos/gtk-demo/listview_filebrowser.c
index e73ad41a0d..ce19060bbe 100644
--- a/demos/gtk-demo/listview_filebrowser.c
+++ b/demos/gtk-demo/listview_filebrowser.c
@@ -18,6 +18,8 @@ struct _FileBrowserView
   GObject parent_instance;
 
   GtkListItemFactory *factory;
+  GtkListItemFactory *section_factory;
+  GtkSorter *section_sorter;
   char *icon_name;
   char *title;
   GtkOrientation orientation;
@@ -27,8 +29,10 @@ enum {
   PROP_0,
   PROP_FACTORY,
   PROP_ICON_NAME,
-  PROP_TITLE,
   PROP_ORIENTATION,
+  PROP_SECTION_FACTORY,
+  PROP_SECTION_SORTER,
+  PROP_TITLE,
 
   N_PROPS
 };
@@ -58,14 +62,22 @@ file_browser_view_get_property (GObject    *object,
       g_value_set_string (value, self->icon_name);
       break;
 
-    case PROP_TITLE:
-      g_value_set_string (value, self->title);
-      break;
-
     case PROP_ORIENTATION:
       g_value_set_enum (value, self->orientation);
       break;
 
+    case PROP_SECTION_FACTORY:
+      g_value_set_object (value, self->section_factory);
+      break;
+
+    case PROP_SECTION_SORTER:
+      g_value_set_object (value, self->section_sorter);
+      break;
+
+    case PROP_TITLE:
+      g_value_set_string (value, self->title);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       break;
@@ -91,15 +103,23 @@ file_browser_view_set_property (GObject      *object,
       self->icon_name = g_value_dup_string (value);
       break;
 
+    case PROP_ORIENTATION:
+      self->orientation = g_value_get_enum (value);
+      break;
+
+    case PROP_SECTION_FACTORY:
+      g_set_object (&self->section_factory, g_value_get_object (value));
+      break;
+
+    case PROP_SECTION_SORTER:
+      g_set_object (&self->section_sorter, g_value_get_object (value));
+      break;
+
     case PROP_TITLE:
       g_free (self->title);
       self->title = g_value_dup_string (value);
       break;
 
-    case PROP_ORIENTATION:
-      self->orientation = g_value_get_enum (value);
-      break;
-
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -139,12 +159,6 @@ file_browser_view_class_init (FileBrowserViewClass *klass)
                          "icon to display for selecting this view",
                          NULL,
                          G_PARAM_READWRITE);
-  properties[PROP_TITLE] =
-    g_param_spec_string ("title",
-                         "title",
-                         "title to display for selecting this view",
-                         NULL,
-                         G_PARAM_READWRITE);
   properties[PROP_ORIENTATION] =
     g_param_spec_enum ("orientation",
                        "orientation",
@@ -152,6 +166,24 @@ file_browser_view_class_init (FileBrowserViewClass *klass)
                        GTK_TYPE_ORIENTATION,
                        GTK_ORIENTATION_VERTICAL,
                        G_PARAM_READWRITE);
+  properties[PROP_SECTION_FACTORY] =
+    g_param_spec_object ("section-factory",
+                         "section factory",
+                         "factory to use for sections or NULL",
+                         GTK_TYPE_LIST_ITEM_FACTORY,
+                         G_PARAM_READWRITE);
+  properties[PROP_SECTION_SORTER] =
+    g_param_spec_object ("section-sorter",
+                         "section sorter",
+                         "sorter to split files into sections or NULL",
+                         GTK_TYPE_SORTER,
+                         G_PARAM_READWRITE);
+  properties[PROP_TITLE] =
+    g_param_spec_string ("title",
+                         "title",
+                         "title to display for selecting this view",
+                         NULL,
+                         G_PARAM_READWRITE);
 
   g_object_class_install_properties (gobject_class, N_PROPS, properties);
 }
diff --git a/demos/gtk-demo/listview_filebrowser.ui b/demos/gtk-demo/listview_filebrowser.ui
index 3fe307d702..c205f85de6 100644
--- a/demos/gtk-demo/listview_filebrowser.ui
+++ b/demos/gtk-demo/listview_filebrowser.ui
@@ -167,8 +167,17 @@
   </object>
   <object class="GtkSingleSelection" id="selection_model">
     <property name="model">
-      <object class="GtkDirectoryList" id="dirlist">
-        <property 
name="attributes">standard::name,standard::display-name,standard::icon,standard::size,standard::content-type</property>
+      <object class="GtkSortListModel">
+        <binding name="section-sorter">
+          <lookup name="section-sorter" type="FileBrowserView">
+            <lookup name="selected-item">selected-view</lookup>
+          </lookup>
+        </binding>
+        <property name="model">
+          <object class="GtkDirectoryList" id="dirlist">
+            <property 
name="attributes">standard::name,standard::display-name,standard::icon,standard::size,standard::content-type</property>
+          </object>
+        </property>
       </object>
     </property>
   </object>
@@ -238,6 +247,11 @@
                 <lookup name="selected-item">selected-view</lookup>
               </lookup>
             </binding>
+            <binding name="section-factory">
+              <lookup name="section-factory" type="FileBrowserView">
+                <lookup name="selected-item">selected-view</lookup>
+              </lookup>
+            </binding>
             <binding name="orientation">
               <lookup name="orientation" type="FileBrowserView">
                 <lookup name="selected-item">selected-view</lookup>
diff --git a/demos/gtk-demo/listview_objects.c b/demos/gtk-demo/listview_objects.c
new file mode 100644
index 0000000000..57401a1ae1
--- /dev/null
+++ b/demos/gtk-demo/listview_objects.c
@@ -0,0 +1,220 @@
+/* Lists/Objects in GTK
+ * #Keywords: GtkListItemFactory, GtkSortListModel, GtkStringList
+ *
+ * This demo uses the GtkListView widget to show all the objects in GTK
+ * grouped by their type.
+ *
+ * It shows how to use sections in GtkListView
+ */
+
+#include <gtk/gtk.h>
+
+/* This is the function that creates the GListModel that we need.
+ */
+static GListModel *
+create_object_list (void)
+{
+  GtkStringList *strings;
+  const GType *types;
+  guint i, n;
+
+  /* We use a GtkStringList here, because it requires the smallest amount of
+   * code, not because it's a great fit.
+   */
+  strings = gtk_string_list_new (NULL);
+
+  /* This function is meant for testing, but we use it here to get some data
+   * to operate on
+   */
+  gtk_test_register_all_types ();
+  types = gtk_test_list_all_types (&n);
+
+  for (i = 0; i < n; i++)
+    {
+      /* Add all the names of the object types in GTK */
+      if (g_type_is_a (types[i], G_TYPE_OBJECT))
+        gtk_string_list_append (strings, g_type_name (types[i]));
+    }
+
+  return G_LIST_MODEL (strings);
+}
+
+/* Make a function that returns a section name for all our types.
+ * Do this by adding a few type checks and returning a made up
+ * section name for it.
+ */
+static char *
+get_section (GtkStringObject *object)
+{
+  GType type;
+
+  type = g_type_from_name (gtk_string_object_get_string (object));
+
+  if (g_type_is_a (type, GTK_TYPE_WIDGET))
+    return g_strdup ("Widget");
+  else if (g_type_is_a (type, GTK_TYPE_FILTER))
+    return g_strdup ("Filter");
+  else if (g_type_is_a (type, GTK_TYPE_SORTER))
+    return g_strdup ("Sorter");
+  else if (g_type_is_a (type, G_TYPE_LIST_MODEL))
+    return g_strdup ("ListModel");
+  else
+    return g_strdup ("Zzz..."); /* boring stuff, cleverly sorted at the end */
+}
+
+/* These functions set up the object names
+ */
+static void
+setup_section_listitem_cb (GtkListItemFactory *factory,
+                           GtkListItem        *list_item)
+{
+  GtkWidget *label;
+
+  label = gtk_label_new ("");
+  gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+  gtk_widget_add_css_class (label, "heading");
+  gtk_widget_set_margin_top (label, 4);
+  gtk_widget_set_margin_bottom (label, 4);
+  gtk_list_item_set_child (list_item, label);
+}
+
+/* Here we need to prepare the listitem for displaying its item. We get the
+ * listitem already set up from the previous function, so we can reuse the
+ * GtkImage widget we set up above.
+ * We get the item - which we know is a GAppInfo because it comes out of
+ * the model we set up above, grab its icon and display it.
+ */
+static void
+bind_section_listitem_cb (GtkListItemFactory *factory,
+                          GtkListItem        *list_item)
+{
+  GtkWidget *label;
+  GtkStringObject *item;
+  char *text;
+
+  label = gtk_list_item_get_child (list_item);
+  item = gtk_list_item_get_item (list_item);
+
+  text = get_section (item);
+  gtk_label_set_label (GTK_LABEL (label), text);
+  g_free (text);
+}
+
+static void
+setup_listitem_cb (GtkListItemFactory *factory,
+                   GtkListItem        *list_item)
+{
+  GtkWidget *label;
+
+  label = gtk_label_new ("");
+  gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+  gtk_list_item_set_child (list_item, label);
+}
+
+/* Here we need to prepare the listitem for displaying its item. We get the
+ * listitem already set up from the previous function, so we can reuse the
+ * GtkImage widget we set up above.
+ * We get the item - which we know is a GAppInfo because it comes out of
+ * the model we set up above, grab its icon and display it.
+ */
+static void
+bind_listitem_cb (GtkListItemFactory *factory,
+                  GtkListItem        *list_item)
+{
+  GtkWidget *label;
+  GtkStringObject *item;
+
+  label = gtk_list_item_get_child (list_item);
+  item = gtk_list_item_get_item (list_item);
+
+  gtk_label_set_label (GTK_LABEL (label), gtk_string_object_get_string (item));
+}
+
+/* In more complex code, we would also need functions to unbind and teardown
+ * the listitem, but this is simple code, so the default implementations are
+ * enough. If we had connected signals, this step would have been necessary.
+ *
+ * The GtkSignalListItemFactory documentation contains more information about
+ * this step.
+ */
+
+static GtkWidget *window = NULL;
+
+GtkWidget *
+do_listview_objects (GtkWidget *do_widget)
+{
+  if (window == NULL)
+    {
+      GtkWidget *list, *sw;
+      GListModel *model;
+      GtkListItemFactory *factory;
+      GtkSorter *sorter;
+
+      /* Create a window and set a few defaults */
+      window = gtk_window_new ();
+      gtk_window_set_default_size (GTK_WINDOW (window), 300, 400);
+      gtk_window_set_display (GTK_WINDOW (window),
+                              gtk_widget_get_display (do_widget));
+      gtk_window_set_title (GTK_WINDOW (window), "Objects in GTK");
+      g_object_add_weak_pointer (G_OBJECT (window), (gpointer *) &window);
+
+      /* The GtkListitemFactory is what is used to create GtkListItems
+       * to display the data from the model. So it is absolutely necessary
+       * to create one.
+       * We will use a GtkSignalListItemFactory because it is the simplest
+       * one to use. Different ones are available for different use cases.
+       * The most powerful one is GtkBuilderListItemFactory which uses
+       * GtkBuilder .ui files, so it requires little code.
+       */
+      factory = gtk_signal_list_item_factory_new ();
+      g_signal_connect (factory, "setup", G_CALLBACK (setup_listitem_cb), NULL);
+      g_signal_connect (factory, "bind", G_CALLBACK (bind_listitem_cb), NULL);
+
+      /* And of course we need to set the data model. Here we call the function
+       * we wrote above that gives us the list of applications. Then we set
+       * it on the list widget.
+       * The list will now take items from the model and use the factory
+       * to create as many listitems as it needs to show itself to the user.
+       */
+      model = create_object_list ();
+
+      /* Wrap the model in a sort model that sorts the objects alphabetically.
+       */
+      sorter = GTK_SORTER (gtk_string_sorter_new (gtk_property_expression_new (GTK_TYPE_STRING_OBJECT, NULL, 
"string")));
+      model = G_LIST_MODEL (gtk_sort_list_model_new (model, sorter));
+
+      /* Create a sorter for the sections and tell the sort model about it
+       */
+      sorter = GTK_SORTER (gtk_string_sorter_new (gtk_cclosure_expression_new (G_TYPE_STRING, NULL, 0, NULL, 
G_CALLBACK (get_section), NULL, NULL)));
+      gtk_string_sorter_set_ignore_case (GTK_STRING_SORTER (sorter), FALSE);
+      gtk_sort_list_model_set_section_sorter (GTK_SORT_LIST_MODEL (model), sorter);
+      g_object_unref (sorter);
+
+      /* Create the list widget here.
+       */
+      list = gtk_list_view_new (GTK_SELECTION_MODEL (gtk_single_selection_new (model)), factory);
+
+      /* Set a factory for sections, otherwise the listview won't use sections.
+       */
+      factory = gtk_signal_list_item_factory_new ();
+      g_signal_connect (factory, "setup", G_CALLBACK (setup_section_listitem_cb), NULL);
+      g_signal_connect (factory, "bind", G_CALLBACK (bind_section_listitem_cb), NULL);
+      gtk_list_view_set_section_factory (GTK_LIST_VIEW (list), factory);
+      g_object_unref (factory);
+
+      /* List widgets should always be contained in a GtkScrolledWindow,
+       * because otherwise they might get too large or they might not
+       * be scrollable.
+       */
+      sw = gtk_scrolled_window_new ();
+      gtk_window_set_child (GTK_WINDOW (window), sw);
+      gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), list);
+    }
+
+  if (!gtk_widget_get_visible (window))
+    gtk_widget_show (window);
+  else
+    gtk_window_destroy (GTK_WINDOW (window));
+
+  return window;
+}
diff --git a/demos/gtk-demo/meson.build b/demos/gtk-demo/meson.build
index f594cd59b2..cc18609f77 100644
--- a/demos/gtk-demo/meson.build
+++ b/demos/gtk-demo/meson.build
@@ -18,8 +18,9 @@ demos = files([
   'css_shadows.c',
   'cursors.c',
   'dialog.c',
-  'drawingarea.c',
   'dnd.c',
+  'drawingarea.c',
+  'dropdown.c',
   'editable_cells.c',
   'entry_completion.c',
   'entry_undo.c',
@@ -28,6 +29,7 @@ demos = files([
   'filtermodel.c',
   'fishbowl.c',
   'fixed.c',
+  'flowbox.c',
   'fontrendering.c',
   'frames.c',
   'gears.c',
@@ -46,20 +48,19 @@ demos = files([
   'links.c',
   'listbox.c',
   'listbox_controls.c',
-  'menu.c',
-  'flowbox.c',
   'list_store.c',
   'listview_applauncher.c',
   'listview_clocks.c',
   'listview_colors.c',
   'listview_filebrowser.c',
   'listview_minesweeper.c',
-  'dropdown.c',
+  'listview_objects.c',
   'listview_settings.c',
   'listview_ucd.c',
   'listview_weather.c',
   'listview_words.c',
   'markup.c',
+  'menu.c',
   'overlay.c',
   'overlay_decorative.c',
   'paint.c',


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