[gtk/inspector-trees] inspector: Stop using GtkTreeView for statistics




commit 7dadff91e4cbc866308cd81acf59f8ad9ce4f38c
Author: Matthias Clasen <mclasen redhat com>
Date:   Thu Oct 6 23:03:15 2022 -0400

    inspector: Stop using GtkTreeView for statistics
    
    GtkTreeView is heading towards deprecation; use
    a GtkColumnView instead.

 gtk/inspector/cellrenderergraph.c | 288 --------------
 gtk/inspector/cellrenderergraph.h |  62 ---
 gtk/inspector/graphdata.c         | 115 +++---
 gtk/inspector/graphdata.h         |  55 +--
 gtk/inspector/graphrenderer.c     | 257 +++++++++++++
 gtk/inspector/graphrenderer.h     |  37 ++
 gtk/inspector/init.c              |   6 +-
 gtk/inspector/meson.build         |   2 +-
 gtk/inspector/statistics.c        | 781 +++++++++++++++++++++++++++++---------
 gtk/inspector/statistics.ui       |  91 +----
 10 files changed, 972 insertions(+), 722 deletions(-)
---
diff --git a/gtk/inspector/graphdata.c b/gtk/inspector/graphdata.c
index c546e148af..9fab32110f 100644
--- a/gtk/inspector/graphdata.c
+++ b/gtk/inspector/graphdata.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014 Benjamin Otte <ottte gnome org>
+ * Copyright (c) 2014 Benjamin Otte <otte gnome org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -24,74 +24,73 @@ enum {
   PROP_N_VALUES
 };
 
-struct _GtkGraphDataPrivate
+struct _GraphData
 {
+  GObject parent;
+
   guint n_values;
   guint offset;
   double *values;
 };
 
 
-G_DEFINE_TYPE_WITH_PRIVATE (GtkGraphData, gtk_graph_data, G_TYPE_OBJECT)
+G_DEFINE_TYPE (GraphData, graph_data, G_TYPE_OBJECT)
 
 static void
-gtk_graph_data_get_property (GObject    *object,
-                             guint       param_id,
-                             GValue     *value,
-                             GParamSpec *pspec)
+graph_data_get_property (GObject    *object,
+                         guint       param_id,
+                         GValue     *value,
+                         GParamSpec *pspec)
 {
-  GtkGraphData *graph = GTK_GRAPH_DATA (object);
-  GtkGraphDataPrivate *priv = graph->priv;
+  GraphData *graph = GRAPH_DATA (object);
 
   switch (param_id)
     {
-      case PROP_N_VALUES:
-        g_value_set_boolean (value, priv->n_values);
-        break;
-      default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+    case PROP_N_VALUES:
+      g_value_set_boolean (value, graph->n_values);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
     }
 }
 
 static void
-gtk_graph_data_finalize (GObject *object)
+graph_data_finalize (GObject *object)
 {
-  GtkGraphData *graph = GTK_GRAPH_DATA (object);
-  GtkGraphDataPrivate *priv = graph->priv;
+  GraphData *graph = GRAPH_DATA (object);
 
-  g_free (priv->values);
+  g_free (graph->values);
 
-  G_OBJECT_CLASS (gtk_graph_data_parent_class)->finalize (object);
+  G_OBJECT_CLASS (graph_data_parent_class)->finalize (object);
 }
 
 static void
-gtk_graph_data_set_property (GObject      *object,
-                             guint         param_id,
-                             const GValue *value,
-                             GParamSpec   *pspec)
+graph_data_set_property (GObject      *object,
+                         guint         param_id,
+                         const GValue *value,
+                         GParamSpec   *pspec)
 {
-  GtkGraphData *graph = GTK_GRAPH_DATA (object);
-  GtkGraphDataPrivate *priv = graph->priv;
+  GraphData *graph = GRAPH_DATA (object);
 
   switch (param_id)
     {
-      case PROP_N_VALUES:
-        priv->n_values = g_value_get_uint (value);
-        priv->values = g_new0 (double, priv->n_values);
-        break;
-      default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+    case PROP_N_VALUES:
+      graph->n_values = g_value_get_uint (value);
+      graph->values = g_new0 (double, graph->n_values);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
     }
 }
 
 static void
-gtk_graph_data_class_init (GtkGraphDataClass *klass)
+graph_data_class_init (GraphDataClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
-  object_class->finalize = gtk_graph_data_finalize;
-  object_class->get_property = gtk_graph_data_get_property;
-  object_class->set_property = gtk_graph_data_set_property;
+  object_class->finalize = graph_data_finalize;
+  object_class->get_property = graph_data_get_property;
+  object_class->set_property = graph_data_set_property;
 
   g_object_class_install_property (object_class,
                                    PROP_N_VALUES,
@@ -101,71 +100,63 @@ gtk_graph_data_class_init (GtkGraphDataClass *klass)
 }
 
 static void
-gtk_graph_data_init (GtkGraphData *graph)
+graph_data_init (GraphData *graph)
 {
-  graph->priv = gtk_graph_data_get_instance_private (graph);
 }
 
-GtkGraphData *
-gtk_graph_data_new (guint n_values)
+GraphData *
+graph_data_new (guint n_values)
 {
-  return g_object_new (GTK_TYPE_GRAPH_DATA,
+  return g_object_new (graph_data_get_type (),
                        "n-values", n_values,
                        NULL);
 }
 
 guint
-gtk_graph_data_get_n_values (GtkGraphData *data)
+graph_data_get_n_values (GraphData *data)
 {
-  return data->priv->n_values;
+  return data->n_values;
 }
 
 double
-gtk_graph_data_get_value (GtkGraphData   *data,
-                          guint           i)
+graph_data_get_value (GraphData *data,
+                      guint      i)
 {
-  GtkGraphDataPrivate *priv = data->priv;
-
-  return priv->values[(priv->offset + i) % priv->n_values];
+  return data->values[(data->offset + i) % data->n_values];
 }
 
 double
-gtk_graph_data_get_minimum (GtkGraphData *data)
+graph_data_get_minimum (GraphData *data)
 {
-  GtkGraphDataPrivate *priv = data->priv;
   double minimum = G_MAXDOUBLE;
   guint i;
 
-  for (i = 0; i < priv->n_values; i++)
+  for (i = 0; i < data->n_values; i++)
     {
-      minimum = MIN (minimum, priv->values[i]);
+      minimum = MIN (minimum, data->values[i]);
     }
 
   return minimum;
 }
 
 double
-gtk_graph_data_get_maximum (GtkGraphData *data)
+graph_data_get_maximum (GraphData *data)
 {
-  GtkGraphDataPrivate *priv = data->priv;
   double maximum = -G_MAXDOUBLE;
   guint i;
 
-  for (i = 0; i < priv->n_values; i++)
+  for (i = 0; i < data->n_values; i++)
     {
-      maximum = MAX (maximum, priv->values[i]);
+      maximum = MAX (maximum, data->values[i]);
     }
 
   return maximum;
 }
 
 void
-gtk_graph_data_prepend_value (GtkGraphData *data,
-                              double        value)
+graph_data_prepend_value (GraphData *data,
+                          double     value)
 {
-  GtkGraphDataPrivate *priv = data->priv;
-
-  priv->offset = (priv->offset + priv->n_values - 1) % priv->n_values;
-  priv->values[priv->offset] = value;
+  data->offset = (data->offset + data->n_values - 1) % data->n_values;
+  data->values[data->offset] = value;
 }
-
diff --git a/gtk/inspector/graphdata.h b/gtk/inspector/graphdata.h
index ee49009198..d746b018d0 100644
--- a/gtk/inspector/graphdata.h
+++ b/gtk/inspector/graphdata.h
@@ -15,57 +15,28 @@
  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef __GTK_GRAPH_DATA_H__
-#define __GTK_GRAPH_DATA_H__
+#ifndef __GRAPH_DATA_H__
+#define __GRAPH_DATA_H__
 
 #include <glib-object.h>
 
 G_BEGIN_DECLS
 
-#define GTK_TYPE_GRAPH_DATA            (gtk_graph_data_get_type ())
-#define GTK_GRAPH_DATA(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_GRAPH_DATA, 
GtkGraphData))
-#define GTK_GRAPH_DATA_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_GRAPH_DATA, 
GtkGraphDataClass))
-#define GTK_IS_GRAPH_DATA(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_GRAPH_DATA))
-#define GTK_IS_GRAPH_DATA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_GRAPH_DATA))
-#define GTK_GRAPH_DATA_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_GRAPH_DATA, 
GtkGraphDataClass))
+typedef struct _GraphData        GraphData;
 
