gtk-demo: Add alternative settings demo
authorBenjamin Otte <otte@redhat.com>
Mon, 8 May 2023 18:47:39 +0000 (20:47 +0200)
committerBenjamin Otte <otte@redhat.com>
Tue, 9 May 2023 15:03:35 +0000 (17:03 +0200)
This one displays a long list of all settings with sections and allows
filtering the list to quickly search for keys.

demos/gtk-demo/demo.gresource.xml
demos/gtk-demo/listview_settings2.c [new file with mode: 0644]
demos/gtk-demo/listview_settings2.ui [new file with mode: 0644]
demos/gtk-demo/meson.build

index 615d782bfef56242d6be7258f7435f466144da55..ba87e860cef4b3cc1241c6180d63b088ab7a9181 100644 (file)
   <gresource prefix="/listview_settings">
     <file>listview_settings.ui</file>
   </gresource>
+  <gresource prefix="/listview_settings2">
+    <file>listview_settings2.ui</file>
+  </gresource>
   <gresource prefix="/listview_ucd_data/">
     <file>ucdnames.data</file>
   </gresource>
     <file>listview_minesweeper.c</file>
     <file>listview_selections.c</file>
     <file>listview_settings.c</file>
+    <file>listview_settings2.c</file>
     <file>listview_ucd.c</file>
     <file>listview_weather.c</file>
     <file>listview_words.c</file>
