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