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