summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/reference/gsk/gsk4-sections.txt1
-rw-r--r--gsk/gskenums.h2
-rw-r--r--gsk/gskrendernode.h5
-rw-r--r--gsk/gskrendernodeimpl.c186
-rw-r--r--gsk/gskrendernodeprivate.h4
-rw-r--r--gtk/gtkrenderborder.c209
-rw-r--r--gtk/gtkrenderborderprivate.h4
-rw-r--r--gtk/inspector/gtktreemodelrendernode.c1
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;