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
93impl Default for GlobalStorage {
94 fn default() -> Self {
95 GlobalStorage::Strong(Default::default())
96 }
97}
98
99pub enum CompiledGlobal {
100 Builtin {
101 name: SmolStr,
102 element: Rc<i_slint_compiler::langtype::BuiltinElement>,
103 public_properties: BTreeMap<SmolStr, PropertyDeclaration>,
105 _original: Rc<Component>,
107 },
108 Component {
109 component: ErasedItemTreeDescription,
110 public_properties: BTreeMap<SmolStr, PropertyDeclaration>,
111 },
112}
113
114impl CompiledGlobal {
115 pub fn names(&self) -> Vec<SmolStr> {
116 match self {
117 CompiledGlobal::Builtin { name, .. } => vec![name.clone()],
118 CompiledGlobal::Component { component, .. } => {
119 generativity::make_guard!(guard);
120 let component = component.unerase(guard);
121 let mut names = component.original.global_aliases();
122 names.push(component.original.root_element.borrow().original_name());
123 names
124 }
125 }
126 }
127
128 pub fn visible_in_public_api(&self) -> bool {
129 match self {
130 CompiledGlobal::Builtin { .. } => false,
131 CompiledGlobal::Component { component, .. } => {
132 generativity::make_guard!(guard);
133 let component = component.unerase(guard);
134 !component.original.exported_global_names.borrow().is_empty()
135 }
136 }
137 }
138
139 pub fn public_properties(&self) -> impl Iterator<Item = (&SmolStr, &PropertyDeclaration)> + '_ {
140 match self {
141 CompiledGlobal::Builtin { public_properties, .. } => public_properties.iter(),
142 CompiledGlobal::Component { public_properties, .. } => public_properties.iter(),
143 }
144 }
145
146 pub fn extend_public_properties(
147 &mut self,
148 iter: impl IntoIterator<Item = (SmolStr, PropertyDeclaration)>,
149 ) {
150 match self {
151 CompiledGlobal::Builtin { public_properties, .. } => public_properties.extend(iter),
152 CompiledGlobal::Component { public_properties, .. } => public_properties.extend(iter),
153 }
154 }
155}
156
157pub trait GlobalComponent {
158 fn invoke_callback(
159 self: Pin<&Self>,
160 callback_name: &SmolStr,
161 args: &[Value],
162 ) -> Result<Value, ()>;
163
164 fn set_callback_handler(
165 self: Pin<&Self>,
166 callback_name: &str,
167 handler: Box<dyn Fn(&[Value]) -> Value>,
168 ) -> Result<(), ()>;
169
170 fn set_property(
171 self: Pin<&Self>,
172 prop_name: &str,
173 value: Value,
174 ) -> Result<(), SetPropertyError>;
175 fn get_property(self: Pin<&Self>, prop_name: &str) -> Result<Value, ()>;
176
177 fn get_property_ptr(self: Pin<&Self>, prop_name: &SmolStr) -> *const ();
178
179 fn eval_function(self: Pin<&Self>, fn_name: &str, args: Vec<Value>) -> Result<Value, ()>;
180
181 fn prepare_for_two_way_binding(
182 self: Pin<&Self>,
183 prop_name: &str,
184 ) -> Result<Pin<Rc<Property<Value>>>, ()>;
185}
186
187pub fn instantiate(
189 description: &CompiledGlobal,
190 globals: &GlobalStorage,
191 root: vtable::VWeak<ItemTreeVTable, ErasedItemTreeBox>,
192) {
193 let GlobalStorage::Strong(globals) = globals else { panic!("Global storage is not strong") };
194
195 let instance = match description {
196 CompiledGlobal::Builtin { element, .. } => {
197 trait Helper {
198 fn instantiate(name: &str) -> Pin<Rc<dyn GlobalComponent>> {
199 panic!("Cannot find native global {name}")
200 }
201 }
202 impl Helper for () {}
203 impl<T: rtti::BuiltinGlobal + 'static, Next: Helper> Helper for (T, Next) {
204 fn instantiate(name: &str) -> Pin<Rc<dyn GlobalComponent>> {
205 if name == T::name() { T::new() } else { Next::instantiate(name) }
206 }
207 }
208 i_slint_backend_selector::NativeGlobals::instantiate(
209 element.native_class.class_name.as_ref(),
210 )
211 }
212 CompiledGlobal::Component { component, .. } => {
213 generativity::make_guard!(guard);
214 let description = component.unerase(guard);
215 let inst = crate::dynamic_item_tree::instantiate(
216 description.clone(),
217 None,
218 Some(root),
219 None,
220 GlobalStorage::Weak(Rc::downgrade(globals)),
221 );
222 inst.run_setup_code();
223 Rc::pin(GlobalComponentInstance(inst))
224 }
225 };
226
227 globals.globals.borrow_mut().extend(
228 description
229 .names()
230 .iter()
231 .map(|name| (crate::normalize_identifier(name).to_string(), instance.clone())),
232 );
233}
234
235pub struct GlobalComponentInstance(vtable::VRc<ItemTreeVTable, ErasedItemTreeBox>);
238
239impl GlobalComponent for GlobalComponentInstance {
240 fn set_property(
241 self: Pin<&Self>,
242 prop_name: &str,
243 value: Value,
244 ) -> Result<(), SetPropertyError> {
245 generativity::make_guard!(guard);
246 let comp = self.0.unerase(guard);
247 comp.description().set_property(comp.borrow(), prop_name, value)
248 }
249
250 fn get_property(self: Pin<&Self>, prop_name: &str) -> Result<Value, ()> {
251 generativity::make_guard!(guard);
252 let comp = self.0.unerase(guard);
253 comp.description().get_property(comp.borrow(), prop_name)
254 }
255
256 fn get_property_ptr(self: Pin<&Self>, prop_name: &SmolStr) -> *const () {
257 generativity::make_guard!(guard);
258 let comp = self.0.unerase(guard);
259 crate::dynamic_item_tree::get_property_ptr(
260 &NamedReference::new(&comp.description().original.root_element, prop_name.clone()),
261 comp.borrow_instance(),
262 )
263 }
264
265 fn invoke_callback(
266 self: Pin<&Self>,
267 callback_name: &SmolStr,
268 args: &[Value],
269 ) -> Result<Value, ()> {
270 generativity::make_guard!(guard);
271 let comp = self.0.unerase(guard);
272 comp.description().invoke(comp.borrow(), callback_name, args)
273 }
274
275 fn set_callback_handler(
276 self: Pin<&Self>,
277 callback_name: &str,
278 handler: Box<dyn Fn(&[Value]) -> Value>,
279 ) -> Result<(), ()> {
280 generativity::make_guard!(guard);
281 let comp = self.0.unerase(guard);
282 comp.description().set_callback_handler(comp.borrow(), callback_name, handler)
283 }
284
285 fn eval_function(self: Pin<&Self>, fn_name: &str, args: Vec<Value>) -> Result<Value, ()> {
286 generativity::make_guard!(guard);
287 let comp = self.0.unerase(guard);
288 let mut ctx =
289 crate::eval::EvalLocalContext::from_function_arguments(comp.borrow_instance(), args);
290 let result = crate::eval::eval_expression(
291 &comp
292 .description()
293 .original
294 .root_element
295 .borrow()
296 .bindings
297 .get(fn_name)
298 .ok_or(())?
299 .borrow()
300 .expression,
301 &mut ctx,
302 );
303 Ok(result)
304 }
305
306 fn prepare_for_two_way_binding(
307 self: Pin<&Self>,
308 prop_name: &str,
309 ) -> Result<Pin<Rc<Property<Value>>>, ()> {
310 generativity::make_guard!(guard);
311 let comp = self.0.unerase(guard);
312 let description = comp.description();
313 let x = description.custom_properties.get(prop_name).ok_or(())?;
314 let item = unsafe { Pin::new_unchecked(&*comp.borrow_instance().as_ptr().add(x.offset)) };
315 Ok(x.prop.prepare_for_two_way_binding(item))
316 }
317}
318
319impl<T: rtti::BuiltinItem + 'static> GlobalComponent for T {
320 fn set_property(
321 self: Pin<&Self>,
322 prop_name: &str,
323 value: Value,
324 ) -> Result<(), SetPropertyError> {
325 let prop = Self::properties()
326 .into_iter()
327 .find(|(k, _)| *k == prop_name)
328 .ok_or(SetPropertyError::NoSuchProperty)?
329 .1;
330 prop.set(self, value, None).map_err(|()| SetPropertyError::WrongType)
331 }
332
333 fn get_property(self: Pin<&Self>, prop_name: &str) -> Result<Value, ()> {
334 let prop = Self::properties().into_iter().find(|(k, _)| *k == prop_name).ok_or(())?.1;
335 prop.get(self)
336 }
337
338 fn get_property_ptr(self: Pin<&Self>, prop_name: &SmolStr) -> *const () {
339 let prop: &dyn rtti::PropertyInfo<Self, Value> =
340 Self::properties().into_iter().find(|(k, _)| *k == prop_name).unwrap().1;
341 unsafe { (self.get_ref() as *const Self as *const u8).add(prop.offset()) as *const () }
342 }
343
344 fn invoke_callback(
345 self: Pin<&Self>,
346 callback_name: &SmolStr,
347 args: &[Value],
348 ) -> Result<Value, ()> {
349 let cb = Self::callbacks().into_iter().find(|(k, _)| *k == callback_name).ok_or(())?.1;
350 cb.call(self, args)
351 }
352
353 fn set_callback_handler(
354 self: Pin<&Self>,
355 callback_name: &str,
356 handler: Box<dyn Fn(&[Value]) -> Value>,
357 ) -> Result<(), ()> {
358 let cb = Self::callbacks().into_iter().find(|(k, _)| *k == callback_name).ok_or(())?.1;
359 cb.set_handler(self, handler)
360 }
361
362 fn eval_function(self: Pin<&Self>, _fn_name: &str, _args: Vec<Value>) -> Result<Value, ()> {
363 Err(())
364 }
365
366 fn prepare_for_two_way_binding(
367 self: Pin<&Self>,
368 prop_name: &str,
369 ) -> Result<Pin<Rc<Property<Value>>>, ()> {
370 Ok(Self::properties()
371 .into_iter()
372 .find(|(k, _)| *k == prop_name)
373 .ok_or(())?
374 .1
375 .prepare_for_two_way_binding(self))
376 }
377}
378
379fn generate(component: &Rc<Component>) -> CompiledGlobal {
380 debug_assert!(component.is_global());
381 match &component.root_element.borrow().base_type {
382 ElementType::Global => {
383 generativity::make_guard!(guard);
384 CompiledGlobal::Component {
385 component: crate::dynamic_item_tree::generate_item_tree(
386 component,
387 None,
388 PopupMenuDescription::Weak(Default::default()),
389 false,
390 guard,
391 )
392 .into(),
393 public_properties: Default::default(),
394 }
395 }
396 ElementType::Builtin(b) => CompiledGlobal::Builtin {
397 name: component.id.clone(),
398 element: b.clone(),
399 public_properties: Default::default(),
400 _original: component.clone(),
401 },
402 ElementType::Error
403 | ElementType::Interface
404 | ElementType::Native(_)
405 | ElementType::Component(_) => unreachable!(),
406 }
407}