Add support for absolute-x and absolute-y properties on any element (#2823)

Fixes #1691
This commit is contained in:
Simon Hausmann 2023-06-06 14:37:53 +02:00 committed by GitHub
parent 3d5dd2405a
commit 1bf05eae7c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 239 additions and 6 deletions

View file

@ -172,6 +172,7 @@ export component BoxShadow inherits Empty {
//-is_internal
}
// Keep an eye on typeregister::logical_point_type() that creates the same type.
export struct Point {
//-name:slint::private_api::Point
x: length,

View file

@ -58,6 +58,7 @@ pub enum BuiltinFunction {
TextInputFocused,
SetTextInputFocused,
ImplicitLayoutInfo(Orientation),
ItemAbsolutePosition,
RegisterCustomFontByPath,
RegisterCustomFontByMemory,
RegisterBitmapFont,
@ -194,6 +195,10 @@ impl BuiltinFunction {
BuiltinFunction::SetTextInputFocused => {
Type::Function { return_type: Box::new(Type::Void), args: vec![Type::Bool] }
}
BuiltinFunction::ItemAbsolutePosition => Type::Function {
return_type: Box::new(crate::typeregister::logical_point_type()),
args: vec![Type::ElementReference],
},
BuiltinFunction::RegisterCustomFontByPath => {
Type::Function { return_type: Box::new(Type::Void), args: vec![Type::String] }
}
@ -261,6 +266,7 @@ impl BuiltinFunction {
BuiltinFunction::SetTextInputFocused => false,
BuiltinFunction::TextInputFocused => false,
BuiltinFunction::ImplicitLayoutInfo(_) => false,
BuiltinFunction::ItemAbsolutePosition => true,
BuiltinFunction::RegisterCustomFontByPath
| BuiltinFunction::RegisterCustomFontByMemory
| BuiltinFunction::RegisterBitmapFont => false,
@ -304,6 +310,7 @@ impl BuiltinFunction {
BuiltinFunction::ArrayLength => true,
BuiltinFunction::Rgb => true,
BuiltinFunction::ImplicitLayoutInfo(_) => true,
BuiltinFunction::ItemAbsolutePosition => true,
BuiltinFunction::SetTextInputFocused => false,
BuiltinFunction::TextInputFocused => true,
BuiltinFunction::RegisterCustomFontByPath

View file

@ -2794,6 +2794,14 @@ fn compile_builtin_function_call(
panic!("internal error: invalid args to ItemMemberFunction {:?}", arguments)
}
}
BuiltinFunction::ItemAbsolutePosition => {
if let [llr::Expression::PropertyReference(pr)] = arguments {
let item_rc = access_item_rc(pr, ctx);
format!("slint_item_absolute_position(&{item_rc})")
} else {
panic!("internal error: invalid args to ItemAbsolutePosition {:?}", arguments)
}
}
BuiltinFunction::RegisterCustomFontByPath => {
if let [llr::Expression::StringLiteral(path)] = arguments {
let window = access_window_field(ctx);

View file

@ -2444,6 +2444,16 @@ fn compile_builtin_function_call(
BuiltinFunction::Translate => {
quote!(slint::private_unstable_api::translate(#(#a),*))
}
BuiltinFunction::ItemAbsolutePosition => {
if let [Expression::PropertyReference(pr)] = arguments {
let item_rc = access_item_rc(pr, ctx);
quote!(
(*#item_rc).map_to_window(Default::default()).to_untyped()
)
} else {
panic!("internal error: invalid args to MapPointToWindow {:?}", arguments)
}
}
}
}

View file

@ -100,6 +100,7 @@ fn builtin_function_cost(function: &BuiltinFunction) -> isize {
BuiltinFunction::ArrayLength => 50,
BuiltinFunction::Rgb => 50,
BuiltinFunction::ImplicitLayoutInfo(_) => isize::MAX,
BuiltinFunction::ItemAbsolutePosition => isize::MAX,
BuiltinFunction::RegisterCustomFontByPath => isize::MAX,
BuiltinFunction::RegisterCustomFontByMemory => isize::MAX,
BuiltinFunction::RegisterBitmapFont => isize::MAX,

View file

@ -25,6 +25,7 @@ mod focus_item;
pub mod generate_item_indices;
pub mod infer_aliases_types;
mod inlining;
mod lower_absolute_coordinates;
mod lower_accessibility;
mod lower_layout;
mod lower_popups;
@ -117,6 +118,7 @@ pub async fn run_passes(
lower_popups::lower_popups(component, &doc.local_registry, diag);
lower_layout::lower_layouts(component, type_loader, diag).await;
default_geometry::default_geometry(component, diag);
lower_absolute_coordinates::lower_absolute_coordinates(component);
z_order::reorder_by_z_order(component, diag);
lower_property_to_element::lower_property_to_element(
component,

View file

@ -19,6 +19,7 @@ use crate::langtype::ElementType;
use crate::layout::LayoutItem;
use crate::layout::Orientation;
use crate::namedreference::NamedReference;
use crate::object_tree::find_parent_element;
use crate::object_tree::Document;
use crate::object_tree::PropertyAnimation;
use crate::object_tree::{Component, ElementRc};
@ -374,12 +375,11 @@ fn recurse_expression(expr: &Expression, vis: &mut impl FnMut(&PropertyPath)) {
g.rect = Default::default(); // already visited;
g.visit_named_references(&mut |nr| vis(&nr.clone().into()))
}
Expression::FunctionCall { function, arguments, .. } => {
if let Expression::BuiltinFunctionReference(
Expression::FunctionCall { function, arguments, .. } => match &**function {
Expression::BuiltinFunctionReference(
BuiltinFunction::ImplicitLayoutInfo(orientation),
_,
) = &**function
{
) => {
if let [Expression::ElementReference(item)] = arguments.as_slice() {
visit_implicit_layout_info_dependencies(
*orientation,
@ -388,7 +388,18 @@ fn recurse_expression(expr: &Expression, vis: &mut impl FnMut(&PropertyPath)) {
);
}
}
}
Expression::BuiltinFunctionReference(BuiltinFunction::ItemAbsolutePosition, _) => {
if let Some(Expression::ElementReference(item)) = arguments.first() {
let mut item = item.upgrade().unwrap();
while let Some(parent) = find_parent_element(&item) {
item = parent;
vis(&NamedReference::new(&item, "x").into());
vis(&NamedReference::new(&item, "y").into());
}
}
}
_ => {}
},
_ => {}
}
}

View file

@ -0,0 +1,77 @@
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
//! This pass creates bindings to "absolute-y" and "absolute-y" properties
//! that can be used to compute the window-absolute coordinates of elements.
use std::cell::RefCell;
use std::rc::Rc;
use crate::expression_tree::{BuiltinFunction, Expression};
use crate::namedreference::NamedReference;
use crate::object_tree::{
recurse_elem_including_sub_components_no_borrow, visit_all_named_references_in_element,
Component, PropertyDeclaration,
};
pub fn lower_absolute_coordinates(component: &Rc<Component>) {
let mut to_materialize = std::collections::HashSet::new();
recurse_elem_including_sub_components_no_borrow(component, &(), &mut |elem, _| {
visit_all_named_references_in_element(elem, |nr| {
if nr.name() == "absolute-x" || nr.name() == "absolute-y" {
to_materialize.insert(nr.clone());
}
});
});
let absolute_point_prop_name = "cached-absolute-xy".to_string();
let point_type = match BuiltinFunction::ItemAbsolutePosition.ty() {
crate::langtype::Type::Function { return_type, .. } => return_type.as_ref().clone(),
_ => unreachable!(),
};
for nr in to_materialize {
let elem = nr.element();
elem.borrow_mut()
.property_declarations
.entry(absolute_point_prop_name.clone())
.or_insert_with(|| PropertyDeclaration {
property_type: point_type.clone(),
..PropertyDeclaration::default()
});
if !elem.borrow().bindings.contains_key(&absolute_point_prop_name) {
let point_binding = Expression::FunctionCall {
function: Box::new(Expression::BuiltinFunctionReference(
BuiltinFunction::ItemAbsolutePosition,
None,
)),
arguments: vec![Expression::ElementReference(Rc::downgrade(&elem))],
source_location: None,
};
elem.borrow_mut()
.bindings
.insert(absolute_point_prop_name.clone(), RefCell::new(point_binding.into()));
}
// Create a binding to the hidden point property. The materialize properties pass is going to create the actual property
// for absolute-x/y.
let x_or_y = nr.name().strip_prefix("absolute-").unwrap().to_string();
let binding = Expression::BinaryExpression {
lhs: Expression::StructFieldAccess {
base: Expression::PropertyReference(NamedReference::new(
&elem,
&absolute_point_prop_name,
))
.into(),
name: x_or_y.clone(),
}
.into(),
rhs: Expression::PropertyReference(NamedReference::new(&elem, &x_or_y)).into(),
op: '+',
};
elem.borrow_mut().bindings.insert(nr.name().to_string(), RefCell::new(binding.into()));
}
}

View file

@ -0,0 +1,15 @@
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
export App := Rectangle {
Rectangle {
x: inner.absolute-x > 10px ? 10px : 0px;
// ^error{The binding for the property 'x' is part of a binding loop}
inner := Rectangle {
// ^error{The binding for the property 'cached-absolute-xy' is part of a binding loop}
// ^^error{The binding for the property 'absolute-x' is part of a binding loop}
}
}
}

View file

@ -21,6 +21,9 @@ pub const RESERVED_GEOMETRY_PROPERTIES: &[(&str, Type)] = &[
("z", Type::Float32),
];
pub(crate) const RESERVED_ABSOLUTE_GEOMETRY_PROPERTIES: &[(&str, Type)] =
&[("absolute-x", Type::LogicalLength), ("absolute-y", Type::LogicalLength)];
pub const RESERVED_LAYOUT_PROPERTIES: &[(&str, Type)] = &[
("min-width", Type::LogicalLength),
("min-height", Type::LogicalLength),
@ -111,6 +114,7 @@ pub const RESERVED_ACCESSIBILITY_PROPERTIES: &[(&str, Type)] = &[
pub fn reserved_properties() -> impl Iterator<Item = (&'static str, Type)> {
RESERVED_GEOMETRY_PROPERTIES
.iter()
.chain(RESERVED_ABSOLUTE_GEOMETRY_PROPERTIES.iter())
.chain(RESERVED_LAYOUT_PROPERTIES.iter())
.chain(RESERVED_OTHER_PROPERTIES.iter())
.chain(RESERVED_DROP_SHADOW_PROPERTIES.iter())
@ -396,3 +400,17 @@ impl TypeRegister {
}
}
}
/// This is identical with builtins.slint's Point type (TODO: use that in the future)
pub fn logical_point_type() -> Type {
Type::Struct {
fields: IntoIterator::into_iter([
("x".to_owned(), Type::LogicalLength),
("y".to_owned(), Type::LogicalLength),
])
.collect(),
name: Some("Point".into()),
node: None,
rust_attributes: None,
}
}