[gtk/wip/otte/canvas: 34/36] demos: Add the planarity game
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/otte/canvas: 34/36] demos: Add the planarity game
- Date: Tue, 5 Jul 2022 05:41:43 +0000 (UTC)
commit 1ebfc91156a70c0eb619cba3537d61d218df20db
Author: Benjamin Otte <otte redhat com>
Date: Wed Jun 29 07:44:03 2022 +0200
demos: Add the planarity game
See https://en.wikipedia.org/wiki/Planarity for what it is.
Or install gplanarity if your distro is into old-school apps that use
GTK 2.7.2 (or newer).
demos/gtk-demo/canvas_planarity.c | 317 ++++++++++++++++++++++++++++++++++++++
demos/gtk-demo/meson.build | 1 +
2 files changed, 318 insertions(+)
---
diff --git a/demos/gtk-demo/canvas_planarity.c b/demos/gtk-demo/canvas_planarity.c
new file mode 100644
index 0000000000..93ab37571b
--- /dev/null
+++ b/demos/gtk-demo/canvas_planarity.c
@@ -0,0 +1,317 @@
+/* Canvas/Planarity
+ *
+ * This demonstrates how the canvas can be used to display different
+ * types of data (vertices and edges) and how to operate on them.
+ */
+
+#include <gtk/gtk.h>
+
+#define GTK_TYPE_DIAGONAL_LINE (gtk_diagonal_line_get_type ())
+G_DECLARE_FINAL_TYPE (GtkDiagonalLine, gtk_diagonal_line, GTK, DIAGONAL_LINE, GtkWidget)
+
+struct _GtkDiagonalLine
+{
+ GtkWidget parent_instance;
+};
+
+struct _GtkDiagonalLineClass
+{
+ GtkWidgetClass parent_class;
+};
+
+static void
+gtk_diagonal_line_snapshot (GtkWidget *widget,
+ GtkSnapshot *snapshot)
+{
+ const float line_width = 6;
+ const int width = gtk_widget_get_width (widget);
+ const int height = gtk_widget_get_height (widget);
+ const double length = sqrt (width * width + height * height);
+ GskRoundedRect clip;
+ GdkRGBA color;
+
+ gtk_snapshot_save (snapshot);
+ gtk_snapshot_rotate (snapshot, atan2 (height, width) * 180 / G_PI);
+
+ gsk_rounded_rect_init_from_rect (&clip,
+ &GRAPHENE_RECT_INIT (
+ -0.5f * line_width,
+ -0.5f * line_width,
+ length + line_width,
+ line_width),
+ 0.5f * line_width);
+ gtk_snapshot_push_rounded_clip (snapshot, &clip);
+ gtk_style_context_get_color (gtk_widget_get_style_context (widget), &color);
+ gtk_snapshot_append_color (snapshot,
+ &color,
+ &clip.bounds);
+ gtk_snapshot_pop (snapshot);
+ gtk_snapshot_restore (snapshot);
+}
+
+/* When defining the GType, we need to implement the GdkPaintable interface */
+G_DEFINE_TYPE (GtkDiagonalLine, gtk_diagonal_line, GTK_TYPE_WIDGET)
+
+/* Here's the boilerplate for the GObject declaration.
+ */
+static void
+gtk_diagonal_line_class_init (GtkDiagonalLineClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ widget_class->snapshot = gtk_diagonal_line_snapshot;
+}
+
+static void
+gtk_diagonal_line_init (GtkDiagonalLine *self)
+{
+}
+
+static GtkWidget *
+gtk_diagonal_line_new (void)
+{
+ return g_object_new (GTK_TYPE_DIAGONAL_LINE, NULL);
+}
+
+typedef struct _PlanarityVertex PlanarityVertex;
+struct _PlanarityVertex
+{
+ GObject parent_instance;
+
+ graphene_point_t position;
+};
+
+#define PLANARITY_TYPE_VERTEX (planarity_vertex_get_type ())
+G_DECLARE_FINAL_TYPE (PlanarityVertex, planarity_vertex, PLANARITY, VERTEX, GObject);
+
+G_DEFINE_TYPE (PlanarityVertex, planarity_vertex, G_TYPE_OBJECT);
+
+static void
+planarity_vertex_class_init (PlanarityVertexClass *klass)
+{
+}
+
+static void
+planarity_vertex_init (PlanarityVertex *self)
+{
+}
+
+static PlanarityVertex *
+planarity_vertex_new (float x,
+ float y)
+{
+ PlanarityVertex *self;
+
+ self = g_object_new (PLANARITY_TYPE_VERTEX, NULL);
+
+ graphene_point_init (&self->position, x, y);
+
+ return self;
+}
+
+typedef struct _PlanarityEdge PlanarityEdge;
+struct _PlanarityEdge
+{
+ GObject parent_instance;
+
+ PlanarityVertex *from;
+ PlanarityVertex *to;
+};
+
+#define PLANARITY_TYPE_EDGE (planarity_edge_get_type ())
+G_DECLARE_FINAL_TYPE (PlanarityEdge, planarity_edge, PLANARITY, EDGE, GObject);
+
+G_DEFINE_TYPE (PlanarityEdge, planarity_edge, G_TYPE_OBJECT);
+
+static void
+planarity_edge_class_init (PlanarityEdgeClass *klass)
+{
+}
+
+static void
+planarity_edge_init (PlanarityEdge *self)
+{
+}
+
+static PlanarityEdge *
+planarity_edge_new (PlanarityVertex *from,
+ PlanarityVertex *to)
+{
+ PlanarityEdge *edge;
+
+ edge = g_object_new (PLANARITY_TYPE_EDGE, NULL);
+
+ edge->from = g_object_ref (from);
+ edge->to = g_object_ref (to);
+
+ return edge;
+}
+
+static void
+set_item_position (GtkCanvasItem *ci)
+{
+ 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);
+}
+
+static void
+move_item (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));
+
+ vertex->position = GRAPHENE_POINT_INIT (CLAMP (vertex->position.x + x, 0, 1), CLAMP (vertex->position.y +
y, 0, 1));
+ set_item_position (ci);
+}
+
+static void
+bind_item (GtkListItemFactory *factory,
+ GtkCanvasItem *ci)
+{
+ GtkCanvasBox *box;
+ gpointer item;
+
+ item = gtk_canvas_item_get_item (ci);
+
+ if (PLANARITY_IS_VERTEX (item))
+ {
+ GtkWidget *widget;
+ GtkGesture *gesture;
+
+ 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);
+ gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (gesture));
+ gtk_canvas_item_set_widget (ci, widget);
+
+ set_item_position (ci);
+ }
+ 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);
+ }
+}
+
+static GListModel *
+create_model (void)
+{
+ GListStore *result, *vertices, *edges;
+ guint n = 10;
+ guint i, j;
+
+ vertices = g_list_store_new (PLANARITY_TYPE_VERTEX);
+ edges = g_list_store_new (PLANARITY_TYPE_EDGE);
+
+ for (i = 0; i < n; i++)
+ {
+ PlanarityVertex *vertex = planarity_vertex_new (g_random_double (), g_random_double ());
+ g_list_store_append (vertices, vertex);
+
+ for (j = 0; j < i; j++)
+ {
+ PlanarityEdge *edge;
+ PlanarityVertex *other;
+ if (g_random_boolean ())
+ continue;
+
+ other = g_list_model_get_item (G_LIST_MODEL (vertices), j);
+ edge = planarity_edge_new (vertex, other);
+ g_object_unref (other);
+ g_list_store_append (edges, edge);
+ g_object_unref (edge);
+ }
+ g_object_unref (vertex);
+ }
+
+ result = g_list_store_new (G_TYPE_LIST_MODEL);
+ /* put edges before vertices due to staking order */
+ g_list_store_append (result, edges);
+ g_object_unref (edges);
+ g_list_store_append (result, vertices);
+ g_object_unref (vertices);
+
+ return G_LIST_MODEL (gtk_flatten_list_model_new (G_LIST_MODEL (result)));
+}
+
+GtkWidget *
+do_canvas_planarity (GtkWidget *do_widget)
+{
+ static GtkWidget *window = NULL;
+
+ if (!window)
+ {
+ GtkWidget *canvas;
+ GListModel *model;
+ 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), 600, 400);
+ g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
+
+ model = create_model ();
+
+ 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 (model, 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/demos/gtk-demo/meson.build b/demos/gtk-demo/meson.build
index 293c488827..4901912bed 100644
--- a/demos/gtk-demo/meson.build
+++ b/demos/gtk-demo/meson.build
@@ -5,6 +5,7 @@ demos = files([
'assistant.c',
'builder.c',
'canvas_intro.c',
+ 'canvas_planarity.c',
'canvas_puzzle.c',
'clipboard.c',
'combobox.c',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]