[gtk/wip/otte/listview: 132/139] columnview: Add a custom LayoutManager



commit d543a2b4f87d331b07c2e9468c29643ec8c801dc
Author: Benjamin Otte <otte redhat com>
Date:   Thu Nov 7 23:40:47 2019 +0100

    columnview: Add a custom LayoutManager
    
    The ColumnView now allocates column widths first and then the individual
    rows use the new layout manager which looks at the column allocations to
    allocate their children.

 gtk/gtkcolumnlistitemfactory.c   |   3 +-
 gtk/gtkcolumnview.c              | 119 ++++++++++++++++++++++++++++++-
 gtk/gtkcolumnviewcell.c          |  32 +++------
 gtk/gtkcolumnviewcellprivate.h   |   5 +-
 gtk/gtkcolumnviewcolumn.c        |  28 +++++++-
 gtk/gtkcolumnviewcolumnprivate.h |   6 ++
 gtk/gtkcolumnviewlayout.c        | 149 +++++++++++++++++++++++++++++++++++++++
 gtk/gtkcolumnviewlayoutprivate.h |  35 +++++++++
 gtk/gtkcolumnviewprivate.h       |   3 +
 gtk/meson.build                  |   5 +-
 10 files changed, 353 insertions(+), 32 deletions(-)
---
diff --git a/gtk/gtkcolumnlistitemfactory.c b/gtk/gtkcolumnlistitemfactory.c
index ac5c3e1766..0b2151f12f 100644
--- a/gtk/gtkcolumnlistitemfactory.c
+++ b/gtk/gtkcolumnlistitemfactory.c
@@ -23,6 +23,7 @@
 
 #include "gtkboxlayout.h"
 #include "gtkcolumnviewcolumnprivate.h"
+#include "gtkcolumnviewlayoutprivate.h"
 #include "gtklistitemfactoryprivate.h"
 #include "gtklistitemprivate.h"
 
@@ -51,7 +52,7 @@ gtk_column_list_item_factory_setup (GtkListItemFactory *factory,
 
   /* FIXME: evil */
   gtk_widget_set_layout_manager (GTK_WIDGET (widget),
-                                 gtk_box_layout_new (GTK_ORIENTATION_HORIZONTAL));
+                                 gtk_column_view_layout_new (self->view));
 
   GTK_LIST_ITEM_FACTORY_CLASS (gtk_column_list_item_factory_parent_class)->setup (factory, widget, 
list_item);
 
