slint_interpreter/
highlight.rs1use crate::dynamic_item_tree::{DynamicComponentVRc, ItemTreeBox};
7use i_slint_compiler::object_tree::{Component, Element, ElementRc};
8use i_slint_core::items::ItemRc;
9use i_slint_core::lengths::LogicalRect;
10use smol_str::SmolStr;
11use std::cell::RefCell;
12use std::path::Path;
13use std::rc::Rc;
14use vtable::VRc;
15
16fn normalize_repeated_element(element: ElementRc) -> ElementRc {
17 if element.borrow().repeated.is_some() {
18 if let i_slint_compiler::langtype::ElementType::Component(base) =
19 &element.borrow().base_type
20 {
21 if base.parent_element.upgrade().is_some() {
22 return base.root_element.clone();
23 }
24 }
25 }
26
27 element
28}
29
30fn collect_highlight_data(
31 component: &DynamicComponentVRc,
32 elements: &[std::rc::Weak<RefCell<Element>>],
33) -> Vec<i_slint_core::lengths::LogicalRect> {
34 let component_instance = VRc::downgrade(component);
35 let component_instance = component_instance.upgrade().unwrap();
36 generativity::make_guard!(guard);
37 let c = component_instance.unerase(guard);
38 let mut values = Vec::new();
39 for element in elements.iter().filter_map(|e| e.upgrade()) {
40 let element = normalize_repeated_element(element);
41 if let Some(repeater_path) = repeater_path(&element) {
42 fill_highlight_data(
43 &repeater_path,
44 &element,
45 &c,
46 &c,
47 ElementPositionFilter::IncludeClipped,
48 &mut values,
49 );
50 }
51 }
52 values
53}
54
55pub(crate) fn component_positions(
56 component_instance: &DynamicComponentVRc,
57 path: &Path,
58 offset: u32,
59) -> Vec<i_slint_core::lengths::LogicalRect> {
60 generativity::make_guard!(guard);
61 let c = component_instance.unerase(guard);
62
63 let elements =
64 find_element_node_at_source_code_position(&c.description().original, path, offset);
65 collect_highlight_data(
66 component_instance,
67 &elements.into_iter().map(|(e, _)| Rc::downgrade(&e)).collect::<Vec<_>>(),
68 )
69}
70
71#[derive(Copy, Clone, Eq, PartialEq)]
73pub enum ElementPositionFilter {
74 IncludeClipped,
76 ExcludeClipped,
78}
79
80pub fn element_positions(
82 component_instance: &DynamicComponentVRc,
83 element: &ElementRc,
84 filter_clipped: ElementPositionFilter,
85) -> Vec<LogicalRect> {
86 generativity::make_guard!(guard);
87 let c = component_instance.unerase(guard);
88
89 let mut values = Vec::new();
90
91 let element = normalize_repeated_element(element.clone());
92 if let Some(repeater_path) = repeater_path(&element) {
93 fill_highlight_data(&repeater_path, &element, &c, &c, filter_clipped, &mut values);
94 }
95 values
96}
97
98pub(crate) fn element_node_at_source_code_position(
99 component_instance: &DynamicComponentVRc,
100 path: &Path,
101 offset: u32,
102) -> Vec<(ElementRc, usize)> {
103 generativity::make_guard!(guard);
104 let c = component_instance.unerase(guard);
105
106 find_element_node_at_source_code_position(&c.description().original, path, offset)
107}
108
109fn fill_highlight_data(
110 repeater_path: &[SmolStr],
111 element: &ElementRc,
112 component_instance: &ItemTreeBox,
113 root_component_instance: &ItemTreeBox,
114 filter_clipped: ElementPositionFilter,
115 values: &mut Vec<i_slint_core::lengths::LogicalRect>,
116) {
117 if element.borrow().repeated.is_some() {
118 return;
120 }
121
122 if let [first, rest @ ..] = repeater_path {
123 generativity::make_guard!(guard);
124 let rep = crate::dynamic_item_tree::get_repeater_by_name(
125 component_instance.borrow_instance(),
126 first.as_str(),
127 guard,
128 );
129 for idx in rep.0.range() {
130 if let Some(c) = rep.0.instance_at(idx) {
131 generativity::make_guard!(guard);
132 fill_highlight_data(
133 rest,
134 element,
135 &c.unerase(guard),
136 root_component_instance,
137 filter_clipped,
138 values,
139 );
140 }
141 }
142 } else {
143 let vrc = VRc::into_dyn(
144 component_instance.borrow_instance().self_weak().get().unwrap().upgrade().unwrap(),
145 );
146 let root_vrc = VRc::into_dyn(
147 root_component_instance.borrow_instance().self_weak().get().unwrap().upgrade().unwrap(),
148 );
149 let index = element.borrow().item_index.get().copied().unwrap();
150 let item_rc = ItemRc::new(vrc.clone(), index);
151 if filter_clipped == ElementPositionFilter::IncludeClipped || item_rc.is_visible() {
152 let geometry = item_rc.geometry();
153 let origin = item_rc.map_to_item_tree(geometry.origin, &root_vrc);
154 let size = geometry.size;
155 values.push(LogicalRect { origin, size });
156 }
157 }
158}
159
160fn find_element_node_at_source_code_position(
162 component: &Rc<Component>,
163 path: &Path,
164 offset: u32,
165) -> Vec<(ElementRc, usize)> {
166 let mut result = Vec::new();
167 i_slint_compiler::object_tree::recurse_elem_including_sub_components(
168 component,
169 &(),
170 &mut |elem, &()| {
171 if elem.borrow().repeated.is_some() {
172 return;
173 }
174 for (index, node_path, node_range) in
175 elem.borrow().debug.iter().enumerate().map(|(i, n)| {
176 let text_range = n
177 .node
178 .QualifiedName()
179 .map(|n| n.text_range())
180 .or_else(|| {
181 n.node
182 .child_token(i_slint_compiler::parser::SyntaxKind::LBrace)
183 .map(|n| n.text_range())
184 })
185 .expect("A Element must contain a LBrace somewhere pretty early");
186
187 (i, n.node.source_file.path(), text_range)
188 })
189 {
190 if node_path == path && node_range.contains(offset.into()) {
191 result.push((elem.clone(), index));
192 }
193 }
194 },
195 );
196 result
197}
198
199fn repeater_path(elem: &ElementRc) -> Option<Vec<SmolStr>> {
200 let enclosing = elem.borrow().enclosing_component.upgrade().unwrap();
201 if let Some(parent) = enclosing.parent_element.upgrade() {
202 parent.borrow().repeated.as_ref()?;
204
205 let mut r = repeater_path(&parent)?;
206 r.push(parent.borrow().id.clone());
207 Some(r)
208 } else {
209 Some(vec![])
210 }
211}