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}
351
352pub async fn build_from_source(
354 source_code: String,
355 path: std::path::PathBuf,
356 mut config: i_slint_compiler::CompilerConfiguration,
357) -> BuildResult {
358 if config.style.as_deref() == Some("native") {
360 #[cfg(target_arch = "wasm32")]
362 let target = web_sys::window()
363 .and_then(|window| window.navigator().platform().ok())
364 .map_or("wasm", |platform| {
365 let platform = platform.to_ascii_lowercase();
366 if platform.contains("mac")
367 || platform.contains("iphone")
368 || platform.contains("ipad")
369 {
370 "apple"
371 } else if platform.contains("android") {
372 "android"
373 } else if platform.contains("win") {
374 "windows"
375 } else if platform.contains("linux") {
376 "linux"
377 } else {
378 "wasm"
379 }
380 });
381 #[cfg(not(target_arch = "wasm32"))]
382 let target = "";
383 config.style = Some(
384 i_slint_common::get_native_style(i_slint_backend_selector::HAS_NATIVE_STYLE, target)
385 .to_string(),
386 );
387 }
388 if std::env::var_os("SLINT_INLINING").is_none() {
392 config.inline_all_elements = false;
393 }
394 config.debug_info = true;
397 let diag = i_slint_compiler::diagnostics::BuildDiagnostics::default();
398 let (path, mut diag, loader, raw_loader) =
399 i_slint_compiler::load_root_file_with_raw_type_loader(
400 &path,
401 &path,
402 source_code,
403 diag,
404 config.clone(),
405 )
406 .await;
407 #[cfg(feature = "internal")]
408 let watch_paths = loader.all_files_to_watch().into_iter().collect();
409 let error_result = |diagnostics| BuildResult {
410 diagnostics,
411 components: Default::default(),
412 #[cfg(feature = "internal")]
413 watch_paths: Vec::new(),
414 #[cfg(feature = "internal")]
415 structs_and_enums: Vec::new(),
416 };
417 if diag.has_errors() {
418 return BuildResult {
419 #[cfg(feature = "internal")]
420 watch_paths,
421 ..error_result(diag.into_iter().collect())
422 };
423 }
424 let type_loader = std::rc::Rc::new(loader);
425 let type_loaders = TypeLoaders {
426 type_loader: Some(type_loader.clone()),
427 raw_type_loader: raw_loader.map(std::rc::Rc::new),
428 originals: Default::default(),
429 };
430 let doc = match type_loader.get_document(&path) {
431 Some(doc) => doc,
432 None => {
433 return BuildResult {
434 #[cfg(feature = "internal")]
435 watch_paths,
436 ..error_result(diag.into_iter().collect())
437 };
438 }
439 };
440 let mut components = std::collections::HashMap::new();
441 for def in build_from_document(doc, &config, type_loaders) {
442 components.insert(def.name().to_string(), def);
443 }
444 if components.is_empty() {
445 diag.push_error_with_span("No component found".into(), Default::default());
446 }
447 #[cfg(feature = "internal")]
448 let structs_and_enums = doc.used_types.borrow().structs_and_enums.clone();
449 BuildResult {
450 diagnostics: diag.into_iter().collect(),
451 components,
452 #[cfg(feature = "internal")]
453 watch_paths,
454 #[cfg(feature = "internal")]
455 structs_and_enums,
456 }
457}