diff --git a/demos/home-automation/ui/components/mainView/widgetLoader.slint b/demos/home-automation/ui/components/mainView/widgetLoader.slint
index b2898ece575..ee64abc826f 100644
--- a/demos/home-automation/ui/components/mainView/widgetLoader.slint
+++ b/demos/home-automation/ui/components/mainView/widgetLoader.slint
@@ -192,8 +192,7 @@ export component WidgetLoader {
id: AppState.component-details[root.index].id;
width: root.width;
height: root.height;
- scale-x: scaler;
- scale-y: scaler;
+ transform-scale: scaler;
}
if root.type == WidgetType.powerInfo: PowerInfo {
diff --git a/demos/home-automation/ui/components/mainView/widgetLoaderSoftwareRenderer.slint b/demos/home-automation/ui/components/mainView/widgetLoaderSoftwareRenderer.slint
index f59410033a1..43e9999fe64 100644
--- a/demos/home-automation/ui/components/mainView/widgetLoaderSoftwareRenderer.slint
+++ b/demos/home-automation/ui/components/mainView/widgetLoaderSoftwareRenderer.slint
@@ -128,8 +128,7 @@ export component WidgetLoaderSoftwareRenderer {
id: AppState.component-details[root.index].id;
width: root.width;
height: root.height;
- scale-x: scaler;
- scale-y: scaler;
+ transform-scale: scaler;
}
if root.type == WidgetType.powerInfo && root.is-visible: PowerInfo {
diff --git a/docs/astro/src/content/docs/reference/common.mdx b/docs/astro/src/content/docs/reference/common.mdx
index 650b8ad4b94..ed29c51914b 100644
--- a/docs/astro/src/content/docs/reference/common.mdx
+++ b/docs/astro/src/content/docs/reference/common.mdx
@@ -72,10 +72,21 @@ The y component of the origin to rotate and scale around.
In the future this will be deprecated in favour of a transform-origin property.
-#### scale-x
-
-#### scale-y
-
+
+#### transform-scale
+
+The scale factor to apply to the element and all its children.
+
+This doesn't affect the geometry (width, height) of the element, but affects the rendering.
+The scale is done around the `rotation-origin` point.
+
+It is also possible to use the `transform-scale-x` and `transform-scale-y` properties to specify the scale factors for the x and y axis.
+
+
+#### transform-scale-x
+
+#### transform-scale-y
+
### opacity
diff --git a/examples/fancy-switches/DarkModeSwitch.slint b/examples/fancy-switches/DarkModeSwitch.slint
index 809eef45416..b8830edcb3d 100644
--- a/examples/fancy-switches/DarkModeSwitch.slint
+++ b/examples/fancy-switches/DarkModeSwitch.slint
@@ -123,14 +123,13 @@ export component DarkModeSwitch {
frameBacker.background: #2A2A2A;
moon.rotation-angle: -20deg;
sun.color: #fc7a10;
- sun.scale-x: 0.8;
- sun.scale-y: 0.8;
+ sun.transform-scale: 0.8;
in {
animate thumb.x, thumb.background, frameBacker.background, sun.color {
duration: 200ms;
easing: ease-out-sine;
}
- animate moon.rotation-angle, sun.scale-x, sun.scale-y {
+ animate moon.rotation-angle, sun.transform-scale {
duration: 1200ms;
easing: ease-out-elastic;
}
@@ -143,14 +142,13 @@ export component DarkModeSwitch {
frameBacker.background: transparent;
moon.rotation-angle: 20deg;
sun.color: #2A2A2A;
- sun.scale-x: 1;
- sun.scale-y: 1;
+ sun.transform-scale: 1;
in {
animate thumb.x, thumb.background, frameBacker.background, moon.rotation-angle {
duration: 200ms;
easing: ease-out-sine;
}
- animate sun.scale-x, sun.scale-y {
+ animate sun.transform-scale {
duration: 1200ms;
easing: ease-out-elastic;
}
diff --git a/internal/compiler/builtins.slint b/internal/compiler/builtins.slint
index d2708f131df..11ce1f915ed 100644
--- a/internal/compiler/builtins.slint
+++ b/internal/compiler/builtins.slint
@@ -83,8 +83,8 @@ export component ComponentContainer inherits Empty {
export component Transform inherits Empty {
in property rotation-angle;
- in property scale-x;
- in property scale-y;
+ in property transform-scale-x;
+ in property transform-scale-y;
in property rotation-origin-x;
in property rotation-origin-y;
//-default_size_binding:expands_to_parent_geometry
diff --git a/internal/compiler/passes.rs b/internal/compiler/passes.rs
index 116237c4140..eb841c2b026 100644
--- a/internal/compiler/passes.rs
+++ b/internal/compiler/passes.rs
@@ -55,7 +55,6 @@ mod visible;
mod z_order;
use crate::expression_tree::Expression;
-use crate::namedreference::NamedReference;
use smol_str::SmolStr;
pub fn ignore_debug_hooks(expr: &Expression) -> &Expression {
@@ -166,30 +165,8 @@ pub async fn run_passes(
);
visible::handle_visible(component, &global_type_registry.borrow(), diag);
lower_shadows::lower_shadow_properties(component, &doc.local_registry, diag);
- lower_property_to_element::lower_property_to_element(
+ lower_property_to_element::lower_transform_properties(
component,
- crate::typeregister::RESERVED_TRANSFORM_PROPERTIES[..3]
- .iter()
- .map(|(prop_name, _)| *prop_name),
- crate::typeregister::RESERVED_TRANSFORM_PROPERTIES[3..]
- .iter()
- .map(|(prop_name, _)| *prop_name),
- Some(&|e, prop| {
- let prop_div_2 = |prop: &str| Expression::BinaryExpression {
- lhs: Expression::PropertyReference(NamedReference::new(e, prop.into())).into(),
- op: '/',
- rhs: Expression::NumberLiteral(2., Default::default()).into(),
- };
-
- match prop {
- "rotation-origin-x" => prop_div_2("width"),
- "rotation-origin-y" => prop_div_2("height"),
- "scale-x" | "scale-y" => Expression::NumberLiteral(1., Default::default()),
- "rotation-angle" => Expression::NumberLiteral(0., Default::default()),
- _ => unreachable!(),
- }
- }),
- &SmolStr::new_static("Transform"),
&global_type_registry.borrow(),
diag,
);
diff --git a/internal/compiler/passes/lower_property_to_element.rs b/internal/compiler/passes/lower_property_to_element.rs
index 489c1956e0c..0d3182696ea 100644
--- a/internal/compiler/passes/lower_property_to_element.rs
+++ b/internal/compiler/passes/lower_property_to_element.rs
@@ -20,7 +20,7 @@ pub(crate) fn lower_property_to_element(
component: &Rc,
property_names: impl Iterator- + Clone,
extra_properties: impl Iterator
- + Clone,
- default_value_for_extra_properties: Option<&dyn Fn(&ElementRc, &str) -> Expression>,
+ default_value_for_extra_properties: Option<&dyn Fn(&ElementRc, &str) -> Option>,
element_name: &SmolStr,
type_register: &TypeRegister,
diag: &mut BuildDiagnostics,
@@ -95,7 +95,7 @@ pub(crate) fn lower_property_to_element(
fn create_property_element(
child: &ElementRc,
properties: impl Iterator
- ,
- default_value_for_extra_properties: Option<&dyn Fn(&ElementRc, &str) -> Expression>,
+ default_value_for_extra_properties: Option<&dyn Fn(&ElementRc, &str) -> Option>,
element_name: &SmolStr,
type_register: &TypeRegister,
) -> ElementRc {
@@ -105,7 +105,9 @@ fn create_property_element(
BindingExpression::new_two_way(NamedReference::new(child, property_name.into()));
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)
+ if let Some(e) = default_value_for_extra_properties(child, property_name) {
+ bind.expression = e;
+ }
}
}
(property_name.into(), bind.into())
@@ -121,3 +123,50 @@ fn create_property_element(
};
element.make_rc()
}
+
+/// Wrapper around lower_property_to_element for the Transform element
+pub fn lower_transform_properties(
+ component: &Rc,
+ tr: &TypeRegister,
+ diag: &mut BuildDiagnostics,
+) {
+ lower_property_to_element(
+ component,
+ crate::typeregister::RESERVED_TRANSFORM_PROPERTIES[..4]
+ .iter()
+ .map(|(prop_name, _)| *prop_name),
+ crate::typeregister::RESERVED_TRANSFORM_PROPERTIES[4..]
+ .iter()
+ .map(|(prop_name, _)| *prop_name),
+ Some(&|e, prop| {
+ let prop_div_2 = |prop: &str| {
+ Some(Expression::BinaryExpression {
+ lhs: Expression::PropertyReference(NamedReference::new(e, prop.into())).into(),
+ op: '/',
+ rhs: Expression::NumberLiteral(2., Default::default()).into(),
+ })
+ };
+
+ match prop {
+ "rotation-origin-x" => prop_div_2("width"),
+ "rotation-origin-y" => prop_div_2("height"),
+ "transform-scale-x" | "transform-scale-y" => {
+ if e.borrow().is_binding_set("transform-scale", true) {
+ Some(Expression::PropertyReference(NamedReference::new(
+ e,
+ SmolStr::new_static("transform-scale"),
+ )))
+ } else {
+ Some(Expression::NumberLiteral(1., Default::default()))
+ }
+ }
+ "transform-scale" => None,
+ "rotation-angle" => Some(Expression::NumberLiteral(0., Default::default())),
+ _ => unreachable!(),
+ }
+ }),
+ &SmolStr::new_static("Transform"),
+ tr,
+ diag,
+ );
+}
diff --git a/internal/compiler/typeregister.rs b/internal/compiler/typeregister.rs
index 062cf114afc..750256f4b0d 100644
--- a/internal/compiler/typeregister.rs
+++ b/internal/compiler/typeregister.rs
@@ -177,8 +177,9 @@ pub const RESERVED_DROP_SHADOW_PROPERTIES: &[(&str, Type)] = &[
pub const RESERVED_TRANSFORM_PROPERTIES: &[(&str, Type)] = &[
("rotation-angle", Type::Angle),
- ("scale-x", Type::Float32),
- ("scale-y", Type::Float32),
+ ("transform-scale-x", Type::Float32),
+ ("transform-scale-y", Type::Float32),
+ ("transform-scale", Type::Float32),
("rotation-origin-x", Type::LogicalLength),
("rotation-origin-y", Type::LogicalLength),
];
diff --git a/internal/core/item_tree.rs b/internal/core/item_tree.rs
index 3f736661d6d..eb61d582ac6 100644
--- a/internal/core/item_tree.rs
+++ b/internal/core/item_tree.rs
@@ -825,8 +825,8 @@ impl ItemRc {
ItemTransform::translation(-origin.x, -origin.y)
.cast()
.then_scale(
- transform_item.as_pin_ref().scale_x(),
- transform_item.as_pin_ref().scale_y(),
+ transform_item.as_pin_ref().transform_scale_x(),
+ transform_item.as_pin_ref().transform_scale_y(),
)
.then_rotate(euclid::Angle {
radians: transform_item.as_pin_ref().rotation_angle().to_radians(),
diff --git a/internal/core/items.rs b/internal/core/items.rs
index 7d8ff8ff55e..1b49e9d463f 100644
--- a/internal/core/items.rs
+++ b/internal/core/items.rs
@@ -1027,8 +1027,8 @@ declare_item_vtable! {
/// The implementation of the `Rotate` element
pub struct Transform {
pub rotation_angle: Property,
- pub scale_x: Property,
- pub scale_y: Property,
+ pub transform_scale_x: Property,
+ pub transform_scale_y: Property,
pub rotation_origin_x: Property,
pub rotation_origin_y: Property,
pub cached_rendering_data: CachedRenderingData,
@@ -1100,7 +1100,7 @@ impl Item for Transform {
let origin =
LogicalVector::from_lengths(self.rotation_origin_x(), self.rotation_origin_y());
(*backend).translate(origin);
- (*backend).scale(self.scale_x(), self.scale_y());
+ (*backend).scale(self.transform_scale_x(), self.transform_scale_y());
(*backend).rotate(self.rotation_angle());
(*backend).translate(-origin);
RenderingResult::ContinueRenderingChildren
diff --git a/tests/cases/elements/event_scaling.slint b/tests/cases/elements/event_scaling.slint
index 9d93c788d8d..f1e89af7016 100644
--- a/tests/cases/elements/event_scaling.slint
+++ b/tests/cases/elements/event_scaling.slint
@@ -20,8 +20,8 @@ export component TestCase {
y: 0px;
width: 50px;
height: 50px;
- scale-x: 200%;
- scale-y: 400%;
+ transform-scale-x: 200%;
+ transform-scale-y: 400%;
rotation-origin-x: 0px;
rotation-origin-y: 0px;
area1 := TouchArea {
diff --git a/tests/cases/elements/scaling2.slint b/tests/cases/elements/scaling2.slint
new file mode 100644
index 00000000000..bb75db32666
--- /dev/null
+++ b/tests/cases/elements/scaling2.slint
@@ -0,0 +1,64 @@
+// Copyright © SixtyFPS GmbH
+// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
+
+export component TestCase {
+ width: 800px;
+ height: 600px;
+
+ in-out property result;
+
+ r-a := Rectangle {
+ x: 10px;
+ y: 10px;
+ width: 20px;
+ height: 20px;
+ rotation-origin-x: 0;
+ rotation-origin-y: 0;
+ transform-scale: 2;
+ TouchArea {
+ clicked => { result += "A"}
+ }
+ }
+
+
+ r-b := Rectangle {
+ x: 100px;
+ y: 100px;
+ width: 20px;
+ height: 20px;
+ transform-scale: 3;
+ transform-scale-x: 0.5;
+ TouchArea {
+ clicked => { result += "B"}
+ }
+ }
+
+ out property test:
+ r-a.transform-scale == 2 && r-a.transform-scale-x == 2 && r-a.transform-scale-y == 2 &&
+ r-b.transform-scale-x == 0.5 && r-b.transform-scale-y == 3;
+
+
+}
+
+/*
+```rust
+let instance = TestCase::new().unwrap();
+
+assert_eq!(instance.get_test(), true);
+
+slint_testing::send_mouse_click(&instance, 10.0 + 39.0 , 10.0 + 39.0);
+assert_eq!(instance.get_result(), "A", "was just clicked inside");
+slint_testing::send_mouse_click(&instance, 10.0 + 19.0 , 10.0 + 41.0);
+assert_eq!(instance.get_result(), "A", "was just clicked outside");
+slint_testing::send_mouse_click(&instance, 10.0 + 41.0 , 10.0 + 19.0);
+assert_eq!(instance.get_result(), "A", "was just clicked outside again");
+
+
+slint_testing::send_mouse_click(&instance, 100.0 + 6. , 100.0 - 19.0);
+assert_eq!(instance.get_result(), "AB", "was just clicked inside");
+slint_testing::send_mouse_click(&instance, 100.0 + 4., 100.0);
+assert_eq!(instance.get_result(), "AB", "was just clicked outside");
+slint_testing::send_mouse_click(&instance, 100.0 + 10., 100. - 21.);
+assert_eq!(instance.get_result(), "AB", "was just clicked outside again");
+```
+*/