mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-04 10:50:00 +00:00
Add support for absolute-x and absolute-y properties on any element (#2823)
Fixes #1691
This commit is contained in:
parent
3d5dd2405a
commit
1bf05eae7c
16 changed files with 239 additions and 6 deletions
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
77
internal/compiler/passes/lower_absolute_coordinates.rs
Normal file
77
internal/compiler/passes/lower_absolute_coordinates.rs
Normal 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()));
|
||||
}
|
||||
}
|
|
@ -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}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue