1use crate::instance::Instance;
10use crate::public_api;
11use crate::{AnimationMode, Value};
12use i_slint_compiler::expression_tree::BuiltinFunction;
13use i_slint_compiler::langtype::Type as LangType;
14use i_slint_compiler::llr::{CompilationUnit, Expression, GlobalComponent};
15use i_slint_compiler::object_tree::PropertyVisibility;
16use i_slint_compiler::parser::normalize_identifier;
17use i_slint_core::item_tree::ItemTreeVTable;
18use smol_str::SmolStr;
19use std::rc::Rc;
20use vtable::VRc;
21
22#[derive(Clone, Default)]
32pub struct TypeLoaders {
33 #[cfg_attr(not(any(feature = "internal", feature = "internal-highlight")), allow(dead_code))]
34 pub type_loader: Option<std::rc::Rc<i_slint_compiler::typeloader::TypeLoader>>,
35 #[cfg_attr(not(feature = "internal-highlight"), allow(dead_code))]
36 pub raw_type_loader: Option<std::rc::Rc<i_slint_compiler::typeloader::TypeLoader>>,
37 pub originals: std::rc::Rc<[std::rc::Rc<i_slint_compiler::object_tree::Component>]>,
43}
44
45#[derive(Clone)]
49pub struct ComponentDefinitionInner {
50 pub compilation_unit: Rc<CompilationUnit>,
51 pub public_index: usize,
52 pub type_loaders: TypeLoaders,
55}
56
57impl ComponentDefinitionInner {
58 pub fn name(&self) -> &str {
59 self.public().name.as_str()
60 }
61
62 pub fn create(&self) -> ComponentInstanceInner {
64 let vrc = Instance::new_with_window(
65 self.compilation_unit.clone(),
66 self.public_index,
67 None,
68 self.type_loaders.clone(),
69 );
70 ComponentInstanceInner(vrc)
71 }
72
73 pub fn create_with_existing_window(
76 &self,
77 window_adapter: i_slint_core::window::WindowAdapterRc,
78 ) -> ComponentInstanceInner {
79 let vrc = Instance::new_with_window(
80 self.compilation_unit.clone(),
81 self.public_index,
82 Some(window_adapter),
83 self.type_loaders.clone(),
84 );
85 ComponentInstanceInner(vrc)
86 }
87
88 pub fn create_embedded(
93 &self,
94 parent: vtable::VWeak<ItemTreeVTable>,
95 parent_item_tree_index: u32,
96 ) -> ComponentInstanceInner {
97 let vrc = Instance::new_embedded(
98 self.compilation_unit.clone(),
99 self.public_index,
100 self.type_loaders.clone(),
101 parent,
102 parent_item_tree_index,
103 );
104 ComponentInstanceInner(vrc)
105 }
106
107 fn public(&self) -> &i_slint_compiler::llr::PublicComponent {
108 &self.compilation_unit.public_components[self.public_index]
109 }
110
111 #[cfg_attr(not(feature = "internal"), allow(dead_code))]
114 pub fn top_level_type(&self) -> i_slint_compiler::llr::TopLevelComponentType {
115 self.public().top_level_type
116 }
117
118 fn properties_with_info(
119 &self,
120 ) -> impl Iterator<Item = (SmolStr, LangType, PropertyVisibility)> + '_ {
121 public_properties_info(&self.public().public_properties)
122 }
123
124 #[cfg_attr(not(feature = "internal"), allow(dead_code))]
129 pub fn properties_and_callbacks(
130 &self,
131 ) -> impl Iterator<Item = (SmolStr, LangType, PropertyVisibility)> + '_ {
132 self.properties_with_info()
133 }
134
135 pub fn properties(&self) -> impl Iterator<Item = (SmolStr, LangType)> + '_ {
138 self.properties_with_info()
139 .filter(|(_, ty, _)| ty.is_property_type())
140 .map(|(n, ty, _)| (n, ty))
141 }
142
143 pub fn callbacks(&self) -> impl Iterator<Item = SmolStr> + '_ {
144 self.properties_with_info()
145 .filter(|(_, ty, _)| matches!(ty, LangType::Callback(_)))
146 .map(|(n, _, _)| n)
147 }
148
149 pub fn functions(&self) -> impl Iterator<Item = SmolStr> + '_ {
150 self.properties_with_info()
151 .filter(|(_, ty, _)| matches!(ty, LangType::Function(_)))
152 .map(|(n, _, _)| n)
153 }
154
155 pub fn globals(&self) -> impl Iterator<Item = SmolStr> + '_ {
158 self.compilation_unit
159 .globals
160 .iter()
161 .filter(|g| visible_in_public_api(g))
162 .flat_map(|g| g.aliases.iter().cloned().chain(std::iter::once(g.name.clone())))
163 }
164
165 fn global_by_name(&self, name: &str) -> Option<&GlobalComponent> {
166 let needle = normalize_identifier(name);
169 self.compilation_unit.globals.iter().filter(|g| visible_in_public_api(g)).find(|g| {
170 normalize_identifier(&g.name) == needle
171 || g.aliases.iter().any(|a| normalize_identifier(a) == needle)
172 })
173 }
174
175 pub fn global_properties_and_callbacks(
176 &self,
177 name: &str,
178 ) -> Option<impl Iterator<Item = (SmolStr, LangType, PropertyVisibility)> + '_> {
179 self.global_by_name(name).map(|g| public_properties_info(&g.public_properties))
180 }
181
182 pub fn global_properties(
183 &self,
184 name: &str,
185 ) -> Option<impl Iterator<Item = (SmolStr, LangType)> + '_> {
186 self.global_properties_and_callbacks(name)
187 .map(|it| it.filter(|(_, ty, _)| ty.is_property_type()).map(|(n, ty, _)| (n, ty)))
188 }
189
190 pub fn global_callbacks(&self, name: &str) -> Option<impl Iterator<Item = SmolStr> + '_> {
191 self.global_properties_and_callbacks(name).map(|it| {
192 it.filter(|(_, ty, _)| matches!(ty, LangType::Callback(_))).map(|(n, _, _)| n)
193 })
194 }
195
196 pub fn global_functions(&self, name: &str) -> Option<impl Iterator<Item = SmolStr> + '_> {
197 self.global_properties_and_callbacks(name).map(|it| {
198 it.filter(|(_, ty, _)| matches!(ty, LangType::Function(_))).map(|(n, _, _)| n)
199 })
200 }
201}
202
203fn public_properties_info<'a>(
204 public_properties: &'a i_slint_compiler::llr::PublicProperties,
205) -> impl Iterator<Item = (SmolStr, LangType, PropertyVisibility)> + 'a {
206 public_properties.values().map(|p| (p.display_name.clone(), p.ty.clone(), p.visibility))
209}
210
211fn visible_in_public_api(g: &GlobalComponent) -> bool {
212 g.exported && !g.is_builtin
214}
215
216#[repr(transparent)]
221pub struct ComponentInstanceInner(pub VRc<ItemTreeVTable, Instance>);
222
223impl Clone for ComponentInstanceInner {
224 fn clone(&self) -> Self {
225 Self(self.0.clone())
226 }
227}
228
229impl ComponentInstanceInner {
230 pub fn vrc(&self) -> &VRc<ItemTreeVTable, Instance> {
233 &self.0
234 }
235
236 pub fn get_property(&self, name: &str) -> Option<Value> {
237 public_api::get(&self.0, name)
238 }
239
240 pub fn set_property(
241 &self,
242 name: &str,
243 value: Value,
244 ) -> Result<(), crate::api::SetPropertyError> {
245 public_api::set(&self.0, name, value)
246 }
247
248 pub fn invoke(&self, name: &str, args: &[Value]) -> Option<Value> {
249 public_api::invoke(&self.0, name, args)
250 }
251
252 pub fn set_callback(
253 &self,
254 name: &str,
255 handler: impl Fn(&[Value]) -> Value + 'static,
256 ) -> Result<(), ()> {
257 public_api::set_callback(&self.0, name, Box::new(handler))
258 }
259
260 pub fn get_global_property(&self, global: &str, property: &str) -> Option<Value> {
261 public_api::get_global(&self.0, global, property)
262 }
263
264 pub fn set_global_property(
265 &self,
266 global: &str,
267 property: &str,
268 value: Value,
269 ) -> Result<(), crate::api::SetPropertyError> {
270 public_api::set_global(&self.0, global, property, value)
271 }
272
273 pub fn set_global_callback(
274 &self,
275 global: &str,
276 name: &str,
277 handler: impl Fn(&[Value]) -> Value + 'static,
278 ) -> Result<(), ()> {
279 public_api::set_global_callback(&self.0, global, name, Box::new(handler))
280 }
281
282 pub fn invoke_global(&self, global: &str, name: &str, args: &[Value]) -> Option<Value> {
283 public_api::invoke_global(&self.0, global, name, args)
284 }
285
286 pub fn window_adapter_ref(
290 &self,
291 ) -> Result<&i_slint_core::window::WindowAdapterRc, i_slint_core::api::PlatformError> {
292 self.0.try_window_adapter()?;
293 Ok(self.0.window_adapter.get().expect("window_adapter just initialized above"))
294 }
295
296 pub fn top_level_type(&self) -> i_slint_compiler::llr::TopLevelComponentType {
299 let unit = &self.0.root_sub_component.compilation_unit;
300 match self.0.public_component_index {
301 Some(idx) => unit.public_components[idx].top_level_type,
302 None => i_slint_compiler::llr::TopLevelComponentType::Window,
303 }
304 }
305
306 pub fn definition(&self) -> ComponentDefinitionInner {
308 let public_index = self.0.public_component_index.unwrap_or(0);
309 ComponentDefinitionInner {
310 compilation_unit: self.0.root_sub_component.compilation_unit.clone(),
311 public_index,
312 type_loaders: self.0.type_loaders.clone(),
313 }
314 }
315}
316
317pub fn build_from_document(
320 document: &i_slint_compiler::object_tree::Document,
321 compiler_config: &i_slint_compiler::CompilerConfiguration,
322 mut type_loaders: TypeLoaders,
323 animation_mode: AnimationMode,
324) -> Vec<ComponentDefinitionInner> {
325 let mut unit =
326 i_slint_compiler::llr::lower_to_item_tree::lower_to_item_tree(document, compiler_config);
327 if matches!(animation_mode, AnimationMode::Static) {
328 make_static(&mut unit);
329 }
330 let unit = Rc::new(unit);
331 type_loaders.originals = document.exported_roots().collect();
334 (0..unit.public_components.len())
335 .map(|public_index| ComponentDefinitionInner {
336 compilation_unit: unit.clone(),
337 public_index,
338 type_loaders: type_loaders.clone(),
339 })
340 .collect()
341}
342
343fn make_static(compilation_unit: &mut CompilationUnit) {
344 fn make_expression_static(expression: &mut Expression) {
345 let replacement = match expression {
346 Expression::BuiltinFunctionCall { function, .. } => match function {
347 BuiltinFunction::AnimationTick => Some(Expression::NumberLiteral(0.)),
348 BuiltinFunction::RestartTimer | BuiltinFunction::UpdateTimers => {
349 Some(Expression::CodeBlock(Vec::new()))
350 }
351 _ => None,
352 },
353 _ => None,
354 };
355 if let Some(replacement) = replacement {
356 *expression = replacement;
357 }
358 }
359
360 compilation_unit.for_each_expression(&mut |expression, _| {
361 expression.borrow_mut().visit_recursive_mut(&mut make_expression_static);
362 });
363 for sub_component in &compilation_unit.sub_components {
364 for popup in &sub_component.popup_windows {
365 popup.position.borrow_mut().visit_recursive_mut(&mut make_expression_static);
366 }
367 }
368 for sub_component in &mut compilation_unit.sub_components {
369 sub_component.timers.clear();
370 sub_component.animations.clear();
371 for (_, binding) in &mut sub_component.property_init {
372 binding.animation = None;
373 }
374 }
375}
376
377pub struct BuildResult {
382 pub diagnostics: Vec<i_slint_compiler::diagnostics::Diagnostic>,
383 pub components: std::collections::HashMap<String, ComponentDefinitionInner>,
384 #[cfg(feature = "internal")]
385 pub watch_paths: Vec<std::path::PathBuf>,
386 #[cfg(feature = "internal")]
387 pub structs_and_enums: Vec<LangType>,
388}
389
390pub async fn build_from_source(
392 source_code: String,
393 path: std::path::PathBuf,
394 mut config: i_slint_compiler::CompilerConfiguration,
395 animation_mode: AnimationMode,
396) -> BuildResult {
397 if config.style.as_deref() == Some("native") {
399 #[cfg(target_arch = "wasm32")]
401 let target = web_sys::window()
402 .and_then(|window| window.navigator().platform().ok())
403 .map_or("wasm", |platform| {
404 let platform = platform.to_ascii_lowercase();
405 if platform.contains("mac")
406 || platform.contains("iphone")
407 || platform.contains("ipad")
408 {
409 "apple"
410 } else if platform.contains("android") {
411 "android"
412 } else if platform.contains("win") {
413 "windows"
414 } else if platform.contains("linux") {
415 "linux"
416 } else {
417 "wasm"
418 }
419 });
420 #[cfg(not(target_arch = "wasm32"))]
421 let target = "";
422 config.style = Some(
423 i_slint_common::get_native_style(i_slint_backend_selector::HAS_NATIVE_STYLE, target)
424 .to_string(),
425 );
426 }
427 if std::env::var_os("SLINT_INLINING").is_none() {
431 config.inline_all_elements = false;
432 }
433 config.debug_info = true;
436 let diag = i_slint_compiler::diagnostics::BuildDiagnostics::default();
437 let (path, mut diag, loader, raw_loader) =
438 i_slint_compiler::load_root_file_with_raw_type_loader(
439 &path,
440 &path,
441 source_code,
442 diag,
443 config.clone(),
444 )
445 .await;
446 #[cfg(feature = "internal")]
447 let watch_paths = loader.all_files_to_watch().into_iter().collect();
448 let error_result = |diagnostics| BuildResult {
449 diagnostics,
450 components: Default::default(),
451 #[cfg(feature = "internal")]
452 watch_paths: Vec::new(),
453 #[cfg(feature = "internal")]
454 structs_and_enums: Vec::new(),
455 };
456 if diag.has_errors() {
457 return BuildResult {
458 #[cfg(feature = "internal")]
459 watch_paths,
460 ..error_result(diag.into_iter().collect())
461 };
462 }
463 let type_loader = std::rc::Rc::new(loader);
464 let type_loaders = TypeLoaders {
465 type_loader: Some(type_loader.clone()),
466 raw_type_loader: raw_loader.map(std::rc::Rc::new),
467 originals: Default::default(),
468 };
469 let doc = match type_loader.get_document(&path) {
470 Some(doc) => doc,
471 None => {
472 return BuildResult {
473 #[cfg(feature = "internal")]
474 watch_paths,
475 ..error_result(diag.into_iter().collect())
476 };
477 }
478 };
479 let mut components = std::collections::HashMap::new();
480 for def in build_from_document(doc, &config, type_loaders, animation_mode) {
481 components.insert(def.name().to_string(), def);
482 }
483 if components.is_empty() {
484 diag.push_error_with_span("No component found".into(), Default::default());
485 }
486 #[cfg(feature = "internal")]
487 let structs_and_enums = doc.used_types.borrow().structs_and_enums.clone();
488 BuildResult {
489 diagnostics: diag.into_iter().collect(),
490 components,
491 #[cfg(feature = "internal")]
492 watch_paths,
493 #[cfg(feature = "internal")]
494 structs_and_enums,
495 }
496}