From f2e45bbc87311f2de8c055f4d11c67fa47bfccdd Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 14 May 2023 23:14:03 -0400 Subject: Quick visualization of rounded rects --- tests/meson.build | 7 ++ tests/testroundedrect.c | 312 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 319 insertions(+) create mode 100644 tests/testroundedrect.c diff --git a/tests/meson.build b/tests/meson.build index c2d08c26de..3807ddc615 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -146,6 +146,13 @@ foreach t: gtk_tests ) endforeach +executable('testroundedrect', + sources: [ 'testroundedrect.c' ], + include_directories: [confinc, gdkinc], + c_args: test_args + common_cflags, + dependencies: [libgtk_static_dep, libm], +) + if libsysprof_dep.found() executable('testperf', sources: 'testperf.c', diff --git a/tests/testroundedrect.c b/tests/testroundedrect.c new file mode 100644 index 0000000000..035bc40cd8 --- /dev/null +++ b/tests/testroundedrect.c @@ -0,0 +1,312 @@ +#include +#include +#include + +#define TEST_TYPE_WIDGET (test_widget_get_type ()) +G_DECLARE_FINAL_TYPE (TestWidget, test_widget, TEST, WIDGET, GtkWidget) + +struct _TestWidget +{ + GtkWidget parent_instance; + + GskRoundedRect rect1; + GskRoundedRect rect2; + GskRoundedRect rect3; + GskRoundedRectIntersection result; +}; + +struct _TestWidgetClass +{ + GtkWidgetClass parent_class; +}; + +G_DEFINE_TYPE (TestWidget, test_widget, GTK_TYPE_WIDGET) + +static void +test_widget_init (TestWidget *self) +{ +} + +static void +test_widget_measure (GtkWidget *widget, + GtkOrientation orientation, + int for_size, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline) +{ + TestWidget *self = TEST_WIDGET (widget); + + if (orientation == GTK_ORIENTATION_VERTICAL) + *minimum = *natural = MAX (self->rect1.bounds.origin.x + self->rect1.bounds.size.width, + self->rect2.bounds.origin.x + self->rect2.bounds.size.width); + else + *minimum = *natural = MAX (self->rect1.bounds.origin.y + self->rect1.bounds.size.height, + self->rect2.bounds.origin.y + self->rect2.bounds.size.height); +} + +static void +test_widget_snapshot (GtkWidget *widget, + GtkSnapshot *snapshot) +{ + TestWidget *self = TEST_WIDGET (widget); + float widths[4] = { 1, 1, 1, 1 }; + GdkRGBA colors1[4]; + GdkRGBA colors2[4]; + GdkRGBA colors3[4]; + GskRoundedRect rect3; + + gdk_rgba_parse (&colors1[0], "red"); + colors1[1] = colors1[2] = colors1[3] = colors1[0]; + + gdk_rgba_parse (&colors2[0], "blue"); + colors2[1] = colors2[2] = colors2[3] = colors2[0]; + + gdk_rgba_parse (&colors3[0], "magenta"); + colors3[1] = colors3[2] = colors3[3] = colors3[0]; + + gtk_snapshot_append_border (snapshot, &self->rect1, widths, colors1); + gtk_snapshot_append_border (snapshot, &self->rect2, widths, colors2); + + switch (gsk_rounded_rect_intersect (&self->rect1, &self->rect2, &rect3)) + { + case GSK_INTERSECTION_NONEMPTY: + gtk_snapshot_append_border (snapshot, &rect3, widths, colors3); + break; + default: + ; + } +} + + +static void +test_widget_class_init (TestWidgetClass *class) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); + + widget_class->snapshot = test_widget_snapshot; + widget_class->measure = test_widget_measure; +} + +static GtkWidget * +test_widget_new (void) +{ + return g_object_new (TEST_TYPE_WIDGET, NULL); +} + +static void +update_intersection (TestWidget *self) +{ + self->result = gsk_rounded_rect_intersect (&self->rect1, &self->rect2, &self->rect3); +} + +static void +test_widget_set_rect1 (TestWidget *self, + GskRoundedRect *rect) +{ + self->rect1 = *rect; + update_intersection (self); + gtk_widget_queue_resize (GTK_WIDGET (self)); +} + +static void +test_widget_set_rect2 (TestWidget *self, + GskRoundedRect *rect) +{ + self->rect2 = *rect; + update_intersection (self); + gtk_widget_queue_resize (GTK_WIDGET (self)); +} + +static gboolean +parse_rect (GtkCssParser *parser, + graphene_rect_t *out_rect) +{ + double numbers[4]; + + if (!gtk_css_parser_consume_number (parser, &numbers[0]) || + !gtk_css_parser_consume_number (parser, &numbers[1]) || + !gtk_css_parser_consume_number (parser, &numbers[2]) || + !gtk_css_parser_consume_number (parser, &numbers[3])) + return FALSE; + + graphene_rect_init (out_rect, numbers[0], numbers[1], numbers[2], numbers[3]); + + return TRUE; +} + +static gboolean +parse_rounded_rect (GtkCssParser *parser, + GskRoundedRect *out_rect) +{ + graphene_rect_t r; + graphene_size_t corners[4]; + double d; + guint i; + + if (!parse_rect (parser, &r)) + return FALSE; + + if (!gtk_css_parser_try_delim (parser, '/')) + { + gsk_rounded_rect_init_from_rect (out_rect, &r, 0); + return TRUE; + } + + for (i = 0; i < 4; i++) + { + if (!gtk_css_parser_has_number (parser)) + break; + if (!gtk_css_parser_consume_number (parser, &d)) + return FALSE; + corners[i].width = d; + } + + if (i == 0) + { + gtk_css_parser_error_syntax (parser, "Expected a number"); + return FALSE; + } + + /* The magic (i - 1) >> 1 below makes it take the correct value + * according to spec. Feel free to check the 4 cases + */ + for (; i < 4; i++) + corners[i].width = corners[(i - 1) >> 1].width; + if (gtk_css_parser_try_delim (parser, '/')) + { + gtk_css_parser_consume_token (parser); + + for (i = 0; i < 4; i++) + { + if (!gtk_css_parser_has_number (parser)) + break; + if (!gtk_css_parser_consume_number (parser, &d)) + return FALSE; + corners[i].height = d; + } + + if (i == 0) + { + gtk_css_parser_error_syntax (parser, "Expected a number"); + return FALSE; + } + + for (; i < 4; i++) + corners[i].height = corners[(i - 1) >> 1].height; + } + else + { + for (i = 0; i < 4; i++) + corners[i].height = corners[i].width; + } + + gsk_rounded_rect_init (out_rect, &r, &corners[0], &corners[1], &corners[2], &corners[3]); + + return TRUE; +} + +static GtkWidget *label; + +static void +update_label (GtkLabel *label, + GskRoundedRectIntersection result) +{ + const char *labels[] = { + "Empty", "Not empty", "Not representable", "Who knows" + }; + + gtk_label_set_label (label, labels[result]); +} + +static void +activate1_cb (GtkEntry *entry, TestWidget *test) +{ + GtkCssParser *parser; + const char *text; + GBytes *bytes; + GskRoundedRect rect; + + text = gtk_editable_get_text (GTK_EDITABLE (entry)); + bytes = g_bytes_new_static (text, strlen (text) + 1); + parser = gtk_css_parser_new_for_bytes (bytes, NULL, NULL, NULL, NULL); + + if (parse_rounded_rect (parser, &rect)) + { + test_widget_set_rect1 (test, &rect); + update_label (GTK_LABEL (label), test->result); + } + + gtk_css_parser_unref (parser); + g_bytes_unref (bytes); +} + +static void +activate2_cb (GtkEntry *entry, TestWidget *test) +{ + GtkCssParser *parser; + const char *text; + GBytes *bytes; + GskRoundedRect rect; + + text = gtk_editable_get_text (GTK_EDITABLE (entry)); + bytes = g_bytes_new_static (text, strlen (text) + 1); + parser = gtk_css_parser_new_for_bytes (bytes, NULL, NULL, NULL, NULL); + + if (parse_rounded_rect (parser, &rect)) + { + test_widget_set_rect2 (test, &rect); + update_label (GTK_LABEL (label), test->result); + } + + gtk_css_parser_unref (parser); + g_bytes_unref (bytes); +} + +int +main (int argc, char *argv[]) +{ + GtkWidget *window; + GtkWidget *box; + GtkWidget *grid; + GtkWidget *entry1; + GtkWidget *entry2; + GtkWidget *test; + + gtk_init (); + + window = gtk_window_new (); + gtk_window_set_default_size (GTK_WINDOW (window), 600, 400); + + box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_window_set_child (GTK_WINDOW (window), box); + + grid = gtk_grid_new (); + gtk_box_append (GTK_BOX (box), grid); + + test = test_widget_new (); + gtk_widget_set_hexpand (test, TRUE); + gtk_widget_set_vexpand (test, TRUE); + gtk_widget_set_halign (test, GTK_ALIGN_CENTER); + gtk_widget_set_valign (test, GTK_ALIGN_CENTER); + gtk_box_append (GTK_BOX (box), test); + + entry1 = gtk_entry_new (); + g_signal_connect (entry1, "activate", G_CALLBACK (activate1_cb), test); + gtk_grid_attach (GTK_GRID (grid), entry1, 0, 0, 1, 1); + + entry2 = gtk_entry_new (); + g_signal_connect (entry2, "activate", G_CALLBACK (activate2_cb), test); + gtk_grid_attach (GTK_GRID (grid), entry2, 0, 1, 1, 1); + + label = gtk_label_new (""); + gtk_grid_attach (GTK_GRID (grid), label, 0, 2, 1, 1); + + gtk_window_present (GTK_WINDOW (window)); + + while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0) + g_main_context_iteration (NULL, FALSE); + + return 0; +} -- cgit v1.2.1