mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-03 07:04:34 +00:00

... if they are not set as actual binding before As reported in #3068 The problem is that the pass will properly create the Rotation or Opacity item, but will not create the two way binding if there is no existing binding. This causes code like `img.rotation-angle = ...` to change the rotation angle of the image, but not its parent Rotation item. Fix it by making sure there is always a binding. Since the change only affect visual representation, I abused one of the screenshot test to test this feature. And I also patched another bug that some #[allow(unused_parens)] was not set in the 'init' callback and would cause a warning in the test
125 lines
5.1 KiB
Rust
125 lines
5.1 KiB
Rust
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
|
|
|
|
//! Pass that lowers synthetic properties such as `opacity` and `layer` properties to their corresponding elements.
|
|
//! For example `f := Foo { opacity: <some float>; }` is mapped to `Opacity { opacity <=> f.opacity; f := Foo { ... } }`
|
|
|
|
use std::cell::RefCell;
|
|
use std::rc::Rc;
|
|
|
|
use crate::diagnostics::BuildDiagnostics;
|
|
use crate::expression_tree::{BindingExpression, Expression, NamedReference};
|
|
use crate::langtype::Type;
|
|
use crate::object_tree::{self, Component, Element, ElementRc};
|
|
use crate::typeregister::TypeRegister;
|
|
|
|
/// If any element in `component` declares a binding to `property_name`, then a new
|
|
/// element of type `element_name` is created, injected as a parent to the element and bindings
|
|
/// to property_name and all properties in extra_properties are mapped.
|
|
/// Default value for the property extra_properties is queried with the `default_value_for_extra_properties`
|
|
pub(crate) fn lower_property_to_element(
|
|
component: &Rc<Component>,
|
|
property_name: &'static str,
|
|
extra_properties: impl Iterator<Item = &'static str> + Clone,
|
|
default_value_for_extra_properties: Option<&dyn Fn(&ElementRc, &str) -> Expression>,
|
|
element_name: &str,
|
|
type_register: &TypeRegister,
|
|
diag: &mut BuildDiagnostics,
|
|
) {
|
|
if let Some(b) = component.root_element.borrow().bindings.get(property_name) {
|
|
diag.push_warning(
|
|
format!(
|
|
"The {} property cannot be used on the root element, it will not be applied",
|
|
property_name
|
|
),
|
|
&*b.borrow(),
|
|
);
|
|
}
|
|
|
|
object_tree::recurse_elem_including_sub_components_no_borrow(component, &(), &mut |elem, _| {
|
|
if elem.borrow().base_type.to_string() == element_name {
|
|
return;
|
|
}
|
|
|
|
let old_children = {
|
|
let mut elem = elem.borrow_mut();
|
|
let new_children = Vec::with_capacity(elem.children.len());
|
|
std::mem::replace(&mut elem.children, new_children)
|
|
};
|
|
|
|
let has_property_binding = |e: &ElementRc| {
|
|
e.borrow().base_type.lookup_property(property_name).property_type != Type::Invalid
|
|
&& (e.borrow().bindings.contains_key(property_name)
|
|
|| e.borrow()
|
|
.property_analysis
|
|
.borrow()
|
|
.get(property_name)
|
|
.map_or(false, |a| a.is_set))
|
|
};
|
|
|
|
for mut child in old_children {
|
|
if child.borrow().repeated.is_some() {
|
|
let root_elem = child.borrow().base_type.as_component().root_element.clone();
|
|
if has_property_binding(&root_elem) {
|
|
object_tree::inject_element_as_repeated_element(
|
|
&child,
|
|
create_property_element(
|
|
&root_elem,
|
|
property_name,
|
|
extra_properties.clone(),
|
|
default_value_for_extra_properties,
|
|
element_name,
|
|
type_register,
|
|
),
|
|
)
|
|
}
|
|
} else if has_property_binding(&child) {
|
|
let new_child = create_property_element(
|
|
&child,
|
|
property_name,
|
|
extra_properties.clone(),
|
|
default_value_for_extra_properties,
|
|
element_name,
|
|
type_register,
|
|
);
|
|
crate::object_tree::adjust_geometry_for_injected_parent(&new_child, &child);
|
|
new_child.borrow_mut().children.push(child);
|
|
child = new_child;
|
|
}
|
|
|
|
elem.borrow_mut().children.push(child);
|
|
}
|
|
});
|
|
}
|
|
|
|
fn create_property_element(
|
|
child: &ElementRc,
|
|
property_name: &'static str,
|
|
extra_properties: impl Iterator<Item = &'static str>,
|
|
default_value_for_extra_properties: Option<&dyn Fn(&ElementRc, &str) -> Expression>,
|
|
element_name: &str,
|
|
type_register: &TypeRegister,
|
|
) -> ElementRc {
|
|
let bindings = core::iter::once(property_name)
|
|
.chain(extra_properties)
|
|
.map(|property_name| {
|
|
let mut bind =
|
|
BindingExpression::new_two_way(NamedReference::new(child, property_name));
|
|
if let Some(default_value_for_extra_properties) = default_value_for_extra_properties {
|
|
if !child.borrow().bindings.contains_key(property_name) {
|
|
bind.expression = default_value_for_extra_properties(child, property_name)
|
|
}
|
|
}
|
|
(property_name.to_string(), bind.into())
|
|
})
|
|
.collect();
|
|
|
|
let element = Element {
|
|
id: format!("{}-{}", child.borrow().id, property_name),
|
|
base_type: type_register.lookup_element(element_name).unwrap(),
|
|
enclosing_component: child.borrow().enclosing_component.clone(),
|
|
bindings,
|
|
..Default::default()
|
|
};
|
|
Rc::new(RefCell::new(element))
|
|
}
|