[gtk/matthiasc/lottie] Play with paths
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/matthiasc/lottie] Play with paths
- Date: Fri, 20 Nov 2020 04:39:07 +0000 (UTC)
commit 0d8e0521233e4e20e2a7aa18d0804fbf89a7510c
Author: Matthias Clasen <mclasen redhat com>
Date: Thu Nov 19 13:41:21 2020 -0500
Play with paths
Add a simple demo for editing a poly-Bezier curve.
tests/curve.c | 377 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
tests/meson.build | 1 +
tests/simple.c | 126 ++++++++++++------
3 files changed, 463 insertions(+), 41 deletions(-)
---
diff --git a/tests/curve.c b/tests/curve.c
new file mode 100644
index 0000000000..2c5c9177a3
--- /dev/null
+++ b/tests/curve.c
@@ -0,0 +1,377 @@
+#include <gtk/gtk.h>
+
+
+#define RADIUS 5
+
+typedef struct
+{
+ GtkWidget parent_instance;
+ graphene_point_t *points;
+ int n_points;
+ int dragged;
+ gboolean edit;
+} DemoWidget;
+
+typedef struct
+{
+ GtkWidgetClass parent_class;
+} DemoWidgetClass;
+
+GType demo_widget_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_TYPE (DemoWidget, demo_widget, GTK_TYPE_WIDGET)
+
+static float
+dist (graphene_point_t *a, graphene_point_t *b)
+{
+ graphene_vec2_t v;
+
+ graphene_vec2_init (&v, a->x - b->x, a->y - b->y);
+ return graphene_vec2_length (&v);
+}
+
+static void
+drag_begin (GtkGestureDrag *gesture,
+ double start_x,
+ double start_y,
+ DemoWidget *self)
+{
+ int i;
+ graphene_point_t p = GRAPHENE_POINT_INIT (start_x, start_y);
+
+ if (self->edit)
+ for (i = 0; i < self->n_points; i++)
+ {
+ if (dist (&self->points[i], &p) < RADIUS)
+ {
+ self->dragged = i;
+ gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+ return;
+ }
+ }
+
+ gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
+}
+
+static void
+drag_update (GtkGestureDrag *gesture,
+ double offset_x,
+ double offset_y,
+ DemoWidget *self)
+{
+ double x, y;
+ double dx, dy;
+ graphene_point_t *c, *p;
+ double a;
+ double d;
+
+ if (self->dragged == -1)
+ return;
+
+ gtk_gesture_drag_get_start_point (gesture, &x, &y);
+
+ x += offset_x;
+ y += offset_y;
+
+ dx = x - self->points[self->dragged].x;
+ dy = y - self->points[self->dragged].y;
+
+ self->points[self->dragged].x += dx;
+ self->points[self->dragged].y += dy;
+
+ if (self->dragged % 3 == 0)
+ {
+ /* point is on curve */
+ self->points[(self->dragged - 1 + self->n_points) % self->n_points].x += dx;
+ self->points[(self->dragged - 1 + self->n_points) % self->n_points].y += dy;
+
+ self->points[(self->dragged + 1) % self->n_points].x += dx;
+ self->points[(self->dragged + 1) % self->n_points].y += dy;
+ }
+ else
+ {
+ if (self->dragged % 3 == 1)
+ {
+ c = &self->points[(self->dragged - 2 + self->n_points) % self->n_points];
+ p = &self->points[(self->dragged - 1 + self->n_points) % self->n_points];
+ }
+ else if (self->dragged % 3 == 2)
+ {
+ c = &self->points[(self->dragged + 2) % self->n_points];
+ p = &self->points[(self->dragged + 1) % self->n_points];
+ }
+ else
+ g_assert_not_reached ();
+
+ a = atan2 (self->points[self->dragged].y - p->y, self->points[self->dragged].x - p->x) + M_PI;
+ d = dist (c, p);
+ c->x = p->x + d * cos (a);
+ c->y = p->y + d * sin (a);
+ }
+
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+drag_end (GtkGestureDrag *gesture,
+ double offset_x,
+ double offset_y,
+ DemoWidget *self)
+{
+ drag_update (gesture, offset_x, offset_y, self);
+ self->dragged = -1;
+}
+
+static void
+init_points (DemoWidget *self)
+{
+ float w = 200;
+ float h = 200;
+ float cx = w / 2;
+ float cy = h / 2;
+ float pad = 20;
+ float r = (w - 2 * pad) / 2;
+ float k = 0.55228;
+ float kr = k * r;
+
+ /* curve 1 */
+ self->points[0] = GRAPHENE_POINT_INIT (cx, pad);
+ self->points[1] = GRAPHENE_POINT_INIT (cx + kr, pad);
+ self->points[2] = GRAPHENE_POINT_INIT (w - pad, cy - kr);
+ self->points[3] = GRAPHENE_POINT_INIT (w - pad, cy);
+ /* curve 2 */
+ self->points[4] = GRAPHENE_POINT_INIT (w - pad, cy + kr);
+ self->points[5] = GRAPHENE_POINT_INIT (cx + kr, h - pad);
+ self->points[6] = GRAPHENE_POINT_INIT (cx, h - pad);
+ /* curve 3 */
+ self->points[7] = GRAPHENE_POINT_INIT (cx - kr, h - pad);
+ self->points[8] = GRAPHENE_POINT_INIT (pad, cy + kr);
+ self->points[9] = GRAPHENE_POINT_INIT (pad, cy);
+ /* curve 4 */
+ self->points[10] = GRAPHENE_POINT_INIT (pad, cy - kr);
+ self->points[11] = GRAPHENE_POINT_INIT (cx - kr, pad);
+}
+
+static void
+demo_widget_init (DemoWidget *self)
+{
+ GtkGesture *gesture;
+
+ self->dragged = -1;
+ self->edit = FALSE;
+
+ gesture = gtk_gesture_drag_new ();
+ g_signal_connect (gesture, "drag-begin", G_CALLBACK (drag_begin), self);
+ g_signal_connect (gesture, "drag-update", G_CALLBACK (drag_update), self);
+ g_signal_connect (gesture, "drag-end", G_CALLBACK (drag_end), self);
+ gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
+
+ self->n_points = 12;
+ self->points = g_new (graphene_point_t, self->n_points);
+
+ init_points (self);
+}
+
+static void
+demo_widget_snapshot (GtkWidget *widget,
+ GtkSnapshot *snapshot)
+{
+ DemoWidget *self = (DemoWidget *)widget;
+ GskPathBuilder *builder;
+ GskPath *path;
+ GskStroke *stroke;
+ int i;
+ float width;
+ float height;
+
+ width = gtk_widget_get_width (widget);
+ height = gtk_widget_get_width (widget);
+
+ builder = gsk_path_builder_new ();
+
+ if (self->edit)
+ {
+ gsk_path_builder_move_to (builder, self->points[0].x, self->points[0].y);
+ for (i = 1; i < self->n_points; i++)
+ {
+ if (i % 3 == 2)
+ gsk_path_builder_move_to (builder, self->points[i].x, self->points[i].y);
+ else
+ gsk_path_builder_line_to (builder, self->points[i].x, self->points[i].y);
+ }
+ gsk_path_builder_line_to (builder, self->points[0].x, self->points[0].y);
+ }
+
+ gsk_path_builder_move_to (builder, self->points[0].x, self->points[0].y);
+ for (i = 1; i < self->n_points; i += 3)
+ {
+ gsk_path_builder_curve_to (builder,
+ self->points[i].x, self->points[i].y,
+ self->points[(i + 1) % self->n_points].x, self->points[(i + 1) %
self->n_points].y,
+ self->points[(i + 2) % self->n_points].x, self->points[(i + 2) %
self->n_points].y);
+ }
+
+ path = gsk_path_builder_free_to_path (builder);
+ stroke = gsk_stroke_new (1);
+ gtk_snapshot_push_stroke (snapshot, path, stroke);
+ gsk_stroke_free (stroke);
+ gsk_path_unref (path);
+
+ gtk_snapshot_append_color (snapshot,
+ &(GdkRGBA){ 0, 0, 0, 1 },
+ &GRAPHENE_RECT_INIT (0, 0, width, height ));
+
+ gtk_snapshot_pop (snapshot);
+
+ if (self->edit)
+ {
+ if (self->dragged != -1)
+ {
+ builder = gsk_path_builder_new ();
+ gsk_path_builder_add_circle (builder, &self->points[self->dragged], RADIUS);
+ path = gsk_path_builder_free_to_path (builder);
+
+ gtk_snapshot_push_fill (snapshot, path, GSK_FILL_RULE_WINDING);
+ gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 1, 1, 0, 1 }, &GRAPHENE_RECT_INIT (0, 0, width,
height));
+ gtk_snapshot_pop (snapshot);
+
+ stroke = gsk_stroke_new (1.0);
+ gtk_snapshot_push_stroke (snapshot, path, stroke);
+ gsk_stroke_free (stroke);
+ gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 0, 1 }, &GRAPHENE_RECT_INIT (0, 0, width,
height));
+ gtk_snapshot_pop (snapshot);
+
+ gsk_path_unref (path);
+ }
+
+ builder = gsk_path_builder_new ();
+
+ for (i = 0; i < self->n_points; i++)
+ {
+ if (i % 3 == 0 && i != self->dragged)
+ gsk_path_builder_add_circle (builder, &self->points[i], RADIUS);
+ }
+
+ path = gsk_path_builder_free_to_path (builder);
+
+ gtk_snapshot_push_fill (snapshot, path, GSK_FILL_RULE_WINDING);
+ gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 1, 0, 0, 1 }, &GRAPHENE_RECT_INIT (0, 0, width,
height));
+ gtk_snapshot_pop (snapshot);
+
+ stroke = gsk_stroke_new (1.0);
+ gtk_snapshot_push_stroke (snapshot, path, stroke);
+ gsk_stroke_free (stroke);
+ gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 0, 1 }, &GRAPHENE_RECT_INIT (0, 0, width,
height));
+ gtk_snapshot_pop (snapshot);
+
+ gsk_path_unref (path);
+
+ builder = gsk_path_builder_new ();
+
+ for (i = 0; i < self->n_points; i++)
+ {
+ if (i % 3 != 0 && i != self->dragged)
+ gsk_path_builder_add_circle (builder, &self->points[i], RADIUS);
+ }
+
+ path = gsk_path_builder_free_to_path (builder);
+
+ gtk_snapshot_push_fill (snapshot, path, GSK_FILL_RULE_WINDING);
+ gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 1, 1 }, &GRAPHENE_RECT_INIT (0, 0, width,
height));
+ gtk_snapshot_pop (snapshot);
+
+ stroke = gsk_stroke_new (1.0);
+ gtk_snapshot_push_stroke (snapshot, path, stroke);
+ gsk_stroke_free (stroke);
+ gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 0, 1 }, &GRAPHENE_RECT_INIT (0, 0, width,
height));
+ gtk_snapshot_pop (snapshot);
+
+ gsk_path_unref (path);
+ }
+}
+
+static void
+demo_widget_measure (GtkWidget *widget,
+ GtkOrientation orientation,
+ int for_size,
+ int *minimum_size,
+ int *natural_size,
+ int *minimum_baseline,
+ int *natural_baseline)
+{
+ *minimum_size = 100;
+ *natural_size = 200;
+}
+
+static void
+demo_widget_class_init (DemoWidgetClass *class)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+
+ widget_class->snapshot = demo_widget_snapshot;
+ widget_class->measure = demo_widget_measure;
+}
+
+static GtkWidget *
+demo_widget_new (void)
+{
+ return g_object_new (demo_widget_get_type (), NULL);
+}
+
+static void
+edit_changed (GtkToggleButton *button,
+ GParamSpec *pspec,
+ DemoWidget *self)
+{
+ self->edit = gtk_toggle_button_get_active (button);
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+reset (GtkButton *button,
+ DemoWidget *self)
+{
+ init_points (self);
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWindow *window;
+ GtkWidget *demo;
+ GtkWidget *edit_toggle;
+ GtkWidget *reset_button;
+ GtkWidget *titlebar;
+
+ gtk_init ();
+
+ window = GTK_WINDOW (gtk_window_new ());
+ gtk_window_set_default_size (GTK_WINDOW (window), 250, 250);
+
+ edit_toggle = gtk_toggle_button_new ();
+ gtk_button_set_icon_name (GTK_BUTTON (edit_toggle), "document-edit-symbolic");
+
+ reset_button = gtk_button_new_from_icon_name ("edit-undo-symbolic");
+
+ titlebar = gtk_header_bar_new ();
+ gtk_header_bar_pack_start (GTK_HEADER_BAR (titlebar), edit_toggle);
+ gtk_header_bar_pack_start (GTK_HEADER_BAR (titlebar), reset_button);
+
+ gtk_window_set_titlebar (GTK_WINDOW (window), titlebar);
+
+ demo = demo_widget_new ();
+
+ g_signal_connect (edit_toggle, "notify::active", G_CALLBACK (edit_changed), demo);
+ g_signal_connect (reset_button, "clicked", G_CALLBACK (reset), demo);
+
+ gtk_window_set_child (window, demo);
+
+ gtk_window_present (window);
+
+ while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0)
+ g_main_context_iteration (NULL, TRUE);
+
+ return 0;
+}
diff --git a/tests/meson.build b/tests/meson.build
index 99f95009f0..e9d456c1fe 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -1,5 +1,6 @@
gtk_tests = [
# testname, optional extra sources
+ ['curve'],
['testupload'],
['testtransform'],
['testdropdown'],
diff --git a/tests/simple.c b/tests/simple.c
index 062afdc9fa..7b24ac9a00 100644
--- a/tests/simple.c
+++ b/tests/simple.c
@@ -1,67 +1,111 @@
-/* simple.c
- * Copyright (C) 2017 Red Hat, Inc
- * Author: Benjamin Otte
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 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
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-#include "config.h"
#include <gtk/gtk.h>
+typedef struct
+{
+ GtkWidget parent_instance;
+} DemoWidget;
+
+typedef struct
+{
+ GtkWidgetClass parent_class;
+} DemoWidgetClass;
+
+GType demo_widget_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_TYPE (DemoWidget, demo_widget, GTK_TYPE_WIDGET)
+
+static void
+demo_widget_init (DemoWidget *demo)
+{
+}
+
+static void
+demo_widget_snapshot (GtkWidget *widget,
+ GtkSnapshot *snapshot)
+{
+ GdkRGBA red, green, yellow, blue;
+ float w, h;
+ GskPathBuilder *builder;
+ GskPath *path;
+
+ gdk_rgba_parse (&red, "red");
+ gdk_rgba_parse (&green, "green");
+ gdk_rgba_parse (&yellow, "yellow");
+ gdk_rgba_parse (&blue, "blue");
+
+ w = gtk_widget_get_width (widget) / 2.0;
+ h = gtk_widget_get_height (widget) / 2.0;
+
+ builder = gsk_path_builder_new ();
+ gsk_path_builder_move_to (builder, 10, 10);
+ gsk_path_builder_curve_to (builder, 100, 10, 110, 20, 110, 30);
+ gsk_path_builder_curve_to (builder, 80, 30, 100, 60, 80, 60);
+ gsk_path_builder_line_to (builder, 120, 100);
+ gsk_path_builder_curve_to (builder, 110, 110, 80, 120, 30, 70);
+ gsk_path_builder_close (builder);
+ path = gsk_path_builder_free_to_path (builder);
+
+ gtk_snapshot_push_fill (snapshot, path, GSK_FILL_RULE_WINDING);
+ gsk_path_unref (path);
+
+ gtk_snapshot_append_color (snapshot, &red,
+ &GRAPHENE_RECT_INIT(0, 0, w, h));
+ gtk_snapshot_append_color (snapshot, &green,
+ &GRAPHENE_RECT_INIT(w, 0, w, h));
+ gtk_snapshot_append_color (snapshot, &yellow,
+ &GRAPHENE_RECT_INIT(0, h, w, h));
+ gtk_snapshot_append_color (snapshot, &blue,
+ &GRAPHENE_RECT_INIT(w, h, w, h));
+
+ gtk_snapshot_pop (snapshot);
+}
+
static void
-hello (void)
+demo_widget_measure (GtkWidget *widget,
+ GtkOrientation orientation,
+ int for_size,
+ int *minimum_size,
+ int *natural_size,
+ int *minimum_baseline,
+ int *natural_baseline)
{
- g_print ("hello world\n");
+ *minimum_size = 100;
+ *natural_size = 200;
}
static void
-quit_cb (GtkWidget *widget,
- gpointer data)
+demo_widget_class_init (DemoWidgetClass *class)
{
- gboolean *done = data;
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
- *done = TRUE;
+ widget_class->snapshot = demo_widget_snapshot;
+ widget_class->measure = demo_widget_measure;
+}
- g_main_context_wakeup (NULL);
+static GtkWidget *
+demo_widget_new (void)
+{
+ return g_object_new (demo_widget_get_type (), NULL);
}
int
main (int argc, char *argv[])
{
- GtkWidget *window, *button;
- gboolean done = FALSE;
+ GtkWindow *window;
+ GtkWidget *demo;
gtk_init ();
- window = gtk_window_new ();
- gtk_window_set_title (GTK_WINDOW (window), "hello world");
- gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
- g_signal_connect (window, "destroy", G_CALLBACK (quit_cb), &done);
+ window = GTK_WINDOW (gtk_window_new ());
- button = gtk_button_new ();
- gtk_button_set_label (GTK_BUTTON (button), "hello world");
- gtk_widget_set_margin_top (button, 10);
- gtk_widget_set_margin_bottom (button, 10);
- gtk_widget_set_margin_start (button, 10);
- gtk_widget_set_margin_end (button, 10);
- g_signal_connect (button, "clicked", G_CALLBACK (hello), NULL);
+ demo = demo_widget_new ();
- gtk_window_set_child (GTK_WINDOW (window), button);
+ gtk_window_set_child (window, demo);
- gtk_widget_show (window);
+ gtk_window_present (window);
- while (!done)
+ while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0)
g_main_context_iteration (NULL, TRUE);
return 0;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]