slint-ui / slint

Slint is a declarative GUI toolkit to build native user interfaces for Rust, C++, or JavaScript apps.
https://slint.dev
Other
17.73k stars 615 forks source link

Please consider allowing to specify initial values for two-way bindings #6105

Open Enyium opened 2 months ago

Enyium commented 2 months ago

When writing a component and using the preview, you can specify example values on property declarations without <=> to develop the component using a realistic appearance. Proper example data may even be essential to preview anything useful at all, if you work with the visible attribute or conditional rendering.

However, as soon as you use a two-way binding, you're robbed of the possibility to preview the UI using example data. Therefore, a possibility to specify data on two-way bindings that's used initially would be good.

How about being able to specify the initial data after a comma? And if the data is preceded by the keyword preview, it's only for the preview and won't be used at runtime.

Since this would then be a general principle, you could use it for all kinds of bindings:

    in-out property <int> foo: 123, preview 456;
    in property <[StandardListViewItem]> model-1 <=> list-view-1.model, preview [{text: "bar"}, {text: "baz"}];
    in property <[StandardListViewItem]> model-2 <=> list-view-2.model, [{text: "qux"}];
    in property <[StandardListViewItem]> model-3 <=> list-view-3.model, [{text: "qux"}], preview [{text: "abc"}];

Perhaps, there would also be a use case for specifying that certain states should only be used in previews, and are removed otherwise, potentially further improving the development experience.

asuper0 commented 2 months ago

As far as I see, you may set initial value to the component you bind to.

in property <[StandardListViewItem]> model-1 <=> list-view-1.model;

list-view-1 := ListView {
  model : [{text: "bar"}, {text: "baz"}];
}
ogoffart commented 2 months ago

Regarding the use of dummy data for previewing, I think this would be better served with an feature for it (we've been thinking about that for some time, i just filled an issue for it: https://github.com/slint-ui/slint/issues/6113)

Other than that, @asuper0 is right that setting the value in the other part is the right thing to do, as the initial value of the property is going to be the right hand side of the expression in case both are set. This is already quite confusing what is going to be the initial value if there is several options (the default value in the base component, or the default value of the linked property)

Enyium commented 2 months ago

Other than that, @asuper0 is right that setting the value in the other part is the right thing to do, as the initial value of the property is going to be the right hand side of the expression in case both are set.

This should be made clear in the docs. It's just vaguely hinted at via the background property in the example there.

in case both are set.

Do I understand this correct that, if you set model-1: [{text: "abc"}] from the outside, this won't supersede the inner model: [{text: "bar"}, {text: "baz"}] of list-view-1, because the inner binding has list-view-1.model on the right?! This would be indeed confusing.

ogoffart commented 2 months ago

Yes, the documentation could be better, I have updated the description of https://github.com/slint-ui/slint/issues/5990

But if you set the value from the outside, it is the outside value that takes precedence.

Enyium commented 2 months ago

But if you set the value from the outside, it is the outside value that takes precedence.

What did you mean then with "in case both are set"? Isn't in the above case model: ... on list-view-1 := ListView { the only place where the value can be set (the right-hand side of the two-way binding)?