[gtk/path-work-rebased: 114/123] Add an interactive path test
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/path-work-rebased: 114/123] Add an interactive path test
- Date: Thu, 7 Apr 2022 20:40:59 +0000 (UTC)
commit 5ae1cba5c503f9e37b766da9ca5021b327cef999
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.
There's also stroke parameters to play with.
tests/curve2.c | 883 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
tests/meson.build | 1 +
2 files changed, 884 insertions(+)
---
diff --git a/tests/curve2.c b/tests/curve2.c
new file mode 100644
index 0000000000..82bc0fd50e
--- /dev/null
+++ b/tests/curve2.c
@@ -0,0 +1,883 @@
+#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;
+ graphene_point_t point;
+ graphene_point_t point2;
+ graphene_vec2_t tangent;
+ double start, end;
+ float curvature;
+ graphene_point_t center;
+
+ gboolean track;
+ gboolean show_bounding_box;
+ gboolean show_points;
+ GtkWidget *label;
+
+ gboolean do_stroke;
+ gboolean do_offset;
+ GskPath *stroke_path;
+ GskPathMeasure *stroke_measure;
+ GskStroke *stroke;
+ GskPath *outline_path;
+ GskStroke *outline_stroke;
+ gboolean inside;
+ GskFillRule fill_rule;
+ gboolean do_fill;
+
+ graphene_rect_t bounds;
+ gboolean show_cairo;
+
+ double zoom;
+};
+
+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)
+{
+ if (self->track)
+ {
+ float distance;
+ char *text;
+ float t;
+
+ gsk_path_measure_get_closest_point_full (self->measure,
+ &GRAPHENE_POINT_INIT (x, y),
+ INFINITY,
+ &distance,
+ &self->point,
+ &t,
+ &self->tangent);
+
+ gsk_path_measure_get_point (self->measure, t, &self->point2, NULL);
+ self->curvature = gsk_path_measure_get_curvature (self->measure, t, &self->center);
+
+ text = g_strdup_printf ("%.1f", distance);
+ gtk_label_set_label (GTK_LABEL (self->label), text);
+
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+ }
+
+ if (self->do_stroke && self->do_fill)
+ {
+ gboolean inside = TRUE;
+
+ inside = gsk_path_measure_in_fill (self->stroke_measure, &GRAPHENE_POINT_INIT (x, y), self->fill_rule);
+
+ if (self->inside != inside)
+ {
+ self->inside = inside;
+ 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
+draw_point (GtkSnapshot *snapshot,
+ const graphene_point_t *pt)
+{
+ graphene_rect_t bounds;
+ GdkRGBA color = { 1, 0, 0, 1 };
+
+ bounds.origin.x = pt->x - 2;
+ bounds.origin.y = pt->y - 2;
+ bounds.size.width = 4;
+ bounds.size.height = 4;
+
+ gtk_snapshot_append_color (snapshot, &color, &bounds);
+}
+
+static gboolean
+point_cb (GskPathOperation op,
+ const graphene_point_t *pts,
+ gsize n_ts,
+ float weight,
+ gpointer user_data)
+{
+ GtkSnapshot *snapshot = user_data;
+
+ switch (op)
+ {
+ case GSK_PATH_MOVE:
+ draw_point (snapshot, &pts[0]);
+ break;
+
+ case GSK_PATH_CLOSE:
+ draw_point (snapshot, &pts[0]);
+ break;
+
+ case GSK_PATH_LINE:
+ draw_point (snapshot, &pts[1]);
+ break;
+
+ case GSK_PATH_CURVE:
+ draw_point (snapshot, &pts[3]);
+ break;
+
+ case GSK_PATH_CONIC:
+ default:
+ g_assert_not_reached ();
+ }
+
+ return TRUE;
+}
+
+static void
+demo_widget_snapshot (GtkWidget *widget,
+ GtkSnapshot *snapshot)
+{
+ DemoWidget *self = DEMO_WIDGET (widget);
+ int width, height;
+ GskStroke *stroke;
+ GskPathBuilder *builder;
+ GskPath *path;
+ graphene_rect_t bounds;
+
+ if (!self->path)
+ return;
+
+ gtk_snapshot_save (snapshot);
+
+ gtk_snapshot_scale (snapshot, self->zoom, self->zoom);
+
+ width = gtk_widget_get_width (widget);
+ height = gtk_widget_get_width (widget);
+
+ if (self->do_stroke)
+ {
+ if (self->show_cairo)
+ {
+ cairo_t *cr;
+
+ graphene_rect_init (&bounds, 0, 0, width, height);
+ cr = gtk_snapshot_append_cairo (snapshot, &bounds);
+ gsk_path_to_cairo (self->path, cr);
+ gsk_stroke_to_cairo (self->stroke, cr);
+ cairo_set_source_rgba (cr, 0, 0, 1, 0.2);
+ cairo_stroke (cr);
+ cairo_destroy (cr);
+ }
+
+ if (self->do_fill && self->inside)
+ {
+ gtk_snapshot_push_fill (snapshot, self->stroke_path, self->fill_rule);
+
+ gtk_snapshot_append_color (snapshot,
+ &(GdkRGBA){ 1, 0, 1, 0.3},
+ &GRAPHENE_RECT_INIT (0, 0, width, height ));
+
+ gtk_snapshot_pop (snapshot);
+ }
+
+ gtk_snapshot_push_stroke (snapshot, self->outline_path, self->outline_stroke);
+
+ gtk_snapshot_append_color (snapshot,
+ &(GdkRGBA){ 0, 0, 0, 0.2},
+ &GRAPHENE_RECT_INIT (0, 0, width, height ));
+
+ gtk_snapshot_pop (snapshot);
+
+ stroke = gsk_stroke_new (1);
+ gtk_snapshot_push_stroke (snapshot, self->path, stroke);
+ gsk_stroke_free (stroke);
+
+ gtk_snapshot_append_color (snapshot,
+ &(GdkRGBA){ 0, 0, 0, 0.3},
+ &GRAPHENE_RECT_INIT (0, 0, width, height ));
+
+ gtk_snapshot_pop (snapshot);
+ }
+ else if (self->do_offset)
+ {
+ for (int i = -3; i < 4; i++)
+ {
+ path = gsk_path_offset (self->path,
+ 5 * i,
+ gsk_stroke_get_line_join (self->stroke),
+ gsk_stroke_get_miter_limit (self->stroke));
+
+ gtk_snapshot_push_stroke (snapshot, path, self->outline_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);
+ }
+ }
+ else
+ {
+ gtk_snapshot_push_stroke (snapshot, self->path, self->outline_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_points)
+ {
+ gsk_path_foreach (self->path, GSK_PATH_FOREACH_ALLOW_CURVE, point_cb, snapshot);
+ }
+
+ if (self->show_bounding_box)
+ {
+ if (gsk_path_get_bounds (self->do_stroke ? self->outline_path : 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)
+ {
+ graphene_point_t p;
+
+ 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);
+
+ if (self->curvature != 0)
+ {
+ builder = gsk_path_builder_new ();
+ gsk_path_builder_add_circle (builder, &self->center, fabs (1/self->curvature));
+ gsk_path_builder_add_circle (builder, &self->center, 3);
+
+ 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){ 1, 0, 1, 1},
+ &GRAPHENE_RECT_INIT (0, 0, width, height ));
+
+ gtk_snapshot_pop (snapshot);
+ }
+
+ gtk_widget_snapshot_child (widget, self->label, snapshot);
+ }
+
+ gtk_snapshot_restore (snapshot);
+}
+
+static void
+demo_widget_measure (GtkWidget *widget,
+ GtkOrientation orientation,
+ int for_size,
+ int *minimum,
+ int *natural,
+ int *minimum_baseline,
+ int *natural_baseline)
+{
+ DemoWidget *self = DEMO_WIDGET (widget);
+ float size;
+
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ size = self->zoom * (self->bounds.origin.x + self->bounds.size.width);
+ else
+ size = self->zoom * (self->bounds.origin.y + self->bounds.size.height);
+
+ *minimum = *natural = (int)size;
+}
+
+static void
+demo_widget_size_allocate (GtkWidget *widget,
+ int width,
+ int height,
+ int baseline)
+{
+ DemoWidget *self = DEMO_WIDGET (widget);
+ GtkRequisition min, nat;
+
+ gtk_widget_get_preferred_size (self->label, &min, &nat);
+
+ gtk_widget_size_allocate (self->label,
+ &(GtkAllocation) { width - nat.width, 0, nat.width, nat.height},
+ -1);
+}
+
+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->stroke_path, gsk_path_unref);
+ g_clear_pointer (&self->stroke_measure, gsk_path_measure_unref);
+ g_clear_pointer (&self->stroke, gsk_stroke_free);
+ g_clear_pointer (&self->outline_path, gsk_path_unref);
+ g_clear_pointer (&self->outline_stroke, gsk_stroke_free);
+ 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;
+ widget_class->measure = demo_widget_measure;
+ widget_class->size_allocate = demo_widget_size_allocate;
+}
+
+static GtkWidget *
+demo_widget_new (void)
+{
+ return g_object_new (DEMO_TYPE_WIDGET, NULL);
+}
+
+static void
+update_outline_path (DemoWidget *self)
+{
+ if (self->stroke_path)
+ {
+ g_clear_pointer (&self->outline_path, gsk_path_unref);
+ self->outline_path = gsk_path_ref (self->stroke_path);
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+ }
+}
+
+static void
+update_stroke_path (DemoWidget *self)
+{
+ g_clear_pointer (&self->stroke_path, gsk_path_unref);
+ g_clear_pointer (&self->stroke_measure, gsk_path_measure_unref);
+
+ if (self->do_stroke)
+ {
+ self->stroke_path = gsk_path_stroke (self->path, self->stroke);
+ self->stroke_measure = gsk_path_measure_new (self->stroke_path);
+ update_outline_path (self);
+ }
+
+ gsk_path_get_bounds (self->orig_path, &self->bounds);
+
+ if (self->stroke_path)
+ {
+ graphene_rect_t b;
+ gsk_path_get_bounds (self->stroke_path, &b);
+ graphene_rect_union (&b, &self->bounds, &self->bounds);
+ }
+
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+}
+
+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_builder_add_segment (builder, measure, 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);
+
+ update_stroke_path (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);
+ gsk_path_builder_line_to (builder, 600, 150);
+ gsk_path_builder_line_to (builder, 530, 190);
+ 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);
+
+ demo->stroke = gsk_stroke_new (20);
+ demo->outline_stroke = gsk_stroke_new (1);
+
+ demo->zoom = 1;
+}
+
+static void
+zoom_changed (GtkRange *range,
+ DemoWidget *self)
+{
+ self->zoom = gtk_range_get_value (range);
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+}
+
+static void
+points_toggled (GtkCheckButton *button,
+ DemoWidget *self)
+{
+ self->show_points = gtk_check_button_get_active (button);
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+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 void
+cairo_toggled (GtkCheckButton *button,
+ DemoWidget *self)
+{
+ self->show_cairo = gtk_check_button_get_active (button);
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+stroke_toggled (GtkCheckButton *button,
+ DemoWidget *self)
+{
+ self->do_stroke = gtk_check_button_get_active (button);
+ update_stroke_path (self);
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+offset_toggled (GtkCheckButton *button,
+ DemoWidget *self)
+{
+ self->do_offset = gtk_check_button_get_active (button);
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+fill_toggled (GtkCheckButton *button,
+ DemoWidget *self)
+{
+ self->do_fill = 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);
+}
+
+static void
+fill_rule_changed (GtkDropDown *combo,
+ GParamSpec *pspec,
+ DemoWidget *self)
+{
+ self->fill_rule = (GskFillRule)gtk_drop_down_get_selected (combo);
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+cap_changed (GtkDropDown *combo,
+ GParamSpec *pspec,
+ DemoWidget *self)
+{
+ gsk_stroke_set_line_cap (self->stroke, (GskLineCap)gtk_drop_down_get_selected (combo));
+ update_stroke_path (self);
+}
+
+static void
+join_changed (GtkDropDown *combo,
+ GParamSpec *pspec,
+ DemoWidget *self)
+{
+ gsk_stroke_set_line_join (self->stroke, (GskLineJoin)gtk_drop_down_get_selected (combo));
+ update_stroke_path (self);
+}
+
+static void
+limit_changed (GtkSpinButton *spin,
+ DemoWidget *self)
+{
+ gsk_stroke_set_miter_limit (self->stroke, gtk_spin_button_get_value (spin));
+ update_stroke_path (self);
+}
+
+static void
+stroke_width_changed (GtkSpinButton *spin,
+ DemoWidget *self)
+{
+ gsk_stroke_set_line_width (self->stroke, gtk_spin_button_get_value (spin));
+ update_stroke_path (self);
+}
+
+static void
+line_width_changed (GtkSpinButton *spin,
+ DemoWidget *self)
+{
+ gsk_stroke_set_line_width (self->outline_stroke, gtk_spin_button_get_value (spin));
+ update_outline_path (self);
+}
+
+static void
+dash_changed (GtkEntry *entry,
+ DemoWidget *self)
+{
+ const char *text;
+ char **s;
+ float dash[20];
+ int i;
+
+ text = gtk_editable_get_text (GTK_EDITABLE (entry));
+ s = g_strsplit (text, " ", -1);
+ for (i = 0; s[i]; i++)
+ {
+ char *end;
+ float f;
+
+ f = g_strtod (s[i], &end);
+ if (*end != '\0')
+ return;
+
+ dash[i] = f;
+ }
+
+ g_strfreev (s);
+
+ gsk_stroke_set_dash (self->stroke, dash, i);
+
+ update_stroke_path (self);
+}
+
+static void
+dash_offset_changed (GtkSpinButton *spin,
+ DemoWidget *self)
+{
+ gsk_stroke_set_dash_offset (self->stroke, gtk_spin_button_get_value (spin));
+ update_stroke_path (self);
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *window, *box, *demo, *entry;
+ GtkWidget *popover, *button, *grid;
+ GtkWidget *header, *toggle, *toggle2, *toggle3;
+ GtkWidget *combo, *spin, *sw, *hbox;
+ GtkWidget *zoom_scale;
+ int row;
+
+ gtk_init ();
+
+ window = gtk_window_new ();
+ gtk_window_set_default_size (GTK_WINDOW (window), 700, 500);
+ 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);
+ sw = gtk_scrolled_window_new ();
+ gtk_box_append (GTK_BOX (box), sw);
+ gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), 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);
+
+ zoom_scale = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 1, 20, 0.25);
+ gtk_range_set_value (GTK_RANGE (zoom_scale), 1);
+ g_signal_connect (zoom_scale, "value-changed", G_CALLBACK (zoom_changed), demo);
+ gtk_widget_set_size_request (zoom_scale, 150, -1);
+ gtk_header_bar_pack_end (GTK_HEADER_BAR (header), zoom_scale);
+
+ 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);
+
+ row = 0;
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ toggle = gtk_check_button_new_with_label ("Plain");
+ gtk_check_button_set_active (GTK_CHECK_BUTTON (toggle), TRUE);
+ gtk_box_append (GTK_BOX (hbox), toggle);
+ gtk_widget_set_hexpand (toggle, TRUE);
+ toggle2 = gtk_check_button_new_with_label ("Stroke");
+ gtk_widget_set_hexpand (toggle2, TRUE);
+ gtk_box_append (GTK_BOX (hbox), toggle2);
+ gtk_check_button_set_group (GTK_CHECK_BUTTON (toggle2), GTK_CHECK_BUTTON (toggle));
+ g_signal_connect (toggle2, "toggled", G_CALLBACK (stroke_toggled), demo);
+ toggle3 = gtk_check_button_new_with_label ("Offset");
+ gtk_widget_set_hexpand (toggle3, TRUE);
+ gtk_box_append (GTK_BOX (hbox), toggle3);
+ gtk_check_button_set_group (GTK_CHECK_BUTTON (toggle3), GTK_CHECK_BUTTON (toggle));
+ g_signal_connect (toggle3, "toggled", G_CALLBACK (offset_toggled), demo);
+ gtk_grid_attach (GTK_GRID (grid), hbox, 0, row++, 2, 1);
+
+ gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Fill rule"), 0, row, 1, 1);
+
+ combo = gtk_drop_down_new_from_strings ((const char *[]){"Winding", "Even-Odd", NULL });
+ g_signal_connect (combo, "notify::selected", G_CALLBACK (fill_rule_changed), demo);
+ gtk_grid_attach (GTK_GRID (grid), combo, 1, row++, 1, 1);
+
+ gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Line cap:"), 0, row, 1, 1);
+ combo = gtk_drop_down_new_from_strings ((const char *[]){"Butt", "Round", "Square", NULL});
+ g_signal_connect (combo, "notify::selected", G_CALLBACK (cap_changed), demo);
+ gtk_grid_attach (GTK_GRID (grid), combo, 1, row++, 1, 1);
+
+ gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Line join:"), 0, row, 1, 1);
+ combo = gtk_drop_down_new_from_strings ((const char *[]){"Miter", "Miter-clip", "Round", "Bevel", "Arcs",
NULL});
+ g_signal_connect (combo, "notify::selected", G_CALLBACK (join_changed), demo);
+ gtk_grid_attach (GTK_GRID (grid), combo, 1, row++, 1, 1);
+
+ gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Miter limit:"), 0, row, 1, 1);
+ spin = gtk_spin_button_new_with_range (0, 10, 1);
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (spin), 1);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), 4);
+ g_signal_connect (spin, "value-changed", G_CALLBACK (limit_changed), demo);
+ gtk_grid_attach (GTK_GRID (grid), spin, 1, row++, 1, 1);
+
+ gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Stroke width:"), 0, row, 1, 1);
+ spin = gtk_spin_button_new_with_range (1, 40, 1);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), 20);
+ g_signal_connect (spin, "value-changed", G_CALLBACK (stroke_width_changed), demo);
+ gtk_grid_attach (GTK_GRID (grid), spin, 1, row++, 1, 1);
+
+ gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Line width:"), 0, row, 1, 1);
+ spin = gtk_spin_button_new_with_range (1, 20, 1);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), 1);
+ g_signal_connect (spin, "value-changed", G_CALLBACK (line_width_changed), demo);
+ gtk_grid_attach (GTK_GRID (grid), spin, 1, row++, 1, 1);
+
+ gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Dash pattern:"), 0, row, 1, 1);
+ entry = gtk_entry_new ();
+ g_signal_connect (entry, "changed", G_CALLBACK (dash_changed), demo);
+ gtk_grid_attach (GTK_GRID (grid), entry, 1, row++, 1, 1);
+
+ gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Dash offset:"), 0, row, 1, 1);
+ spin = gtk_spin_button_new_with_range (-1000, 1000, 10);
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (spin), 1);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), 0);
+ g_signal_connect (spin, "value-changed", G_CALLBACK (dash_offset_changed), demo);
+ gtk_grid_attach (GTK_GRID (grid), spin, 1, row++, 1, 1);
+
+ toggle = gtk_check_button_new_with_label ("Show points");
+ g_signal_connect (toggle, "toggled", G_CALLBACK (points_toggled), demo);
+ gtk_grid_attach (GTK_GRID (grid), toggle, 1, row++, 1, 1);
+
+ 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, 1, row++, 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, 1, row++, 1, 1);
+
+ toggle = gtk_check_button_new_with_label ("Highlight on hover");
+ g_signal_connect (toggle, "toggled", G_CALLBACK (fill_toggled), demo);
+ gtk_grid_attach (GTK_GRID (grid), toggle, 1, row++, 1, 1);
+
+ toggle = gtk_check_button_new_with_label ("Compare cairo stroke");
+ g_signal_connect (toggle, "toggled", G_CALLBACK (cairo_toggled), demo);
+ gtk_grid_attach (GTK_GRID (grid), toggle, 1, row++, 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.01);
+ 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.01);
+ 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 d837b5af6d..3264b90bbb 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -1,5 +1,6 @@
gtk_tests = [
# testname, optional extra sources
+ ['curve2'],
['input'],
['testpopup'],
['testupload'],
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]