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