Skip to main content

i_slint_compiler/
passes.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4mod apply_default_properties_from_style;
5mod binding_analysis;
6mod border_radius;
7mod check_expressions;
8mod check_public_api;
9mod clip;
10mod collect_custom_fonts;
11mod collect_globals;
12mod collect_init_code;
13mod collect_libraries;
14mod collect_structs_and_enums;
15mod collect_subcomponents;
16mod compile_paths;
17mod const_propagation;
18mod deduplicate_property_read;
19mod default_geometry;
20mod deprecated_rotation_origin;
21#[cfg(feature = "software-renderer")]
22mod embed_glyphs;
23mod embed_images;
24mod flickable;
25mod focus_handling;
26pub mod generate_item_indices;
27pub mod infer_aliases_types;
28mod inject_debug_hooks;
29mod inlining;
30mod lower_absolute_coordinates;
31mod lower_accessibility;
32mod lower_component_container;
33mod lower_layout;
34mod lower_menus;
35mod lower_platform;
36mod lower_popups;
37mod lower_property_to_element;
38mod lower_repeated_rows;
39mod lower_shadows;
40mod lower_states;
41mod lower_tabwidget;
42mod lower_text_input_interface;
43mod lower_timers;
44pub mod materialize_fake_properties;
45pub mod move_declarations;
46mod optimize_useless_rectangles;
47mod purity_check;
48mod remove_aliases;
49mod remove_return;
50mod remove_unused_properties;
51mod repeater_component;
52pub mod resolve_native_classes;
53pub mod resolving;
54mod unique_id;
55mod visible;
56mod windows;
57mod z_order;
58
59use crate::expression_tree::Expression;
60use smol_str::SmolStr;
61
62pub use binding_analysis::GlobalAnalysis;
63
64pub fn ignore_debug_hooks(expr: &Expression) -> &Expression {
65    let mut expr = expr;
66    loop {
67        match expr {
68            Expression::DebugHook { expression, .. } => expr = expression.as_ref(),
69            _ => return expr,
70        }
71    }
72}
73
74pub async fn run_passes(
75    doc: &mut crate::object_tree::Document,
76    type_loader: &mut crate::typeloader::TypeLoader,
77    keep_raw: bool,
78    diag: &mut crate::diagnostics::BuildDiagnostics,
79) -> Option<crate::typeloader::TypeLoader> {
80    let style_metrics = {
81        // Ignore import errors
82        let mut build_diags_to_ignore = crate::diagnostics::BuildDiagnostics::default();
83        // Import from style-base.slint instead of std-widgets.slint to avoid loading
84        // the entire widget library just to get StyleMetrics.
85        type_loader
86            .import_component("style-base.slint", "StyleMetrics", &mut build_diags_to_ignore)
87            .await
88            .unwrap_or_else(|| panic!("can't load style metrics"))
89    };
90
91    let palette = {
92        // Ignore import errors
93        let mut build_diags_to_ignore = crate::diagnostics::BuildDiagnostics::default();
94        // Import from style-base.slint instead of std-widgets.slint to avoid loading
95        // the entire widget library just to get Palette.
96        type_loader
97            .import_component("style-base.slint", "Palette", &mut build_diags_to_ignore)
98            .await
99            .unwrap_or_else(|| panic!("can't load palette"))
100    };
101
102    let global_type_registry = type_loader.global_type_registry.clone();
103
104    run_import_passes(doc, type_loader, diag);
105    check_public_api::check_public_api(doc, &type_loader.compiler_config, diag);
106
107    let raw_type_loader =
108        keep_raw.then(|| crate::typeloader::snapshot_with_extra_doc(type_loader, doc).unwrap());
109
110    collect_libraries::collect_libraries(doc);
111    collect_subcomponents::collect_subcomponents(doc);
112    lower_tabwidget::lower_tabwidget(doc, type_loader, diag).await;
113    lower_menus::lower_menus(doc, type_loader, diag).await;
114    lower_component_container::lower_component_container(doc, type_loader, diag);
115    collect_subcomponents::collect_subcomponents(doc);
116
117    doc.visit_all_used_components(|component| {
118        apply_default_properties_from_style::apply_default_properties_from_style(
119            component,
120            &style_metrics,
121            &palette,
122            diag,
123        );
124        lower_states::lower_states(component, &doc.local_registry, diag);
125        lower_text_input_interface::lower_text_input_interface(component);
126        compile_paths::compile_paths(
127            component,
128            &doc.local_registry,
129            type_loader.compiler_config.embed_resources,
130            diag,
131        );
132        repeater_component::process_repeater_components(component);
133        lower_popups::lower_popups(component, &doc.local_registry, diag);
134        collect_init_code::collect_init_code(component);
135        lower_timers::lower_timers(component, diag);
136    });
137
138    inlining::inline(doc, inlining::InlineSelection::InlineOnlyRequiredComponents, diag);
139    collect_subcomponents::collect_subcomponents(doc);
140
141    for root_component in doc.exported_roots() {
142        focus_handling::call_focus_on_init(&root_component);
143        windows::ensure_window(&root_component, &doc.local_registry, &style_metrics, diag);
144    }
145    if let Some(popup_menu_impl) = &doc.popup_menu_impl {
146        focus_handling::call_focus_on_init(popup_menu_impl);
147    }
148
149    doc.visit_all_used_components(|component| {
150        border_radius::handle_border_radius(component, diag);
151        deprecated_rotation_origin::handle_rotation_origin(component, diag);
152        flickable::handle_flickable(component, &global_type_registry.borrow());
153        lower_layout::lower_layouts(component, type_loader, &style_metrics, diag);
154        default_geometry::default_geometry(component, diag);
155        lower_absolute_coordinates::lower_absolute_coordinates(component);
156        z_order::reorder_by_z_order(component, diag);
157        lower_property_to_element::lower_property_to_element(
158            component,
159            core::iter::once("opacity"),
160            core::iter::empty(),
161            None,
162            &SmolStr::new_static("Opacity"),
163            &global_type_registry.borrow(),
164            diag,
165        );
166        lower_property_to_element::lower_property_to_element(
167            component,
168            core::iter::once("cache-rendering-hint"),
169            core::iter::empty(),
170            None,
171            &SmolStr::new_static("Layer"),
172            &global_type_registry.borrow(),
173            diag,
174        );
175        visible::handle_visible(component, &global_type_registry.borrow(), diag);
176        lower_shadows::lower_shadow_properties(component, &doc.local_registry, diag);
177        lower_property_to_element::lower_transform_properties(
178            component,
179            &global_type_registry.borrow(),
180            diag,
181        );
182        clip::handle_clip(component, &global_type_registry.borrow(), diag);
183        if type_loader.compiler_config.accessibility {
184            lower_accessibility::lower_accessibility_properties(component, diag);
185        }
186        lower_repeated_rows::lower_repeated_rows(component, &global_type_registry.borrow());
187        materialize_fake_properties::materialize_fake_properties(component);
188    });
189    for root_component in doc.exported_roots() {
190        lower_layout::check_window_layout(&root_component);
191    }
192    collect_globals::collect_globals(doc, diag);
193
194    if type_loader.compiler_config.inline_all_elements {
195        inlining::inline(doc, inlining::InlineSelection::InlineAllComponents, diag);
196        doc.used_types.borrow_mut().sub_components.clear();
197    }
198
199    let global_analysis =
200        binding_analysis::binding_analysis(doc, &type_loader.compiler_config, diag);
201    collect_globals::mark_library_globals(doc);
202    unique_id::assign_unique_id(doc);
203
204    doc.visit_all_used_components(|component| {
205        lower_platform::lower_platform(component, type_loader);
206
207        // Don't perform the empty rectangle removal when debug info is requested, because the resulting
208        // item tree ends up with a hierarchy where certain items have children that aren't child elements
209        // but siblings or sibling children. We need a new data structure to perform a correct element tree
210        // traversal.
211        if !type_loader.compiler_config.debug_info {
212            optimize_useless_rectangles::optimize_useless_rectangles(component);
213        }
214        move_declarations::move_declarations(component);
215    });
216
217    remove_aliases::remove_aliases(doc, diag);
218    remove_return::remove_return(doc);
219
220    doc.visit_all_used_components(|component| {
221        if !diag.has_errors() {
222            // binding loop causes panics in const_propagation
223            const_propagation::const_propagation(component, &global_analysis);
224        }
225        deduplicate_property_read::deduplicate_property_read(component);
226        if !component.is_global() {
227            resolve_native_classes::resolve_native_classes(component);
228        }
229    });
230
231    remove_unused_properties::remove_unused_properties(doc);
232    // collect globals once more: After optimizations we might have less globals
233    collect_globals::collect_globals(doc, diag);
234    collect_structs_and_enums::collect_structs_and_enums(doc);
235
236    doc.visit_all_used_components(|component| {
237        if !component.is_global() {
238            generate_item_indices::generate_item_indices(component);
239        }
240    });
241
242    embed_images::embed_images(
243        doc,
244        type_loader.compiler_config.embed_resources,
245        type_loader.compiler_config.const_scale_factor.unwrap_or(1.),
246        &type_loader.compiler_config.resource_url_mapper,
247        diag,
248    )
249    .await;
250
251    #[cfg(feature = "bundle-translations")]
252    if let Some(path) = &type_loader.compiler_config.translation_path_bundle {
253        match crate::translations::TranslationsBuilder::load_translations(
254            path,
255            type_loader.compiler_config.translation_domain.as_deref().unwrap_or(""),
256        ) {
257            Ok(builder) => {
258                doc.translation_builder = Some(builder);
259            }
260            Err(err) => {
261                diag.push_error(
262                    format!("Cannot load bundled translation: {err}"),
263                    doc.node.as_ref().expect("Unexpected empty document"),
264                );
265            }
266        }
267    }
268
269    match type_loader.compiler_config.embed_resources {
270        #[cfg(feature = "software-renderer")]
271        crate::EmbedResourcesKind::EmbedTextures => {
272            let mut characters_seen = std::collections::HashSet::new();
273
274            let sf = type_loader.compiler_config.const_scale_factor.unwrap_or(1.) as f64;
275
276            // Include at least the default font sizes used in the MCU backend
277            let mut font_pixel_sizes = vec![(12. * sf) as i16];
278            doc.visit_all_used_components(|component| {
279                embed_glyphs::collect_font_sizes_used(component, sf, &mut font_pixel_sizes);
280                embed_glyphs::scan_string_literals(component, &mut characters_seen);
281            });
282
283            // This is not perfect, as this includes translations that may not be used.
284            #[cfg(feature = "bundle-translations")]
285            if let Some(translation_builder) = doc.translation_builder.as_ref() {
286                translation_builder.collect_characters_seen(&mut characters_seen);
287            }
288
289            embed_glyphs::embed_glyphs(
290                doc,
291                &type_loader.compiler_config,
292                font_pixel_sizes,
293                characters_seen,
294                std::iter::once(&*doc).chain(type_loader.all_documents()),
295                diag,
296            );
297        }
298        _ => {
299            // Create font registration calls for custom fonts, unless we're embedding pre-rendered glyphs
300            collect_custom_fonts::collect_custom_fonts(
301                doc,
302                std::iter::once(&*doc).chain(type_loader.all_documents()),
303                type_loader.compiler_config.embed_resources
304                    == crate::EmbedResourcesKind::EmbedAllResources,
305            );
306        }
307    };
308
309    raw_type_loader
310}
311
312/// Run the passes on imported documents
313pub fn run_import_passes(
314    doc: &crate::object_tree::Document,
315    type_loader: &crate::typeloader::TypeLoader,
316    diag: &mut crate::diagnostics::BuildDiagnostics,
317) {
318    inject_debug_hooks::inject_debug_hooks(doc, type_loader);
319    infer_aliases_types::resolve_aliases(doc, diag);
320    resolving::resolve_expressions(doc, type_loader, diag);
321    purity_check::purity_check(doc, diag);
322    focus_handling::replace_forward_focus_bindings_with_focus_functions(doc, diag);
323    check_expressions::check_expressions(doc, diag);
324    windows::warn_about_child_windows(doc, diag);
325    unique_id::check_unique_id(doc, diag);
326}