slint-ui / slint

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

Two-way bindings to item in a model #2013

Open god-anubis opened 1 year ago

god-anubis commented 1 year ago

Maybe this is fixed in the updated syntax, but with the current version of 0.3.3 (using rust) issues exist in how/where properties are declared that determine if two-way bindings are possible in .slint files.

Regular properties work with two-way bindings as expected:

export Sample1 := Rectangle {
    in-out property <string> myprop1;
    in-out property <int> myprop2;
    LineEdit { text <=> myprop1; }
    SpinBox { value <=> myprop2; }
}

But if a struct is used things break:

export struct MyStruct := {
    string_prop: string,
    int_prop: int,
}

export Sample2 := Rectangle {
    in-out property <MyStruct> myprops;
    LineEdit { text <=> myprops.string_prop; } // gives: The expression in a two way binding must be a property reference
    LineEdit { text: myprops.string_prop; } // One-way bindings work
}

There is a workaround for the struct problem using inheritance though (not sure if slint is intended to be used like this):

export FakeStruct := Rectangle {
    in-out property <string> string_prop;
    in-out propert <int> int_prop;
}

export Sample2a := FakeStruct {
    LineEdit { text <=> string_prop; } // now that string_prop is inherited it works
}

Also the use of arrays (at least in for loops) may break things:

// This one works
export Sample3 := Rectangle {
    in-out property <[string]> multiprop;
    in-out property <[length]> widths;
    for my_prop[idx] in multiprop : HorizontalLayout {
        width: widths[idx];
        Text { text: my_prop; }
    }
}

// But this doesn't
export Sample3 := Rectangle {
    in-out property <[string]> multiprop;
    in-out property <[length]> widths;
    for my_prop[idx] in multiprop : HorizontalLayout {
        width: widths[idx];
        Text { text <=> my_prop; } // gives: The expression in a two way binding must be a property reference
    }
}

// And neither does this
export Sample3 := Rectangle {
    in-out property <[string]> multiprop;
    in-out property <[length]> widths;
    in-out property <string> sometext;
    for my_prop[idx] in multiprop : HorizontalLayout {
        width <=> widths[idx]; // gives: The expression in a two way binding must be a property reference
        Text { text <=> sometext; } // now works with non-array property
    }
}

Would the above examples be expected to work? Feel free to point out any incorrect assumptions on my part or any part of the documentation that mentions or explains the observed behaviour that I might have missed.

ogoffart commented 1 year ago

Thank you for your detailed and informative report.

You're encountering three separate issues related to binding:

We have ongoing discussions about the struct binding issue in #814 For the two other issue we could use a similar approach to the struct binding issue and call model.set_row_data behind the scenes when the property is changed. However, implementing any of these issues is not a trivial task.

I am rewording the title of this issue to keep track of the binding to model issue, while the binding to struct is tracked in #814