1use crate::dynamic_item_tree::{DynamicComponentVRc, ItemTreeBox};
7use i_slint_compiler::object_tree::{Component, Element, ElementRc};
8use i_slint_core::graphics::euclid;
9use i_slint_core::items::ItemRc;
10use i_slint_core::lengths::{LogicalPoint, LogicalRect};
11use smol_str::SmolStr;
12use std::cell::RefCell;
13use std::path::Path;
14use std::rc::Rc;
15use vtable::VRc;
16
17fn normalize_repeated_element(element: ElementRc) -> ElementRc {
18 if element.borrow().repeated.is_some()
19 && let i_slint_compiler::langtype::ElementType::Component(base) =
20 &element.borrow().base_type
21 && base.parent_element().is_some()
22 {
23 return base.root_element.clone();
24 }
25
26 element
27}
28
29#[derive(Clone, Copy, Debug, Default)]
31pub struct HighlightedRect {
32 pub rect: LogicalRect,
34 pub angle: f32,
36}
37impl HighlightedRect {
38 pub fn contains(&self, position: LogicalPoint) -> bool {
40 let center = self.rect.center();
41 let rotation = euclid::Rotation2D::radians((-self.angle).to_radians());
42 let transformed = center + rotation.transform_vector(position - center);
43 self.rect.contains(transformed)
44 }
45}
46
47fn collect_highlight_data(
48 component: &DynamicComponentVRc,
49 elements: &[std::rc::Weak<RefCell<Element>>],
50) -> Vec<HighlightedRect> {
51 let component_instance = VRc::downgrade(component);
52 let component_instance = component_instance.upgrade().unwrap();
53 generativity::make_guard!(guard);
54 let c = component_instance.unerase(guard);
55 let mut values = Vec::new();
56 for element in elements.iter().filter_map(|e| e.upgrade()) {
57 let element = normalize_repeated_element(element);
58 if let Some(repeater_path) = repeater_path(&element) {
59 fill_highlight_data(
60 &repeater_path,
61 &element,
62 &c,
63 &c,
64 ElementPositionFilter::IncludeClipped,
65 &mut values,
66 );
67 }
68 }
69 values
70}
71
72pub(crate) fn component_positions(
73 component_instance: &DynamicComponentVRc,
74 path: &Path,
75 offset: u32,
76) -> Vec<HighlightedRect> {
77 generativity::make_guard!(guard);
78 let c = component_instance.unerase(guard);
79
80 let elements =
81 find_element_node_at_source_code_position(&c.description().original, path, offset);
82 collect_highlight_data(
83 component_instance,
84 &elements.into_iter().map(|(e, _)| Rc::downgrade(&e)).collect::<Vec<_>>(),
85 )
86}
87
88#[derive(Copy, Clone, Eq, PartialEq)]
90pub enum ElementPositionFilter {
91 IncludeClipped,
93 ExcludeClipped,
95}
96
97pub fn element_positions(
99 component_instance: &DynamicComponentVRc,
100 element: &ElementRc,
101 filter_clipped: ElementPositionFilter,
102) -> Vec<HighlightedRect> {
103 generativity::make_guard!(guard);
104 let c = component_instance.unerase(guard);
105
106 let mut values = Vec::new();
107
108 let element = normalize_repeated_element(element.clone());
109 if let Some(repeater_path) = repeater_path(&element) {
110 fill_highlight_data(&repeater_path, &element, &c, &c, filter_clipped, &mut values);
111 }
112 values
113}
114
115pub(crate) fn element_node_at_source_code_position(
116 component_instance: &DynamicComponentVRc,
117 path: &Path,
118 offset: u32,
119) -> Vec<(ElementRc, usize)> {
120 generativity::make_guard!(guard);
121 let c = component_instance.unerase(guard);
122
123 find_element_node_at_source_code_position(&c.description().original, path, offset)
124}
125
126fn fill_highlight_data(
127 repeater_path: &[SmolStr],
128 element: &ElementRc,
129 component_instance: &ItemTreeBox,
130 root_component_instance: &ItemTreeBox,
131 filter_clipped: ElementPositionFilter,
132 values: &mut Vec<HighlightedRect>,
133) {
134 if element.borrow().repeated.is_some() {
135 return;
137 }
138
139 if let [first, rest @ ..] = repeater_path {
140 generativity::make_guard!(guard);
141 let rep = crate::dynamic_item_tree::get_repeater_by_name(
142 component_instance.borrow_instance(),
143 first.as_str(),
144 guard,
145 );
146 for idx in rep.0.range() {
147 if let Some(c) = rep.0.instance_at(idx) {
148 generativity::make_guard!(guard);
149 fill_highlight_data(
150 rest,
151 element,
152 &c.unerase(guard),
153 root_component_instance,
154 filter_clipped,
155 values,
156 );
157 }
158 }
159 } else {
160 let vrc = VRc::into_dyn(
161 component_instance.borrow_instance().self_weak().get().unwrap().upgrade().unwrap(),
162 );
163 let root_vrc = VRc::into_dyn(
164 root_component_instance.borrow_instance().self_weak().get().unwrap().upgrade().unwrap(),
165 );
166 let index = element.borrow().item_index.get().copied().unwrap();
167 let item_rc = ItemRc::new(vrc.clone(), index);
168 if filter_clipped == ElementPositionFilter::IncludeClipped || item_rc.is_visible() {
169 let geometry = item_rc.geometry();
170 if geometry.size.is_empty() {
171 return;
172 }
173 let origin = item_rc.map_to_item_tree(geometry.origin, &root_vrc);
174 let top_right = item_rc.map_to_item_tree(
175 geometry.origin + euclid::vec2(geometry.size.width, 0.),
176 &root_vrc,
177 );
178 let delta = top_right - origin;
179 let width = delta.length();
180 let height = geometry.size.height * width / geometry.size.width;
181 let angle_rad = delta.y.atan2(delta.x);
183 let (sin, cos) = angle_rad.sin_cos();
184 let center = euclid::point2(
185 origin.x + (width / 2.0) * cos - (height / 2.0) * sin,
186 origin.y + (width / 2.0) * sin + (height / 2.0) * cos,
187 );
188 values.push(HighlightedRect {
189 rect: LogicalRect {
190 origin: center - euclid::vec2(width / 2.0, height / 2.0),
191 size: euclid::size2(width, height),
192 },
193 angle: angle_rad.to_degrees(),
194 });
195 }
196 }
197}
198
199fn find_element_node_at_source_code_position(
201 component: &Rc<Component>,
202 path: &Path,
203 offset: u32,
204) -> Vec<(ElementRc, usize)> {
205 let mut result = Vec::new();
206 i_slint_compiler::object_tree::recurse_elem_including_sub_components(
207 component,
208 &(),
209 &mut |elem, &()| {
210 if elem.borrow().repeated.is_some() {
211 return;
212 }
213 for (index, node_path, node_range) in
214 elem.borrow().debug.iter().enumerate().map(|(i, n)| {
215 let text_range = n
216 .node
217 .QualifiedName()
218 .map(|n| n.text_range())
219 .or_else(|| {
220 n.node
221 .child_token(i_slint_compiler::parser::SyntaxKind::LBrace)
222 .map(|n| n.text_range())
223 })
224 .expect("A Element must contain a LBrace somewhere pretty early");
225
226 (i, n.node.source_file.path(), text_range)
227 })
228 {
229 if node_path == path && node_range.contains(offset.into()) {
230 result.push((elem.clone(), index));
231 }
232 }
233 },
234 );
235 result
236}
237
238fn repeater_path(elem: &ElementRc) -> Option<Vec<SmolStr>> {
239 let enclosing = elem.borrow().enclosing_component.upgrade().unwrap();
240 if let Some(parent) = enclosing.parent_element() {
241 parent.borrow().repeated.as_ref()?;
243
244 let mut r = repeater_path(&parent)?;
245 r.push(parent.borrow().id.clone());
246 Some(r)
247 } else {
248 Some(Vec::new())
249 }
250}