i_slint_core/items/
path.rs1use super::{FillRule, Item, ItemConsts, ItemRc, ItemRendererRef, LineCap, RenderingResult};
12use crate::graphics::{Brush, PathData, PathDataIterator};
13use crate::input::{
14 FocusEvent, FocusEventResult, InputEventFilterResult, InputEventResult, KeyEvent,
15 KeyEventResult, MouseEvent,
16};
17use crate::item_rendering::CachedRenderingData;
18
19use crate::layout::{LayoutInfo, Orientation};
20use crate::lengths::{
21 LogicalBorderRadius, LogicalLength, LogicalRect, LogicalSize, LogicalVector, PointLengths,
22 RectLengths,
23};
24#[cfg(feature = "rtti")]
25use crate::rtti::*;
26use crate::window::WindowAdapter;
27use crate::{Coord, Property};
28use alloc::rc::Rc;
29use const_field_offset::FieldOffsets;
30use core::pin::Pin;
31use euclid::num::Zero;
32use i_slint_core_macros::*;
33
34#[repr(C)]
36#[derive(FieldOffsets, Default, SlintElement)]
37#[pin]
38pub struct Path {
39 pub elements: Property<PathData>,
40 pub fill: Property<Brush>,
41 pub fill_rule: Property<FillRule>,
42 pub stroke: Property<Brush>,
43 pub stroke_width: Property<LogicalLength>,
44 pub stroke_line_cap: Property<LineCap>,
45 pub viewbox_x: Property<f32>,
46 pub viewbox_y: Property<f32>,
47 pub viewbox_width: Property<f32>,
48 pub viewbox_height: Property<f32>,
49 pub clip: Property<bool>,
50 pub anti_alias: Property<bool>,
51 pub cached_rendering_data: CachedRenderingData,
52}
53
54impl Item for Path {
55 fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
56
57 fn layout_info(
58 self: Pin<&Self>,
59 _orientation: Orientation,
60 _window_adapter: &Rc<dyn WindowAdapter>,
61 _self_rc: &ItemRc,
62 ) -> LayoutInfo {
63 LayoutInfo { stretch: 1., ..LayoutInfo::default() }
64 }
65
66 fn input_event_filter_before_children(
67 self: Pin<&Self>,
68 event: MouseEvent,
69 _window_adapter: &Rc<dyn WindowAdapter>,
70 self_rc: &ItemRc,
71 ) -> InputEventFilterResult {
72 if let Some(pos) = event.position() {
73 let geometry = self_rc.geometry();
74 if self.clip()
75 && (pos.x < 0 as _
76 || pos.y < 0 as _
77 || pos.x_length() > geometry.width_length()
78 || pos.y_length() > geometry.height_length())
79 {
80 return InputEventFilterResult::Intercept;
81 }
82 }
83 InputEventFilterResult::ForwardAndIgnore
84 }
85
86 fn input_event(
87 self: Pin<&Self>,
88 _: MouseEvent,
89 _window_adapter: &Rc<dyn WindowAdapter>,
90 _self_rc: &ItemRc,
91 ) -> InputEventResult {
92 InputEventResult::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 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}