[gtk/wip/otte/canvas: 1/2] canvas: Completely rework bounds computation




commit ad2f4a27be8c205c0e5033a8170df7af248bd9d2
Author: Benjamin Otte <otte redhat com>
Date:   Fri Jul 8 07:24:42 2022 +0200

    canvas: Completely rework bounds computation
    
    Instead of using a descriptive language to describe how to compute the
    bounds, just set a vfunc and let the application provide a function.
    
    That saves a lot of code - but makes the canvas rely on user code.

 demos/gtk-demo/canvas_intro.c     |  58 +++--
 demos/gtk-demo/canvas_planarity.c | 127 +++++-----
 demos/gtk-demo/canvas_puzzle.c    |  68 ++---
 gtk/gtk.h                         |   1 -
 gtk/gtkcanvas.c                   | 106 +++-----
 gtk/gtkcanvas.h                   |   2 +-
 gtk/gtkcanvasbox.c                | 203 ++-------------
 gtk/gtkcanvasbox.h                |  45 ++--
 gtk/gtkcanvasboxprivate.h         |  34 ---
 gtk/gtkcanvasitem.c               | 274 ++++++++++----------
 gtk/gtkcanvasitem.h               |  14 +-
 gtk/gtkcanvasitemprivate.h        |   8 +-
 gtk/gtkcanvasvector.c             | 181 --------------
 gtk/gtkcanvasvector.h             |  75 ------
 gtk/gtkcanvasvectorimpl.c         | 512 --------------------------------------
 gtk/gtkcanvasvectorprivate.h      | 115 ---------
 gtk/gtktypes.h                    |   1 -
 gtk/meson.build                   |   3 -
 18 files changed, 372 insertions(+), 1455 deletions(-)
---
diff --git a/demos/gtk-demo/canvas_intro.c b/demos/gtk-demo/canvas_intro.c
index 2ef1846321..4bf99fb3ec 100644
--- a/demos/gtk-demo/canvas_intro.c
+++ b/demos/gtk-demo/canvas_intro.c
@@ -10,31 +10,51 @@
 #define WIDTH 400
 #define HEIGHT 300
 
+static gboolean
+center_item (GtkCanvasItem *ci,
+             GtkCanvasBox  *out_box,
+             gpointer       unused)
+{
+  const GtkCanvasBox *viewport;
+  GtkWidget *widget;
+  int width, height;
+
+  /* We need to check if the viewport is available.
+   * If the canvas was scrolling, it might not be avaiable yet.
+   */
+  viewport = gtk_canvas_get_viewport (gtk_canvas_item_get_canvas (ci));
+  if (viewport == NULL)
+    return FALSE;
+
+  /* Measure the widget min for min so that it will line-break. */
+  widget = gtk_canvas_item_get_widget (ci);
+  gtk_widget_measure (widget, GTK_ORIENTATION_HORIZONTAL, -1, &width, NULL, NULL, NULL);
+  gtk_widget_measure (widget, GTK_ORIENTATION_VERTICAL, width, &height, NULL, NULL, NULL);
+
+  /* Initialize the bounds for this widget:
+   * - the point is the center of the canvas' viewport
+   * - the size is the one we just computed
+   * - we want the origin point to be at the center
+   */
+  gtk_canvas_box_init (out_box,
+                       viewport->size.width * 0.5,
+                       viewport->size.height * 0.5,
+                       0, 0,
+                       0.5, 0.5);
+
+  return TRUE;
+}
+
 static void
 bind_item (GtkListItemFactory *factory,
            GtkCanvasItem      *ci)
 {
-  GtkCanvasVector *point;
-  const GtkCanvasVector *size;
-  GtkCanvasBox *box;
-
   gtk_canvas_item_set_widget (ci, gtk_canvas_item_get_item (ci));
 
-  /* Also center the item, so we do something interesting */
-  point = gtk_canvas_vector_new (0, 0);
-  box = gtk_canvas_box_new (point,
-                            gtk_canvas_get_viewport_size (gtk_canvas_item_get_canvas (ci)),
-                            0.0, 0.0);
-  gtk_canvas_vector_free (point);
-
-  point = gtk_canvas_vector_new_from_box (box, 0.5, 0.5);
-  gtk_canvas_box_free (box);
-  size = gtk_canvas_vector_get_item_measure (ci, GTK_CANVAS_ITEM_MEASURE_MIN_FOR_MIN);
-  box = gtk_canvas_box_new (point, size, 0.5, 0.5);
-  gtk_canvas_vector_free (point);
-
-  gtk_canvas_item_set_bounds (ci, box);
-  gtk_canvas_box_free (box);
+  /* Set a function to compute the position */
+  gtk_canvas_item_set_compute_bounds (ci,
+                                      center_item,
+                                      NULL, NULL);
 }
 
 GtkWidget *
diff --git a/demos/gtk-demo/canvas_planarity.c b/demos/gtk-demo/canvas_planarity.c
index 93ab37571b..f10c84fa34 100644
--- a/demos/gtk-demo/canvas_planarity.c
+++ b/demos/gtk-demo/canvas_planarity.c
@@ -78,7 +78,7 @@ struct _PlanarityVertex
 {
   GObject parent_instance;
 
-  graphene_point_t position;
+  GtkOrigin position;
 };
 
 #define PLANARITY_TYPE_VERTEX (planarity_vertex_get_type ())
@@ -97,14 +97,15 @@ planarity_vertex_init (PlanarityVertex *self)
 }
 
 static PlanarityVertex *
-planarity_vertex_new (float x,
-                      float y)
+planarity_vertex_new (float horizontal,
+                      float vertical)
 {
   PlanarityVertex *self;
 
   self = g_object_new (PLANARITY_TYPE_VERTEX, NULL);
 
-  graphene_point_init (&self->position, x, y);
+  self->position.horizontal = horizontal;
+  self->position.vertical = vertical;
 
   return self;
 }
@@ -147,54 +148,83 @@ planarity_edge_new (PlanarityVertex *from,
   return edge;
 }
 
-static void
-set_item_position (GtkCanvasItem *ci)
+static gboolean
+set_vertex_bounds (GtkCanvasItem *ci,
+                   GtkCanvasBox  *out_box,
+                   gpointer       user_data)
 {
   PlanarityVertex *vertex = gtk_canvas_item_get_item (ci);
-  GtkCanvasVector *point, *size;
-  GtkCanvasBox *box, *viewport;
-  float x = vertex->position.x;
-  float y = vertex->position.y;
-
-  point = gtk_canvas_vector_new (0, 0);
-  viewport = gtk_canvas_box_new (point,
-                                 gtk_canvas_get_viewport_size (gtk_canvas_item_get_canvas (ci)),
-                                 0.0, 0.0);
-  gtk_canvas_vector_free (point);
-
-  point = gtk_canvas_vector_new_from_box (viewport, x, y);
-  gtk_canvas_box_free (viewport);
-  size = gtk_canvas_vector_new (0, 0);
-  box = gtk_canvas_box_new (point, size, x, y);
-  gtk_canvas_vector_free (point);
-  gtk_canvas_vector_free (size);
-
-  gtk_canvas_item_set_bounds (ci, box);
-  gtk_canvas_box_free (box);
+  const GtkCanvasBox *viewport;
+
+  viewport = gtk_canvas_get_viewport (gtk_canvas_item_get_canvas (ci));
+  if (viewport == NULL)
+    return FALSE;
+
+  gtk_canvas_box_init (out_box,
+                       viewport->size.width * vertex->position.horizontal,
+                       viewport->size.height * vertex->position.vertical,
+                       0, 0,
+                       vertex->position.horizontal,
+                       vertex->position.vertical);
+
+  return TRUE;
 }
 
 static void
-move_item (GtkGestureDrag *gesture,
-           double          x,
-           double          y,
-           GtkCanvasItem  *ci)
+move_vertex (GtkGestureDrag *gesture,
+             double          x,
+             double          y,
+             GtkCanvasItem  *ci)
 {
   GtkCanvas *canvas = gtk_canvas_item_get_canvas (ci);
-  GtkWidget *widget = gtk_canvas_item_get_widget (ci);
   PlanarityVertex *vertex = gtk_canvas_item_get_item (ci);
 
-  x /= (gtk_widget_get_width (GTK_WIDGET (canvas)) - gtk_widget_get_width (widget));
-  y /= (gtk_widget_get_height (GTK_WIDGET (canvas)) - gtk_widget_get_height (widget));
+  x /= gtk_widget_get_width (GTK_WIDGET (canvas));
+  y /= gtk_widget_get_height (GTK_WIDGET (canvas));
+
+  vertex->position.horizontal = CLAMP (vertex->position.horizontal + x, 0, 1);
+  vertex->position.vertical = CLAMP (vertex->position.vertical + y, 0, 1);
 
-  vertex->position = GRAPHENE_POINT_INIT (CLAMP (vertex->position.x + x, 0, 1), CLAMP (vertex->position.y + 
y, 0, 1));
-  set_item_position (ci);
+  gtk_canvas_item_invalidate_bounds (ci);
+}
+
+static gboolean
+set_edge_bounds (GtkCanvasItem *ci,
+                 GtkCanvasBox  *out_box,
+                 gpointer       user_data)
+{
+  PlanarityEdge *edge = gtk_canvas_item_get_item (ci);
+  GtkCanvas *canvas = gtk_canvas_item_get_canvas (ci);
+  GtkCanvasItem *from_item, *to_item;
+  const GtkCanvasBox *from_box, *to_box;
+  graphene_rect_t from_rect, to_rect;
+  graphene_point_t from_center, to_center;
+
+  from_item = gtk_canvas_lookup_item (canvas, edge->from);
+  to_item = gtk_canvas_lookup_item (canvas, edge->to);
+  from_box = gtk_canvas_item_get_allocation (from_item);
+  to_box = gtk_canvas_item_get_allocation (to_item);
+  if (from_box == NULL || to_box == NULL)
+    return FALSE;
+
+  gtk_canvas_box_to_rect (from_box, &from_rect);
+  gtk_canvas_box_to_rect (to_box, &to_rect);
+  graphene_rect_get_center (&from_rect, &from_center);
+  graphene_rect_get_center (&to_rect, &to_center);
+
+  gtk_canvas_box_init (out_box,
+                       from_center.x, from_center.y,
+                       to_center.x - from_center.x,
+                       to_center.y - from_center.y,
+                       0, 0);
+
+  return TRUE;
 }
 
 static void
 bind_item (GtkListItemFactory *factory,
            GtkCanvasItem      *ci)
 {
-  GtkCanvasBox *box;
   gpointer item;
 
   item = gtk_canvas_item_get_item (ci);
@@ -207,34 +237,17 @@ bind_item (GtkListItemFactory *factory,
       widget = gtk_image_new_from_icon_name ("media-record-symbolic");
       gtk_image_set_icon_size (GTK_IMAGE (widget), GTK_ICON_SIZE_LARGE);
       gesture = gtk_gesture_drag_new ();
-      g_signal_connect (gesture, "drag-update", G_CALLBACK (move_item), ci);
-      g_signal_connect (gesture, "drag-end", G_CALLBACK (move_item), ci);
+      g_signal_connect (gesture, "drag-update", G_CALLBACK (move_vertex), ci);
+      g_signal_connect (gesture, "drag-end", G_CALLBACK (move_vertex), ci);
       gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (gesture));
       gtk_canvas_item_set_widget (ci, widget);
 
-      set_item_position (ci);
+      gtk_canvas_item_set_compute_bounds (ci, set_vertex_bounds, NULL, NULL);
     }
   else if (PLANARITY_IS_EDGE (item))
     {
-      GtkCanvas *canvas = gtk_canvas_item_get_canvas (ci);
-      PlanarityEdge *edge = PLANARITY_EDGE (item);
-      GtkCanvasItem *from_item, *to_item;
-      const GtkCanvasBox *from_box, *to_box;
-      GtkCanvasVector *from_point, *to_point;
-
       gtk_canvas_item_set_widget (ci, gtk_diagonal_line_new ());
-
-      from_item = gtk_canvas_lookup_item (canvas, edge->from);
-      to_item = gtk_canvas_lookup_item (canvas, edge->to);
-      from_box = gtk_canvas_box_get_item_allocation (from_item);
-      to_box = gtk_canvas_box_get_item_allocation (to_item);
-      from_point = gtk_canvas_vector_new_from_box (from_box, 0.5, 0.5);
-      to_point = gtk_canvas_vector_new_from_box (to_box, 0.5, 0.5);
-      box = gtk_canvas_box_new_points (from_point, to_point);
-      gtk_canvas_item_set_bounds (ci, box);
-      gtk_canvas_box_free (box);
-      gtk_canvas_vector_free (from_point);
-      gtk_canvas_vector_free (to_point);
+      gtk_canvas_item_set_compute_bounds (ci, set_edge_bounds, NULL, NULL);
     }
 }
 
diff --git a/demos/gtk-demo/canvas_puzzle.c b/demos/gtk-demo/canvas_puzzle.c
index f1ee30703b..4fe20c3327 100644
--- a/demos/gtk-demo/canvas_puzzle.c
+++ b/demos/gtk-demo/canvas_puzzle.c
@@ -9,32 +9,25 @@
 
 #include "puzzlepiece.h"
 
-static void
-set_item_position (GtkCanvasItem *ci,
-                   float          x,
-                   float          y)
+static gboolean
+set_position_from_origin (GtkCanvasItem *ci,
+                          GtkCanvasBox  *out_box,
+                          gpointer       user_data)
 {
-  GtkCanvasVector *point, *size;
-  GtkCanvasBox *box, *viewport;
-
-  x = CLAMP (x, 0, 1);
-  y = CLAMP (y, 0, 1);
-
-  point = gtk_canvas_vector_new (0, 0);
-  viewport = gtk_canvas_box_new (point,
-                                 gtk_canvas_get_viewport_size (gtk_canvas_item_get_canvas (ci)),
-                                 0.0, 0.0);
-  gtk_canvas_vector_free (point);
-
-  point = gtk_canvas_vector_new_from_box (viewport, x, y);
-  gtk_canvas_box_free (viewport);
-  size = gtk_canvas_vector_new (0, 0);
-  box = gtk_canvas_box_new (point, size, x, y);
-  gtk_canvas_vector_free (point);
-  gtk_canvas_vector_free (size);
-
-  gtk_canvas_item_set_bounds (ci, box);
-  gtk_canvas_box_free (box);
+  GtkOrigin *origin = g_object_get_data (G_OBJECT (ci), "position");
+  const GtkCanvasBox *viewport = gtk_canvas_get_viewport (gtk_canvas_item_get_canvas (ci));
+
+  if (viewport == NULL)
+    return FALSE;
+
+  gtk_canvas_box_init (out_box,
+                       viewport->size.width * origin->horizontal,
+                       viewport->size.height * origin->vertical,
+                       0, 0,
+                       origin->horizontal,
+                       origin->vertical);
+
+  return TRUE;
 }
 
 static void
@@ -44,14 +37,15 @@ move_item (GtkGestureDrag *gesture,
            GtkCanvasItem  *ci)
 {
   GtkCanvas *canvas = gtk_canvas_item_get_canvas (ci);
-  graphene_rect_t bounds;
+  GtkOrigin *origin = g_object_get_data (G_OBJECT (ci), "position");
 
-  if (!gtk_canvas_box_eval (gtk_canvas_item_get_bounds (ci), &bounds))
-    return;
+  origin->horizontal += x / gtk_widget_get_width (GTK_WIDGET (canvas));
+  origin->vertical += y / gtk_widget_get_height (GTK_WIDGET (canvas));
+  origin->horizontal = CLAMP (origin->horizontal, 0, 1);
+  origin->vertical = CLAMP (origin->vertical, 0, 1);
+  g_print ("%g %g\n", origin->horizontal, origin->vertical);
 
-  set_item_position (ci,
-                     (bounds.origin.x + x) / (gtk_widget_get_width (GTK_WIDGET (canvas)) - 
bounds.size.width),
-                     (bounds.origin.y + y) / (gtk_widget_get_height (GTK_WIDGET (canvas)) - 
bounds.size.height));
+  gtk_canvas_item_invalidate_bounds (ci);
 }
 
 static void
@@ -60,6 +54,7 @@ bind_item (GtkListItemFactory *factory,
 {
   GtkWidget *widget;
   GtkGesture *gesture;
+  GtkOrigin *origin;
 
   widget = gtk_picture_new_for_paintable (gtk_canvas_item_get_item (ci));
   gtk_picture_set_can_shrink (GTK_PICTURE (widget), FALSE);
@@ -69,8 +64,15 @@ bind_item (GtkListItemFactory *factory,
   gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (gesture));
   gtk_canvas_item_set_widget (ci, widget);
 
-  /* Also center the item, so we do something interesting */
-  set_item_position (ci, g_random_double (), g_random_double ());
+  /* Set a random position */
+  origin = g_new (GtkOrigin, 1);
+  origin->horizontal = g_random_double ();
+  origin->vertical = g_random_double ();
+  g_object_set_data_full (G_OBJECT (ci), "position", origin, g_free);
+
+  gtk_canvas_item_set_compute_bounds (ci,
+                                      set_position_from_origin,
+                                      NULL, NULL);
 }
 
 static GListModel *
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 010d0fed01..051471c6f6 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -62,7 +62,6 @@
 #include <gtk/gtkcanvas.h>
 #include <gtk/gtkcanvasbox.h>
 #include <gtk/gtkcanvasitem.h>
-#include <gtk/gtkcanvasvector.h>
 #include <gtk/gtkcellarea.h>
 #include <gtk/gtkcellareabox.h>
 #include <gtk/gtkcellareacontext.h>
diff --git a/gtk/gtkcanvas.c b/gtk/gtkcanvas.c
index 2d5527ea08..2a78fddfa2 100644
--- a/gtk/gtkcanvas.c
+++ b/gtk/gtkcanvas.c
@@ -23,7 +23,6 @@
 
 #include "gtkcanvasbox.h"
 #include "gtkcanvasitemprivate.h"
-#include "gtkcanvasvectorprivate.h"
 #include "gtkintl.h"
 #include "gtklistitemfactory.h"
 #include "gtkwidgetprivate.h"
@@ -53,7 +52,8 @@ struct _GtkCanvas
   GtkCanvasItems items;
   GHashTable *item_lookup;
 
-  GtkCanvasVector viewport_size;
+  GtkCanvasBox viewport;
+  guint viewport_valid : 1;
 };
 
 enum
@@ -164,7 +164,6 @@ gtk_canvas_finalize (GObject *object)
   GtkCanvas *self = GTK_CANVAS (object);
 
   g_hash_table_unref (self->item_lookup);
-  gtk_canvas_vector_finish (&self->viewport_size);
 
   G_OBJECT_CLASS (gtk_canvas_parent_class)->finalize (object);
 }
@@ -218,15 +217,17 @@ gtk_canvas_set_property (GObject      *object,
 }
 
 static void
-gtk_canvas_validate_variables (GtkCanvas *self)
+gtk_canvas_invalidate_allocation (GtkCanvas *self)
 {
   int i;
 
+  self->viewport_valid = FALSE;
+
   for (i = 0; i < gtk_canvas_items_get_size (&self->items); i++)
     {
       GtkCanvasItem *ci = gtk_canvas_items_get (&self->items, i);
 
-      gtk_canvas_item_validate_variables (ci);
+      gtk_canvas_item_invalidate_allocation (ci);
     }
 }
 
