1use crate::Value;
10use crate::erased::{SubComponentCallback, SubComponentProperty};
11use crate::eval::{EvalContext, eval_expression};
12use i_slint_compiler::llr::{CompilationUnit, GlobalIdx, LocalMemberIndex};
13use i_slint_core::rtti;
14use i_slint_core::{Callback, Property};
15use std::pin::Pin;
16use std::rc::Rc;
17use typed_index_collections::TiVec;
18
19pub trait NativeGlobal {
23 fn get_property(self: Pin<&Self>, name: &str) -> Option<Value>;
24 fn set_property(
25 self: Pin<&Self>,
26 name: &str,
27 value: Value,
28 animation: Option<i_slint_core::items::PropertyAnimation>,
29 ) -> Result<(), ()>;
30 fn invoke_callback(self: Pin<&Self>, name: &str, args: &[Value]) -> Option<Value>;
31 fn set_callback_handler(
32 self: Pin<&Self>,
33 name: &str,
34 handler: Box<dyn Fn(&[Value]) -> Value>,
35 ) -> Result<(), ()>;
36 fn prepare_property_for_two_way_binding(
37 self: Pin<&Self>,
38 name: &str,
39 ) -> Option<Pin<Rc<Property<Value>>>>;
40}
41
42impl<T: rtti::BuiltinGlobal + 'static> NativeGlobal for T {
43 fn get_property(self: Pin<&Self>, name: &str) -> Option<Value> {
44 let (_, prop) = T::properties::<Value>().into_iter().find(|(k, _)| *k == name)?;
45 prop.get(self).ok()
46 }
47
48 fn set_property(
49 self: Pin<&Self>,
50 name: &str,
51 value: Value,
52 animation: Option<i_slint_core::items::PropertyAnimation>,
53 ) -> Result<(), ()> {
54 let (_, prop) = T::properties::<Value>().into_iter().find(|(k, _)| *k == name).ok_or(())?;
55 prop.set(self, value, animation)
56 }
57
58 fn invoke_callback(self: Pin<&Self>, name: &str, args: &[Value]) -> Option<Value> {
59 let (_, cb) = T::callbacks::<Value>().into_iter().find(|(k, _)| *k == name)?;
60 cb.call(self, args).ok()
61 }
62
63 fn set_callback_handler(
64 self: Pin<&Self>,
65 name: &str,
66 handler: Box<dyn Fn(&[Value]) -> Value>,
67 ) -> Result<(), ()> {
68 let (_, cb) = T::callbacks::<Value>().into_iter().find(|(k, _)| *k == name).ok_or(())?;
69 cb.set_handler(self, handler)
70 }
71
72 fn prepare_property_for_two_way_binding(
73 self: Pin<&Self>,
74 name: &str,
75 ) -> Option<Pin<Rc<Property<Value>>>> {
76 let (_, prop) = T::properties::<Value>().into_iter().find(|(k, _)| *k == name)?;
77 Some(prop.prepare_for_two_way_binding(self))
78 }
79}
80
81fn instantiate_native_global(class_name: &str) -> Option<Pin<Rc<dyn NativeGlobal>>> {
84 trait Helper {
85 fn instantiate(_name: &str) -> Option<Pin<Rc<dyn NativeGlobal>>> {
86 None
87 }
88 }
89 impl Helper for () {}
90 impl<T: rtti::BuiltinGlobal + 'static, Next: Helper> Helper for (T, Next) {
91 fn instantiate(name: &str) -> Option<Pin<Rc<dyn NativeGlobal>>> {
92 if name == T::name() { Some(T::new()) } else { Next::instantiate(name) }
93 }
94 }
95 <i_slint_backend_selector::NativeGlobals as Helper>::instantiate(class_name)
96}
97
98pub struct GlobalInstance {
99 pub compilation_unit: Rc<CompilationUnit>,
100 pub global_idx: GlobalIdx,
101 pub properties: TiVec<i_slint_compiler::llr::PropertyIdx, SubComponentProperty>,
102 pub callbacks: TiVec<i_slint_compiler::llr::CallbackIdx, SubComponentCallback>,
103 pub callback_trackers: TiVec<
106 i_slint_compiler::llr::CallbackIdx,
107 Option<std::pin::Pin<Rc<i_slint_core::properties::Property<()>>>>,
108 >,
109 pub change_trackers: Vec<i_slint_core::properties::ChangeTracker>,
111 pub native: Option<Pin<Rc<dyn NativeGlobal>>>,
115}
116
117pub struct GlobalStorage {
119 globals: TiVec<GlobalIdx, Option<Rc<GlobalInstance>>>,
120 pub root: std::cell::OnceCell<
122 vtable::VWeak<i_slint_core::item_tree::ItemTreeVTable, crate::instance::Instance>,
123 >,
124 pub debug_hook_callback: std::cell::RefCell<Option<crate::debug_hook::DebugHookCallback>>,
126}
127
128impl GlobalStorage {
129 pub fn new(compilation_unit: &Rc<CompilationUnit>) -> Self {
132 let globals = compilation_unit
133 .globals
134 .iter_enumerated()
135 .map(|(idx, global)| {
136 if global.is_builtin {
137 let native = instantiate_native_global(&global.name)?;
140 return Some(Rc::new(GlobalInstance {
141 compilation_unit: compilation_unit.clone(),
142 global_idx: idx,
143 properties: TiVec::new(),
144 callbacks: TiVec::new(),
145 callback_trackers: TiVec::new(),
146 change_trackers: Vec::new(),
147 native: Some(native),
148 }));
149 }
150 let properties = global
151 .properties
152 .iter()
153 .map(|p| Rc::pin(Property::new(crate::eval::default_value_for_type(&p.ty))))
154 .collect();
155 let callbacks =
156 global.callbacks.iter().map(|_| Rc::pin(Callback::default())).collect();
157 let callback_trackers = global
158 .callbacks
159 .iter()
160 .map(|c| {
161 c.needs_tracker
162 .then(|| Rc::pin(i_slint_core::properties::Property::new(())))
163 })
164 .collect();
165 Some(Rc::new(GlobalInstance {
166 compilation_unit: compilation_unit.clone(),
167 global_idx: idx,
168 properties,
169 callbacks,
170 callback_trackers,
171 change_trackers: std::iter::repeat_with(Default::default)
172 .take(global.change_callbacks.len())
173 .collect(),
174 native: None,
175 }))
176 })
177 .collect();
178 Self {
179 globals,
180 root: std::cell::OnceCell::new(),
181 debug_hook_callback: std::cell::RefCell::new(None),
182 }
183 }
184
185 pub fn get(&self, idx: GlobalIdx) -> Option<&Rc<GlobalInstance>> {
186 self.globals.get(idx)?.as_ref()
187 }
188
189 pub fn find_by_name<'a>(
192 &'a self,
193 compilation_unit: &'a CompilationUnit,
194 name: &str,
195 ) -> Option<(&'a i_slint_compiler::llr::GlobalComponent, &'a Rc<GlobalInstance>)> {
196 let needle = i_slint_compiler::parser::normalize_identifier(name);
197 for (idx, global) in compilation_unit.globals.iter_enumerated() {
198 if !global.exported {
199 continue;
200 }
201 let name_matches =
202 i_slint_compiler::parser::normalize_identifier(&global.name) == needle;
203 let alias_matches = global
204 .aliases
205 .iter()
206 .any(|a| i_slint_compiler::parser::normalize_identifier(a) == needle);
207 if name_matches || alias_matches {
208 return self.get(idx).map(|inst| (global, inst));
209 }
210 }
211 None
212 }
213}
214
215pub fn install_global_bindings(storage: &Rc<GlobalStorage>) {
219 for g in storage.globals.iter().flatten() {
220 install_for_global(g, storage);
221 }
222 for g in storage.globals.iter().flatten() {
223 install_global_change_trackers(g, storage);
224 }
225}
226
227fn install_global_change_trackers(g: &Rc<GlobalInstance>, storage: &Rc<GlobalStorage>) {
228 let cu = g.compilation_unit.clone();
229 let global = &cu.globals[g.global_idx];
230 for (idx, (prop_idx, expr)) in global.change_callbacks.iter().enumerate() {
231 let weak_storage_get = Rc::downgrade(storage);
232 let weak_storage_set = Rc::downgrade(storage);
233 let global_idx = g.global_idx;
234 let prop_idx = *prop_idx;
235 let notify_expr = expr.borrow().clone();
236 g.change_trackers[idx].init(
237 (),
238 move |()| -> Value {
239 let Some(st) = weak_storage_get.upgrade() else { return Value::Void };
240 let Some(gi) = st.get(global_idx) else { return Value::Void };
241 Pin::as_ref(&gi.properties[prop_idx]).get()
242 },
243 {
244 let cu = cu.clone();
245 move |(), _| {
246 let Some(st) = weak_storage_set.upgrade() else { return };
247 let mut ctx = EvalContext::for_global(Rc::downgrade(&st), cu.clone());
248 eval_expression(&mut ctx, ¬ify_expr);
249 }
250 },
251 );
252 }
253}
254
255fn install_for_global(g: &Rc<GlobalInstance>, storage: &Rc<GlobalStorage>) {
256 if g.native.is_some() {
257 return;
260 }
261 let cu = g.compilation_unit.clone();
262 let global = &cu.globals[g.global_idx];
263 for (member, binding) in &global.init_values {
264 let expr = binding.expression.borrow().clone();
265 let weak_storage = Rc::downgrade(storage);
266
267 match member {
268 LocalMemberIndex::Property(idx) => {
269 let prop = Pin::as_ref(&g.properties[*idx]);
270 if binding.kind == i_slint_compiler::llr::BindingKind::Constant {
271 let mut ctx = EvalContext::for_global(weak_storage.clone(), cu.clone());
272 prop.set(eval_expression(&mut ctx, &expr));
273 continue;
274 }
275 let expr = expr.clone();
276 let cu = cu.clone();
277 prop.set_binding(move || {
278 let mut ctx = EvalContext::for_global(weak_storage.clone(), cu.clone());
279 eval_expression(&mut ctx, &expr)
280 });
281 }
282 LocalMemberIndex::Callback(idx) => {
283 let cb = Pin::as_ref(&g.callbacks[*idx]);
284 let expr = expr.clone();
285 let cu = cu.clone();
286 let arg_types = global.callbacks[*idx].args.clone();
287 cb.set_handler(move |args: &[Value]| -> Value {
288 let mut ctx = EvalContext::for_global(weak_storage.clone(), cu.clone());
289 ctx.function_arg_types = arg_types.clone();
290 ctx.function_arguments = args.to_vec();
291 eval_expression(&mut ctx, &expr)
292 });
293 }
294 LocalMemberIndex::Function(_)
295 | LocalMemberIndex::Native { .. }
296 | LocalMemberIndex::Timer(_) => {
297 }
300 }
301 }
302}