Add stroke-line-join for Path (#9912)

* Add `stroke-line-join` for `Path`
* Add `LineJoin` enum
* Add `stroke-line-join` property for `Path`
* Set line_join in Skia and FemtoVG renders
* Set pen_join_style in Qt backend
* Add example test case
* Docs: Add `stroke-line-join` property for `Path`
This commit is contained in:
Lance 2025-11-03 17:25:04 +08:00 committed by GitHub
parent 0bdb99d2c4
commit bbd5d8554b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 95 additions and 4 deletions

View file

@ -47,6 +47,11 @@ The width of the outline.
The appearance of the ends of the path's outline.
</SlintProperty>
### stroke-line-join
<SlintProperty propName="stroke-line-join" typeName="enum" enumName="LineJoin" defaultValue='miter'>
The appearance of the joins between segments of stroked paths.
</SlintProperty>
### width
<SlintProperty propName="width" typeName="length">
If non-zero, the path will be scaled to fit into the specified width.

View file

@ -20,8 +20,8 @@ use i_slint_core::item_rendering::{
use i_slint_core::item_tree::ParentItemTraversalMode;
use i_slint_core::item_tree::{ItemTreeRc, ItemTreeRef, ItemTreeWeak};
use i_slint_core::items::{
self, ColorScheme, FillRule, ImageRendering, ItemRc, ItemRef, Layer, LineCap, MouseCursor,
Opacity, PointerEventButton, RenderingResult, TextWrap,
self, ColorScheme, FillRule, ImageRendering, ItemRc, ItemRef, Layer, LineCap, LineJoin,
MouseCursor, Opacity, PointerEventButton, RenderingResult, TextWrap,
};
use i_slint_core::layout::Orientation;
use i_slint_core::lengths::{
@ -716,6 +716,12 @@ impl ItemRenderer for QtItemRenderer<'_> {
LineCap::Round => 0x20,
LineCap::Square => 0x10,
};
let stroke_pen_join_style: i32 = match path.stroke_line_join() {
LineJoin::Miter => 0x00,
LineJoin::Round => 0x80,
LineJoin::Bevel => 0x40,
};
let pos = qttypes::QPoint { x: offset.x as _, y: offset.y as _ };
let mut painter_path = QPainterPath::default();
@ -762,11 +768,12 @@ impl ItemRenderer for QtItemRenderer<'_> {
stroke_brush as "QBrush",
stroke_width as "float",
stroke_pen_cap_style as "int",
stroke_pen_join_style as "int",
anti_alias as "bool"] {
(*painter)->save();
auto cleanup = qScopeGuard([&] { (*painter)->restore(); });
(*painter)->translate(pos);
(*painter)->setPen(stroke_width > 0 ? QPen(stroke_brush, stroke_width, Qt::SolidLine, Qt::PenCapStyle(stroke_pen_cap_style)) : Qt::NoPen);
(*painter)->setPen(stroke_width > 0 ? QPen(stroke_brush, stroke_width, Qt::SolidLine, Qt::PenCapStyle(stroke_pen_cap_style), Qt::PenJoinStyle(stroke_pen_join_style)) : Qt::NoPen);
(*painter)->setBrush(fill_brush);
(*painter)->setRenderHint(QPainter::Antialiasing, anti_alias);
(*painter)->drawPath(painter_path);

View file

@ -480,6 +480,16 @@ macro_rules! for_each_enums {
Square,
}
/// This enum describes the appearance of the joins between segments of stroked paths.
enum LineJoin {
/// The stroke joins with a sharp corner or a clipped corner, depending on the miter limit.
Miter,
/// The stroke joins with a smooth, rounded corner.
Round,
/// The stroke joins with a beveled (flattened) corner.
Bevel,
}
/// This enum describes the detected operating system types.
#[non_exhaustive]
enum OperatingSystemType {

View file

@ -501,6 +501,7 @@ export component Path {
in property <brush> stroke;
in property <length> stroke-width;
in property <LineCap> stroke-line-cap;
in property <LineJoin> stroke-line-join;
in property <string> commands; // 'fake' hardcoded in typeregister.rs
in property <float> viewbox-x;
in property <float> viewbox-y;

View file

@ -8,7 +8,9 @@ When adding an item or a property, it needs to be kept in sync with different pl
Lookup the [`crate::items`] module documentation.
*/
use super::{FillRule, Item, ItemConsts, ItemRc, ItemRendererRef, LineCap, RenderingResult};
use super::{
FillRule, Item, ItemConsts, ItemRc, ItemRendererRef, LineCap, LineJoin, RenderingResult,
};
use crate::graphics::{Brush, PathData, PathDataIterator};
use crate::input::{
FocusEvent, FocusEventResult, InputEventFilterResult, InputEventResult, KeyEvent,
@ -41,6 +43,7 @@ pub struct Path {
pub stroke: Property<Brush>,
pub stroke_width: Property<LogicalLength>,
pub stroke_line_cap: Property<LineCap>,
pub stroke_line_join: Property<LineJoin>,
pub viewbox_x: Property<f32>,
pub viewbox_y: Property<f32>,
pub viewbox_width: Property<f32>,

View file

@ -461,6 +461,11 @@ impl<'a, R: femtovg::Renderer + TextureImporter> ItemRenderer for GLItemRenderer
items::LineCap::Round => femtovg::LineCap::Round,
items::LineCap::Square => femtovg::LineCap::Square,
});
paint.set_line_join(match path.stroke_line_join() {
items::LineJoin::Miter => femtovg::LineJoin::Miter,
items::LineJoin::Round => femtovg::LineJoin::Round,
items::LineJoin::Bevel => femtovg::LineJoin::Bevel,
});
paint.set_anti_alias(anti_alias);
paint
});

View file

@ -656,6 +656,11 @@ impl ItemRenderer for SkiaItemRenderer<'_> {
i_slint_core::items::LineCap::Round => skia_safe::PaintCap::Round,
i_slint_core::items::LineCap::Square => skia_safe::PaintCap::Square,
});
border_paint.set_stroke_join(match path.stroke_line_join() {
i_slint_core::items::LineJoin::Miter => skia_safe::PaintJoin::Miter,
i_slint_core::items::LineJoin::Round => skia_safe::PaintJoin::Round,
i_slint_core::items::LineJoin::Bevel => skia_safe::PaintJoin::Bevel,
});
border_paint.set_stroke(true);
self.canvas.draw_path(&skpath, &border_paint);
}

View file

@ -0,0 +1,55 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
PathLineJoin := Window {
GridLayout {
Row {
Text {
text: "The path to the right should have rounded corners (line-join: round)";
}
Path {
commands: "M10.5 15.5 9 17l-1.5-1.5";
fill: transparent;
stroke: white;
stroke-width: 10px;
stroke-line-join: round;
}
}
Row {
Text {
text: "The path to the right should have sharp pointed corners (line-join: miter)";
}
Path {
commands: "M10.5 15.5 9 17l-1.5-1.5";
fill: transparent;
stroke: white;
stroke-width: 10px;
stroke-line-join: miter;
}
}
Row {
Text {
text: "The path to the right should have beveled/flat corners (line-join: bevel)";
}
Path {
commands: "M10.5 15.5 9 17l-1.5-1.5";
fill: transparent;
stroke: white;
stroke-width: 10px;
stroke-line-join: bevel;
}
}
Row {
Text {
text: "Zigzag pattern with round joins - should show smooth rounded corners";
}
Path {
commands: "m15 17-1.5-1.5L12 17l-1.5-1.5L9 17l-1.5-1.5";
fill: transparent;
stroke: white;
stroke-width: 10px;
stroke-line-join: round;
}
}
}
}