slint/internal/compiler/passes/lower_component_container.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

104 lines
3.7 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
//! This pass lowers `ComponentContainer`s.
//!
//! The `ComponentContainer` is lowered to something like this:
//!
//! ```slint
//! ComponentContainer {
//! if false: Emtpy {}
//! }
//! ```
//!
//! The Repeater that is inserted is where the ComponentContainer will embed
//! its `ItemTree` under. It is never evaluated as a conditional, so we should
//! be able to use some invalid expression, but that triggers asserts in later
//! compiler passes.
//!
//! The Repeater that is marked as `is_component_placeholder`, so that
//! we can generate the expected code later.
//!
//! The `Empty` behind the conditional `Repeater` is never used.
use crate::diagnostics::BuildDiagnostics;
use crate::langtype::ElementType;
use crate::typeloader::TypeLoader;
use crate::{expression_tree, object_tree::*};
use std::cell::RefCell;
use std::rc::Rc;
pub fn lower_component_container(
doc: &Document,
type_loader: &mut TypeLoader,
diag: &mut BuildDiagnostics,
) {
let empty_type = type_loader.global_type_registry.borrow().empty_type();
doc.visit_all_used_components(|component| {
recurse_elem_including_sub_components_no_borrow(component, &(), &mut |elem, _| {
if matches!(&elem.borrow().builtin_type(), Some(b) if b.name == "ComponentContainer") {
diagnose_component_container(elem, diag);
process_component_container(elem, &empty_type);
}
})
});
}
fn diagnose_component_container(element: &ElementRc, diag: &mut BuildDiagnostics) {
let elem = element.borrow();
if !elem.children.is_empty() {
diag.push_error("ComponentContainers may not have children".into(), &*element.borrow());
}
if let Some(cip) =
elem.enclosing_component.upgrade().unwrap().child_insertion_point.borrow().clone()
{
if Rc::ptr_eq(&cip.parent, element) {
diag.push_error(
"The @children placeholder cannot appear in a ComponentContainer".into(),
&*element.borrow(),
);
}
}
}
fn process_component_container(element: &ElementRc, empty_type: &ElementType) {
let suffix = element.borrow().id.clone();
let component = Rc::new_cyclic(|component_weak| {
let root_element = Rc::new(RefCell::new(Element {
id: smol_str::format_smolstr!("component_container_internal_{}", suffix),
base_type: empty_type.clone(),
enclosing_component: component_weak.clone(),
..Default::default()
}));
Component {
node: element.borrow().debug.first().map(|n| n.node.clone().into()),
id: smol_str::format_smolstr!("ComponentContainerInternal_{}", suffix),
root_element,
..Default::default()
}
});
let mut elem = element.borrow_mut();
let embedded_element = Element::make_rc(Element {
base_type: ElementType::Component(component.clone()),
id: smol_str::format_smolstr!("component_container_placeholder_{}", suffix),
debug: elem.debug.clone(),
enclosing_component: elem.enclosing_component.clone(),
default_fill_parent: (true, true),
inline_depth: elem.inline_depth,
repeated: Some(RepeatedElementInfo {
model: expression_tree::Expression::BoolLiteral(false),
model_data_id: Default::default(),
index_id: Default::default(),
is_conditional_element: true,
is_listview: None,
}),
is_component_placeholder: true,
..Default::default()
});
elem.children.push(embedded_element);
}