slint/tests/cases/properties/changes.slint
Olivier Goffart ff2e2e6731
Compiler: Fix changed callback on private global properties
The code would fail to compile because the property would not be seen as
used and would be removed, but not the change callback.

Fixes #8269

Also fix a segfault in the added test because it will initialize the
change callback (and therefore query the properties) because the
SharedGlobal structure is fully initialized.
So we must only initialize the change callback on global after the
SharedGlobal is fully initialized
2025-04-25 16:10:12 +02:00

260 lines
7.6 KiB
Text

// 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 global Glob {
in-out property <int> v: 55;
in-out property <string> r;
changed v => {
r += "|" + v;
}
private property <int> other: v;
changed other => { r += "=" + v }
}
component Chaining {
public function do-change() {
chain-a +=1;
chain-f +=1;
chain-i +=1;
}
property <int> chain-a;
out property <int> chain-a-count;
changed chain-a => { chain-a-count += 1; }
property <int> chain-b;
changed chain-b => { chain-a += 1; }
property <int> chain-c;
changed chain-c => { chain-b += 1; }
property <int> chain-d;
changed chain-d => { chain-c += 1; }
property <int> chain-e;
changed chain-e => { chain-d += 1; }
property <int> chain-f;
changed chain-f => { chain-e += 1; }
property <int> chain-g;
changed chain-g => { chain-f += 1; }
property <int> chain-h;
changed chain-h => { chain-g += 1; }
property <int> chain-i;
changed chain-i => { chain-h += 1; }
}
component SubCompo {
in-out property <int> v: 456;
in-out property <string> result;
changed v => {
result += "sub("+v+")";
}
}
component SubCompoInline {
in-out property <int> v: 456;
in-out property <string> result;
changed v => {
result += "sub2("+v+")";
}
@children
}
component WithAliasToNative {
out property has-focus <=> ti.has_focus;
out property text <=> ti.text;
ti := TextInput {}
}
export component TestCase inherits Window {
in-out property <string> result;
in property <int> value: 56;
changed value => {
if false { return; }
result += "value(" + value + ")";
}
property <int> other: clamp(value + 1, 50, 100);
changed other => {
result += "other(" + other + ")";
debug("Other changed");
}
out property<int> count;
changed result => {
count += 1;
}
WithAliasToNative {
// just make sure this compiles despite has_focus being unused otherwise
changed has_focus => { debug(self.text); }
}
chaining := Chaining {}
public function chaining-do-change() { chaining.do-change(); }
out property chaining-a-count <=> chaining.chain-a-count;
sub2 := SubCompoInline {
v: 123;
changed v => {
self.result += "root2("+self.v+")";
}
result <=> sub.result;
sub := SubCompo {
v: 789;
changed v => {
self.result += "root("+self.v+")";
}
}
}
public function sub-do-change() { sub.v += 1; sub2.v += 1; }
out property sub-result <=> sub.result;
changed sub-result => {
result += "||" + sub-result;
}
Rectangle {
probably-optimized := Rectangle {
property <int> foo: other;
changed foo => {
result += "foo,";
}
}
}
}
/*
```rust
let instance = TestCase::new().unwrap();
slint_testing::mock_elapsed_time(1000);
assert_eq!(instance.get_result(), "");
instance.set_value(56);
slint_testing::mock_elapsed_time(1000);
assert_eq!(instance.get_result(), ""); // so far, nothing have changed
assert_eq!(instance.get_count(), 0);
instance.set_value(142);
assert_eq!(instance.get_result(), "");
assert_eq!(instance.get_count(), 0);
slint_testing::mock_elapsed_time(1);
assert_eq!(instance.get_result(), "other(100)foo,value(142)");
assert_eq!(instance.get_count(), 1);
instance.set_value(8); // this one is going to be merged in the other
instance.set_value(141);
slint_testing::mock_elapsed_time(1);
assert_eq!(instance.get_result(), "other(100)foo,value(142)value(141)");
assert_eq!(instance.get_count(), 2);
// Changing a value and back doesn't have effect
instance.set_value(85);
instance.set_value(141);
slint_testing::mock_elapsed_time(1);
assert_eq!(instance.get_result(), "other(100)foo,value(142)value(141)");
assert_eq!(instance.get_count(), 2);
instance.set_result("".into());
instance.invoke_chaining_do_change();
slint_testing::mock_elapsed_time(1);
assert_eq!(instance.get_chaining_a_count(), 3);
assert_eq!(instance.get_sub_result(), "");
instance.invoke_sub_do_change();
slint_testing::mock_elapsed_time(100);
assert_eq!(instance.get_sub_result(), "sub2(124)root2(124)sub(790)root(790)");
assert_eq!(instance.get_result(), "||sub2(124)root2(124)sub(790)root(790)");
// Global
instance.global::<Glob<'_>>().set_v(88);
assert_eq!(instance.global::<Glob<'_>>().get_r(), "");
slint_testing::mock_elapsed_time(100);
assert_eq!(instance.global::<Glob<'_>>().get_r(), "=88|88");
```
```cpp
auto handle = TestCase::create();
const TestCase &instance = *handle;
slint_testing::mock_elapsed_time(1000);
assert_eq(instance.get_result(), "");
instance.set_value(56);
slint_testing::mock_elapsed_time(1000);
assert_eq(instance.get_result(), ""); // so far, nothing have changed
assert_eq(instance.get_count(), 0);
instance.set_value(142);
assert_eq(instance.get_result(), "");
assert_eq(instance.get_count(), 0);
slint_testing::mock_elapsed_time(1);
assert_eq(instance.get_result(), "other(100)foo,value(142)");
assert_eq(instance.get_count(), 1);
instance.set_value(8); // this one is going to be merged in the other
instance.set_value(141);
slint_testing::mock_elapsed_time(1);
assert_eq(instance.get_result(), "other(100)foo,value(142)value(141)");
assert_eq(instance.get_count(), 2);
// Changing a value and back doesn't have effect
instance.set_value(85);
instance.set_value(141);
slint_testing::mock_elapsed_time(1);
assert_eq(instance.get_result(), "other(100)foo,value(142)value(141)");
assert_eq(instance.get_count(), 2);
instance.set_result("");
instance.invoke_chaining_do_change();
slint_testing::mock_elapsed_time(1);
assert_eq(instance.get_chaining_a_count(), 3);
assert_eq(instance.get_sub_result(), "");
instance.invoke_sub_do_change();
slint_testing::mock_elapsed_time(100);
assert_eq(instance.get_sub_result(), "sub2(124)root2(124)sub(790)root(790)");
assert_eq(instance.get_result(), "||sub2(124)root2(124)sub(790)root(790)");
// Global
instance.global<Glob>().set_v(88);
assert_eq(instance.global<Glob>().get_r(), "");
slint_testing::mock_elapsed_time(100);
assert_eq(instance.global<Glob>().get_r(), "=88|88");
```
```js
var instance = new slint.TestCase({});
slintlib.private_api.mock_elapsed_time(1000);
assert.equal(instance.result, "");
instance.value = 56;
slintlib.private_api.mock_elapsed_time(1000);
assert.equal(instance.result, ""); // so far, nothing have changed
instance.value = 142;
assert.equal(instance.result, "");
slintlib.private_api.mock_elapsed_time(1);
assert.equal(instance.result, "other(100)foo,value(142)");
instance.value = 8; // this one is going to be merged in the other
instance.value = 141;
slintlib.private_api.mock_elapsed_time(1);
assert.equal(instance.result, "other(100)foo,value(142)value(141)");
// Changing a value and back doesn't have effect
instance.value = 85;
instance.value = 141;
slintlib.private_api.mock_elapsed_time(1);
assert.equal(instance.result, "other(100)foo,value(142)value(141)");
instance.result = "";
instance.chaining_do_change();
slintlib.private_api.mock_elapsed_time(1);
assert.equal(instance.chaining_a_count, 3);
assert.equal(instance.sub_result, "");
instance.sub_do_change();
slintlib.private_api.mock_elapsed_time(100);
assert.equal(instance.sub_result, "sub2(124)root2(124)sub(790)root(790)");
assert.equal(instance.result, "||sub2(124)root2(124)sub(790)root(790)");
// Global
instance.Glob.v = 88;
assert.equal(instance.Glob.r, "");
slintlib.private_api.mock_elapsed_time(100);
assert.equal(instance.Glob.r, "=88|88");
```
*/