diff --git a/api/cpp/lib.rs b/api/cpp/lib.rs index d49dfd8ce28..e2c9c31f306 100644 --- a/api/cpp/lib.rs +++ b/api/cpp/lib.rs @@ -152,6 +152,16 @@ pub extern "C" fn slint_string_to_float(string: &SharedString, value: &mut f32) } } +#[no_mangle] +pub extern "C" fn slint_string_is_empty(string: &SharedString) -> bool { + string.is_empty() +} + +#[no_mangle] +pub extern "C" fn slint_string_length(string: &SharedString) -> usize { + string.len() +} + #[no_mangle] pub extern "C" fn slint_string_to_usize(string: &SharedString, value: &mut usize) -> bool { match string.as_str().parse::() { diff --git a/internal/compiler/expression_tree.rs b/internal/compiler/expression_tree.rs index 74076ab248d..53576effd7f 100644 --- a/internal/compiler/expression_tree.rs +++ b/internal/compiler/expression_tree.rs @@ -52,6 +52,10 @@ pub enum BuiltinFunction { StringToFloat, /// the "42".is_float() StringIsFloat, + /// the "42".is_empty() + StringIsEmpty, + /// the "42".length() + StringLength, ColorRgbaStruct, ColorHsvaStruct, ColorBrighter, @@ -164,6 +168,8 @@ declare_builtin_function_types!( ItemFontMetrics: (Type::ElementReference) -> crate::typeregister::font_metrics_type(), StringToFloat: (Type::String) -> Type::Float32, StringIsFloat: (Type::String) -> Type::Bool, + StringIsEmpty: (Type::String) -> Type::Bool, + StringLength: (Type::String) -> Type::Int32, ImplicitLayoutInfo(..): (Type::ElementReference) -> crate::typeregister::layout_info_type(), ColorRgbaStruct: (Type::Color) -> Type::Struct(Rc::new(Struct { fields: IntoIterator::into_iter([ @@ -274,6 +280,7 @@ impl BuiltinFunction { BuiltinFunction::ItemMemberFunction(..) => false, BuiltinFunction::ItemFontMetrics => false, // depends also on Window's font properties BuiltinFunction::StringToFloat | BuiltinFunction::StringIsFloat => true, + BuiltinFunction::StringIsEmpty | BuiltinFunction::StringLength => true, BuiltinFunction::ColorRgbaStruct | BuiltinFunction::ColorHsvaStruct | BuiltinFunction::ColorBrighter @@ -343,6 +350,7 @@ impl BuiltinFunction { BuiltinFunction::ItemMemberFunction(..) => false, BuiltinFunction::ItemFontMetrics => true, BuiltinFunction::StringToFloat | BuiltinFunction::StringIsFloat => true, + BuiltinFunction::StringIsEmpty | BuiltinFunction::StringLength => true, BuiltinFunction::ColorRgbaStruct | BuiltinFunction::ColorHsvaStruct | BuiltinFunction::ColorBrighter diff --git a/internal/compiler/generator/cpp.rs b/internal/compiler/generator/cpp.rs index 9ec1d723e9d..ca59a218960 100644 --- a/internal/compiler/generator/cpp.rs +++ b/internal/compiler/generator/cpp.rs @@ -3553,6 +3553,14 @@ fn compile_builtin_function_call( ctx.generator_state.conditional_includes.cstdlib.set(true); format!("[](const auto &a){{ float res = 0; slint::cbindgen_private::slint_string_to_float(&a, &res); return res; }}({})", a.next().unwrap()) } + BuiltinFunction::StringIsEmpty => { + ctx.generator_state.conditional_includes.cstdlib.set(true); + format!("[](const auto &a){{ return slint::cbindgen_private::slint_string_is_empty(&a); }}({})", a.next().unwrap()) + } + BuiltinFunction::StringLength => { + ctx.generator_state.conditional_includes.cstdlib.set(true); + format!("[](const auto &a){{ return slint::cbindgen_private::slint_string_length(&a); }}({})", a.next().unwrap()) + } BuiltinFunction::ColorRgbaStruct => { format!("{}.to_argb_uint()", a.next().unwrap()) } diff --git a/internal/compiler/generator/rust.rs b/internal/compiler/generator/rust.rs index 744e7e22961..4e5c72c0c3c 100644 --- a/internal/compiler/generator/rust.rs +++ b/internal/compiler/generator/rust.rs @@ -2929,6 +2929,8 @@ fn compile_builtin_function_call( quote!(#(#a)*.as_str().parse::().unwrap_or_default()) } BuiltinFunction::StringIsFloat => quote!(#(#a)*.as_str().parse::().is_ok()), + BuiltinFunction::StringIsEmpty => quote!(#(#a)*.as_str().is_empty()), + BuiltinFunction::StringLength => quote!(#(#a)*.as_str().len() as i32), BuiltinFunction::ColorRgbaStruct => quote!( #(#a)*.to_argb_u8()), BuiltinFunction::ColorHsvaStruct => quote!( #(#a)*.to_hsva()), BuiltinFunction::ColorBrighter => { diff --git a/internal/compiler/llr/optim_passes/inline_expressions.rs b/internal/compiler/llr/optim_passes/inline_expressions.rs index 9a25f8e2db4..9be00ab4821 100644 --- a/internal/compiler/llr/optim_passes/inline_expressions.rs +++ b/internal/compiler/llr/optim_passes/inline_expressions.rs @@ -111,6 +111,8 @@ fn builtin_function_cost(function: &BuiltinFunction) -> isize { BuiltinFunction::ItemFontMetrics => PROPERTY_ACCESS_COST, BuiltinFunction::StringToFloat => 50, BuiltinFunction::StringIsFloat => 50, + BuiltinFunction::StringIsEmpty => 50, + BuiltinFunction::StringLength => 50, BuiltinFunction::ColorRgbaStruct => 50, BuiltinFunction::ColorHsvaStruct => 50, BuiltinFunction::ColorBrighter => 50, diff --git a/internal/compiler/lookup.rs b/internal/compiler/lookup.rs index 13fc149314d..76cf2de1555 100644 --- a/internal/compiler/lookup.rs +++ b/internal/compiler/lookup.rs @@ -980,6 +980,8 @@ impl<'a> LookupObject for StringExpression<'a> { let mut f = |s, res| f(&SmolStr::new_static(s), res); None.or_else(|| f("is-float", member_function(BuiltinFunction::StringIsFloat))) .or_else(|| f("to-float", member_function(BuiltinFunction::StringToFloat))) + .or_else(|| f("is-empty", member_function(BuiltinFunction::StringIsEmpty))) + .or_else(|| f("length", member_function(BuiltinFunction::StringLength))) } } struct ColorExpression<'a>(&'a Expression); diff --git a/internal/interpreter/eval.rs b/internal/interpreter/eval.rs index 7fdc3ecd25c..e52a5d8d9e9 100644 --- a/internal/interpreter/eval.rs +++ b/internal/interpreter/eval.rs @@ -909,6 +909,26 @@ fn call_builtin_function( panic!("Argument not a string"); } } + BuiltinFunction::StringIsEmpty => { + if arguments.len() != 1 { + panic!("internal error: incorrect argument count to StringIsEmpty") + } + if let Value::String(s) = eval_expression(&arguments[0], local_context) { + Value::Bool(s.as_str().is_empty()) + } else { + panic!("Argument not a string"); + } + } + BuiltinFunction::StringLength => { + if arguments.len() != 1 { + panic!("internal error: incorrect argument count to StringLength") + } + if let Value::String(s) = eval_expression(&arguments[0], local_context) { + Value::Number(s.as_str().len() as f64) + } else { + panic!("Argument not a string"); + } + } BuiltinFunction::ColorRgbaStruct => { if arguments.len() != 1 { panic!("internal error: incorrect argument count to ColorRGBAComponents") diff --git a/tests/cases/types/string_length.slint b/tests/cases/types/string_length.slint new file mode 100644 index 00000000000..51ad4175f28 --- /dev/null +++ b/tests/cases/types/string_length.slint @@ -0,0 +1,38 @@ +// 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 { + in property hello: "hello"; + in property empty; + + out property empty_is_empty: empty.is-empty(); + out property hello_is_not_empty: !hello.is-empty(); + out property test_is_empty: empty_is_empty && hello_is_not_empty; + out property hello_length: hello.length(); + out property empty_length: empty.length(); + out property hello_length_is_5: hello_length == 5; + out property empty_length_is_0: empty_length == 0; + out property test_length: hello_length_is_5 && empty_length_is_0; + out property test: test_is_empty && test_length; +} + + +/* + +```cpp +auto handle = TestCase::create(); +const TestCase &instance = *handle; +assert(instance.get_test()); +``` + +```rust +let instance = TestCase::new().unwrap(); +assert!(instance.get_test()); +``` + +```js +var instance = new slint.TestCase({}); +assert(instance.test); +``` + +*/