1use crate::api::Value;
5use crate::dynamic_item_tree::{
6 ErasedItemTreeBox, ErasedItemTreeDescription, PopupMenuDescription,
7};
8use crate::SetPropertyError;
9use core::cell::RefCell;
10use core::pin::Pin;
11use i_slint_compiler::langtype::ElementType;
12use i_slint_compiler::namedreference::NamedReference;
13use i_slint_compiler::object_tree::{Component, Document, PropertyDeclaration};
14use i_slint_core::item_tree::ItemTreeVTable;
15use i_slint_core::rtti;
16use smol_str::SmolStr;
17use std::collections::{BTreeMap, HashMap};
18use std::rc::Rc;
19
20pub struct CompiledGlobalCollection {
21 pub compiled_globals: Vec<CompiledGlobal>,
23 pub exported_globals_by_name: BTreeMap<SmolStr, usize>,
26}
27
28impl CompiledGlobalCollection {
29 pub fn compile(doc: &Document) -> Self {
30 let mut exported_globals_by_name = BTreeMap::new();
31 let compiled_globals = doc
32 .used_types
33 .borrow()
34 .globals
35 .iter()
36 .enumerate()
37 .map(|(index, component)| {
38 let mut global = generate(component);
39
40 if !component.exported_global_names.borrow().is_empty() {
41 global.extend_public_properties(
42 component.root_element.borrow().property_declarations.clone(),
43 );
44
45 exported_globals_by_name.extend(
46 component
47 .exported_global_names
48 .borrow()
49 .iter()
50 .map(|exported_name| (exported_name.name.clone(), index)),
51 )
52 }
53
54 global
55 })
56 .collect();
57 Self { compiled_globals, exported_globals_by_name }
58 }
59}
60
61#[derive(Clone)]
62pub enum GlobalStorage {
63 Strong(Rc<RefCell<HashMap<String, Pin<Rc<dyn GlobalComponent>>>>>),
64 Weak(std::rc::Weak<RefCell<HashMap<String, Pin<Rc<dyn GlobalComponent>>>>>),
66}
67
68impl GlobalStorage {
69 pub fn get(&self, name: &str) -> Option<Pin<Rc<dyn GlobalComponent>>> {
70 match self {
71 GlobalStorage::Strong(storage) => storage.borrow().get(name).cloned(),
72 GlobalStorage::Weak(storage) => storage.upgrade().unwrap().borrow().get(name).cloned(),
73 }
74 }
75}
76
77impl Default for GlobalStorage {
78 fn default() -> Self {
79 GlobalStorage::Strong(Default::default())
80 }
81}
82
83pub enum CompiledGlobal {
84 Builtin {
85 name: SmolStr,
86 element: Rc<i_slint_compiler::langtype::BuiltinElement>,
87 public_properties: BTreeMap<SmolStr, PropertyDeclaration>,
89 _original: Rc<Component>,
91 },
92 Component {
93 component: ErasedItemTreeDescription,
94 public_properties: BTreeMap<SmolStr, PropertyDeclaration>,
95 },
96}
97
98impl CompiledGlobal {
99 pub fn names(&self) -> Vec<SmolStr> {
100 match self {
101 CompiledGlobal::Builtin { name, .. } => vec![name.clone()],
102 CompiledGlobal::Component { component, .. } => {
103 generativity::make_guard!(guard);
104 let component = component.unerase(guard);
105 let mut names = component.original.global_aliases();
106 names.push(component.original.root_element.borrow().original_name());
107 names
108 }
109 }
110 }
111
112 pub fn visible_in_public_api(&self) -> bool {
113 match self {
114 CompiledGlobal::Builtin { .. } => false,
115 CompiledGlobal::Component { component, .. } => {
116 generativity::make_guard!(guard);
117 let component = component.unerase(guard);
118 let is_exported = !component.original.exported_global_names.borrow().is_empty();
119 is_exported
120 }
121 }
122 }
123
124 pub fn public_properties(&self) -> impl Iterator<Item = (&SmolStr, &PropertyDeclaration)> + '_ {
125 match self {
126 CompiledGlobal::Builtin { public_properties, .. } => public_properties.iter(),
127 CompiledGlobal::Component { public_properties, .. } => public_properties.iter(),
128 }
129 }
130
131 pub fn extend_public_properties(
132 &mut self,
133 iter: impl IntoIterator<Item = (SmolStr, PropertyDeclaration)>,
134 ) {
135 match self {
136 CompiledGlobal::Builtin { public_properties, .. } => public_properties.extend(iter),
137 CompiledGlobal::Component { public_properties, .. } => public_properties.extend(iter),
138 }
139 }
140}
141
142pub trait GlobalComponent {
143 fn invoke_callback(
144 self: Pin<&Self>,
145 callback_name: &SmolStr,
146 args: &[Value],
147 ) -> Result<Value, ()>;
148
149 fn set_callback_handler(
150 self: Pin<&Self>,
151 callback_name: &str,
152 handler: Box<dyn Fn(&[Value]) -> Value>,
153 ) -> Result<(), ()>;
154
155 fn set_property(
156 self: Pin<&Self>,
157 prop_name: &str,
158 value: Value,
159 ) -> Result<(), SetPropertyError>;
160 fn get_property(self: Pin<&Self>, prop_name: &str) -> Result<Value, ()>;
161
162 fn get_property_ptr(self: Pin<&Self>, prop_name: &SmolStr) -> *const ();
163
164 fn eval_function(self: Pin<&Self>, fn_name: &str, args: Vec<Value>) -> Result<Value, ()>;
165}
166
167pub fn instantiate(
169 description: &CompiledGlobal,
170 globals: &mut GlobalStorage,
171 root: vtable::VWeak<ItemTreeVTable, ErasedItemTreeBox>,
172) {
173 let GlobalStorage::Strong(ref mut globals) = globals else {
174 panic!("Global storage is not strong")
175 };
176
177 let instance = match description {
178 CompiledGlobal::Builtin { element, .. } => {
179 trait Helper {
180 fn instantiate(name: &str) -> Pin<Rc<dyn GlobalComponent>> {
181 panic!("Cannot find native global {name}")
182 }
183 }
184 impl Helper for () {}
185 impl<T: rtti::BuiltinGlobal + 'static, Next: Helper> Helper for (T, Next) {
186 fn instantiate(name: &str) -> Pin<Rc<dyn GlobalComponent>> {
187 if name == T::name() {
188 T::new()
189 } else {
190 Next::instantiate(name)
191 }
192 }
193 }
194 i_slint_backend_selector::NativeGlobals::instantiate(
195 element.native_class.class_name.as_ref(),
196 )
197 }
198 CompiledGlobal::Component { component, .. } => {
199 generativity::make_guard!(guard);
200 let description = component.unerase(guard);
201 let inst = crate::dynamic_item_tree::instantiate(
202 description.clone(),
203 None,
204 Some(root),
205 None,
206 GlobalStorage::Weak(Rc::downgrade(globals)),
207 );
208 inst.run_setup_code();
209 Rc::pin(GlobalComponentInstance(inst))
210 }
211 };
212
213 globals.borrow_mut().extend(
214 description
215 .names()
216 .iter()
217 .map(|name| (crate::normalize_identifier(name).to_string(), instance.clone())),
218 );
219}
220
221pub struct GlobalComponentInstance(vtable::VRc<ItemTreeVTable, ErasedItemTreeBox>);
224
225impl GlobalComponent for GlobalComponentInstance {
226 fn set_property(
227 self: Pin<&Self>,
228 prop_name: &str,
229 value: Value,
230 ) -> Result<(), SetPropertyError> {
231 generativity::make_guard!(guard);
232 let comp = self.0.unerase(guard);
233 comp.description().set_property(comp.borrow(), prop_name, value)
234 }
235
236 fn get_property(self: Pin<&Self>, prop_name: &str) -> Result<Value, ()> {
237 generativity::make_guard!(guard);
238 let comp = self.0.unerase(guard);
239 comp.description().get_property(comp.borrow(), prop_name)
240 }
241
242 fn get_property_ptr(self: Pin<&Self>, prop_name: &SmolStr) -> *const () {
243 generativity::make_guard!(guard);
244 let comp = self.0.unerase(guard);
245 crate::dynamic_item_tree::get_property_ptr(
246 &NamedReference::new(&comp.description().original.root_element, prop_name.clone()),
247 comp.borrow_instance(),
248 )
249 }
250
251 fn invoke_callback(
252 self: Pin<&Self>,
253 callback_name: &SmolStr,
254 args: &[Value],
255 ) -> Result<Value, ()> {
256 generativity::make_guard!(guard);
257 let comp = self.0.unerase(guard);
258 comp.description().invoke(comp.borrow(), callback_name, args)
259 }
260
261 fn set_callback_handler(
262 self: Pin<&Self>,
263 callback_name: &str,
264 handler: Box<dyn Fn(&[Value]) -> Value>,
265 ) -> Result<(), ()> {
266 generativity::make_guard!(guard);
267 let comp = self.0.unerase(guard);
268 comp.description().set_callback_handler(comp.borrow(), callback_name, handler)
269 }
270
271 fn eval_function(self: Pin<&Self>, fn_name: &str, args: Vec<Value>) -> Result<Value, ()> {
272 generativity::make_guard!(guard);
273 let comp = self.0.unerase(guard);
274 let mut ctx =
275 crate::eval::EvalLocalContext::from_function_arguments(comp.borrow_instance(), args);
276 let result = crate::eval::eval_expression(
277 &comp
278 .description()
279 .original
280 .root_element
281 .borrow()
282 .bindings
283 .get(fn_name)
284 .ok_or(())?
285 .borrow()
286 .expression,
287 &mut ctx,
288 );
289 Ok(result)
290 }
291}
292
293impl<T: rtti::BuiltinItem + 'static> GlobalComponent for T {
294 fn set_property(
295 self: Pin<&Self>,
296 prop_name: &str,
297 value: Value,
298 ) -> Result<(), SetPropertyError> {
299 let prop = Self::properties()
300 .into_iter()
301 .find(|(k, _)| *k == prop_name)
302 .ok_or(SetPropertyError::NoSuchProperty)?
303 .1;
304 prop.set(self, value, None).map_err(|()| SetPropertyError::WrongType)
305 }
306
307 fn get_property(self: Pin<&Self>, prop_name: &str) -> Result<Value, ()> {
308 let prop = Self::properties().into_iter().find(|(k, _)| *k == prop_name).ok_or(())?.1;
309 prop.get(self)
310 }
311
312 fn get_property_ptr(self: Pin<&Self>, prop_name: &SmolStr) -> *const () {
313 let prop: &dyn rtti::PropertyInfo<Self, Value> =
314 Self::properties().into_iter().find(|(k, _)| *k == prop_name).unwrap().1;
315 unsafe { (self.get_ref() as *const Self as *const u8).add(prop.offset()) as *const () }
316 }
317
318 fn invoke_callback(
319 self: Pin<&Self>,
320 callback_name: &SmolStr,
321 args: &[Value],
322 ) -> Result<Value, ()> {
323 let cb = Self::callbacks().into_iter().find(|(k, _)| *k == callback_name).ok_or(())?.1;
324 cb.call(self, args)
325 }
326
327 fn set_callback_handler(
328 self: Pin<&Self>,
329 callback_name: &str,
330 handler: Box<dyn Fn(&[Value]) -> Value>,
331 ) -> Result<(), ()> {
332 let cb = Self::callbacks().into_iter().find(|(k, _)| *k == callback_name).ok_or(())?.1;
333 cb.set_handler(self, handler)
334 }
335
336 fn eval_function(self: Pin<&Self>, _fn_name: &str, _args: Vec<Value>) -> Result<Value, ()> {
337 Err(())
338 }
339}
340
341fn generate(component: &Rc<Component>) -> CompiledGlobal {
342 debug_assert!(component.is_global());
343 match &component.root_element.borrow().base_type {
344 ElementType::Global => {
345 generativity::make_guard!(guard);
346 CompiledGlobal::Component {
347 component: crate::dynamic_item_tree::generate_item_tree(
348 component,
349 None,
350 PopupMenuDescription::Weak(Default::default()),
351 false,
352 guard,
353 )
354 .into(),
355 public_properties: Default::default(),
356 }
357 }
358 ElementType::Builtin(b) => CompiledGlobal::Builtin {
359 name: component.id.clone(),
360 element: b.clone(),
361 public_properties: Default::default(),
362 _original: component.clone(),
363 },
364 ElementType::Error | ElementType::Native(_) | ElementType::Component(_) => unreachable!(),
365 }
366}