1#![allow(clippy::mutable_key_type)] use crate::expression_tree::{Expression, NamedReference};
9use crate::langtype::ElementType;
10use crate::object_tree::*;
11use by_address::ByAddress;
12use core::cell::RefCell;
13use smol_str::{SmolStr, format_smolstr};
14use std::collections::{BTreeMap, HashMap, HashSet};
15use std::rc::Rc;
16
17type RenameMap = HashMap<(ByAddress<ElementRc>, SmolStr), SmolStr>;
23
24struct Declarations {
25 property_declarations: BTreeMap<SmolStr, PropertyDeclaration>,
26}
27impl Declarations {
28 fn take_from_element(e: &mut Element) -> Self {
29 e.shadowing_members.clear();
31 Declarations { property_declarations: core::mem::take(&mut e.property_declarations) }
32 }
33}
34
35pub fn move_declarations(component: &Rc<Component>) {
36 simplify_optimized_items_recursive(component);
37 let mut renames = RenameMap::new();
38 collect_renames(component, &mut renames);
39 do_move_declarations(component, &renames);
40}
41
42fn collect_renames(component: &Rc<Component>, renames: &mut RenameMap) {
44 let mut elements = Vec::new();
45 recurse_elem(&component.root_element, &(), &mut |elem, _| {
46 if elem.borrow().repeated.is_some() {
47 if let ElementType::Component(base) = &elem.borrow().base_type {
48 collect_renames(base, renames);
49 }
50 } else if !Rc::ptr_eq(elem, &component.root_element) {
51 elements.push(elem.clone());
52 }
53 });
54 elements.extend(component.optimized_elements.borrow().iter().cloned());
55
56 let mut used: HashSet<SmolStr> =
57 component.root_element.borrow().property_declarations.keys().cloned().collect();
58 for elem in elements {
59 for prop in elem.borrow().property_declarations.keys() {
60 let base = map_name(&elem, prop);
61 let mut name = base.clone();
62 let mut suffix = 1;
63 while !used.insert(name.clone()) {
64 name = format_smolstr!("{base}-{suffix}");
65 suffix += 1;
66 }
67 renames.insert((ByAddress(elem.clone()), prop.clone()), name);
68 }
69 }
70
71 component.popup_windows.borrow().iter().for_each(|p| collect_renames(&p.component, renames));
72 component.menu_item_tree.borrow().iter().for_each(|c| collect_renames(c, renames));
73}
74
75fn do_move_declarations(component: &Rc<Component>, renames: &RenameMap) {
76 let mut decl = Declarations::take_from_element(&mut component.root_element.borrow_mut());
77 component
78 .popup_windows
79 .borrow()
80 .iter()
81 .for_each(|f| do_move_declarations(&f.component, renames));
82 component.menu_item_tree.borrow().iter().for_each(|c| do_move_declarations(c, renames));
83
84 let mut new_root_bindings = HashMap::new();
85 let mut new_root_change_callbacks = HashMap::new();
86 let mut new_root_property_analysis = BTreeMap::new();
87
88 let move_bindings_and_animations = &mut |elem: &ElementRc| {
89 visit_all_named_references_in_element(elem, |nr| fixup_reference(nr, renames));
90
91 if elem.borrow().repeated.is_some() {
92 if let ElementType::Component(base) = &elem.borrow().base_type {
93 do_move_declarations(base, renames);
94 } else {
95 panic!(
96 "Repeated element should have a component as base because of the repeater_component.rs pass"
97 )
98 }
99 debug_assert!(
100 elem.borrow().property_declarations.is_empty() && elem.borrow().children.is_empty(),
101 "Repeated element should be empty because of the repeater_component.rs pass"
102 );
103 return;
104 }
105
106 let bindings = elem.borrow_mut().take_bindings_including_synthetic();
108 let mut new_bindings = BindingsMap::default();
109 for (k, e) in bindings {
110 let will_be_moved = elem.borrow().property_declarations.contains_key(&k);
111 if will_be_moved {
112 new_root_bindings.insert(moved_name(renames, elem, &k), e);
113 } else {
114 new_bindings.insert(k, e);
115 }
116 }
117 elem.borrow_mut().extend_bindings_including_synthetic(new_bindings);
118
119 let property_analysis = elem.borrow().property_analysis.take();
120 let mut new_property_analysis = BTreeMap::new();
121 for (prop, a) in property_analysis {
122 let will_be_moved = elem.borrow().property_declarations.contains_key(&prop);
123 if will_be_moved {
124 new_root_property_analysis.insert(moved_name(renames, elem, &prop), a);
125 } else {
126 new_property_analysis.insert(prop, a);
127 }
128 }
129 *elem.borrow().property_analysis.borrow_mut() = new_property_analysis;
130
131 let change_callbacks = core::mem::take(&mut elem.borrow_mut().change_callbacks);
133 let mut new_change_callbacks = BTreeMap::<SmolStr, RefCell<Vec<Expression>>>::default();
134 for (k, e) in change_callbacks {
135 let will_be_moved = elem.borrow().property_declarations.contains_key(&k);
136 if will_be_moved {
137 new_root_change_callbacks.insert(moved_name(renames, elem, &k), e);
138 } else {
139 new_change_callbacks.insert(k, e);
140 }
141 }
142 elem.borrow_mut().change_callbacks = new_change_callbacks;
143 };
144
145 component.optimized_elements.borrow().iter().for_each(&mut *move_bindings_and_animations);
146 recurse_elem(&component.root_element, &(), &mut |e, _| move_bindings_and_animations(e));
147
148 component
149 .root_constraints
150 .borrow_mut()
151 .visit_named_references(&mut |nr| fixup_reference(nr, renames));
152 component.popup_windows.borrow_mut().iter_mut().for_each(|p| {
153 fixup_reference(&mut p.x, renames);
154 fixup_reference(&mut p.y, renames);
155 if let Some(is_open) = &mut p.is_open {
159 fixup_reference(is_open, renames);
160 }
161 visit_all_named_references(&p.component, &mut |nr| fixup_reference(nr, renames))
162 });
163 component.timers.borrow_mut().iter_mut().for_each(|t| {
164 fixup_reference(&mut t.interval, renames);
165 fixup_reference(&mut t.running, renames);
166 fixup_reference(&mut t.triggered, renames);
167 });
168 component.menu_item_tree.borrow_mut().iter_mut().for_each(|c| {
169 visit_all_named_references(c, &mut |nr| fixup_reference(nr, renames));
170 });
171 component.init_code.borrow_mut().iter_mut().for_each(|expr| {
172 visit_named_references_in_expression(expr, &mut |nr| fixup_reference(nr, renames));
173 });
174 for pd in decl.property_declarations.values_mut() {
175 if let Some(nr) = pd.is_alias.as_mut() {
176 fixup_reference(nr, renames)
177 }
178 }
179
180 let move_properties = &mut |elem: &ElementRc| {
181 let elem_decl = Declarations::take_from_element(&mut elem.borrow_mut());
182 decl.property_declarations.extend(elem_decl.property_declarations.into_iter().map(
183 |(p, mut d)| {
184 let name = moved_name(renames, elem, &p);
185 d.moved_from = Some(p);
186 (name, d)
187 },
188 ));
189 };
190
191 recurse_elem(&component.root_element, &(), &mut |elem, _| move_properties(elem));
192
193 component.optimized_elements.borrow().iter().for_each(move_properties);
194
195 {
196 let mut r = component.root_element.borrow_mut();
197 r.property_declarations = decl.property_declarations;
198 r.extend_bindings_including_synthetic(new_root_bindings);
199 r.property_analysis.borrow_mut().extend(new_root_property_analysis);
200 r.change_callbacks.extend(new_root_change_callbacks);
201 }
202}
203
204fn fixup_reference(nr: &mut NamedReference, renames: &RenameMap) {
206 let e = nr.element();
207 let parent_component = e.borrow().enclosing_component.upgrade().unwrap();
208 if !Rc::ptr_eq(&e, &parent_component.root_element)
209 && e.borrow().property_declarations.contains_key(nr.name())
210 {
211 *nr =
212 NamedReference::new(&parent_component.root_element, moved_name(renames, &e, nr.name()));
213 }
214}
215
216fn map_name(e: &ElementRc, s: &SmolStr) -> SmolStr {
217 format_smolstr!("{}-{}", e.borrow().id, s)
218}
219
220fn moved_name(renames: &RenameMap, e: &ElementRc, s: &SmolStr) -> SmolStr {
221 renames.get(&(ByAddress(e.clone()), s.clone())).cloned().unwrap_or_else(|| map_name(e, s))
222}
223
224fn simplify_optimized_items_recursive(component: &Rc<Component>) {
225 simplify_optimized_items(component.optimized_elements.borrow().as_slice());
226 component
227 .popup_windows
228 .borrow()
229 .iter()
230 .for_each(|f| simplify_optimized_items_recursive(&f.component));
231 recurse_elem(&component.root_element, &(), &mut |elem, _| {
232 if elem.borrow().repeated.is_some()
233 && let ElementType::Component(base) = &elem.borrow().base_type
234 {
235 simplify_optimized_items_recursive(base);
236 }
237 });
238}
239
240fn simplify_optimized_items(items: &[ElementRc]) {
244 for elem in items {
245 recurse_elem(elem, &(), &mut |elem, _| {
246 let base = core::mem::take(&mut elem.borrow_mut().base_type);
247 if let ElementType::Builtin(c) = base {
248 elem.borrow_mut().property_declarations.extend(c.properties.iter().map(
250 |(k, v)| {
251 (
252 k.clone(),
253 PropertyDeclaration {
254 property_type: v.ty.clone(),
255 ..Default::default()
256 },
257 )
258 },
259 ));
260 } else {
261 unreachable!("Only builtin items should be optimized")
262 }
263 })
264 }
265}