mirror of
https://github.com/slint-ui/slint.git
synced 2025-09-30 05:44:52 +00:00
Add support for stroking paths
This will make it easier to visualize the path for layouts.
This commit is contained in:
parent
dfe95a2f6d
commit
f45ff6ce79
6 changed files with 101 additions and 8 deletions
|
@ -75,6 +75,8 @@ fn main() {
|
|||
y: 300.,
|
||||
elements: PathElements::StaticElements(TRIANGLE_PATH.into()),
|
||||
fill_color: Color::from_rgb(0, 128, 255),
|
||||
stroke_color: Color::BLACK,
|
||||
stroke_width: 2.0,
|
||||
});
|
||||
render_cache.allocate_entry(path_primitive)
|
||||
};
|
||||
|
|
|
@ -279,6 +279,8 @@ impl TypeRegister {
|
|||
path.properties.insert("x".to_owned(), Type::Float32);
|
||||
path.properties.insert("y".to_owned(), Type::Float32);
|
||||
path.properties.insert("fill_color".to_owned(), Type::Color);
|
||||
path.properties.insert("stroke_color".to_owned(), Type::Color);
|
||||
path.properties.insert("stroke_width".to_owned(), Type::Float32);
|
||||
path.disallow_global_types_as_child_elements = true;
|
||||
|
||||
let path_elements = {
|
||||
|
|
|
@ -207,6 +207,8 @@ impl Color {
|
|||
pub const BLACK: Color = Color::from_rgb(0, 0, 0);
|
||||
/// A constant for the white color
|
||||
pub const WHITE: Color = Color::from_rgb(255, 255, 255);
|
||||
/// A constant for the transparent color
|
||||
pub const TRANSPARENT: Color = Color::from_rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
impl From<u32> for Color {
|
||||
|
@ -428,6 +430,8 @@ pub enum RenderingPrimitive {
|
|||
y: f32,
|
||||
elements: crate::PathElements,
|
||||
fill_color: Color,
|
||||
stroke_color: Color,
|
||||
stroke_width: f32,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -270,6 +270,8 @@ pub struct Path {
|
|||
pub y: Property<f32>,
|
||||
pub elements: Property<PathElements>,
|
||||
pub fill_color: Property<Color>,
|
||||
pub stroke_color: Property<Color>,
|
||||
pub stroke_width: Property<f32>,
|
||||
pub cached_rendering_data: CachedRenderingData,
|
||||
}
|
||||
|
||||
|
@ -291,6 +293,8 @@ impl Item for Path {
|
|||
y: Self::field_offsets().y.apply_pin(self).get(context),
|
||||
elements: Self::field_offsets().elements.apply_pin(self).get(context),
|
||||
fill_color: Self::field_offsets().fill_color.apply_pin(self).get(context),
|
||||
stroke_color: Self::field_offsets().stroke_color.apply_pin(self).get(context),
|
||||
stroke_width: Self::field_offsets().stroke_width.apply_pin(self).get(context),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,10 @@ use glow::{Context as GLContext, HasContext};
|
|||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use itertools::Itertools;
|
||||
use lyon::tessellation::geometry_builder::{BuffersBuilder, VertexBuffers};
|
||||
use lyon::tessellation::{FillAttributes, FillOptions, FillTessellator};
|
||||
use lyon::tessellation::{
|
||||
FillAttributes, FillOptions, FillTessellator, StrokeAttributes, StrokeOptions,
|
||||
StrokeTessellator,
|
||||
};
|
||||
use sixtyfps_corelib::abi::datastructures::{
|
||||
Color, ComponentWindow, ComponentWindowOpaque, Point, Rect, RenderingPrimitive, Resource, Size,
|
||||
};
|
||||
|
@ -111,6 +114,7 @@ pub struct GLRenderer {
|
|||
pub struct GLRenderingPrimitivesBuilder {
|
||||
context: Rc<glow::Context>,
|
||||
fill_tesselator: FillTessellator,
|
||||
stroke_tesselator: StrokeTessellator,
|
||||
texture_atlas: Rc<RefCell<TextureAtlas>>,
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
platform_data: Rc<RefCell<PlatformData>>,
|
||||
|
@ -226,6 +230,7 @@ impl GraphicsBackend for GLRenderer {
|
|||
GLRenderingPrimitivesBuilder {
|
||||
context: self.context.clone(),
|
||||
fill_tesselator: FillTessellator::new(),
|
||||
stroke_tesselator: StrokeTessellator::new(),
|
||||
texture_atlas: self.texture_atlas.clone(),
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
platform_data: self.platform_data.clone(),
|
||||
|
@ -306,7 +311,9 @@ impl RenderingPrimitivesBuilder for GLRenderingPrimitivesBuilder {
|
|||
rect_path.line_to(Point::new(*width, *height));
|
||||
rect_path.line_to(Point::new(0.0, *height));
|
||||
rect_path.close();
|
||||
self.create_path(&rect_path.build(), FillStyle::SolidColor(*color))
|
||||
self.fill_path(&rect_path.build(), FillStyle::SolidColor(*color))
|
||||
.into_iter()
|
||||
.collect()
|
||||
}
|
||||
RenderingPrimitive::Image { x: _, y: _, source } => {
|
||||
match source {
|
||||
|
@ -337,8 +344,39 @@ impl RenderingPrimitivesBuilder for GLRenderingPrimitivesBuilder {
|
|||
if *font_pixel_size != 0. { *font_pixel_size } else { 48.0 * 72. / 96. };
|
||||
smallvec![self.create_glyph_runs(text, font_family, pixel_size, *color)]
|
||||
}
|
||||
RenderingPrimitive::Path { x: _, y: _, elements, fill_color } => self
|
||||
.create_path(elements.build_path().iter(), FillStyle::SolidColor(*fill_color)),
|
||||
RenderingPrimitive::Path {
|
||||
x: _,
|
||||
y: _,
|
||||
elements,
|
||||
fill_color,
|
||||
stroke_color,
|
||||
stroke_width,
|
||||
} => {
|
||||
let mut primitives = SmallVec::new();
|
||||
|
||||
if *fill_color != Color::TRANSPARENT {
|
||||
primitives.extend(
|
||||
self.fill_path(
|
||||
elements.build_path().iter(),
|
||||
FillStyle::SolidColor(*fill_color),
|
||||
)
|
||||
.into_iter(),
|
||||
);
|
||||
}
|
||||
|
||||
if *stroke_color != Color::TRANSPARENT {
|
||||
primitives.extend(
|
||||
self.stroke_path(
|
||||
elements.build_path().iter(),
|
||||
*stroke_color,
|
||||
*stroke_width,
|
||||
)
|
||||
.into_iter(),
|
||||
);
|
||||
}
|
||||
|
||||
primitives
|
||||
}
|
||||
},
|
||||
rendering_primitive: primitive,
|
||||
}
|
||||
|
@ -346,11 +384,11 @@ impl RenderingPrimitivesBuilder for GLRenderingPrimitivesBuilder {
|
|||
}
|
||||
|
||||
impl GLRenderingPrimitivesBuilder {
|
||||
fn create_path(
|
||||
fn fill_path(
|
||||
&mut self,
|
||||
path: impl IntoIterator<Item = lyon::path::PathEvent>,
|
||||
style: FillStyle,
|
||||
) -> GLRenderingPrimitives {
|
||||
) -> Option<GLRenderingPrimitive> {
|
||||
let mut geometry: VertexBuffers<Vertex, u16> = VertexBuffers::new();
|
||||
|
||||
let fill_opts = FillOptions::default();
|
||||
|
@ -368,13 +406,53 @@ impl GLRenderingPrimitivesBuilder {
|
|||
.unwrap();
|
||||
|
||||
if geometry.vertices.len() == 0 || geometry.indices.len() == 0 {
|
||||
return SmallVec::new();
|
||||
return None;
|
||||
}
|
||||
|
||||
let vertices = GLArrayBuffer::new(&self.context, &geometry.vertices);
|
||||
let indices = GLIndexBuffer::new(&self.context, &geometry.indices);
|
||||
|
||||
smallvec![GLRenderingPrimitive::FillPath { vertices, indices, style }.into()]
|
||||
Some(GLRenderingPrimitive::FillPath { vertices, indices, style }.into())
|
||||
}
|
||||
|
||||
fn stroke_path(
|
||||
&mut self,
|
||||
path: impl IntoIterator<Item = lyon::path::PathEvent>,
|
||||
stroke_color: Color,
|
||||
stroke_width: f32,
|
||||
) -> Option<GLRenderingPrimitive> {
|
||||
let mut geometry: VertexBuffers<Vertex, u16> = VertexBuffers::new();
|
||||
|
||||
let stroke_opts = StrokeOptions::DEFAULT.with_line_width(stroke_width);
|
||||
|
||||
self.stroke_tesselator
|
||||
.tessellate(
|
||||
path,
|
||||
&stroke_opts,
|
||||
&mut BuffersBuilder::new(
|
||||
&mut geometry,
|
||||
|pos: lyon::math::Point, _: StrokeAttributes| Vertex {
|
||||
_pos: [pos.x as f32, pos.y as f32],
|
||||
},
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if geometry.vertices.len() == 0 || geometry.indices.len() == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let vertices = GLArrayBuffer::new(&self.context, &geometry.vertices);
|
||||
let indices = GLIndexBuffer::new(&self.context, &geometry.indices);
|
||||
|
||||
Some(
|
||||
GLRenderingPrimitive::FillPath {
|
||||
vertices,
|
||||
indices,
|
||||
style: FillStyle::SolidColor(stroke_color),
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
fn create_image(
|
||||
|
|
|
@ -106,6 +106,9 @@ Hello := Rectangle {
|
|||
x: 100;
|
||||
y: 300;
|
||||
fill_color: green;
|
||||
stroke_color: black;
|
||||
stroke_width: 2.0;
|
||||
|
||||
LineTo {
|
||||
x: 100;
|
||||
y: 50;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue