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