[gtk/matthiasc/lottie-tests: 1/2] Add an interactive path test
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/matthiasc/lottie-tests: 1/2] Add an interactive path test
- Date: Sat, 28 Nov 2020 17:53:14 +0000 (UTC)
commit eb247837133b84d2069afb974a30f87461ad54c0
Author: Matthias Clasen <mclasen redhat com>
Date: Tue Nov 24 14:24:40 2020 -0500
Add an interactive path test
This one is for interactive exploring of svg paths.
You can enter an SVG path in the entry and hit Enter
to see how GSK renders it. If you click the button
in the headerbar, you can see what GTK thinks the
closest point, tangent and distance are wrt. to the
mouse position, and the bounding box of the path.
tests/curve2.c | 398 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
tests/meson.build | 1 +
2 files changed, 399 insertions(+)
---
diff --git a/tests/curve2.c b/tests/curve2.c
new file mode 100644
index 0000000000..c4be5cf8f0
--- /dev/null
+++ b/tests/curve2.c
@@ -0,0 +1,398 @@
+#include <gtk/gtk.h>
+
+#define DEMO_TYPE_WIDGET (demo_widget_get_type ())
+G_DECLARE_FINAL_TYPE (DemoWidget, demo_widget, DEMO, WIDGET, GtkWidget)
+
+struct _DemoWidget
+{
+ GtkWidget parent_instance;
+ GskPath *orig_path;
+ GskPath *path;
+ GskPathMeasure *measure;
+ double x, y;
+ graphene_point_t point;
+ graphene_point_t point2;
+ graphene_vec2_t tangent;
+ double start, end;
+
+ gboolean track;
+ gboolean show_bounding_box;
+ GtkWidget *label;
+};
+
+struct _DemoWidgetClass
+{
+ GtkWidgetClass parent_class;
+};
+
+G_DEFINE_TYPE (DemoWidget, demo_widget, GTK_TYPE_WIDGET)
+
+static void
+motion (GtkEventControllerMotion *controller,
+ double x,
+ double y,
+ DemoWidget *self)
+{
+ float distance;
+ char *text;
+ float t;
+
+ if (!self->track)
+ return;
+
+ self->x = x;
+ self->y = y;
+ gsk_path_measure_get_closest_point_full (self->measure,
+ &GRAPHENE_POINT_INIT (x, y),
+ FLT_MAX,
+ &distance,
+ &self->point,
+ &t,
+ &self->tangent);
+
+ gsk_path_measure_get_point (self->measure, t, &self->point2, NULL);
+
+ text = g_strdup_printf ("%.1f", distance);
+ gtk_label_set_label (GTK_LABEL (self->label), text);
+
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+demo_widget_init (DemoWidget *self)
+{
+ GtkEventController *controller;
+
+ self->start = 0;
+ self->end = 1;
+
+ self->label = gtk_label_new ("");
+ gtk_widget_set_parent (self->label, GTK_WIDGET (self));
+ gtk_widget_set_halign (self->label, GTK_ALIGN_END);
+ gtk_widget_set_valign (self->label, GTK_ALIGN_START);
+
+ controller = gtk_event_controller_motion_new ();
+ g_signal_connect (controller, "motion", G_CALLBACK (motion), self);
+ gtk_widget_add_controller (GTK_WIDGET (self), controller);
+}
+
+static void
+demo_widget_snapshot (GtkWidget *widget,
+ GtkSnapshot *snapshot)
+{
+ DemoWidget *self = DEMO_WIDGET (widget);
+ int width, height;
+ GskStroke *stroke;
+ GskPathBuilder *builder;
+ GskPath *path;
+ graphene_point_t p;
+
+ if (!self->path)
+ return;
+
+ width = gtk_widget_get_width (widget);
+ height = gtk_widget_get_width (widget);
+
+ stroke = gsk_stroke_new (1.0);
+ gtk_snapshot_push_stroke (snapshot, self->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);
+
+ if (self->show_bounding_box)
+ {
+ graphene_rect_t bounds;
+
+ if (gsk_path_get_bounds (self->path, &bounds))
+ {
+ builder = gsk_path_builder_new ();
+
+ gsk_path_builder_add_rect (builder, &bounds);
+
+ path = gsk_path_builder_free_to_path (builder);
+
+ 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, 0.5},
+ &GRAPHENE_RECT_INIT (0, 0, width, height ));
+
+ gtk_snapshot_pop (snapshot);
+
+ gsk_path_unref (path);
+ }
+ }
+
+ if (self->track)
+ {
+ p.x = self->point.x + graphene_vec2_get_x (&self->tangent) * 40;
+ p.y = self->point.y + graphene_vec2_get_y (&self->tangent) * 40;
+
+ builder = gsk_path_builder_new ();
+
+ gsk_path_builder_move_to (builder, self->point.x, self->point.y);
+ gsk_path_builder_line_to (builder, p.x, p.y);
+
+ path = gsk_path_builder_free_to_path (builder);
+
+ 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 ();
+
+ gsk_path_builder_add_circle (builder, &self->point, 5);
+ gsk_path_builder_add_circle (builder, &p, 2.5);
+ gsk_path_builder_add_circle (builder, &self->point2, 5);
+
+ path = gsk_path_builder_free_to_path (builder);
+
+ gtk_snapshot_push_fill (snapshot, path, 0);
+ 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);
+
+ gtk_widget_snapshot_child (widget, self->label, snapshot);
+ }
+}
+
+static void
+demo_widget_dispose (GObject *object)
+{
+ DemoWidget *self = DEMO_WIDGET (object);
+
+ g_clear_pointer (&self->path, gsk_path_unref);
+ g_clear_pointer (&self->measure, gsk_path_measure_unref);
+ g_clear_pointer (&self->label, gtk_widget_unparent);
+
+ G_OBJECT_CLASS (demo_widget_parent_class)->dispose (object);
+}
+
+static void
+demo_widget_class_init (DemoWidgetClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+
+ object_class->dispose = demo_widget_dispose;
+
+ widget_class->snapshot = demo_widget_snapshot;
+
+ gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
+}
+
+static GtkWidget *
+demo_widget_new (void)
+{
+ return g_object_new (DEMO_TYPE_WIDGET, NULL);
+}
+
+static void
+update_path (DemoWidget *self)
+{
+ g_clear_pointer (&self->path, gsk_path_unref);
+ g_clear_pointer (&self->measure, gsk_path_measure_unref);
+
+ if (self->start > 0 || self->end < 1)
+ {
+ GskPathMeasure *measure;
+ GskPathBuilder *builder;
+ float length;
+
+ measure = gsk_path_measure_new (self->orig_path);
+ length = gsk_path_measure_get_length (measure);
+ builder = gsk_path_builder_new ();
+ gsk_path_measure_add_segment (measure, builder, self->start * length, self->end * length);
+ self->path = gsk_path_builder_free_to_path (builder);
+ gsk_path_measure_unref (measure);
+ }
+ else
+ self->path = gsk_path_ref (self->orig_path);
+
+ self->measure = gsk_path_measure_new (self->path);
+
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+demo_widget_set_path (DemoWidget *self,
+ GskPath *path)
+{
+ g_clear_pointer (&self->orig_path, gsk_path_unref);
+ self->orig_path = gsk_path_ref (path);
+
+ update_path (self);
+}
+
+static void
+activate (GtkEntry *entry,
+ DemoWidget *demo)
+{
+ GskPath *path;
+
+ path = gsk_path_parse (gtk_editable_get_text (GTK_EDITABLE (entry)));
+ if (path)
+ {
+ demo_widget_set_path (demo, path);
+ gsk_path_unref (path);
+ }
+}
+
+static void
+init_demo (DemoWidget *demo,
+ GtkEditable *editable)
+{
+ GskPathBuilder *builder;
+ GskPath *path;
+ char *string;
+
+ builder = gsk_path_builder_new ();
+ gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (150, 150), 100);
+ gsk_path_builder_add_rect (builder, &GRAPHENE_RECT_INIT (100, 100, 100, 100));
+ gsk_path_builder_move_to (builder, 300, 150);
+ gsk_path_builder_curve_to (builder, 300, 50, 400, 50, 400, 150);
+ gsk_path_builder_curve_to (builder, 400, 250, 500, 250, 500, 150);
+ path = gsk_path_builder_free_to_path (builder);
+
+ demo_widget_set_path (demo, path);
+
+ string = gsk_path_to_string (path);
+ gtk_editable_set_text (editable, string);
+ g_free (string);
+ gsk_path_unref (path);
+}
+
+static void
+track_toggled (GtkCheckButton *button,
+ DemoWidget *self)
+{
+ self->track = gtk_check_button_get_active (button);
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+bb_toggled (GtkCheckButton *button,
+ DemoWidget *self)
+{
+ self->show_bounding_box = gtk_check_button_get_active (button);
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static GtkWidget *start_scale;
+static GtkWidget *end_scale;
+
+static void
+range_changed (GtkRange *range,
+ DemoWidget *self)
+{
+ double start, end;
+
+ if (range == GTK_RANGE (start_scale))
+ {
+ start = gtk_range_get_value (range);
+ end = MAX (start, gtk_range_get_value (GTK_RANGE (end_scale)));
+ gtk_range_set_value (GTK_RANGE (end_scale), end);
+ }
+ else
+ {
+ end = gtk_range_get_value (range);
+ start = MIN (end, gtk_range_get_value (GTK_RANGE (start_scale)));
+ gtk_range_set_value (GTK_RANGE (start_scale), start);
+ }
+
+ self->start = start;
+ self->end = end;
+
+ update_path (self);
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *window, *box, *demo, *entry;
+ GtkWidget *popover, *button, *grid;
+ GtkWidget *header, *toggle;
+
+ gtk_init ();
+
+ window = gtk_window_new ();
+ gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
+ box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_window_set_child (GTK_WINDOW (window), box);
+
+ demo = demo_widget_new ();
+ gtk_widget_set_hexpand (demo, TRUE);
+ gtk_widget_set_vexpand (demo, TRUE);
+ gtk_box_append (GTK_BOX (box), demo);
+
+ header = gtk_header_bar_new ();
+ button = gtk_menu_button_new ();
+ gtk_menu_button_set_icon_name (GTK_MENU_BUTTON (button), "emblem-system-symbolic");
+ gtk_header_bar_pack_start (GTK_HEADER_BAR (header), button);
+ gtk_window_set_titlebar (GTK_WINDOW (window), header);
+
+ popover = gtk_popover_new ();
+ gtk_menu_button_set_popover (GTK_MENU_BUTTON (button), popover);
+
+ grid = gtk_grid_new ();
+ gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
+ gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
+ gtk_popover_set_child (GTK_POPOVER (popover), grid);
+
+ toggle = gtk_check_button_new_with_label ("Show closest point");
+ g_signal_connect (toggle, "toggled", G_CALLBACK (track_toggled), demo);
+ gtk_grid_attach (GTK_GRID (grid), toggle, 0, 0, 1, 1);
+
+ toggle = gtk_check_button_new_with_label ("Show bounding box");
+ g_signal_connect (toggle, "toggled", G_CALLBACK (bb_toggled), demo);
+ gtk_grid_attach (GTK_GRID (grid), toggle, 0, 1, 1, 1);
+
+ entry = gtk_entry_new ();
+ g_signal_connect (entry, "activate", G_CALLBACK (activate), demo);
+ gtk_box_append (GTK_BOX (box), entry);
+
+ start_scale = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 0, 1, 0.1);
+ g_signal_connect (start_scale, "value-changed", G_CALLBACK (range_changed), demo);
+ gtk_box_append (GTK_BOX (box), start_scale);
+
+ end_scale = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 0, 1, 0.1);
+ gtk_range_set_value (GTK_RANGE (end_scale), 1);
+ g_signal_connect (end_scale, "value-changed", G_CALLBACK (range_changed), demo);
+ gtk_box_append (GTK_BOX (box), end_scale);
+
+ init_demo (DEMO_WIDGET (demo), GTK_EDITABLE (entry));
+
+ gtk_window_present (GTK_WINDOW (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..42c492b67f 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -1,5 +1,6 @@
gtk_tests = [
# testname, optional extra sources
+ ['curve2'],
['testupload'],
['testtransform'],
['testdropdown'],
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]