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