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