mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-03 02:13:21 +00:00
153 lines
5.1 KiB
Rust
153 lines
5.1 KiB
Rust
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
|
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
|
|
|
|
//! After inlining and moving declarations, all Element::base_type should be Type::BuiltinElement. This pass resolves them
|
|
//! to NativeClass and picking a variant that only contains the used properties.
|
|
|
|
use std::collections::HashSet;
|
|
use std::rc::Rc;
|
|
|
|
use crate::langtype::{ElementType, NativeClass};
|
|
use crate::object_tree::{recurse_elem_including_sub_components, Component};
|
|
|
|
pub fn resolve_native_classes(component: &Component) {
|
|
recurse_elem_including_sub_components(component, &(), &mut |elem, _| {
|
|
let new_native_class = {
|
|
let elem = elem.borrow();
|
|
|
|
let base_type = match &elem.base_type {
|
|
ElementType::Component(_) => {
|
|
// recurse_elem_including_sub_components will recurse into it
|
|
return;
|
|
}
|
|
ElementType::Builtin(b) => b,
|
|
ElementType::Native(_) => {
|
|
// already native
|
|
return;
|
|
}
|
|
ElementType::Global | ElementType::Error => panic!("This should not happen"),
|
|
};
|
|
|
|
let analysis = elem.property_analysis.borrow();
|
|
let native_properties_used: HashSet<_> = elem
|
|
.bindings
|
|
.keys()
|
|
.chain(analysis.iter().filter(|(_, v)| v.is_used()).map(|(k, _)| k))
|
|
.filter(|k| {
|
|
!elem.property_declarations.contains_key(*k)
|
|
&& base_type.as_ref().properties.contains_key(*k)
|
|
})
|
|
.collect();
|
|
|
|
select_minimal_class_based_on_property_usage(
|
|
&elem.base_type.as_builtin().native_class,
|
|
native_properties_used.into_iter(),
|
|
)
|
|
};
|
|
|
|
elem.borrow_mut().base_type = ElementType::Native(new_native_class);
|
|
})
|
|
}
|
|
|
|
fn lookup_property_distance(mut class: Rc<NativeClass>, name: &str) -> (usize, Rc<NativeClass>) {
|
|
let mut distance = 0;
|
|
loop {
|
|
if class.properties.contains_key(name) {
|
|
return (distance, class);
|
|
}
|
|
distance += 1;
|
|
class = class.parent.as_ref().unwrap().clone();
|
|
}
|
|
}
|
|
|
|
fn select_minimal_class_based_on_property_usage<'a>(
|
|
class: &Rc<NativeClass>,
|
|
properties_used: impl Iterator<Item = &'a String>,
|
|
) -> Rc<NativeClass> {
|
|
let mut minimal_class = class.clone();
|
|
while let Some(class) = minimal_class.parent.clone() {
|
|
minimal_class = class;
|
|
}
|
|
let (_min_distance, minimal_class) = properties_used.fold(
|
|
(std::usize::MAX, minimal_class),
|
|
|(current_distance, current_class), prop_name| {
|
|
let (prop_distance, prop_class) = lookup_property_distance(class.clone(), prop_name);
|
|
|
|
if prop_distance < current_distance {
|
|
(prop_distance, prop_class)
|
|
} else {
|
|
(current_distance, current_class)
|
|
}
|
|
},
|
|
);
|
|
minimal_class
|
|
}
|
|
|
|
#[test]
|
|
fn test_select_minimal_class_based_on_property_usage() {
|
|
use crate::langtype::{BuiltinPropertyInfo, Type};
|
|
let first = Rc::new(NativeClass::new_with_properties(
|
|
"first_class",
|
|
[("first_prop".to_owned(), BuiltinPropertyInfo::new(Type::Int32))].iter().cloned(),
|
|
));
|
|
|
|
let mut second = NativeClass::new_with_properties(
|
|
"second_class",
|
|
[("second_prop".to_owned(), BuiltinPropertyInfo::new(Type::Int32))].iter().cloned(),
|
|
);
|
|
second.parent = Some(first.clone());
|
|
let second = Rc::new(second);
|
|
|
|
let reduce_to_first =
|
|
select_minimal_class_based_on_property_usage(&second, ["first_prop".to_owned()].iter());
|
|
|
|
assert_eq!(reduce_to_first.class_name, first.class_name);
|
|
|
|
let reduce_to_second =
|
|
select_minimal_class_based_on_property_usage(&second, ["second_prop".to_owned()].iter());
|
|
|
|
assert_eq!(reduce_to_second.class_name, second.class_name);
|
|
|
|
let reduce_to_second = select_minimal_class_based_on_property_usage(
|
|
&second,
|
|
["first_prop".to_owned(), "second_prop".to_owned()].iter(),
|
|
);
|
|
|
|
assert_eq!(reduce_to_second.class_name, second.class_name);
|
|
}
|
|
|
|
#[test]
|
|
fn select_minimal_class() {
|
|
let tr = crate::typeregister::TypeRegister::builtin();
|
|
let tr = tr.borrow();
|
|
let rect = tr.lookup_element("Rectangle").unwrap();
|
|
let rect = rect.as_builtin();
|
|
assert_eq!(
|
|
select_minimal_class_based_on_property_usage(
|
|
&rect.native_class,
|
|
["x".to_owned(), "width".to_owned()].iter()
|
|
)
|
|
.class_name,
|
|
"Empty",
|
|
);
|
|
assert_eq!(
|
|
select_minimal_class_based_on_property_usage(&rect.native_class, [].iter()).class_name,
|
|
"Empty",
|
|
);
|
|
assert_eq!(
|
|
select_minimal_class_based_on_property_usage(
|
|
&rect.native_class,
|
|
["border-width".to_owned()].iter()
|
|
)
|
|
.class_name,
|
|
"BorderRectangle",
|
|
);
|
|
assert_eq!(
|
|
select_minimal_class_based_on_property_usage(
|
|
&rect.native_class,
|
|
["border-width".to_owned(), "x".to_owned()].iter()
|
|
)
|
|
.class_name,
|
|
"BorderRectangle",
|
|
);
|
|
}
|