testsections: A testbed for sections
authorMatthias Clasen <mclasen@redhat.com>
Wed, 17 May 2023 20:27:06 +0000 (16:27 -0400)
committerMatthias Clasen <mclasen@redhat.com>
Sun, 28 May 2023 01:30:14 +0000 (21:30 -0400)
Add a simple test client that lets us compare
and explore the section handling in listview
and gridview.

tests/meson.build
tests/testsections.c [new file with mode: 0644]

index 3294b47d0800b91ea623cad981d7f0c5abea053c..3552c2199091233639e8ff9f3a104e56ee975f73 100644 (file)
@@ -1,5 +1,6 @@
 gtk_tests = [
   # testname, optional extra sources
+  ['testsections'],
   ['testfilelauncher'],
   ['input'],
   ['testpopup'],
diff --git a/tests/testsections.c b/tests/testsections.c
new file mode 100644 (file)
index 0000000..8ca03d5
--- /dev/null
@@ -0,0 +1,389 @@
+#include <gtk/gtk.h>
+
+static void
+setup_item (GtkSignalListItemFactory *self,
+            GObject                  *object)
+{
+  GtkListItem *list_item = GTK_LIST_ITEM (object);
+  GtkWidget *child = gtk_label_new ("");
+
+  gtk_label_set_xalign (GTK_LABEL (child), 0);
+  gtk_list_item_set_child (list_item, child);
+}
+
+static void
+bind_item (GtkSignalListItemFactory *self,
+           GObject                  *object)
+{
+  GtkListItem *list_item = GTK_LIST_ITEM (object);
+  GObject *item = gtk_list_item_get_item (list_item);
+  GtkWidget *child = gtk_list_item_get_child (list_item);
+
+  gtk_label_set_label (GTK_LABEL (child),
+                       gtk_string_object_get_string (GTK_STRING_OBJECT (item)));
+}
+
+static char *
+reverse_word (const char *word)
+{
+  GString *s = g_string_new ("");
+  const char *p;
+  gunichar c;
+  gboolean capitalize;
+
+  capitalize = g_unichar_isupper (g_utf8_get_char (word));
+
+  p = word + strlen (word);
+  while ((p = g_utf8_find_prev_char (word, p)) != NULL)
+    {
+      c = g_utf8_get_char (p);
+
+      if (s->len == 0 && capitalize)
+        c = g_unichar_toupper (c);
+      else
+        c = g_unichar_tolower (c);
+
+      g_string_append_unichar (s, c);
+    }
+
+  return g_string_free (s, FALSE);
+}
+
+static void
+bind_item_reverse (GtkSignalListItemFactory *self,
+                   GObject                  *object)
+{
+  GtkListItem *list_item = GTK_LIST_ITEM (object);
+  GObject *item = gtk_list_item_get_item (list_item);
+  GtkWidget *child = gtk_list_item_get_child (list_item);
+  char *word;
+
+  word = reverse_word (gtk_string_object_get_string (GTK_STRING_OBJECT (item)));
+  gtk_label_set_label (GTK_LABEL (child), word);
+  g_free (word);
+}
+
+static void
+setup_header (GtkSignalListItemFactory *self,
+              GObject                  *object)
+{
+  GtkListHeader *header = GTK_LIST_HEADER (object);
+  GtkWidget *child = gtk_label_new ("");
+
+  gtk_label_set_xalign (GTK_LABEL (child), 0);
+  gtk_list_header_set_child (header, child);
+}
+
+static char *
+get_first (GObject *this)
+{
+  const char *s = gtk_string_object_get_string (GTK_STRING_OBJECT (this));
+  char buffer[6] = { 0, };
+
+  g_unichar_to_utf8 (g_unichar_toupper (g_utf8_get_char (s)), buffer);
+
+  return g_strdup (buffer);
+}
+
+static void
+bind_header (GtkSignalListItemFactory *self,
+             GObject                  *object)
+{
+  GtkListHeader *header = GTK_LIST_HEADER (object);
+  GObject *item = gtk_list_header_get_item (header);
+  GtkWidget *child = gtk_list_header_get_child (header);
+  PangoAttrList *attrs;
+  char *string;
+
+  string = get_first (item);
+
+  gtk_label_set_label (GTK_LABEL (child), string);
+  attrs = pango_attr_list_new ();
+  pango_attr_list_insert (attrs, pango_attr_scale_new (PANGO_SCALE_X_LARGE));
+  pango_attr_list_insert (attrs, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
+  gtk_label_set_attributes (GTK_LABEL (child), attrs);
+  pango_attr_list_unref (attrs);
+  g_free (string);
+}
+
+static const char *strings[] = {
+  "Alpha", "Andromeda", "Anaphylaxis", "Anaheim", "Beer", "Branch", "Botulism", "Banana",
+  "Bee", "Crane", "Caldera", "Copper", "Crowd", "Dora", "Dolphin", "Dam", "Ding",
+ NULL,
+};
+
+gboolean done_reading = FALSE;
+
+static gboolean
+dump_sections (gpointer data)
+{
+  GtkSectionModel *model = data;
+
+  if (!done_reading)
+    return G_SOURCE_CONTINUE;
+
+  for (unsigned int i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (model)); i++)
+    {
+      unsigned int s, e;
+      gtk_section_model_get_section (model, i, &s, &e);
+      g_print ("(%u %u)\n", s, e - 1);
+      i = e;
+    }
+
+  return G_SOURCE_REMOVE;
+}
+
+
+static void
+read_lines_cb (GObject      *object,
+               GAsyncResult *result,
+               gpointer      data)
+{
+  GBufferedInputStream *stream = G_BUFFERED_INPUT_STREAM (object);
+  GtkStringList *stringlist = data;
+  GError *error = NULL;
+  gsize size;
+  GPtrArray *lines;
+  gssize n_filled;
+  const char *buffer, *newline;
+
+  n_filled = g_buffered_input_stream_fill_finish (stream, result, &error);
+  if (n_filled < 0)
+    {
+      g_print ("Could not read data: %s\n", error->message);
+      g_clear_error (&error);
+      g_object_unref (stringlist);
+      return;
+    }
+
+  buffer = g_buffered_input_stream_peek_buffer (stream, &size);
+
+  if (n_filled == 0)
+    {
+      if (size)
+        gtk_string_list_take (stringlist, g_utf8_make_valid (buffer, size));
+      g_object_unref (stringlist);
+      done_reading = TRUE;
+      return;
+    }
+
+  lines = NULL;
+  while ((newline = memchr (buffer, '\n', size)))
+    {
+      if (newline > buffer)
+        {
+          if (lines == NULL)
+            lines = g_ptr_array_new_with_free_func (g_free);
+          g_ptr_array_add (lines, g_utf8_make_valid (buffer, newline - buffer));
+        }
+      if (g_input_stream_skip (G_INPUT_STREAM (stream), newline - buffer + 1, NULL, &error) < 0)
+        {
+          g_clear_error (&error);
+          break;
+        }
+      buffer = g_buffered_input_stream_peek_buffer (stream, &size);
+    }
+  if (lines == NULL)
+    {
+      g_buffered_input_stream_set_buffer_size (stream, g_buffered_input_stream_get_buffer_size (stream) + 4096);
+    }
+  else
+    {
+      g_ptr_array_add (lines, NULL);
+      gtk_string_list_splice (stringlist, g_list_model_get_n_items (G_LIST_MODEL (stringlist)), 0, (const char **) lines->pdata);
+      g_ptr_array_free (lines, TRUE);
+    }
+
+  g_buffered_input_stream_fill_async (stream, -1, G_PRIORITY_HIGH_IDLE, NULL, read_lines_cb, data);
+}
+
+static void
+file_is_open_cb (GObject      *file,
+                 GAsyncResult *result,
+                 gpointer      data)
+{
+  GError *error = NULL;
+  GFileInputStream *file_stream;
+  GBufferedInputStream *stream;
+
+  file_stream = g_file_read_finish (G_FILE (file), result, &error);
+  if (file_stream == NULL)
+    {
+      g_print ("Could not open file: %s\n", error->message);
+      g_error_free (error);
+      g_object_unref (data);
+      return;
+    }
+
+  stream = G_BUFFERED_INPUT_STREAM (g_buffered_input_stream_new (G_INPUT_STREAM (file_stream)));
+  g_buffered_input_stream_fill_async (stream, -1, G_PRIORITY_HIGH_IDLE, NULL, read_lines_cb, data);
+  g_object_unref (stream);
+}
+
+static void
+load_file (GtkStringList *list,
+           GFile         *file)
+{
+  gtk_string_list_splice (list, 0, g_list_model_get_n_items (G_LIST_MODEL (list)), NULL);
+  g_file_read_async (file, G_PRIORITY_HIGH_IDLE, NULL, file_is_open_cb, g_object_ref (list));
+}
+
+static void
+toggle_cb (GtkCheckButton *check, GtkWidget *list)
+{
+  GtkListItemFactory *header_factory = NULL;
+
+  if (gtk_check_button_get_active (check))
+    {
+      header_factory = gtk_signal_list_item_factory_new ();
+      g_signal_connect (header_factory, "setup", G_CALLBACK (setup_header), NULL);
+      g_signal_connect (header_factory, "bind", G_CALLBACK (bind_header), NULL);
+    }
+
+  g_object_set (list, "header-factory", header_factory, NULL);
+
+  g_clear_object (&header_factory);
+}
+
+static void
+value_changed_cb (GtkAdjustment *adj, gpointer data)
+{
+  g_print ("horizontal adjustment changed to %f\n",  gtk_adjustment_get_value (adj));
+}
+
+int
+main (int argc, char *argv[])
+{
+  GtkWidget *window;
+  GtkWidget *sw;
+  GtkWidget *lv;
+  GtkWidget *gv;
+  GtkWidget *cv;
+  GtkWidget *header;
+  GtkWidget *toggle;
+  GtkWidget *switcher;
+  GtkWidget *stack;
+  GtkListItemFactory *factory;
+  GtkExpression *expression;
+  GtkSortListModel *sortmodel;
+  GtkSelectionModel *selection;
+  GtkStringList *stringlist;
+  GtkAdjustment *adj;
+  GtkColumnViewColumn *column;
+
+  stringlist = gtk_string_list_new (NULL);
+
+ if (argc > 1)
+    {
+      GFile *file = g_file_new_for_commandline_arg (argv[1]);
+      load_file (stringlist, file);
+      g_object_unref (file);
+    }
+  else
+    {
+      for (int i = 0; strings[i]; i++)
+        gtk_string_list_append (stringlist, strings[i]);
+      done_reading = TRUE;
+    }
+
+  gtk_init ();
+
+  window = gtk_window_new ();
+  gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
+
+  header = gtk_header_bar_new ();
+  gtk_window_set_titlebar (GTK_WINDOW (window), header);
+
+  toggle = gtk_check_button_new ();
+  gtk_widget_set_valign (toggle, GTK_ALIGN_CENTER);
+  gtk_header_bar_pack_start (GTK_HEADER_BAR (header), toggle);
+
+  stack = gtk_stack_new ();
+  gtk_window_set_child (GTK_WINDOW (window), stack);
+
+  switcher = gtk_stack_switcher_new ();
+  gtk_header_bar_set_title_widget (GTK_HEADER_BAR (header), switcher);
+
+  gtk_stack_switcher_set_stack (GTK_STACK_SWITCHER (switcher), GTK_STACK (stack));
+
+  expression = gtk_property_expression_new (GTK_TYPE_STRING_OBJECT, NULL, "string");
+  sortmodel = gtk_sort_list_model_new (G_LIST_MODEL (stringlist),
+                                       GTK_SORTER (gtk_string_sorter_new (expression)));
+  expression = gtk_cclosure_expression_new (G_TYPE_STRING, NULL, 0, NULL, (GCallback) get_first, NULL, NULL);
+  gtk_sort_list_model_set_section_sorter (sortmodel, GTK_SORTER (gtk_string_sorter_new (expression)));
+  selection = GTK_SELECTION_MODEL (gtk_no_selection_new (G_LIST_MODEL (sortmodel)));
+
+  /* list */
+
+  sw = gtk_scrolled_window_new ();
+  gtk_stack_add_titled (GTK_STACK (stack), sw, "list", "List");
+
+  factory = gtk_signal_list_item_factory_new ();
+  g_signal_connect (factory, "setup", G_CALLBACK (setup_item), NULL);
+  g_signal_connect (factory, "bind", G_CALLBACK (bind_item), NULL);
+
+  lv = gtk_list_view_new (g_object_ref (selection), factory);
+  gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), lv);
+
+  g_signal_connect (toggle, "toggled", G_CALLBACK (toggle_cb), lv);
+
+  /* grid */
+
+  sw = gtk_scrolled_window_new ();
+  gtk_stack_add_titled (GTK_STACK (stack), sw, "grid", "Grid");
+
+  factory = gtk_signal_list_item_factory_new ();
+  g_signal_connect (factory, "setup", G_CALLBACK (setup_item), NULL);
+  g_signal_connect (factory, "bind", G_CALLBACK (bind_item), NULL);
+
+  gv = gtk_grid_view_new (g_object_ref (selection), factory);
+  gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), gv);
+
+  g_signal_connect (toggle, "toggled", G_CALLBACK (toggle_cb), gv);
+
+  gtk_grid_view_set_min_columns (GTK_GRID_VIEW (gv), 5);
+
+  adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (sw));
+  g_signal_connect (adj, "value-changed", G_CALLBACK (value_changed_cb), NULL);
+
+  /* columns */
+
+  sw = gtk_scrolled_window_new ();
+  gtk_stack_add_titled (GTK_STACK (stack), sw, "columns", "Columns");
+
+  cv = gtk_column_view_new (g_object_ref (selection));
+  gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), cv);
+
+  factory = gtk_signal_list_item_factory_new ();
+  g_signal_connect (factory, "setup", G_CALLBACK (setup_item), NULL);
+  g_signal_connect (factory, "bind", G_CALLBACK (bind_item), NULL);
+
+  column = gtk_column_view_column_new ("Word", factory);
+  gtk_column_view_append_column (GTK_COLUMN_VIEW (cv), column);
+  gtk_column_view_column_set_expand (column, TRUE);
+  gtk_column_view_column_set_resizable (column, TRUE);
+  g_object_unref (column);
+
+  factory = gtk_signal_list_item_factory_new ();
+  g_signal_connect (factory, "setup", G_CALLBACK (setup_item), NULL);
+  g_signal_connect (factory, "bind", G_CALLBACK (bind_item_reverse), NULL);
+
+  column = gtk_column_view_column_new ("Reverse", factory);
+  gtk_column_view_append_column (GTK_COLUMN_VIEW (cv), column);
+  gtk_column_view_column_set_expand (column, TRUE);
+  gtk_column_view_column_set_resizable (column, TRUE);
+  g_object_unref (column);
+
+  g_signal_connect (toggle, "toggled", G_CALLBACK (toggle_cb), cv);
+
+  gtk_window_present (GTK_WINDOW (window));
+
+  g_timeout_add (500, dump_sections, selection);
+
+  while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0)
+    g_main_context_iteration (NULL, FALSE);
+
+  g_object_unref (selection);
+
+  return 0;
+}