diff --git a/gtk/gtkcolumnview.c b/gtk/gtkcolumnview.c
index 00b5e3a21b..8582e7293d 100644
--- a/gtk/gtkcolumnview.c
+++ b/gtk/gtkcolumnview.c
@@ -118,6 +118,93 @@ G_DEFINE_TYPE_WITH_CODE (GtkColumnView, gtk_column_view, GTK_TYPE_WIDGET,
 static GParamSpec *properties[N_PROPS] = { NULL, };
 static guint signals[LAST_SIGNAL] = { 0 };
 
+static void
+gtk_column_view_measure (GtkWidget      *widget,
+                         GtkOrientation  orientation,
+                         int             for_size,
+                         int            *minimum,
+                         int            *natural,
+                         int            *minimum_baseline,
+                         int            *natural_baseline)
+{
+  GtkColumnView *self = GTK_COLUMN_VIEW (widget);
+
+  if (orientation == GTK_ORIENTATION_HORIZONTAL)
+    {
+      gtk_column_view_measure_across (self, minimum, natural);
+    }
+  else
+    {
+      gtk_widget_measure (GTK_WIDGET (self->listview),
+                          orientation, for_size,
+                          minimum, natural, 
+                          minimum_baseline, natural_baseline);
+    }
+}
+
+static int
+gtk_column_view_allocate_columns (GtkColumnView *self,
+                                  int            width)
+{
+  GtkScrollablePolicy scroll_policy;
+  int col_min, col_nat, widget_min, widget_nat, extra, col_size, x;
+  guint i;
+
+  gtk_column_view_measure_across (self, &col_min, &col_nat);
+  gtk_widget_measure (GTK_WIDGET (self->listview),
+                      GTK_ORIENTATION_HORIZONTAL, -1,
+                      &widget_min, &widget_nat,
+                      NULL, NULL);
+
+  scroll_policy = gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (self->listview));
+  if (scroll_policy == GTK_SCROLL_MINIMUM)
+    {
+      extra = widget_min - col_min;
+      col_size = col_min;
+    }
+  else
+    {
+      extra = widget_nat - col_nat;
+      col_size = col_nat;
+    }
+  width -= extra;
+  width = MAX (width, col_size);
+
+  x = 0;
+  for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->columns)); i++)
+    {
+      GtkColumnViewColumn *column;
+
+      column = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
+      gtk_column_view_column_measure (column, &col_min, &col_nat);
+      if (scroll_policy == GTK_SCROLL_MINIMUM)
+        col_size = col_min;
+      else
+        col_size = col_nat;
+
+      gtk_column_view_column_allocate (column, x, col_size);
+      x += col_size;
+
+      g_object_unref (column);
+    }
+
+  return width + extra;
+}
+
+static void
+gtk_column_view_allocate (GtkWidget *widget,
+                          int        width,
+                          int        height,
+                          int        baseline)
+{
+  GtkColumnView *self = GTK_COLUMN_VIEW (widget);
+  int full_width;
+
+  full_width = gtk_column_view_allocate_columns (self, width);
+
+  gtk_widget_allocate (GTK_WIDGET (self->listview), full_width, height, baseline, NULL);
+}
+
 static void
 gtk_column_view_activate_cb (GtkListView   *listview,
                              guint          pos,
@@ -261,6 +348,9 @@ gtk_column_view_class_init (GtkColumnViewClass *klass)
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
   gpointer iface;
 
+  widget_class->measure = gtk_column_view_measure;
+  widget_class->size_allocate = gtk_column_view_allocate;
+
   gobject_class->dispose = gtk_column_view_dispose;
   gobject_class->finalize = gtk_column_view_finalize;
   gobject_class->get_property = gtk_column_view_get_property;
@@ -362,7 +452,6 @@ gtk_column_view_init (GtkColumnView *self)
   gtk_css_node_add_class (gtk_widget_get_css_node (GTK_WIDGET (self)),
                           g_quark_from_static_string (I_("view")));
 
-  gtk_widget_set_layout_manager (GTK_WIDGET (self), gtk_box_layout_new (GTK_ORIENTATION_VERTICAL));
   gtk_widget_set_overflow (GTK_WIDGET (self), GTK_OVERFLOW_HIDDEN);
 }
 
@@ -531,3 +620,31 @@ gtk_column_view_remove_column (GtkColumnView       *self,
   g_list_store_remove (self->columns, i);
 }
 
+void
+gtk_column_view_measure_across (GtkColumnView *self,
+                                int           *minimum,
+                                int           *natural)
+{
+  guint i;
+  int min, nat;
+
+  min = 0;
+  nat = 0;
+
+  for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->columns)); i++)
+    {
+      GtkColumnViewColumn *column;
+      int col_min, col_nat;
+
+      column = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
+      gtk_column_view_column_measure (column, &col_min, &col_nat);
+      min += col_min;
+      nat += col_nat;
+
+      g_object_unref (column);
+    }
+
+  *minimum = min;
+  *natural = nat;
+}
+
diff --git a/gtk/gtkcolumnviewcell.c b/gtk/gtkcolumnviewcell.c
index db3085dbe2..ecaa39dd46 100644
--- a/gtk/gtkcolumnviewcell.c
+++ b/gtk/gtkcolumnviewcell.c
@@ -44,17 +44,6 @@ struct _GtkColumnViewCellClass
 
 G_DEFINE_TYPE (GtkColumnViewCell, gtk_column_view_cell, GTK_TYPE_LIST_ITEM_WIDGET)
 
-void
-gtk_column_view_cell_measure_contents (GtkColumnViewCell *self,
-                                       int               *minimum,
-                                       int               *natural)
-{
-  GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self));
-
-  if (child)
-    gtk_widget_measure (child, GTK_ORIENTATION_HORIZONTAL, -1, minimum, natural, NULL, NULL);
-}
-
 static void
 gtk_column_view_cell_measure (GtkWidget      *widget,
                               GtkOrientation  orientation,
@@ -64,19 +53,10 @@ gtk_column_view_cell_measure (GtkWidget      *widget,
                               int            *minimum_baseline,
                               int            *natural_baseline)
 {
-  GtkColumnViewCell *self = GTK_COLUMN_VIEW_CELL (widget);
+  GtkWidget *child = gtk_widget_get_first_child (widget);
 
-  if (orientation == GTK_ORIENTATION_HORIZONTAL)
-    {
-      gtk_column_view_column_measure (self->column, minimum, natural);
-    }
-  else
-    {
-      GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self));
-
-      if (child)
-        gtk_widget_measure (child, orientation, for_size, minimum, natural, minimum_baseline, 
natural_baseline);
-    }
+  if (child)
+    gtk_widget_measure (child, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
 }
 
 static void
@@ -202,3 +182,9 @@ gtk_column_view_cell_get_prev (GtkColumnViewCell *self)
 {
   return self->prev_cell;
 }
+
+GtkColumnViewColumn *
+gtk_column_view_cell_get_column (GtkColumnViewCell *self)
+{
+  return self->column;
+}
diff --git a/gtk/gtkcolumnviewcellprivate.h b/gtk/gtkcolumnviewcellprivate.h
index 1ebdc710f4..4378d082e7 100644
--- a/gtk/gtkcolumnviewcellprivate.h
+++ b/gtk/gtkcolumnviewcellprivate.h
@@ -42,10 +42,7 @@ void                    gtk_column_view_cell_remove             (GtkColumnViewCe
 
 GtkColumnViewCell *     gtk_column_view_cell_get_next           (GtkColumnViewCell      *self);
 GtkColumnViewCell *     gtk_column_view_cell_get_prev           (GtkColumnViewCell      *self);
-
-void                    gtk_column_view_cell_measure_contents   (GtkColumnViewCell      *self,
-                                                                 int                    *minimum,
-                                                                 int                    *natural);
+GtkColumnViewColumn *   gtk_column_view_cell_get_column         (GtkColumnViewCell      *self);
 
 G_END_DECLS
 
diff --git a/gtk/gtkcolumnviewcolumn.c b/gtk/gtkcolumnviewcolumn.c
index 03f8d52fa5..c18cf7716e 100644
--- a/gtk/gtkcolumnviewcolumn.c
+++ b/gtk/gtkcolumnviewcolumn.c
@@ -52,6 +52,8 @@ struct _GtkColumnViewColumn
 
   int minimum_size_request;
   int natural_size_request;
+  int allocation_offset;
+  int allocation_size;
 
   /* This list isn't sorted - this is just caching for performance */
   GtkColumnViewCell *first_cell; /* no reference, just caching */
@@ -310,7 +312,11 @@ gtk_column_view_column_measure (GtkColumnViewColumn *self,
 
       for (cell = self->first_cell; cell; cell = gtk_column_view_cell_get_next (cell))
         {
-          gtk_column_view_cell_measure_contents (cell, &cell_min, &cell_nat);
+          gtk_widget_measure (GTK_WIDGET (cell),
+                              GTK_ORIENTATION_HORIZONTAL,
+                              -1,
+                              &cell_min, &cell_nat,
+                              NULL, NULL);
 
           min = MAX (min, cell_min);
           nat = MAX (nat, cell_nat);
@@ -324,6 +330,26 @@ gtk_column_view_column_measure (GtkColumnViewColumn *self,
   *natural = self->natural_size_request;
 }
 
+void
+gtk_column_view_column_allocate (GtkColumnViewColumn *self,
+                                 int                  offset,
+                                 int                  size)
+{
+  self->allocation_offset = offset;
+  self->allocation_size = size;
+}
+
+void
+gtk_column_view_column_get_allocation (GtkColumnViewColumn *self,
+                                       int                 *offset,
+                                       int                 *size)
+{
+  if (offset)
+    *offset = self->allocation_offset;
+  if (size)
+    *size = self->allocation_size;
+}
+
 static void
 gtk_column_view_column_create_cells (GtkColumnViewColumn *self)
 {
diff --git a/gtk/gtkcolumnviewcolumnprivate.h b/gtk/gtkcolumnviewcolumnprivate.h
index fc0fe07c78..d7e06a5b0f 100644
--- a/gtk/gtkcolumnviewcolumnprivate.h
+++ b/gtk/gtkcolumnviewcolumnprivate.h
@@ -37,5 +37,11 @@ void                    gtk_column_view_column_queue_resize             (GtkColu
 void                    gtk_column_view_column_measure                  (GtkColumnViewColumn    *self,
                                                                          int                    *minimum,
                                                                          int                    *natural);
+void                    gtk_column_view_column_allocate                 (GtkColumnViewColumn    *self,
+                                                                         int                     offset,
+                                                                         int                     size);
+void                    gtk_column_view_column_get_allocation           (GtkColumnViewColumn    *self,
+                                                                         int                    *offset,
+                                                                         int                    *size);
 
 #endif  /* __GTK_COLUMN_VIEW_COLUMN_PRIVATE_H__ */
diff --git a/gtk/gtkcolumnviewlayout.c b/gtk/gtkcolumnviewlayout.c
new file mode 100644
index 0000000000..3afd6a2af3
--- /dev/null
+++ b/gtk/gtkcolumnviewlayout.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright © 2019 Benjamin Otte
+ *
+ * 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.1 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/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+#include "config.h"
+
+#include "gtkcolumnviewlayoutprivate.h"
+
+#include "gtkcolumnviewcellprivate.h"
+#include "gtkcolumnviewcolumnprivate.h"
+#include "gtkcolumnviewprivate.h"
+#include "gtkwidgetprivate.h"
+
+struct _GtkColumnViewLayout
+{
+  GtkLayoutManager parent_instance;
+
+  GtkColumnView *view; /* no reference */
+};
+
+G_DEFINE_TYPE (GtkColumnViewLayout, gtk_column_view_layout, GTK_TYPE_LAYOUT_MANAGER)
+
+static void
+gtk_column_view_layout_measure_along (GtkColumnViewLayout *self,
+                                      GtkWidget           *widget,
+                                      int                  for_size,
+                                      int                 *minimum,
+                                      int                 *natural,
+                                      int                 *minimum_baseline,
+                                      int                 *natural_baseline)
+{
+  GtkOrientation orientation = GTK_ORIENTATION_VERTICAL;
+  GtkWidget *child;
+
+  for (child = _gtk_widget_get_first_child (widget);
+       child != NULL;
+       child = _gtk_widget_get_next_sibling (child))
+    {
+      int child_min = 0;
+      int child_nat = 0;
+      int child_min_baseline = -1;
+      int child_nat_baseline = -1;
+
+      gtk_widget_measure (child, orientation, for_size,
+                          &child_min, &child_nat,
+                          &child_min_baseline, &child_nat_baseline);
+
+      *minimum = MAX (*minimum, child_min);
+      *natural = MAX (*natural, child_nat);
+
+      if (child_min_baseline > -1)
+        *minimum_baseline = MAX (*minimum_baseline, child_min_baseline);
+      if (child_nat_baseline > -1)
+        *natural_baseline = MAX (*natural_baseline, child_nat_baseline);
+    }
+}
+
+static void
+gtk_column_view_layout_measure (GtkLayoutManager *layout,
+                                GtkWidget        *widget,
+                                GtkOrientation    orientation,
+                                int               for_size,
+                                int              *minimum,
+                                int              *natural,
+                                int              *minimum_baseline,
+                                int              *natural_baseline)
+{
+  GtkColumnViewLayout *self = GTK_COLUMN_VIEW_LAYOUT (layout);
+
+  if (orientation == GTK_ORIENTATION_HORIZONTAL)
+    {
+      gtk_column_view_measure_across (GTK_COLUMN_VIEW (self->view),
+                                      minimum,
+                                      natural);
+    }
+  else
+    {
+      gtk_column_view_layout_measure_along (self,
+                                            widget, 
+                                            for_size,
+                                            minimum,
+                                            natural,
+                                            minimum_baseline,
+                                            natural_baseline);
+    }
+}
+
+static void
+gtk_column_view_layout_allocate (GtkLayoutManager *layout_manager,
+                                 GtkWidget        *widget,
+                                 int               width,
+                                 int               height,
+                                 int               baseline)
+{
+  GtkWidget *child;
+
+  for (child = _gtk_widget_get_first_child (widget);
+       child != NULL;
+       child = _gtk_widget_get_next_sibling (child))
+    {
+      GtkColumnViewCell *cell = GTK_COLUMN_VIEW_CELL (child);
+      GtkColumnViewColumn *column = gtk_column_view_cell_get_column (cell);
+      int col_x, col_width;
+
+      gtk_column_view_column_get_allocation (column, &col_x, &col_width);
+      gtk_widget_size_allocate (child, &(GtkAllocation) { col_x, 0, col_width, height }, baseline);
+    }
+}
+
+static void
+gtk_column_view_layout_class_init (GtkColumnViewLayoutClass *klass)
+{
+  GtkLayoutManagerClass *layout_manager_class = GTK_LAYOUT_MANAGER_CLASS (klass);
+
+  layout_manager_class->measure = gtk_column_view_layout_measure;
+  layout_manager_class->allocate = gtk_column_view_layout_allocate;
+}
+
+static void
+gtk_column_view_layout_init (GtkColumnViewLayout *self)
+{
+}
+
+GtkLayoutManager *
+gtk_column_view_layout_new (GtkColumnView *view)
+{
+  GtkColumnViewLayout *result;
+
+  result = g_object_new (GTK_TYPE_COLUMN_VIEW_LAYOUT, NULL);
+
+  result->view = view;
+
+  return GTK_LAYOUT_MANAGER (result);
+}
diff --git a/gtk/gtkcolumnviewlayoutprivate.h b/gtk/gtkcolumnviewlayoutprivate.h
new file mode 100644
index 0000000000..6e786cdbd5
--- /dev/null
+++ b/gtk/gtkcolumnviewlayoutprivate.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright © 2019 Benjamin Otte
+ *
+ * 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.1 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/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+#ifndef __GTK_COLUMN_VIEW_LAYOUT_PRIVATE_H__
+#define __GTK_COLUMN_VIEW_LAYOUT_PRIVATE_H__
+
+#include <gtk/gtkcolumnview.h>
+#include <gtk/gtklayoutmanager.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_COLUMN_VIEW_LAYOUT (gtk_column_view_layout_get_type ())
+
+G_DECLARE_FINAL_TYPE (GtkColumnViewLayout, gtk_column_view_layout, GTK, COLUMN_VIEW_LAYOUT, GtkLayoutManager)
+
+GtkLayoutManager *      gtk_column_view_layout_new              (GtkColumnView          *view);
+
+
+#endif  /* __GTK_COLUMN_VIEW_LAYOUT_PRIVATE_H__ */
diff --git a/gtk/gtkcolumnviewprivate.h b/gtk/gtkcolumnviewprivate.h
index 417a942ab4..d95577fe98 100644
--- a/gtk/gtkcolumnviewprivate.h
+++ b/gtk/gtkcolumnviewprivate.h
@@ -22,5 +22,8 @@
 
 #include "gtk/gtkcolumnview.h"
 
+void                    gtk_column_view_measure_across          (GtkColumnView          *self,
+                                                                 int                    *minimum,
+                                                                 int                    *natural);
 
 #endif  /* __GTK_COLUMN_VIEW_PRIVATE_H__ */
diff --git a/gtk/meson.build b/gtk/meson.build
index 23af1e1005..da1e828d0b 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -37,6 +37,9 @@ gtk_private_sources = files([
   'gtkcolorpickershell.c',
   'gtkcolorscale.c',
   'gtkcolorswatch.c',
+  'gtkcolumnlistitemfactory.c',
+  'gtkcolumnviewcell.c',
+  'gtkcolumnviewlayout.c',
   'gtkconstraintexpression.c',
   'gtkconstraintsolver.c',
   'gtkconstraintvflparser.c',
@@ -205,9 +208,7 @@ gtk_public_sources = files([
   'gtkcolorchooserdialog.c',
   'gtkcolorchooserwidget.c',
   'gtkcolorutils.c',
-  'gtkcolumnlistitemfactory.c',
   'gtkcolumnview.c',
-  'gtkcolumnviewcell.c',
   'gtkcolumnviewcolumn.c',
   'gtkcombobox.c',
   'gtkcomboboxtext.c',


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