blob: 035bc40cd801ebce35dcee15fa58e33e70905a3e [file] [edit]
#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;
}