[gtk/wip/otte/lottie: 27/79] gtk-demo: Add cute maze demo
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/otte/lottie: 27/79] gtk-demo: Add cute maze demo
- Date: Thu, 24 Dec 2020 00:42:59 +0000 (UTC)
commit e452d8ee5e3d06ece8ba518694b1d2cfa179a93f
Author: Benjamin Otte <otte redhat com>
Date: Thu Nov 26 01:29:42 2020 +0100
gtk-demo: Add cute maze demo
demos/gtk-demo/demo.gresource.xml | 1 +
demos/gtk-demo/meson.build | 1 +
demos/gtk-demo/path_maze.c | 338 ++++++++++++++++++++++++++++++++++++++
3 files changed, 340 insertions(+)
---
diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml
index 4c1c3c3cc4..d4f00fdfc1 100644
--- a/demos/gtk-demo/demo.gresource.xml
+++ b/demos/gtk-demo/demo.gresource.xml
@@ -320,6 +320,7 @@
<file>panes.c</file>
<file>password_entry.c</file>
<file>path_fill.c</file>
+ <file>path_maze.c</file>
<file>path_text.c</file>
<file>peg_solitaire.c</file>
<file>pickers.c</file>
diff --git a/demos/gtk-demo/meson.build b/demos/gtk-demo/meson.build
index 2044f71d64..8bfea79e89 100644
--- a/demos/gtk-demo/meson.build
+++ b/demos/gtk-demo/meson.build
@@ -69,6 +69,7 @@ demos = files([
'panes.c',
'password_entry.c',
'path_fill.c',
+ 'path_maze.c',
'path_text.c',
'peg_solitaire.c',
'pickers.c',
diff --git a/demos/gtk-demo/path_maze.c b/demos/gtk-demo/path_maze.c
new file mode 100644
index 0000000000..07452c4975
--- /dev/null
+++ b/demos/gtk-demo/path_maze.c
@@ -0,0 +1,338 @@
+/* Path/Maze
+ *
+ * This demo shows how to use a GskPath to create a maze and use
+ * gsk_path_measure_get_closest_point() to check the mouse stays
+ * on the path.
+ *
+ * It also shows off the performance of GskPath (or not) as this
+ * is a rather complex path.
+ */
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "paintable.h"
+
+#define MAZE_GRID_SIZE 20
+#define MAZE_STROKE_SIZE_ACTIVE (MAZE_GRID_SIZE - 4)
+#define MAZE_STROKE_SIZE_INACTIVE (MAZE_GRID_SIZE - 12)
+#define MAZE_WIDTH 31
+#define MAZE_HEIGHT 21
+
+#define GTK_TYPE_MAZE (gtk_maze_get_type ())
+G_DECLARE_FINAL_TYPE (GtkMaze, gtk_maze, GTK, MAZE, GtkWidget)
+
+struct _GtkMaze
+{
+ GtkWidget parent_instance;
+
+ int width;
+ int height;
+ GskPath *path;
+ GskPathMeasure *measure;
+ GdkPaintable *background;
+
+ gboolean active;
+};
+
+struct _GtkMazeClass
+{
+ GtkWidgetClass parent_class;
+};
+
+G_DEFINE_TYPE (GtkMaze, gtk_maze, GTK_TYPE_WIDGET)
+
+static void
+gtk_maze_measure (GtkWidget *widget,
+ GtkOrientation orientation,
+ int for_size,
+ int *minimum,
+ int *natural,
+ int *minimum_baseline,
+ int *natural_baseline)
+{
+ GtkMaze *self = GTK_MAZE (widget);
+
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ *minimum = *natural = self->width;
+ else
+ *minimum = *natural = self->height;
+}
+
+static void
+gtk_maze_snapshot (GtkWidget *widget,
+ GdkSnapshot *snapshot)
+{
+ GtkMaze *self = GTK_MAZE (widget);
+ double width = gtk_widget_get_width (widget);
+ double height = gtk_widget_get_height (widget);
+ GskStroke *stroke;
+
+ stroke = gsk_stroke_new (MAZE_STROKE_SIZE_INACTIVE);
+ if (self->active)
+ gsk_stroke_set_line_width (stroke, MAZE_STROKE_SIZE_ACTIVE);
+ gsk_stroke_set_line_join (stroke, GSK_LINE_JOIN_ROUND);
+ gsk_stroke_set_line_cap (stroke, GSK_LINE_CAP_ROUND);
+ gtk_snapshot_push_stroke (snapshot, self->path, stroke);
+ gsk_stroke_free (stroke);
+
+ if (self->background)
+ {
+ gdk_paintable_snapshot (self->background, snapshot, width, height);
+ }
+ else
+ {
+ gtk_snapshot_append_linear_gradient (snapshot,
+ &GRAPHENE_RECT_INIT (0, 0, width, height),
+ &GRAPHENE_POINT_INIT (0, 0),
+ &GRAPHENE_POINT_INIT (width, height),
+ (GskColorStop[8]) {
+ { 0.0, { 1.0, 0.0, 0.0, 1.0 } },
+ { 0.2, { 1.0, 0.0, 0.0, 1.0 } },
+ { 0.3, { 1.0, 1.0, 0.0, 1.0 } },
+ { 0.4, { 0.0, 1.0, 0.0, 1.0 } },
+ { 0.6, { 0.0, 1.0, 1.0, 1.0 } },
+ { 0.7, { 0.0, 0.0, 1.0, 1.0 } },
+ { 0.8, { 1.0, 0.0, 1.0, 1.0 } },
+ { 1.0, { 1.0, 0.0, 1.0, 1.0 } }
+ },
+ 8);
+ }
+
+ gtk_snapshot_pop (snapshot);
+
+}
+
+static void
+gtk_maze_dispose (GObject *object)
+{
+ GtkMaze *self = GTK_MAZE (object);
+
+ g_clear_pointer (&self->path, gsk_path_unref);
+ g_clear_pointer (&self->measure, gsk_path_measure_unref);
+ if (self->background)
+ {
+ g_signal_handlers_disconnect_matched (self->background, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
+ g_clear_object (&self->background);
+ }
+
+ G_OBJECT_CLASS (gtk_maze_parent_class)->dispose (object);
+}
+
+static void
+gtk_maze_class_init (GtkMazeClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gtk_maze_dispose;
+
+ widget_class->measure = gtk_maze_measure;
+ widget_class->snapshot = gtk_maze_snapshot;
+}
+
+static void
+pointer_motion (GtkEventControllerMotion *controller,
+ double x,
+ double y,
+ GtkMaze *self)
+{
+ if (!self->active)
+ return;
+
+ if (gsk_path_measure_get_closest_point (self->measure, &GRAPHENE_POINT_INIT (x, y), NULL) <=
MAZE_STROKE_SIZE_ACTIVE / 2.0f)
+ return;
+
+ self->active = FALSE;
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+pointer_leave (GtkEventControllerMotion *controller,
+ GtkMaze *self)
+{
+ if (!self->active)
+ {
+ self->active = TRUE;
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+ }
+}
+
+static void
+gtk_maze_init (GtkMaze *self)
+{
+ GtkEventController *controller;
+
+ controller = GTK_EVENT_CONTROLLER (gtk_event_controller_motion_new ());
+ g_signal_connect (controller, "motion", G_CALLBACK (pointer_motion), self);
+ g_signal_connect (controller, "leave", G_CALLBACK (pointer_leave), self);
+ gtk_widget_add_controller (GTK_WIDGET (self), controller);
+
+ self->active = TRUE;
+}
+
+static void
+gtk_maze_set_path (GtkMaze *self,
+ GskPath *path)
+{
+ g_clear_pointer (&self->path, gsk_path_unref);
+ g_clear_pointer (&self->measure, gsk_path_measure_unref);
+ self->path = gsk_path_ref (path);
+ self->measure = gsk_path_measure_new (path);
+
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+GtkWidget *
+gtk_maze_new (GskPath *path,
+ GdkPaintable *background,
+ int width,
+ int height)
+{
+ GtkMaze *self;
+
+ self = g_object_new (GTK_TYPE_MAZE, NULL);
+
+ gtk_maze_set_path (self, path);
+ gsk_path_unref (path);
+ self->background = background;
+ if (self->background)
+ {
+ g_signal_connect_swapped (self->background, "invalidate-contents", G_CALLBACK (gtk_widget_queue_draw),
self);
+ g_signal_connect_swapped (self->background, "invalidate-size", G_CALLBACK (gtk_widget_queue_resize),
self);
+ }
+ self->width = width;
+ self->height = height;
+
+ return GTK_WIDGET (self);
+}
+
+static void
+add_point_to_maze (GtkBitset *maze,
+ GskPathBuilder *builder,
+ guint x,
+ guint y)
+{
+ gboolean set[4] = { };
+ guint dir;
+
+ gtk_bitset_add (maze, y * MAZE_WIDTH + x);
+
+ while (TRUE)
+ {
+ set[0] = set[0] || x == 0 || gtk_bitset_contains (maze, y * MAZE_WIDTH + x - 1);
+ set[1] = set[1] || y == 0 || gtk_bitset_contains (maze, (y - 1) * MAZE_WIDTH + x);
+ set[2] = set[2] || x + 1 == MAZE_WIDTH || gtk_bitset_contains (maze, y * MAZE_WIDTH + x + 1);
+ set[3] = set[3] || y + 1 == MAZE_HEIGHT || gtk_bitset_contains (maze, (y + 1) * MAZE_WIDTH + x);
+
+ if (set[0] && set[1] && set[2] && set[3])
+ return;
+
+ do
+ {
+ dir = g_random_int_range (0, 4);
+ }
+ while (set[dir]);
+
+ switch (dir)
+ {
+ case 0:
+ gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
+ gsk_path_builder_line_to (builder, (x - 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
+ add_point_to_maze (maze, builder, x - 1, y);
+ break;
+
+ case 1:
+ gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
+ gsk_path_builder_line_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y - 0.5) * MAZE_GRID_SIZE);
+ add_point_to_maze (maze, builder, x, y - 1);
+ break;
+
+ case 2:
+ gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
+ gsk_path_builder_line_to (builder, (x + 1.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
+ add_point_to_maze (maze, builder, x + 1, y);
+ break;
+
+ case 3:
+ gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
+ gsk_path_builder_line_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 1.5) * MAZE_GRID_SIZE);
+ add_point_to_maze (maze, builder, x, y + 1);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ }
+}
+
+static GskPath *
+create_path_for_maze (GtkWidget *widget)
+{
+ GskPathBuilder *builder;
+ GtkBitset *maze;
+
+ builder = gsk_path_builder_new ();
+ maze = gtk_bitset_new_empty ();
+ /* make sure the outer lines are unreachable:
+ * Set the full range, then remove the center again. */
+ gtk_bitset_add_range (maze, 0, MAZE_WIDTH * MAZE_HEIGHT);
+ gtk_bitset_remove_rectangle (maze, MAZE_WIDTH + 1, MAZE_WIDTH - 2, MAZE_HEIGHT - 2, MAZE_WIDTH);
+
+ /* Fill the maze */
+ add_point_to_maze (maze, builder, MAZE_WIDTH / 2, MAZE_HEIGHT / 2);
+
+ /* Add start and stop lines */
+ gsk_path_builder_move_to (builder, 1.5 * MAZE_GRID_SIZE, -0.5 * MAZE_GRID_SIZE);
+ gsk_path_builder_line_to (builder, 1.5 * MAZE_GRID_SIZE, 1.5 * MAZE_GRID_SIZE);
+ gsk_path_builder_move_to (builder, (MAZE_WIDTH - 1.5) * MAZE_GRID_SIZE, (MAZE_HEIGHT - 1.5) *
MAZE_GRID_SIZE);
+ gsk_path_builder_line_to (builder, (MAZE_WIDTH - 1.5) * MAZE_GRID_SIZE, (MAZE_HEIGHT + 0.5) *
MAZE_GRID_SIZE);
+
+
+ gtk_bitset_unref (maze);
+
+ return gsk_path_builder_free_to_path (builder);
+}
+
+GtkWidget *
+do_path_maze (GtkWidget *do_widget)
+{
+ static GtkWidget *window = NULL;
+
+ if (!window)
+ {
+ GtkWidget *maze;
+ GtkMediaStream *stream;
+ GskPath *path;
+
+ window = gtk_window_new ();
+ gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
+ gtk_window_set_title (GTK_WINDOW (window), "Follow the maze with the mouse");
+ g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
+
+#if 0
+ stream = gtk_media_file_new_for_resource ("/images/gtk-logo.webm");
+#else
+ stream = gtk_nuclear_media_stream_new ();
+#endif
+ gtk_media_stream_play (stream);
+ gtk_media_stream_set_loop (stream, TRUE);
+
+ path = create_path_for_maze (window);
+
+ maze = gtk_maze_new (path,
+ GDK_PAINTABLE (stream),
+ MAZE_WIDTH * MAZE_GRID_SIZE,
+ MAZE_HEIGHT * MAZE_GRID_SIZE);
+
+ gtk_window_set_child (GTK_WINDOW (window), maze);
+ }
+
+ if (!gtk_widget_get_visible (window))
+ gtk_widget_show (window);
+ else
+ gtk_window_destroy (GTK_WINDOW (window));
+
+ return window;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]