[gtk: 1/2] tests: Add testdatatable




commit 53f3b4b9d9488dedfaf46567da9234df0b1343fa
Author: Ivan Molodetskikh <yalterz gmail com>
Date:   Tue Jun 28 21:46:21 2022 -0700

    tests: Add testdatatable
    
    Add a GtkColumnView scrolling performance test similar to the one used
    previously in https://gitlab.gnome.org/GNOME/gtk/-/issues/3334.
    
    The test creates a table with 20 columns and 10,000 rows and scrolls it
    to a random position every frame, while measuring the frame times.
    
    There is a commandline flag to pick the cell widget between none (for
    benchmarking raw column view scrolling) and various label types. There
    is also a commandline switch to disable automatic scrolling in case a
    manual assessment is desired. Finally, there's an argument for
    controlling the number of columns.

 tests/meson.build     |   3 +-
 tests/testdatatable.c | 297 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 299 insertions(+), 1 deletion(-)
---
diff --git a/tests/meson.build b/tests/meson.build
index d837b5af6d..50f8e29a1b 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -116,7 +116,8 @@ gtk_tests = [
   ['testtexture'],
   ['testwindowdrag'],
   ['testinhibitshortcuts'],
-  ['testzoom']
+  ['testzoom'],
+  ['testdatatable', ['frame-stats.c', 'variable.c']]
 ]
 
 if os_unix
diff --git a/tests/testdatatable.c b/tests/testdatatable.c
new file mode 100644
index 0000000000..bb9f86dc4f
--- /dev/null
+++ b/tests/testdatatable.c
@@ -0,0 +1,297 @@
+/* -*- mode: C; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
+
+#include <gtk/gtk.h>
+
+#include "frame-stats.h"
+
+
+/* This is our dummy item for the model. */
+#define DATA_TABLE_TYPE_ITEM (data_table_item_get_type ())
+G_DECLARE_FINAL_TYPE (DataTableItem, data_table_item, DATA_TABLE, ITEM, GObject)
+
+struct _DataTableItem
+{
+  GObject parent_instance;
+  int data;
+};
+
+struct _DataTableItemClass
+{
+  GObjectClass parent_class;
+};
+
+G_DEFINE_TYPE (DataTableItem, data_table_item, G_TYPE_OBJECT)
+
+static void data_table_item_init (DataTableItem *item) {}
+
+static void data_table_item_class_init (DataTableItemClass *class) {}
+
+static DataTableItem *
+data_table_item_new (int data)
+{
+  DataTableItem *item = g_object_new (DATA_TABLE_TYPE_ITEM, NULL);
+  item->data = data;
+  return item;
+}
+
+
+static void
+set_adjustment_to_fraction (GtkAdjustment *adjustment,
+                            double         fraction)
+{
+  double upper = gtk_adjustment_get_upper (adjustment);
+  double lower = gtk_adjustment_get_lower (adjustment);
+  double page_size = gtk_adjustment_get_page_size (adjustment);
+
+  gtk_adjustment_set_value (adjustment,
+                            (1 - fraction) * lower +
+                            fraction * (upper - page_size));
+}
+
+static gboolean
+scroll_column_view (GtkWidget     *column_view,
+                    GdkFrameClock *frame_clock,
+                    gpointer       user_data)
+{
+  GtkAdjustment *vadjustment;
+
+  vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (column_view));
+
+  set_adjustment_to_fraction (vadjustment, g_random_double ());
+
+  return TRUE;
+}
+
+enum WidgetType
+{
+  WIDGET_TYPE_NONE,
+  WIDGET_TYPE_LABEL,
+  WIDGET_TYPE_TEXT,
+  WIDGET_TYPE_INSCRIPTION,
+};
+
+static enum WidgetType widget_type = WIDGET_TYPE_INSCRIPTION;
+
+static void
+setup (GtkSignalListItemFactory *factory,
+       GObject                  *listitem)
+{
+  GtkWidget *widget;
+
+  switch (widget_type)
+    {
+    case WIDGET_TYPE_NONE:
+      /* It's actually a box, just to request size similar to labels. */
+      widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+      gtk_widget_set_size_request (widget, 50, 18);
+      break;
+    case WIDGET_TYPE_LABEL:
+      widget = gtk_label_new ("");
+      break;
+    case WIDGET_TYPE_TEXT:
+      widget = gtk_text_new ();
+      break;
+    case WIDGET_TYPE_INSCRIPTION:
+      widget = gtk_inscription_new ("");
+      gtk_inscription_set_min_chars (GTK_INSCRIPTION (widget), 6);
+      break;
+    default:
+      g_assert_not_reached ();
+    }
+
+  gtk_list_item_set_child (GTK_LIST_ITEM (listitem), widget);
+}
+
+static void
+bind (GtkSignalListItemFactory *factory,
+      GObject                  *listitem,
+      gpointer                  name)
+{
+  GtkWidget *widget;
+  GObject *item;
+
+  widget = gtk_list_item_get_child (GTK_LIST_ITEM (listitem));
+  item = gtk_list_item_get_item (GTK_LIST_ITEM (listitem));
+
+  char buffer[16] = { 0, };
+  g_snprintf (buffer,
+              sizeof (buffer),
+              "%c%d",
+              GPOINTER_TO_INT (name),
+              DATA_TABLE_ITEM (item)->data);
+
+  switch (widget_type)
+    {
+    case WIDGET_TYPE_NONE:
+      break;
+    case WIDGET_TYPE_LABEL:
+      gtk_label_set_label (GTK_LABEL (widget), buffer);
+      break;
+    case WIDGET_TYPE_TEXT:
+      gtk_editable_set_text (GTK_EDITABLE (widget), buffer);
+      break;
+    case WIDGET_TYPE_INSCRIPTION:
+      gtk_inscription_set_text (GTK_INSCRIPTION (widget), buffer);
+      break;
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static gboolean
+parse_widget_arg (const gchar* option_name,
+                  const gchar* value,
+                  gpointer data,
+                  GError** error)
+{
+  if (!g_strcmp0 (value, "none"))
+    {
+      widget_type = WIDGET_TYPE_NONE;
+      return TRUE;
+    }
+  else if (!g_strcmp0 (value, "label"))
+    {
+      widget_type = WIDGET_TYPE_LABEL;
+      return TRUE;
+    }
+  else if (!g_strcmp0 (value, "text"))
+    {
+      widget_type = WIDGET_TYPE_TEXT;
+      return TRUE;
+    }
+  else if (!g_strcmp0 (value, "inscription"))
+    {
+      widget_type = WIDGET_TYPE_INSCRIPTION;
+      return TRUE;
+    }
+  else
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+                           "Invalid option value");
+      return FALSE;
+    }
+}
+
+static gboolean no_auto_scroll = FALSE;
+static gint n_columns = 20;
+
+static GOptionEntry options[] = {
+  {
+    "widget",
+    'w',
+    G_OPTION_FLAG_NONE,
+    G_OPTION_ARG_CALLBACK,
+    parse_widget_arg,
+    "Cell item widget to use, can be one of: none, label, text, inscription",
+    "WIDGET"
+  },
+  {
+    "no-auto-scroll",
+    'n',
+    G_OPTION_FLAG_NONE,
+    G_OPTION_ARG_NONE,
+    &no_auto_scroll,
+    "Disable automatic scrolling",
+    NULL
+  },
+  {
+    "columns",
+    'c',
+    G_OPTION_FLAG_NONE,
+    G_OPTION_ARG_INT,
+    &n_columns,
+    "Column count",
+    "COUNT"
+  },
+  { NULL }
+};
+
+static void
+quit_cb (GtkWidget *widget,
+         gpointer   data)
+{
+  gboolean *done = data;
+
+  *done = TRUE;
+
+  g_main_context_wakeup (NULL);
+}
+
+int
+main (int argc, char **argv)
+{
+  GtkWidget *window;
+  GtkWidget *scrolled_window;
+  GListStore *store;
+  int i;
+  GtkMultiSelection *multi_selection;
+  GtkWidget *column_view;
+  GError *error = NULL;
+  gboolean done = FALSE;
+
+  GOptionContext *context = g_option_context_new (NULL);
+  g_option_context_add_main_entries (context, options, NULL);
+  frame_stats_add_options (g_option_context_get_main_group (context));
+
+  if (!g_option_context_parse (context, &argc, &argv, &error))
+    {
+      g_printerr ("Option parsing failed: %s\n", error->message);
+      return 1;
+    }
+
+  gtk_init ();
+
+  window = gtk_window_new ();
+  frame_stats_ensure (GTK_WINDOW (window));
+  gtk_window_set_default_size (GTK_WINDOW (window), 1700, 900);
+
+  scrolled_window = gtk_scrolled_window_new ();
+  gtk_window_set_child (GTK_WINDOW (window), scrolled_window);
+
+  store = g_list_store_new (DATA_TABLE_TYPE_ITEM);
+  for (i = 0; i < 10000; ++i)
+    {
+      DataTableItem *item = data_table_item_new (i);
+      g_list_store_append (store, item);
+      g_object_unref (item);
+    }
+
+  multi_selection = gtk_multi_selection_new (G_LIST_MODEL (store));
+  column_view = gtk_column_view_new (GTK_SELECTION_MODEL (multi_selection));
+
+  gtk_column_view_set_show_column_separators (GTK_COLUMN_VIEW (column_view), TRUE);
+  gtk_column_view_set_show_row_separators (GTK_COLUMN_VIEW (column_view), TRUE);
+  gtk_widget_add_css_class (column_view, "data-table");
+
+  for (i = 0; i < MIN (n_columns, 127 - 65); ++i)
+    {
+      const char name[] = { 'A' + i, '\0' };
+
+      GtkListItemFactory *factory = gtk_signal_list_item_factory_new ();
+      g_signal_connect (factory, "setup", G_CALLBACK (setup), NULL);
+      g_signal_connect (factory, "bind", G_CALLBACK (bind), GINT_TO_POINTER (name[0]));
+
+      gtk_column_view_append_column (GTK_COLUMN_VIEW (column_view),
+                                     gtk_column_view_column_new (name, factory));
+    }
+
+  gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolled_window),
+                                 column_view);
+
+  if (!no_auto_scroll)
+    {
+      gtk_widget_add_tick_callback (column_view,
+                                    scroll_column_view,
+                                    NULL,
+                                    NULL);
+    }
+
+  gtk_widget_show (window);
+  g_signal_connect (window, "destroy",
+                    G_CALLBACK (quit_cb), &done);
+
+  while (!done)
+    g_main_context_iteration (NULL, TRUE);
+
+  return 0;
+}


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