mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-03 18:29:09 +00:00

... while still being kebab-case in .slint Some enums might become public API and we want to have them as PascalCase to respect the rust conventions
667 lines
24 KiB
Rust
667 lines
24 KiB
Rust
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
|
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
|
|
|
|
use by_address::ByAddress;
|
|
|
|
use crate::expression_tree::Expression as tree_Expression;
|
|
use crate::langtype::Type;
|
|
use crate::llr::item_tree::*;
|
|
use crate::namedreference::NamedReference;
|
|
use crate::object_tree::{Component, ElementRc};
|
|
use std::collections::HashMap;
|
|
use std::rc::Rc;
|
|
|
|
use super::lower_expression::ExpressionContext;
|
|
|
|
pub fn lower_to_item_tree(component: &Rc<Component>) -> PublicComponent {
|
|
let mut state = LoweringState::default();
|
|
|
|
let mut globals = Vec::new();
|
|
for g in &component.used_types.borrow().globals {
|
|
let count = globals.len();
|
|
globals.push(lower_global(g, count, &mut state));
|
|
}
|
|
for c in &component.used_types.borrow().sub_components {
|
|
let sc = lower_sub_component(c, &state, None);
|
|
state.sub_components.insert(ByAddress(c.clone()), sc);
|
|
}
|
|
|
|
let sc = lower_sub_component(component, &state, None);
|
|
let public_properties = public_properties(component, &sc.mapping, &state);
|
|
let item_tree = ItemTree {
|
|
tree: make_tree(&state, &component.root_element, &sc, &[]),
|
|
root: Rc::try_unwrap(sc.sub_component).unwrap(),
|
|
parent_context: None,
|
|
};
|
|
let root = PublicComponent {
|
|
item_tree,
|
|
globals,
|
|
sub_components: component
|
|
.used_types
|
|
.borrow()
|
|
.sub_components
|
|
.iter()
|
|
.map(|tree_sub_compo| {
|
|
state.sub_components[&ByAddress(tree_sub_compo.clone())].sub_component.clone()
|
|
})
|
|
.collect(),
|
|
public_properties,
|
|
};
|
|
super::optim_passes::run_passes(&root);
|
|
root
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub struct LoweringState {
|
|
global_properties: HashMap<NamedReference, PropertyReference>,
|
|
sub_components: HashMap<ByAddress<Rc<Component>>, LoweredSubComponent>,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum LoweredElement {
|
|
SubComponent { sub_component_index: usize },
|
|
NativeItem { item_index: usize },
|
|
Repeated { repeated_index: usize },
|
|
}
|
|
|
|
#[derive(Default, Debug, Clone)]
|
|
pub struct LoweredSubComponentMapping {
|
|
pub element_mapping: HashMap<ByAddress<ElementRc>, LoweredElement>,
|
|
pub property_mapping: HashMap<NamedReference, PropertyReference>,
|
|
}
|
|
|
|
impl LoweredSubComponentMapping {
|
|
pub fn map_property_reference(
|
|
&self,
|
|
from: &NamedReference,
|
|
state: &LoweringState,
|
|
) -> PropertyReference {
|
|
if let Some(x) = self.property_mapping.get(from) {
|
|
return x.clone();
|
|
}
|
|
if let Some(x) = state.global_properties.get(from) {
|
|
return x.clone();
|
|
}
|
|
let element = from.element();
|
|
if let Some(alias) = element
|
|
.borrow()
|
|
.property_declarations
|
|
.get(from.name())
|
|
.and_then(|x| x.is_alias.as_ref())
|
|
{
|
|
return self.map_property_reference(alias, state);
|
|
}
|
|
match self.element_mapping.get(&element.clone().into()).unwrap() {
|
|
LoweredElement::SubComponent { sub_component_index } => {
|
|
if let Type::Component(base) = &element.borrow().base_type {
|
|
return property_reference_within_sub_component(
|
|
state.map_property_reference(&NamedReference::new(
|
|
&base.root_element,
|
|
from.name(),
|
|
)),
|
|
*sub_component_index,
|
|
);
|
|
}
|
|
unreachable!()
|
|
}
|
|
LoweredElement::NativeItem { item_index } => {
|
|
return PropertyReference::InNativeItem {
|
|
sub_component_path: vec![],
|
|
item_index: *item_index,
|
|
prop_name: from.name().into(),
|
|
};
|
|
}
|
|
LoweredElement::Repeated { .. } => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct LoweredSubComponent {
|
|
sub_component: Rc<SubComponent>,
|
|
mapping: LoweredSubComponentMapping,
|
|
}
|
|
|
|
impl LoweringState {
|
|
pub fn map_property_reference(&self, from: &NamedReference) -> PropertyReference {
|
|
if let Some(x) = self.global_properties.get(from) {
|
|
return x.clone();
|
|
}
|
|
|
|
let element = from.element();
|
|
let enclosing = self
|
|
.sub_components
|
|
.get(&element.borrow().enclosing_component.upgrade().unwrap().into())
|
|
.unwrap();
|
|
|
|
enclosing.mapping.map_property_reference(from, self)
|
|
}
|
|
}
|
|
|
|
// Map a PropertyReference within a `sub_component` to a PropertyReference to the component containing it
|
|
fn property_reference_within_sub_component(
|
|
mut prop_ref: PropertyReference,
|
|
sub_component: usize,
|
|
) -> PropertyReference {
|
|
match &mut prop_ref {
|
|
PropertyReference::Local { sub_component_path, .. }
|
|
| PropertyReference::InNativeItem { sub_component_path, .. } => {
|
|
sub_component_path.insert(0, sub_component);
|
|
}
|
|
PropertyReference::InParent { .. } => panic!("the sub-component had no parents"),
|
|
PropertyReference::Global { .. } => (),
|
|
}
|
|
prop_ref
|
|
}
|
|
|
|
impl LoweringState {
|
|
fn sub_component(&self, component: &Rc<Component>) -> &LoweredSubComponent {
|
|
&self.sub_components[&ByAddress(component.clone())]
|
|
}
|
|
}
|
|
|
|
fn component_id(component: &Rc<Component>) -> String {
|
|
if component.is_global() {
|
|
component.root_element.borrow().id.clone()
|
|
} else if component.id.is_empty() {
|
|
format!("Component_{}", component.root_element.borrow().id)
|
|
} else if component.is_sub_component() {
|
|
format!("{}_{}", component.id, component.root_element.borrow().id)
|
|
} else {
|
|
component.id.clone()
|
|
}
|
|
}
|
|
|
|
fn lower_sub_component(
|
|
component: &Rc<Component>,
|
|
state: &LoweringState,
|
|
parent_context: Option<&ExpressionContext>,
|
|
) -> LoweredSubComponent {
|
|
let mut sub_component = SubComponent {
|
|
name: component_id(component),
|
|
properties: Default::default(),
|
|
items: Default::default(),
|
|
repeated: Default::default(),
|
|
popup_windows: Default::default(),
|
|
sub_components: Default::default(),
|
|
property_init: Default::default(),
|
|
animations: Default::default(),
|
|
two_way_bindings: Default::default(),
|
|
const_properties: Default::default(),
|
|
init_code: Default::default(),
|
|
// just initialize to dummy expression right now and it will be set later
|
|
layout_info_h: super::Expression::BoolLiteral(false).into(),
|
|
layout_info_v: super::Expression::BoolLiteral(false).into(),
|
|
accessible_prop: Default::default(),
|
|
prop_analysis: Default::default(),
|
|
};
|
|
let mut mapping = LoweredSubComponentMapping::default();
|
|
let mut repeated = vec![];
|
|
let mut accessible_prop = Vec::new();
|
|
|
|
if let Some(parent) = component.parent_element.upgrade() {
|
|
// Add properties for the model data and index
|
|
if parent.borrow().repeated.as_ref().map_or(false, |x| !x.is_conditional_element) {
|
|
sub_component.properties.push(Property {
|
|
name: "model_data".into(),
|
|
ty: crate::expression_tree::Expression::RepeaterModelReference {
|
|
element: component.parent_element.clone(),
|
|
}
|
|
.ty(),
|
|
..Property::default()
|
|
});
|
|
sub_component.properties.push(Property {
|
|
name: "model_index".into(),
|
|
ty: Type::Int32,
|
|
..Property::default()
|
|
});
|
|
}
|
|
};
|
|
|
|
let s: Option<ElementRc> = None;
|
|
let mut repeater_offset = 0;
|
|
crate::object_tree::recurse_elem(&component.root_element, &s, &mut |element, parent| {
|
|
let elem = element.borrow();
|
|
for (p, x) in &elem.property_declarations {
|
|
if x.is_alias.is_some() {
|
|
continue;
|
|
}
|
|
let property_index = sub_component.properties.len();
|
|
mapping.property_mapping.insert(
|
|
NamedReference::new(element, p),
|
|
PropertyReference::Local { sub_component_path: vec![], property_index },
|
|
);
|
|
sub_component.properties.push(Property {
|
|
name: format!("{}_{}", elem.id, p),
|
|
ty: x.property_type.clone(),
|
|
..Property::default()
|
|
});
|
|
}
|
|
if elem.repeated.is_some() {
|
|
mapping.element_mapping.insert(
|
|
element.clone().into(),
|
|
LoweredElement::Repeated { repeated_index: repeated.len() },
|
|
);
|
|
repeated.push(element.clone());
|
|
return None;
|
|
}
|
|
match &elem.base_type {
|
|
Type::Component(comp) => {
|
|
let lc = state.sub_component(comp);
|
|
let ty = lc.sub_component.clone();
|
|
let sub_component_index = sub_component.sub_components.len();
|
|
mapping.element_mapping.insert(
|
|
element.clone().into(),
|
|
LoweredElement::SubComponent { sub_component_index },
|
|
);
|
|
sub_component.sub_components.push(SubComponentInstance {
|
|
ty: ty.clone(),
|
|
name: elem.id.clone(),
|
|
index_in_tree: *elem.item_index.get().unwrap(),
|
|
index_of_first_child_in_tree: *elem.item_index_of_first_children.get().unwrap(),
|
|
repeater_offset,
|
|
});
|
|
repeater_offset += ty.repeater_count();
|
|
}
|
|
|
|
Type::Native(n) => {
|
|
let item_index = sub_component.items.len();
|
|
mapping
|
|
.element_mapping
|
|
.insert(element.clone().into(), LoweredElement::NativeItem { item_index });
|
|
let is_flickable_viewport = elem.is_flickable_viewport;
|
|
sub_component.items.push(Item {
|
|
ty: n.clone(),
|
|
name: if is_flickable_viewport {
|
|
parent.as_ref().unwrap().borrow().id.clone()
|
|
} else {
|
|
elem.id.clone()
|
|
},
|
|
index_in_tree: *elem.item_index.get().unwrap(),
|
|
is_flickable_viewport,
|
|
})
|
|
}
|
|
_ => unreachable!(),
|
|
};
|
|
for (key, nr) in &elem.accessibility_props.0 {
|
|
// TODO: we also want to split by type (role/string/...)
|
|
let enum_value =
|
|
crate::generator::to_pascal_case(key.strip_prefix("accessible-").unwrap());
|
|
accessible_prop.push((*elem.item_index.get().unwrap(), enum_value, nr.clone()));
|
|
}
|
|
Some(element.clone())
|
|
});
|
|
let ctx = ExpressionContext { mapping: &mapping, state, parent: parent_context, component };
|
|
crate::generator::handle_property_bindings_init(component, |e, p, binding| {
|
|
let prop = ctx.map_property_reference(&NamedReference::new(e, p));
|
|
for tw in &binding.two_way_bindings {
|
|
sub_component.two_way_bindings.push((prop.clone(), ctx.map_property_reference(tw)))
|
|
}
|
|
if !matches!(binding.expression, tree_Expression::Invalid) {
|
|
let expression =
|
|
super::lower_expression::lower_expression(&binding.expression, &ctx).into();
|
|
|
|
let is_constant = binding.analysis.as_ref().map_or(false, |a| a.is_const);
|
|
let animation = binding
|
|
.animation
|
|
.as_ref()
|
|
.filter(|_| !is_constant)
|
|
.map(|a| super::lower_expression::lower_animation(a, &ctx));
|
|
|
|
sub_component.prop_analysis.insert(
|
|
prop.clone(),
|
|
PropAnalysis {
|
|
property_init: Some(sub_component.property_init.len()),
|
|
analysis: get_property_analysis(e, p),
|
|
},
|
|
);
|
|
|
|
let is_state_info = matches!(
|
|
e.borrow().lookup_property(p).property_type,
|
|
Type::Struct { name: Some(name), .. } if name.ends_with("::StateInfo")
|
|
);
|
|
|
|
sub_component.property_init.push((
|
|
prop.clone(),
|
|
BindingExpression {
|
|
expression,
|
|
animation,
|
|
is_constant,
|
|
is_state_info,
|
|
use_count: 0.into(),
|
|
},
|
|
));
|
|
}
|
|
|
|
if e.borrow()
|
|
.property_analysis
|
|
.borrow()
|
|
.get(p)
|
|
.map_or(true, |a| a.is_set || a.is_set_externally)
|
|
{
|
|
if let Some(anim) = binding.animation.as_ref() {
|
|
match super::lower_expression::lower_animation(anim, &ctx) {
|
|
Animation::Static(anim) => {
|
|
sub_component.animations.insert(prop, anim);
|
|
}
|
|
Animation::Transition(_) => {
|
|
// Cannot set a property with a transition anyway
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
sub_component.repeated =
|
|
repeated.into_iter().map(|elem| lower_repeated_component(&elem, &ctx)).collect();
|
|
for s in &mut sub_component.sub_components {
|
|
s.repeater_offset += sub_component.repeated.len();
|
|
}
|
|
sub_component.popup_windows = component
|
|
.popup_windows
|
|
.borrow()
|
|
.iter()
|
|
.map(|popup| lower_popup_component(&popup.component, &ctx))
|
|
.collect();
|
|
|
|
crate::generator::for_each_const_properties(component, |elem, n| {
|
|
let x = ctx.map_property_reference(&NamedReference::new(elem, n));
|
|
sub_component.const_properties.push(x);
|
|
});
|
|
|
|
sub_component.init_code = component
|
|
.setup_code
|
|
.borrow()
|
|
.iter()
|
|
.map(|e| super::lower_expression::lower_expression(e, &ctx).into())
|
|
.collect();
|
|
|
|
sub_component.layout_info_h = super::lower_expression::get_layout_info(
|
|
&component.root_element,
|
|
&ctx,
|
|
&component.root_constraints.borrow(),
|
|
crate::layout::Orientation::Horizontal,
|
|
)
|
|
.into();
|
|
sub_component.layout_info_v = super::lower_expression::get_layout_info(
|
|
&component.root_element,
|
|
&ctx,
|
|
&component.root_constraints.borrow(),
|
|
crate::layout::Orientation::Vertical,
|
|
)
|
|
.into();
|
|
|
|
sub_component.accessible_prop = accessible_prop
|
|
.into_iter()
|
|
.map(|(idx, key, nr)| {
|
|
let mut expr = super::Expression::PropertyReference(ctx.map_property_reference(&nr));
|
|
match nr.ty() {
|
|
Type::Bool => {
|
|
expr = super::Expression::Condition {
|
|
condition: expr.into(),
|
|
true_expr: super::Expression::StringLiteral("true".into()).into(),
|
|
false_expr: super::Expression::StringLiteral("false".into()).into(),
|
|
};
|
|
}
|
|
Type::Int32 | Type::Float32 => {
|
|
expr = super::Expression::Cast { from: expr.into(), to: Type::String };
|
|
}
|
|
Type::String => {}
|
|
Type::Enumeration(e) if e.name == "AccessibleRole" => {}
|
|
_ => panic!("Invalid type for accessible property"),
|
|
}
|
|
|
|
((idx, key), expr.into())
|
|
})
|
|
.collect();
|
|
|
|
LoweredSubComponent { sub_component: Rc::new(sub_component), mapping }
|
|
}
|
|
|
|
fn get_property_analysis(elem: &ElementRc, p: &str) -> crate::object_tree::PropertyAnalysis {
|
|
let mut a = elem.borrow().property_analysis.borrow().get(p).cloned().unwrap_or_default();
|
|
let mut elem = elem.clone();
|
|
loop {
|
|
let base = elem.borrow().base_type.clone();
|
|
match base {
|
|
Type::Native(n) => {
|
|
if n.properties.get(p).map_or(false, |p| p.is_native_output) {
|
|
a.is_set = true;
|
|
}
|
|
}
|
|
Type::Component(c) => {
|
|
elem = c.root_element.clone();
|
|
if let Some(a2) = elem.borrow().property_analysis.borrow().get(p) {
|
|
a.merge_with_base(a2);
|
|
}
|
|
continue;
|
|
}
|
|
_ => (),
|
|
};
|
|
return a;
|
|
}
|
|
}
|
|
|
|
fn lower_repeated_component(elem: &ElementRc, ctx: &ExpressionContext) -> RepeatedElement {
|
|
let e = elem.borrow();
|
|
let component = e.base_type.as_component().clone();
|
|
let repeated = e.repeated.as_ref().unwrap();
|
|
|
|
let sc = lower_sub_component(&component, ctx.state, Some(ctx));
|
|
|
|
let map_inner_prop = |p| {
|
|
sc.mapping
|
|
.map_property_reference(&NamedReference::new(&component.root_element, p), ctx.state)
|
|
};
|
|
|
|
let listview = repeated.is_listview.as_ref().map(|lv| ListViewInfo {
|
|
viewport_y: ctx.map_property_reference(&lv.viewport_y),
|
|
viewport_height: ctx.map_property_reference(&lv.viewport_height),
|
|
viewport_width: ctx.map_property_reference(&lv.viewport_width),
|
|
listview_height: ctx.map_property_reference(&lv.listview_height),
|
|
listview_width: ctx.map_property_reference(&lv.listview_width),
|
|
|
|
prop_y: map_inner_prop("y"),
|
|
prop_width: map_inner_prop("width"),
|
|
prop_height: map_inner_prop("height"),
|
|
});
|
|
|
|
RepeatedElement {
|
|
model: super::lower_expression::lower_expression(&repeated.model, ctx).into(),
|
|
sub_tree: ItemTree {
|
|
tree: make_tree(ctx.state, &component.root_element, &sc, &[]),
|
|
root: Rc::try_unwrap(sc.sub_component).unwrap(),
|
|
parent_context: Some(e.enclosing_component.upgrade().unwrap().id.clone()),
|
|
},
|
|
index_prop: (!repeated.is_conditional_element).then(|| 1),
|
|
data_prop: (!repeated.is_conditional_element).then(|| 0),
|
|
index_in_tree: *e.item_index.get().unwrap(),
|
|
listview,
|
|
}
|
|
}
|
|
|
|
fn lower_popup_component(component: &Rc<Component>, ctx: &ExpressionContext) -> ItemTree {
|
|
let sc = lower_sub_component(component, ctx.state, Some(ctx));
|
|
ItemTree {
|
|
tree: make_tree(ctx.state, &component.root_element, &sc, &[]),
|
|
root: Rc::try_unwrap(sc.sub_component).unwrap(),
|
|
parent_context: Some(
|
|
component
|
|
.parent_element
|
|
.upgrade()
|
|
.unwrap()
|
|
.borrow()
|
|
.enclosing_component
|
|
.upgrade()
|
|
.unwrap()
|
|
.id
|
|
.clone(),
|
|
),
|
|
}
|
|
}
|
|
|
|
fn lower_global(
|
|
global: &Rc<Component>,
|
|
global_index: usize,
|
|
state: &mut LoweringState,
|
|
) -> GlobalComponent {
|
|
let mut mapping = LoweredSubComponentMapping::default();
|
|
let mut properties = vec![];
|
|
let mut const_properties = vec![];
|
|
let mut prop_analysis = vec![];
|
|
|
|
for (p, x) in &global.root_element.borrow().property_declarations {
|
|
let property_index = properties.len();
|
|
let nr = NamedReference::new(&global.root_element, p);
|
|
mapping.property_mapping.insert(
|
|
nr.clone(),
|
|
PropertyReference::Local { sub_component_path: vec![], property_index },
|
|
);
|
|
|
|
properties.push(Property {
|
|
name: p.clone(),
|
|
ty: x.property_type.clone(),
|
|
..Property::default()
|
|
});
|
|
if !matches!(x.property_type, Type::Callback { .. }) {
|
|
const_properties.push(nr.is_constant());
|
|
} else {
|
|
const_properties.push(false);
|
|
}
|
|
prop_analysis.push(
|
|
global
|
|
.root_element
|
|
.borrow()
|
|
.property_analysis
|
|
.borrow()
|
|
.get(p)
|
|
.cloned()
|
|
.unwrap_or_default(),
|
|
);
|
|
state
|
|
.global_properties
|
|
.insert(nr.clone(), PropertyReference::Global { global_index, property_index });
|
|
}
|
|
|
|
let mut init_values = vec![None; properties.len()];
|
|
|
|
let ctx = ExpressionContext { mapping: &mapping, state, parent: None, component: global };
|
|
for (prop, binding) in &global.root_element.borrow().bindings {
|
|
assert!(binding.borrow().two_way_bindings.is_empty());
|
|
assert!(binding.borrow().animation.is_none());
|
|
let expression =
|
|
super::lower_expression::lower_expression(&binding.borrow().expression, &ctx).into();
|
|
|
|
let nr = NamedReference::new(&global.root_element, prop);
|
|
let property_index = match mapping.property_mapping[&nr] {
|
|
PropertyReference::Local { property_index, .. } => property_index,
|
|
_ => unreachable!(),
|
|
};
|
|
let is_constant = binding.borrow().analysis.as_ref().map_or(false, |a| a.is_const);
|
|
init_values[property_index] = Some(BindingExpression {
|
|
expression,
|
|
animation: None,
|
|
is_constant,
|
|
is_state_info: false,
|
|
use_count: 0.into(),
|
|
});
|
|
}
|
|
|
|
let is_builtin = if let Some(builtin) = global.root_element.borrow().native_class() {
|
|
// We just generate the property so we know how to address them
|
|
for (p, x) in &builtin.properties {
|
|
let property_index = properties.len();
|
|
properties.push(Property { name: p.clone(), ty: x.ty.clone(), ..Property::default() });
|
|
let nr = NamedReference::new(&global.root_element, p);
|
|
state
|
|
.global_properties
|
|
.insert(nr, PropertyReference::Global { global_index, property_index });
|
|
prop_analysis.push(
|
|
global
|
|
.root_element
|
|
.borrow()
|
|
.property_analysis
|
|
.borrow()
|
|
.get(p)
|
|
.cloned()
|
|
.unwrap_or_default(),
|
|
);
|
|
}
|
|
true
|
|
} else {
|
|
false
|
|
};
|
|
|
|
let public_properties = public_properties(global, &mapping, state);
|
|
GlobalComponent {
|
|
name: global.root_element.borrow().id.clone(),
|
|
properties,
|
|
init_values,
|
|
const_properties,
|
|
public_properties,
|
|
exported: !global.exported_global_names.borrow().is_empty(),
|
|
aliases: global.global_aliases(),
|
|
is_builtin,
|
|
prop_analysis,
|
|
}
|
|
}
|
|
|
|
fn make_tree(
|
|
state: &LoweringState,
|
|
element: &ElementRc,
|
|
component: &LoweredSubComponent,
|
|
sub_component_path: &[usize],
|
|
) -> TreeNode {
|
|
let e = element.borrow();
|
|
let children = e.children.iter().map(|c| make_tree(state, c, component, sub_component_path));
|
|
match component.mapping.element_mapping.get(&ByAddress(element.clone())).unwrap() {
|
|
LoweredElement::SubComponent { sub_component_index } => {
|
|
let sub_component = e.sub_component().unwrap();
|
|
let new_sub_component_path = sub_component_path
|
|
.iter()
|
|
.copied()
|
|
.chain(std::iter::once(*sub_component_index))
|
|
.collect::<Vec<_>>();
|
|
let mut tree_node = make_tree(
|
|
state,
|
|
&sub_component.root_element,
|
|
state.sub_component(sub_component),
|
|
&new_sub_component_path,
|
|
);
|
|
tree_node.children.extend(children);
|
|
tree_node.is_accessible |= !e.accessibility_props.0.is_empty();
|
|
tree_node
|
|
}
|
|
LoweredElement::NativeItem { item_index } => TreeNode {
|
|
is_accessible: !e.accessibility_props.0.is_empty(),
|
|
sub_component_path: sub_component_path.into(),
|
|
item_index: *item_index,
|
|
children: children.collect(),
|
|
repeated: false,
|
|
},
|
|
LoweredElement::Repeated { repeated_index } => TreeNode {
|
|
is_accessible: false,
|
|
sub_component_path: sub_component_path.into(),
|
|
item_index: *repeated_index,
|
|
children: vec![],
|
|
repeated: true,
|
|
},
|
|
}
|
|
}
|
|
|
|
fn public_properties(
|
|
component: &Component,
|
|
mapping: &LoweredSubComponentMapping,
|
|
state: &LoweringState,
|
|
) -> PublicProperties {
|
|
component
|
|
.root_element
|
|
.borrow()
|
|
.property_declarations
|
|
.iter()
|
|
.filter(|(_, c)| c.expose_in_public_api)
|
|
.map(|(p, c)| {
|
|
let property_reference = mapping
|
|
.map_property_reference(&NamedReference::new(&component.root_element, p), state);
|
|
(p.clone(), (c.property_type.clone(), property_reference))
|
|
})
|
|
.collect()
|
|
}
|