Fix path fitting

Include the stroke width in the boundaries when fitting a path into the
size of a `Path` element.
This commit is contained in:
Simon Hausmann 2021-02-04 09:06:33 +01:00
parent fd3c8bf9fa
commit d92c8cab23
3 changed files with 28 additions and 4 deletions

View file

@ -25,6 +25,7 @@ When adding an item or a property, it needs to be kept in sync with different pl
#![allow(missing_docs)] // because documenting each property of items is redundent #![allow(missing_docs)] // because documenting each property of items is redundent
use crate::component::ComponentVTable; use crate::component::ComponentVTable;
use crate::graphics::PathDataIterator;
use crate::graphics::{Brush, Color, PathData, Point, Rect, Size}; use crate::graphics::{Brush, Color, PathData, Point, Rect, Size};
use crate::input::{ use crate::input::{
FocusEvent, InputEventResult, KeyEvent, KeyEventResult, KeyEventType, MouseEvent, FocusEvent, InputEventResult, KeyEvent, KeyEventResult, KeyEventType, MouseEvent,
@ -600,6 +601,22 @@ impl Item for Path {
} }
} }
impl Path {
/// Returns an iterator of the events of the path and an offset, so that the
/// shape fits into the width/height of the path while respecting the stroke
/// width.
pub fn fitted_path_events(
self: Pin<&Self>,
) -> (euclid::default::Vector2D<f32>, PathDataIterator) {
let stroke_width = self.stroke_width();
let bounds_width = (self.width() - stroke_width).max(0.);
let bounds_height = (self.height() - stroke_width).max(0.);
let offset = euclid::default::Vector2D::new(stroke_width / 2., stroke_width / 2.);
let event_iterator = self.elements().iter_fitted(bounds_width, bounds_height);
(offset, event_iterator)
}
}
impl ItemConsts for Path { impl ItemConsts for Path {
const cached_rendering_data_offset: const_field_offset::FieldOffset<Path, CachedRenderingData> = const cached_rendering_data_offset: const_field_offset::FieldOffset<Path, CachedRenderingData> =
Path::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection(); Path::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();

View file

@ -954,8 +954,11 @@ impl ItemRenderer for GLItemRenderer {
if matches!(elements, sixtyfps_corelib::PathData::None) { if matches!(elements, sixtyfps_corelib::PathData::None) {
return; return;
} }
let (offset, path_events) = path.fitted_path_events();
let mut fpath = femtovg::Path::new(); let mut fpath = femtovg::Path::new();
for x in elements.iter_fitted(path.width(), path.height()).iter() { for x in path_events.iter() {
match x { match x {
lyon_path::Event::Begin { at } => { lyon_path::Event::Begin { at } => {
fpath.move_to(at.x, at.y); fpath.move_to(at.x, at.y);
@ -983,7 +986,7 @@ impl ItemRenderer for GLItemRenderer {
border_paint.set_line_width(path.stroke_width()); border_paint.set_line_width(path.stroke_width());
self.shared_data.canvas.borrow_mut().save_with(|canvas| { self.shared_data.canvas.borrow_mut().save_with(|canvas| {
canvas.translate(pos.x + path.x(), pos.y + path.y()); canvas.translate(pos.x + path.x() + offset.x, pos.y + path.y() + offset.y);
canvas.fill_path(&mut fpath, fill_paint); canvas.fill_path(&mut fpath, fill_paint);
canvas.stroke_path(&mut fpath, border_paint); canvas.stroke_path(&mut fpath, border_paint);
}) })

View file

@ -372,12 +372,16 @@ impl ItemRenderer for QtItemRenderer<'_> {
} }
// FIXME: handle width/height // FIXME: handle width/height
//let rect: qttypes::QRectF = get_geometry!(pos, items::Path, path); //let rect: qttypes::QRectF = get_geometry!(pos, items::Path, path);
let pos = qttypes::QPoint { x: (pos.x + path.x()) as _, y: (pos.y + path.y()) as _ };
let fill_brush: qttypes::QBrush = path.fill().into(); let fill_brush: qttypes::QBrush = path.fill().into();
let stroke_brush: qttypes::QBrush = path.stroke().into(); let stroke_brush: qttypes::QBrush = path.stroke().into();
let stroke_width: f32 = path.stroke_width(); let stroke_width: f32 = path.stroke_width();
let (offset, path_events) = path.fitted_path_events();
let pos = qttypes::QPoint {
x: (pos.x + path.x() + offset.x) as _,
y: (pos.y + path.y() + offset.y) as _,
};
let mut painter_path = QPainterPath::default(); let mut painter_path = QPainterPath::default();
for x in elements.iter_fitted(path.width(), path.height()).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 {
qttypes::QPointF { x: p.x as _, y: p.y as _ } qttypes::QPointF { x: p.x as _, y: p.y as _ }