slint/internal/compiler/passes.rs
Tobias Hunger 93f72b8c99
Some checks are pending
autofix.ci / format_fix (push) Waiting to run
autofix.ci / lint_typecheck (push) Waiting to run
CI / files-changed (push) Waiting to run
CI / build_and_test (--exclude bevy-example, ubuntu-22.04, 1.82) (push) Blocked by required conditions
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, --exclude bevy-example, windows-2022, 1.82) (push) Blocked by required conditions
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, macos-14, stable) (push) Blocked by required conditions
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, windows-2022, beta) (push) Blocked by required conditions
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, windows-2022, stable) (push) Blocked by required conditions
CI / build_and_test (ubuntu-22.04, nightly) (push) Blocked by required conditions
CI / node_test (macos-14) (push) Blocked by required conditions
CI / node_test (ubuntu-22.04) (push) Blocked by required conditions
CI / node_test (windows-2022) (push) Blocked by required conditions
CI / python_test (macos-14) (push) Blocked by required conditions
CI / python_test (ubuntu-22.04) (push) Blocked by required conditions
CI / python_test (windows-2022) (push) Blocked by required conditions
CI / cpp_test_driver (macos-13) (push) Blocked by required conditions
CI / cpp_test_driver (ubuntu-22.04) (push) Blocked by required conditions
CI / cpp_test_driver (windows-2022) (push) Blocked by required conditions
CI / cpp_cmake (macos-14, 1.82) (push) Blocked by required conditions
CI / cpp_cmake (ubuntu-22.04, stable) (push) Blocked by required conditions
CI / cpp_cmake (windows-2022, nightly) (push) Blocked by required conditions
CI / cpp_package_test (push) Blocked by required conditions
CI / vsce_build_test (push) Blocked by required conditions
CI / mcu (pico-st7789, thumbv6m-none-eabi) (push) Blocked by required conditions
CI / mcu (pico2-st7789, thumbv8m.main-none-eabihf) (push) Blocked by required conditions
CI / mcu (stm32h735g, thumbv7em-none-eabihf) (push) Blocked by required conditions
CI / mcu-embassy (push) Blocked by required conditions
CI / ffi_32bit_build (push) Blocked by required conditions
CI / docs (push) Blocked by required conditions
CI / wasm (push) Blocked by required conditions
CI / wasm_demo (push) Blocked by required conditions
CI / tree-sitter (push) Blocked by required conditions
CI / updater_test (0.3.0) (push) Blocked by required conditions
CI / fmt_test (push) Blocked by required conditions
CI / esp-idf-quick (push) Blocked by required conditions
CI / android (push) Blocked by required conditions
CI / miri (push) Blocked by required conditions
CI / test-figma-inspector (push) Blocked by required conditions
core: Fix the component container
This fixes the contents of the `n`-th repeater inside the
`ComponentContainer` to also show up in the `n`-th repeater *after*
the `ComponentContainer`.

The ComponentContainer is lowered to a Comonent Container and a
dynamic tree node. The tree node is managed manually and I messed
up the repeater indices since there were no entries in the repeater
array for the embedded components. That mad things hard to keep
consistent as components get merged.

This chnages that: It lowers a Component Container to Something like this:

```slint
    ComponentContainer {
        if false: Emtpy {}
    }
```

This way the standard mechanismns make sure we have something to put into
the repeater list and that unscrews the indices, saving a bit of code along
the way.

The inserted repeated node is still marked as `is_component_placeholder`, so
that we can wire it up as needed.
2025-06-05 13:48:16 +02:00

338 lines
12 KiB
Rust

// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
mod apply_default_properties_from_style;
mod binding_analysis;
mod border_radius;
mod check_expressions;
mod check_public_api;
mod check_rotation;
mod clip;
mod collect_custom_fonts;
mod collect_globals;
mod collect_init_code;
mod collect_structs_and_enums;
mod collect_subcomponents;
mod compile_paths;
mod const_propagation;
mod deduplicate_property_read;
mod default_geometry;
#[cfg(feature = "software-renderer")]
mod embed_glyphs;
mod embed_images;
mod ensure_window;
mod flickable;
mod focus_handling;
pub mod generate_item_indices;
pub mod infer_aliases_types;
mod inject_debug_hooks;
mod inlining;
mod lower_absolute_coordinates;
mod lower_accessibility;
mod lower_component_container;
mod lower_layout;
mod lower_menus;
mod lower_platform;
mod lower_popups;
mod lower_property_to_element;
mod lower_shadows;
mod lower_states;
mod lower_tabwidget;
mod lower_text_input_interface;
mod lower_timers;
pub mod materialize_fake_properties;
pub mod move_declarations;
mod optimize_useless_rectangles;
mod purity_check;
mod remove_aliases;
mod remove_return;
mod remove_unused_properties;
mod repeater_component;
pub mod resolve_native_classes;
pub mod resolving;
mod unique_id;
mod visible;
mod z_order;
use crate::expression_tree::Expression;
use crate::namedreference::NamedReference;
use smol_str::SmolStr;
pub fn ignore_debug_hooks(expr: &Expression) -> &Expression {
let mut expr = expr;
loop {
match expr {
Expression::DebugHook { expression, .. } => expr = expression.as_ref(),
_ => return expr,
}
}
}
pub async fn run_passes(
doc: &mut crate::object_tree::Document,
type_loader: &mut crate::typeloader::TypeLoader,
keep_raw: bool,
diag: &mut crate::diagnostics::BuildDiagnostics,
) -> Option<crate::typeloader::TypeLoader> {
let style_metrics = {
// Ignore import errors
let mut build_diags_to_ignore = crate::diagnostics::BuildDiagnostics::default();
type_loader
.import_component("std-widgets.slint", "StyleMetrics", &mut build_diags_to_ignore)
.await
.unwrap_or_else(|| panic!("can't load style metrics"))
};
let palette = {
// Ignore import errors
let mut build_diags_to_ignore = crate::diagnostics::BuildDiagnostics::default();
type_loader
.import_component("std-widgets.slint", "Palette", &mut build_diags_to_ignore)
.await
.unwrap_or_else(|| panic!("can't load palette"))
};
let global_type_registry = type_loader.global_type_registry.clone();
run_import_passes(doc, type_loader, diag);
check_public_api::check_public_api(doc, &type_loader.compiler_config, diag);
let raw_type_loader =
keep_raw.then(|| crate::typeloader::snapshot_with_extra_doc(type_loader, doc).unwrap());
collect_subcomponents::collect_subcomponents(doc);
doc.visit_all_used_components(|component| {
compile_paths::compile_paths(
component,
&doc.local_registry,
type_loader.compiler_config.embed_resources,
diag,
);
});
lower_tabwidget::lower_tabwidget(doc, type_loader, diag).await;
lower_menus::lower_menus(doc, type_loader, diag).await;
lower_component_container::lower_component_container(doc, type_loader, diag);
collect_subcomponents::collect_subcomponents(doc);
doc.visit_all_used_components(|component| {
apply_default_properties_from_style::apply_default_properties_from_style(
component,
&style_metrics,
&palette,
diag,
);
lower_states::lower_states(component, &doc.local_registry, diag);
lower_text_input_interface::lower_text_input_interface(component);
lower_platform::lower_platform(component, type_loader);
repeater_component::process_repeater_components(component);
lower_popups::lower_popups(component, &doc.local_registry, diag);
collect_init_code::collect_init_code(component);
lower_timers::lower_timers(component, diag);
});
inlining::inline(doc, inlining::InlineSelection::InlineOnlyRequiredComponents, diag);
collect_subcomponents::collect_subcomponents(doc);
for root_component in doc.exported_roots() {
focus_handling::call_focus_on_init(&root_component);
ensure_window::ensure_window(&root_component, &doc.local_registry, &style_metrics, diag);
}
if let Some(popup_menu_impl) = &doc.popup_menu_impl {
focus_handling::call_focus_on_init(popup_menu_impl);
}
doc.visit_all_used_components(|component| {
border_radius::handle_border_radius(component, diag);
flickable::handle_flickable(component, &global_type_registry.borrow());
lower_layout::lower_layouts(component, type_loader, &style_metrics, diag);
default_geometry::default_geometry(component, diag);
lower_absolute_coordinates::lower_absolute_coordinates(component);
z_order::reorder_by_z_order(component, diag);
lower_property_to_element::lower_property_to_element(
component,
"opacity",
core::iter::empty(),
None,
&SmolStr::new_static("Opacity"),
&global_type_registry.borrow(),
diag,
);
lower_property_to_element::lower_property_to_element(
component,
"cache-rendering-hint",
core::iter::empty(),
None,
&SmolStr::new_static("Layer"),
&global_type_registry.borrow(),
diag,
);
visible::handle_visible(component, &global_type_registry.borrow(), diag);
lower_shadows::lower_shadow_properties(component, &doc.local_registry, diag);
lower_property_to_element::lower_property_to_element(
component,
crate::typeregister::RESERVED_ROTATION_PROPERTIES[0].0,
crate::typeregister::RESERVED_ROTATION_PROPERTIES[1..]
.iter()
.map(|(prop_name, _)| *prop_name),
Some(&|e, prop| Expression::BinaryExpression {
lhs: Expression::PropertyReference(NamedReference::new(
e,
match prop {
"rotation-origin-x" => SmolStr::new_static("width"),
"rotation-origin-y" => SmolStr::new_static("height"),
"rotation-angle" => return Expression::Invalid,
_ => unreachable!(),
},
))
.into(),
op: '/',
rhs: Expression::NumberLiteral(2., Default::default()).into(),
}),
&SmolStr::new_static("Rotate"),
&global_type_registry.borrow(),
diag,
);
clip::handle_clip(component, &global_type_registry.borrow(), diag);
if type_loader.compiler_config.accessibility {
lower_accessibility::lower_accessibility_properties(component, diag);
}
materialize_fake_properties::materialize_fake_properties(component);
});
for root_component in doc.exported_roots() {
lower_layout::check_window_layout(&root_component);
}
collect_globals::collect_globals(doc, diag);
if type_loader.compiler_config.inline_all_elements {
inlining::inline(doc, inlining::InlineSelection::InlineAllComponents, diag);
doc.used_types.borrow_mut().sub_components.clear();
}
binding_analysis::binding_analysis(doc, &type_loader.compiler_config, diag);
unique_id::assign_unique_id(doc);
doc.visit_all_used_components(|component| {
// Don't perform the empty rectangle removal when debug info is requested, because the resulting
// item tree ends up with a hierarchy where certain items have children that aren't child elements
// but siblings or sibling children. We need a new data structure to perform a correct element tree
// traversal.
if !type_loader.compiler_config.debug_info {
optimize_useless_rectangles::optimize_useless_rectangles(component);
}
move_declarations::move_declarations(component);
});
remove_aliases::remove_aliases(doc, diag);
remove_return::remove_return(doc);
doc.visit_all_used_components(|component| {
if !diag.has_errors() {
// binding loop causes panics in const_propagation
const_propagation::const_propagation(component);
}
deduplicate_property_read::deduplicate_property_read(component);
if !component.is_global() {
resolve_native_classes::resolve_native_classes(component);
}
});
remove_unused_properties::remove_unused_properties(doc);
collect_structs_and_enums::collect_structs_and_enums(doc);
doc.visit_all_used_components(|component| {
if !component.is_global() {
generate_item_indices::generate_item_indices(component);
}
});
// collect globals once more: After optimizations we might have less globals
collect_globals::collect_globals(doc, diag);
embed_images::embed_images(
doc,
type_loader.compiler_config.embed_resources,
type_loader.compiler_config.const_scale_factor,
&type_loader.compiler_config.resource_url_mapper,
diag,
)
.await;
#[cfg(feature = "bundle-translations")]
if let Some(path) = &type_loader.compiler_config.translation_path_bundle {
match crate::translations::TranslationsBuilder::load_translations(
path,
type_loader.compiler_config.translation_domain.as_deref().unwrap_or(""),
) {
Ok(builder) => {
doc.translation_builder = Some(builder);
}
Err(err) => {
diag.push_error(
format!("Cannot load bundled translation: {err}"),
doc.node.as_ref().expect("Unexpected empty document"),
);
}
}
}
match type_loader.compiler_config.embed_resources {
#[cfg(feature = "software-renderer")]
crate::EmbedResourcesKind::EmbedTextures => {
let mut characters_seen = std::collections::HashSet::new();
// Include at least the default font sizes used in the MCU backend
let mut font_pixel_sizes =
vec![(12. * type_loader.compiler_config.const_scale_factor) as i16];
doc.visit_all_used_components(|component| {
embed_glyphs::collect_font_sizes_used(
component,
type_loader.compiler_config.const_scale_factor,
&mut font_pixel_sizes,
);
embed_glyphs::scan_string_literals(component, &mut characters_seen);
});
// This is not perfect, as this includes translations that may not be used.
#[cfg(feature = "bundle-translations")]
if let Some(translation_builder) = doc.translation_builder.as_ref() {
translation_builder.collect_characters_seen(&mut characters_seen);
}
embed_glyphs::embed_glyphs(
doc,
&type_loader.compiler_config,
font_pixel_sizes,
characters_seen,
std::iter::once(&*doc).chain(type_loader.all_documents()),
diag,
);
}
_ => {
// Create font registration calls for custom fonts, unless we're embedding pre-rendered glyphs
collect_custom_fonts::collect_custom_fonts(
doc,
std::iter::once(&*doc).chain(type_loader.all_documents()),
type_loader.compiler_config.embed_resources
== crate::EmbedResourcesKind::EmbedAllResources,
);
}
};
raw_type_loader
}
/// Run the passes on imported documents
pub fn run_import_passes(
doc: &crate::object_tree::Document,
type_loader: &crate::typeloader::TypeLoader,
diag: &mut crate::diagnostics::BuildDiagnostics,
) {
inject_debug_hooks::inject_debug_hooks(doc, type_loader);
infer_aliases_types::resolve_aliases(doc, diag);
resolving::resolve_expressions(doc, type_loader, diag);
purity_check::purity_check(doc, diag);
focus_handling::replace_forward_focus_bindings_with_focus_functions(doc, diag);
check_expressions::check_expressions(doc, diag);
check_rotation::check_rotation(doc, diag);
unique_id::check_unique_id(doc, diag);
}