mirror of
https://github.com/slint-ui/slint.git
synced 2025-09-30 22:01:13 +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.,
|
y: 300.,
|
||||||
elements: PathElements::StaticElements(TRIANGLE_PATH.into()),
|
elements: PathElements::StaticElements(TRIANGLE_PATH.into()),
|
||||||
fill_color: Color::from_rgb(0, 128, 255),
|
fill_color: Color::from_rgb(0, 128, 255),
|
||||||
|
stroke_color: Color::BLACK,
|
||||||
|
stroke_width: 2.0,
|
||||||
});
|
});
|
||||||
render_cache.allocate_entry(path_primitive)
|
render_cache.allocate_entry(path_primitive)
|
||||||
};
|
};
|
||||||
|
|
|
@ -279,6 +279,8 @@ impl TypeRegister {
|
||||||
path.properties.insert("x".to_owned(), Type::Float32);
|
path.properties.insert("x".to_owned(), Type::Float32);
|
||||||
path.properties.insert("y".to_owned(), Type::Float32);
|
path.properties.insert("y".to_owned(), Type::Float32);
|
||||||
path.properties.insert("fill_color".to_owned(), Type::Color);
|
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;
|
path.disallow_global_types_as_child_elements = true;
|
||||||
|
|
||||||
let path_elements = {
|
let path_elements = {
|
||||||
|
|
|
@ -207,6 +207,8 @@ impl Color {
|
||||||
pub const BLACK: Color = Color::from_rgb(0, 0, 0);
|
pub const BLACK: Color = Color::from_rgb(0, 0, 0);
|
||||||
/// A constant for the white color
|
/// A constant for the white color
|
||||||
pub const WHITE: Color = Color::from_rgb(255, 255, 255);
|
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 {
|
impl From<u32> for Color {
|
||||||
|
@ -428,6 +430,8 @@ pub enum RenderingPrimitive {
|
||||||
y: f32,
|
y: f32,
|
||||||
elements: crate::PathElements,
|
elements: crate::PathElements,
|
||||||
fill_color: Color,
|
fill_color: Color,
|
||||||
|
stroke_color: Color,
|
||||||
|
stroke_width: f32,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -270,6 +270,8 @@ pub struct Path {
|
||||||
pub y: Property<f32>,
|
pub y: Property<f32>,
|
||||||
pub elements: Property<PathElements>,
|
pub elements: Property<PathElements>,
|
||||||
pub fill_color: Property<Color>,
|
pub fill_color: Property<Color>,
|
||||||
|
pub stroke_color: Property<Color>,
|
||||||
|
pub stroke_width: Property<f32>,
|
||||||
pub cached_rendering_data: CachedRenderingData,
|
pub cached_rendering_data: CachedRenderingData,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,6 +293,8 @@ impl Item for Path {
|
||||||
y: Self::field_offsets().y.apply_pin(self).get(context),
|
y: Self::field_offsets().y.apply_pin(self).get(context),
|
||||||
elements: Self::field_offsets().elements.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),
|
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"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use lyon::tessellation::geometry_builder::{BuffersBuilder, VertexBuffers};
|
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::{
|
use sixtyfps_corelib::abi::datastructures::{
|
||||||
Color, ComponentWindow, ComponentWindowOpaque, Point, Rect, RenderingPrimitive, Resource, Size,
|
Color, ComponentWindow, ComponentWindowOpaque, Point, Rect, RenderingPrimitive, Resource, Size,
|
||||||
};
|
};
|
||||||
|
@ -111,6 +114,7 @@ pub struct GLRenderer {
|
||||||
pub struct GLRenderingPrimitivesBuilder {
|
pub struct GLRenderingPrimitivesBuilder {
|
||||||
context: Rc<glow::Context>,
|
context: Rc<glow::Context>,
|
||||||
fill_tesselator: FillTessellator,
|
fill_tesselator: FillTessellator,
|
||||||
|
stroke_tesselator: StrokeTessellator,
|
||||||
texture_atlas: Rc<RefCell<TextureAtlas>>,
|
texture_atlas: Rc<RefCell<TextureAtlas>>,
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
platform_data: Rc<RefCell<PlatformData>>,
|
platform_data: Rc<RefCell<PlatformData>>,
|
||||||
|
@ -226,6 +230,7 @@ impl GraphicsBackend for GLRenderer {
|
||||||
GLRenderingPrimitivesBuilder {
|
GLRenderingPrimitivesBuilder {
|
||||||
context: self.context.clone(),
|
context: self.context.clone(),
|
||||||
fill_tesselator: FillTessellator::new(),
|
fill_tesselator: FillTessellator::new(),
|
||||||
|
stroke_tesselator: StrokeTessellator::new(),
|
||||||
texture_atlas: self.texture_atlas.clone(),
|
texture_atlas: self.texture_atlas.clone(),
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
platform_data: self.platform_data.clone(),
|
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(*width, *height));
|
||||||
rect_path.line_to(Point::new(0.0, *height));
|
rect_path.line_to(Point::new(0.0, *height));
|
||||||
rect_path.close();
|
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 } => {
|
RenderingPrimitive::Image { x: _, y: _, source } => {
|
||||||
match source {
|
match source {
|
||||||
|
@ -337,8 +344,39 @@ impl RenderingPrimitivesBuilder for GLRenderingPrimitivesBuilder {
|
||||||
if *font_pixel_size != 0. { *font_pixel_size } else { 48.0 * 72. / 96. };
|
if *font_pixel_size != 0. { *font_pixel_size } else { 48.0 * 72. / 96. };
|
||||||
smallvec![self.create_glyph_runs(text, font_family, pixel_size, *color)]
|
smallvec![self.create_glyph_runs(text, font_family, pixel_size, *color)]
|
||||||
}
|
}
|
||||||
RenderingPrimitive::Path { x: _, y: _, elements, fill_color } => self
|
RenderingPrimitive::Path {
|
||||||
.create_path(elements.build_path().iter(), FillStyle::SolidColor(*fill_color)),
|
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,
|
rendering_primitive: primitive,
|
||||||
}
|
}
|
||||||
|
@ -346,11 +384,11 @@ impl RenderingPrimitivesBuilder for GLRenderingPrimitivesBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GLRenderingPrimitivesBuilder {
|
impl GLRenderingPrimitivesBuilder {
|
||||||
fn create_path(
|
fn fill_path(
|
||||||
&mut self,
|
&mut self,
|
||||||
path: impl IntoIterator<Item = lyon::path::PathEvent>,
|
path: impl IntoIterator<Item = lyon::path::PathEvent>,
|
||||||
style: FillStyle,
|
style: FillStyle,
|
||||||
) -> GLRenderingPrimitives {
|
) -> Option<GLRenderingPrimitive> {
|
||||||
let mut geometry: VertexBuffers<Vertex, u16> = VertexBuffers::new();
|
let mut geometry: VertexBuffers<Vertex, u16> = VertexBuffers::new();
|
||||||
|
|
||||||
let fill_opts = FillOptions::default();
|
let fill_opts = FillOptions::default();
|
||||||
|
@ -368,13 +406,53 @@ impl GLRenderingPrimitivesBuilder {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
if geometry.vertices.len() == 0 || geometry.indices.len() == 0 {
|
if geometry.vertices.len() == 0 || geometry.indices.len() == 0 {
|
||||||
return SmallVec::new();
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let vertices = GLArrayBuffer::new(&self.context, &geometry.vertices);
|
let vertices = GLArrayBuffer::new(&self.context, &geometry.vertices);
|
||||||
let indices = GLIndexBuffer::new(&self.context, &geometry.indices);
|
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(
|
fn create_image(
|
||||||
|
|
|
@ -106,6 +106,9 @@ Hello := Rectangle {
|
||||||
x: 100;
|
x: 100;
|
||||||
y: 300;
|
y: 300;
|
||||||
fill_color: green;
|
fill_color: green;
|
||||||
|
stroke_color: black;
|
||||||
|
stroke_width: 2.0;
|
||||||
|
|
||||||
LineTo {
|
LineTo {
|
||||||
x: 100;
|
x: 100;
|
||||||
y: 50;
|
y: 50;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue