[gnome-builder/wip/chergert/layout: 51/118] util: add IdeFancyTreeView



commit c762fb7c350218911c27770a2bfb90760beb844e
Author: Christian Hergert <chergert redhat com>
Date:   Fri Jun 30 20:26:24 2017 -0700

    util: add IdeFancyTreeView
    
    This is a treeview that handles all of the hacks necessary to
    get reflowing of text working (using IdeCellRendererFancy) and
    make that convenient for consumers.
    
    The places we know we need this today (as GtkListBox is not
    designed to handle the use-case) is build warnings and TODOs.

 libide/ide.h                      |    1 +
 libide/meson.build                |    2 +
 libide/util/ide-fancy-tree-view.c |  197 +++++++++++++++++++++++++++++++++++++
 libide/util/ide-fancy-tree-view.h |   45 +++++++++
 plugins/todo/gbp-todo-panel.c     |   93 +-----------------
 5 files changed, 248 insertions(+), 90 deletions(-)
---
diff --git a/libide/ide.h b/libide/ide.h
index d23ede7..3f3a3e2 100644
--- a/libide/ide.h
+++ b/libide/ide.h
@@ -149,6 +149,7 @@ G_BEGIN_DECLS
 #include "transfers/ide-transfer-button.h"
 #include "transfers/ide-transfer-manager.h"
 #include "util/ide-cell-renderer-fancy.h"
+#include "util/ide-fancy-tree-view.h"
 #include "util/ide-flatpak.h"
 #include "util/ide-gtk.h"
 #include "util/ide-line-reader.h"
diff --git a/libide/meson.build b/libide/meson.build
index f8d3625..891d332 100644
--- a/libide/meson.build
+++ b/libide/meson.build
@@ -194,6 +194,7 @@ libide_public_headers = [
   'transfers/ide-transfers-button.h',
   'transfers/ide-transfers-progress-icon.h',
   'util/ide-cell-renderer-fancy.h',
+  'util/ide-fancy-tree-view.h',
   'util/ide-flatpak.h',
   'util/ide-glib.h',
   'util/ide-gtk.h',
@@ -395,6 +396,7 @@ libide_public_sources = [
   'transfers/ide-transfers-button.c',
   'transfers/ide-transfers-progress-icon.c',
   'util/ide-cell-renderer-fancy.c',
+  'util/ide-fancy-tree-view.c',
   'util/ide-flatpak.c',
   'util/ide-glib.c',
   'util/ide-gtk.c',
diff --git a/libide/util/ide-fancy-tree-view.c b/libide/util/ide-fancy-tree-view.c
new file mode 100644
index 0000000..dbe0c27
--- /dev/null
+++ b/libide/util/ide-fancy-tree-view.c
@@ -0,0 +1,197 @@
+/* ide-fancy-tree-view.c
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-fancy-tree-view"
+
+#include "ide-macros.h"
+
+#include "util/ide-cell-renderer-fancy.h"
+#include "util/ide-fancy-tree-view.h"
+
+/**
+ * SECTION:ide-fancy-tree-view:
+ * @title: IdeFancyTreeView
+ * @short_description: a stylized treeview for use in sidebars
+ *
+ * This is a helper #GtkTreeView that matches the style that
+ * Builder uses for treeviews which can reflow text. It is a
+ * useful base class because it does all of the hacks necessary
+ * to make this work without ruining your code.
+ *
+ * It only has a single column, and comes setup with a single
+ * cell (an #IdeCellRendererFancy) to render the conten.
+ *
+ * Since: 3.26
+ */
+
+typedef struct
+{
+  gint  last_width;
+  guint relayout_source;
+} IdeFancyTreeViewPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeFancyTreeView, ide_fancy_tree_view, GTK_TYPE_TREE_VIEW)
+
+static void
+ide_fancy_tree_view_destroy (GtkWidget *widget)
+{
+  IdeFancyTreeView *self = (IdeFancyTreeView *)widget;
+  IdeFancyTreeViewPrivate *priv = ide_fancy_tree_view_get_instance_private (self);
+
+  ide_clear_source (&priv->relayout_source);
+
+  GTK_WIDGET_CLASS (ide_fancy_tree_view_parent_class)->destroy (widget);
+}
+
+static gboolean
+queue_relayout_in_idle (gpointer user_data)
+{
+  IdeFancyTreeView *self = user_data;
+  IdeFancyTreeViewPrivate *priv = ide_fancy_tree_view_get_instance_private (self);
+  GtkAllocation alloc;
+  guint n_columns;
+
+  g_assert (IDE_IS_FANCY_TREE_VIEW (self));
+
+  gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
+
+  if (alloc.width == priv->last_width)
+    goto cleanup;
+
+  priv->last_width = alloc.width;
+
+  n_columns = gtk_tree_view_get_n_columns (GTK_TREE_VIEW (self));
+
+  for (guint i = 0; i < n_columns; i++)
+    {
+      GtkTreeViewColumn *column;
+
+      column = gtk_tree_view_get_column (GTK_TREE_VIEW (self), i);
+      gtk_tree_view_column_queue_resize (column);
+    }
+
+cleanup:
+  priv->relayout_source = 0;
+
+  return G_SOURCE_REMOVE;
+}
+
+
+static void
+ide_fancy_tree_view_size_allocate (GtkWidget *widget,
+                                   GtkAllocation *alloc)
+{
+  IdeFancyTreeView *self = (IdeFancyTreeView *)widget;
+  IdeFancyTreeViewPrivate *priv = ide_fancy_tree_view_get_instance_private (self);
+
+  g_assert (IDE_IS_FANCY_TREE_VIEW (self));
+
+  GTK_WIDGET_CLASS (ide_fancy_tree_view_parent_class)->size_allocate (widget, alloc);
+
+  if (priv->last_width != alloc->width)
+    {
+      /*
+       * We must perform our queued relayout from an idle callback
+       * so that we don't affect this draw cycle. If we do that, we
+       * will get empty content flashes for the current frame. This
+       * allows us to draw the current frame slightly incorrect but
+       * fixup on the next frame (which looks much nicer from a user
+       * point of view).
+       */
+      if (priv->relayout_source == 0)
+        priv->relayout_source =
+          gdk_threads_add_idle_full (G_PRIORITY_LOW + 100,
+                                     queue_relayout_in_idle,
+                                     g_object_ref (self),
+                                     g_object_unref);
+    }
+}
+
+static void
+ide_fancy_tree_view_class_init (IdeFancyTreeViewClass *klass)
+{
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  widget_class->size_allocate = ide_fancy_tree_view_size_allocate;
+  widget_class->destroy = ide_fancy_tree_view_destroy;
+}
+
+static void
+ide_fancy_tree_view_init (IdeFancyTreeView *self)
+{
+  GtkTreeViewColumn *column;
+  GtkCellRenderer *cell;
+
+  g_object_set (self,
+                "activate-on-single-click", TRUE,
+                "headers-visible", FALSE,
+                NULL);
+
+  column = g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
+                         "expand", TRUE,
+                         "visible", TRUE,
+                         NULL);
+  gtk_tree_view_append_column (GTK_TREE_VIEW (self), column);
+
+  cell = g_object_new (IDE_TYPE_CELL_RENDERER_FANCY,
+                       "visible", TRUE,
+                       "xalign", 0.0f,
+                       "xpad", 4,
+                       "ypad", 6,
+                       NULL);
+  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (column), cell, TRUE);
+}
+
+GtkWidget *
+ide_fancy_tree_view_new (void)
+{
+  return g_object_new (IDE_TYPE_FANCY_TREE_VIEW, NULL);
+}
+
+/**
+ * ide_fancy_tree_view_set_data_func:
+ * @self: a #IdeFancyTreeView
+ * @func: (closure func_data) (scope async) (nullable): a callback
+ * @func_data: data for @func
+ * @func_data_destroy: destroy notify for @func_data
+ *
+ * Sets the data func to use to update the text for the
+ * #IdeCellRendererFancy cell renderer.
+ *
+ * Since: 3.26
+ */
+void
+ide_fancy_tree_view_set_data_func (IdeFancyTreeView      *self,
+                                   GtkCellLayoutDataFunc  func,
+                                   gpointer               func_data,
+                                   GDestroyNotify         func_data_destroy)
+{
+  GtkTreeViewColumn *column;
+  GList *cells;
+
+  g_return_if_fail (IDE_IS_FANCY_TREE_VIEW (self));
+
+  column = gtk_tree_view_get_column (GTK_TREE_VIEW (self), 0);
+  cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+
+  if (cells->data != NULL)
+    gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column), cells->data,
+                                        func, func_data, func_data_destroy);
+
+  g_list_free (cells);
+}
diff --git a/libide/util/ide-fancy-tree-view.h b/libide/util/ide-fancy-tree-view.h
new file mode 100644
index 0000000..10de4e1
--- /dev/null
+++ b/libide/util/ide-fancy-tree-view.h
@@ -0,0 +1,45 @@
+/* ide-fancy-tree-view.h
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_FANCY_TREE_VIEW (ide_fancy_tree_view_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (IdeFancyTreeView, ide_fancy_tree_view, IDE, FANCY_TREE_VIEW, GtkTreeView)
+
+struct _IdeFancyTreeViewClass
+{
+  GtkTreeViewClass parent_class;
+
+  gpointer _reserved1;
+  gpointer _reserved2;
+  gpointer _reserved3;
+  gpointer _reserved4;
+};
+
+GtkWidget *ide_fancy_tree_view_new           (void);
+void       ide_fancy_tree_view_set_data_func (IdeFancyTreeView      *self,
+                                              GtkCellLayoutDataFunc  func,
+                                              gpointer               func_data,
+                                              GDestroyNotify         func_data_destroy);
+
+G_END_DECLS
diff --git a/plugins/todo/gbp-todo-panel.c b/plugins/todo/gbp-todo-panel.c
index 037eb8e..f227bdf 100644
--- a/plugins/todo/gbp-todo-panel.c
+++ b/plugins/todo/gbp-todo-panel.c
@@ -29,9 +29,6 @@ struct _GbpTodoPanel
 
   GtkTreeView  *tree_view;
   GbpTodoModel *model;
-
-  guint         last_width;
-  guint         relayout_source;
 };
 
 G_DEFINE_TYPE (GbpTodoPanel, gbp_todo_panel, GTK_TYPE_BIN)
@@ -202,68 +199,6 @@ gbp_todo_panel_query_tooltip (GbpTodoPanel *self,
   return FALSE;
 }
 
-static gboolean
-queue_relayout_in_idle (gpointer user_data)
-{
-  GbpTodoPanel *self = user_data;
-  GtkAllocation alloc;
-  guint n_columns;
-
-  g_assert (GBP_IS_TODO_PANEL (self));
-
-  gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
-
-  if (alloc.width == self->last_width)
-    goto cleanup;
-
-  self->last_width = alloc.width;
-
-  n_columns = gtk_tree_view_get_n_columns (self->tree_view);
-
-  for (guint i = 0; i < n_columns; i++)
-    {
-      GtkTreeViewColumn *column;
-
-      column = gtk_tree_view_get_column (self->tree_view, i);
-      gtk_tree_view_column_queue_resize (column);
-    }
-
-cleanup:
-  self->relayout_source = 0;
-
-  return G_SOURCE_REMOVE;
-}
-
-static void
-gbp_todo_panel_size_allocate (GtkWidget     *widget,
-                              GtkAllocation *alloc)
-{
-  GbpTodoPanel *self = (GbpTodoPanel *)widget;
-
-  g_assert (GBP_IS_TODO_PANEL (self));
-  g_assert (alloc != NULL);
-
-  GTK_WIDGET_CLASS (gbp_todo_panel_parent_class)->size_allocate (widget, alloc);
-
-  if (self->last_width != alloc->width)
-    {
-      /*
-       * We must perform our queued relayout from an idle callback
-       * so that we don't affect this draw cycle. If we do that, we
-       * will get empty content flashes for the current frame. This
-       * allows us to draw the current frame slightly incorrect but
-       * fixup on the next frame (which looks much nicer from a user
-       * point of view).
-       */
-      if (self->relayout_source == 0)
-        self->relayout_source =
-          gdk_threads_add_idle_full (G_PRIORITY_LOW + 100,
-                                     queue_relayout_in_idle,
-                                     g_object_ref (self),
-                                     g_object_unref);
-    }
-}
-
 static void
 gbp_todo_panel_destroy (GtkWidget *widget)
 {
@@ -274,7 +209,6 @@ gbp_todo_panel_destroy (GtkWidget *widget)
   if (self->tree_view != NULL)
     gtk_tree_view_set_model (self->tree_view, NULL);
 
-  ide_clear_source (&self->relayout_source);
   g_clear_object (&self->model);
 
   GTK_WIDGET_CLASS (gbp_todo_panel_parent_class)->destroy (widget);
@@ -328,7 +262,6 @@ gbp_todo_panel_class_init (GbpTodoPanelClass *klass)
   object_class->set_property = gbp_todo_panel_set_property;
 
   widget_class->destroy = gbp_todo_panel_destroy;
-  widget_class->size_allocate = gbp_todo_panel_size_allocate;
 
   properties [PROP_MODEL] =
     g_param_spec_object ("model",
@@ -344,8 +277,6 @@ static void
 gbp_todo_panel_init (GbpTodoPanel *self)
 {
   GtkWidget *scroller;
-  GtkTreeViewColumn *column;
-  GtkCellRenderer *cell;
 
   scroller = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
                            "visible", TRUE,
@@ -353,10 +284,8 @@ gbp_todo_panel_init (GbpTodoPanel *self)
                            NULL);
   gtk_container_add (GTK_CONTAINER (self), scroller);
 
-  self->tree_view = g_object_new (GTK_TYPE_TREE_VIEW,
-                                  "activate-on-single-click", TRUE,
+  self->tree_view = g_object_new (IDE_TYPE_FANCY_TREE_VIEW,
                                   "has-tooltip", TRUE,
-                                  "headers-visible", FALSE,
                                   "visible", TRUE,
                                   NULL);
   g_signal_connect (self->tree_view,
@@ -373,24 +302,8 @@ gbp_todo_panel_init (GbpTodoPanel *self)
                             self);
   gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (self->tree_view));
 
-  column = g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
-                         "expand", TRUE,
-                         "visible", TRUE,
-                         NULL);
-  gtk_tree_view_append_column (self->tree_view, column);
-
-  cell = g_object_new (IDE_TYPE_CELL_RENDERER_FANCY,
-                       "visible", TRUE,
-                       "xalign", 0.0f,
-                       "xpad", 4,
-                       "ypad", 6,
-                       NULL);
-  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (column), cell, TRUE);
-
-  gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column),
-                                      cell,
-                                      gbp_todo_panel_cell_data_func,
-                                      NULL, NULL);
+  ide_fancy_tree_view_set_data_func (IDE_FANCY_TREE_VIEW (self->tree_view),
+                                     gbp_todo_panel_cell_data_func, NULL, NULL);
 }
 
 /**


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