mirror of
https://github.com/slint-ui/slint.git
synced 2025-09-27 20:42:25 +00:00

Parents surch as Opacity, Clip, and co, used to steal the x and y property of their children, making the property not what they ought to be. Now that we refactored recently the code so that geometry need not to be always linked to a property of the same name, we can dissociate the x and y property of these generated elements and their content so that the actual "x" property of the former elementstay some value, despite its relative item property is now 0. Had to change a bit of code that was still assuming a literal "height" or "width" or "y" or "x" property that no longer worked when the geometry is dissociated from its property Fix #1072
173 lines
6.7 KiB
Rust
173 lines
6.7 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 `drop-shadow-*` properties to proper shadow elements
|
|
// At the moment only shadows on `Rectangle` elements are supported, i.e. the drop shadow
|
|
// of a rectangle is a box shadow.
|
|
|
|
use crate::diagnostics::BuildDiagnostics;
|
|
use crate::expression_tree::BindingExpression;
|
|
use crate::{expression_tree::Expression, object_tree::*};
|
|
use crate::{expression_tree::NamedReference, typeregister::TypeRegister};
|
|
use std::cell::RefCell;
|
|
use std::collections::HashMap;
|
|
use std::rc::Rc;
|
|
|
|
// Creates a new element for the drop shadow properties that'll be a sibling to the specified
|
|
// sibling element.
|
|
fn create_box_shadow_element(
|
|
shadow_property_bindings: HashMap<String, BindingExpression>,
|
|
sibling_element: &ElementRc,
|
|
type_register: &TypeRegister,
|
|
diag: &mut BuildDiagnostics,
|
|
) -> Option<Element> {
|
|
if matches!(sibling_element.borrow().builtin_type(), Some(b) if b.name != "Rectangle") {
|
|
for (shadow_prop_name, shadow_prop_binding) in shadow_property_bindings {
|
|
diag.push_error(
|
|
format!("The {shadow_prop_name} property is only supported on Rectangle elements right now"),
|
|
&shadow_prop_binding,
|
|
);
|
|
}
|
|
return None;
|
|
}
|
|
|
|
let mut element = Element {
|
|
id: format!("{}-shadow", sibling_element.borrow().id),
|
|
base_type: type_register.lookup_element("BoxShadow").unwrap(),
|
|
enclosing_component: sibling_element.borrow().enclosing_component.clone(),
|
|
bindings: shadow_property_bindings
|
|
.into_iter()
|
|
.map(|(shadow_prop_name, expr)| {
|
|
(shadow_prop_name.strip_prefix("drop-shadow-").unwrap().to_string(), expr.into())
|
|
})
|
|
.collect(),
|
|
..Default::default()
|
|
};
|
|
|
|
// FIXME: remove the border-radius manual mapping.
|
|
if sibling_element.borrow().bindings.contains_key("border-radius") {
|
|
element.bindings.insert(
|
|
"border-radius".to_string(),
|
|
RefCell::new(
|
|
Expression::PropertyReference(NamedReference::new(
|
|
sibling_element,
|
|
"border-radius",
|
|
))
|
|
.into(),
|
|
),
|
|
);
|
|
}
|
|
|
|
Some(element)
|
|
}
|
|
|
|
// For a repeated element, this function creates a new element for the drop shadow properties that
|
|
// will act as the new root element in the repeater. The former root will become a child.
|
|
fn inject_shadow_element_in_repeated_element(
|
|
shadow_property_bindings: HashMap<String, BindingExpression>,
|
|
repeated_element: &ElementRc,
|
|
type_register: &TypeRegister,
|
|
diag: &mut BuildDiagnostics,
|
|
) {
|
|
let element_with_shadow_property =
|
|
&repeated_element.borrow().base_type.as_component().root_element.clone();
|
|
|
|
let shadow_element = match create_box_shadow_element(
|
|
shadow_property_bindings,
|
|
element_with_shadow_property,
|
|
type_register,
|
|
diag,
|
|
) {
|
|
Some(element) => element,
|
|
None => return,
|
|
};
|
|
|
|
crate::object_tree::inject_element_as_repeated_element(
|
|
repeated_element,
|
|
Element::make_rc(shadow_element),
|
|
);
|
|
}
|
|
|
|
fn take_shadow_property_bindings(element: &ElementRc) -> HashMap<String, BindingExpression> {
|
|
crate::typeregister::RESERVED_DROP_SHADOW_PROPERTIES
|
|
.iter()
|
|
.flat_map(|(shadow_property_name, _)| {
|
|
let shadow_property_name = shadow_property_name.to_string();
|
|
let mut element = element.borrow_mut();
|
|
element.bindings.remove(&shadow_property_name).map(|binding| {
|
|
// Remove the drop-shadow property that was also materialized as a fake property by now.
|
|
element.property_declarations.remove(&shadow_property_name);
|
|
(shadow_property_name, binding.into_inner())
|
|
})
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
pub fn lower_shadow_properties(
|
|
component: &Rc<Component>,
|
|
type_register: &TypeRegister,
|
|
diag: &mut BuildDiagnostics,
|
|
) {
|
|
for (shadow_prop_name, shadow_prop_binding) in
|
|
take_shadow_property_bindings(&component.root_element)
|
|
{
|
|
diag.push_warning(
|
|
format!("The {} property cannot be used on the root element, the shadow will not be visible", shadow_prop_name),
|
|
&shadow_prop_binding,
|
|
);
|
|
}
|
|
|
|
recurse_elem_including_sub_components_no_borrow(component, &(), &mut |elem, _| {
|
|
// When encountering a repeater where the repeated element has a `drop-shadow` property, we create a new
|
|
// dedicated shadow element and make the previously repeated element a child of that. This ensures rendering
|
|
// underneath while maintaining the hierarchy for the repeater.
|
|
// The geometry properties are aliased using two-way bindings (which may be eliminated in a later pass).
|
|
|
|
if elem.borrow().repeated.is_some() {
|
|
let component = elem.borrow().base_type.as_component().clone(); // CHECK if clone can be removed if we change borrow
|
|
|
|
let drop_shadow_properties = take_shadow_property_bindings(&component.root_element);
|
|
if !drop_shadow_properties.is_empty() {
|
|
drop(component);
|
|
inject_shadow_element_in_repeated_element(
|
|
drop_shadow_properties,
|
|
elem,
|
|
type_register,
|
|
diag,
|
|
);
|
|
}
|
|
}
|
|
|
|
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)
|
|
};
|
|
|
|
// When encountering a `drop-shadow` property in a supported element, we create a new dedicated
|
|
// shadow element and insert it *before* the element that had the `drop-shadow` property, to ensure
|
|
// that it is rendered underneath.
|
|
for child in old_children {
|
|
let drop_shadow_properties = take_shadow_property_bindings(&child);
|
|
if !drop_shadow_properties.is_empty() {
|
|
let mut shadow_elem = match create_box_shadow_element(
|
|
drop_shadow_properties,
|
|
&child,
|
|
type_register,
|
|
diag,
|
|
) {
|
|
Some(element) => element,
|
|
None => {
|
|
elem.borrow_mut().children.push(child);
|
|
continue;
|
|
}
|
|
};
|
|
|
|
shadow_elem.geometry_props = elem.borrow().geometry_props.clone();
|
|
|
|
elem.borrow_mut().children.push(Element::make_rc(shadow_elem));
|
|
}
|
|
elem.borrow_mut().children.push(child);
|
|
}
|
|
});
|
|
}
|