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