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, InternalKeyEvent,
17 KeyEventResult, MouseEvent,
18};
19use crate::item_rendering::CachedRenderingData;
20
21use crate::items::ImageFit;
22use crate::layout::{LayoutInfo, Orientation};
23use crate::lengths::{
24 LogicalBorderRadius, LogicalLength, LogicalRect, LogicalSize, LogicalVector, RectLengths,
25};
26#[cfg(feature = "rtti")]
27use crate::rtti::*;
28use crate::window::WindowAdapter;
29use crate::{Coord, Property};
30use alloc::rc::Rc;
31use const_field_offset::FieldOffsets;
32use core::pin::Pin;
33use euclid::num::Zero;
34use i_slint_core_macros::*;
35
36#[repr(C)]
38#[derive(FieldOffsets, Default, SlintElement)]
39#[pin]
40pub struct Path {
41 pub elements: Property<PathData>,
42 pub fill: Property<Brush>,
43 pub fill_rule: Property<FillRule>,
44 pub stroke: Property<Brush>,
45 pub stroke_width: Property<LogicalLength>,
46 pub stroke_line_cap: Property<LineCap>,
47 pub stroke_line_join: Property<LineJoin>,
48 pub stroke_miter_limit: Property<f32>,
49 pub viewbox_x: Property<f32>,
50 pub viewbox_y: Property<f32>,
51 pub viewbox_width: Property<f32>,
52 pub viewbox_height: Property<f32>,
53 pub fit: Property<ImageFit>,
54 pub clip: Property<bool>,
55 pub anti_alias: Property<bool>,
56 pub cached_rendering_data: CachedRenderingData,
57}
58
59impl Item for Path {
60 fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
61
62 fn deinit(self: Pin<&Self>, _window_adapter: &Rc<dyn WindowAdapter>) {}
63
64 fn layout_info(
65 self: Pin<&Self>,
66 _orientation: Orientation,
67 _cross_axis_constraint: Coord,
68 _window_adapter: &Rc<dyn WindowAdapter>,
69 _self_rc: &ItemRc,
70 ) -> LayoutInfo {
71 LayoutInfo { stretch: 1., ..LayoutInfo::default() }
72 }
73
74 fn input_event_filter_before_children(
75 self: Pin<&Self>,
76 _: &MouseEvent,
77 _window_adapter: &Rc<dyn WindowAdapter>,
78 _self_rc: &ItemRc,
79 _: &mut super::MouseCursor,
80 ) -> InputEventFilterResult {
81 InputEventFilterResult::ForwardAndIgnore
82 }
83
84 fn input_event(
85 self: Pin<&Self>,
86 _: &MouseEvent,
87 _window_adapter: &Rc<dyn WindowAdapter>,
88 _self_rc: &ItemRc,
89 _: &mut super::MouseCursor,
90 ) -> InputEventResult {
91 InputEventResult::EventIgnored
92 }
93
94 fn capture_key_event(
95 self: Pin<&Self>,
96 _: &InternalKeyEvent,
97 _window_adapter: &Rc<dyn WindowAdapter>,
98 _self_rc: &ItemRc,
99 ) -> KeyEventResult {
100 KeyEventResult::EventIgnored
101 }
102
103 fn key_event(
104 self: Pin<&Self>,
105 _: &InternalKeyEvent,
106 _window_adapter: &Rc<dyn WindowAdapter>,
107 _self_rc: &ItemRc,
108 ) -> KeyEventResult {
109 KeyEventResult::EventIgnored
110 }
111
112 fn focus_event(
113 self: Pin<&Self>,
114 _: &FocusEvent,
115 _window_adapter: &Rc<dyn WindowAdapter>,
116 _self_rc: &ItemRc,
117 ) -> FocusEventResult {
118 FocusEventResult::FocusIgnored
119 }
120
121 fn render(
122 self: Pin<&Self>,
123 backend: &mut ItemRendererRef,
124 self_rc: &ItemRc,
125 size: LogicalSize,
126 ) -> RenderingResult {
127 let clip = self.clip();
128 if clip {
129 (*backend).save_state();
130 (*backend).combine_clip(
131 size.into(),
132 LogicalBorderRadius::zero(),
133 LogicalLength::zero(),
134 );
135 }
136 (*backend).draw_path(self, self_rc, size);
137 if clip {
138 (*backend).restore_state();
139 }
140 RenderingResult::ContinueRenderingChildren
141 }
142
143 fn bounding_rect(
144 self: core::pin::Pin<&Self>,
145 _window_adapter: &Rc<dyn WindowAdapter>,
146 _self_rc: &ItemRc,
147 geometry: LogicalRect,
148 ) -> LogicalRect {
149 geometry
150 }
151
152 fn clips_children(self: core::pin::Pin<&Self>) -> bool {
153 false
154 }
155}
156
157impl Path {
158 pub fn fitted_path_events(
162 self: Pin<&Self>,
163 self_rc: &ItemRc,
164 ) -> Option<(LogicalVector, PathDataIterator)> {
165 let mut elements_iter = self.elements().iter()?;
166
167 let stroke_width = self.stroke_width();
168 let geometry = self_rc.geometry();
169 let bounds_width = (geometry.width_length() - stroke_width).max(LogicalLength::zero());
170 let bounds_height = (geometry.height_length() - stroke_width).max(LogicalLength::zero());
171 let offset =
172 LogicalVector::from_lengths(stroke_width / 2 as Coord, stroke_width / 2 as Coord);
173
174 let viewbox_width = self.viewbox_width();
175 let viewbox_height = self.viewbox_height();
176
177 let maybe_viewbox = if viewbox_width > 0. && viewbox_height > 0. {
178 Some(
179 euclid::rect(self.viewbox_x(), self.viewbox_y(), viewbox_width, viewbox_height)
180 .to_box2d(),
181 )
182 } else {
183 None
184 };
185
186 elements_iter.fit(
187 bounds_width.get() as _,
188 bounds_height.get() as _,
189 maybe_viewbox,
190 self.fit(),
191 );
192 (offset, elements_iter).into()
193 }
194}
195
196impl ItemConsts for Path {
197 const cached_rendering_data_offset: const_field_offset::FieldOffset<Path, CachedRenderingData> =
198 Path::FIELD_OFFSETS.cached_rendering_data().as_unpinned_projection();
199}