-typedef struct _GtkGraphData        GtkGraphData;
-typedef struct _GtkGraphDataClass   GtkGraphDataClass;
-typedef struct _GtkGraphDataPrivate GtkGraphDataPrivate;
+G_DECLARE_FINAL_TYPE (GraphData, graph_data, GRAPH, DATA, GObject)
 
-struct _GtkGraphData
-{
-  GObject              object;
+GraphData       *graph_data_new             (guint        n_values);
 
-  /*< private >*/
-  GtkGraphDataPrivate *priv;
-};
+guint            graph_data_get_n_values    (GraphData   *data);
+double           graph_data_get_value       (GraphData   *data,
+                                             guint        i);
+double           graph_data_get_minimum     (GraphData   *data);
+double           graph_data_get_maximum     (GraphData   *data);
 
-struct _GtkGraphDataClass
-{
-  GObjectClass parent_class;
-
-  /* Padding for future expansion */
-  void (*_gtk_reserved1) (void);
-  void (*_gtk_reserved2) (void);
-  void (*_gtk_reserved3) (void);
-  void (*_gtk_reserved4) (void);
-};
-
-
-GType            gtk_graph_data_get_type        (void) G_GNUC_CONST;
-
-GtkGraphData    *gtk_graph_data_new             (guint           n_values);
-
-guint            gtk_graph_data_get_n_values    (GtkGraphData   *data);
-double           gtk_graph_data_get_value       (GtkGraphData   *data,
-                                                 guint           i);
-double           gtk_graph_data_get_minimum     (GtkGraphData   *data);
-double           gtk_graph_data_get_maximum     (GtkGraphData   *data);
-
-void             gtk_graph_data_prepend_value   (GtkGraphData   *data,
-                                                 double          value);
+void             graph_data_prepend_value   (GraphData   *data,
+                                             double       value);
 
 G_END_DECLS
 
