1use 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#[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 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}