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