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