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