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 context: i_slint_core::SlintContext,
125 pub debug_hook_callback: std::cell::RefCell<Option<crate::debug_hook::DebugHookCallback>>,
127}
128
129impl GlobalStorage {
130 pub fn new(
133 compilation_unit: &Rc<CompilationUnit>,
134 context: i_slint_core::SlintContext,
135 ) -> Self {
136 let globals = compilation_unit
137 .globals
138 .iter_enumerated()
139 .map(|(idx, global)| {
140 if global.is_builtin {
141 let native = instantiate_native_global(&global.name)?;
144 return Some(Rc::new(GlobalInstance {
145 compilation_unit: compilation_unit.clone(),
146 global_idx: idx,
147 properties: TiVec::new(),
148 callbacks: TiVec::new(),
149 callback_trackers: TiVec::new(),
150 change_trackers: Vec::new(),
151 native: Some(native),
152 }));
153 }
154 let properties = global
155 .properties
156 .iter()
157 .map(|p| Rc::pin(Property::new(crate::eval::default_value_for_type(&p.ty))))
158 .collect();
159 let callbacks =
160 global.callbacks.iter().map(|_| Rc::pin(Callback::default())).collect();
161 let callback_trackers = global
162 .callbacks
163 .iter()
164 .map(|c| {
165 c.needs_tracker
166 .then(|| Rc::pin(i_slint_core::properties::Property::new(())))
167 })
168 .collect();
169 Some(Rc::new(GlobalInstance {
170 compilation_unit: compilation_unit.clone(),
171 global_idx: idx,
172 properties,
173 callbacks,
174 callback_trackers,
175 change_trackers: std::iter::repeat_with(Default::default)
176 .take(global.change_callbacks.len())
177 .collect(),
178 native: None,
179 }))
180 })
181 .collect();
182 Self {
183 globals,
184 root: std::cell::OnceCell::new(),
185 context,
186 debug_hook_callback: std::cell::RefCell::new(None),
187 }
188 }
189
190 pub fn get(&self, idx: GlobalIdx) -> Option<&Rc<GlobalInstance>> {
191 self.globals.get(idx)?.as_ref()
192 }
193
194 pub fn find_by_name<'a>(
197 &'a self,
198 compilation_unit: &'a CompilationUnit,
199 name: &str,
200 ) -> Option<(&'a i_slint_compiler::llr::GlobalComponent, &'a Rc<GlobalInstance>)> {
201 let needle = i_slint_compiler::parser::normalize_identifier(name);
202 for (idx, global) in compilation_unit.globals.iter_enumerated() {
203 if !global.exported {
204 continue;
205 }
206 let name_matches =
207 i_slint_compiler::parser::normalize_identifier(&global.name) == needle;
208 let alias_matches = global
209 .aliases
210 .iter()
211 .any(|a| i_slint_compiler::parser::normalize_identifier(a) == needle);
212 if name_matches || alias_matches {
213 return self.get(idx).map(|inst| (global, inst));
214 }
215 }
216 None
217 }
218}
219
220pub fn install_global_bindings(storage: &Rc<GlobalStorage>) {
224 for g in storage.globals.iter().flatten() {
225 install_for_global(g, storage);
226 }
227 for g in storage.globals.iter().flatten() {
228 install_global_change_trackers(g, storage);
229 }
230}
231
232fn install_global_change_trackers(g: &Rc<GlobalInstance>, storage: &Rc<GlobalStorage>) {
233 let cu = g.compilation_unit.clone();
234 let global = &cu.globals[g.global_idx];
235 for (idx, (prop_idx, expr)) in global.change_callbacks.iter().enumerate() {
236 let weak_storage_get = Rc::downgrade(storage);
237 let weak_storage_set = Rc::downgrade(storage);
238 let global_idx = g.global_idx;
239 let prop_idx = *prop_idx;
240 let notify_expr = expr.borrow().clone();
241 g.change_trackers[idx].init(
242 (),
243 move |()| -> Value {
244 let Some(st) = weak_storage_get.upgrade() else { return Value::Void };
245 let Some(gi) = st.get(global_idx) else { return Value::Void };
246 Pin::as_ref(&gi.properties[prop_idx]).get()
247 },
248 {
249 let cu = cu.clone();
250 move |(), _| {
251 let Some(st) = weak_storage_set.upgrade() else { return };
252 let mut ctx = EvalContext::for_global(Rc::downgrade(&st), cu.clone());
253 eval_expression(&mut ctx, ¬ify_expr);
254 }
255 },
256 );
257 }
258}
259
260fn install_for_global(g: &Rc<GlobalInstance>, storage: &Rc<GlobalStorage>) {
261 if g.native.is_some() {
262 return;
265 }
266 let cu = g.compilation_unit.clone();
267 let global = &cu.globals[g.global_idx];
268 for (member, binding) in &global.init_values {
269 let expr = binding.expression.borrow().clone();
270 let weak_storage = Rc::downgrade(storage);
271
272 match member {
273 LocalMemberIndex::Property(idx) => {
274 let prop = Pin::as_ref(&g.properties[*idx]);
275 if binding.kind == i_slint_compiler::llr::BindingKind::Constant {
276 let mut ctx = EvalContext::for_global(weak_storage.clone(), cu.clone());
277 prop.set(eval_expression(&mut ctx, &expr));
278 continue;
279 }
280 let expr = expr.clone();
281 let cu = cu.clone();
282 prop.set_binding(move || {
283 let mut ctx = EvalContext::for_global(weak_storage.clone(), cu.clone());
284 eval_expression(&mut ctx, &expr)
285 });
286 }
287 LocalMemberIndex::Callback(idx) => {
288 let cb = Pin::as_ref(&g.callbacks[*idx]);
289 let expr = expr.clone();
290 let cu = cu.clone();
291 let arg_types = global.callbacks[*idx].args.clone();
292 cb.set_handler(move |args: &[Value]| -> Value {
293 let mut ctx = EvalContext::for_global(weak_storage.clone(), cu.clone());
294 ctx.function_arg_types = arg_types.clone();
295 ctx.function_arguments = args.to_vec();
296 eval_expression(&mut ctx, &expr)
297 });
298 }
299 LocalMemberIndex::Function(_)
300 | LocalMemberIndex::Native { .. }
301 | LocalMemberIndex::Timer(_) => {
302 }
305 }
306 }
307}