[gtk/wip/otte/json: 20/21] demos: Add a simple JSON demo




commit 2309ef982b7f9d7023256d6a961dd2d5ee123580
Author: Benjamin Otte <otte redhat com>
Date:   Mon Nov 29 09:46:49 2021 +0100

    demos: Add a simple JSON demo
    
    Download the GTK releases from the gitlab REST API and display them in a
    list.
    
    Much creative.

 demos/gtk-demo/demo.gresource.xml   |   4 +
 demos/gtk-demo/listview_releases.c  | 256 ++++++++++++++++++++++++++++++++++++
 demos/gtk-demo/listview_releases.ui |  95 +++++++++++++
 demos/gtk-demo/meson.build          |   4 +-
 4 files changed, 358 insertions(+), 1 deletion(-)
---
diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml
index 83e798fd25..d2599abb1f 100644
--- a/demos/gtk-demo/demo.gresource.xml
+++ b/demos/gtk-demo/demo.gresource.xml
@@ -192,6 +192,9 @@
     <file>listview_minesweeper.ui</file>
     <file>listview_minesweeper_cell.ui</file>
   </gresource>
+  <gresource prefix="/listview_releases">
+    <file>listview_releases.ui</file>
+  </gresource>
   <gresource prefix="/listview_settings">
     <file>listview_settings.ui</file>
   </gresource>
@@ -304,6 +307,7 @@
     <file>listview_clocks.c</file>
     <file>listview_filebrowser.c</file>
     <file>listview_minesweeper.c</file>
+    <file>listview_releases.c</file>
     <file>listview_settings.c</file>
     <file>listview_ucd.c</file>
     <file>listview_weather.c</file>
diff --git a/demos/gtk-demo/listview_releases.c b/demos/gtk-demo/listview_releases.c
new file mode 100644
index 0000000000..10f15a01dc
--- /dev/null
+++ b/demos/gtk-demo/listview_releases.c
@@ -0,0 +1,256 @@
+/* Lists/Releases
+ * #Keywords: GtkListItemFactory, GListModel,JSON
+ *
+ * This demo downloads GTK's latest release and displays them in a list.
+ *
+ * It shows how hard it still is to get JSON into lists.
+ */
+
+#include <gtk/gtk.h>
+
+#include "gtk/json/gtkjsonparserprivate.h"
+
+#define GTK_TYPE_RELEASE (gtk_release_get_type ())
+
+G_DECLARE_FINAL_TYPE (GtkRelease, gtk_release, GTK, RELEASE, GObject)
+
+struct _GtkRelease
+{
+  GObject parent_instance;
+
+  char *name;
+  GDateTime *timestamp;
+};
+
+struct _GtkReleaseClass
+{
+  GObjectClass parent_class;
+};
+
+enum {
+  PROP_0,
+  PROP_NAME,
+  PROP_TIMESTAMP,
+
+  N_PROPS
+};
+
+G_DEFINE_TYPE (GtkRelease, gtk_release, G_TYPE_OBJECT)
+static GParamSpec *properties[N_PROPS] = { NULL, };
+
+static void
+gtk_release_finalize (GObject *object)
+{
+  GtkRelease *self = GTK_RELEASE (object);
+
+  g_free (self->name);
+  g_clear_pointer (&self->timestamp, g_date_time_unref);
+
+  G_OBJECT_CLASS (gtk_release_parent_class)->finalize (object);
+}
+
+static void
+gtk_release_get_property (GObject    *object,
+                          guint       property_id,
+                          GValue     *value,
+                          GParamSpec *pspec)
+{
+  GtkRelease *self = GTK_RELEASE (object);
+
+  switch (property_id)
+    {
+    case PROP_NAME:
+      g_value_set_string (value, self->name);
+      break;
+
+    case PROP_TIMESTAMP:
+      {
+        if (self->timestamp)
+          g_value_take_string (value, g_date_time_format (self->timestamp, "%x"));
+        else
+          g_value_set_string (value, "---");
+      }
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gtk_release_class_init (GtkReleaseClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize = gtk_release_finalize;
+  gobject_class->get_property = gtk_release_get_property;
+
+  properties[PROP_NAME] =
+    g_param_spec_string ("name", NULL, NULL, NULL, G_PARAM_READABLE);
+  properties[PROP_TIMESTAMP] =
+    g_param_spec_string ("timestamp", NULL, NULL, NULL, G_PARAM_READABLE);
+
+  g_object_class_install_properties (gobject_class, N_PROPS, properties);
+}
+
+static void
+gtk_release_init (GtkRelease *self)
+{
+}
+
+static GtkRelease *
+gtk_release_new (const char *name,
+                 GDateTime  *timestamp)
+{
+  GtkRelease *result;
+
+  result = g_object_new (GTK_TYPE_RELEASE, NULL);
+
+  result->name = g_strdup (name);
+  if (timestamp)
+    result->timestamp = g_date_time_ref (timestamp);
+
+  return result;
+}
+
+static void
+loaded_some_releases_cb (GObject      *file,
+                         GAsyncResult *res,
+                         gpointer      userdata)
+{
+  GListStore *store = userdata;
+  GBytes *bytes;
+  GtkJsonParser *parser;
+  GError *error = NULL;
+
+  bytes = g_file_load_bytes_finish (G_FILE (file), res, NULL, &error);
+  if (bytes == NULL)
+    {
+      g_printerr ("Error loading: %s\n", error->message);
+      g_clear_error (&error);
+      return;
+    }
+
+  parser = gtk_json_parser_new_for_bytes (bytes);
+  g_bytes_unref (bytes);
+
+  gtk_json_parser_start_array (parser);
+  do
+    {
+      enum { NAME, COMMIT };
+      static const char *options[] = { "name", "commit", NULL };
+      GDateTime *created = NULL;
+      char *name = NULL;
+
+      gtk_json_parser_start_object (parser);
+      do
+        {
+          switch (gtk_json_parser_select_member (parser, options))
+            {
+              case NAME:
+                g_clear_pointer (&name, g_free);
+                name = gtk_json_parser_get_string (parser);
+                break;
+              case COMMIT:
+                g_clear_pointer (&created, g_date_time_unref);
+                gtk_json_parser_start_object (parser);
+                if (gtk_json_parser_find_member (parser, "created_at"))
+                  {
+                    char *created_string = gtk_json_parser_get_string (parser);
+                    created = g_date_time_new_from_iso8601 (created_string, NULL);
+                    g_free (created_string);
+                  }
+                gtk_json_parser_end (parser);
+                break;
+              default:
+                break;
+            }
+        }
+      while (gtk_json_parser_next (parser));
+      gtk_json_parser_end (parser);
+
+      if (name)
+        {
+          GtkRelease *release = gtk_release_new (name, created);
+          g_list_store_append (store, release);
+          g_object_unref (release);
+        }
+      g_clear_pointer (&name, g_free);
+      g_clear_pointer (&created, g_date_time_unref);
+    }
+  while (gtk_json_parser_next (parser));
+  gtk_json_parser_end (parser);
+
+  if (gtk_json_parser_get_error (parser))
+    {
+      const GError *json_error = gtk_json_parser_get_error (parser);
+
+      g_printerr ("Error parsing: %s\n", json_error->message);
+    }
+  gtk_json_parser_free (parser);
+
+  gtk_toggle_button_set_active (g_object_get_data (G_OBJECT (store), "togglebutton"), FALSE);
+}
+
+static void
+load_some_releases (GListStore *store)
+{
+  GFile *file;
+  guint n_items;
+  char *url;
+
+  n_items = g_list_model_get_n_items (G_LIST_MODEL (store));
+  if (n_items == 0)
+    url = g_strdup ("https://gitlab.gnome.org/api/v4/projects/665/repository/tags";);
+  else
+    url = g_strdup_printf ("https://gitlab.gnome.org/api/v4/projects/665/repository/tags?page=%u";, (n_items 
+ 39) / 20);
+  file = g_file_new_for_uri (url);
+
+  g_file_load_bytes_async (file, NULL, loaded_some_releases_cb, store);
+  g_object_unref (file);
+  g_free (url);
+}
+
+static GtkWidget *window = NULL;
+
+GtkWidget *
+do_listview_releases (GtkWidget *do_widget)
+{
+  if (window == NULL)
+    {
+      GtkBuilderScope *scope;
+      GtkBuilder *builder;
+      GListStore *list;
+      GtkWidget *more_button;
+
+      g_type_ensure (GTK_TYPE_RELEASE);
+
+      scope = gtk_builder_cscope_new ();
+      gtk_builder_cscope_add_callback_symbol (GTK_BUILDER_CSCOPE (scope), "load_some_releases", 
(GCallback)load_some_releases);
+
+      builder = gtk_builder_new ();
+      gtk_builder_set_scope (builder, scope);
+      g_object_unref (scope);
+
+      gtk_builder_add_from_resource (builder, "/listview_releases/listview_releases.ui", NULL);
+
+      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);
+
+      more_button = GTK_WIDGET (gtk_builder_get_object (builder, "more_button"));
+      list = G_LIST_STORE (gtk_builder_get_object (builder, "list"));
+      g_object_set_data (G_OBJECT (list), "togglebutton", more_button);
+      load_some_releases (list);
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (more_button), TRUE);
+    }
+
+  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/listview_releases.ui b/demos/gtk-demo/listview_releases.ui
new file mode 100644
index 0000000000..730e477345
--- /dev/null
+++ b/demos/gtk-demo/listview_releases.ui
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <object class="GListStore" id="list">
+    <property name="item-type">GtkRelease</property>
+  </object>
+  <object class="GtkWindow" id="window">
+    <property name="title" translatable="yes">Releases</property>
+    <property name="default-width">640</property>
+    <property name="default-height">480</property>
+    <child type="titlebar">
+      <object class="GtkHeaderBar">
+        <child type="start">
+          <object class="GtkToggleButton" id="more_button">
+            <property name="label">Moreā€¦</property>
+            <property name="sensitive" bind-source="more_button" bind-property="active" 
bind-flags="invert-boolean" />
+            <signal name="clicked" handler="load_some_releases" object="list" swapped="yes"/>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="GtkScrolledWindow">
+        <property name="hexpand">1</property>
+        <property name="vexpand">1</property>
+        <child>
+          <object class="GtkColumnView" id="columnview">
+            <property name="model">
+              <object class="GtkSingleSelection">
+                <property name="model">list</property>
+              </object>
+            </property>
+            <style>
+              <class name="data-table"/>
+            </style>
+            <child>
+              <object class="GtkColumnViewColumn" id="name_column">
+                <property name="title">Name</property>
+                <property name="resizable">1</property>
+                <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="GtkLabel">
+        <property name="xalign">0</property>
+        <binding name="label">
+          <lookup name="name" type="GtkRelease">
+            <lookup name="item">GtkListItem</lookup>
+          </lookup>
+        </binding>
+      </object>
+    </property>
+  </template>
+</interface>
+                ]]></property>
+                  </object>
+                </property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkColumnViewColumn">
+                <property name="title">Date</property>
+                <property name="resizable">1</property>
+                <property name="expand">1</property>
+                <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="GtkLabel">
+        <property name="xalign">0</property>
+        <binding name="label">
+          <lookup name="timestamp" type="GtkRelease">
+            <lookup name="item">GtkListItem</lookup>
+          </lookup>
+        </binding>
+      </object>
+    </property>
+  </template>
+</interface>
+                ]]></property>
+                  </object>
+                </property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/demos/gtk-demo/meson.build b/demos/gtk-demo/meson.build
index 3576399f04..4328643373 100644
--- a/demos/gtk-demo/meson.build
+++ b/demos/gtk-demo/meson.build
@@ -55,6 +55,7 @@ demos = files([
   'listview_filebrowser.c',
   'listview_minesweeper.c',
   'dropdown.c',
+  'listview_releases.c',
   'listview_settings.c',
   'listview_ucd.c',
   'listview_weather.c',
@@ -100,7 +101,7 @@ demos = files([
   'video_player.c',
 ])
 
-gtkdemo_deps = [ libgtk_dep, ]
+gtkdemo_deps = [ libgtk_dep, libgtk_json_dep ]
 
 extra_demo_sources = files([
   'main.c',
@@ -244,6 +245,7 @@ executable('gtk4-demo',
   include_directories: confinc,
   gui_app: true,
   link_args: extra_demo_ldflags,
+  link_with: [ libgtk_json, ],
   install: true,
 )
 


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