[gtk/matthiasc/lottie2: 40/40] Add an interactive path test




commit f0fc63ec420ec149e69a6e09eae719e7083d2f62
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.
    
    This is fun and a good debugging aid.

 tests/curve2.c    | 276 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/meson.build |   1 +
 2 files changed, 277 insertions(+)
---
diff --git a/tests/curve2.c b/tests/curve2.c
new file mode 100644
index 0000000000..e83b3d29e8
--- /dev/null
+++ b/tests/curve2.c
@@ -0,0 +1,276 @@
+#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 *path;
+  GskPathMeasure *measure;
+  double x, y;
+  graphene_point_t point;
+  graphene_vec2_t tangent;
+
+  gboolean track;
+  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;
+
+  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,
+                                           NULL,
+                                           &self->tangent);
+
+  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->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->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, 10);
+      gsk_path_builder_add_circle (builder, &p, 2.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
+demo_widget_set_path (DemoWidget *self,
+                      GskPath    *path)
+{
+  g_clear_pointer (&self->path, gsk_path_unref);
+  self->path = gsk_path_ref (path);
+
+  g_clear_pointer (&self->measure, gsk_path_measure_unref);
+  self->measure = gsk_path_measure_new (self->path);
+
+  gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+activate (GtkEntry *entry,
+          DemoWidget *demo)
+{
+  GskPath *path;
+
+  path = gsk_path_new_from_string (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, 100, 100, 100, 100);
+  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 (GtkToggleButton *button,
+               DemoWidget      *self)
+{
+  self->track = gtk_toggle_button_get_active (button);
+  gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+int
+main (int argc, char *argv[])
+{
+  GtkWidget *window, *box, *demo, *entry;
+  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);
+
+  header = gtk_header_bar_new ();
+  toggle = gtk_toggle_button_new ();
+  gtk_button_set_icon_name (GTK_BUTTON (toggle), "emblem-system-symbolic");
+  gtk_header_bar_pack_start (GTK_HEADER_BAR (header), toggle);
+  gtk_window_set_titlebar (GTK_WINDOW (window), header);
+
+  demo = demo_widget_new ();
+
+  g_signal_connect (toggle, "toggled", G_CALLBACK (track_toggled), demo);
+
+  gtk_widget_set_hexpand (demo, TRUE);
+  gtk_widget_set_vexpand (demo, TRUE);
+  gtk_box_append (GTK_BOX (box), demo);
+
+  entry = gtk_entry_new ();
+  g_signal_connect (entry, "activate", G_CALLBACK (activate), demo);
+
+  init_demo (DEMO_WIDGET (demo), GTK_EDITABLE (entry));
+
+  gtk_box_append (GTK_BOX (box), 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]