Add support for Path.fill-rule

For some reason it's not working with the Qt renderer though
This commit is contained in:
Simon Hausmann 2021-02-10 14:03:23 +01:00
parent 600186220c
commit 2dd5ea61bb
16 changed files with 102 additions and 14 deletions

View file

@ -14,6 +14,7 @@ All notable changes to this project will be documented in this file.
- `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 for `else if`
- The path fill rule can now be specified using `Path::fill-rule`.
### Fixed
- `Image::image-fit`'s `cover` and `contains` varient are fixed to match the CSS spec

View file

@ -81,6 +81,7 @@ using cbindgen_private::ImageFit;
using cbindgen_private::KeyEvent;
using cbindgen_private::EventResult;
using cbindgen_private::KeyboardModifiers;
using cbindgen_private::FillRule;
namespace private_api {
using ItemTreeNode = cbindgen_private::ItemTreeNode<uint8_t>;

View file

@ -183,6 +183,7 @@ accordingly.
### Common Path Properties
* **`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-width`** (*length*): The width of the outline.
* **`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.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).

View file

@ -275,6 +275,7 @@ export Path := _ {
property <length> height;
property <brush> fill;
property <brush> fill_color <=> fill;
property <FillRule> fill_rule;
property <brush> stroke;
property <color> stroke_color <=> stroke;
property <length> stroke_width;

View file

@ -2353,9 +2353,7 @@ fn compile_path_events(events: &crate::expression_tree::PathEvents) -> (Vec<Stri
coordinates.push(to);
"sixtyfps::PathEvent::Cubic"
}
Event::End { last, first, close } => {
debug_assert_eq!(coordinates.first(), Some(&first));
debug_assert_eq!(coordinates.last(), Some(&last));
Event::End { close, .. } => {
if *close {
"sixtyfps::PathEvent::EndClosed"
} else {

View file

@ -2012,9 +2012,7 @@ fn compile_path_events(events: &crate::expression_tree::PathEvents) -> TokenStre
coordinates.push(to);
quote!(sixtyfps::re_exports::PathEvent::Cubic)
}
Event::End { last, first, close } => {
debug_assert_eq!(coordinates.first(), Some(&first));
debug_assert_eq!(coordinates.last(), Some(&last));
Event::End { close, .. } => {
if *close {
quote!(sixtyfps::re_exports::PathEvent::EndClosed)
} else {

View file

@ -145,6 +145,7 @@ impl TypeRegister {
);
declare_enum("ImageFit", &["fill", "contain", "cover"]);
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::Int32.to_string());

View file

@ -564,6 +564,20 @@ ItemVTable_static! {
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
#[repr(C)]
#[derive(FieldOffsets, Default, SixtyFPSElement)]
@ -575,6 +589,7 @@ pub struct Path {
pub height: Property<f32>,
pub elements: Property<PathData>,
pub fill: Property<Brush>,
pub fill_rule: Property<FillRule>,
pub stroke: Property<Brush>,
pub stroke_width: Property<f32>,
pub cached_rendering_data: CachedRenderingData,

View file

@ -45,6 +45,7 @@ declare_ValueType![
crate::input::KeyEvent,
crate::items::EventResult,
crate::Brush,
crate::items::FillRule,
];
/// What kind of animation is on a binding

View file

@ -645,6 +645,7 @@ fn generate_component<'id>(
"TextWrap" => property_info::<sixtyfps_corelib::items::TextWrap>(),
"TextOverflow" => property_info::<sixtyfps_corelib::items::TextOverflow>(),
"ImageFit" => property_info::<sixtyfps_corelib::items::ImageFit>(),
"FillRule" => property_info::<sixtyfps_corelib::items::FillRule>(),
_ => panic!("unkown enum"),
},
_ => panic!("bad type"),

View file

@ -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::input::KeyEventType, KeyEventType);
declare_value_enum_conversion!(corelib::items::EventResult, EventResult);
declare_value_enum_conversion!(corelib::items::FillRule, FillRule);
impl TryFrom<corelib::animations::Instant> for Value {
type Error = ();
@ -994,9 +995,7 @@ fn convert_from_lyon_path<'a>(
coordinates.push(to);
PathEvent::Cubic
}
Event::End { last, first, close } => {
debug_assert_eq!(coordinates.first(), Some(&first));
debug_assert_eq!(coordinates.last(), Some(&last));
Event::End { close, .. } => {
if *close {
PathEvent::EndClosed
} else {

View file

@ -18,7 +18,8 @@ use sixtyfps_corelib::graphics::{
};
use sixtyfps_corelib::item_rendering::{CachedRenderingData, ItemRenderer};
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::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| {
paint.set_line_width(path.stroke_width());
paint

View file

@ -9,7 +9,7 @@
LICENSE END */
/*! Generated with
```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
*/
@ -17,7 +17,7 @@ then add licence header and this doc
#![allow(non_camel_case_types)]
#![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_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_unknown: Qt_Key = 33554431;
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;

View file

@ -13,7 +13,7 @@ use items::{ImageFit, TextHorizontalAlignment, TextVerticalAlignment};
use sixtyfps_corelib::graphics::{Brush, FontRequest, Point, Rect, RenderingCache};
use sixtyfps_corelib::input::{InternalKeyCode, KeyEvent, KeyEventType, MouseEventType};
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::slice::Slice;
use sixtyfps_corelib::window::PlatformWindow;
@ -187,6 +187,12 @@ impl QPainterPath {
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,
@ -383,6 +389,12 @@ impl ItemRenderer for QtItemRenderer<'_> {
y: (pos.y + path.y() + offset.y) as _,
};
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() {
impl From<Point> for qttypes::QPointF {
fn from(p: Point) -> Self {

View 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;
}
}
}
}

View file

@ -63,6 +63,7 @@ fn gen_corelib(root_dir: &Path, include_dir: &Path) -> anyhow::Result<()> {
"TextInput",
"Clip",
"BoxShadow",
"FillRule",
]
.iter()
.map(|x| x.to_string())