@@ -240,9 +241,13 @@ gtk_canvas_allocate (GtkWidget *widget,
   gboolean missing, force, success;
   gsize i;
 
-  gtk_canvas_validate_variables (self);
+  gtk_canvas_invalidate_allocation (self);
 
-  gtk_canvas_vector_init_constant (gtk_canvas_vector_get_variable (&self->viewport_size), width, height);
+  self->viewport_valid = TRUE;
+  gtk_canvas_box_init (&self->viewport,
+                       0, 0,
+                       width, height,
+                       0, 0);
 
   force = FALSE;
   do
@@ -254,71 +259,17 @@ gtk_canvas_allocate (GtkWidget *widget,
       for (i = 0; i < gtk_canvas_items_get_size (&self->items); i++)
         {
           GtkCanvasItem *ci = gtk_canvas_items_get (&self->items, i);
-          GtkWidget *child = gtk_canvas_item_get_widget (ci);
-          const GtkCanvasBox *bounds;
-          graphene_rect_t rect;
-          int x, y, w, h;
 
-          if (child == NULL || gtk_canvas_item_has_allocation (ci))
+          if (gtk_canvas_item_has_allocation (ci))
             continue;
 
-          bounds = gtk_canvas_item_get_bounds (ci);
-          if (!gtk_canvas_box_eval (bounds, &rect))
-            {
-              if (force)
-                {
-                  rect = *graphene_rect_zero ();
-                  /* XXX: set force to FALSE? */
-                }
-              else
-                {
-                  missing = TRUE;
-                  continue;
-                }
-            }
-
-          if (gtk_widget_get_request_mode (child) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
-            {
-              gtk_widget_measure (child, GTK_ORIENTATION_HORIZONTAL, -1, &w, NULL, NULL, NULL);
-              w = MAX (w, ceil (ABS (rect.size.width)));
-              gtk_widget_measure (child, GTK_ORIENTATION_VERTICAL, w, &h, NULL, NULL, NULL);
-              h = MAX (h, ceil (ABS (rect.size.height)));
-            }
-          else
-            {
-              gtk_widget_measure (child, GTK_ORIENTATION_VERTICAL, -1, &h, NULL, NULL, NULL);
-              h = MAX (h, ceil (ABS (rect.size.height)));
-              gtk_widget_measure (child, GTK_ORIENTATION_HORIZONTAL, h, &w, NULL, NULL, NULL);
-              w = MAX (w, ceil (ABS (rect.size.width)));
-            }
-
-          if (rect.size.width < 0)
-            w = -w;
-          if (rect.size.height < 0)
-            h = -h;
-          if (ABS (w) > ABS (rect.size.width) || ABS (h) > ABS (rect.size.height))
-            {
-              graphene_vec2_t origin;
-
-              if (!gtk_canvas_vector_eval (gtk_canvas_box_get_origin (bounds), &origin))
-                graphene_vec2_init_from_vec2 (&origin, graphene_vec2_zero ());
-
-              if (ABS (w) > ABS (rect.size.width))
-                x = round (rect.origin.x + graphene_vec2_get_x (&origin) * (rect.size.width - w));
-              else
-                x = round (rect.origin.x);
-              if (ABS (h) > ABS (rect.size.height))
-                y = round (rect.origin.y + graphene_vec2_get_y (&origin) * (rect.size.height - h));
-              else
-                y = round (rect.origin.y);
-            }
-          else
+          if (!gtk_canvas_item_allocate (ci, force))
             {
-              x = round (rect.origin.x);
-              y = round (rect.origin.y);
+              g_assert (!force);
+              missing = TRUE;
+              continue;
             }
 
-          gtk_canvas_item_allocate (ci, &GRAPHENE_RECT_INIT (x, y, w, h));
           success = TRUE;
         }
       if (!success)
@@ -380,8 +331,6 @@ static void
 gtk_canvas_init (GtkCanvas *self)
 {
   self->item_lookup = g_hash_table_new (g_direct_hash, g_direct_equal);
-
-  gtk_canvas_vector_init_variable (&self->viewport_size, "viewport.size");
 }
 
 /**
@@ -554,8 +503,23 @@ gtk_canvas_lookup_item (GtkCanvas *self,
   return g_hash_table_lookup (self->item_lookup, item);
 }
 
-const GtkCanvasVector *
-gtk_canvas_get_viewport_size (GtkCanvas *self)
+/**
+ * gtk_canvas_get_viewport:
+ * @self: a `GtkCanvas`
+ *
+ * Gets the viewport of the canvas. If no viewport is available,
+ * in particular if it has not been determined during size
+ * allocation, %NULL is returned.
+ *
+ * Returns: The viewport
+ **/
+const GtkCanvasBox *
+gtk_canvas_get_viewport (GtkCanvas *self)
 {
-  return &self->viewport_size;
+  g_return_val_if_fail (GTK_IS_CANVAS (self), NULL);
+
+  if (!self->viewport_valid)
+    return NULL;
+
+  return &self->viewport;
 }
diff --git a/gtk/gtkcanvas.h b/gtk/gtkcanvas.h
index 75e8f8b093..98e7a398e8 100644
--- a/gtk/gtkcanvas.h
+++ b/gtk/gtkcanvas.h
@@ -54,7 +54,7 @@ GtkCanvasItem *         gtk_canvas_lookup_item                  (GtkCanvas
                                                                  gpointer                item);
 
 GDK_AVAILABLE_IN_ALL
-const GtkCanvasVector * gtk_canvas_get_viewport_size            (GtkCanvas              *self);
+const GtkCanvasBox *    gtk_canvas_get_viewport                 (GtkCanvas              *self);
 
 G_END_DECLS
 
diff --git a/gtk/gtkcanvasbox.c b/gtk/gtkcanvasbox.c
index 56388c2bab..cb5a175afa 100644
--- a/gtk/gtkcanvasbox.c
+++ b/gtk/gtkcanvasbox.c
@@ -29,141 +29,12 @@
 
 #include "config.h"
 
-#include "gtkcanvasboxprivate.h"
-
-/* {{{ Boilerplate */
+#include "gtkcanvasbox.h"
 
 G_DEFINE_BOXED_TYPE (GtkCanvasBox, gtk_canvas_box,
                      gtk_canvas_box_copy,
                      gtk_canvas_box_free)
 
-void
-gtk_canvas_box_init (GtkCanvasBox          *self,
-                     const GtkCanvasVector *point,
-                     const GtkCanvasVector *size,
-                     const GtkCanvasVector *origin)
-{
-  gtk_canvas_vector_init_copy (&self->point, point);
-  gtk_canvas_vector_init_copy (&self->size, size);
-  gtk_canvas_vector_init_copy (&self->origin, origin);
-}
-
-void
-gtk_canvas_box_init_copy (GtkCanvasBox       *self,
-                          const GtkCanvasBox *source)
-{
-  gtk_canvas_box_init (self, &source->point, &source->size, &source->origin);
-}
-
-void
-gtk_canvas_box_finish (GtkCanvasBox *self)
-{
-  gtk_canvas_vector_finish (&self->point);
-  gtk_canvas_vector_finish (&self->size);
-  gtk_canvas_vector_finish (&self->origin);
-}
-
-void
-gtk_canvas_box_init_variable (GtkCanvasBox *self,
-                              const char   *format,
-                              ...)
-{
-  char *name;
-  va_list args;
-
-  va_start (args, format);
-  name = g_strdup_vprintf (format, args);
-  va_end (args);
-
-  gtk_canvas_vector_init_variable (&self->point, "%s.point", name);
-  gtk_canvas_vector_init_variable (&self->size, "%s.size", name);
-  gtk_canvas_vector_init_variable (&self->origin, "%s.origin", name);
-
-  g_free (name);
-}
-
-void
-gtk_canvas_box_update_variable (GtkCanvasBox       *self,
-                                const GtkCanvasBox *other)
-{
-  gtk_canvas_vector_init_copy (gtk_canvas_vector_get_variable (&self->point), &other->point);
-  gtk_canvas_vector_init_copy (gtk_canvas_vector_get_variable (&self->size), &other->size);
-  gtk_canvas_vector_init_copy (gtk_canvas_vector_get_variable (&self->origin), &other->origin);
-}
-
-/**
- * gtk_canvas_box_new_points:
- * @point1: the first point
- * @point2: the second point
- *
- * Creates a new box describing the rectangle between the two
- * points
- *
- * Returns: a new box
- **/
-GtkCanvasBox *
-gtk_canvas_box_new_points (const GtkCanvasVector *point1,
-                           const GtkCanvasVector *point2)
-{
-  GtkCanvasVector size, origin;
-  GtkCanvasBox *result;
-  graphene_vec2_t minus_one;
-
-  g_return_val_if_fail (point1 != NULL, NULL);
-  g_return_val_if_fail (point2 != NULL, NULL);
-
-  graphene_vec2_init (&minus_one, -1.f, -1.f);
-  gtk_canvas_vector_init_sum (&size,
-                              graphene_vec2_one (),
-                              point2,
-                              &minus_one,
-                              point1,
-                              NULL);
-  gtk_canvas_vector_init_constant (&origin, 0, 0);
-  result = g_slice_new (GtkCanvasBox);
-  gtk_canvas_box_init (result, (GtkCanvasVector *) point1, &size, &origin);
-  gtk_canvas_vector_finish (&size);
-  gtk_canvas_vector_finish (&origin);
-
-  return result;
-}
-
-/**
- * gtk_canvas_box_new:
- * @point: the origin point of the box
- * @size: size of the box
- * @origin_x: x coordinate of origin
- * @origin_y: y coordinate of origin
- *
- * Creates a new box of the given size relative to the given point.
- * The origin describes where in the box the point is located.
- * (0, 0) means the point describes the top left of the box, (1, 1)
- * describes the bottom right, and (0.5, 0.5) is the center.
- *
- * Returns: a new box
- **/
-GtkCanvasBox *
-gtk_canvas_box_new (const GtkCanvasVector *point,
-                    const GtkCanvasVector *size,
-                    float                  origin_x,
-                    float                  origin_y)
-{
-  GtkCanvasBox *self;
-  GtkCanvasVector origin;
-
-  g_return_val_if_fail (point != NULL, NULL);
-  g_return_val_if_fail (size != NULL, NULL);
-
-  gtk_canvas_vector_init_constant (&origin, origin_x, origin_y);
-
-  self = g_slice_new (GtkCanvasBox);
-  gtk_canvas_box_init (self, point, size, &origin);
-
-  gtk_canvas_vector_finish (&origin);
-
-  return self;
-}
-
 GtkCanvasBox *
 gtk_canvas_box_copy (const GtkCanvasBox *self)
 {
@@ -171,8 +42,7 @@ gtk_canvas_box_copy (const GtkCanvasBox *self)
 
   g_return_val_if_fail (self != NULL, NULL);
 
-  copy = g_slice_new (GtkCanvasBox);
-  gtk_canvas_box_init_copy (copy, self);
+  copy = g_slice_dup (GtkCanvasBox, self);
 
   return copy;
 }
@@ -180,59 +50,32 @@ gtk_canvas_box_copy (const GtkCanvasBox *self)
 void
 gtk_canvas_box_free (GtkCanvasBox *self)
 {
-  gtk_canvas_box_finish (self);
-
   g_slice_free (GtkCanvasBox, self);
 }
 
-gboolean
-gtk_canvas_box_eval (const GtkCanvasBox *self,
-                     graphene_rect_t    *rect)
-{
-  graphene_vec2_t point, size, origin;
-
-  g_return_val_if_fail (self != NULL, FALSE);
-  g_return_val_if_fail (rect != NULL, FALSE);
-
-  if (!gtk_canvas_vector_eval (&self->point, &point) ||
-      !gtk_canvas_vector_eval (&self->size, &size) ||
-      !gtk_canvas_vector_eval (&self->origin, &origin))
-    {
-      *rect = *graphene_rect_zero ();
-      return FALSE;
-    }
-
-  graphene_vec2_multiply (&origin, &size, &origin);
-  graphene_vec2_subtract (&point, &origin, &point);
-
-  *rect = GRAPHENE_RECT_INIT (graphene_vec2_get_x (&point),
-                              graphene_vec2_get_y (&point),
-                              graphene_vec2_get_x (&size),
-                              graphene_vec2_get_y (&size));
-
-  return TRUE;
-}
-
-const GtkCanvasVector *
-gtk_canvas_box_get_point (const GtkCanvasBox *self)
-{
-  g_return_val_if_fail (self != NULL, NULL);
-
-  return &self->point;
-}
-
-const GtkCanvasVector *
-gtk_canvas_box_get_size (const GtkCanvasBox *self)
+void
+gtk_canvas_box_init (GtkCanvasBox *self,
+                     float         point_x,
+                     float         point_y,
+                     float         width,
+                     float         height,
+                     float         origin_horizontal,
+                     float         origin_vertical)
 {
-  g_return_val_if_fail (self != NULL, NULL);
-
-  return &self->size;
+  self->point.x = point_x;
+  self->point.y = point_y;
+  self->size.width = width;
+  self->size.height = height;
+  self->origin.horizontal = origin_horizontal;
+  self->origin.vertical = origin_vertical;
 }
 
-const GtkCanvasVector *
-gtk_canvas_box_get_origin (const GtkCanvasBox *self)
+void
+gtk_canvas_box_to_rect (const GtkCanvasBox *self,
+                        graphene_rect_t    *rect)
 {
-  g_return_val_if_fail (self != NULL, NULL);
-
-  return &self->origin;
+  rect->size = self->size;
+  rect->origin = GRAPHENE_POINT_INIT (self->point.x - self->origin.horizontal * self->size.width,
+                                      self->point.y - self->origin.vertical * self->size.height);   
+  graphene_rect_normalize (rect);
 }
diff --git a/gtk/gtkcanvasbox.h b/gtk/gtkcanvasbox.h
index 2f08699f5a..e9bba1bc83 100644
--- a/gtk/gtkcanvasbox.h
+++ b/gtk/gtkcanvasbox.h
@@ -30,6 +30,20 @@
 
 G_BEGIN_DECLS
 
+typedef struct _GtkOrigin GtkOrigin;
+
+struct _GtkOrigin {
+  float           horizontal;
+  float           vertical;
+};
+
+struct _GtkCanvasBox
+{
+  graphene_point_t point;
+  graphene_size_t  size;
+  GtkOrigin        origin;
+};
+
 #define GTK_TYPE_CANVAS_BOX (gtk_canvas_box_get_type ())
 
 GDK_AVAILABLE_IN_ALL
@@ -41,30 +55,17 @@ GDK_AVAILABLE_IN_ALL
 void                    gtk_canvas_box_free                   (GtkCanvasBox             *self);
 
 GDK_AVAILABLE_IN_ALL
-const GtkCanvasVector * gtk_canvas_box_get_point              (const GtkCanvasBox       *self) G_GNUC_PURE;
-GDK_AVAILABLE_IN_ALL
-const GtkCanvasVector * gtk_canvas_box_get_size               (const GtkCanvasBox       *self) G_GNUC_PURE;
-GDK_AVAILABLE_IN_ALL
-const GtkCanvasVector * gtk_canvas_box_get_origin             (const GtkCanvasBox       *self) G_GNUC_PURE;
-
-GDK_AVAILABLE_IN_ALL
-gboolean                gtk_canvas_box_eval                   (const GtkCanvasBox       *self,
-                                                               graphene_rect_t          *rect) 
G_GNUC_WARN_UNUSED_RESULT;
-
-GDK_AVAILABLE_IN_ALL
-GtkCanvasBox *          gtk_canvas_box_new                    (const GtkCanvasVector    *point,
-                                                               const GtkCanvasVector    *size,
-                                                               float                     origin_x,
-                                                               float                     origin_y);
-GDK_AVAILABLE_IN_ALL
-GtkCanvasBox *          gtk_canvas_box_new_points             (const GtkCanvasVector    *point1,
-                                                               const GtkCanvasVector    *point2);
+void                    gtk_canvas_box_init                   (GtkCanvasBox             *self,
+                                                               float                     point_x,
+                                                               float                     point_y,
+                                                               float                     width,
+                                                               float                     height,
+                                                               float                     origin_horizontal,
+                                                               float                     origin_vertical);
 
 GDK_AVAILABLE_IN_ALL
-const GtkCanvasBox *    gtk_canvas_box_get_item_bounds        (GtkCanvasItem            *item);
-GDK_AVAILABLE_IN_ALL
-const GtkCanvasBox *    gtk_canvas_box_get_item_allocation    (GtkCanvasItem            *item);
-
+void                    gtk_canvas_box_to_rect                (const GtkCanvasBox       *self,
+                                                               graphene_rect_t          *rect);
 G_END_DECLS
 
 #endif /* __GTK_BOX_H__ */
diff --git a/gtk/gtkcanvasitem.c b/gtk/gtkcanvasitem.c
index 0ed91e7291..e19c9017e1 100644
--- a/gtk/gtkcanvasitem.c
+++ b/gtk/gtkcanvasitem.c
@@ -22,7 +22,7 @@
 #include "gtkcanvasitemprivate.h"
 
 #include "gtkcanvas.h"
-#include "gtkcanvasboxprivate.h"
+#include "gtkcanvasbox.h"
 #include "gtkintl.h"
 #include "gtklistitemfactoryprivate.h"
 #include "gtkwidget.h"
@@ -41,17 +41,19 @@ struct _GtkCanvasItem
   GtkCanvas *canvas;
   gpointer item;
   GtkWidget *widget;
+  GtkCanvasItemComputeBoundsFunc compute_bounds_func;
+  gpointer user_data;
+  GDestroyNotify user_destroy;
+
   GtkCanvasBox bounds;
-  GtkCanvasBox bounds_var;
-  GtkCanvasBox allocation_var;
+  GtkCanvasBox allocation;
 
-  GtkCanvasVector size_vecs[4];
+  guint has_allocation : 1;
 };
 
 enum
 {
   PROP_0,
-  PROP_BOUNDS,
   PROP_CANVAS,
   PROP_ITEM,
   PROP_WIDGET,
@@ -74,23 +76,12 @@ gtk_canvas_item_dispose (GObject *object)
   g_assert (self->item == NULL);
   g_assert (self->widget == NULL);
 
-  G_OBJECT_CLASS (gtk_canvas_item_parent_class)->dispose (object);
-}
+  if (self->user_destroy)
+    self->user_destroy (self->user_data);
+  self->user_destroy = NULL;
+  self->user_data = NULL;
 
-static void
-gtk_canvas_item_finalize (GObject *object)
-{
-  GtkCanvasItem *self = GTK_CANVAS_ITEM (object);
-  int i;
-
-  for (i = 0; i < 4; i++)
-    gtk_canvas_vector_finish (&self->size_vecs[i]);
-
-  gtk_canvas_box_finish (&self->bounds);
-  gtk_canvas_box_finish (&self->bounds_var);
-  gtk_canvas_box_finish (&self->allocation_var);
-
-  G_OBJECT_CLASS (gtk_canvas_item_parent_class)->finalize (object);
+  G_OBJECT_CLASS (gtk_canvas_item_parent_class)->dispose (object);
 }
 
 static void
@@ -103,10 +94,6 @@ gtk_canvas_item_get_property (GObject    *object,
 
   switch (property_id)
     {
-    case PROP_BOUNDS:
-      g_value_set_boxed (value, &self->bounds);
-      break;
-
     case PROP_CANVAS:
       g_value_set_object (value, self->canvas);
       break;
@@ -135,10 +122,6 @@ gtk_canvas_item_set_property (GObject      *object,
 
   switch (property_id)
     {
-    case PROP_BOUNDS:
-      gtk_canvas_item_set_bounds (self, g_value_get_boxed (value));
-      break;
-
     case PROP_WIDGET:
       gtk_canvas_item_set_widget (self, g_value_get_object (value));
       break;
@@ -155,20 +138,9 @@ gtk_canvas_item_class_init (GtkCanvasItemClass *klass)
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
   gobject_class->dispose = gtk_canvas_item_dispose;
-  gobject_class->finalize = gtk_canvas_item_finalize;
   gobject_class->get_property = gtk_canvas_item_get_property;
   gobject_class->set_property = gtk_canvas_item_set_property;
 
-  /**
-   * GtkCanvasItem:bounds: (attributes org.gtk.Property.get=gtk_canvas_item_get_bounds 
org.gtk.Property.set=gtk_canvas_item_set_bounds)
-   *
-   * The bounds to place the widget into.
-   */
-  properties[PROP_BOUNDS] =
-    g_param_spec_boxed ("bounds", NULL, NULL,
-                        GTK_TYPE_CANVAS_BOX,
-                        G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
-
   /**
    * GtkCanvasItem:canvas: (attributes org.gtk.Property.get=gtk_canvas_item_get_canvas 
org.gtk.Property.set=gtk_canvas_item_set_canvas)
    *
@@ -205,17 +177,6 @@ gtk_canvas_item_class_init (GtkCanvasItemClass *klass)
 static void
 gtk_canvas_item_init (GtkCanvasItem *self)
 {
-  gtk_canvas_vector_init_variable (&self->size_vecs[0], "item%p.min_for_min", self);
-  gtk_canvas_vector_init_variable (&self->size_vecs[1], "item%p.min_for_nat", self);
-  gtk_canvas_vector_init_variable (&self->size_vecs[2], "item%p.nat_for_min", self);
-  gtk_canvas_vector_init_variable (&self->size_vecs[3], "item%p.nat_for_nat", self);
-
-  gtk_canvas_vector_init_constant (&self->bounds.point, 0, 0);
-  gtk_canvas_vector_init_copy (&self->bounds.size, &self->size_vecs[GTK_CANVAS_ITEM_MEASURE_NAT_FOR_NAT]);
-  gtk_canvas_vector_init_constant (&self->bounds.origin, 0.5, 0.5);
-  gtk_canvas_box_init_variable (&self->bounds_var, "item%p.bounds", self);
-  gtk_canvas_box_update_variable (&self->bounds_var, &self->bounds);
-  gtk_canvas_box_init_variable (&self->allocation_var, "item%p.allocation", self);
 }
 
 GtkCanvasItem *
@@ -236,70 +197,72 @@ gtk_canvas_item_new (GtkCanvas *canvas,
 }
 
 void
-gtk_canvas_item_validate_variables (GtkCanvasItem *self)
+gtk_canvas_item_invalidate_allocation (GtkCanvasItem *self)
 {
-  int w[4], h[4], i;
+  self->has_allocation = FALSE;
+}
 
-  if (self->widget == NULL)
+gboolean
+gtk_canvas_item_allocate (GtkCanvasItem *self,
+                          gboolean       force)
+{
+  int w, h;
+
+  g_assert (!self->has_allocation);
+
+  if (!self->compute_bounds_func)
     {
-      memset (w, 0, sizeof (w));
-      memset (h, 0, sizeof (h));
+      gtk_canvas_box_init (&self->bounds, 0, 0, 0, 0, 0.5, 0.5);
     }
-  else
+  else if (!self->compute_bounds_func (self, &self->bounds, self->user_data))
+    {
+      if (!force)
+        return FALSE;
+      gtk_canvas_box_init (&self->bounds, 0, 0, 0, 0, 0.5, 0.5);
+    }
+
+  if (self->widget)
     {
       if (gtk_widget_get_request_mode (self->widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
         {
-          gtk_widget_measure (self->widget, GTK_ORIENTATION_HORIZONTAL, -1, &w[0], &w[2], NULL, NULL);
-          w[1] = w[0];
-          gtk_widget_measure (self->widget, GTK_ORIENTATION_VERTICAL, w[0], &h[0], &h[1], NULL, NULL);
-          w[3] = w[2];
-          gtk_widget_measure (self->widget, GTK_ORIENTATION_VERTICAL, w[2], &h[2], &h[3], NULL, NULL);
+          gtk_widget_measure (self->widget, GTK_ORIENTATION_HORIZONTAL, -1, &w, NULL, NULL, NULL);
+          w = MAX (w, ceil (ABS (self->bounds.size.width)));
+          gtk_widget_measure (self->widget, GTK_ORIENTATION_VERTICAL, w, &h, NULL, NULL, NULL);
+          h = MAX (h, ceil (ABS (self->bounds.size.height)));
         }
       else
         {
-          gtk_widget_measure (self->widget, GTK_ORIENTATION_VERTICAL, -1, &h[0], &h[2], NULL, NULL);
-          h[1] = h[0];
-          gtk_widget_measure (self->widget, GTK_ORIENTATION_HORIZONTAL, h[0], &w[0], &w[1], NULL, NULL);
-          h[3] = h[2];
-          gtk_widget_measure (self->widget, GTK_ORIENTATION_HORIZONTAL, h[2], &w[2], &w[3], NULL, NULL);
+          gtk_widget_measure (self->widget, GTK_ORIENTATION_VERTICAL, -1, &h, NULL, NULL, NULL);
+          h = MAX (h, ceil (ABS (self->bounds.size.height)));
+          gtk_widget_measure (self->widget, GTK_ORIENTATION_HORIZONTAL, h, &w, NULL, NULL, NULL);
+          w = MAX (w, ceil (ABS (self->bounds.size.width)));
         }
-    }
 
-  for (i = 0; i < 4; i++)
+      if (self->bounds.size.width >= 0)
+        w = MAX (self->bounds.size.width, w);
+      else
+        w = MIN (self->bounds.size.width, -w);
+      if (self->bounds.size.height >= 0)
+        h = MAX (self->bounds.size.height, h);
+      else
+        h = MIN (self->bounds.size.height, -h);
+    }
+  else
     {
-      gtk_canvas_vector_init_constant (
-          gtk_canvas_vector_get_variable (&self->size_vecs[i]),
-          0, 0);
+      w = 0;
+      h = 0;
     }
 
-  gtk_canvas_vector_init_invalid (
-      gtk_canvas_vector_get_variable (&self->allocation_var.point));
-  gtk_canvas_vector_init_invalid (
-      gtk_canvas_vector_get_variable (&self->allocation_var.size));
-  gtk_canvas_vector_init_invalid (
-      gtk_canvas_vector_get_variable (&self->allocation_var.origin));
-}
+  gtk_canvas_box_init (&self->allocation,
+                       round (self->bounds.point.x - self->bounds.origin.horizontal * w)
+                         + self->bounds.origin.horizontal * w,
+                       round (self->bounds.point.y - self->bounds.origin.vertical * h)
+                         + self->bounds.origin.vertical * h,
+                       w, h,
+                       self->bounds.origin.horizontal, self->bounds.origin.vertical);
+  self->has_allocation = TRUE;
 
-void
-gtk_canvas_item_allocate (GtkCanvasItem   *self,
-                          graphene_rect_t *rect)
-{
-  graphene_vec2_t origin;
-
-  if (!gtk_canvas_vector_eval (&self->bounds.origin, &origin))
-    graphene_vec2_init_from_vec2 (&origin, graphene_vec2_zero ());
-
-  gtk_canvas_vector_init_constant (
-      gtk_canvas_vector_get_variable (&self->allocation_var.point),
-      rect->origin.x + graphene_vec2_get_x (&origin) * rect->size.width,
-      rect->origin.y + graphene_vec2_get_y (&origin) * rect->size.height);
-  gtk_canvas_vector_init_constant (
-      gtk_canvas_vector_get_variable (&self->allocation_var.size),
-      rect->size.width, rect->size.height);
-  gtk_canvas_vector_init_constant (
-      gtk_canvas_vector_get_variable (&self->allocation_var.origin),
-      graphene_vec2_get_x (&origin),
-      graphene_vec2_get_y (&origin));
+  return TRUE;
 }
 
 void
@@ -312,13 +275,9 @@ gtk_canvas_item_allocate_widget (GtkCanvasItem *self,
   if (self->widget == NULL)
     return;
 
-  if (!gtk_canvas_box_eval (&self->allocation_var, &allocation))
-    {
-      /* gtkcanvas.c will not call this function otherwise */
-      g_assert_not_reached ();
-    }
-
+  gtk_canvas_box_to_rect (&self->allocation, &allocation);
   graphene_rect_normalize (&allocation);
+
   gtk_widget_size_allocate (self->widget,
                             &(GtkAllocation) {
                               allocation.origin.x - dx,
@@ -331,32 +290,7 @@ gtk_canvas_item_allocate_widget (GtkCanvasItem *self,
 gboolean
 gtk_canvas_item_has_allocation (GtkCanvasItem *self)
 {
-  return !gtk_canvas_vector_is_invalid (gtk_canvas_vector_get_variable (&self->allocation_var.point));
-}
-
-const GtkCanvasVector *
-gtk_canvas_vector_get_item_measure (GtkCanvasItem        *item,
-                                    GtkCanvasItemMeasure  measure)
-{
-  g_return_val_if_fail (GTK_IS_CANVAS_ITEM (item), NULL);
-
-  return &item->size_vecs[measure];
-}
-
-const GtkCanvasBox *
-gtk_canvas_box_get_item_bounds (GtkCanvasItem *item)
-{
-  g_return_val_if_fail (GTK_IS_CANVAS_ITEM (item), NULL);
-
-  return &item->bounds_var;
-}
-
-const GtkCanvasBox *
-gtk_canvas_box_get_item_allocation (GtkCanvasItem *item)
-{
-  g_return_val_if_fail (GTK_IS_CANVAS_ITEM (item), NULL);
-
-  return &item->allocation_var;
+  return self->has_allocation;
 }
 
 /**
@@ -395,44 +329,98 @@ gtk_canvas_item_get_item (GtkCanvasItem *self)
 }
 
 /**
- * gtk_canvas_item_set_bounds: (attributes org.gtk.Method.set_property=bounds)
+ * gtk_canvas_item_set_compute_bounds:
  * @self: a `GtkCanvasItem`
- * @bounds: (transfer none): the bounds to allocate the widget in
+ * @compute_bounds_func: the function to compute bounds
+ * @user_data: (nullable): user data to pass to @compute_bounds_func
+ * @user_destroy: destroy notify for @user_data
+ *
+ * Sets the function to call to compute bounds during allocation.
  *
- * Sets the box to allocate the widget into.
+ * This function may be called multiple times if it returned %FALSE
+ * previously.
+ *
+ * Because of that the function is expected to be pure - not set
+ * any properties or have other side effects - and idempotent - 
+ * return the same result if called multiple times in order.
  */
 void
-gtk_canvas_item_set_bounds (GtkCanvasItem      *self,
-                            const GtkCanvasBox *bounds)
+gtk_canvas_item_set_compute_bounds (GtkCanvasItem                  *self,
+                                    GtkCanvasItemComputeBoundsFunc  compute_bounds_func,
+                                    gpointer                        user_data,
+                                    GDestroyNotify                  user_destroy)
 {
   g_return_if_fail (GTK_IS_CANVAS_ITEM (self));
-  g_return_if_fail (bounds != NULL);
 
-  gtk_canvas_box_init_copy (&self->bounds, bounds);
-  gtk_canvas_box_update_variable (&self->bounds_var, bounds);
+  if (self->user_destroy)
+    self->user_destroy (self->user_data);
+
+  self->compute_bounds_func = compute_bounds_func;
+  self->user_data = user_data;
+  self->user_destroy = user_destroy;
+}
+
+void
+gtk_canvas_item_invalidate_bounds (GtkCanvasItem *self)
+{
+  g_return_if_fail (GTK_IS_CANVAS_ITEM (self));
 
   if (self->canvas)
     gtk_widget_queue_allocate (GTK_WIDGET (self->canvas));
-
-  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_BOUNDS]);
 }
 
 /**
- * gtk_canvas_item_get_bounds: (attributes org.gtk.Method.get_property=bounds)
+ * gtk_canvas_item_get_bounds:
  * @self: a `GtkCanvasItem`
  *
  * Gets the bounds that are used to allocate the widget
  *
- * Returns: (transfer none): The bounds
+ * If the bounds are not known yet - for example when called during the size
+ * allocation phase before this item has succesfully computed its bounds -
+ * this function returns %NULL.
+ *
+ * See also gtk_canvas_item_get_allocation().
+ *
+ * Returns: (transfer none) (nullable): The bounds
  */
 const GtkCanvasBox *
 gtk_canvas_item_get_bounds (GtkCanvasItem *self)
 {
   g_return_val_if_fail (GTK_IS_CANVAS_ITEM (self), NULL);
 
+  if (!self->has_allocation)
+    return NULL;
+
   return &self->bounds;
 }
 
+/**
+ * gtk_canvas_item_get_allocation:
+ * @self: a `GtkCanvasItem`
+ *
+ * Gets the allocation assigned to the widget.
+ *
+ * If the bounds are not known yet - for example when called during the size
+ * allocation phase before this item has succesfully computed its bounds -
+ * this function returns %NULL.
+ *
+ * Compared with gtk_canvas_item_get_bounds(), this function returns the actual
+ * box used to allocate the widget, which may be different from the bounds
+ * to conform to its size requirements.
+ *
+ * Returns: (transfer none) (nullable): The allocation
+ */
+const GtkCanvasBox *
+gtk_canvas_item_get_allocation (GtkCanvasItem *self)
+{
+  g_return_val_if_fail (GTK_IS_CANVAS_ITEM (self), NULL);
+
+  if (!self->has_allocation)
+    return NULL;
+
+  return &self->allocation;
+}
+
 /**
  * gtk_canvas_item_set_widget: (attributes org.gtk.Method.set_property=widget)
  * @self: a `GtkCanvasItem`
diff --git a/gtk/gtkcanvasitem.h b/gtk/gtkcanvasitem.h
index 15f3ae7bf4..488656aa2f 100644
--- a/gtk/gtkcanvasitem.h
+++ b/gtk/gtkcanvasitem.h
@@ -35,6 +35,9 @@ G_BEGIN_DECLS
 GDK_AVAILABLE_IN_ALL
 G_DECLARE_FINAL_TYPE (GtkCanvasItem, gtk_canvas_item, GTK, CANVAS_ITEM, GObject)
 
+typedef gboolean        (* GtkCanvasItemComputeBoundsFunc)      (GtkCanvasItem          *ci,
+                                                                 GtkCanvasBox           *out_box,
+                                                                 gpointer                user_data);
 GDK_AVAILABLE_IN_ALL
 GtkCanvas *             gtk_canvas_item_get_canvas              (GtkCanvasItem          *self);
 GDK_AVAILABLE_IN_ALL
@@ -47,10 +50,17 @@ GDK_AVAILABLE_IN_ALL
 GtkWidget *             gtk_canvas_item_get_widget              (GtkCanvasItem          *self);
 
 GDK_AVAILABLE_IN_ALL
-void                    gtk_canvas_item_set_bounds              (GtkCanvasItem          *self,
-                                                                 const GtkCanvasBox     *box);
+void                    gtk_canvas_item_set_compute_bounds      (GtkCanvasItem          *self,
+                                                                 GtkCanvasItemComputeBoundsFunc 
compute_bounds_func,
+                                                                 gpointer                user_data,
+                                                                 GDestroyNotify          user_destroy);
+GDK_AVAILABLE_IN_ALL
+void                    gtk_canvas_item_invalidate_bounds       (GtkCanvasItem          *self);
+
 GDK_AVAILABLE_IN_ALL
 const GtkCanvasBox *    gtk_canvas_item_get_bounds              (GtkCanvasItem          *self);
+GDK_AVAILABLE_IN_ALL
+const GtkCanvasBox *    gtk_canvas_item_get_allocation          (GtkCanvasItem          *self);
 
 G_END_DECLS
 
diff --git a/gtk/gtkcanvasitemprivate.h b/gtk/gtkcanvasitemprivate.h
index cf1adbfc24..f52377cecd 100644
--- a/gtk/gtkcanvasitemprivate.h
+++ b/gtk/gtkcanvasitemprivate.h
@@ -3,16 +3,14 @@
 
 #include "gtkcanvasitem.h"
 
-#include "gtkcanvasvectorprivate.h"
-
 G_BEGIN_DECLS
 
 GtkCanvasItem *         gtk_canvas_item_new                      (GtkCanvas             *canvas,
                                                                   gpointer               item);
 
-void                    gtk_canvas_item_validate_variables       (GtkCanvasItem         *self);
-void                    gtk_canvas_item_allocate                 (GtkCanvasItem         *self,
-                                                                  graphene_rect_t       *rect);
+void                    gtk_canvas_item_invalidate_allocation    (GtkCanvasItem         *self);
+gboolean                gtk_canvas_item_allocate                 (GtkCanvasItem         *self,
+                                                                  gboolean               force);
 void                    gtk_canvas_item_allocate_widget          (GtkCanvasItem         *self,
                                                                   float                  dx,
                                                                   float                  dy);
diff --git a/gtk/gtktypes.h b/gtk/gtktypes.h
index da949a6bd9..cbf6045163 100644
--- a/gtk/gtktypes.h
+++ b/gtk/gtktypes.h
@@ -40,7 +40,6 @@ typedef struct _GtkBuilderScope        GtkBuilderScope;
 typedef struct _GtkCanvas              GtkCanvas;
 typedef struct _GtkCanvasBox           GtkCanvasBox;
 typedef struct _GtkCanvasItem          GtkCanvasItem;
-typedef struct _GtkCanvasVector        GtkCanvasVector;
 typedef struct _GtkCssStyleChange      GtkCssStyleChange;
 typedef struct _GtkEventController     GtkEventController;
 typedef struct _GtkGesture             GtkGesture;
diff --git a/gtk/meson.build b/gtk/meson.build
index 594808de9e..35b2774400 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -32,7 +32,6 @@ gtk_private_sources = files([
   'gtkbuilderprecompile.c',
   'gtkbuiltinicon.c',
   'gtkcellareaboxcontext.c',
-  'gtkcanvasvectorimpl.c',
   'gtkcoloreditor.c',
   'gtkcolorplane.c',
   'gtkcolorpicker.c',
@@ -186,7 +185,6 @@ gtk_public_sources = files([
   'gtkcanvas.c',
   'gtkcanvasbox.c',
   'gtkcanvasitem.c',
-  'gtkcanvasvector.c',
   'gtkcellarea.c',
   'gtkcellareabox.c',
   'gtkcellareacontext.c',
@@ -481,7 +479,6 @@ gtk_public_headers = files([
   'gtkcanvas.h',
   'gtkcanvasbox.h',
   'gtkcanvasitem.h',
-  'gtkcanvasvector.h',
   'gtkcenterbox.h',
   'gtkcenterlayout.h',
   'gtkcellarea.h',


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