Compare commits
2 Commits
gtk-gtk-co
...
rounded-re
Author | SHA1 | Date | |
---|---|---|---|
|
f2e45bbc87 | ||
|
9965cfbc3b |
@@ -689,6 +689,230 @@ gsk_rounded_rect_intersect_with_rect (const GskRoundedRect *self,
|
||||
return GSK_INTERSECTION_NONEMPTY;
|
||||
}
|
||||
|
||||
static inline void
|
||||
rect_corner (const graphene_rect_t *r,
|
||||
unsigned int i,
|
||||
graphene_point_t *p)
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
case GSK_CORNER_TOP_LEFT:
|
||||
graphene_rect_get_top_left (r, p);
|
||||
break;
|
||||
case GSK_CORNER_TOP_RIGHT:
|
||||
graphene_rect_get_top_right (r, p);
|
||||
break;
|
||||
case GSK_CORNER_BOTTOM_RIGHT:
|
||||
graphene_rect_get_bottom_right (r, p);
|
||||
break;
|
||||
case GSK_CORNER_BOTTOM_LEFT:
|
||||
graphene_rect_get_bottom_left (r, p);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
corner_rect (const GskRoundedRect *s,
|
||||
unsigned int i,
|
||||
graphene_rect_t *r)
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
case GSK_CORNER_TOP_LEFT:
|
||||
graphene_rect_init (r,
|
||||
s->bounds.origin.x,
|
||||
s->bounds.origin.y,
|
||||
s->corner[i].width,
|
||||
s->corner[i].height);
|
||||
break;
|
||||
case GSK_CORNER_TOP_RIGHT:
|
||||
graphene_rect_init (r,
|
||||
s->bounds.origin.x + s->bounds.size.width - s->corner[i].width,
|
||||
s->bounds.origin.y,
|
||||
s->corner[i].width,
|
||||
s->corner[i].height);
|
||||
break;
|
||||
case GSK_CORNER_BOTTOM_RIGHT:
|
||||
graphene_rect_init (r,
|
||||
s->bounds.origin.x + s->bounds.size.width - s->corner[i].width,
|
||||
s->bounds.origin.y + s->bounds.size.height - s->corner[i].height,
|
||||
s->corner[i].width,
|
||||
s->corner[i].height);
|
||||
break;
|
||||
case GSK_CORNER_BOTTOM_LEFT:
|
||||
graphene_rect_init (r,
|
||||
s->bounds.origin.x,
|
||||
s->bounds.origin.y + s->bounds.size.height - s->corner[i].height,
|
||||
s->corner[i].width,
|
||||
s->corner[i].height);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
point_in_interior (const graphene_point_t *p,
|
||||
const graphene_rect_t *r)
|
||||
{
|
||||
if (graphene_rect_contains_point (r, p))
|
||||
{
|
||||
if (p->x > r->origin.x && p->x < r->origin.x + r->size.width)
|
||||
return TRUE;
|
||||
|
||||
if (p->y > r->origin.y && p->y < r->origin.y + r->size.height)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GskRoundedRectIntersection
|
||||
gsk_rounded_rect_intersect (const GskRoundedRect *self,
|
||||
const GskRoundedRect *other,
|
||||
GskRoundedRect *result)
|
||||
{
|
||||
if (!graphene_rect_intersection (&self->bounds, &other->bounds, &result->bounds))
|
||||
return GSK_INTERSECTION_EMPTY;
|
||||
|
||||
for (unsigned int i = 0; i < 4; i++)
|
||||
{
|
||||
graphene_point_t p, p1, p2;
|
||||
|
||||
rect_corner (&self->bounds, i, &p1);
|
||||
rect_corner (&other->bounds, i, &p2);
|
||||
rect_corner (&result->bounds, i, &p);
|
||||
|
||||
if (graphene_point_equal (&p, &p1))
|
||||
{
|
||||
if (graphene_point_equal (&p, &p2))
|
||||
{
|
||||
graphene_rect_t c;
|
||||
graphene_rect_t d;
|
||||
|
||||
corner_rect (self, i, &c);
|
||||
corner_rect (other, i, &d);
|
||||
|
||||
/* corners coincide */
|
||||
if (graphene_rect_contains_rect (&c, &d))
|
||||
{
|
||||
graphene_point_t q1, q2;
|
||||
|
||||
rect_corner (&c, (i + 1) % 4, &q1);
|
||||
rect_corner (&c, (i + 3) % 4, &q2);
|
||||
|
||||
if (gsk_rounded_rect_contains_point (other, &q1) &&
|
||||
gsk_rounded_rect_contains_point (other, &q2))
|
||||
result->corner[i] = self->corner[i];
|
||||
else
|
||||
return GSK_INTERSECTION_NOT_REPRESENTABLE;
|
||||
}
|
||||
else if (graphene_rect_contains_rect (&d, &c))
|
||||
{
|
||||
graphene_point_t q1, q2;
|
||||
|
||||
rect_corner (&d, (i + 1) % 4, &q1);
|
||||
rect_corner (&d, (i + 3) % 4, &q2);
|
||||
|
||||
if (gsk_rounded_rect_contains_point (self, &q1) &&
|
||||
gsk_rounded_rect_contains_point (self, &q2))
|
||||
result->corner[i] = other->corner[i];
|
||||
else
|
||||
return GSK_INTERSECTION_NOT_REPRESENTABLE;
|
||||
}
|
||||
else
|
||||
return GSK_INTERSECTION_NOT_REPRESENTABLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
graphene_rect_t c;
|
||||
graphene_point_t q1, q2;
|
||||
|
||||
corner_rect (self, i, &c);
|
||||
|
||||
rect_corner (&c, (i + 1) % 4, &q1);
|
||||
rect_corner (&c, (i + 3) % 4, &q2);
|
||||
|
||||
if (gsk_rounded_rect_contains_point (other, &q1) &&
|
||||
gsk_rounded_rect_contains_point (other, &q2))
|
||||
{
|
||||
if (gsk_rounded_rect_contains_point (other, &p))
|
||||
result->corner[i] = self->corner[i];
|
||||
else
|
||||
#if 1
|
||||
return GSK_INTERSECTION_NEEDS_QUARTIC;
|
||||
#else
|
||||
if (/* no intersection for i */)
|
||||
result->corner[i] = self->corner[i];
|
||||
else
|
||||
return GSK_INTERSECTION_NOT_REPRESENTABLE;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
return GSK_INTERSECTION_NOT_REPRESENTABLE;
|
||||
}
|
||||
}
|
||||
else if (graphene_point_equal (&p, &p2))
|
||||
{
|
||||
graphene_rect_t d;
|
||||
graphene_point_t q1, q2;
|
||||
|
||||
corner_rect (other, i, &d);
|
||||
|
||||
rect_corner (&d, (i + 1) % 4, &q1);
|
||||
rect_corner (&d, (i + 3) % 4, &q2);
|
||||
|
||||
if (gsk_rounded_rect_contains_point (self, &q1) &&
|
||||
gsk_rounded_rect_contains_point (self, &q2))
|
||||
{
|
||||
if (gsk_rounded_rect_contains_point (self, &p))
|
||||
result->corner[i] = other->corner[i];
|
||||
else
|
||||
#if 1
|
||||
return GSK_INTERSECTION_NEEDS_QUARTIC;
|
||||
#else
|
||||
if (/* no intersection for i */
|
||||
result->corner[i] = other->corner[i];
|
||||
else
|
||||
return GSK_INTERSECTION_NOT_REPRESENTABLE;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
return GSK_INTERSECTION_NOT_REPRESENTABLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
graphene_rect_t c, d;
|
||||
|
||||
corner_rect (self, (i + 2) % 4, &c);
|
||||
if (graphene_rect_contains_point (&c, &p) &&
|
||||
!gsk_rounded_rect_contains_point (self, &p))
|
||||
return GSK_INTERSECTION_EMPTY;
|
||||
|
||||
corner_rect (other, (i + 2) % 4, &d);
|
||||
if (graphene_rect_contains_point (&d, &p) &&
|
||||
!gsk_rounded_rect_contains_point (other, &p))
|
||||
return GSK_INTERSECTION_EMPTY;
|
||||
|
||||
for (unsigned int j = 0; j < 4; j++)
|
||||
{
|
||||
corner_rect (self, j, &c);
|
||||
corner_rect (other, j, &d);
|
||||
|
||||
if (point_in_interior (&p, &c) ||
|
||||
point_in_interior (&p, &d))
|
||||
return GSK_INTERSECTION_NOT_REPRESENTABLE;
|
||||
}
|
||||
|
||||
result->corner[i] = (graphene_size_t) { 0, 0 };
|
||||
}
|
||||
}
|
||||
|
||||
return GSK_INTERSECTION_NONEMPTY;
|
||||
}
|
||||
|
||||
static void
|
||||
append_arc (cairo_t *cr, double angle1, double angle2, gboolean negative)
|
||||
{
|
||||
|
@@ -37,13 +37,17 @@ char * gsk_rounded_rect_to_string (const GskRounde
|
||||
typedef enum {
|
||||
GSK_INTERSECTION_EMPTY,
|
||||
GSK_INTERSECTION_NONEMPTY,
|
||||
GSK_INTERSECTION_NOT_REPRESENTABLE
|
||||
GSK_INTERSECTION_NOT_REPRESENTABLE,
|
||||
GSK_INTERSECTION_NEEDS_QUARTIC
|
||||
} GskRoundedRectIntersection;
|
||||
|
||||
GskRoundedRectIntersection gsk_rounded_rect_intersect_with_rect (const GskRoundedRect *self,
|
||||
const graphene_rect_t *rect,
|
||||
GskRoundedRect *result) G_GNUC_PURE;
|
||||
|
||||
GskRoundedRectIntersection gsk_rounded_rect_intersect (const GskRoundedRect *self,
|
||||
const GskRoundedRect *other,
|
||||
GskRoundedRect *result) G_GNUC_PURE;
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
@@ -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',
|
||||
|
312
tests/testroundedrect.c
Normal file
312
tests/testroundedrect.c
Normal file
@@ -0,0 +1,312 @@
|
||||
#include <gtk/gtk.h>
|
||||
#include <gtk/css/gtkcssparserprivate.h>
|
||||
#include <gsk/gskroundedrectprivate.h>
|
||||
|
||||
#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;
|
||||
}
|
Reference in New Issue
Block a user