Fix conversion to from float to int

Round the value.

The previous behavior is that

 - For the interpreter, we were rounding (same as new behavior)
 - for Rust and C++ we were truncating, unless the properties were
   inlinined and then we were keeping it as float
This commit is contained in:
Olivier Goffart 2024-07-23 14:06:23 +02:00
parent ef92c1a161
commit f5d003d1e2
5 changed files with 59 additions and 27 deletions

View file

@ -286,9 +286,10 @@ conversions are allowed between some types for convenience.
The following conversions are possible:
- `int` can be converted implicitly to `float` and vice-versa
- `int` can be converted implicitly to `float` and vice-versa.
When converting from `float` to `int`, the value is rounded to the nearest integer.
- `int` and `float` can be converted implicitly to `string`
- `physical-length` and `length` can be converted implicitly to each other only in
- `physical-length`, relative-font-size, and `length` can be converted implicitly to each other only in
context where the pixel ratio is known.
- the units type (`length`, `physical-length`, `duration`, ...) can't be converted to numbers (`float` or `int`)
but they can be divided by themselves to result in a number. Similarly, a number can be multiplied by one of
@ -297,7 +298,7 @@ The following conversions are possible:
- Struct types convert with another struct type if they have the same property names and their types can be converted.
The source struct can have either missing properties, or extra properties. But not both.
- Arrays generally don't convert between each other. Array literals can be converted if the element types are convertible.
- String can be converted to float by using the `to-float` function. That function returns 0 if the string isen't
- String can be converted to float by using the `to-float` function. That function returns 0 if the string isn't
a valid number. You can check with `is-float()` if the string contains a valid number
```slint,no-preview
@ -314,5 +315,6 @@ export component Example {
property<string> xxx: "42.1";
property<float> xxx1: xxx.to-float(); // 42.1
property<bool> xxx2: xxx.is-float(); // true
property<int> xxx3: 45.8; // 46
}
```

View file

@ -249,7 +249,7 @@ fn rgb_macro(
op: '*',
}
} else {
expr.maybe_convert_to(Type::Int32, &n, diag)
expr.maybe_convert_to(Type::Float32, &n, diag)
}
} else {
expr.maybe_convert_to(Type::Float32, &n, diag)

View file

@ -345,7 +345,6 @@ use std::num::NonZeroUsize;
struct ConditionalIncludes {
iostream: Cell<bool>,
cstdlib: Cell<bool>,
cmath: Cell<bool>,
}
#[derive(Clone)]
@ -545,6 +544,7 @@ pub fn generate(
file.includes.push("<array>".into());
file.includes.push("<limits>".into());
file.includes.push("<slint.h>".into());
file.includes.push("<cmath>".into());
for (path, er) in doc.embedded_file_resources.borrow().iter() {
match &er.kind {
@ -879,10 +879,6 @@ pub fn generate(
file.includes.push("<cstdlib>".into());
}
if conditional_includes.cmath.get() {
file.includes.push("<cmath>".into());
}
file
}
@ -2768,11 +2764,14 @@ fn compile_expression(expr: &llr::Expression, ctx: &EvaluationContext) -> String
Expression::Cast { from, to } => {
let f = compile_expression(from, ctx);
match (from.ty(ctx), to) {
(Type::Float32, Type::Int32) => {
format!("std::round({f})")
}
(from, Type::String) if from.as_unit_product().is_some() => {
format!("slint::SharedString::from_number({})", f)
}
(Type::Float32, Type::Model) | (Type::Int32, Type::Model) => {
format!("std::make_shared<slint::private_api::UIntModel>(std::max(0, {}))", f)
format!("std::make_shared<slint::private_api::UIntModel>(std::max<int>(0, {}))", f)
}
(Type::Array(_), Type::Model) => f,
(Type::Float32, Type::Color) => {
@ -3131,59 +3130,45 @@ fn compile_builtin_function_call(
format!("std::cout << {} << std::endl;", a.join("<<"))
}
BuiltinFunction::Mod => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::fmod({}, {})", a.next().unwrap(), a.next().unwrap())
}
BuiltinFunction::Round => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::round({})", a.next().unwrap())
}
BuiltinFunction::Ceil => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::ceil({})", a.next().unwrap())
}
BuiltinFunction::Floor => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::floor({})", a.next().unwrap())
}
BuiltinFunction::Sqrt => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::sqrt({})", a.next().unwrap())
}
BuiltinFunction::Abs => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::abs({})", a.next().unwrap())
}
BuiltinFunction::Log => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::log({}) / std::log({})", a.next().unwrap(), a.next().unwrap())
}
BuiltinFunction::Pow => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::pow(({}), ({}))", a.next().unwrap(), a.next().unwrap())
}
BuiltinFunction::Sin => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::sin(({}) * {})", a.next().unwrap(), pi_180)
}
BuiltinFunction::Cos => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::cos(({}) * {})", a.next().unwrap(), pi_180)
}
BuiltinFunction::Tan => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::tan(({}) * {})", a.next().unwrap(), pi_180)
}
BuiltinFunction::ASin => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::asin({}) / {}", a.next().unwrap(), pi_180)
}
BuiltinFunction::ACos => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::acos({}) / {}", a.next().unwrap(), pi_180)
}
BuiltinFunction::ATan => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::atan({}) / {}", a.next().unwrap(), pi_180)
}
BuiltinFunction::SetFocusItem => {

View file

@ -1987,6 +1987,9 @@ fn compile_expression(expr: &Expression, ctx: &EvaluationContext) -> TokenStream
Expression::Cast { from, to } => {
let f = compile_expression(from, ctx);
match (from.ty(ctx), to) {
(Type::Float32, Type::Int32) => {
quote!(((#f as f32).round() as i32))
}
(from, Type::String) if from.as_unit_product().is_some() => {
quote!(sp::SharedString::from(sp::format!("{}", #f).as_str()))
}
@ -2645,9 +2648,9 @@ fn compile_builtin_function_call(
let (r, g, b, a) =
(a.next().unwrap(), a.next().unwrap(), a.next().unwrap(), a.next().unwrap());
quote!({
let r: u8 = (#r as u32).max(0).min(255) as u8;
let g: u8 = (#g as u32).max(0).min(255) as u8;
let b: u8 = (#b as u32).max(0).min(255) as u8;
let r: u8 = (#r as u32).min(255) as u8;
let g: u8 = (#g as u32).min(255) as u8;
let b: u8 = (#b as u32).min(255) as u8;
let a: u8 = (255. * (#a as f32)).max(0.).min(255.) as u8;
sp::Color::from_argb_u8(a, r, g, b)
})

View file

@ -0,0 +1,42 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
export component TestCase {
property <float> float-var: 26.7;
// Going through a private property of type int should really cast to int
private property <int> tens-digit-var: float-var / 10;
// test that it rounds
out property <int> int-val: -7.6;
out property <string> text: tens-digit-var;
out property <bool> test: tens-digit-var == 3 && text == "3" && int-val == -8;
}
/*
```cpp
auto handle = TestCase::create();
const TestCase &instance = *handle;
assert_eq(instance.get_text(), "3");
assert_eq(instance.get_int_val(), -8);
assert(instance.get_test());
```
```rust
let instance = TestCase::new().unwrap();
assert_eq!(instance.get_text(), "3");
assert_eq!(instance.get_int_val(), -8);
assert!(instance.get_test());
```
```js
var instance = new slint.TestCase({});
assert.equal(instance.text, "3");
assert.equal(instance.int_val, -8);
assert(instance.test);
```
*/