mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-04 10:50:00 +00:00
Generate multiple components in Rust and C++ (#5449)
When lowering to the LLR, generate one PulbicComponent for each exported component in the main file Closes #784
This commit is contained in:
parent
c89ea56abb
commit
3764312561
18 changed files with 257 additions and 167 deletions
|
@ -71,11 +71,6 @@ pub fn generate(
|
|||
#![allow(unused_variables)]
|
||||
#![allow(unreachable_code)]
|
||||
|
||||
if matches!(doc.root_component.root_element.borrow().base_type, ElementType::Error) {
|
||||
// empty document, nothing to generate
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match format {
|
||||
#[cfg(feature = "cpp")]
|
||||
OutputFormat::Cpp(config) => {
|
||||
|
|
|
@ -326,7 +326,7 @@ mod cpp_ast {
|
|||
}
|
||||
|
||||
use crate::expression_tree::{BuiltinFunction, EasingCurve, MinMaxOp};
|
||||
use crate::langtype::{ElementType, Enumeration, EnumerationValue, NativeClass, Type};
|
||||
use crate::langtype::{Enumeration, EnumerationValue, NativeClass, Type};
|
||||
use crate::layout::Orientation;
|
||||
use crate::llr::{
|
||||
self, EvaluationContext as llr_EvaluationContext, ParentCtx as llr_ParentCtx,
|
||||
|
@ -761,14 +761,6 @@ pub fn generate(
|
|||
}
|
||||
}
|
||||
|
||||
if matches!(
|
||||
doc.root_component.root_element.borrow().base_type,
|
||||
ElementType::Error | ElementType::Global
|
||||
) {
|
||||
// empty document, nothing to generate
|
||||
return file;
|
||||
}
|
||||
|
||||
let llr = llr::lower_to_item_tree::lower_to_item_tree(&doc, compiler_config);
|
||||
|
||||
// Forward-declare the root so that sub-components can access singletons, the window, etc.
|
||||
|
|
|
@ -13,7 +13,7 @@ Some convention used in the generated code:
|
|||
*/
|
||||
|
||||
use crate::expression_tree::{BuiltinFunction, EasingCurve, MinMaxOp, OperatorClass};
|
||||
use crate::langtype::{ElementType, Enumeration, EnumerationValue, Type};
|
||||
use crate::langtype::{Enumeration, EnumerationValue, Type};
|
||||
use crate::layout::Orientation;
|
||||
use crate::llr::{
|
||||
self, EvaluationContext as llr_EvaluationContext, Expression, ParentCtx as llr_ParentCtx,
|
||||
|
@ -160,16 +160,12 @@ pub fn generate(doc: &Document, compiler_config: &CompilerConfiguration) -> Toke
|
|||
})
|
||||
.unzip();
|
||||
|
||||
if matches!(
|
||||
doc.root_component.root_element.borrow().base_type,
|
||||
ElementType::Error | ElementType::Global
|
||||
) {
|
||||
// empty document, nothing to generate
|
||||
return TokenStream::default();
|
||||
}
|
||||
|
||||
let llr = crate::llr::lower_to_item_tree::lower_to_item_tree(&doc, &compiler_config);
|
||||
|
||||
if llr.public_components.is_empty() {
|
||||
return Default::default();
|
||||
}
|
||||
|
||||
let sub_compos = llr
|
||||
.sub_components
|
||||
.iter()
|
||||
|
@ -213,7 +209,7 @@ pub fn generate(doc: &Document, compiler_config: &CompilerConfiguration) -> Toke
|
|||
const _THE_SAME_VERSION_MUST_BE_USED_FOR_THE_COMPILER_AND_THE_RUNTIME : slint::#version_check = slint::#version_check;
|
||||
}
|
||||
#[allow(unused_imports)]
|
||||
pub use slint_generated::{#(#compo_ids),* #(,#structs_and_enums_ids)* #(,#globals_ids)* #(,#named_exports)*};
|
||||
pub use slint_generated::{#(#compo_ids,)* #(#structs_and_enums_ids,)* #(#globals_ids,)* #(#named_exports,)*};
|
||||
#[allow(unused_imports)]
|
||||
pub use slint::{ComponentHandle as _, Global as _, ModelExt as _};
|
||||
}
|
||||
|
|
|
@ -19,8 +19,6 @@ pub fn lower_to_item_tree(
|
|||
) -> CompilationUnit {
|
||||
let mut state = LoweringState::default();
|
||||
|
||||
let component = &document.root_component;
|
||||
|
||||
let mut globals = Vec::new();
|
||||
for g in &document.used_types.borrow().globals {
|
||||
let count = globals.len();
|
||||
|
@ -31,22 +29,29 @@ pub fn lower_to_item_tree(
|
|||
state.sub_components.insert(ByAddress(c.clone()), sc);
|
||||
}
|
||||
|
||||
let sc = lower_sub_component(component, &state, None, &compiler_config);
|
||||
let public_properties = public_properties(component, &sc.mapping, &state);
|
||||
let mut item_tree = ItemTree {
|
||||
tree: make_tree(&state, &component.root_element, &sc, &[]),
|
||||
root: Rc::try_unwrap(sc.sub_component).unwrap(),
|
||||
parent_context: None,
|
||||
};
|
||||
// For C++ codegen, the root component must have the same name as the public component
|
||||
item_tree.root.name = component.id.clone();
|
||||
let public_components = document
|
||||
.exported_roots()
|
||||
.map(|component| {
|
||||
let sc = lower_sub_component(&component, &state, None, &compiler_config);
|
||||
let public_properties = public_properties(&component, &sc.mapping, &state);
|
||||
let mut item_tree = ItemTree {
|
||||
tree: make_tree(&state, &component.root_element, &sc, &[]),
|
||||
root: Rc::try_unwrap(sc.sub_component).unwrap(),
|
||||
parent_context: None,
|
||||
};
|
||||
// For C++ codegen, the root component must have the same name as the public component
|
||||
item_tree.root.name = component.id.clone();
|
||||
PublicComponent {
|
||||
item_tree,
|
||||
public_properties,
|
||||
private_properties: component.private_properties.borrow().clone(),
|
||||
name: component.id.clone(),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let root = CompilationUnit {
|
||||
public_components: vec![PublicComponent {
|
||||
item_tree,
|
||||
public_properties,
|
||||
private_properties: component.private_properties.borrow().clone(),
|
||||
name: component.id.clone(),
|
||||
}],
|
||||
public_components,
|
||||
globals,
|
||||
sub_components: document
|
||||
.used_types
|
||||
|
|
|
@ -44,7 +44,6 @@ pub struct Document {
|
|||
pub node: Option<syntax_nodes::Document>,
|
||||
pub inner_components: Vec<Rc<Component>>,
|
||||
pub inner_types: Vec<Type>,
|
||||
pub root_component: Rc<Component>,
|
||||
pub local_registry: TypeRegister,
|
||||
/// A list of paths to .ttf/.ttc files that are supposed to be registered on
|
||||
/// startup for custom font use.
|
||||
|
@ -165,23 +164,6 @@ impl Document {
|
|||
let mut exports = Exports::from_node(&node, &inner_components, &local_registry, diag);
|
||||
exports.add_reexports(reexports, diag);
|
||||
|
||||
let root_component = exports
|
||||
.last_exported_component
|
||||
.clone()
|
||||
.or_else(|| {
|
||||
node.ImportSpecifier()
|
||||
.last()
|
||||
.and_then(|import| {
|
||||
crate::typeloader::ImportedName::extract_imported_names(&import).last()
|
||||
})
|
||||
.and_then(|import| local_registry.lookup_element(&import.internal_name).ok())
|
||||
.and_then(|c| match c {
|
||||
ElementType::Component(c) => Some(c),
|
||||
_ => None,
|
||||
})
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let custom_fonts = foreign_imports
|
||||
.into_iter()
|
||||
.filter_map(|import| {
|
||||
|
@ -247,7 +229,6 @@ impl Document {
|
|||
|
||||
Document {
|
||||
node: Some(node),
|
||||
root_component,
|
||||
inner_components,
|
||||
inner_types,
|
||||
local_registry,
|
||||
|
@ -258,13 +239,42 @@ impl Document {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn exported_roots(&self) -> impl Iterator<Item = Rc<Component>> + DoubleEndedIterator + '_ {
|
||||
let mut iter = self
|
||||
.exports
|
||||
.iter()
|
||||
.filter_map(|e| e.1.as_ref().left())
|
||||
.filter(|c| !c.is_global())
|
||||
.cloned()
|
||||
.peekable();
|
||||
// If that is empty, we return the last import. (We need to chain because we need to return the same type for `impl Iterator`)
|
||||
let extra = if iter.peek().is_some() {
|
||||
None
|
||||
} else {
|
||||
self.node
|
||||
.as_ref()
|
||||
.and_then(|n| n.ImportSpecifier().last())
|
||||
.and_then(|import| {
|
||||
crate::typeloader::ImportedName::extract_imported_names(&import).last()
|
||||
})
|
||||
.and_then(|import| self.local_registry.lookup_element(&import.internal_name).ok())
|
||||
.and_then(|c| match c {
|
||||
ElementType::Component(c) => Some(c),
|
||||
_ => None,
|
||||
})
|
||||
};
|
||||
iter.chain(extra)
|
||||
}
|
||||
|
||||
/// visit all root and used component (including globals)
|
||||
pub fn visit_all_used_components(&self, mut v: impl FnMut(&Rc<Component>)) {
|
||||
let used_types = self.used_types.borrow();
|
||||
for c in &used_types.sub_components {
|
||||
v(c);
|
||||
}
|
||||
v(&self.root_component);
|
||||
for c in self.exported_roots() {
|
||||
v(&c);
|
||||
}
|
||||
for c in &used_types.globals {
|
||||
v(c);
|
||||
}
|
||||
|
@ -2394,7 +2404,6 @@ impl ExportedName {
|
|||
pub struct Exports {
|
||||
#[deref]
|
||||
components_or_types: Vec<(ExportedName, Either<Rc<Component>, Type>)>,
|
||||
last_exported_component: Option<Rc<Component>>,
|
||||
}
|
||||
|
||||
impl Exports {
|
||||
|
@ -2427,18 +2436,10 @@ impl Exports {
|
|||
};
|
||||
|
||||
let mut sorted_exports_with_duplicates: Vec<(ExportedName, _)> = Vec::new();
|
||||
let mut last_exported_component = None;
|
||||
|
||||
let mut extend_exports =
|
||||
|it: &mut dyn Iterator<Item = (ExportedName, Either<Rc<Component>, Type>)>| {
|
||||
for (name, compo_or_type) in it {
|
||||
match compo_or_type.as_ref().left() {
|
||||
Some(compo) if !compo.is_global() => {
|
||||
last_exported_component = Some(compo.clone())
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let pos = sorted_exports_with_duplicates
|
||||
.partition_point(|(existing_name, _)| existing_name.name <= name.name);
|
||||
sorted_exports_with_duplicates.insert(pos, (name, compo_or_type));
|
||||
|
@ -2558,12 +2559,7 @@ impl Exports {
|
|||
))
|
||||
}
|
||||
}
|
||||
|
||||
if last_exported_component.is_none() {
|
||||
last_exported_component = inner_components.last().cloned();
|
||||
}
|
||||
|
||||
Self { components_or_types: sorted_deduped_exports, last_exported_component }
|
||||
Self { components_or_types: sorted_deduped_exports }
|
||||
}
|
||||
|
||||
pub fn add_reexports(
|
||||
|
@ -2612,13 +2608,7 @@ impl Exports {
|
|||
})
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
components_or_types,
|
||||
last_exported_component: self
|
||||
.last_exported_component
|
||||
.as_ref()
|
||||
.map(|lec| snapshotter.snapshot_component(lec)),
|
||||
}
|
||||
Self { components_or_types }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -51,7 +51,6 @@ mod visible;
|
|||
mod z_order;
|
||||
|
||||
use crate::expression_tree::Expression;
|
||||
use crate::langtype::ElementType;
|
||||
use crate::namedreference::NamedReference;
|
||||
|
||||
pub async fn run_passes(
|
||||
|
@ -60,14 +59,6 @@ pub async fn run_passes(
|
|||
keep_raw: bool,
|
||||
diag: &mut crate::diagnostics::BuildDiagnostics,
|
||||
) -> Option<crate::typeloader::TypeLoader> {
|
||||
if matches!(
|
||||
doc.root_component.root_element.borrow().base_type,
|
||||
ElementType::Error | ElementType::Global
|
||||
) {
|
||||
// If there isn't a root component, we shouldn't do any of these passes
|
||||
return None;
|
||||
}
|
||||
|
||||
let style_metrics = {
|
||||
// Ignore import errors
|
||||
let mut build_diags_to_ignore = crate::diagnostics::BuildDiagnostics::default();
|
||||
|
@ -78,7 +69,6 @@ pub async fn run_passes(
|
|||
};
|
||||
|
||||
let global_type_registry = type_loader.global_type_registry.clone();
|
||||
let root_component = &doc.root_component;
|
||||
run_import_passes(doc, type_loader, diag);
|
||||
check_public_api::check_public_api(doc, diag);
|
||||
|
||||
|
@ -113,9 +103,10 @@ pub async fn run_passes(
|
|||
inlining::inline(doc, inlining::InlineSelection::InlineOnlyRequiredComponents, diag);
|
||||
collect_subcomponents::collect_subcomponents(doc);
|
||||
|
||||
focus_handling::call_focus_on_init(root_component);
|
||||
|
||||
ensure_window::ensure_window(root_component, &doc.local_registry, &style_metrics, diag);
|
||||
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);
|
||||
}
|
||||
|
||||
doc.visit_all_used_components(|component| {
|
||||
border_radius::handle_border_radius(component, diag);
|
||||
|
@ -175,7 +166,9 @@ pub async fn run_passes(
|
|||
}
|
||||
materialize_fake_properties::materialize_fake_properties(component);
|
||||
});
|
||||
lower_layout::check_window_layout(root_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 {
|
||||
|
|
|
@ -9,7 +9,9 @@ use crate::diagnostics::{BuildDiagnostics, DiagnosticLevel};
|
|||
use crate::object_tree::{Component, Document, PropertyVisibility};
|
||||
|
||||
pub fn check_public_api(doc: &Document, diag: &mut BuildDiagnostics) {
|
||||
check_public_api_component(&doc.root_component, diag);
|
||||
for c in doc.exported_roots() {
|
||||
check_public_api_component(&c, diag);
|
||||
}
|
||||
for (export_name, e) in &*doc.exports {
|
||||
if let Some(c) = e.as_ref().left() {
|
||||
if c.is_global() {
|
||||
|
|
|
@ -55,11 +55,13 @@ pub fn collect_custom_fonts<'a>(
|
|||
Box::new(|font_path| Expression::StringLiteral(font_path.clone()))
|
||||
};
|
||||
|
||||
doc.root_component.init_code.borrow_mut().font_registration_code.extend(
|
||||
all_fonts.into_iter().map(|font_path| Expression::FunctionCall {
|
||||
function: Box::new(registration_function.clone()),
|
||||
arguments: vec![prepare_font_registration_argument(font_path)],
|
||||
source_location: None,
|
||||
}),
|
||||
);
|
||||
for c in doc.exported_roots() {
|
||||
c.init_code.borrow_mut().font_registration_code.extend(all_fonts.iter().map(|font_path| {
|
||||
Expression::FunctionCall {
|
||||
function: Box::new(registration_function.clone()),
|
||||
arguments: vec![prepare_font_registration_argument(font_path)],
|
||||
source_location: None,
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,9 @@ pub fn collect_globals(doc: &Document, _diag: &mut BuildDiagnostics) {
|
|||
}
|
||||
}
|
||||
}
|
||||
collect_in_component(&doc.root_component, &mut set, &mut sorted_globals);
|
||||
for component in doc.exported_roots() {
|
||||
collect_in_component(&component, &mut set, &mut sorted_globals);
|
||||
}
|
||||
for component in &doc.used_types.borrow().sub_components {
|
||||
collect_in_component(component, &mut set, &mut sorted_globals);
|
||||
}
|
||||
|
|
|
@ -14,9 +14,9 @@ use std::rc::Rc;
|
|||
pub fn collect_subcomponents(doc: &Document) {
|
||||
let mut result = vec![];
|
||||
let mut hash = HashSet::new();
|
||||
|
||||
collect_subcomponents_recursive(&doc.root_component, &mut result, &mut hash);
|
||||
|
||||
for component in doc.exported_roots() {
|
||||
collect_subcomponents_recursive(&component, &mut result, &mut hash);
|
||||
}
|
||||
doc.used_types.borrow_mut().sub_components = result;
|
||||
}
|
||||
|
||||
|
|
|
@ -225,7 +225,9 @@ export component Foo {
|
|||
assert!(!diag.has_error());
|
||||
|
||||
let out_binding = doc
|
||||
.root_component
|
||||
.inner_components
|
||||
.last()
|
||||
.unwrap()
|
||||
.root_element
|
||||
.borrow()
|
||||
.bindings
|
||||
|
|
|
@ -43,7 +43,7 @@ pub fn embed_glyphs<'a>(
|
|||
) {
|
||||
use crate::diagnostics::Spanned;
|
||||
|
||||
let generic_diag_location = doc.root_component.root_element.borrow().to_source_location();
|
||||
let generic_diag_location = doc.node.as_ref().map(|n| n.to_source_location());
|
||||
|
||||
characters_seen.extend(
|
||||
('a'..='z')
|
||||
|
@ -100,7 +100,7 @@ fn embed_glyphs_with_fontdb<'a>(
|
|||
characters_seen: HashSet<char>,
|
||||
all_docs: impl Iterator<Item = &'a crate::object_tree::Document> + 'a,
|
||||
diag: &mut BuildDiagnostics,
|
||||
generic_diag_location: crate::diagnostics::SourceLocation,
|
||||
generic_diag_location: Option<crate::diagnostics::SourceLocation>,
|
||||
) {
|
||||
let fallback_fonts = get_fallback_fonts(fontdb);
|
||||
|
||||
|
@ -121,35 +121,25 @@ fn embed_glyphs_with_fontdb<'a>(
|
|||
let default_font_ids = if !fontdb.default_font_family_ids.is_empty() {
|
||||
fontdb.default_font_family_ids.clone()
|
||||
} else {
|
||||
let (family, source_location) = doc
|
||||
.root_component
|
||||
.root_element
|
||||
.borrow()
|
||||
.bindings
|
||||
.get("default-font-family")
|
||||
.and_then(|binding| match &binding.borrow().expression {
|
||||
Expression::StringLiteral(family) => {
|
||||
Some((Some(family.clone()), binding.borrow().span.clone()))
|
||||
doc.exported_roots().filter_map(|c| {
|
||||
c.root_element.borrow().bindings.get("default-font-family").and_then(|binding| {
|
||||
match &binding.borrow().expression {
|
||||
Expression::StringLiteral(family) => {
|
||||
Some((Some(family.clone()), binding.borrow().span.clone()))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
match fontdb.query_with_family(Default::default(), family.as_deref()) {
|
||||
Some(id) => vec![id],
|
||||
None => {
|
||||
}).filter_map(|(family, source_location)| {
|
||||
fontdb.query_with_family(Default::default(), family.as_deref()).or_else(|| {
|
||||
if let Some(source_location) = source_location {
|
||||
diag.push_error_with_span("could not find font that provides specified family, falling back to Sans-Serif".to_string(), source_location);
|
||||
} else {
|
||||
diag.push_error(
|
||||
"internal error: fontdb could not determine a default font for sans-serif"
|
||||
.to_string(),
|
||||
&generic_diag_location,
|
||||
);
|
||||
diag.push_error("internal error: fontdb could not determine a default font for sans-serif" .to_string(), &generic_diag_location);
|
||||
};
|
||||
return;
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
}).collect()
|
||||
};
|
||||
|
||||
let default_font_paths = default_font_ids
|
||||
|
@ -265,16 +255,16 @@ fn embed_glyphs_with_fontdb<'a>(
|
|||
},
|
||||
);
|
||||
|
||||
doc.root_component.init_code.borrow_mut().font_registration_code.push(
|
||||
Expression::FunctionCall {
|
||||
for c in doc.exported_roots() {
|
||||
c.init_code.borrow_mut().font_registration_code.push(Expression::FunctionCall {
|
||||
function: Box::new(Expression::BuiltinFunctionReference(
|
||||
BuiltinFunction::RegisterBitmapFont,
|
||||
None,
|
||||
)),
|
||||
arguments: vec![Expression::NumberLiteral(resource_id as _, Unit::None)],
|
||||
source_location: None,
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Make sure to embed the default font first, because that becomes the default at run-time.
|
||||
|
|
|
@ -12,7 +12,7 @@ use std::cell::RefCell;
|
|||
use std::collections::{HashMap, HashSet};
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub enum InlineSelection {
|
||||
InlineAllComponents,
|
||||
InlineOnlyRequiredComponents,
|
||||
|
@ -21,6 +21,7 @@ pub enum InlineSelection {
|
|||
pub fn inline(doc: &Document, inline_selection: InlineSelection, diag: &mut BuildDiagnostics) {
|
||||
fn inline_components_recursively(
|
||||
component: &Rc<Component>,
|
||||
roots: &HashSet<ByAddress<Rc<Component>>>,
|
||||
inline_selection: InlineSelection,
|
||||
diag: &mut BuildDiagnostics,
|
||||
) {
|
||||
|
@ -28,7 +29,7 @@ pub fn inline(doc: &Document, inline_selection: InlineSelection, diag: &mut Buil
|
|||
let base = elem.borrow().base_type.clone();
|
||||
if let ElementType::Component(c) = base {
|
||||
// First, make sure that the component itself is properly inlined
|
||||
inline_components_recursively(&c, inline_selection, diag);
|
||||
inline_components_recursively(&c, roots, inline_selection, diag);
|
||||
|
||||
if c.parent_element.upgrade().is_some() {
|
||||
// We should not inline a repeated element
|
||||
|
@ -43,27 +44,30 @@ pub fn inline(doc: &Document, inline_selection: InlineSelection, diag: &mut Buil
|
|||
|| element_require_inlining(elem)
|
||||
// We always inline the root in case the element that instantiate this component needs full inlining
|
||||
|| Rc::ptr_eq(elem, &component.root_element)
|
||||
// We always inline other roots as a component can't be both a sub component and a root
|
||||
|| roots.contains(&ByAddress(c.clone()))
|
||||
}
|
||||
} {
|
||||
inline_element(elem, &c, component, diag);
|
||||
}
|
||||
}
|
||||
});
|
||||
component
|
||||
.popup_windows
|
||||
.borrow()
|
||||
.iter()
|
||||
.for_each(|p| inline_components_recursively(&p.component, inline_selection, diag))
|
||||
component.popup_windows.borrow().iter().for_each(|p| {
|
||||
inline_components_recursively(&p.component, roots, inline_selection, diag)
|
||||
})
|
||||
}
|
||||
let mut roots = HashSet::new();
|
||||
if inline_selection == InlineSelection::InlineOnlyRequiredComponents {
|
||||
for component in doc.exported_roots() {
|
||||
roots.insert(ByAddress(component.clone()));
|
||||
}
|
||||
}
|
||||
for component in doc.exported_roots() {
|
||||
inline_components_recursively(&component, &roots, inline_selection, diag);
|
||||
let mut init_code = component.init_code.borrow_mut();
|
||||
let inlined_init_code = core::mem::take(&mut init_code.inlined_init_code);
|
||||
init_code.constructor_code.splice(0..0, inlined_init_code.into_values());
|
||||
}
|
||||
inline_components_recursively(&doc.root_component, inline_selection, diag);
|
||||
|
||||
let mut init_code = doc.root_component.init_code.borrow_mut();
|
||||
let inlined_init_code = core::mem::take(&mut init_code.inlined_init_code);
|
||||
init_code.constructor_code.splice(0..0, inlined_init_code.into_values());
|
||||
}
|
||||
|
||||
fn clone_tuple<U: Clone, V: Clone>((u, v): (&U, &V)) -> (U, V) {
|
||||
(u.clone(), v.clone())
|
||||
}
|
||||
|
||||
fn element_key(e: ElementRc) -> ByAddress<ElementRc> {
|
||||
|
@ -90,7 +94,11 @@ fn inline_element(
|
|||
let priority_delta = 1 + elem_mut.inline_depth;
|
||||
elem_mut.base_type = inlined_component.root_element.borrow().base_type.clone();
|
||||
elem_mut.property_declarations.extend(
|
||||
inlined_component.root_element.borrow().property_declarations.iter().map(clone_tuple),
|
||||
inlined_component.root_element.borrow().property_declarations.iter().map(|(name, decl)| {
|
||||
let mut decl = decl.clone();
|
||||
decl.expose_in_public_api = false;
|
||||
(name.clone(), decl)
|
||||
}),
|
||||
);
|
||||
|
||||
for (p, a) in inlined_component.root_element.borrow().property_analysis.borrow().iter() {
|
||||
|
|
|
@ -25,6 +25,16 @@ fn create_repeater_components(component: &Rc<Component>) {
|
|||
let parent_element = Rc::downgrade(elem);
|
||||
let mut elem = elem.borrow_mut();
|
||||
|
||||
if matches!(&elem.base_type, ElementType::Component(c) if c.parent_element.upgrade().is_some())
|
||||
{
|
||||
debug_assert!(std::rc::Weak::ptr_eq(
|
||||
&parent_element,
|
||||
&elem.base_type.as_component().parent_element
|
||||
));
|
||||
// Already processed (can happen if a component is both used and exported root)
|
||||
return;
|
||||
}
|
||||
|
||||
let comp = Rc::new(Component {
|
||||
root_element: Rc::new(RefCell::new(Element {
|
||||
id: elem.id.clone(),
|
||||
|
|
|
@ -12,7 +12,9 @@ use std::rc::Rc;
|
|||
/// It currently does so by adding a number to the existing id
|
||||
pub fn assign_unique_id(doc: &Document) {
|
||||
let mut count = 0;
|
||||
assign_unique_id_in_component(&doc.root_component, &mut count);
|
||||
for component in doc.exported_roots() {
|
||||
assign_unique_id_in_component(&component, &mut count);
|
||||
}
|
||||
for c in &doc.used_types.borrow().sub_components {
|
||||
assign_unique_id_in_component(c, &mut count);
|
||||
}
|
||||
|
|
|
@ -183,7 +183,6 @@ impl Snapshotter {
|
|||
}
|
||||
|
||||
fn snapshot_document(&mut self, document: &object_tree::Document) -> object_tree::Document {
|
||||
let root_component = self.snapshot_component(&document.root_component);
|
||||
let inner_components =
|
||||
document.inner_components.iter().map(|ic| self.snapshot_component(ic)).collect();
|
||||
let exports = document.exports.snapshot(self);
|
||||
|
@ -192,7 +191,6 @@ impl Snapshotter {
|
|||
node: document.node.clone(),
|
||||
inner_components,
|
||||
inner_types: document.inner_types.clone(),
|
||||
root_component,
|
||||
local_registry: document.local_registry.snapshot(self),
|
||||
custom_fonts: document.custom_fonts.clone(),
|
||||
exports,
|
||||
|
|
|
@ -8,7 +8,7 @@ use core::ptr::NonNull;
|
|||
use dynamic_type::{Instance, InstanceBox};
|
||||
use i_slint_compiler::diagnostics::SourceFileVersion;
|
||||
use i_slint_compiler::expression_tree::{Expression, NamedReference};
|
||||
use i_slint_compiler::langtype::{ElementType, Type};
|
||||
use i_slint_compiler::langtype::Type;
|
||||
use i_slint_compiler::object_tree::ElementRc;
|
||||
use i_slint_compiler::{diagnostics::BuildDiagnostics, object_tree::PropertyDeclaration};
|
||||
use i_slint_compiler::{generator, object_tree, parser, CompilerConfiguration};
|
||||
|
@ -844,17 +844,20 @@ pub async fn load(
|
|||
#[allow(unused_mut)]
|
||||
let mut it = {
|
||||
let doc = loader.get_document(&path).unwrap();
|
||||
if matches!(
|
||||
doc.root_component.root_element.borrow().base_type,
|
||||
ElementType::Global | ElementType::Error
|
||||
) {
|
||||
let root_component = doc
|
||||
.exports
|
||||
.iter()
|
||||
.filter_map(|e| Some((&e.0.name_ident, e.1.as_ref().left()?)))
|
||||
.max_by_key(|(n, _)| n.text_range().end())
|
||||
.map(|(_, c)| c.clone());
|
||||
let Some(root_component) = root_component.or_else(|| doc.exported_roots().last())
|
||||
else {
|
||||
diag.push_error_with_span("No component found".into(), Default::default());
|
||||
return (Err(()), diag);
|
||||
}
|
||||
};
|
||||
|
||||
let compiled_globals = CompiledGlobalCollection::compile(&doc);
|
||||
|
||||
generate_item_tree(&doc.root_component, Some(compiled_globals), guard)
|
||||
generate_item_tree(&root_component, Some(compiled_globals), guard)
|
||||
};
|
||||
|
||||
#[cfg(feature = "highlight")]
|
||||
|
|
100
tests/cases/exports/multiple_components.slint
Normal file
100
tests/cases/exports/multiple_components.slint
Normal file
|
@ -0,0 +1,100 @@
|
|||
// 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
|
||||
|
||||
export global G {
|
||||
in property <string> global-property: "Hello";
|
||||
}
|
||||
|
||||
struct S { val: string }
|
||||
|
||||
component Shared {
|
||||
in property <string> n;
|
||||
out property <S> out: { val: G.global-property + " " + n };
|
||||
for xx in 2 : Rectangle {}
|
||||
}
|
||||
|
||||
/// This component is both exported and Used
|
||||
export component Used {
|
||||
in-out property <int> name;
|
||||
for xx in 4 : Rectangle { }
|
||||
}
|
||||
|
||||
export component FirstTest {
|
||||
|
||||
out property <string> global-prop: G.global-property;
|
||||
out property <string> o: shared.out.val;
|
||||
shared := Shared {
|
||||
n: "Oli";
|
||||
}
|
||||
|
||||
Used {}
|
||||
|
||||
// FIXME! as of now, only the last component's test property is evaluated by the test framework
|
||||
out property <bool> test: false;
|
||||
}
|
||||
|
||||
export component Z {
|
||||
out property <bool> test: false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export component SecondTest {
|
||||
out property <string> global-prop: G.global-property;
|
||||
out property <string> out: shared.out.val;
|
||||
|
||||
shared := Shared {
|
||||
n: "Sim";
|
||||
}
|
||||
|
||||
out property <bool> test: out == "Hello Sim";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
```rust
|
||||
let instance1 = FirstTest::new().unwrap();
|
||||
|
||||
instance1.global::<G<'_>>().set_global_property("Hallo".into());
|
||||
|
||||
let instance2 = SecondTest::new().unwrap();
|
||||
|
||||
let instance3 = SecondTest::new().unwrap();
|
||||
|
||||
instance3.global::<G<'_>>().set_global_property("Bonjour".into());
|
||||
|
||||
assert_eq!(instance1.get_o(), "Hallo Oli");
|
||||
assert_eq!(instance2.get_out(), "Hello Sim");
|
||||
assert_eq!(instance3.get_out(), "Bonjour Sim");
|
||||
```
|
||||
|
||||
```cpp
|
||||
auto handle1 = FirstTest::create();
|
||||
const FirstTest &instance1 = *handle1;
|
||||
instance1.global<G>().set_global_property("Hallo");
|
||||
|
||||
auto handle2 = SecondTest::create();
|
||||
const SecondTest &instance2 = *handle2;
|
||||
|
||||
auto handle3 = SecondTest::create();
|
||||
const SecondTest &instance3 = *handle3;
|
||||
|
||||
instance3.global<G>().set_global_property("Bonjour");
|
||||
|
||||
assert_eq(instance1.get_o(), "Hallo Oli");
|
||||
assert_eq(instance2.get_out(), "Hello Sim");
|
||||
assert_eq(instance3.get_out(), "Bonjour Sim");
|
||||
|
||||
struct Shared {};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue