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