Skip to main content

i_slint_core/items/
path.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4/*!
5This module contains the builtin Path related items.
6
7When adding an item or a property, it needs to be kept in sync with different place.
8Lookup the [`crate::items`] module documentation.
9*/
10
11use super::{
12    FillRule, Item, ItemConsts, ItemRc, ItemRendererRef, LineCap, LineJoin, RenderingResult,
13};
14use crate::graphics::{Brush, PathData, PathDataIterator};
15use crate::input::{
16    FocusEvent, FocusEventResult, InputEventFilterResult, InputEventResult, KeyEvent,
17    KeyEventResult, MouseEvent,
18};
19use crate::item_rendering::CachedRenderingData;
20
21use crate::layout::{LayoutInfo, Orientation};
22use crate::lengths::{
23    LogicalBorderRadius, LogicalLength, LogicalRect, LogicalSize, LogicalVector, RectLengths,
24};
25#[cfg(feature = "rtti")]
26use crate::rtti::*;
27use crate::window::WindowAdapter;
28use crate::{Coord, Property};
29use alloc::rc::Rc;
30use const_field_offset::FieldOffsets;
31use core::pin::Pin;
32use euclid::num::Zero;
33use i_slint_core_macros::*;
34
35/// The implementation of the `Path` element
36#[repr(C)]
37#[derive(FieldOffsets, Default, SlintElement)]
38#[pin]
39pub struct Path {
40    pub elements: Property<PathData>,
41    pub fill: Property<Brush>,
42    pub fill_rule: Property<FillRule>,
43    pub stroke: Property<Brush>,
44    pub stroke_width: Property<LogicalLength>,
45    pub stroke_line_cap: Property<LineCap>,
46    pub stroke_line_join: Property<LineJoin>,
47    pub viewbox_x: Property<f32>,
48    pub viewbox_y: Property<f32>,
49    pub viewbox_width: Property<f32>,
50    pub viewbox_height: Property<f32>,
51    pub clip: Property<bool>,
52    pub anti_alias: Property<bool>,
53    pub cached_rendering_data: CachedRenderingData,
54}
55
56impl Item for Path {
57    fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
58
59    fn layout_info(
60        self: Pin<&Self>,
61        _orientation: Orientation,
62        _window_adapter: &Rc<dyn WindowAdapter>,
63        _self_rc: &ItemRc,
64    ) -> LayoutInfo {
65        LayoutInfo { stretch: 1., ..LayoutInfo::default() }
66    }
67
68    fn input_event_filter_before_children(
69        self: Pin<&Self>,
70        _: &MouseEvent,
71        _window_adapter: &Rc<dyn WindowAdapter>,
72        _self_rc: &ItemRc,
73    ) -> InputEventFilterResult {
74        InputEventFilterResult::ForwardAndIgnore
75    }
76
77    fn input_event(
78        self: Pin<&Self>,
79        _: &MouseEvent,
80        _window_adapter: &Rc<dyn WindowAdapter>,
81        _self_rc: &ItemRc,
82    ) -> InputEventResult {
83        InputEventResult::EventIgnored
84    }
85
86    fn capture_key_event(
87        self: Pin<&Self>,
88        _: &KeyEvent,
89        _window_adapter: &Rc<dyn WindowAdapter>,
90        _self_rc: &ItemRc,
91    ) -> KeyEventResult {
92        KeyEventResult::EventIgnored
93    }
94
95    fn key_event(
96        self: Pin<&Self>,
97        _: &KeyEvent,
98        _window_adapter: &Rc<dyn WindowAdapter>,
99        _self_rc: &ItemRc,
100    ) -> KeyEventResult {
101        KeyEventResult::EventIgnored
102    }
103
104    fn focus_event(
105        self: Pin<&Self>,
106        _: &FocusEvent,
107        _window_adapter: &Rc<dyn WindowAdapter>,
108        _self_rc: &ItemRc,
109    ) -> FocusEventResult {
110        FocusEventResult::FocusIgnored
111    }
112
113    fn render(
114        self: Pin<&Self>,
115        backend: &mut ItemRendererRef,
116        self_rc: &ItemRc,
117        size: LogicalSize,
118    ) -> RenderingResult {
119        let clip = self.clip();
120        if clip {
121            (*backend).save_state();
122            (*backend).combine_clip(
123                size.into(),
124                LogicalBorderRadius::zero(),
125                LogicalLength::zero(),
126            );
127        }
128        (*backend).draw_path(self, self_rc, size);
129        if clip {
130            (*backend).restore_state();
131        }
132        RenderingResult::ContinueRenderingChildren
133    }
134
135    fn bounding_rect(
136        self: core::pin::Pin<&Self>,
137        _window_adapter: &Rc<dyn WindowAdapter>,
138        _self_rc: &ItemRc,
139        geometry: LogicalRect,
140    ) -> LogicalRect {
141        geometry
142    }
143
144    fn clips_children(self: core::pin::Pin<&Self>) -> bool {
145        false
146    }
147}
148
149impl Path {
150    /// Returns an iterator of the events of the path and an offset, so that the
151    /// shape fits into the width/height of the path while respecting the stroke
152    /// width.
153    pub fn fitted_path_events(
154        self: Pin<&Self>,
155        self_rc: &ItemRc,
156    ) -> Option<(LogicalVector, PathDataIterator)> {
157        let mut elements_iter = self.elements().iter()?;
158
159        let stroke_width = self.stroke_width();
160        let geometry = self_rc.geometry();
161        let bounds_width = (geometry.width_length() - stroke_width).max(LogicalLength::zero());
162        let bounds_height = (geometry.height_length() - stroke_width).max(LogicalLength::zero());
163        let offset =
164            LogicalVector::from_lengths(stroke_width / 2 as Coord, stroke_width / 2 as Coord);
165
166        let viewbox_width = self.viewbox_width();
167        let viewbox_height = self.viewbox_height();
168
169        let maybe_viewbox = if viewbox_width > 0. && viewbox_height > 0. {
170            Some(
171                euclid::rect(self.viewbox_x(), self.viewbox_y(), viewbox_width, viewbox_height)
172                    .to_box2d(),
173            )
174        } else {
175            None
176        };
177
178        elements_iter.fit(bounds_width.get() as _, bounds_height.get() as _, maybe_viewbox);
179        (offset, elements_iter).into()
180    }
181}
182
183impl ItemConsts for Path {
184    const cached_rendering_data_offset: const_field_offset::FieldOffset<Path, CachedRenderingData> =
185        Path::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
186}