mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-01 06:11:16 +00:00
Add support for Path.fill-rule
For some reason it's not working with the Qt renderer though
This commit is contained in:
parent
600186220c
commit
2dd5ea61bb
16 changed files with 102 additions and 14 deletions
|
@ -14,6 +14,7 @@ All notable changes to this project will be documented in this file.
|
||||||
- `0` can be converted to anything with units
|
- `0` can be converted to anything with units
|
||||||
- Support power of unit in intermediate expression. (eg: `3px * width / height` is now supported but used to be an error)
|
- Support power of unit in intermediate expression. (eg: `3px * width / height` is now supported but used to be an error)
|
||||||
- Support for `else if`
|
- Support for `else if`
|
||||||
|
- The path fill rule can now be specified using `Path::fill-rule`.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- `Image::image-fit`'s `cover` and `contains` varient are fixed to match the CSS spec
|
- `Image::image-fit`'s `cover` and `contains` varient are fixed to match the CSS spec
|
||||||
|
|
|
@ -81,6 +81,7 @@ using cbindgen_private::ImageFit;
|
||||||
using cbindgen_private::KeyEvent;
|
using cbindgen_private::KeyEvent;
|
||||||
using cbindgen_private::EventResult;
|
using cbindgen_private::EventResult;
|
||||||
using cbindgen_private::KeyboardModifiers;
|
using cbindgen_private::KeyboardModifiers;
|
||||||
|
using cbindgen_private::FillRule;
|
||||||
|
|
||||||
namespace private_api {
|
namespace private_api {
|
||||||
using ItemTreeNode = cbindgen_private::ItemTreeNode<uint8_t>;
|
using ItemTreeNode = cbindgen_private::ItemTreeNode<uint8_t>;
|
||||||
|
|
|
@ -183,6 +183,7 @@ accordingly.
|
||||||
### Common Path Properties
|
### Common Path Properties
|
||||||
|
|
||||||
* **`fill`** (*brush*): The color for filling the shape of the path.
|
* **`fill`** (*brush*): The color for filling the shape of the path.
|
||||||
|
* **`fill-rule`** (enum *[`FillRule`](#fillrule)*): The fill rule to use for the path. (default value: `nonzero`)
|
||||||
* **`stroke`** (*brush*): The color for drawing the outline of the path.
|
* **`stroke`** (*brush*): The color for drawing the outline of the path.
|
||||||
* **`stroke-width`** (*length*): The width of the outline.
|
* **`stroke-width`** (*length*): The width of the outline.
|
||||||
* **`width`** (*length*): If non-zero, the path will be scaled to fit into the specified width.
|
* **`width`** (*length*): If non-zero, the path will be scaled to fit into the specified width.
|
||||||
|
@ -640,3 +641,11 @@ This enum describes whether an event was rejected or accepted by an event handle
|
||||||
* **`EventResult.reject`**: The event is rejected by this event handler and may then be handled by parent item
|
* **`EventResult.reject`**: The event is rejected by this event handler and may then be handled by parent item
|
||||||
* **`EventResult.accept`**: The event is accepted and won't be processed further
|
* **`EventResult.accept`**: The event is accepted and won't be processed further
|
||||||
|
|
||||||
|
## `FillRule`
|
||||||
|
|
||||||
|
This enum describes the different ways of deciding what the inside of a shape described by a path shall be.
|
||||||
|
|
||||||
|
### Values
|
||||||
|
|
||||||
|
* **`FillRule.nonzero`**: The ["nonzero" fill rule as defined in SVG](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill-rule#nonzero).
|
||||||
|
* **`FillRule.evenodd`**: The ["evenodd" fill rule as defined in SVG](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill-rule#evenodd).
|
||||||
|
|
|
@ -275,6 +275,7 @@ export Path := _ {
|
||||||
property <length> height;
|
property <length> height;
|
||||||
property <brush> fill;
|
property <brush> fill;
|
||||||
property <brush> fill_color <=> fill;
|
property <brush> fill_color <=> fill;
|
||||||
|
property <FillRule> fill_rule;
|
||||||
property <brush> stroke;
|
property <brush> stroke;
|
||||||
property <color> stroke_color <=> stroke;
|
property <color> stroke_color <=> stroke;
|
||||||
property <length> stroke_width;
|
property <length> stroke_width;
|
||||||
|
|
|
@ -2353,9 +2353,7 @@ fn compile_path_events(events: &crate::expression_tree::PathEvents) -> (Vec<Stri
|
||||||
coordinates.push(to);
|
coordinates.push(to);
|
||||||
"sixtyfps::PathEvent::Cubic"
|
"sixtyfps::PathEvent::Cubic"
|
||||||
}
|
}
|
||||||
Event::End { last, first, close } => {
|
Event::End { close, .. } => {
|
||||||
debug_assert_eq!(coordinates.first(), Some(&first));
|
|
||||||
debug_assert_eq!(coordinates.last(), Some(&last));
|
|
||||||
if *close {
|
if *close {
|
||||||
"sixtyfps::PathEvent::EndClosed"
|
"sixtyfps::PathEvent::EndClosed"
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -2012,9 +2012,7 @@ fn compile_path_events(events: &crate::expression_tree::PathEvents) -> TokenStre
|
||||||
coordinates.push(to);
|
coordinates.push(to);
|
||||||
quote!(sixtyfps::re_exports::PathEvent::Cubic)
|
quote!(sixtyfps::re_exports::PathEvent::Cubic)
|
||||||
}
|
}
|
||||||
Event::End { last, first, close } => {
|
Event::End { close, .. } => {
|
||||||
debug_assert_eq!(coordinates.first(), Some(&first));
|
|
||||||
debug_assert_eq!(coordinates.last(), Some(&last));
|
|
||||||
if *close {
|
if *close {
|
||||||
quote!(sixtyfps::re_exports::PathEvent::EndClosed)
|
quote!(sixtyfps::re_exports::PathEvent::EndClosed)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -145,6 +145,7 @@ impl TypeRegister {
|
||||||
);
|
);
|
||||||
declare_enum("ImageFit", &["fill", "contain", "cover"]);
|
declare_enum("ImageFit", &["fill", "contain", "cover"]);
|
||||||
declare_enum("EventResult", &["reject", "accept"]);
|
declare_enum("EventResult", &["reject", "accept"]);
|
||||||
|
declare_enum("FillRule", &["nonzero", "evenodd"]);
|
||||||
|
|
||||||
register.supported_property_animation_types.insert(Type::Float32.to_string());
|
register.supported_property_animation_types.insert(Type::Float32.to_string());
|
||||||
register.supported_property_animation_types.insert(Type::Int32.to_string());
|
register.supported_property_animation_types.insert(Type::Int32.to_string());
|
||||||
|
|
|
@ -564,6 +564,20 @@ ItemVTable_static! {
|
||||||
pub static ClipVTable for Clip
|
pub static ClipVTable for Clip
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, strum_macros::EnumString, strum_macros::Display)]
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub enum FillRule {
|
||||||
|
nonzero,
|
||||||
|
evenodd,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for FillRule {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::nonzero
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The implementation of the `Path` element
|
/// The implementation of the `Path` element
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(FieldOffsets, Default, SixtyFPSElement)]
|
#[derive(FieldOffsets, Default, SixtyFPSElement)]
|
||||||
|
@ -575,6 +589,7 @@ pub struct Path {
|
||||||
pub height: Property<f32>,
|
pub height: Property<f32>,
|
||||||
pub elements: Property<PathData>,
|
pub elements: Property<PathData>,
|
||||||
pub fill: Property<Brush>,
|
pub fill: Property<Brush>,
|
||||||
|
pub fill_rule: Property<FillRule>,
|
||||||
pub stroke: Property<Brush>,
|
pub stroke: Property<Brush>,
|
||||||
pub stroke_width: Property<f32>,
|
pub stroke_width: Property<f32>,
|
||||||
pub cached_rendering_data: CachedRenderingData,
|
pub cached_rendering_data: CachedRenderingData,
|
||||||
|
|
|
@ -45,6 +45,7 @@ declare_ValueType![
|
||||||
crate::input::KeyEvent,
|
crate::input::KeyEvent,
|
||||||
crate::items::EventResult,
|
crate::items::EventResult,
|
||||||
crate::Brush,
|
crate::Brush,
|
||||||
|
crate::items::FillRule,
|
||||||
];
|
];
|
||||||
|
|
||||||
/// What kind of animation is on a binding
|
/// What kind of animation is on a binding
|
||||||
|
|
|
@ -645,6 +645,7 @@ fn generate_component<'id>(
|
||||||
"TextWrap" => property_info::<sixtyfps_corelib::items::TextWrap>(),
|
"TextWrap" => property_info::<sixtyfps_corelib::items::TextWrap>(),
|
||||||
"TextOverflow" => property_info::<sixtyfps_corelib::items::TextOverflow>(),
|
"TextOverflow" => property_info::<sixtyfps_corelib::items::TextOverflow>(),
|
||||||
"ImageFit" => property_info::<sixtyfps_corelib::items::ImageFit>(),
|
"ImageFit" => property_info::<sixtyfps_corelib::items::ImageFit>(),
|
||||||
|
"FillRule" => property_info::<sixtyfps_corelib::items::FillRule>(),
|
||||||
_ => panic!("unkown enum"),
|
_ => panic!("unkown enum"),
|
||||||
},
|
},
|
||||||
_ => panic!("bad type"),
|
_ => panic!("bad type"),
|
||||||
|
|
|
@ -253,6 +253,7 @@ declare_value_enum_conversion!(corelib::layout::LayoutAlignment, LayoutAlignment
|
||||||
declare_value_enum_conversion!(corelib::items::ImageFit, ImageFit);
|
declare_value_enum_conversion!(corelib::items::ImageFit, ImageFit);
|
||||||
declare_value_enum_conversion!(corelib::input::KeyEventType, KeyEventType);
|
declare_value_enum_conversion!(corelib::input::KeyEventType, KeyEventType);
|
||||||
declare_value_enum_conversion!(corelib::items::EventResult, EventResult);
|
declare_value_enum_conversion!(corelib::items::EventResult, EventResult);
|
||||||
|
declare_value_enum_conversion!(corelib::items::FillRule, FillRule);
|
||||||
|
|
||||||
impl TryFrom<corelib::animations::Instant> for Value {
|
impl TryFrom<corelib::animations::Instant> for Value {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
@ -994,9 +995,7 @@ fn convert_from_lyon_path<'a>(
|
||||||
coordinates.push(to);
|
coordinates.push(to);
|
||||||
PathEvent::Cubic
|
PathEvent::Cubic
|
||||||
}
|
}
|
||||||
Event::End { last, first, close } => {
|
Event::End { close, .. } => {
|
||||||
debug_assert_eq!(coordinates.first(), Some(&first));
|
|
||||||
debug_assert_eq!(coordinates.last(), Some(&last));
|
|
||||||
if *close {
|
if *close {
|
||||||
PathEvent::EndClosed
|
PathEvent::EndClosed
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -18,7 +18,8 @@ use sixtyfps_corelib::graphics::{
|
||||||
};
|
};
|
||||||
use sixtyfps_corelib::item_rendering::{CachedRenderingData, ItemRenderer};
|
use sixtyfps_corelib::item_rendering::{CachedRenderingData, ItemRenderer};
|
||||||
use sixtyfps_corelib::items::{
|
use sixtyfps_corelib::items::{
|
||||||
ImageFit, Item, TextHorizontalAlignment, TextOverflow, TextVerticalAlignment, TextWrap,
|
FillRule, ImageFit, Item, TextHorizontalAlignment, TextOverflow, TextVerticalAlignment,
|
||||||
|
TextWrap,
|
||||||
};
|
};
|
||||||
use sixtyfps_corelib::properties::Property;
|
use sixtyfps_corelib::properties::Property;
|
||||||
use sixtyfps_corelib::window::ComponentWindow;
|
use sixtyfps_corelib::window::ComponentWindow;
|
||||||
|
@ -820,7 +821,14 @@ impl ItemRenderer for GLItemRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let fill_paint = self.brush_to_paint(path.fill(), &mut fpath);
|
let fill_paint = self.brush_to_paint(path.fill(), &mut fpath).map(|mut fill_paint| {
|
||||||
|
fill_paint.set_fill_rule(match path.fill_rule() {
|
||||||
|
FillRule::nonzero => femtovg::FillRule::NonZero,
|
||||||
|
FillRule::evenodd => femtovg::FillRule::EvenOdd,
|
||||||
|
});
|
||||||
|
fill_paint
|
||||||
|
});
|
||||||
|
|
||||||
let border_paint = self.brush_to_paint(path.stroke(), &mut fpath).map(|mut paint| {
|
let border_paint = self.brush_to_paint(path.stroke(), &mut fpath).map(|mut paint| {
|
||||||
paint.set_line_width(path.stroke_width());
|
paint.set_line_width(path.stroke_width());
|
||||||
paint
|
paint
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
LICENSE END */
|
LICENSE END */
|
||||||
/*! Generated with
|
/*! Generated with
|
||||||
```sh
|
```sh
|
||||||
bindgen /usr/include/qt/QtCore/qnamespace.h --whitelist-type Qt::Key --whitelist-type Qt::KeyboardModifier --whitelist-type Qt::AlignmentFlag --whitelist-type Qt::TextFlag -o sixtyfps_runtime/rendering_backends/qt/key_generated.rs -- -I /usr/include/qt -xc++
|
bindgen /usr/include/qt/QtCore/qnamespace.h --whitelist-type Qt::Key --whitelist-type Qt::KeyboardModifier --whitelist-type Qt::AlignmentFlag --whitelist-type Qt::TextFlag --whitelist-type Qt::FillRule -o sixtyfps_runtime/rendering_backends/qt/key_generated.rs -- -I /usr/include/qt -xc++
|
||||||
```
|
```
|
||||||
then add licence header and this doc
|
then add licence header and this doc
|
||||||
*/
|
*/
|
||||||
|
@ -17,7 +17,7 @@ then add licence header and this doc
|
||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
#![allow(non_upper_case_globals)]
|
#![allow(non_upper_case_globals)]
|
||||||
|
|
||||||
/* automatically generated by rust-bindgen 0.56.0 */
|
/* automatically generated by rust-bindgen 0.57.0 */
|
||||||
|
|
||||||
pub const Qt_KeyboardModifier_NoModifier: Qt_KeyboardModifier = 0;
|
pub const Qt_KeyboardModifier_NoModifier: Qt_KeyboardModifier = 0;
|
||||||
pub const Qt_KeyboardModifier_ShiftModifier: Qt_KeyboardModifier = 33554432;
|
pub const Qt_KeyboardModifier_ShiftModifier: Qt_KeyboardModifier = 33554432;
|
||||||
|
@ -528,3 +528,6 @@ pub const Qt_Key_Key_Camera: Qt_Key = 17825824;
|
||||||
pub const Qt_Key_Key_CameraFocus: Qt_Key = 17825825;
|
pub const Qt_Key_Key_CameraFocus: Qt_Key = 17825825;
|
||||||
pub const Qt_Key_Key_unknown: Qt_Key = 33554431;
|
pub const Qt_Key_Key_unknown: Qt_Key = 33554431;
|
||||||
pub type Qt_Key = ::std::os::raw::c_uint;
|
pub type Qt_Key = ::std::os::raw::c_uint;
|
||||||
|
pub const Qt_FillRule_OddEvenFill: Qt_FillRule = 0;
|
||||||
|
pub const Qt_FillRule_WindingFill: Qt_FillRule = 1;
|
||||||
|
pub type Qt_FillRule = ::std::os::raw::c_uint;
|
||||||
|
|
|
@ -13,7 +13,7 @@ use items::{ImageFit, TextHorizontalAlignment, TextVerticalAlignment};
|
||||||
use sixtyfps_corelib::graphics::{Brush, FontRequest, Point, Rect, RenderingCache};
|
use sixtyfps_corelib::graphics::{Brush, FontRequest, Point, Rect, RenderingCache};
|
||||||
use sixtyfps_corelib::input::{InternalKeyCode, KeyEvent, KeyEventType, MouseEventType};
|
use sixtyfps_corelib::input::{InternalKeyCode, KeyEvent, KeyEventType, MouseEventType};
|
||||||
use sixtyfps_corelib::item_rendering::{CachedRenderingData, ItemRenderer};
|
use sixtyfps_corelib::item_rendering::{CachedRenderingData, ItemRenderer};
|
||||||
use sixtyfps_corelib::items::{self, ItemRef, TextOverflow, TextWrap};
|
use sixtyfps_corelib::items::{self, FillRule, ItemRef, TextOverflow, TextWrap};
|
||||||
use sixtyfps_corelib::properties::PropertyTracker;
|
use sixtyfps_corelib::properties::PropertyTracker;
|
||||||
use sixtyfps_corelib::slice::Slice;
|
use sixtyfps_corelib::slice::Slice;
|
||||||
use sixtyfps_corelib::window::PlatformWindow;
|
use sixtyfps_corelib::window::PlatformWindow;
|
||||||
|
@ -187,6 +187,12 @@ impl QPainterPath {
|
||||||
self->closeSubpath();
|
self->closeSubpath();
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_fill_rule(&mut self, rule: key_generated::Qt_FillRule) {
|
||||||
|
cpp! { unsafe [self as "QPainterPath*", rule as "Qt::FillRule" ] {
|
||||||
|
self->setFillRule(rule);
|
||||||
|
}}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given a position offset and an object of a given type that has x,y,width,height properties,
|
/// Given a position offset and an object of a given type that has x,y,width,height properties,
|
||||||
|
@ -383,6 +389,12 @@ impl ItemRenderer for QtItemRenderer<'_> {
|
||||||
y: (pos.y + path.y() + offset.y) as _,
|
y: (pos.y + path.y() + offset.y) as _,
|
||||||
};
|
};
|
||||||
let mut painter_path = QPainterPath::default();
|
let mut painter_path = QPainterPath::default();
|
||||||
|
|
||||||
|
painter_path.set_fill_rule(match path.fill_rule() {
|
||||||
|
FillRule::nonzero => key_generated::Qt_FillRule_WindingFill,
|
||||||
|
FillRule::evenodd => key_generated::Qt_FillRule_OddEvenFill,
|
||||||
|
});
|
||||||
|
|
||||||
for x in path_events.iter() {
|
for x in path_events.iter() {
|
||||||
impl From<Point> for qttypes::QPointF {
|
impl From<Point> for qttypes::QPointF {
|
||||||
fn from(p: Point) -> Self {
|
fn from(p: Point) -> Self {
|
||||||
|
|
39
tests/cases/examples/path_fill_rule.60
Normal file
39
tests/cases/examples/path_fill_rule.60
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/* LICENSE BEGIN
|
||||||
|
This file is part of the SixtyFPS Project -- https://sixtyfps.io
|
||||||
|
Copyright (c) 2020 Olivier Goffart <olivier.goffart@sixtyfps.io>
|
||||||
|
Copyright (c) 2020 Simon Hausmann <simon.hausmann@sixtyfps.io>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
This file is also available under commercial licensing terms.
|
||||||
|
Please contact info@sixtyfps.io for more information.
|
||||||
|
LICENSE END */
|
||||||
|
|
||||||
|
PathFillRule := Window {
|
||||||
|
GridLayout {
|
||||||
|
Row {
|
||||||
|
Text {
|
||||||
|
text: "The rectangle to the right should have a hole in the center";
|
||||||
|
}
|
||||||
|
Path {
|
||||||
|
commands: "M210,0 h90 v90 h-90 z M230,20 v50 h50 v-50 z";
|
||||||
|
fill: black;
|
||||||
|
fill-rule: evenodd;
|
||||||
|
stroke: red;
|
||||||
|
stroke-width: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Row {
|
||||||
|
Text {
|
||||||
|
text: "The rectangle to the right should be filled in the center";
|
||||||
|
}
|
||||||
|
Path {
|
||||||
|
commands: "M210,0 h90 v90 h-90 z M230,20 v50 h50 v-50 z";
|
||||||
|
fill: black;
|
||||||
|
fill-rule: nonzero;
|
||||||
|
stroke: red;
|
||||||
|
stroke-width: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -63,6 +63,7 @@ fn gen_corelib(root_dir: &Path, include_dir: &Path) -> anyhow::Result<()> {
|
||||||
"TextInput",
|
"TextInput",
|
||||||
"Clip",
|
"Clip",
|
||||||
"BoxShadow",
|
"BoxShadow",
|
||||||
|
"FillRule",
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| x.to_string())
|
.map(|x| x.to_string())
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue