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