diff --git a/tests/curve2.c b/tests/curve2.c new file mode 100644 index 0000000000..4e7981f291 --- /dev/null +++ b/tests/curve2.c @@ -0,0 +1,912 @@ +#include + +#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_QUAD: + draw_point (snapshot, &pts[2]); + break; + + case GSK_PATH_CUBIC: + 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_QUAD|GSK_PATH_FOREACH_ALLOW_CUBIC, 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, + const char *file) +{ + GskPath *path = NULL; + char *string; + + if (file) + { + char *buffer; + gsize size; + GError *error = NULL; + + if (!g_file_get_contents (file, &buffer, &size, &error)) + { + g_print ("%s\n", error->message); + g_error_free (error); + } + else + { + buffer = g_strstrip (buffer); + path = gsk_path_parse (buffer); + if (path == NULL) + g_print ("Failed to parse path.\n'%s'", buffer); + g_free (buffer); + } + } + + if (path == NULL) + { + GskPathBuilder *builder; + 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_cubic_to (builder, 300, 50, 400, 50, 400, 150); + gsk_path_builder_cubic_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, 0.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), argc > 1 ? argv[1] : NULL); + + 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 3552c21990..3260dd4c4e 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,5 +1,6 @@ gtk_tests = [ # testname, optional extra sources + ['curve2'], ['testsections'], ['testfilelauncher'], ['input'],