diff --git a/demos/gtk-demo/listview_settings2.c b/demos/gtk-demo/listview_settings2.c
new file mode 100644 (file)
index 0000000..4f3e87f
--- /dev/null
@@ -0,0 +1,230 @@
+/* Lists/Settings v2
+ * #Keywords: GtkListHeaderFactory, GtkSectionModel
+ *
+ * This demo shows a settings viewer for GSettings.
+ *
+ * It demonstrates how to implement support for sections with GtkListView.
+ *
+ * It also shows how to quickly flatten a large tree of items into a list
+ * that can be filtered to find the itmes one is looking for.
+ */
+
+#include <gtk/gtk.h>
+
+#include "settings-key.h"
+
+static void
+item_value_changed (GtkEditableLabel  *label,
+                    GParamSpec        *pspec,
+                    GtkColumnViewCell *cell)
+{
+  SettingsKey *self;
+  GSettingsSchemaKey *key;
+  const char *text;
+  const GVariantType *type;
+  GVariant *variant;
+  GError *error = NULL;
+  const char *name;
+  char *value;
+
+  text = gtk_editable_get_text (GTK_EDITABLE (label));
+
+  self = gtk_column_view_cell_get_item (cell);
+  key = settings_key_get_key (self);
+
+  type = g_settings_schema_key_get_value_type (key);
+  name = g_settings_schema_key_get_name (key);
+
+  variant = g_variant_parse (type, text, NULL, NULL, &error);
+  if (!variant)
+    {
+      g_warning ("%s", error->message);
+      g_clear_error (&error);
+      goto revert;
+    }
+
+  if (!g_settings_schema_key_range_check (key, variant))
+    {
+      g_warning ("Not a valid value for %s", name);
+      goto revert;
+    }
+
+  g_settings_set_value (settings_key_get_settings (self), name, variant);
+  g_variant_unref (variant);
+  return;
+
+revert:
+  gtk_widget_error_bell (GTK_WIDGET (label));
+
+  g_object_get (self, "value", &value, NULL);
+  gtk_editable_set_text (GTK_EDITABLE (label), value);
+  g_free (value);
+}
+
+static int
+strvcmp (gconstpointer p1,
+         gconstpointer p2)
+{
+  const char * const *s1 = p1;
+  const char * const *s2 = p2;
+
+  return strcmp (*s1, *s2);
+}
+
+static gpointer
+map_settings_to_keys (gpointer item,
+                      gpointer unused)
+{
+  GSettings *settings = item;
+  GSettingsSchema *schema;
+  GListStore *store;
+  char **keys;
+  guint i;
+
+  g_object_get (settings, "settings-schema", &schema, NULL);
+
+  store = g_list_store_new (SETTINGS_TYPE_KEY);
+
+  keys = g_settings_schema_list_keys (schema);
+
+  for (i = 0; keys[i] != NULL; i++)
+    {
+      GSettingsSchemaKey *almost_there = g_settings_schema_get_key (schema, keys[i]);
+      SettingsKey *finally = settings_key_new (settings, almost_there);
+      g_list_store_append (store, finally);
+      g_object_unref (finally);
+      g_settings_schema_key_unref (almost_there);
+    }
+
+  g_strfreev (keys);
+  g_settings_schema_unref (schema);
+  g_object_unref (settings);
+
+  return store;
+}
+
+static GListModel *
+create_settings_model (gpointer item,
+                       gpointer unused)
+{
+  GSettings *settings = item;
+  char **schemas;
+  GListStore *result;
+  guint i;
+
+  if (settings == NULL)
+    {
+      g_settings_schema_source_list_schemas (g_settings_schema_source_get_default (),
+                                             TRUE,
+                                             &schemas,
+                                             NULL);
+    }
+  else
+    {
+      schemas = g_settings_list_children (settings);
+    }
+
+  if (schemas == NULL || schemas[0] == NULL)
+    {
+      g_free (schemas);
+      return NULL;
+    }
+
+  qsort (schemas, g_strv_length (schemas), sizeof (char *), strvcmp);
+
+  result = g_list_store_new (G_TYPE_SETTINGS);
+  for (i = 0;  schemas[i] != NULL; i++)
+    {
+      GSettings *child;
+
+      if (settings == NULL)
+        child = g_settings_new (schemas[i]);
+      else
+        child = g_settings_get_child (settings, schemas[i]);
+
+      g_list_store_append (result, child);
+      g_object_unref (child);
+    }
+
+  g_strfreev (schemas);
+
+  return G_LIST_MODEL (result);
+}
+
+static void
+search_enabled (GtkSearchEntry *entry)
+{
+  gtk_editable_set_text (GTK_EDITABLE (entry), "");
+}
+
+static void
+stop_search (GtkSearchEntry *entry,
+             gpointer data)
+{
+  gtk_editable_set_text (GTK_EDITABLE (entry), "");
+}
+
+static GtkWidget *window = NULL;
+
+GtkWidget *
+do_listview_settings2 (GtkWidget *do_widget)
+{
+  if (window == NULL)
+    {
+      GtkListView *listview;
+      GListModel *model;
+      GtkTreeListModel *treemodel;
+      GtkNoSelection *selection;
+      GtkBuilderScope *scope;
+      GtkBuilder *builder;
+      GError *error = NULL;
+      GtkFilter *filter;
+
+      g_type_ensure (SETTINGS_TYPE_KEY);
+
+      scope = gtk_builder_cscope_new ();
+      gtk_builder_cscope_add_callback (scope, search_enabled);
+      gtk_builder_cscope_add_callback (scope, stop_search);
+      gtk_builder_cscope_add_callback (scope, settings_key_get_search_string);
+      gtk_builder_cscope_add_callback (scope, item_value_changed);
+
+      builder = gtk_builder_new ();
+      gtk_builder_set_scope (builder, scope);
+      g_object_unref (scope);
+
+      gtk_builder_add_from_resource (builder, "/listview_settings2/listview_settings2.ui", &error);
+      g_assert_no_error (error);
+
+      window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
+      gtk_window_set_display (GTK_WINDOW (window),
+                              gtk_widget_get_display (do_widget));
+      g_object_add_weak_pointer (G_OBJECT (window), (gpointer *) &window);
+
+      listview = GTK_LIST_VIEW (gtk_builder_get_object (builder, "listview"));
+      filter = GTK_FILTER (gtk_builder_get_object (builder, "filter"));
+
+      model = create_settings_model (NULL, NULL);
+      treemodel = gtk_tree_list_model_new (model,
+                                           TRUE,
+                                           TRUE,
+                                           create_settings_model,
+                                           NULL,
+                                           NULL);
+      model = G_LIST_MODEL (gtk_map_list_model_new (G_LIST_MODEL (treemodel), map_settings_to_keys, NULL, NULL));
+      model = G_LIST_MODEL (gtk_flatten_list_model_new (model));
+      model = G_LIST_MODEL (gtk_filter_list_model_new (model, g_object_ref (filter)));
+      selection = gtk_no_selection_new (model);
+
+      gtk_list_view_set_model (GTK_LIST_VIEW (listview), GTK_SELECTION_MODEL (selection));
+      g_object_unref (selection);
+
+      g_object_unref (builder);
+    }
+
+  if (!gtk_widget_get_visible (window))
+    gtk_widget_set_visible (window, TRUE);
+  else
+    gtk_window_destroy (GTK_WINDOW (window));
+
+  return window;
+}
diff --git a/demos/gtk-demo/listview_settings2.ui b/demos/gtk-demo/listview_settings2.ui
new file mode 100644 (file)
index 0000000..0600f63
--- /dev/null
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <object class="GtkStringFilter" id="filter">
+    <property name="expression">
+      <closure type="gchararray" function="settings_key_get_search_string" />
+    </property>
+    <property name="search" bind-source="entry" bind-property="text" />
+  </object>
+  <object class="GtkWindow" id="window">
+    <property name="title" translatable="yes">Settings</property>
+    <property name="default-width">640</property>
+    <property name="default-height">480</property>
+    <child type="titlebar">
+      <object class="GtkHeaderBar">
+        <child type="end">
+          <object class="GtkToggleButton" id="search_button">
+            <property name="icon-name">system-search-symbolic</property>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="GtkBox">
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkSearchBar">
+            <property name="search-mode-enabled" bind-source="search_button" bind-property="active" bind-flags="bidirectional"/>
+            <signal name="notify::search-mode-enabled" handler="search_enabled" object="entry"/>
+            <child>
+              <object class="GtkSearchEntry" id="entry">
+                <signal name="stop-search" handler="stop_search"/>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkScrolledWindow">
+            <child>
+              <object class="GtkListView" id="listview">
+                <property name="vexpand">1</property>
+                <style>
+                  <class name="rich-list"/>
+                </style>
+                <property name="factory">
+                  <object class="GtkBuilderListItemFactory">
+                    <property name="bytes"><![CDATA[
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="GtkListItem">
+    <property name="child">
+      <object class="GtkBox">
+        <child>
+          <object class="GtkBox">
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="xalign">0</property>
+                <binding name="label">
+                  <lookup name="name" type="SettingsKey">
+                    <lookup name="item">GtkListItem</lookup>
+                  </lookup>
+                </binding>
+              </object>
+            </child>
+            <child>
+              <object class="GtkLabel">
+                <style>
+                  <class name="dim-label"/>
+                </style>
+                <property name="xalign">0</property>
+                <property name="ellipsize">end</property>
+                <binding name="label">
+                  <lookup name="summary" type="SettingsKey">
+                    <lookup name="item">GtkListItem</lookup>
+                  </lookup>
+                </binding>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkEntry">
+            <property name="hexpand">1</property>
+            <property name="halign">end</property>
+            <binding name="text">
+              <lookup name="value" type="SettingsKey">
+                <lookup name="item">GtkListItem</lookup>
+              </lookup>
+            </binding>
+            <signal name="notify::label" handler="item_value_changed"/>
+          </object>
+        </child>
+      </object>
+    </property>
+  </template>
+</interface>
+                    ]]></property>
+                  </object>
+                </property>
+                <property name="header-factory">
+                  <object class="GtkBuilderListItemFactory">
+                    <property name="bytes"><![CDATA[
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="GtkListHeader">
+    <property name="child">
+      <object class="GtkLabel">
+        <property name="xalign">0</property>
+        <binding name="label">
+          <lookup name="schema" type="GSettings">
+            <lookup name="settings" type="SettingsKey">
+              <lookup name="item">GtkListHeader</lookup>
+            </lookup>
+          </lookup>
+        </binding>
+      </object>
+    </property>
+  </template>
+</interface>
+                    ]]></property>
+                  </object>
+                </property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
index f1ce4ffc6bcec17eb4fbf4a505dd1826a05bb4b5..976c3ec997da08737259f478d72840f84abaa523 100644 (file)
@@ -57,6 +57,7 @@ demos = files([
   'listview_minesweeper.c',
   'listview_selections.c',
   'listview_settings.c',
+  'listview_settings2.c',
   'listview_ucd.c',
   'listview_weather.c',
   'listview_words.c',