-#endif /* __GTK_GRAPH_DATA_H__ */
+#endif /* __GRAPH_DATA_H__ */
diff --git a/gtk/inspector/graphrenderer.c b/gtk/inspector/graphrenderer.c
new file mode 100644
index 0000000000..6ade21e5b2
--- /dev/null
+++ b/gtk/inspector/graphrenderer.c
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2014 Benjamin Otte <otte gnome org>
+ *
+ * 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 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/>.
+ */
+
+#include "config.h"
+
+#include "graphrenderer.h"
+
+#include "graphdata.h"
+
+#include "gtksnapshot.h"
+#include "gtkstylecontext.h"
+
+enum {
+  PROP_0,
+  PROP_DATA,
+  PROP_MINIMUM,
+  PROP_MAXIMUM
+};
+
+struct _GraphRenderer
+{
+  GtkWidget parent;
+
+  GraphData *data;
+  double minimum;
+  double maximum;
+};
+
+G_DEFINE_TYPE (GraphRenderer, graph_renderer, GTK_TYPE_WIDGET)
+
+static void
+graph_renderer_dispose (GObject *object)
+{
+  GraphRenderer *self = GRAPH_RENDERER (object);
+
+  g_clear_object (&self->data);
+
+  G_OBJECT_CLASS (graph_renderer_parent_class)->dispose (object);
+}
+
+static void
+graph_renderer_get_property (GObject    *object,
+                             guint       param_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+  GraphRenderer *self = GRAPH_RENDERER (object);
+
+  switch (param_id)
+    {
+      case PROP_DATA:
+        g_value_set_object (value, self->data);
+        break;
+      case PROP_MINIMUM:
+        g_value_set_double (value, self->minimum);
+        break;
+      case PROP_MAXIMUM:
+        g_value_set_double (value, self->maximum);
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+    }
+}
+
+static void
+graph_renderer_set_property (GObject      *object,
+                             guint         param_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+  GraphRenderer *self = GRAPH_RENDERER (object);
+
+  switch (param_id)
+    {
+      case PROP_DATA:
+        graph_renderer_set_data (self, g_value_get_object (value));
+        break;
+      case PROP_MINIMUM:
+        if (self->minimum != g_value_get_double (value))
+          {
+            self->minimum = g_value_get_double (value);
+            g_object_notify_by_pspec (object, pspec);
+          }
+        break;
+      case PROP_MAXIMUM:
+        if (self->maximum != g_value_get_double (value))
+          {
+            self->maximum = g_value_get_double (value);
+            g_object_notify_by_pspec (object, pspec);
+          }
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+    }
+}
+
+#define MIN_HEIGHT 24
+#define MIN_WIDTH (3 * MIN_HEIGHT)
+
+static void
+graph_renderer_measure (GtkWidget      *widget,
+                        GtkOrientation  orientation,
+                        int             for_size,
+                        int            *minimum,
+                        int            *natural,
+                        int            *minimum_baseline,
+                        int            *natural_baseline)
+{
+  if (orientation == GTK_ORIENTATION_HORIZONTAL)
+    *minimum = *natural = MIN_WIDTH;
+  else
+    *minimum = *natural = MIN_HEIGHT;
+}
+
+static void
+graph_renderer_snapshot (GtkWidget   *widget,
+                         GtkSnapshot *snapshot)
+{
+  GraphRenderer *self = GRAPH_RENDERER (widget);
+  GtkStyleContext *context;
+  double minimum, maximum, diff;
+  double x, y, width, height;
+  cairo_t *cr;
+  GdkRGBA color;
+  guint i, n;
+
+#define LINE_WIDTH 1.0
+
+  if (self->data == NULL)
+    return;
+
+  if (self->minimum == -G_MAXDOUBLE)
+    minimum = graph_data_get_minimum (self->data);
+  else
+    minimum = self->minimum;
+
+  if (self->maximum == G_MAXDOUBLE)
+    maximum = graph_data_get_maximum (self->data);
+  else
+    maximum = self->maximum;
+
+  diff = maximum - minimum;
+
+  context = gtk_widget_get_style_context (widget);
+  gtk_style_context_get_color (context, &color);
+
+  cr = gtk_snapshot_append_cairo (snapshot,
+                                  &GRAPHENE_RECT_INIT (
+                                      0, 0,
+                                      gtk_widget_get_width (widget),
+                                      gtk_widget_get_height (widget)
+                                  ));
+
+  cairo_set_line_width (cr, 1.0);
+
+  x = LINE_WIDTH / 2.0;
+  y = LINE_WIDTH / 2.0;
+  width = gtk_widget_get_width (widget) - LINE_WIDTH;
+  height = gtk_widget_get_height (widget) - LINE_WIDTH;
+
+  cairo_move_to (cr, x, y + height);
+
+  if (diff > 0)
+    {
+      n = graph_data_get_n_values (self->data);
+      for (i = 0; i < n; i++)
+        {
+          double val = graph_data_get_value (self->data, i);
+
+          val = (val - minimum) / diff;
+          val = y + height - val * height;
+
+          cairo_line_to (cr, x + width * i / (n - 1), val);
+        }
+    }
+
+  cairo_line_to (cr, x + width, y + height);
+  cairo_close_path (cr);
+
+  gdk_cairo_set_source_rgba (cr, &color);
+  cairo_stroke_preserve (cr);
+
+  color.alpha *= 0.2;
+  gdk_cairo_set_source_rgba (cr, &color);
+  cairo_fill (cr);
+
+  cairo_destroy (cr);
+}
+
+static void
+graph_renderer_class_init (GraphRendererClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->dispose = graph_renderer_dispose;
+  object_class->get_property = graph_renderer_get_property;
+  object_class->set_property = graph_renderer_set_property;
+
+  widget_class->measure = graph_renderer_measure;
+  widget_class->snapshot = graph_renderer_snapshot;
+
+  g_object_class_install_property (object_class,
+                                   PROP_DATA,
+                                   g_param_spec_object ("data", NULL, NULL,
+                                                        graph_data_get_type (),
+                                                        G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
+
+  g_object_class_install_property (object_class,
+                                   PROP_MINIMUM,
+                                   g_param_spec_double ("minimum", NULL, NULL,
+                                                        -G_MAXDOUBLE, G_MAXDOUBLE, -G_MAXDOUBLE,
+                                                        G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
+
+  g_object_class_install_property (object_class,
+                                   PROP_MINIMUM,
+                                   g_param_spec_double ("maximum", NULL, NULL,
+                                                        -G_MAXDOUBLE, G_MAXDOUBLE, G_MAXDOUBLE,
+                                                        G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
+}
+
+static void
+graph_renderer_init (GraphRenderer *self)
+{
+  self->minimum = -G_MAXDOUBLE;
+  self->maximum = G_MAXDOUBLE;
+}
+
+GraphRenderer *
+graph_renderer_new (void)
+{
+  return g_object_new (graph_renderer_get_type (), NULL);
+}
+
+void
+graph_renderer_set_data (GraphRenderer *self,
+                         GraphData     *data)
+{
+  if (g_set_object (&self->data, data))
+    g_object_notify (G_OBJECT (self), "data");
+
+  gtk_widget_queue_draw (GTK_WIDGET (self));
+}
diff --git a/gtk/inspector/graphrenderer.h b/gtk/inspector/graphrenderer.h
new file mode 100644
index 0000000000..11f0a7ca9f
--- /dev/null
+++ b/gtk/inspector/graphrenderer.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2014 Benjamin Otte <otte gnome org>
+ *
+ * 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 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/>.
+ */
+
+#ifndef __GRAPH_RENDERER_H__
+#define __GRAPH_RENDERER_H__
+
+#include <gtk/gtkwidget.h>
+#include "graphdata.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GraphRenderer GraphRenderer;
+
+G_DECLARE_FINAL_TYPE (GraphRenderer, graph_renderer, GRAPH, RENDERER, GtkWidget);
+
+GraphRenderer *graph_renderer_new      (void);
+
+void           graph_renderer_set_data (GraphRenderer *self,
+                                        GraphData     *data);
+
+G_END_DECLS
+
+#endif /* __GRAPH_RENDERER_H__ */
diff --git a/gtk/inspector/init.c b/gtk/inspector/init.c
index 32f8cc1e1b..cc58d943db 100644
--- a/gtk/inspector/init.c
+++ b/gtk/inspector/init.c
@@ -26,7 +26,7 @@
 
 #include "a11y.h"
 #include "actions.h"
-#include "cellrenderergraph.h"
+#include "graphrenderer.h"
 #include "clipboard.h"
 #include "controllers.h"
 #include "css-editor.h"
@@ -66,8 +66,8 @@ gtk_inspector_init (void)
 
   g_type_ensure (G_TYPE_LIST_STORE);
 
-  g_type_ensure (GTK_TYPE_CELL_RENDERER_GRAPH);
-  g_type_ensure (GTK_TYPE_GRAPH_DATA);
+  g_type_ensure (graph_data_get_type ());
+  g_type_ensure (graph_renderer_get_type ());
   g_type_ensure (GTK_TYPE_INSPECTOR_A11Y);
   g_type_ensure (GTK_TYPE_INSPECTOR_ACTIONS);
   g_type_ensure (GTK_TYPE_INSPECTOR_CLIPBOARD);
diff --git a/gtk/inspector/meson.build b/gtk/inspector/meson.build
index f5bbe02cf9..3022031689 100644
--- a/gtk/inspector/meson.build
+++ b/gtk/inspector/meson.build
@@ -4,7 +4,6 @@ inspector_sources = files(
   'action-holder.c',
   'actions.c',
   'baselineoverlay.c',
-  'cellrenderergraph.c',
   'clipboard.c',
   'controllers.c',
   'css-editor.c',
@@ -14,6 +13,7 @@ inspector_sources = files(
   'fpsoverlay.c',
   'general.c',
   'graphdata.c',
+  'graphrenderer.c',
   'gtktreemodelcssnode.c',
   'gtkdataviewer.c',
   'highlightoverlay.c',
diff --git a/gtk/inspector/statistics.c b/gtk/inspector/statistics.c
index b094fc2e93..c51e72c292 100644
--- a/gtk/inspector/statistics.c
+++ b/gtk/inspector/statistics.c
@@ -20,21 +20,217 @@
 #include "statistics.h"
 
 #include "graphdata.h"
+#include "graphrenderer.h"
 
-#include "gtkcelllayout.h"
-#include "gtkcellrenderertext.h"
 #include "gtklabel.h"
 #include "gtksearchbar.h"
 #include "gtkstack.h"
 #include "gtktogglebutton.h"
-#include "gtktreeselection.h"
-#include "gtktreeview.h"
 #include "gtkeventcontrollerkey.h"
 #include "gtkmain.h"
 #include "gtkliststore.h"
+#include "gtkcolumnview.h"
+#include "gtkcolumnviewcolumn.h"
+#include "gtksingleselection.h"
+#include "gtksignallistitemfactory.h"
+#include "gtklistitem.h"
+#include "gtkstringsorter.h"
+#include "gtknumericsorter.h"
+#include "gtksortlistmodel.h"
+#include "gtksearchentry.h"
 
 #include <glib/gi18n-lib.h>
 
+/* {{{ TypeData object */
+
+typedef struct _TypeData TypeData;
+
+G_DECLARE_FINAL_TYPE (TypeData, type_data, TYPE, DATA, GObject);
+
+struct _TypeData {
+  GObject parent;
+
+  GType type;
+  GraphData *self;
+  GraphData *cumulative;
+};
+
+enum {
+  TYPE_DATA_PROP_NAME = 1,
+  TYPE_DATA_PROP_SELF1,
+  TYPE_DATA_PROP_CUMULATIVE1,
+  TYPE_DATA_PROP_SELF2,
+  TYPE_DATA_PROP_CUMULATIVE2,
+  TYPE_DATA_PROP_SELF,
+  TYPE_DATA_PROP_CUMULATIVE,
+};
+
+G_DEFINE_TYPE (TypeData, type_data, G_TYPE_OBJECT);
+
+static void
+type_data_init (TypeData *self)
+{
+}
+
+static void
+type_data_finalize (GObject *object)
+{
+  TypeData *self = TYPE_DATA (object);
+
+  g_object_unref (self->self);
+  g_object_unref (self->cumulative);
+
+  G_OBJECT_CLASS (type_data_parent_class)->finalize (object);
+}
+
+static void
+type_data_get_property (GObject    *object,
+                        guint       property_id,
+                        GValue     *value,
+                        GParamSpec *pspec)
+{
+  TypeData *self = TYPE_DATA (object);
+
+  switch (property_id)
+    {
+    case TYPE_DATA_PROP_NAME:
+      g_value_set_string (value, g_type_name (self->type));
+      break;
+
+    case TYPE_DATA_PROP_SELF1:
+      g_value_set_int (value, (int) graph_data_get_value (self->self, 1));
+      break;
+
+    case TYPE_DATA_PROP_CUMULATIVE1:
+      g_value_set_int (value, (int) graph_data_get_value (self->cumulative, 1));
+      break;
+
+    case TYPE_DATA_PROP_SELF2:
+      g_value_set_int (value, (int) graph_data_get_value (self->self, 0));
+      break;
+
+    case TYPE_DATA_PROP_CUMULATIVE2:
+      g_value_set_int (value, (int) graph_data_get_value (self->cumulative, 0));
+      break;
+
+    case TYPE_DATA_PROP_SELF:
+      g_value_set_object (value, self->self);
+      break;
+
+    case TYPE_DATA_PROP_CUMULATIVE:
+      g_value_set_object (value, self->cumulative);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+type_data_class_init (TypeDataClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->finalize = type_data_finalize;
+  object_class->get_property = type_data_get_property;
+
+  g_object_class_install_property (object_class,
+                                   TYPE_DATA_PROP_NAME,
+                                   g_param_spec_string ("name", NULL, NULL,
+                                                        NULL,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (object_class,
+                                   TYPE_DATA_PROP_SELF1,
+                                   g_param_spec_int ("self1", NULL, NULL,
+                                                     0, G_MAXINT, 0,
+                                                     G_PARAM_READABLE |
+                                                     G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (object_class,
+                                   TYPE_DATA_PROP_CUMULATIVE1,
+                                   g_param_spec_int ("cumulative1", NULL, NULL,
+                                                     0, G_MAXINT, 0,
+                                                     G_PARAM_READABLE |
+                                                     G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (object_class,
+                                   TYPE_DATA_PROP_SELF2,
+                                   g_param_spec_int ("self2", NULL, NULL,
+                                                     0, G_MAXINT, 0,
+                                                     G_PARAM_READABLE |
+                                                     G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (object_class,
+                                   TYPE_DATA_PROP_CUMULATIVE2,
+                                   g_param_spec_int ("cumulative2", NULL, NULL,
+                                                     0, G_MAXINT, 0,
+                                                     G_PARAM_READABLE |
+                                                     G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (object_class,
+                                   TYPE_DATA_PROP_SELF,
+                                   g_param_spec_object ("self", NULL, NULL,
+                                                        graph_data_get_type (),
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (object_class,
+                                   TYPE_DATA_PROP_CUMULATIVE,
+                                   g_param_spec_object ("cumulative", NULL, NULL,
+                                                        graph_data_get_type (),
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_STATIC_STRINGS));
+}
+
+static TypeData *
+type_data_new (GType type)
+{
+  TypeData *self;
+
+  self = g_object_new (type_data_get_type (), NULL);
+
+  self->type = type;
+  self->self = graph_data_new (60);
+  self->cumulative = graph_data_new (60);
+
+  return self;
+}
+
+static void
+type_data_update (TypeData *data,
+                  int       self,
+                  int       cumulative)
+{
+  int value;
+
+  g_object_freeze_notify (G_OBJECT (data));
+
+  value = graph_data_get_value (data->self, 0);
+  if (value != self)
+    g_object_notify (G_OBJECT (data), "self2");
+  if (value != graph_data_get_value (data->self, 1))
+    g_object_notify (G_OBJECT (data), "self1");
+
+  g_object_notify (G_OBJECT (data), "self");
+  graph_data_prepend_value (data->self, self);
+
+  value = graph_data_get_value (data->cumulative, 0);
+  if (value != cumulative)
+    g_object_notify (G_OBJECT (data), "cumulative2");
+  if (value != graph_data_get_value (data->cumulative, 1))
+    g_object_notify (G_OBJECT (data), "cumulative1");
+
+  g_object_notify (G_OBJECT (data), "cumulative");
+  graph_data_prepend_value (data->cumulative, cumulative);
+
+  g_object_thaw_notify (G_OBJECT (data));
+}
+
+/* }}} */
+
 enum
 {
   PROP_0,
@@ -45,43 +241,16 @@ struct _GtkInspectorStatisticsPrivate
 {
   GtkWidget *stack;
   GtkWidget *excuse;
-  GtkTreeModel *model;
-  GtkTreeView  *view;
+  GtkWidget *view;
   GtkWidget *button;
-  GHashTable *data;
-  GtkTreeViewColumn *column_self1;
-  GtkCellRenderer *renderer_self1;
-  GtkTreeViewColumn *column_cumulative1;
-  GtkCellRenderer *renderer_cumulative1;
-  GtkTreeViewColumn *column_self2;
-  GtkCellRenderer *renderer_self2;
-  GtkTreeViewColumn *column_cumulative2;
-  GtkCellRenderer *renderer_cumulative2;
-  GHashTable *counts;
+  GListStore *data;
+  GtkSingleSelection *selection;
+  GHashTable *types;
   guint update_source_id;
   GtkWidget *search_entry;
   GtkWidget *search_bar;
 };
 
-typedef struct {
-  GType type;
-  GtkTreeIter treeiter;
-  GtkGraphData *self;
-  GtkGraphData *cumulative;
-} TypeData;
-
-enum
-{
-  COLUMN_TYPE,
-  COLUMN_TYPE_NAME,
-  COLUMN_SELF1,
-  COLUMN_CUMULATIVE1,
-  COLUMN_SELF2,
-  COLUMN_CUMULATIVE2,
-  COLUMN_SELF_DATA,
-  COLUMN_CUMULATIVE_DATA
-};
-
 G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorStatistics, gtk_inspector_statistics, GTK_TYPE_BOX)
 
 static int
@@ -92,6 +261,7 @@ add_type_count (GtkInspectorStatistics *sl, GType type)
   GType *children;
   guint n_children;
   int i;
+  guint idx;
   TypeData *data;
 
   cumulative = 0;
@@ -100,35 +270,25 @@ add_type_count (GtkInspectorStatistics *sl, GType type)
   for (i = 0; i < n_children; i++)
     cumulative += add_type_count (sl, children[i]);
 
-  data = g_hash_table_lookup (sl->priv->counts, GSIZE_TO_POINTER (type));
-  if (!data)
+  idx = GPOINTER_TO_UINT (g_hash_table_lookup (sl->priv->types, GSIZE_TO_POINTER (type)));
+  if (idx == 0)
     {
-      data = g_new0 (TypeData, 1);
-      data->type = type;
-      data->self = gtk_graph_data_new (60);
-      data->cumulative = gtk_graph_data_new (60);
-      gtk_list_store_append (GTK_LIST_STORE (sl->priv->model), &data->treeiter);
-      gtk_list_store_set (GTK_LIST_STORE (sl->priv->model), &data->treeiter,
-                          COLUMN_TYPE, data->type,
-                          COLUMN_TYPE_NAME, g_type_name (data->type),
-                          COLUMN_SELF_DATA, data->self,
-                          COLUMN_CUMULATIVE_DATA, data->cumulative,
-                          -1);
-      g_hash_table_insert (sl->priv->counts, GSIZE_TO_POINTER (type), data);
+      g_list_store_append (sl->priv->data, type_data_new (type));
+      idx = g_list_model_get_n_items (G_LIST_MODEL (sl->priv->data));
+      g_hash_table_insert (sl->priv->types, GSIZE_TO_POINTER (type), GUINT_TO_POINTER (idx));
     }
 
+  data = g_list_model_get_item (G_LIST_MODEL (sl->priv->data), idx - 1);
+
+  g_assert (data->type == type);
+
   self = g_type_get_instance_count (type);
   cumulative += self;
 
-  gtk_graph_data_prepend_value (data->self, self);
-  gtk_graph_data_prepend_value (data->cumulative, cumulative);
+  type_data_update (data, self, cumulative);
+
+  g_object_unref (data);
 
-  gtk_list_store_set (GTK_LIST_STORE (sl->priv->model), &data->treeiter,
-                      COLUMN_SELF1, (int) gtk_graph_data_get_value (data->self, 1),
-                      COLUMN_CUMULATIVE1, (int) gtk_graph_data_get_value (data->cumulative, 1),
-                      COLUMN_SELF2, (int) gtk_graph_data_get_value (data->self, 0),
-                      COLUMN_CUMULATIVE2, (int) gtk_graph_data_get_value (data->cumulative, 0),
-                      -1);
   return cumulative;
 }
 
@@ -196,192 +356,449 @@ instance_counts_enabled (void)
 }
 
 static void
-cell_data_data (GtkCellLayout   *layout,
-                GtkCellRenderer *cell,
-                GtkTreeModel    *model,
-                GtkTreeIter     *iter,
-                gpointer         data)
+search_changed (GtkSearchEntry         *entry,
+                GtkInspectorStatistics *sl)
+{
+  const char *text;
+  GListModel *model;
+
+  text = gtk_editable_get_text (GTK_EDITABLE (entry));
+  model = gtk_single_selection_get_model (sl->priv->selection);
+
+  for (guint i = 0; i < g_list_model_get_n_items (model); i++)
+    {
+      TypeData *data = g_list_model_get_item (model, i);
+      char *string;
+
+      g_object_unref (data);
+
+      string = g_ascii_strdown (g_type_name (data->type), -1);
+      if (g_str_has_prefix (string, text))
+        {
+          g_free (string);
+          gtk_single_selection_set_selected (sl->priv->selection, i);
+          return;
+        }
+
+       g_free (string);
+    }
+
+  gtk_single_selection_set_selected (sl->priv->selection, GTK_INVALID_LIST_POSITION);
+}
+
+static void
+root (GtkWidget *widget)
+{
+  GtkInspectorStatistics *sl = GTK_INSPECTOR_STATISTICS (widget);
+  GtkWidget *toplevel;
+
+  GTK_WIDGET_CLASS (gtk_inspector_statistics_parent_class)->root (widget);
+
+  toplevel = GTK_WIDGET (gtk_widget_get_root (widget));
+
+  gtk_search_bar_set_key_capture_widget (GTK_SEARCH_BAR (sl->priv->search_bar), toplevel);
+}
+
+static void
+unroot (GtkWidget *widget)
+{
+  GTK_WIDGET_CLASS (gtk_inspector_statistics_parent_class)->unroot (widget);
+}
+
+static void
+setup_label (GtkSignalListItemFactory *factory,
+             GtkListItem              *list_item)
+{
+  GtkWidget *label;
+
+  label = gtk_label_new (NULL);
+  gtk_label_set_xalign (GTK_LABEL (label), 0.);
+  gtk_list_item_set_child (list_item, label);
+}
+
+static void
+bind_name (GtkSignalListItemFactory *factory,
+           GtkListItem              *list_item)
+{
+  GtkWidget *label;
+  TypeData *data;
+
+  data = gtk_list_item_get_item (list_item);
+  label = gtk_list_item_get_child (list_item);
+  gtk_label_set_text (GTK_LABEL (label), g_type_name (data->type));
+}
+
+static void
+set_self1 (TypeData   *data,
+           GParamSpec *pspec,
+           GtkWidget  *label)
 {
-  int column;
   int count;
   char *text;
 
-  column = GPOINTER_TO_INT (data);
+  g_object_get (data, "self1", &count, NULL);
+  text = g_strdup_printf ("%d", count);
+  gtk_label_set_text (GTK_LABEL (label), text);
+  g_free (text);
+}
+
+static void
+bind_self1 (GtkSignalListItemFactory *factory,
+            GtkListItem              *list_item)
+{
+  GtkWidget *label;
+  TypeData *data;
+
+  label = gtk_list_item_get_child (list_item);
+  data = gtk_list_item_get_item (list_item);
+
+  set_self1 (data, NULL, label);
+  g_signal_connect (data, "notify::self1", G_CALLBACK (set_self1), label);
+}
+
+static void
+unbind_self1 (GtkSignalListItemFactory *factory,
+              GtkListItem              *list_item)
+{
+  GtkWidget *label;
+  TypeData *data;
+
+  label = gtk_list_item_get_child (list_item);
+  data = gtk_list_item_get_item (list_item);
+
+  g_signal_handlers_disconnect_by_func (data, G_CALLBACK (set_self1), label);
+}
 
-  gtk_tree_model_get (model, iter, column, &count, -1);
+static void
+set_cumulative1 (TypeData   *data,
+                 GParamSpec *pspec,
+                 GtkWidget  *label)
+{
+  int count;
+  char *text;
 
+  g_object_get (data, "cumulative1", &count, NULL);
   text = g_strdup_printf ("%d", count);
-  g_object_set (cell, "text", text, NULL);
+  gtk_label_set_text (GTK_LABEL (label), text);
   g_free (text);
 }
 
 static void
-cell_data_delta (GtkCellLayout   *layout,
-                 GtkCellRenderer *cell,
-                 GtkTreeModel    *model,
-                 GtkTreeIter     *iter,
-                 gpointer         data)
+bind_cumulative1 (GtkSignalListItemFactory *factory,
+                  GtkListItem              *list_item)
+{
+  GtkWidget *label;
+  TypeData *data;
+
+  label = gtk_list_item_get_child (list_item);
+  data = gtk_list_item_get_item (list_item);
+
+  set_cumulative1 (data, NULL, label);
+  g_signal_connect (data, "notify::cumulative1", G_CALLBACK (set_cumulative1), label);
+}
+
+static void
+unbind_cumulative1 (GtkSignalListItemFactory *factory,
+                    GtkListItem              *list_item)
+{
+  GtkWidget *label;
+  TypeData *data;
+
+  label = gtk_list_item_get_child (list_item);
+  data = gtk_list_item_get_item (list_item);
+
+  g_signal_handlers_disconnect_by_func (data, G_CALLBACK (set_cumulative1), label);
+}
+
+static void
+set_self2 (TypeData   *data,
+           GParamSpec *pspec,
+           GtkWidget  *label)
 {
-  int column;
   int count1;
   int count2;
   char *text;
 
-  column = GPOINTER_TO_INT (data);
-
-  gtk_tree_model_get (model, iter, column - 2, &count1, column, &count2, -1);
-
+  g_object_get (data, "self1", &count1, NULL);
+  g_object_get (data, "self2", &count2, NULL);
   if (count2 > count1)
     text = g_strdup_printf ("%d (↗ %d)", count2, count2 - count1);
   else if (count2 < count1)
     text = g_strdup_printf ("%d (↘ %d)", count2, count1 - count2);
   else
     text = g_strdup_printf ("%d", count2);
-  g_object_set (cell, "text", text, NULL);
+  gtk_label_set_text (GTK_LABEL (label), text);
   g_free (text);
 }
 
 static void
-type_data_free (gpointer data)
+bind_self2 (GtkSignalListItemFactory *factory,
+            GtkListItem              *list_item)
 {
-  TypeData *type_data = data;
+  GtkWidget *label;
+  TypeData *data;
 
-  g_object_unref (type_data->self);
-  g_object_unref (type_data->cumulative);
+  label = gtk_list_item_get_child (list_item);
+  data = gtk_list_item_get_item (list_item);
 
-  g_free (type_data);
+  set_self2 (data, NULL, label);
+  g_signal_connect (data, "notify::self1", G_CALLBACK (set_self2), label);
+  g_signal_connect (data, "notify::self2", G_CALLBACK (set_self2), label);
 }
 
-static gboolean
-key_pressed (GtkEventController     *controller,
-             guint                   keyval,
-             guint                   keycode,
-             GdkModifierType         state,
-             GtkInspectorStatistics *sl)
+static void
+unbind_self2 (GtkSignalListItemFactory *factory,
+              GtkListItem              *list_item)
 {
-  if (gtk_widget_get_mapped (GTK_WIDGET (sl)))
-    {
-      if (keyval == GDK_KEY_Return ||
-          keyval == GDK_KEY_ISO_Enter ||
-          keyval == GDK_KEY_KP_Enter)
-        {
-          GtkTreeSelection *selection;
-          GtkTreeModel *model;
-          GtkTreeIter iter;
-          GtkTreePath *path;
-
-          selection = gtk_tree_view_get_selection (sl->priv->view);
-          if (gtk_tree_selection_get_selected (selection, &model, &iter))
-            {
-              path = gtk_tree_model_get_path (model, &iter);
-              gtk_tree_view_row_activated (sl->priv->view, path, NULL);
-              gtk_tree_path_free (path);
-
-              return GDK_EVENT_STOP;
-            }
-        }
-    }
+  GtkWidget *label;
+  TypeData *data;
+
+  label = gtk_list_item_get_child (list_item);
+  data = gtk_list_item_get_item (list_item);
 
-  return GDK_EVENT_PROPAGATE;
+  g_signal_handlers_disconnect_by_func (data, G_CALLBACK (set_self2), label);
 }
 
-static gboolean
-match_string (const char *string,
-              const char *text)
+static void
+set_cumulative2 (TypeData   *data,
+                 GParamSpec *pspec,
+                 GtkWidget  *label)
+{
+  int count1;
+  int count2;
+  char *text;
+
+  g_object_get (data, "cumulative1", &count1, NULL);
+  g_object_get (data, "cumulative2", &count2, NULL);
+  if (count2 > count1)
+    text = g_strdup_printf ("%d (↗ %d)", count2, count2 - count1);
+  else if (count2 < count1)
+    text = g_strdup_printf ("%d (↘ %d)", count2, count1 - count2);
+  else
+    text = g_strdup_printf ("%d", count2);
+  gtk_label_set_text (GTK_LABEL (label), text);
+  g_free (text);
+}
+
+static void
+bind_cumulative2 (GtkSignalListItemFactory *factory,
+                  GtkListItem              *list_item)
 {
-  char *lower;
-  gboolean match = FALSE;
+  GtkWidget *label;
+  TypeData *data;
 
-  if (string)
-    {
-      lower = g_ascii_strdown (string, -1);
-      match = g_str_has_prefix (lower, text);
-      g_free (lower);
-    }
+  label = gtk_list_item_get_child (list_item);
+  data = gtk_list_item_get_item (list_item);
 
-  return match;
+  set_cumulative2 (data, NULL, label);
+  g_signal_connect (data, "notify::cumulative1", G_CALLBACK (set_cumulative2), label);
+  g_signal_connect (data, "notify::cumulative2", G_CALLBACK (set_cumulative2), label);
 }
 
-static gboolean
-match_row (GtkTreeModel *model,
-           int           column,
-           const char   *key,
-           GtkTreeIter  *iter,
-           gpointer      data)
+static void
+unbind_cumulative2 (GtkSignalListItemFactory *factory,
+                    GtkListItem              *list_item)
 {
-  char *type;
-  gboolean match;
+  GtkWidget *label;
+  TypeData *data;
 
-  gtk_tree_model_get (model, iter, column, &type, -1);
+  label = gtk_list_item_get_child (list_item);
+  data = gtk_list_item_get_item (list_item);
 
-  match = match_string (type, key);
+  g_signal_handlers_disconnect_by_func (data, G_CALLBACK (set_cumulative2), label);
+}
 
-  g_free (type);
+static void
+setup_graph (GtkSignalListItemFactory *factory,
+             GtkListItem              *list_item)
+{
+  gtk_list_item_set_child (list_item, GTK_WIDGET (graph_renderer_new ()));
+}
 
-  return !match;
+static void
+set_graph_self (TypeData   *data,
+                GParamSpec *pspec,
+                GtkWidget  *graph)
+{
+  graph_renderer_set_data (GRAPH_RENDERER (graph), data->self);
 }
 
 static void
-destroy_controller (GtkEventController *controller)
+bind_graph_self (GtkSignalListItemFactory *factory,
+                 GtkListItem              *list_item)
 {
-  gtk_widget_remove_controller (gtk_event_controller_get_widget (controller), controller);
+  GtkWidget *graph;
+  TypeData *data;
+
+  data = gtk_list_item_get_item (list_item);
+  graph = gtk_list_item_get_child (list_item);
+
+  set_graph_self (data, NULL, graph);
+  g_signal_connect (data, "notify::self", G_CALLBACK (set_graph_self), graph);
 }
 
 static void
-root (GtkWidget *widget)
+unbind_graph_self (GtkSignalListItemFactory *factory,
+                   GtkListItem              *list_item)
 {
-  GtkInspectorStatistics *sl = GTK_INSPECTOR_STATISTICS (widget);
-  GtkEventController *controller;
-  GtkWidget *toplevel;
+  GtkWidget *graph;
+  TypeData *data;
 
-  GTK_WIDGET_CLASS (gtk_inspector_statistics_parent_class)->root (widget);
+  data = gtk_list_item_get_item (list_item);
+  graph = gtk_list_item_get_child (list_item);
 
-  toplevel = GTK_WIDGET (gtk_widget_get_root (widget));
+  g_signal_handlers_disconnect_by_func (data, G_CALLBACK (set_graph_self), graph);
+}
 
-  controller = gtk_event_controller_key_new ();
-  g_object_set_data_full (G_OBJECT (toplevel), "statistics-controller", controller, 
(GDestroyNotify)destroy_controller);
-  g_signal_connect (controller, "key-pressed", G_CALLBACK (key_pressed), widget);
-  gtk_widget_add_controller (toplevel, controller);
+static void
+set_graph_cumulative (TypeData   *data,
+                      GParamSpec *pspec,
+                      GtkWidget  *graph)
+{
+  graph_renderer_set_data (GRAPH_RENDERER (graph), data->cumulative);
+}
 
-  gtk_search_bar_set_key_capture_widget (GTK_SEARCH_BAR (sl->priv->search_bar), toplevel);
+static void
+bind_graph_cumulative (GtkSignalListItemFactory *factory,
+                       GtkListItem              *list_item)
+{
+  GtkWidget *graph;
+  TypeData *data;
+
+  data = gtk_list_item_get_item (list_item);
+  graph = gtk_list_item_get_child (list_item);
+
+  set_graph_cumulative (data, NULL, graph);
+  g_signal_connect (data, "notify::cumulative", G_CALLBACK (set_graph_cumulative), graph);
 }
 
 static void
-unroot (GtkWidget *widget)
+unbind_graph_cumulative (GtkSignalListItemFactory *factory,
+                         GtkListItem              *list_item)
 {
-  GtkWidget *toplevel;
+  GtkWidget *graph;
+  TypeData *data;
 
-  toplevel = GTK_WIDGET (gtk_widget_get_root (widget));
-  g_object_set_data (G_OBJECT (toplevel), "statistics-controller", NULL);
+  data = gtk_list_item_get_item (list_item);
+  graph = gtk_list_item_get_child (list_item);
 
-  GTK_WIDGET_CLASS (gtk_inspector_statistics_parent_class)->unroot (widget);
+  g_signal_handlers_disconnect_by_func (data, G_CALLBACK (set_graph_cumulative), graph);
 }
 
 static void
 gtk_inspector_statistics_init (GtkInspectorStatistics *sl)
 {
+  GtkColumnViewColumn *column;
+  GtkListItemFactory *factory;
+  GtkSorter *sorter;
+  GtkSortListModel *sort_model;
+
   sl->priv = gtk_inspector_statistics_get_instance_private (sl);
   gtk_widget_init_template (GTK_WIDGET (sl));
-  gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (sl->priv->column_self1),
-                                      sl->priv->renderer_self1,
-                                      cell_data_data,
-                                      GINT_TO_POINTER (COLUMN_SELF1), NULL);
-  gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (sl->priv->column_cumulative1),
-                                      sl->priv->renderer_cumulative1,
-                                      cell_data_data,
-                                      GINT_TO_POINTER (COLUMN_CUMULATIVE1), NULL);
-  gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (sl->priv->column_self2),
-                                      sl->priv->renderer_self2,
-                                      cell_data_delta,
-                                      GINT_TO_POINTER (COLUMN_SELF2), NULL);
-  gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (sl->priv->column_cumulative2),
-                                      sl->priv->renderer_cumulative2,
-                                      cell_data_delta,
-                                      GINT_TO_POINTER (COLUMN_CUMULATIVE2), NULL);
-  sl->priv->counts = g_hash_table_new_full (NULL, NULL, NULL, type_data_free);
-
-  gtk_tree_view_set_search_entry (sl->priv->view, GTK_EDITABLE (sl->priv->search_entry));
-  gtk_tree_view_set_search_equal_func (sl->priv->view, match_row, sl, NULL);
+  sl->priv->types = g_hash_table_new (NULL, NULL);
+
+  sl->priv->data = g_list_store_new (type_data_get_type ());
+
+  sort_model = gtk_sort_list_model_new (G_LIST_MODEL (sl->priv->data),
+                                        g_object_ref (gtk_column_view_get_sorter (GTK_COLUMN_VIEW 
(sl->priv->view))));
+
+  sl->priv->selection = gtk_single_selection_new (G_LIST_MODEL (sort_model));
+  gtk_single_selection_set_can_unselect (sl->priv->selection, TRUE);
+
+  gtk_column_view_set_model (GTK_COLUMN_VIEW (sl->priv->view), GTK_SELECTION_MODEL (sl->priv->selection));
+
+  g_object_unref (sl->priv->selection);
+
+  column = g_list_model_get_item (gtk_column_view_get_columns (GTK_COLUMN_VIEW (sl->priv->view)), 0);
+
+  factory = gtk_signal_list_item_factory_new ();
+  g_signal_connect (factory, "setup", G_CALLBACK (setup_label), NULL);
+  g_signal_connect (factory, "bind", G_CALLBACK (bind_name), NULL);
+
+  gtk_column_view_column_set_factory (column, factory);
+  sorter = GTK_SORTER (gtk_string_sorter_new (gtk_property_expression_new (type_data_get_type (), NULL, 
"name")));
+  gtk_column_view_column_set_sorter (column, sorter);
+  g_object_unref (sorter);
+  g_object_unref (factory);
+  g_object_unref (column);
+
+  column = g_list_model_get_item (gtk_column_view_get_columns (GTK_COLUMN_VIEW (sl->priv->view)), 1);
+
+  factory = gtk_signal_list_item_factory_new ();
+  g_signal_connect (factory, "setup", G_CALLBACK (setup_label), NULL);
+  g_signal_connect (factory, "bind", G_CALLBACK (bind_self1), NULL);
+  g_signal_connect (factory, "unbind", G_CALLBACK (unbind_self1), NULL);
+
+  gtk_column_view_column_set_factory (column, factory);
+  sorter = GTK_SORTER (gtk_numeric_sorter_new (gtk_property_expression_new (type_data_get_type (), NULL, 
"self1")));
+  gtk_column_view_column_set_sorter (column, sorter);
+  g_object_unref (sorter);
+  g_object_unref (factory);
+  g_object_unref (column);
+
+  column = g_list_model_get_item (gtk_column_view_get_columns (GTK_COLUMN_VIEW (sl->priv->view)), 2);
+
+  factory = gtk_signal_list_item_factory_new ();
+  g_signal_connect (factory, "setup", G_CALLBACK (setup_label), NULL);
+  g_signal_connect (factory, "bind", G_CALLBACK (bind_cumulative1), NULL);
+  g_signal_connect (factory, "unbind", G_CALLBACK (unbind_cumulative1), NULL);
+
+  gtk_column_view_column_set_factory (column, factory);
+  sorter = GTK_SORTER (gtk_numeric_sorter_new (gtk_property_expression_new (type_data_get_type (), NULL, 
"cumulative1")));
+  gtk_column_view_column_set_sorter (column, sorter);
+  g_object_unref (sorter);
+  g_object_unref (factory);
+  g_object_unref (column);
+
+  column = g_list_model_get_item (gtk_column_view_get_columns (GTK_COLUMN_VIEW (sl->priv->view)), 3);
+
+  factory = gtk_signal_list_item_factory_new ();
+  g_signal_connect (factory, "setup", G_CALLBACK (setup_label), NULL);
+  g_signal_connect (factory, "bind", G_CALLBACK (bind_self2), NULL);
+  g_signal_connect (factory, "unbind", G_CALLBACK (unbind_self2), NULL);
+
+  gtk_column_view_column_set_factory (column, factory);
+  sorter = GTK_SORTER (gtk_numeric_sorter_new (gtk_property_expression_new (type_data_get_type (), NULL, 
"self2")));
+  gtk_column_view_column_set_sorter (column, sorter);
+  g_object_unref (sorter);
+  g_object_unref (factory);
+  g_object_unref (column);
+
+  column = g_list_model_get_item (gtk_column_view_get_columns (GTK_COLUMN_VIEW (sl->priv->view)), 4);
+
+  factory = gtk_signal_list_item_factory_new ();
+  g_signal_connect (factory, "setup", G_CALLBACK (setup_label), NULL);
+  g_signal_connect (factory, "bind", G_CALLBACK (bind_cumulative2), NULL);
+  g_signal_connect (factory, "unbind", G_CALLBACK (unbind_cumulative2), NULL);
+
+  gtk_column_view_column_set_factory (column, factory);
+  sorter = GTK_SORTER (gtk_numeric_sorter_new (gtk_property_expression_new (type_data_get_type (), NULL, 
"cumulative2")));
+  gtk_column_view_column_set_sorter (column, sorter);
+  g_object_unref (sorter);
+  g_object_unref (factory);
+  g_object_unref (column);
+
+  column = g_list_model_get_item (gtk_column_view_get_columns (GTK_COLUMN_VIEW (sl->priv->view)), 5);
+
+  factory = gtk_signal_list_item_factory_new ();
+  g_signal_connect (factory, "setup", G_CALLBACK (setup_graph), NULL);
+  g_signal_connect (factory, "bind", G_CALLBACK (bind_graph_self), NULL);
+  g_signal_connect (factory, "unbind", G_CALLBACK (unbind_graph_self), NULL);
+
+  gtk_column_view_column_set_factory (column, factory);
+  g_object_unref (factory);
+  g_object_unref (column);
+
+  column = g_list_model_get_item (gtk_column_view_get_columns (GTK_COLUMN_VIEW (sl->priv->view)), 6);
+
+  factory = gtk_signal_list_item_factory_new ();
+  g_signal_connect (factory, "setup", G_CALLBACK (setup_graph), NULL);
+  g_signal_connect (factory, "bind", G_CALLBACK (bind_graph_cumulative), NULL);
+  g_signal_connect (factory, "unbind", G_CALLBACK (unbind_graph_cumulative), NULL);
+
+  gtk_column_view_column_set_factory (column, factory);
+  g_object_unref (factory);
+  g_object_unref (column);
 }
 
 static void
@@ -389,8 +806,7 @@ constructed (GObject *object)
 {
   GtkInspectorStatistics *sl = GTK_INSPECTOR_STATISTICS (object);
 
-  g_signal_connect (sl->priv->button, "toggled",
-                    G_CALLBACK (toggle_record), sl);
+  g_signal_connect (sl->priv->button, "toggled", G_CALLBACK (toggle_record), sl);
 
   if (has_instance_counts ())
     update_type_counts (sl);
@@ -411,7 +827,7 @@ finalize (GObject *object)
   if (sl->priv->update_source_id)
     g_source_remove (sl->priv->update_source_id);
 
-  g_hash_table_unref (sl->priv->counts);
+  g_hash_table_unref (sl->priv->types);
 
   G_OBJECT_CLASS (gtk_inspector_statistics_parent_class)->finalize (object);
 }
@@ -477,19 +893,10 @@ gtk_inspector_statistics_class_init (GtkInspectorStatisticsClass *klass)
   gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/inspector/statistics.ui");
   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, view);
   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, stack);
-  gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, model);
-  gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, column_self1);
-  gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, renderer_self1);
-  gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, column_cumulative1);
-  gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, renderer_cumulative1);
-  gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, column_self2);
-  gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, renderer_self2);
-  gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, column_cumulative2);
-  gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, renderer_cumulative2);
   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, search_entry);
   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, search_bar);
   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, excuse);
-
+  gtk_widget_class_bind_template_callback (widget_class, search_changed);
 }
 
-// vim: set et sw=2 ts=2:
+/* vim:set foldmethod=marker expandtab: */
diff --git a/gtk/inspector/statistics.ui b/gtk/inspector/statistics.ui
index b081e3452d..5ce17f52fe 100644
--- a/gtk/inspector/statistics.ui
+++ b/gtk/inspector/statistics.ui
@@ -1,16 +1,4 @@
 <interface domain="gtk40">
-  <object class="GtkListStore" id="model">
-    <columns>
-      <column type="GType"/>
-      <column type="gchararray"/>
-      <column type="gint"/>
-      <column type="gint"/>
-      <column type="gint"/>
-      <column type="gint"/>
-      <column type="GtkGraphData"/>
-      <column type="GtkGraphData"/>
-    </columns>
-  </object>
   <template class="GtkInspectorStatistics" parent="GtkBox">
     <property name="orientation">vertical</property>
     <child>
@@ -27,6 +15,7 @@
                     <child>
                       <object class="GtkSearchEntry" id="search_entry">
                         <property name="max-width-chars">40</property>
+                        <signal name="search-changed" handler="search_changed"/>
                       </object>
                     </child>
                   </object>
@@ -37,97 +26,45 @@
                     <property name="vexpand">1</property>
                     <property name="vscrollbar-policy">always</property>
                     <child>
-                      <object class="GtkTreeView" id="view">
-                        <property name="model">model</property>
-                        <property name="search-column">1</property>
+                      <object class="GtkColumnView" id="view">
+                        <style>
+                          <class name="data-table"/>
+                          <class name="list"/>
+                        </style>
                         <child>
-                          <object class="GtkTreeViewColumn">
-                            <property name="sort-column-id">1</property>
+                          <object class="GtkColumnViewColumn">
                             <property name="title" translatable="yes">Type</property>
-                            <child>
-                              <object class="GtkCellRendererText">
-                                <property name="scale">0.8</property>
-                              </object>
-                              <attributes>
-                                <attribute name="text">1</attribute>
-                              </attributes>
-                            </child>
                           </object>
                         </child>
                         <child>
-                          <object class="GtkTreeViewColumn" id="column_self1">
-                            <property name="sort-column-id">2</property>
+                          <object class="GtkColumnViewColumn" id="column_self1">
                             <property name="title" translatable="yes">Self 1</property>
-                            <child>
-                              <object class="GtkCellRendererText" id="renderer_self1">
-                                <property name="scale">0.8</property>
-                              </object>
-                            </child>
                           </object>
                         </child>
                         <child>
-                          <object class="GtkTreeViewColumn" id="column_cumulative1">
-                            <property name="sort-column-id">3</property>
+                          <object class="GtkColumnViewColumn" id="column_cumulative1">
                             <property name="title" translatable="yes">Cumulative 1</property>
-                            <child>
-                              <object class="GtkCellRendererText" id="renderer_cumulative1">
-                                <property name="scale">0.8</property>
-                              </object>
-                            </child>
                           </object>
                         </child>
                         <child>
-                          <object class="GtkTreeViewColumn" id="column_self2">
-                            <property name="sort-column-id">4</property>
+                          <object class="GtkColumnViewColumn" id="column_self2">
                             <property name="title" translatable="yes">Self 2</property>
-                            <child>
-                              <object class="GtkCellRendererText" id="renderer_self2">
-                                <property name="scale">0.8</property>
-                              </object>
-                            </child>
                           </object>
                         </child>
                         <child>
-                          <object class="GtkTreeViewColumn" id="column_cumulative2">
-                            <property name="sort-column-id">5</property>
+                          <object class="GtkColumnViewColumn" id="column_cumulative2">
                             <property name="title" translatable="yes">Cumulative 2</property>
-                            <child>
-                              <object class="GtkCellRendererText" id="renderer_cumulative2">
-                                <property name="scale">0.8</property>
-                              </object>
-                            </child>
                           </object>
                         </child>
                         <child>
-                          <object class="GtkTreeViewColumn" id="column_self_graph">
-                            <property name="sort-column-id">4</property>
+                          <object class="GtkColumnViewColumn" id="column_self_graph">
                             <property name="title" translatable="yes">Self</property>
-                            <child>
-                              <object class="GtkCellRendererGraph" id="renderer_self_graph">
-                                <property name="minimum">0</property>
-                                <property name="xpad">1</property>
-                                <property name="ypad">1</property>
-                              </object>
-                              <attributes>
-                                <attribute name="data">6</attribute>
-                              </attributes>
-                            </child>
                           </object>
                         </child>
                         <child>
-                          <object class="GtkTreeViewColumn" id="column_cumulative_graph">
-                            <property name="sort-column-id">5</property>
+                          <object class="GtkColumnViewColumn" id="column_cumulative_graph">
                             <property name="title" translatable="yes">Cumulative</property>
-                            <child>
-                              <object class="GtkCellRendererGraph" id="renderer_cumulative_graph">
-                                <property name="minimum">0</property>
-                                <property name="xpad">1</property>
-                                <property name="ypad">1</property>
-                              </object>
-                              <attributes>
-                                <attribute name="data">7</attribute>
-                              </attributes>
-                            </child>
+                            <property name="expand">1</property>
                           </object>
                         </child>
                       </object>


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