summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--demos/gtk-demo/canvas_puzzle.c79
-rw-r--r--gtk/gtk.h5
-rw-r--r--gtk/gtkcanvas.c427
-rw-r--r--gtk/gtkcanvas.h55
-rw-r--r--gtk/gtkcanvasbox.c286
-rw-r--r--gtk/gtkcanvasbox.h58
-rw-r--r--gtk/gtkcanvasboxprivate.h54
-rw-r--r--gtk/gtkcanvasitem.c353
-rw-r--r--gtk/gtkcanvasitem.h57
-rw-r--r--gtk/gtkcanvasitemprivate.h20
-rw-r--r--gtk/gtkcanvaslayout.h91
-rw-r--r--gtk/gtkcanvaspoint.c297
-rw-r--r--gtk/gtkcanvaspoint.h65
-rw-r--r--gtk/gtkcanvaspointprivate.h52
-rw-r--r--gtk/gtkcanvassize.c392
-rw-r--r--gtk/gtkcanvassize.h66
-rw-r--r--gtk/gtkcanvassizeprivate.h56
-rw-r--r--gtk/gtktypes.h5
-rw-r--r--gtk/meson.build10
19 files changed, 2428 insertions, 0 deletions
diff --git a/demos/gtk-demo/canvas_puzzle.c b/demos/gtk-demo/canvas_puzzle.c
new file mode 100644
index 0000000000..8fae9fa177
--- /dev/null
+++ b/demos/gtk-demo/canvas_puzzle.c
@@ -0,0 +1,79 @@
+/* Canvas/Intro
+ *
+ * GtkCanvas is a very powerful canvas widget. Here is
+ * a simple Hello World demo to get accustomed to how
+ * it works.
+ */
+
+#include <gtk/gtk.h>
+
+#define WIDTH 400
+#define HEIGHT 300
+
+static void
+bind_item (GtkListItemFactory *factory,
+ GtkCanvasItem *ci)
+{
+ GtkCanvasPoint *point;
+ GtkCanvasSize *size;
+ GtkCanvasBox *box;
+
+ widget = gtk_picture_new_for_paintable (gtk_canvas_item_get_item (ci));
+ gtk_canvas_item_set_widget (ci, widget);
+
+ /* Also cener the item, so we do something interesting */
+ point = gtk_canvas_point_new (WIDTH / 2.0, HEIGHT / 2.0);
+ size = gtk_canvas_size_new_measure_item (ci, GTK_CANVAS_ITEM_MEASURE_MIN_FOR_MIN);
+ box = gtk_canvas_box_new (point, size, 0.5, 0.5);
+ gtk_canvas_item_set_bounds (ci, box);
+ gtk_canvas_box_free (box);
+ gtk_canvas_size_free (size);
+ gtk_canvas_point_free (point);
+}
+
+GtkWidget *
+do_canvas_intro (GtkWidget *do_widget)
+{
+ static GtkWidget *window = NULL;
+
+ if (!window)
+ {
+ GtkWidget *canvas, *widget;
+ GListStore *store;
+ GtkListItemFactory *factory;
+
+ window = gtk_window_new ();
+ gtk_window_set_display (GTK_WINDOW (window),
+ gtk_widget_get_display (do_widget));
+ gtk_window_set_default_size (GTK_WINDOW (window), WIDTH, HEIGHT);
+ g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
+
+ /* GtkCanvas manages its items using an external list.
+ * We do a very simple thing and put the widgets in the list
+ * that the canvas should display.
+ */
+ store = g_list_store_new (GTK_TYPE_WIDGET);
+ widget = gtk_label_new ("Hello World");
+ g_list_store_append (store, widget);
+
+ /* GtkCanvas maps the items from the list to the canvas using factories.
+ * Set up a simple factory here that just maps the widget directly
+ * onto the canvas.
+ */
+ factory = gtk_signal_list_item_factory_new ();
+ g_signal_connect (factory, "bind", G_CALLBACK (bind_item), NULL);
+
+ /* Create the canvas.
+ * We hand it the factory and the model, and then everything happens by itself.
+ */
+ canvas = gtk_canvas_new (G_LIST_MODEL (store), factory);
+ gtk_window_set_child (GTK_WINDOW (window), canvas);
+ }
+
+ if (!gtk_widget_get_visible (window))
+ gtk_widget_show (window);
+ else
+ gtk_window_destroy (GTK_WINDOW (window));
+
+ return window;
+}
diff --git a/gtk/gtk.h b/gtk/gtk.h
index bac384a2e0..9ed46e9f96 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -59,6 +59,11 @@
#include <gtk/gtkbuilderscope.h>
#include <gtk/gtkbutton.h>
#include <gtk/gtkcalendar.h>
+#include <gtk/gtkcanvas.h>
+#include <gtk/gtkcanvasbox.h>
+#include <gtk/gtkcanvasitem.h>
+#include <gtk/gtkcanvaspoint.h>
+#include <gtk/gtkcanvassize.h>
#include <gtk/gtkcellarea.h>
#include <gtk/gtkcellareabox.h>
#include <gtk/gtkcellareacontext.h>
diff --git a/gtk/gtkcanvas.c b/gtk/gtkcanvas.c
new file mode 100644
index 0000000000..18770f5906
--- /dev/null
+++ b/gtk/gtkcanvas.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright © 2022 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 "gtkcanvas.h"
+
+#include "gtkcanvasbox.h"
+#include "gtkcanvasitemprivate.h"
+#include "gtkintl.h"
+#include "gtklistitemfactory.h"
+#include "gtkwidgetprivate.h"
+
+#define GDK_ARRAY_NAME gtk_canvas_items
+#define GDK_ARRAY_TYPE_NAME GtkCanvasItems
+#define GDK_ARRAY_ELEMENT_TYPE GtkCanvasItem *
+#define GDK_ARRAY_FREE_FUNC g_object_unref
+#include "gdk/gdkarrayimpl.c"
+
+/**
+ * GtkCanvas:
+ *
+ * `GtkCanvas` is a widget that allows developers to place a list of items
+ * using their own method.
+ *
+ * ![An example GtkCanvas](canvas.png)
+ */
+
+struct _GtkCanvas
+{
+ GtkWidget parent_instance;
+
+ GListModel *model;
+ GtkListItemFactory *factory;
+
+ GtkCanvasItems items;
+};
+
+enum
+{
+ PROP_0,
+ PROP_FACTORY,
+ PROP_MODEL,
+
+ N_PROPS
+};
+
+G_DEFINE_FINAL_TYPE (GtkCanvas, gtk_canvas, GTK_TYPE_WIDGET)
+
+static GParamSpec *properties[N_PROPS] = { NULL, };
+
+static void
+gtk_canvas_clear_factory (GtkCanvas *self)
+{
+ if (self->factory == NULL)
+ return;
+
+ g_clear_object (&self->factory);
+}
+
+static void
+gtk_canvas_remove_items (GtkCanvas *self,
+ guint pos,
+ guint n_items)
+{
+ guint i;
+
+ /* We first run the factory code on all items, so that the
+ * factory code can reference the items.
+ * Only then do we get rid of them.
+ */
+ for (i = pos; i < pos + n_items; i++)
+ {
+ gtk_canvas_item_teardown (gtk_canvas_items_get (&self->items, i), self->factory);
+ }
+}
+
+static void
+gtk_canvas_add_items (GtkCanvas *self,
+ guint pos,
+ guint n_items)
+{
+ guint i;
+
+ /* We first create all the items and then run the factory code
+ * on them, so that the factory code can reference the items.
+ */
+ for (i = pos; i < pos + n_items; i++)
+ {
+ *gtk_canvas_items_index (&self->items, i) = gtk_canvas_item_new (self,
+ g_list_model_get_item (self->model, i));
+ }
+ for (i = pos; i < pos + n_items; i++)
+ {
+ gtk_canvas_item_setup (gtk_canvas_items_get (&self->items, i), self->factory);
+ }
+}
+
+static void
+gtk_canvas_items_changed_cb (GListModel *model,
+ guint pos,
+ guint removed,
+ guint added,
+ GtkCanvas *self)
+{
+ gtk_canvas_remove_items (self, pos, removed);
+
+ gtk_canvas_items_splice (&self->items, pos, removed, FALSE, NULL, added);
+
+ gtk_canvas_add_items (self, pos, added);
+}
+
+static void
+gtk_canvas_clear_model (GtkCanvas *self)
+{
+ if (self->model == NULL)
+ return;
+
+ g_signal_handlers_disconnect_by_func (self->model,
+ gtk_canvas_items_changed_cb,
+ self);
+ g_clear_object (&self->model);
+}
+
+static void
+gtk_canvas_dispose (GObject *object)
+{
+ GtkCanvas *self = GTK_CANVAS (object);
+
+ gtk_canvas_clear_model (self);
+ gtk_canvas_clear_factory (self);
+
+ G_OBJECT_CLASS (gtk_canvas_parent_class)->dispose (object);
+}
+
+static void
+gtk_canvas_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkCanvas *self = GTK_CANVAS (object);
+
+ switch (property_id)
+ {
+ case PROP_FACTORY:
+ g_value_set_object (value, self->factory);
+ break;
+
+ case PROP_MODEL:
+ g_value_set_object (value, self->model);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_canvas_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkCanvas *self = GTK_CANVAS (object);
+
+ switch (property_id)
+ {
+ case PROP_FACTORY:
+ gtk_canvas_set_factory (self, g_value_get_object (value));
+ break;
+
+ case PROP_MODEL:
+ gtk_canvas_set_model (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_canvas_allocate (GtkWidget *widget,
+ int width,
+ int height,
+ int baseline)
+{
+ GtkCanvas *self = GTK_CANVAS (widget);
+ gsize i;
+
+ 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);
+ graphene_rect_t rect;
+ int x, y, w, h;
+
+ if (child == NULL)
+ continue;
+
+ if (!gtk_canvas_box_eval (gtk_canvas_item_get_bounds (ci), &rect))
+ rect = *graphene_rect_zero ();
+
+ 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 (rect.size.width));
+ gtk_widget_measure (child, GTK_ORIENTATION_VERTICAL, w, &h, NULL, NULL, NULL);
+ h = MAX (h, ceil (rect.size.height));
+ }
+ else
+ {
+ gtk_widget_measure (child, GTK_ORIENTATION_VERTICAL, -1, &h, NULL, NULL, NULL);
+ h = MAX (h, ceil (rect.size.height));
+ gtk_widget_measure (child, GTK_ORIENTATION_HORIZONTAL, h, &w, NULL, NULL, NULL);
+ w = MAX (w, ceil (rect.size.width));
+ }
+
+ /* FIXME: Adapt to growing rect */
+ x = round (rect.origin.x);
+ y = round (rect.origin.y);
+
+ gtk_widget_size_allocate (child, &(GtkAllocation) { x, y, w, h }, -1);
+ }
+}
+
+static void
+gtk_canvas_class_init (GtkCanvasClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ widget_class->size_allocate = gtk_canvas_allocate;
+
+ gobject_class->dispose = gtk_canvas_dispose;
+ gobject_class->get_property = gtk_canvas_get_property;
+ gobject_class->set_property = gtk_canvas_set_property;
+
+ /**
+ * GtkCanvas:factory: (attributes org.gtk.Property.get=gtk_canvas_get_factory org.gtk.Property.set=gtk_canvas_set_factory)
+ *
+ * The factory used to set up canvasitems from items in the model.
+ */
+ properties[PROP_FACTORY] =
+ g_param_spec_object ("factory", NULL, NULL,
+ GTK_TYPE_LIST_ITEM_FACTORY,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * GtkCanvas:model: (attributes org.gtk.Property.get=gtk_canvas_get_model org.gtk.Property.set=gtk_canvas_set_model)
+ *
+ * The model with the items to display
+ */
+ properties[PROP_MODEL] =
+ g_param_spec_object ("model", NULL, NULL,
+ G_TYPE_LIST_MODEL,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (gobject_class, N_PROPS, properties);
+
+ gtk_widget_class_set_css_name (widget_class, I_("canvas"));
+}
+
+static void
+gtk_canvas_init (GtkCanvas *self)
+{
+}
+
+/**
+ * gtk_canvas_new:
+ * @model: (nullable) (transfer full): the model to use
+ * @factory: (nullable) (transfer full): The factory to populate items with
+ *
+ * Creates a new `GtkCanvas` that uses the given @factory for
+ * mapping items to widgets.
+ *
+ * The function takes ownership of the
+ * arguments, so you can write code like
+ * ```c
+ * canvas = gtk_canvas_new (create_model (),
+ * gtk_builder_list_item_factory_new_from_resource ("/resource.ui"));
+ * ```
+ *
+ * Returns: a new `GtkCanvas` using the given @model and @factory
+ */
+GtkWidget *
+gtk_canvas_new (GListModel *model,
+ GtkListItemFactory *factory)
+{
+ GtkWidget *result;
+
+ g_return_val_if_fail (model == NULL || G_IS_LIST_MODEL (model), NULL);
+ g_return_val_if_fail (factory == NULL || GTK_IS_LIST_ITEM_FACTORY (factory), NULL);
+
+ result = g_object_new (GTK_TYPE_CANVAS,
+ "factory", factory,
+ "model", model,
+ NULL);
+
+ g_clear_object (&model);
+ g_clear_object (&factory);
+
+ return result;
+}
+
+/**
+ * gtk_canvas_set_factory: (attributes org.gtk.Method.set_property=factory)
+ * @self: a `GtkCanvas`
+ * @factory: (nullable) (transfer none): the factory to use
+ *
+ * Sets the `GtkListItemFactory` to use for populating canvas items.
+ */
+void
+gtk_canvas_set_factory (GtkCanvas *self,
+ GtkListItemFactory *factory)
+{
+ guint n_items;
+
+ g_return_if_fail (GTK_IS_CANVAS (self));
+ g_return_if_fail (factory == NULL || GTK_IS_LIST_ITEM_FACTORY (factory));
+
+ if (self->factory == factory)
+ return;
+
+ n_items = self->model ? g_list_model_get_n_items (G_LIST_MODEL (self->model)) : 0;
+ gtk_canvas_remove_items (self, 0, n_items);
+
+ g_set_object (&self->factory, factory);
+ gtk_canvas_items_splice (&self->items, 0, n_items, FALSE, NULL, n_items);
+
+ gtk_canvas_add_items (self, 0, n_items);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]);
+}
+
+/**
+ * gtk_canvas_get_factory: (attributes org.gtk.Method.get_property=factory)
+ * @self: a `GtkCanvas`
+ *
+ * Gets the factory that's currently used to populate canvas items.
+ *
+ * Returns: (nullable) (transfer none): The factory in use
+ */
+GtkListItemFactory *
+gtk_canvas_get_factory (GtkCanvas *self)
+{
+ g_return_val_if_fail (GTK_IS_CANVAS (self), NULL);
+
+ return self->factory;
+}
+
+/**
+ * gtk_canvas_set_model: (attributes org.gtk.Method.set_property=model)
+ * @self: a `GtkCanvas`
+ * @model: (nullable) (transfer none): the model to use
+ *
+ * Sets the model containing the items to populate the canvas with.
+ */
+void
+gtk_canvas_set_model (GtkCanvas *self,
+ GListModel *model)
+{
+ g_return_if_fail (GTK_IS_CANVAS (self));
+ g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
+
+ if (self->model == model)
+ return;
+
+ gtk_canvas_clear_model (self);
+
+ if (model)
+ {
+ guint added;
+
+ self->model = g_object_ref (model);
+
+ g_signal_connect (model,
+ "items-changed",
+ G_CALLBACK (gtk_canvas_items_changed_cb),
+ self);
+
+ added = g_list_model_get_n_items (G_LIST_MODEL (model));
+ gtk_canvas_items_splice (&self->items, 0, gtk_canvas_items_get_size (&self->items), FALSE, NULL, added);
+ gtk_canvas_add_items (self, 0, added);
+ }
+ else
+ {
+ gtk_canvas_items_clear (&self->items);
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
+}
+
+/**
+ * gtk_canvas_get_model: (attributes org.gtk.Method.get_property=model)
+ * @self: a `GtkCanvas`
+ *
+ * Gets the model that's currently used for the displayed items.
+ *
+ * Returns: (nullable) (transfer none): The model in use
+ */
+GListModel *
+gtk_canvas_get_model (GtkCanvas *self)
+{
+ g_return_val_if_fail (GTK_IS_CANVAS (self), NULL);
+
+ return self->model;
+}
+
diff --git a/gtk/gtkcanvas.h b/gtk/gtkcanvas.h
new file mode 100644
index 0000000000..d8425997a3
--- /dev/null
+++ b/gtk/gtkcanvas.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright © 2022 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_CANVAS_H__
+#define __GTK_CANVAS_H__
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gtk/gtktypes.h>
+#include <gtk/gtkwidget.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CANVAS (gtk_canvas_get_type ())
+
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GtkCanvas, gtk_canvas, GTK, CANVAS, GtkWidget)
+
+GDK_AVAILABLE_IN_ALL
+GtkWidget * gtk_canvas_new (GListModel *children,
+ GtkListItemFactory *factory);
+
+GDK_AVAILABLE_IN_ALL
+void gtk_canvas_set_model (GtkCanvas *self,
+ GListModel *children);
+GDK_AVAILABLE_IN_ALL
+GListModel * gtk_canvas_get_model (GtkCanvas *self);
+GDK_AVAILABLE_IN_ALL
+void gtk_canvas_set_factory (GtkCanvas *self,
+ GtkListItemFactory *factory);
+GDK_AVAILABLE_IN_ALL
+GtkListItemFactory* gtk_canvas_get_factory (GtkCanvas *self);
+
+
+G_END_DECLS
+
+#endif /* __GTK_CANVAS_H__ */
diff --git a/gtk/gtkcanvasbox.c b/gtk/gtkcanvasbox.c
new file mode 100644
index 0000000000..4260990d78
--- /dev/null
+++ b/gtk/gtkcanvasbox.c
@@ -0,0 +1,286 @@
+/*
+ * Copyright © 2022 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>
+ */
+
+
+/**
+ * GtkCanvasBox:
+ *
+ * `GtkCanvasBox` describes an axis-aligned rectangular box inside
+ * a `GtkCanvas`.
+ *
+ * A box can have no size and be just a single point.
+ */
+
+#include "config.h"
+
+#include "gtkcanvasboxprivate.h"
+
+#include "gtkcanvaspointprivate.h"
+#include "gtkcanvassizeprivate.h"
+
+/* {{{ Boilerplate */
+
+struct _GtkCanvasBoxClass
+{
+ const char *type_name;
+
+ void (* copy) (GtkCanvasBox *self,
+ const GtkCanvasBox *source);
+ void (* finish) (GtkCanvasBox *self);
+ gboolean (* eval) (const GtkCanvasBox *self,
+ graphene_rect_t *rect);
+};
+
+G_DEFINE_BOXED_TYPE (GtkCanvasBox, gtk_canvas_box,
+ gtk_canvas_box_copy,
+ gtk_canvas_box_free)
+
+static gpointer
+gtk_canvas_box_alloc (const GtkCanvasBoxClass *class)
+{
+ GtkCanvasBox *self = g_slice_new (GtkCanvasBox);
+
+ self->class = class;
+
+ return self;
+}
+
+void
+gtk_canvas_box_init_copy (GtkCanvasBox *self,
+ const GtkCanvasBox *source)
+{
+ self->class = source->class;
+ self->class->copy (self, source);
+}
+
+void
+gtk_canvas_box_finish (GtkCanvasBox *self)
+{
+ self->class->finish (self);
+}
+
+/* }}} */
+/* {{{ POINTS */
+
+static void
+gtk_canvas_box_points_copy (GtkCanvasBox *box,
+ const GtkCanvasBox *source_box)
+{
+ GtkCanvasBoxPoints *self = &box->points;
+ const GtkCanvasBoxPoints *source = &source_box->points;
+
+ gtk_canvas_point_init_copy (&self->point1, &source->point1);
+ gtk_canvas_point_init_copy (&self->point2, &source->point2);
+}
+
+static void
+gtk_canvas_box_points_finish (GtkCanvasBox *box)
+{
+ GtkCanvasBoxPoints *self = &box->points;
+
+ gtk_canvas_point_finish (&self->point1);
+ gtk_canvas_point_finish (&self->point2);
+}
+
+static gboolean
+gtk_canvas_box_points_eval (const GtkCanvasBox *box,
+ graphene_rect_t *rect)
+{
+ const GtkCanvasBoxPoints *self = &box->points;
+ float x1, x2, y1, y2;
+
+ if (!gtk_canvas_point_eval (&self->point1, &x1, &y1) ||
+ !gtk_canvas_point_eval (&self->point2, &x2, &y2))
+ return FALSE;
+
+ graphene_rect_init (rect, x1, y1, x2 - x1, y2 - y1);
+ return TRUE;
+}
+
+static const GtkCanvasBoxClass GTK_CANVAS_BOX_POINTS_CLASS =
+{
+ "GtkCanvasBoxPoints",
+ gtk_canvas_box_points_copy,
+ gtk_canvas_box_points_finish,
+ gtk_canvas_box_points_eval,
+};
+
+/**
+ * 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 GtkCanvasPoint *point1,
+ const GtkCanvasPoint *point2)
+{
+ GtkCanvasBoxPoints *self;
+
+ g_return_val_if_fail (point1 != NULL, NULL);
+ g_return_val_if_fail (point2 != NULL, NULL);
+
+ self = gtk_canvas_box_alloc (&GTK_CANVAS_BOX_POINTS_CLASS);
+
+ gtk_canvas_point_init_copy (&self->point1, point1);
+ gtk_canvas_point_init_copy (&self->point2, point2);
+
+ return (GtkCanvasBox *) self;
+}
+
+/* }}} */
+/* {{{ SIZE */
+
+static void
+gtk_canvas_box_size_copy (GtkCanvasBox *box,
+ const GtkCanvasBox *source_box)
+{
+ const GtkCanvasBoxSize *source = &source_box->size;
+
+ gtk_canvas_box_init (box, &source->point, &source->size, source->origin_x, source->origin_y);
+}
+
+static void
+gtk_canvas_box_size_finish (GtkCanvasBox *box)
+{
+ GtkCanvasBoxSize *self = &box->size;
+
+ gtk_canvas_point_finish (&self->point);
+ gtk_canvas_size_finish (&self->size);
+}
+
+static gboolean
+gtk_canvas_box_size_eval (const GtkCanvasBox *box,
+ graphene_rect_t *rect)
+{
+ const GtkCanvasBoxSize *self = &box->size;
+ float x, y, width, height;
+
+ if (!gtk_canvas_point_eval (&self->point, &x, &y) ||
+ !gtk_canvas_size_eval (&self->size, &width, &height))
+ return FALSE;
+
+ graphene_rect_init (rect,
+ x - width * self->origin_x,
+ y - height * self->origin_y,
+ width, height);
+
+ return TRUE;
+}
+
+static const GtkCanvasBoxClass GTK_CANVAS_BOX_SIZE_CLASS =
+{
+ "GtkCanvasBoxSize",
+ gtk_canvas_box_size_copy,
+ gtk_canvas_box_size_finish,
+ gtk_canvas_box_size_eval,
+};
+
+void
+gtk_canvas_box_init (GtkCanvasBox *box,
+ const GtkCanvasPoint *point,
+ const GtkCanvasSize *size,
+ float origin_x,
+ float origin_y)
+{
+ GtkCanvasBoxSize *self = &box->size;
+
+ self->class = &GTK_CANVAS_BOX_SIZE_CLASS;
+
+ gtk_canvas_point_init_copy (&self->point, point);
+ gtk_canvas_size_init_copy (&self->size, size);
+ self->origin_x = origin_x;
+ self->origin_y = origin_y;
+}
+
+/**
+ * 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 GtkCanvasPoint *point,
+ const GtkCanvasSize *size,
+ float origin_x,
+ float origin_y)
+{
+ GtkCanvasBox *self;
+
+ g_return_val_if_fail (point != NULL, NULL);
+ g_return_val_if_fail (size != NULL, NULL);
+
+ self = gtk_canvas_box_alloc (&GTK_CANVAS_BOX_SIZE_CLASS);
+
+ gtk_canvas_box_init (self, point, size, origin_x, origin_y);
+
+ return self;
+}
+
+/* }}} */
+/* {{{ PUBLIC API */
+
+GtkCanvasBox *
+gtk_canvas_box_copy (const GtkCanvasBox *self)
+{
+ GtkCanvasBox *copy;
+
+ g_return_val_if_fail (self != NULL, NULL);
+
+ copy = gtk_canvas_box_alloc (self->class);
+
+ gtk_canvas_box_init_copy (copy, self);
+
+ return copy;
+}
+
+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)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (rect != NULL, FALSE);
+
+ if (self->class->eval (self, rect))
+ return TRUE;
+
+ *rect = *graphene_rect_zero ();
+ return FALSE;
+}
+
diff --git a/gtk/gtkcanvasbox.h b/gtk/gtkcanvasbox.h
new file mode 100644
index 0000000000..4211ca323c
--- /dev/null
+++ b/gtk/gtkcanvasbox.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright © 2022 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_CANVAS_BOX_H__
+#define __GTK_CANVAS_BOX_H__
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gtk/gtktypes.h>
+#include <graphene.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CANVAS_BOX (gtk_canvas_box_get_type ())
+
+GDK_AVAILABLE_IN_ALL
+GType gtk_canvas_box_get_type (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_ALL
+GtkCanvasBox * gtk_canvas_box_copy (const GtkCanvasBox *self);
+GDK_AVAILABLE_IN_ALL
+void gtk_canvas_box_free (GtkCanvasBox *self);
+
+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 GtkCanvasPoint *point,
+ const GtkCanvasSize *size,
+ float origin_x,
+ float origin_y);
+GDK_AVAILABLE_IN_ALL
+GtkCanvasBox * gtk_canvas_box_new_points (const GtkCanvasPoint *point1,
+ const GtkCanvasPoint *point2);
+
+G_END_DECLS
+
+#endif /* __GTK_BOX_H__ */
diff --git a/gtk/gtkcanvasboxprivate.h b/gtk/gtkcanvasboxprivate.h
new file mode 100644
index 0000000000..f28610c38d
--- /dev/null
+++ b/gtk/gtkcanvasboxprivate.h
@@ -0,0 +1,54 @@
+#ifndef __GTK_CANVAS_BOX_PRIVATE_H__
+#define __GTK_CANVAS_BOX_PRIVATE_H__
+
+#include "gtkcanvasbox.h"
+
+#include "gtkcanvaspointprivate.h"
+#include "gtkcanvassizeprivate.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GtkCanvasBoxClass GtkCanvasBoxClass;
+typedef struct _GtkCanvasBoxPoints GtkCanvasBoxPoints;
+typedef struct _GtkCanvasBoxSize GtkCanvasBoxSize;
+
+struct _GtkCanvasBoxPoints
+{
+ const GtkCanvasBoxClass *class;
+
+ GtkCanvasPoint point1;
+ GtkCanvasPoint point2;
+};
+
+struct _GtkCanvasBoxSize
+{
+ const GtkCanvasBoxClass *class;
+
+ GtkCanvasPoint point;
+ GtkCanvasSize size;
+ float origin_x;
+ float origin_y;
+};
+
+struct _GtkCanvasBox
+{
+ union {
+ const GtkCanvasBoxClass *class;
+ GtkCanvasBoxPoints points;
+ GtkCanvasBoxSize size;
+ };
+};
+
+
+void gtk_canvas_box_init (GtkCanvasBox *self,
+ const GtkCanvasPoint *point,
+ const GtkCanvasSize *size,
+ float origin_x,
+ float origin_y);
+void gtk_canvas_box_init_copy (GtkCanvasBox *self,
+ const GtkCanvasBox *source);
+void gtk_canvas_box_finish (GtkCanvasBox *self);
+
+G_END_DECLS
+
+#endif /* __GTK_CANVAS_BOX_PRIVATE_H__ */
diff --git a/gtk/gtkcanvasitem.c b/gtk/gtkcanvasitem.c
new file mode 100644
index 0000000000..1a56374a9b
--- /dev/null
+++ b/gtk/gtkcanvasitem.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright © 2022 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 "gtkcanvasitemprivate.h"
+
+#include "gtkcanvas.h"
+#include "gtkcanvasboxprivate.h"
+#include "gtkcanvaspointprivate.h"
+#include "gtkcanvassizeprivate.h"
+#include "gtkintl.h"
+#include "gtklistitemfactoryprivate.h"
+#include "gtkwidget.h"
+
+/**
+ * GtkCanvasItem:
+ *
+ * `GtkCanvasItem` holds all information relevant for placing a widget
+ * onto the canvas.
+ */
+
+struct _GtkCanvasItem
+{
+ GObject parent_instance;
+
+ GtkCanvas *canvas;
+ gpointer item;
+ GtkWidget *widget;
+ GtkCanvasBox bounds;
+};
+
+enum
+{
+ PROP_0,
+ PROP_BOUNDS,
+ PROP_CANVAS,
+ PROP_ITEM,
+ PROP_WIDGET,
+
+ N_PROPS
+};
+
+G_DEFINE_FINAL_TYPE (GtkCanvasItem, gtk_canvas_item, G_TYPE_OBJECT)
+
+static GParamSpec *properties[N_PROPS] = { NULL, };
+
+static void
+gtk_canvas_item_dispose (GObject *object)
+{
+ GtkCanvasItem *self = GTK_CANVAS_ITEM (object);
+
+ /* holds a reference */
+ g_assert (self->canvas == NULL);
+ /* must have been deleted in teardown */
+ g_assert (self->item == NULL);
+ g_assert (self->widget == NULL);
+
+ gtk_canvas_box_finish (&self->bounds);
+
+ G_OBJECT_CLASS (gtk_canvas_item_parent_class)->dispose (object);
+}
+
+static void
+gtk_canvas_item_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkCanvasItem *self = GTK_CANVAS_ITEM (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;
+
+ case PROP_ITEM:
+ g_value_set_object (value, self->item);
+ break;
+
+ case PROP_WIDGET:
+ g_value_set_object (value, self->widget);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_canvas_item_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkCanvasItem *self = GTK_CANVAS_ITEM (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;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_canvas_item_class_init (GtkCanvasItemClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->dispose = gtk_canvas_item_dispose;
+ 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)
+ *
+ * The canvas this item belongs to or %NULL if the canvas has been destroyed
+ */
+ properties[PROP_CANVAS] =
+ g_param_spec_object ("canvas", NULL, NULL,
+ GTK_TYPE_CANVAS,
+ G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * GtkCanvasItem:item: (attributes org.gtk.Property.get=gtk_canvas_item_get_item org.gtk.Property.set=gtk_canvas_item_set_item)
+ *
+ * The item represented by this canvas item.
+ */
+ properties[PROP_ITEM] =
+ g_param_spec_object ("item", NULL, NULL,
+ G_TYPE_OBJECT,
+ G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * GtkCanvasItem:widget: (attributes org.gtk.Property.get=gtk_canvas_item_get_widget org.gtk.Property.set=gtk_canvas_item_set_widget)
+ *
+ * The widget managed.
+ */
+ properties[PROP_WIDGET] =
+ g_param_spec_object ("widget", NULL, NULL,
+ GTK_TYPE_WIDGET,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (gobject_class, N_PROPS, properties);
+}
+
+static void
+gtk_canvas_item_init (GtkCanvasItem *self)
+{
+ GtkCanvasPoint point;
+ GtkCanvasSize size;
+
+ gtk_canvas_point_init (&point, 0, 0);
+ gtk_canvas_size_init_measure_item (&size, self, GTK_CANVAS_ITEM_MEASURE_NAT_FOR_NAT);
+ gtk_canvas_box_init (&self->bounds, &point, &size, 0, 0);
+ gtk_canvas_size_finish (&size);
+ gtk_canvas_point_finish (&point);
+}
+
+GtkCanvasItem *
+gtk_canvas_item_new (GtkCanvas *canvas,
+ gpointer item)
+{
+ GtkCanvasItem *self;
+
+ self = g_object_new (GTK_TYPE_CANVAS_ITEM,
+ NULL);
+
+ /* no reference, the canvas references us */
+ self->canvas = canvas;
+ /* transfer full */
+ self->item = item;
+
+ return self;
+}
+
+/**
+ * gtk_canvas_item_get_canvas: (attributes org.gtk.Method.get_property=canvas)
+ * @self: a `GtkCanvasItem`
+ *
+ * Gets the canvas this item belongs to.
+ *
+ * If the canvas has discarded this item, this property willbe set to %NULL.
+ *
+ * Returns: (nullable) (transfer none): The canvas
+ */
+GtkCanvas *
+gtk_canvas_item_get_canvas (GtkCanvasItem *self)
+{
+ g_return_val_if_fail (GTK_IS_CANVAS_ITEM (self), NULL);
+
+ return self->canvas;
+}
+
+/**
+ * gtk_canvas_item_get_item: (attributes org.gtk.Method.get_property=item)
+ * @self: a `GtkCanvasItem`
+ *
+ * Gets the item that is associated with this canvasitem or %NULL if the canvas has
+ * discarded this canvasitem.
+ *
+ * Returns: (transfer none) (nullable) (type GObject): The item.
+ */
+gpointer
+gtk_canvas_item_get_item (GtkCanvasItem *self)
+{
+ g_return_val_if_fail (GTK_IS_CANVAS_ITEM (self), NULL);
+
+ return self->item;
+}
+
+/**
+ * gtk_canvas_item_set_bounds: (attributes org.gtk.Method.set_property=bounds)
+ * @self: a `GtkCanvasItem`
+ * @bounds: (transfer none): the bounds to allocate the widget in
+ *
+ * Sets the box to allocate the widget into.
+ */
+void
+gtk_canvas_item_set_bounds (GtkCanvasItem *self,
+ const GtkCanvasBox *bounds)
+{
+ g_return_if_fail (GTK_IS_CANVAS_ITEM (self));
+ g_return_if_fail (bounds != NULL);
+
+ gtk_canvas_box_init_copy (&self->bounds, bounds);
+ 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)
+ * @self: a `GtkCanvasItem`
+ *
+ * Gets the bounds that are used to allocate the widget
+ *
+ * Returns: (transfer none): The bounds
+ */
+const GtkCanvasBox *
+gtk_canvas_item_get_bounds (GtkCanvasItem *self)
+{
+ g_return_val_if_fail (GTK_IS_CANVAS_ITEM (self), NULL);
+
+ return &self->bounds;
+}
+
+/**
+ * gtk_canvas_item_set_widget: (attributes org.gtk.Method.set_property=widget)
+ * @self: a `GtkCanvasItem`
+ * @widget: (nullable) (transfer none): the widget to use
+ *
+ * Sets the widget to be displayed by this item.
+ */
+void
+gtk_canvas_item_set_widget (GtkCanvasItem *self,
+ GtkWidget *widget)
+{
+ g_return_if_fail (GTK_IS_CANVAS_ITEM (self));
+ g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget));
+
+ if (self->widget == widget)
+ return;
+
+ if (self->widget)
+ {
+ if (self->canvas)
+ gtk_widget_unparent (self->widget);
+ g_object_unref (self->widget);
+ }
+
+ self->widget = g_object_ref_sink (widget);
+
+ if (self->canvas)
+ {
+ /* FIXME: Put in right spot */
+ gtk_widget_set_parent (widget, GTK_WIDGET (self->canvas));
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_WIDGET]);
+}
+
+/**
+ * gtk_canvas_item_get_widget: (attributes org.gtk.Method.get_property=widget)
+ * @self: a `GtkCanvasItem`
+ *
+ * Gets the widget that's currently displayed by this canvasitem
+ *
+ * Returns: (nullable) (transfer none): The widget in use
+ */
+GtkWidget *
+gtk_canvas_item_get_widget (GtkCanvasItem *self)
+{
+ g_return_val_if_fail (GTK_IS_CANVAS_ITEM (self), NULL);
+
+ return self->widget;
+}
+
+void
+gtk_canvas_item_setup (GtkCanvasItem *self,
+ GtkListItemFactory *factory)
+{
+ gtk_list_item_factory_setup (factory, G_OBJECT (self), TRUE, NULL, NULL);
+}
+
+void
+gtk_canvas_item_teardown (GtkCanvasItem *self,
+ GtkListItemFactory *factory)
+{
+ gtk_list_item_factory_teardown (factory, G_OBJECT (self), TRUE, NULL, NULL);
+}
diff --git a/gtk/gtkcanvasitem.h b/gtk/gtkcanvasitem.h
new file mode 100644
index 0000000000..15f3ae7bf4
--- /dev/null
+++ b/gtk/gtkcanvasitem.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright © 2022 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_CANVAS_ITEM_H__
+#define __GTK_CANVAS_ITEM_H__
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gtk/gtktypes.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CANVAS_ITEM (gtk_canvas_item_get_type ())
+
+/* GtkCanvasItem */
+
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GtkCanvasItem, gtk_canvas_item, GTK, CANVAS_ITEM, GObject)
+
+GDK_AVAILABLE_IN_ALL
+GtkCanvas * gtk_canvas_item_get_canvas (GtkCanvasItem *self);
+GDK_AVAILABLE_IN_ALL
+gpointer gtk_canvas_item_get_item (GtkCanvasItem *self);
+
+GDK_AVAILABLE_IN_ALL
+void gtk_canvas_item_set_widget (GtkCanvasItem *self,
+ GtkWidget *widget);
+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);
+GDK_AVAILABLE_IN_ALL
+const GtkCanvasBox * gtk_canvas_item_get_bounds (GtkCanvasItem *self);
+
+G_END_DECLS
+
+#endif /* __GTK_CANVAS_ITEM_H__ */
diff --git a/gtk/gtkcanvasitemprivate.h b/gtk/gtkcanvasitemprivate.h
new file mode 100644
index 0000000000..f22916e925
--- /dev/null
+++ b/gtk/gtkcanvasitemprivate.h
@@ -0,0 +1,20 @@
+#ifndef __GTK_CANVAS_ITEM_PRIVATE_H__
+#define __GTK_CANVAS_ITEM_PRIVATE_H__
+
+#include "gtkcanvasitem.h"
+
+G_BEGIN_DECLS
+
+GtkCanvasItem * gtk_canvas_item_new (GtkCanvas *canvas,
+ gpointer item);
+
+void gtk_canvas_item_clear_canvas (GtkCanvasItem *self);
+
+void gtk_canvas_item_setup (GtkCanvasItem *self,
+ GtkListItemFactory *factory);
+void gtk_canvas_item_teardown (GtkCanvasItem *self,
+ GtkListItemFactory *factory);
+
+G_END_DECLS
+
+#endif /* __GTK_CANVAS_ITEM_PRIVATE_H__ */
diff --git a/gtk/gtkcanvaslayout.h b/gtk/gtkcanvaslayout.h
new file mode 100644
index 0000000000..c85e64ba2a
--- /dev/null
+++ b/gtk/gtkcanvaslayout.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright © 2022 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_CANVAS_H__
+#define __GTK_CANVAS_H__
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gtk/gtklayoutmanager.h>
+
+G_BEGIN_DECLS
+
+struct _GtkPosition {
+ float relative;
+ float absolute;
+};
+
+#define GTK_TYPE_CANVAS_LAYOUT (gtk_canvas_layout_get_type ())
+#define GTK_TYPE_CANVAS_LAYOUT_CHILD (gtk_canvas_layout_child_get_type ())
+
+/* GtkCanvasLayout */
+
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GtkCanvasLayout, gtk_canvas_layout, GTK, CANVAS_LAYOUT, GtkLayoutManager)
+
+GDK_AVAILABLE_IN_ALL
+GtkLayoutManager * gtk_canvas_layout_new (void);
+
+/* GtkCanvasLayoutChild */
+
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GtkCanvasLayoutChild, gtk_canvas_layout_child, GTK, CANVAS_LAYOUT_CHILD, GtkLayoutChild)
+
+GDK_AVAILABLE_IN_ALL
+void gtk_canvas_layout_child_set_x (GtkCanvasLayoutChild *self,
+ const GtkPosition *position);
+GDK_AVAILABLE_IN_ALL
+const GtkPosition * gtk_canvas_layout_child_get_x (GtkCanvasLayoutChild *self);
+GDK_AVAILABLE_IN_ALL
+void gtk_canvas_layout_child_set_y (GtkCanvasLayoutChild *self,
+ const GtkPosition *position);
+GDK_AVAILABLE_IN_ALL
+const GtkPosition * gtk_canvas_layout_child_get_y (GtkCanvasLayoutChild *self);
+GDK_AVAILABLE_IN_ALL
+void gtk_canvas_layout_child_set_origin_x (GtkCanvasLayoutChild *self,
+ const GtkPosition *position);
+GDK_AVAILABLE_IN_ALL
+const GtkPosition * gtk_canvas_layout_child_get_origin_x (GtkCanvasLayoutChild *self);
+GDK_AVAILABLE_IN_ALL
+void gtk_canvas_layout_child_set_origin_y (GtkCanvasLayoutChild *self,
+ const GtkPosition *position);
+GDK_AVAILABLE_IN_ALL
+const GtkPosition * gtk_canvas_layout_child_get_origin_y (GtkCanvasLayoutChild *self);
+GDK_AVAILABLE_IN_ALL
+void gtk_canvas_layout_child_set_transform (GtkCanvasLayoutChild *self,
+ GskTransform *transform);
+GDK_AVAILABLE_IN_ALL
+const GskTransform * gtk_canvas_layout_child_get_transform (GtkCanvasLayoutChild *self);
+
+GDK_AVAILABLE_IN_ALL
+void gtk_canvas_layout_child_set_hpolicy (GtkCanvasLayoutChild *self,
+ GtkScrollablePolicy policy);
+GDK_AVAILABLE_IN_ALL
+GtkScrollablePolicy gtk_canvas_layout_child_get_hpolicy (GtkCanvasLayoutChild *self);
+GDK_AVAILABLE_IN_ALL
+void gtk_canvas_layout_child_set_vpolicy (GtkCanvasLayoutChild *self,
+ GtkScrollablePolicy policy);
+GDK_AVAILABLE_IN_ALL
+GtkScrollablePolicy gtk_canvas_layout_child_get_vpolicy (GtkCanvasLayoutChild *self);
+
+G_END_DECLS
+
+#endif /* __GTK_CANVAS_H__ */
diff --git a/gtk/gtkcanvaspoint.c b/gtk/gtkcanvaspoint.c
new file mode 100644
index 0000000000..df812b5cf4
--- /dev/null
+++ b/gtk/gtkcanvaspoint.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright © 2022 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>
+ */
+
+
+/**
+ * GtkCanvasPoint:
+ *
+ * `GtkCanvasPoint` describes a point in a `GtkCanvas`.
+ */
+
+#include "config.h"
+
+#include "gtkcanvaspointprivate.h"
+
+#include "gtkcanvasbox.h"
+
+/* {{{ Boilerplate */
+
+struct _GtkCanvasPointClass
+{
+ const char *type_name;
+
+ void (* copy) (GtkCanvasPoint *self,
+ const GtkCanvasPoint *source);
+ void (* finish) (GtkCanvasPoint *self);
+ gboolean (* eval) (const GtkCanvasPoint *self,
+ float *x,
+ float *y);
+};
+
+G_DEFINE_BOXED_TYPE (GtkCanvasPoint, gtk_canvas_point,
+ gtk_canvas_point_copy,
+ gtk_canvas_point_free)
+
+static gpointer
+gtk_canvas_point_alloc (const GtkCanvasPointClass *class)
+{
+ GtkCanvasPoint *self = g_slice_new (GtkCanvasPoint);
+
+ self->class = class;
+
+ return self;
+}
+
+void
+gtk_canvas_point_init_copy (GtkCanvasPoint *self,
+ const GtkCanvasPoint *source)
+{
+ self->class = source->class;
+ self->class->copy (self, source);
+}
+
+void
+gtk_canvas_point_finish (GtkCanvasPoint *self)
+{
+ self->class->finish (self);
+}
+
+/* }}} */
+/* {{{ OFFSET */
+
+static void
+gtk_canvas_point_offset_copy (GtkCanvasPoint *point,
+ const GtkCanvasPoint *source_point)
+{
+ GtkCanvasPointOffset *self = &point->offset;
+ const GtkCanvasPointOffset *source = &source_point->offset;
+
+ *self = *source;
+
+ if (source->other)
+ self->other = gtk_canvas_point_copy (source->other);
+}
+
+static void
+gtk_canvas_point_offset_finish (GtkCanvasPoint *point)
+{
+ GtkCanvasPointOffset *self = &point->offset;
+
+ if (self->other)
+ gtk_canvas_point_free (self->other);
+}
+
+static gboolean
+gtk_canvas_point_offset_eval (const GtkCanvasPoint *point,
+ float *x,
+ float *y)
+{
+ const GtkCanvasPointOffset *self = &point->offset;
+
+ if (self->other != NULL)
+ {
+ if (!gtk_canvas_point_eval (self->other, x, y))
+ return FALSE;
+
+ *x += self->dx;
+ *y += self->dy;
+ }
+ else
+ {
+ *x = self->dx;
+ *y = self->dy;
+ }
+
+ return TRUE;
+}
+
+static const GtkCanvasPointClass GTK_CANVAS_POINT_OFFSET_CLASS =
+{
+ "GtkCanvasPointOffset",
+ gtk_canvas_point_offset_copy,
+ gtk_canvas_point_offset_finish,
+ gtk_canvas_point_offset_eval,
+};
+
+void
+gtk_canvas_point_init (GtkCanvasPoint *point,
+ float x,
+ float y)
+{
+ GtkCanvasPointOffset *self = &point->offset;
+
+ self->class = &GTK_CANVAS_POINT_OFFSET_CLASS;
+
+ self->other = NULL;
+ self->dx = x;
+ self->dy = y;
+}
+
+/**
+ * gtk_canvas_point_new:
+ * @x: x coordinate
+ * @y: y coordinate
+ *
+ * Creates a new point at the given coordinate.
+ *
+ * Returns: a new point
+ **/
+GtkCanvasPoint *
+gtk_canvas_point_new (float x,
+ float y)
+{
+ GtkCanvasPoint *self;
+
+ self = gtk_canvas_point_alloc (&GTK_CANVAS_POINT_OFFSET_CLASS);
+
+ gtk_canvas_point_init (self, x, y);
+
+ return self;
+}
+
+/* }}} */
+/* {{{ BOX */
+
+static void
+gtk_canvas_point_box_copy (GtkCanvasPoint *point,
+ const GtkCanvasPoint *source_point)
+{
+ GtkCanvasPointBox *self = &point->box;
+ const GtkCanvasPointBox *source = &source_point->box;
+
+ *self = *source;
+
+ self->box = gtk_canvas_box_copy (source->box);
+}
+
+static void
+gtk_canvas_point_box_finish (GtkCanvasPoint *point)
+{
+ GtkCanvasPointBox *self = &point->box;
+
+ gtk_canvas_box_free (self->box);
+}
+
+static gboolean
+gtk_canvas_point_box_eval (const GtkCanvasPoint *point,
+ float *x,
+ float *y)
+{
+ const GtkCanvasPointBox *self = &point->box;
+ graphene_rect_t rect;
+
+ if (!gtk_canvas_box_eval (self->box, &rect))
+ return FALSE;
+
+ *x = rect.origin.x + self->offset_x + self->origin_x * rect.size.width;
+ *y = rect.origin.y + self->offset_y + self->origin_y * rect.size.height;
+
+ return TRUE;
+}
+
+static const GtkCanvasPointClass GTK_CANVAS_POINT_BOX_CLASS =
+{
+ "GtkCanvasPointBox",
+ gtk_canvas_point_box_copy,
+ gtk_canvas_point_box_finish,
+ gtk_canvas_point_box_eval,
+};
+
+/**
+ * gtk_canvas_point_new_from_box:
+ * @box: a box
+ * @origin_x: x coordinate of box origin
+ * @origin_y: y coordinate of box origin
+ * @offset_x: offset in x direction
+ * @offset_y: offset in y direction
+ *
+ * Creates a point relative to the given box.
+ *
+ * The origin describes where in the box the point is, with
+ * (0, 0) being the top left and (1, 1) being the bottom right
+ * corner of the box.
+ *
+ * The offset is then added to the origin. It may be negative.
+ *
+ * Returns: a new point
+ **/
+GtkCanvasPoint *
+gtk_canvas_point_new_from_box (const GtkCanvasBox *box,
+ float origin_x,
+ float origin_y,
+ float offset_x,
+ float offset_y)
+{
+ GtkCanvasPointBox *self;
+
+ g_return_val_if_fail (box != NULL, NULL);
+
+ self = gtk_canvas_point_alloc (&GTK_CANVAS_POINT_BOX_CLASS);
+
+ self->box = gtk_canvas_box_copy (box);
+ self->origin_x = origin_x;
+ self->origin_y = origin_y;
+ self->offset_x = offset_x;
+ self->offset_y = offset_y;
+
+ return (GtkCanvasPoint *) self;
+}
+
+/* }}} */
+/* {{{ PUBLIC API */
+
+GtkCanvasPoint *
+gtk_canvas_point_copy (const GtkCanvasPoint *self)
+{
+ GtkCanvasPoint *copy;
+
+ g_return_val_if_fail (self != NULL, NULL);
+
+ copy = gtk_canvas_point_alloc (self->class);
+
+ gtk_canvas_point_init_copy (copy, self);
+
+ return copy;
+}
+
+void
+gtk_canvas_point_free (GtkCanvasPoint *self)
+{
+ gtk_canvas_point_finish (self);
+
+ g_slice_free (GtkCanvasPoint, self);
+}
+
+gboolean
+gtk_canvas_point_eval (const GtkCanvasPoint *self,
+ float *x,
+ float *y)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (x != NULL, FALSE);
+ g_return_val_if_fail (y != NULL, FALSE);
+
+ if (self->class->eval (self, x, y))
+ return TRUE;
+
+ *x = 0;
+ *y = 0;
+ return FALSE;
+}
+
diff --git a/gtk/gtkcanvaspoint.h b/gtk/gtkcanvaspoint.h
new file mode 100644
index 0000000000..5a35ba3423
--- /dev/null
+++ b/gtk/gtkcanvaspoint.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright © 2022 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_CANVAS_POINT_H__
+#define __GTK_CANVAS_POINT_H__
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gtk/gtktypes.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CANVAS_POINT (gtk_canvas_point_get_type ())
+
+GDK_AVAILABLE_IN_ALL
+GType gtk_canvas_point_get_type (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_ALL
+GtkCanvasPoint * gtk_canvas_point_copy (const GtkCanvasPoint *self);
+GDK_AVAILABLE_IN_ALL
+void gtk_canvas_point_free (GtkCanvasPoint *self);
+
+GDK_AVAILABLE_IN_ALL
+gboolean gtk_canvas_point_eval (const GtkCanvasPoint *self,
+ float *x,
+ float *y) G_GNUC_WARN_UNUSED_RESULT;
+
+GDK_AVAILABLE_IN_ALL
+GtkCanvasPoint * gtk_canvas_point_new (float x,
+ float y);
+GDK_AVAILABLE_IN_ALL
+GtkCanvasPoint * gtk_canvas_point_new_from_box (const GtkCanvasBox *box,
+ float origin_x,
+ float origin_y,
+ float offset_x,
+ float offset_y);
+GDK_AVAILABLE_IN_ALL
+GtkCanvasPoint * gtk_canvas_point_new_from_item (GtkCanvasItem *item,
+ float origin_x,
+ float origin_y,
+ float offset_x,
+ float offset_y);
+
+G_END_DECLS
+
+#endif /* __GTK_POINT_H__ */
diff --git a/gtk/gtkcanvaspointprivate.h b/gtk/gtkcanvaspointprivate.h
new file mode 100644
index 0000000000..1d10efa6b3
--- /dev/null
+++ b/gtk/gtkcanvaspointprivate.h
@@ -0,0 +1,52 @@
+#ifndef __GTK_CANVAS_POINT_PRIVATE_H__
+#define __GTK_CANVAS_POINT_PRIVATE_H__
+
+#include "gtkcanvaspoint.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GtkCanvasPointClass GtkCanvasPointClass;
+typedef struct _GtkCanvasPointBox GtkCanvasPointBox;
+typedef struct _GtkCanvasPointItem GtkCanvasPointItem;
+typedef struct _GtkCanvasPointOffset GtkCanvasPointOffset;
+
+struct _GtkCanvasPointBox
+{
+ const GtkCanvasPointClass *class;
+
+ GtkCanvasBox *box;
+ float origin_x;
+ float origin_y;
+ float offset_x;
+ float offset_y;
+};
+
+struct _GtkCanvasPointOffset
+{
+ const GtkCanvasPointClass *class;
+
+ GtkCanvasPoint *other;
+ float dx;
+ float dy;
+};
+
+struct _GtkCanvasPoint
+{
+ union {
+ const GtkCanvasPointClass *class;
+ GtkCanvasPointBox box;
+ GtkCanvasPointOffset offset;
+ };
+};
+
+
+void gtk_canvas_point_init (GtkCanvasPoint * point,
+ float x,
+ float y);
+void gtk_canvas_point_init_copy (GtkCanvasPoint *self,
+ const GtkCanvasPoint *source);
+void gtk_canvas_point_finish (GtkCanvasPoint *self);
+
+G_END_DECLS
+
+#endif /* __GTK_CANVAS_POINT_PRIVATE_H__ */
diff --git a/gtk/gtkcanvassize.c b/gtk/gtkcanvassize.c
new file mode 100644
index 0000000000..b67b870eec
--- /dev/null
+++ b/gtk/gtkcanvassize.c
@@ -0,0 +1,392 @@
+/*
+ * Copyright © 2022 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>
+ */
+
+
+/**
+ * GtkCanvasSize:
+ *
+ * `GtkCanvasSize` describes a size in a `GtkCanvas`.
+ */
+
+#include "config.h"
+
+#include "gtkcanvassizeprivate.h"
+
+#include "gtkcanvasbox.h"
+#include "gtkcanvasitem.h"
+#include "gtkwidget.h"
+
+/* {{{ Boilerplate */
+
+struct _GtkCanvasSizeClass
+{
+ const char *type_name;
+
+ void (* copy) (GtkCanvasSize *self,
+ const GtkCanvasSize *source);
+ void (* finish) (GtkCanvasSize *self);
+ gboolean (* eval) (const GtkCanvasSize *self,
+ float *width,
+ float *height);
+};
+
+G_DEFINE_BOXED_TYPE (GtkCanvasSize, gtk_canvas_size,
+ gtk_canvas_size_copy,
+ gtk_canvas_size_free)
+
+static gpointer
+gtk_canvas_size_alloc (const GtkCanvasSizeClass *class)
+{
+ GtkCanvasSize *self = g_slice_new (GtkCanvasSize);
+
+ self->class = class;
+
+ return self;
+}
+
+void
+gtk_canvas_size_init_copy (GtkCanvasSize *self,
+ const GtkCanvasSize *source)
+{
+ self->class = source->class;
+ self->class->copy (self, source);
+}
+
+void
+gtk_canvas_size_finish (GtkCanvasSize *self)
+{
+ self->class->finish (self);
+}
+
+/* }}} */
+/* {{{ ABSOLUTE */
+
+static void
+gtk_canvas_size_absolute_copy (GtkCanvasSize *size,
+ const GtkCanvasSize *source_size)
+{
+ GtkCanvasSizeAbsolute *self = &size->absolute;
+ const GtkCanvasSizeAbsolute *source = &source_size->absolute;
+
+ *self = *source;
+}
+
+static void
+gtk_canvas_size_absolute_finish (GtkCanvasSize *size)
+{
+}
+
+static gboolean
+gtk_canvas_size_absolute_eval (const GtkCanvasSize *size,
+ float *width,
+ float *height)
+{
+ const GtkCanvasSizeAbsolute *self = &size->absolute;
+
+ *width = self->width;
+ *height = self->height;
+
+ return TRUE;
+}
+
+static const GtkCanvasSizeClass GTK_CANVAS_SIZE_ABSOLUTE_CLASS =
+{
+ "GtkCanvasSizeAbsolute",
+ gtk_canvas_size_absolute_copy,
+ gtk_canvas_size_absolute_finish,
+ gtk_canvas_size_absolute_eval,
+};
+
+/**
+ * gtk_canvas_size_new:
+ * @width: width of the size
+ * @height: height of the size
+ *
+ * Creates a new size with the given dimensions
+ *
+ * Returns: a new size
+ **/
+GtkCanvasSize *
+gtk_canvas_size_new (float width,
+ float height)
+{
+ GtkCanvasSizeAbsolute *self;
+
+ self = gtk_canvas_size_alloc (&GTK_CANVAS_SIZE_ABSOLUTE_CLASS);
+ self->width = width;
+ self->height = height;
+
+ return (GtkCanvasSize *) self;
+}
+
+/* }}} */
+/* {{{ BOX */
+
+static void
+gtk_canvas_size_box_copy (GtkCanvasSize *size,
+ const GtkCanvasSize *source_size)
+{
+ GtkCanvasSizeBox *self = &size->box;
+ const GtkCanvasSizeBox *source = &source_size->box;
+
+ *self = *source;
+
+ self->box = gtk_canvas_box_copy (source->box);
+}
+
+static void
+gtk_canvas_size_box_finish (GtkCanvasSize *size)
+{
+ GtkCanvasSizeBox *self = &size->box;
+
+ gtk_canvas_box_free (self->box);
+}
+
+static gboolean
+gtk_canvas_size_box_eval (const GtkCanvasSize *size,
+ float *width,
+ float *height)
+{
+ const GtkCanvasSizeBox *self = &size->box;
+ graphene_rect_t rect;
+
+ if (!gtk_canvas_box_eval (self->box, &rect))
+ return FALSE;
+
+ *width = rect.size.width;
+ *height = rect.size.height;
+
+ return TRUE;
+}
+
+static const GtkCanvasSizeClass GTK_CANVAS_SIZE_BOX_CLASS =
+{
+ "GtkCanvasSizeBox",
+ gtk_canvas_size_box_copy,
+ gtk_canvas_size_box_finish,
+ gtk_canvas_size_box_eval,
+};
+
+/**
+ * gtk_canvas_size_new_from_box:
+ * @box: a box
+ *
+ * Creates a size for the given box.
+ *
+ * Returns: a new size
+ **/
+GtkCanvasSize *
+gtk_canvas_size_new_from_box (const GtkCanvasBox *box)
+{
+ GtkCanvasSizeBox *self;
+
+ g_return_val_if_fail (box != NULL, NULL);
+
+ self = gtk_canvas_size_alloc (&GTK_CANVAS_SIZE_BOX_CLASS);
+
+ /* FIXME: We could potentially just copy the box's size here */
+ self->box = gtk_canvas_box_copy (box);
+
+ return (GtkCanvasSize *) self;
+}
+
+/* }}} */
+/* {{{ MEASURE */
+
+static void
+gtk_canvas_size_measure_copy (GtkCanvasSize *size,
+ const GtkCanvasSize *source_size)
+{
+ const GtkCanvasSizeMeasure *source = &source_size->measure;
+
+ gtk_canvas_size_init_measure_item (size, source->item, source->measure);
+}
+
+static void
+gtk_canvas_size_measure_finish (GtkCanvasSize *size)
+{
+}
+
+static gboolean
+gtk_canvas_size_measure_eval (const GtkCanvasSize *size,
+ float *width,
+ float *height)
+{
+ const GtkCanvasSizeMeasure *self = &size->measure;
+ GtkWidget *widget;
+ int w, h;
+
+ if (self->item == NULL)
+ return FALSE;
+
+ widget = gtk_canvas_item_get_widget (self->item);
+ if (widget == NULL)
+ {
+ *width = 0;
+ *height = 0;
+ return TRUE;
+ }
+
+ if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
+ {
+ switch (self->measure)
+ {
+ case GTK_CANVAS_ITEM_MEASURE_MIN_FOR_MIN:
+ gtk_widget_measure (widget, GTK_ORIENTATION_HORIZONTAL, -1, &w, NULL, NULL, NULL);
+ gtk_widget_measure (widget, GTK_ORIENTATION_VERTICAL, w, &h, NULL, NULL, NULL);
+ break;
+ case GTK_CANVAS_ITEM_MEASURE_MIN_FOR_NAT:
+ gtk_widget_measure (widget, GTK_ORIENTATION_HORIZONTAL, -1, NULL, &w, NULL, NULL);
+ gtk_widget_measure (widget, GTK_ORIENTATION_VERTICAL, w, &h, NULL, NULL, NULL);
+ break;
+ case GTK_CANVAS_ITEM_MEASURE_NAT_FOR_MIN:
+ gtk_widget_measure (widget, GTK_ORIENTATION_HORIZONTAL, -1, &w, NULL, NULL, NULL);
+ gtk_widget_measure (widget, GTK_ORIENTATION_VERTICAL, w, NULL, &h, NULL, NULL);
+ break;
+ case GTK_CANVAS_ITEM_MEASURE_NAT_FOR_NAT:
+ gtk_widget_measure (widget, GTK_ORIENTATION_HORIZONTAL, -1, NULL, &w, NULL, NULL);
+ gtk_widget_measure (widget, GTK_ORIENTATION_VERTICAL, w, NULL, &h, NULL, NULL);
+ break;
+ default:
+ g_assert_not_reached ();
+ w = h = 0;
+ break;
+ }
+ }
+ else
+ {
+ switch (self->measure)
+ {
+ case GTK_CANVAS_ITEM_MEASURE_MIN_FOR_MIN:
+ gtk_widget_measure (widget, GTK_ORIENTATION_VERTICAL, -1, &h, NULL, NULL, NULL);
+ gtk_widget_measure (widget, GTK_ORIENTATION_HORIZONTAL, h, &w, NULL, NULL, NULL);
+ break;
+ case GTK_CANVAS_ITEM_MEASURE_MIN_FOR_NAT:
+ gtk_widget_measure (widget, GTK_ORIENTATION_VERTICAL, -1, NULL, &h, NULL, NULL);
+ gtk_widget_measure (widget, GTK_ORIENTATION_HORIZONTAL, h, &w, NULL, NULL, NULL);
+ break;
+ case GTK_CANVAS_ITEM_MEASURE_NAT_FOR_MIN:
+ gtk_widget_measure (widget, GTK_ORIENTATION_VERTICAL, -1, &h, NULL, NULL, NULL);
+ gtk_widget_measure (widget, GTK_ORIENTATION_HORIZONTAL, h, NULL, &w, NULL, NULL);
+ break;
+ case GTK_CANVAS_ITEM_MEASURE_NAT_FOR_NAT:
+ gtk_widget_measure (widget, GTK_ORIENTATION_VERTICAL, -1, NULL, &h, NULL, NULL);
+ gtk_widget_measure (widget, GTK_ORIENTATION_HORIZONTAL, h, NULL, &w, NULL, NULL);
+ break;
+ default:
+ g_assert_not_reached ();
+ w = h = 0;
+ break;
+ }
+ }
+
+ *width = w;
+ *height = h;
+
+ return TRUE;
+}
+
+static const GtkCanvasSizeClass GTK_CANVAS_SIZE_MEASURE_CLASS =
+{
+ "GtkCanvasSizeMeasure",
+ gtk_canvas_size_measure_copy,
+ gtk_canvas_size_measure_finish,
+ gtk_canvas_size_measure_eval,
+};
+
+void
+gtk_canvas_size_init_measure_item (GtkCanvasSize *size,
+ GtkCanvasItem *item,
+ GtkCanvasItemMeasurement measure)
+{
+ GtkCanvasSizeMeasure *self = &size->measure;
+
+ self->class = &GTK_CANVAS_SIZE_MEASURE_CLASS;
+
+ self->item = item;
+ self->measure = measure;
+}
+
+/**
+ * gtk_canvas_size_new_measure_item:
+ * @item: the item
+ * @measure: how to measure the item
+ *
+ * Measures the widget of @item with the given method to determine
+ * a size.
+ *
+ * Returns: a new size
+ **/
+GtkCanvasSize *
+gtk_canvas_size_new_measure_item (GtkCanvasItem *item,
+ GtkCanvasItemMeasurement measure)
+{
+ GtkCanvasSize *self;
+
+ g_return_val_if_fail (GTK_IS_CANVAS_ITEM (item), NULL);
+
+ self = gtk_canvas_size_alloc (&GTK_CANVAS_SIZE_MEASURE_CLASS);
+
+ gtk_canvas_size_init_measure_item (self, item, measure);
+
+ return self;
+}
+
+/* }}} */
+/* {{{ PUBLIC API */
+
+GtkCanvasSize *
+gtk_canvas_size_copy (const GtkCanvasSize *self)
+{
+ GtkCanvasSize *copy;
+
+ g_return_val_if_fail (self != NULL, NULL);
+
+ copy = gtk_canvas_size_alloc (self->class);
+
+ gtk_canvas_size_init_copy (copy, self);
+
+ return copy;
+}
+
+void
+gtk_canvas_size_free (GtkCanvasSize *self)
+{
+ gtk_canvas_size_finish (self);
+
+ g_slice_free (GtkCanvasSize, self);
+}
+
+gboolean
+gtk_canvas_size_eval (const GtkCanvasSize *self,
+ float *width,
+ float *height)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (width != NULL, FALSE);
+ g_return_val_if_fail (height != NULL, FALSE);
+
+ if (self->class->eval (self, width, height))
+ return TRUE;
+
+ *width = 0;
+ *height = 0;
+ return FALSE;
+}
+
diff --git a/gtk/gtkcanvassize.h b/gtk/gtkcanvassize.h
new file mode 100644
index 0000000000..63a9903e6a
--- /dev/null
+++ b/gtk/gtkcanvassize.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright © 2022 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_CANVAS_SIZE_H__
+#define __GTK_CANVAS_SIZE_H__
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gtk/gtktypes.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CANVAS_SIZE (gtk_canvas_size_get_type ())
+
+GDK_AVAILABLE_IN_ALL
+GType gtk_canvas_size_get_type (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_ALL
+GtkCanvasSize * gtk_canvas_size_copy (const GtkCanvasSize *self);
+GDK_AVAILABLE_IN_ALL
+void gtk_canvas_size_free (GtkCanvasSize *self);
+
+GDK_AVAILABLE_IN_ALL
+gboolean gtk_canvas_size_eval (const GtkCanvasSize *self,
+ float *width,
+ float *height) G_GNUC_WARN_UNUSED_RESULT;
+
+GDK_AVAILABLE_IN_ALL
+GtkCanvasSize * gtk_canvas_size_new (float width,
+ float height);
+GDK_AVAILABLE_IN_ALL
+GtkCanvasSize * gtk_canvas_size_new_from_box (const GtkCanvasBox *box);
+
+typedef enum {
+ GTK_CANVAS_ITEM_MEASURE_MIN_FOR_MIN,
+ GTK_CANVAS_ITEM_MEASURE_MIN_FOR_NAT,
+ GTK_CANVAS_ITEM_MEASURE_NAT_FOR_MIN,
+ GTK_CANVAS_ITEM_MEASURE_NAT_FOR_NAT
+} GtkCanvasItemMeasurement;
+
+GDK_AVAILABLE_IN_ALL
+GtkCanvasSize * gtk_canvas_size_new_measure_item (GtkCanvasItem *item,
+ GtkCanvasItemMeasurement measure);
+
+G_END_DECLS
+
+#endif /* __GTK_SIZE_H__ */
diff --git a/gtk/gtkcanvassizeprivate.h b/gtk/gtkcanvassizeprivate.h
new file mode 100644
index 0000000000..83e14800fa
--- /dev/null
+++ b/gtk/gtkcanvassizeprivate.h
@@ -0,0 +1,56 @@
+#ifndef __GTK_CANVAS_SIZE_PRIVATE_H__
+#define __GTK_CANVAS_SIZE_PRIVATE_H__
+
+#include "gtkcanvassize.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GtkCanvasSizeClass GtkCanvasSizeClass;
+typedef struct _GtkCanvasSizeAbsolute GtkCanvasSizeAbsolute;
+typedef struct _GtkCanvasSizeBox GtkCanvasSizeBox;
+typedef struct _GtkCanvasSizeMeasure GtkCanvasSizeMeasure;
+
+struct _GtkCanvasSizeAbsolute
+{
+ const GtkCanvasSizeClass *class;
+
+ float width;
+ float height;
+};
+
+struct _GtkCanvasSizeBox
+{
+ const GtkCanvasSizeClass *class;
+
+ GtkCanvasBox *box;
+};
+
+struct _GtkCanvasSizeMeasure
+{
+ const GtkCanvasSizeClass *class;
+
+ GtkCanvasItem *item;
+ GtkCanvasItemMeasurement measure;
+};
+
+struct _GtkCanvasSize
+{
+ union {
+ const GtkCanvasSizeClass *class;
+ GtkCanvasSizeAbsolute absolute;
+ GtkCanvasSizeBox box;
+ GtkCanvasSizeMeasure measure;
+ };
+};
+
+
+void gtk_canvas_size_init_measure_item (GtkCanvasSize *self,
+ GtkCanvasItem *item,
+ GtkCanvasItemMeasurement measure);
+void gtk_canvas_size_init_copy (GtkCanvasSize *self,
+ const GtkCanvasSize *source);
+void gtk_canvas_size_finish (GtkCanvasSize *self);
+
+G_END_DECLS
+
+#endif /* __GTK_CANVAS_SIZE_PRIVATE_H__ */
diff --git a/gtk/gtktypes.h b/gtk/gtktypes.h
index 71afde94b0..0bfe629a89 100644
--- a/gtk/gtktypes.h
+++ b/gtk/gtktypes.h
@@ -37,6 +37,11 @@ typedef struct _GtkAdjustment GtkAdjustment;
typedef struct _GtkBitset GtkBitset;
typedef struct _GtkBuilder GtkBuilder;
typedef struct _GtkBuilderScope GtkBuilderScope;
+typedef struct _GtkCanvas GtkCanvas;
+typedef struct _GtkCanvasBox GtkCanvasBox;
+typedef struct _GtkCanvasItem GtkCanvasItem;
+typedef struct _GtkCanvasPoint GtkCanvasPoint;
+typedef struct _GtkCanvasSize GtkCanvasSize;
typedef struct _GtkCssStyleChange GtkCssStyleChange;
typedef struct _GtkEventController GtkEventController;
typedef struct _GtkGesture GtkGesture;
diff --git a/gtk/meson.build b/gtk/meson.build
index 2e404f2ac4..63f01bbea8 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -182,6 +182,11 @@ gtk_public_sources = files([
'gtkbuilderscope.c',
'gtkbutton.c',
'gtkcalendar.c',
+ 'gtkcanvas.c',
+ 'gtkcanvasbox.c',
+ 'gtkcanvasitem.c',
+ 'gtkcanvaspoint.c',
+ 'gtkcanvassize.c',
'gtkcellarea.c',
'gtkcellareabox.c',
'gtkcellareacontext.c',
@@ -473,6 +478,11 @@ gtk_public_headers = files([
'gtkbuilderscope.h',
'gtkbutton.h',
'gtkcalendar.h',
+ 'gtkcanvas.h',
+ 'gtkcanvasbox.h',
+ 'gtkcanvasitem.h',
+ 'gtkcanvaspoint.h',
+ 'gtkcanvassize.h',
'gtkcenterbox.h',
'gtkcenterlayout.h',
'gtkcellarea.h',