diff options
-rw-r--r-- | docs/reference/gsk/gsk4-sections.txt | 1 | ||||
-rw-r--r-- | gsk/gskenums.h | 2 | ||||
-rw-r--r-- | gsk/gskrendernode.h | 5 | ||||
-rw-r--r-- | gsk/gskrendernodeimpl.c | 186 | ||||
-rw-r--r-- | gsk/gskrendernodeprivate.h | 4 | ||||
-rw-r--r-- | gtk/gtkrenderborder.c | 209 | ||||
-rw-r--r-- | gtk/gtkrenderborderprivate.h | 4 | ||||
-rw-r--r-- | gtk/inspector/gtktreemodelrendernode.c | 1 |
8 files changed, 389 insertions, 23 deletions
diff --git a/docs/reference/gsk/gsk4-sections.txt b/docs/reference/gsk/gsk4-sections.txt index 3b47dab517..023abe5d56 100644 --- a/docs/reference/gsk/gsk4-sections.txt +++ b/docs/reference/gsk/gsk4-sections.txt @@ -35,6 +35,7 @@ gsk_render_node_set_name gsk_color_node_new gsk_linear_gradient_node_new gsk_repeating_linear_gradient_node_new +gsk_border_node_new gsk_texture_node_new gsk_cairo_node_new gsk_cairo_node_get_draw_context diff --git a/gsk/gskenums.h b/gsk/gskenums.h index 1d46c9230e..7bad39d1c2 100644 --- a/gsk/gskenums.h +++ b/gsk/gskenums.h @@ -31,6 +31,7 @@ * @GSK_LINEAR_GRADIENT_NODE: A node drawing a linear gradient * @GSK_REPEATING_LINEAR_GRADIENT_NODE: A node drawing a repeating * linear gradient + * @GSK_BORDER_NODE: A node stroking a border around an area * @GSK_TEXTURE_NODE: A node drawing a #GskTexture * @GSK_TRANSFORM_NODE: A node that renders its child after applying a * matrix transform @@ -51,6 +52,7 @@ typedef enum { GSK_COLOR_NODE, GSK_LINEAR_GRADIENT_NODE, GSK_REPEATING_LINEAR_GRADIENT_NODE, + GSK_BORDER_NODE, GSK_TEXTURE_NODE, GSK_TRANSFORM_NODE, GSK_OPACITY_NODE, diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h index 14fa797db9..a349b8b302 100644 --- a/gsk/gskrendernode.h +++ b/gsk/gskrendernode.h @@ -74,6 +74,11 @@ GskRenderNode * gsk_repeating_linear_gradient_node_new (const graphene_ gsize n_color_stops); GDK_AVAILABLE_IN_3_90 +GskRenderNode * gsk_border_node_new (const GskRoundedRect *outline, + const float border_width[4], + const GdkRGBA border_color[4]); + +GDK_AVAILABLE_IN_3_90 GskRenderNode * gsk_cairo_node_new (const graphene_rect_t *bounds); GDK_AVAILABLE_IN_3_90 cairo_t * gsk_cairo_node_get_draw_context (GskRenderNode *node, diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c index ee3cb46015..c5d8d615d3 100644 --- a/gsk/gskrendernodeimpl.c +++ b/gsk/gskrendernodeimpl.c @@ -271,6 +271,192 @@ gsk_repeating_linear_gradient_node_new (const graphene_rect_t *bounds, return &self->render_node; } +/*** GSK_BORDER_NODE ***/ + +typedef struct _GskBorderNode GskBorderNode; + +struct _GskBorderNode +{ + GskRenderNode render_node; + + GskRoundedRect outline; + float border_width[4]; + GdkRGBA border_color[4]; +}; + +static void +gsk_border_node_finalize (GskRenderNode *node) +{ +} + +static void +gsk_border_node_make_immutable (GskRenderNode *node) +{ +} + +static void +gsk_border_node_draw (GskRenderNode *node, + cairo_t *cr) +{ + GskBorderNode *self = (GskBorderNode *) node; + GskRoundedRect inside; + + cairo_save (cr); + + gsk_rounded_rect_init_copy (&inside, &self->outline); + gsk_rounded_rect_shrink (&inside, + self->border_width[0], self->border_width[1], + self->border_width[2], self->border_width[3]); + + cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD); + gsk_rounded_rect_path (&self->outline, cr); + gsk_rounded_rect_path (&inside, cr); + + if (gdk_rgba_equal (&self->border_color[0], &self->border_color[1]) && + gdk_rgba_equal (&self->border_color[0], &self->border_color[2]) && + gdk_rgba_equal (&self->border_color[0], &self->border_color[3])) + { + gdk_cairo_set_source_rgba (cr, &self->border_color[0]); + cairo_fill (cr); + } + else + { + const graphene_rect_t *bounds = &self->outline.bounds; + /* distance to center "line": + * +-------------------------+ + * | | + * | | + * | ---this-line--- | + * | | + * | | + * +-------------------------+ + * That line is equidistant from all sides. It's either horiontal + * or vertical, depending on if the rect is wider or taller. + * We use the 4 sides spanned up by connecting the line to the corner + * points to color the regions of the rectangle differently. + * Note that the call to cairo_fill() will add the potential final + * segment by closing the path, so we don't have to care. + */ + float dst = MIN (bounds->size.width, bounds->size.height) / 2.0; + + cairo_clip (cr); + + /* top */ + cairo_move_to (cr, bounds->origin.x + dst, bounds->origin.y + dst); + cairo_rel_line_to (cr, - dst, - dst); + cairo_rel_line_to (cr, bounds->size.width, 0); + cairo_rel_line_to (cr, - dst, dst); + gdk_cairo_set_source_rgba (cr, &self->border_color[0]); + cairo_fill (cr); + + /* right */ + cairo_move_to (cr, bounds->origin.x + bounds->size.width - dst, bounds->origin.y + dst); + cairo_rel_line_to (cr, dst, - dst); + cairo_rel_line_to (cr, 0, bounds->size.height); + cairo_rel_line_to (cr, - dst, - dst); + gdk_cairo_set_source_rgba (cr, &self->border_color[1]); + cairo_fill (cr); + + /* bottom */ + cairo_move_to (cr, bounds->origin.x + bounds->size.width - dst, bounds->origin.y + bounds->size.height - dst); + cairo_rel_line_to (cr, dst, dst); + cairo_rel_line_to (cr, - bounds->size.width, 0); + cairo_rel_line_to (cr, dst, - dst); + gdk_cairo_set_source_rgba (cr, &self->border_color[2]); + cairo_fill (cr); + + /* left */ + cairo_move_to (cr, bounds->origin.x + dst, bounds->origin.y + bounds->size.height - dst); + cairo_rel_line_to (cr, - dst, dst); + cairo_rel_line_to (cr, 0, - bounds->size.height); + cairo_rel_line_to (cr, dst, dst); + gdk_cairo_set_source_rgba (cr, &self->border_color[3]); + cairo_fill (cr); + } + + cairo_restore (cr); +} + +static void +gsk_border_node_get_bounds (GskRenderNode *node, + graphene_rect_t *bounds) +{ + GskBorderNode *self = (GskBorderNode *) node; + + graphene_rect_init_from_rect (bounds, &self->outline.bounds); +} + +static const GskRenderNodeClass GSK_BORDER_NODE_CLASS = { + GSK_BORDER_NODE, + sizeof (GskBorderNode), + "GskBorderNode", + gsk_border_node_finalize, + gsk_border_node_make_immutable, + gsk_border_node_draw, + gsk_border_node_get_bounds +}; + +const GskRoundedRect * +gsk_border_node_peek_outline (GskRenderNode *node) +{ + GskBorderNode *self = (GskBorderNode *) node; + + return &self->outline; +} + +float +gsk_border_node_get_width (GskRenderNode *node, + guint i) +{ + GskBorderNode *self = (GskBorderNode *) node; + + return self->border_width[i]; +} + +const GdkRGBA * +gsk_border_node_peek_color (GskRenderNode *node, + guint i) +{ + GskBorderNode *self = (GskBorderNode *) node; + + return &self->border_color[i]; +} + +/** + * gsk_border_node_new: + * @outline: a #GskRoundedRect describing the outline of the border + * @border_width: the stroke width of the border on the top, right, bottom and + * left side respectively. + * @border_color: the color used on the top, right, bottom and left side. + * + * Creates a #GskRenderNode that will stroke a border rectangle inside the + * given @outline. The 4 sides of the border can have different widths and + * colors. + * + * Returns: A new #GskRenderNode + * + * Since: 3.90 + */ +GskRenderNode * +gsk_border_node_new (const GskRoundedRect *outline, + const float border_width[4], + const GdkRGBA border_color[4]) +{ + GskBorderNode *self; + + g_return_val_if_fail (outline != NULL, NULL); + g_return_val_if_fail (border_width != NULL, NULL); + g_return_val_if_fail (border_color != NULL, NULL); + + self = (GskBorderNode *) gsk_render_node_new (&GSK_BORDER_NODE_CLASS); + + gsk_rounded_rect_init_copy (&self->outline, outline); + memcpy (self->border_width, border_width, sizeof (self->border_width)); + memcpy (self->border_color, border_color, sizeof (self->border_color)); + + return &self->render_node; +} + /*** GSK_TEXTURE_NODE ***/ typedef struct _GskTextureNode GskTextureNode; diff --git a/gsk/gskrendernodeprivate.h b/gsk/gskrendernodeprivate.h index e945ee7b2c..24e87e8880 100644 --- a/gsk/gskrendernodeprivate.h +++ b/gsk/gskrendernodeprivate.h @@ -48,6 +48,10 @@ void gsk_render_node_get_bounds (GskRenderNode *node, graphene_rect_t *frame); double gsk_opacity_node_get_opacity (GskRenderNode *node); +const GskRoundedRect * gsk_border_node_peek_outline (GskRenderNode *node); +float gsk_border_node_get_width (GskRenderNode *node, guint i); +const GdkRGBA * gsk_border_node_peek_color (GskRenderNode *node, guint i); + cairo_surface_t *gsk_cairo_node_get_surface (GskRenderNode *node); GskTexture *gsk_texture_node_get_texture (GskRenderNode *node); diff --git a/gtk/gtkrenderborder.c b/gtk/gtkrenderborder.c index 5a17516499..7f739c3d64 100644 --- a/gtk/gtkrenderborder.c +++ b/gtk/gtkrenderborder.c @@ -406,6 +406,44 @@ render_frame_fill (cairo_t *cr, } static void +snapshot_frame_fill (GtkSnapshot *snapshot, + const GskRoundedRect *outline, + const float border_width[4], + const GdkRGBA colors[4], + guint hidden_side) +{ + GskRoundedRect offset_outline; + GskRenderNode *node; + double off_x, off_y; + + if (hidden_side) + { + GdkRGBA real_colors[4]; + guint i; + + for (i = 0; i < 4; i++) + { + if (hidden_side & (1 << i)) + real_colors[i] = (GdkRGBA) { 0, 0, 0, 0 }; + else + real_colors[i] = colors[i]; + } + + snapshot_frame_fill (snapshot, outline, border_width, real_colors, 0); + return; + } + + gtk_snapshot_get_offset (snapshot, &off_x, &off_y); + gsk_rounded_rect_init_copy (&offset_outline, outline); + gsk_rounded_rect_offset (&offset_outline, off_x, off_y); + + node = gsk_border_node_new (&offset_outline, border_width, colors); + gsk_render_node_set_name (node, "Border"); + gtk_snapshot_append_node (snapshot, node); + gsk_render_node_unref (node); +} + +static void set_stroke_style (cairo_t *cr, double line_width, GtkBorderStyle style, @@ -540,6 +578,24 @@ render_frame_stroke (cairo_t *cr, } static void +snapshot_frame_stroke (GtkSnapshot *snapshot, + GskRoundedRect *outline, + const float border_width[4], + GdkRGBA colors[4], + guint hidden_side, + GtkBorderStyle stroke_style) +{ + double double_width[4] = { border_width[0], border_width[1], border_width[2], border_width[3] }; + cairo_t *cr; + + cr = gtk_snapshot_append_cairo_node (snapshot, + &outline->bounds, + "BorderStroke"); + render_frame_stroke (cr, outline, double_width, colors, hidden_side, stroke_style); + cairo_destroy (cr); +} + +static void color_shade (const GdkRGBA *color, gdouble factor, GdkRGBA *color_return) @@ -679,6 +735,128 @@ render_border (cairo_t *cr, cairo_restore (cr); } +static void +snapshot_border (GtkSnapshot *snapshot, + GskRoundedRect *border_box, + const float border_width[4], + GdkRGBA colors[4], + GtkBorderStyle border_style[4]) +{ + guint hidden_side = 0; + guint i, j; + + for (i = 0; i < 4; i++) + { + if (hidden_side & (1 << i)) + continue; + + /* NB: code below divides by this value */ + /* a border smaller than this will not noticably modify + * pixels on screen, and since we don't compare with 0, + * we'll use this value */ + if (border_width[i] < 1.0 / 1024) + continue; + + switch (border_style[i]) + { + case GTK_BORDER_STYLE_NONE: + case GTK_BORDER_STYLE_HIDDEN: + case GTK_BORDER_STYLE_SOLID: + break; + case GTK_BORDER_STYLE_INSET: + if (i == 1 || i == 2) + color_shade (&colors[i], 1.8, &colors[i]); + break; + case GTK_BORDER_STYLE_OUTSET: + if (i == 0 || i == 3) + color_shade (&colors[i], 1.8, &colors[i]); + break; + case GTK_BORDER_STYLE_DOTTED: + case GTK_BORDER_STYLE_DASHED: + { + guint dont_draw = hidden_side; + + for (j = 0; j < 4; j++) + { + if (border_style[j] == border_style[i]) + hidden_side |= (1 << j); + else + dont_draw |= (1 << j); + } + + snapshot_frame_stroke (snapshot, border_box, border_width, colors, dont_draw, border_style[i]); + } + break; + case GTK_BORDER_STYLE_DOUBLE: + { + GskRoundedRect other_box; + float other_border[4]; + guint dont_draw = hidden_side; + + for (j = 0; j < 4; j++) + { + if (border_style[j] == GTK_BORDER_STYLE_DOUBLE) + hidden_side |= (1 << j); + else + dont_draw |= (1 << j); + + other_border[j] = border_width[j] / 3; + } + + snapshot_frame_fill (snapshot, border_box, other_border, colors, dont_draw); + + other_box = *border_box; + gsk_rounded_rect_shrink (&other_box, + 2 * other_border[GTK_CSS_TOP], + 2 * other_border[GTK_CSS_RIGHT], + 2 * other_border[GTK_CSS_BOTTOM], + 2 * other_border[GTK_CSS_LEFT]); + snapshot_frame_fill (snapshot, &other_box, other_border, colors, dont_draw); + } + break; + case GTK_BORDER_STYLE_GROOVE: + case GTK_BORDER_STYLE_RIDGE: + { + GskRoundedRect other_box; + GdkRGBA other_colors[4]; + guint dont_draw = hidden_side; + float other_border[4]; + + for (j = 0; j < 4; j++) + { + other_colors[j] = colors[j]; + if ((j == 0 || j == 3) ^ (border_style[j] == GTK_BORDER_STYLE_RIDGE)) + color_shade (&other_colors[j], 1.8, &other_colors[j]); + else + color_shade (&colors[j], 1.8, &colors[j]); + if (border_style[j] == GTK_BORDER_STYLE_GROOVE || + border_style[j] == GTK_BORDER_STYLE_RIDGE) + hidden_side |= (1 << j); + else + dont_draw |= (1 << j); + other_border[j] = border_width[j] / 2; + } + + snapshot_frame_fill (snapshot, border_box, other_border, colors, dont_draw); + + other_box = *border_box; + gsk_rounded_rect_shrink (&other_box, + other_border[GTK_CSS_TOP], + other_border[GTK_CSS_RIGHT], + other_border[GTK_CSS_BOTTOM], + other_border[GTK_CSS_LEFT]); + snapshot_frame_fill (snapshot, &other_box, other_border, other_colors, dont_draw); + } + break; + default: + g_assert_not_reached (); + break; + } + } + + snapshot_frame_fill (snapshot, border_box, border_width, colors, hidden_side); +} + gboolean gtk_css_style_render_has_border (GtkCssStyle *style) { @@ -744,13 +922,13 @@ gtk_css_style_render_border (GtkCssStyle *style, void gtk_css_style_snapshot_border (GtkCssStyle *style, - GtkSnapshot *state, + GtkSnapshot *snapshot, gdouble width, gdouble height, GtkJunctionSides junction) { GtkBorderImage border_image; - double border_width[4]; + float border_width[4]; graphene_rect_t bounds; cairo_t *cr; @@ -763,10 +941,12 @@ gtk_css_style_snapshot_border (GtkCssStyle *style, if (gtk_border_image_init (&border_image, style)) { - cr = gtk_snapshot_append_cairo_node (state, + double double_width[4] = { border_width[0], border_width[1], border_width[2], border_width[3] }; + + cr = gtk_snapshot_append_cairo_node (snapshot, &bounds, "Border Image"); - gtk_border_image_render (&border_image, border_width, cr, 0, 0, width, height); + gtk_border_image_render (&border_image, double_width, cr, 0, 0, width, height); cairo_destroy (cr); } else @@ -782,10 +962,6 @@ gtk_css_style_snapshot_border (GtkCssStyle *style, border_width[3] == 0) return; - cr = gtk_snapshot_append_cairo_node (state, - &bounds, - "Border"); - border_style[0] = _gtk_css_border_style_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_TOP_STYLE)); border_style[1] = _gtk_css_border_style_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_RIGHT_STYLE)); border_style[2] = _gtk_css_border_style_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_BOTTOM_STYLE)); @@ -799,9 +975,7 @@ gtk_css_style_snapshot_border (GtkCssStyle *style, _gtk_rounded_box_init_rect (&border_box, 0, 0, width, height); _gtk_rounded_box_apply_border_radius_for_style (&border_box, style, junction); - render_border (cr, &border_box, border_width, colors, border_style); - - cairo_destroy (cr); + snapshot_border (snapshot, &border_box, border_width, colors, border_style); } } @@ -904,20 +1078,19 @@ gtk_css_style_render_outline (GtkCssStyle *style, void gtk_css_style_snapshot_outline (GtkCssStyle *style, - GtkSnapshot *state, + GtkSnapshot *snapshot, gdouble width, gdouble height) { GtkBorderStyle border_style[4]; GskRoundedRect border_box; - double border_width[4]; + float border_width[4]; GdkRGBA colors[4]; border_style[0] = _gtk_css_border_style_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_OUTLINE_STYLE)); if (border_style[0] != GTK_BORDER_STYLE_NONE) { cairo_rectangle_t rect; - cairo_t *cr; compute_outline_rect (style, 0, 0, width, height, &rect); @@ -930,13 +1103,7 @@ gtk_css_style_snapshot_outline (GtkCssStyle *style, _gtk_rounded_box_init_rect (&border_box, rect.x, rect.y, rect.width, rect.height); _gtk_rounded_box_apply_outline_radius_for_style (&border_box, style, GTK_JUNCTION_NONE); - cr = gtk_snapshot_append_cairo_node (state, - &GRAPHENE_RECT_INIT (rect.x, rect.y, rect.width, rect.height), - "Outline"); - - render_border (cr, &border_box, border_width, colors, border_style); - - cairo_destroy (cr); + snapshot_border (snapshot, &border_box, border_width, colors, border_style); } } diff --git a/gtk/gtkrenderborderprivate.h b/gtk/gtkrenderborderprivate.h index f89cdcb600..4de9d6da35 100644 --- a/gtk/gtkrenderborderprivate.h +++ b/gtk/gtkrenderborderprivate.h @@ -44,7 +44,7 @@ gboolean gtk_css_style_render_border_get_clip (GtkCssStyle gdouble height, GdkRectangle *out_clip) G_GNUC_WARN_UNUSED_RESULT; void gtk_css_style_snapshot_border (GtkCssStyle *style, - GtkSnapshot *state, + GtkSnapshot *snapshot, gdouble width, gdouble height, GtkJunctionSides junction); @@ -57,7 +57,7 @@ void gtk_css_style_render_outline (GtkCssStyle gdouble width, gdouble height); void gtk_css_style_snapshot_outline (GtkCssStyle *style, - GtkSnapshot *state, + GtkSnapshot *snapshot, gdouble width, gdouble height); gboolean gtk_css_style_render_outline_get_clip (GtkCssStyle *style, diff --git a/gtk/inspector/gtktreemodelrendernode.c b/gtk/inspector/gtktreemodelrendernode.c index fff0a06f41..96b0d944e9 100644 --- a/gtk/inspector/gtktreemodelrendernode.c +++ b/gtk/inspector/gtktreemodelrendernode.c @@ -527,6 +527,7 @@ append_node (GtkTreeModelRenderNode *nodemodel, case GSK_COLOR_NODE: case GSK_LINEAR_GRADIENT_NODE: case GSK_REPEATING_LINEAR_GRADIENT_NODE: + case GSK_BORDER_NODE: /* no children */ break; |