mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-04 02:39:28 +00:00
Polish of the rotation feature
- Add a check that this only Applies to Image element without children - Default the origin to the center of the Image - Add docs and test cc: #1481
This commit is contained in:
parent
09e685ef51
commit
6d12b276a9
7 changed files with 174 additions and 43 deletions
|
@ -27,6 +27,7 @@ All notable changes to this project are documented in this file.
|
|||
send a mouse or touch event to a window.
|
||||
- Added `animation-tick()`
|
||||
- `SharedString` implements `std::fmt::Write` and added `slint::format!`
|
||||
- `Image` can now be rotated with the `rotation-*` properties
|
||||
|
||||
## [0.2.5] - 2022-07-06
|
||||
|
||||
|
|
|
@ -174,6 +174,10 @@ An Image can be used to represent an image loaded from an image file.
|
|||
the sizes provided by the **`source`** image. If the `Image` is **not** in a layout and only **one** of the two sizes are
|
||||
specified, then the other defaults to the specified value scaled according to the aspect ratio of the **`source`** image.
|
||||
|
||||
* **`rotation-angle`** (*angle*), **`rotation-origin-x`** (*length*), **`rotation-origin-y`** (*length*):
|
||||
Rotate the image by the given angle around the specified origin point. The default origin point is the center of the element.
|
||||
When these properties are present, the Image cannot have any children elements.
|
||||
|
||||
### Example
|
||||
|
||||
```slint
|
||||
|
|
|
@ -6,6 +6,7 @@ mod binding_analysis;
|
|||
mod check_aliases;
|
||||
mod check_expressions;
|
||||
mod check_public_api;
|
||||
mod check_rotation;
|
||||
mod clip;
|
||||
mod collect_custom_fonts;
|
||||
mod collect_globals;
|
||||
|
@ -42,9 +43,11 @@ mod unique_id;
|
|||
mod visible;
|
||||
mod z_order;
|
||||
|
||||
use std::{collections::HashSet, rc::Rc};
|
||||
|
||||
use crate::expression_tree::Expression;
|
||||
use crate::langtype::Type;
|
||||
use crate::namedreference::NamedReference;
|
||||
use std::collections::HashSet;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub async fn run_passes(
|
||||
doc: &crate::object_tree::Document,
|
||||
|
@ -75,6 +78,7 @@ pub async fn run_passes(
|
|||
infer_aliases_types::resolve_aliases(doc, diag);
|
||||
resolving::resolve_expressions(doc, type_loader, diag);
|
||||
check_expressions::check_expressions(doc, diag);
|
||||
check_rotation::check_rotation(doc, diag);
|
||||
unique_id::check_unique_id(doc, diag);
|
||||
check_public_api::check_public_api(doc, diag);
|
||||
|
||||
|
@ -128,6 +132,7 @@ pub async fn run_passes(
|
|||
component,
|
||||
"opacity",
|
||||
core::iter::empty(),
|
||||
None,
|
||||
"Opacity",
|
||||
&global_type_registry.borrow(),
|
||||
diag,
|
||||
|
@ -136,6 +141,7 @@ pub async fn run_passes(
|
|||
component,
|
||||
"cache-rendering-hint",
|
||||
core::iter::empty(),
|
||||
None,
|
||||
"Layer",
|
||||
&global_type_registry.borrow(),
|
||||
diag,
|
||||
|
@ -147,6 +153,20 @@ pub async fn run_passes(
|
|||
crate::typeregister::RESERVED_ROTATION_PROPERTIES[1..]
|
||||
.iter()
|
||||
.map(|(prop_name, _)| *prop_name),
|
||||
Some(&|e, prop| Expression::BinaryExpression {
|
||||
lhs: Expression::PropertyReference(NamedReference::new(
|
||||
e,
|
||||
match prop {
|
||||
"rotation-origin-x" => "width",
|
||||
"rotation-origin-y" => "height",
|
||||
"rotation-angle" => return Expression::Invalid,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
))
|
||||
.into(),
|
||||
op: '/',
|
||||
rhs: Expression::NumberLiteral(2., Default::default()).into(),
|
||||
}),
|
||||
"Rotate",
|
||||
&global_type_registry.borrow(),
|
||||
diag,
|
||||
|
@ -244,5 +264,6 @@ pub fn run_import_passes(
|
|||
infer_aliases_types::resolve_aliases(doc, diag);
|
||||
resolving::resolve_expressions(doc, type_loader, diag);
|
||||
check_expressions::check_expressions(doc, diag);
|
||||
check_rotation::check_rotation(doc, diag);
|
||||
unique_id::check_unique_id(doc, diag);
|
||||
}
|
||||
|
|
51
internal/compiler/passes/check_rotation.rs
Normal file
51
internal/compiler/passes/check_rotation.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
|
||||
|
||||
use crate::diagnostics::BuildDiagnostics;
|
||||
use crate::diagnostics::Spanned;
|
||||
use crate::langtype::Type;
|
||||
use crate::object_tree::Element;
|
||||
|
||||
/// Check that the rotation is only on Image
|
||||
pub fn check_rotation(doc: &crate::object_tree::Document, diag: &mut BuildDiagnostics) {
|
||||
for cmp in &doc.inner_components {
|
||||
crate::object_tree::recurse_elem_including_sub_components(cmp, &(), &mut |elem, _| {
|
||||
let e = elem.borrow();
|
||||
if crate::typeregister::RESERVED_ROTATION_PROPERTIES
|
||||
.iter()
|
||||
.any(|(property_name, _)| is_property_set(&*e, *&property_name))
|
||||
{
|
||||
if matches!(e.native_class(), Some(native) if native.class_name != "ClippedImage") {
|
||||
let span = e
|
||||
.bindings
|
||||
.get("rotation-angle")
|
||||
.and_then(|e| e.borrow().span.clone())
|
||||
.unwrap_or_else(|| e.to_source_location());
|
||||
|
||||
diag.push_error_with_span(
|
||||
"rotation properties can only be applied to the Image element".into(),
|
||||
span,
|
||||
);
|
||||
} else if has_any_children(&*e) {
|
||||
diag.push_error_with_span(
|
||||
"Elements with rotation properties cannot have children elements".into(),
|
||||
e.to_source_location(),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if this element or its base have any children.
|
||||
fn has_any_children(e: &Element) -> bool {
|
||||
!e.children.is_empty()
|
||||
|| matches!(&e.base_type, Type::Component(base) if has_any_children(&base.root_element.borrow()))
|
||||
}
|
||||
|
||||
/// Returns true if the property is set.
|
||||
fn is_property_set(e: &Element, property_name: &str) -> bool {
|
||||
e.bindings.contains_key(property_name)
|
||||
|| e.property_analysis.borrow().get(property_name).map_or(false, |a| a.is_set)
|
||||
|| matches!(&e.base_type, Type::Component(base) if is_property_set(&base.root_element.borrow(), property_name))
|
||||
}
|
|
@ -8,20 +8,20 @@ use std::cell::RefCell;
|
|||
use std::rc::Rc;
|
||||
|
||||
use crate::diagnostics::BuildDiagnostics;
|
||||
use crate::expression_tree::{BindingExpression, NamedReference};
|
||||
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 the first property in `properties`, then a new
|
||||
// element of type `element_name` is created, injected as a parent to the element and bindings to all
|
||||
// remaining properties in `properties` are mapped. This way for example ["rotation-angle", "rotation-origin-x", "rotation-origin-y"]
|
||||
// creates a `Rotate` element when `rotation-angle` is used and any optional `rotation-origin-*` bindings are redirected to the
|
||||
// `Rotate` element.
|
||||
/// 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 balue for the property extra_properties is queried witht 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,
|
||||
|
@ -67,6 +67,7 @@ pub(crate) fn lower_property_to_element(
|
|||
&root_elem,
|
||||
property_name,
|
||||
extra_properties.clone(),
|
||||
default_value_for_extra_properties,
|
||||
element_name,
|
||||
type_register,
|
||||
),
|
||||
|
@ -77,6 +78,7 @@ pub(crate) fn lower_property_to_element(
|
|||
&child,
|
||||
property_name,
|
||||
extra_properties.clone(),
|
||||
default_value_for_extra_properties,
|
||||
element_name,
|
||||
type_register,
|
||||
);
|
||||
|
@ -94,6 +96,7 @@ 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 {
|
||||
|
@ -107,7 +110,12 @@ fn create_property_element(
|
|||
.into(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
default_value_for_extra_properties.map(|f| {
|
||||
(
|
||||
property_name.to_string(),
|
||||
BindingExpression::from(f(child, property_name)).into(),
|
||||
)
|
||||
})
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
|
64
internal/compiler/tests/syntax/basic/rotation.slint
Normal file
64
internal/compiler/tests/syntax/basic/rotation.slint
Normal file
|
@ -0,0 +1,64 @@
|
|||
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
|
||||
|
||||
Ex1 := Rectangle {
|
||||
Rectangle {
|
||||
rotation-origin-x: width / 2;
|
||||
rotation-angle: 45deg;
|
||||
// ^error{rotation properties can only be applied to the Image element}
|
||||
rotation-origin-y: width / 2;
|
||||
}
|
||||
Rectangle {
|
||||
// ^error{rotation properties can only be applied to the Image element}
|
||||
rotation-origin-x: width / 2;
|
||||
rotation-origin-y: width / 2;
|
||||
}
|
||||
}
|
||||
|
||||
RotImg := Image {
|
||||
rotation-angle: 45deg;
|
||||
}
|
||||
|
||||
JustAnImage := Image {}
|
||||
|
||||
ImageWithChild := Image {
|
||||
Rectangle {}
|
||||
}
|
||||
|
||||
Ex2 := Rectangle {
|
||||
Image {
|
||||
// ^error{Elements with rotation properties cannot have children elements}
|
||||
rotation-angle: 45deg;
|
||||
Rectangle {}
|
||||
}
|
||||
RotImg {
|
||||
// ^error{Elements with rotation properties cannot have children elements}
|
||||
Rectangle {}
|
||||
}
|
||||
ImageWithChild {
|
||||
// ^error{Elements with rotation properties cannot have children elements}
|
||||
rotation-origin-x: 45px;
|
||||
}
|
||||
JustAnImage {
|
||||
// ^error{Elements with rotation properties cannot have children elements}
|
||||
rotation-angle: 45deg;
|
||||
Rectangle {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ex3 := Rectangle {
|
||||
i1 := Image {
|
||||
// ^error{Elements with rotation properties cannot have children elements}
|
||||
Rectangle {}
|
||||
}
|
||||
i2 := Rectangle {}
|
||||
// ^error{rotation properties can only be applied to the Image element}
|
||||
|
||||
TouchArea {
|
||||
clicked => {
|
||||
i1.rotation-angle = 60deg;
|
||||
i2.rotation-origin-x = 10px;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,18 +17,15 @@ TestCase := Window {
|
|||
x: 50px;
|
||||
y: 50px;
|
||||
|
||||
Rotate {
|
||||
angle: 45deg;
|
||||
origin-x: r.x;
|
||||
origin-y: r.y;
|
||||
|
||||
r := Rectangle {
|
||||
x: 50px;
|
||||
y: 50px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background: green;
|
||||
}
|
||||
Image {
|
||||
rotation-angle: 45deg;
|
||||
x: 50px;
|
||||
y: 50px;
|
||||
rotation-origin-x: 0;
|
||||
rotation-origin-y: 0;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
source: @image-url("../../../logo/slint-logo-square-light-128x128.png");
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
@ -48,29 +45,14 @@ TestCase := Window {
|
|||
Rectangle {
|
||||
x: 200px;
|
||||
y: 50px;
|
||||
border-width: 1px;
|
||||
border-color: black;
|
||||
width: i2.width;
|
||||
height: i2.height;
|
||||
|
||||
Rotate {
|
||||
angle: 315deg;
|
||||
origin-x: r2.x;
|
||||
origin-y: r2.y;
|
||||
|
||||
r2 := Rectangle {
|
||||
x: 50px;
|
||||
y: 50px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background: @linear-gradient(90deg, red, blue);
|
||||
}
|
||||
i2 := Image {
|
||||
rotation-angle: 315deg;
|
||||
source: @image-url("../../../logo/slint-logo-square-light-128x128.png");
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
x: 50px;
|
||||
y: 50px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-width: 1px;
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue