IMO the current CSS priorities are wrong. A value set from code should be adjacent to an in-line style in terms of precedence. They are both ways of saying saying “this particular instance should have this value”
Scott > On Jan 30, 2024, at 8:31 AM, John Hendrikx <[email protected]> wrote: > > Hi Michael, > > I think we first need to decide what the correct behavior is for CSS > properties, as the "bind" solution IMHO is a bug. > > The StyleOrigin enum encodes the relative priorities of styles and user set > values, but it is incomplete and not fully enforced. There is (currently) > actually a 5th level (next to USER_AGENT, USER, AUTHOR and INLINE) where it > checks the binding state (it has no choice, as it will get an exception > otherwise, or has to call `unbind` first). Whether that's a bug or should > more formally be accepted as the correct behavior remains to be seen. Also > the AUTHOR and INLINE levels are only best effort, as setting a value in code > (USER level) will override AUTHOR and INLINE values **temporarily** until the > next CSS pass... > > So first questions to answer IMHO are: > > 1) Does it make sense to treat values set in code (which IMHO should always > be the last word on anything) as less important than values from AUTHOR and > INLINE styles? This is specified in the CSS document, but not fully enforced. > > 2) Should bound values (which IMHO fall under "values set from code") be able > to override AUTHOR and INLINE styles? Why are they being treated differently > at all? Which StyleOrigin level do they fall under? "USER"? A 5th level > with higher priority than "INLINE"? > > 3) Should properties which hold an AUTHOR or INLINE value reject calls to > `setValue` (to make it clear they are temporary and that your code is > probably wrong)? This currently is not in line with the CSS document. Note > that this will be slightly annoying for the CSS engine as it may not be able > to "reset" such values as easily as before (which is probably the reason it > currently works the way it does, but that's no excuse IMHO). > > As for your potential solution, if you introduce a constant binding system > (to solve a CSS problem), does that make sense for properties as a whole? > What can be achieved with `bindConstant` that can't be done with `setValue`? > `bindConstant` will become the "setter" that always works (never throws an > exception...), but probably at a higher cost than using `setValue`. Would it > not make more sense to only have such methods on the Styleable properties > (which can then also signal this by using an even higher precedence > StyleOrigin instead of relying on bound/unbound) once there is agreement on > the above questions? > > In other words, I'd look more in the direction of providing users with a > better "setter" only for CSS properties, that also uses a different > StyleOrigin, and to bring both binding and setting in line with the CSS > document's specification (or alternatively, to change that specification). > This means that the normal setter provided next to the property method (ie. > setXXX) would have to default to some standard behavior, while a more > specific setter provided on the property itself can have an overriding > behavior, something like: > > setX() -> calls cssProperty.setValue() > cssProperty.setValue() -> sets values if not originated from an AUTHOR or > INLINE stylesheet, otherwise throws exception (as if bound) > cssProperty.forceValue() -> sets value unconditionally, setting > StyleOrigin to some new to introduce 5th level > (StyleOrigin.FORCED/DEVELOPER/DEBUG/CONSTANT/FINAL) > > Binding can then either be categorized as the StyleOrigin.FORCED or if it is > StyleOrigin.USER, the CSS engine is free to **unbind** if the need arises. > > --John > > >> On 30/01/2024 09:25, Michael Strauß wrote: >> Hi everyone, >> >> I'm interested in hearing your thoughts on the following proposal, >> which could increase the expressiveness of the JavaFX Property API: >> >> Problem >> ------- >> The JavaFX CSS system applies the following order of precedence to >> determine the value of a styleable property (in ascending >> specificity): >> 1. user-agent stylesheets >> 2. values set from code >> 3. Scene stylesheets >> 4. Parent stylesheets >> 5. inline styles >> >> While this system works quite well in general, applications sometimes >> need to override individual property values from code. However, this >> doesn't work reliably in the presence of Scene or Parent stylesheets. >> There are two usual workarounds to solve this problem: >> >> A) Use an inline style instead of setting the property value directly. >> >> This is obviously not a good solution, as you'll lose the strong >> typing afforded by the Java language. Additionally, the value must be >> a true constant, it can't be an expression or the result of a >> computation. >> >> B) Create an `ObservableValue` instance that holds the desired value, >> and bind the property to it. >> >> This is a much better solution. However, what we really want is just >> the binding semantics: the bound property becomes unmodifiable and has >> the highest precedence in the CSS cascade. But the API only gives us >> binding semantics if we give it an `ObservableValue`. >> >> >> Solution >> -------- >> I'm proposing to separate the toggles "binding semantics" and >> "observability". While observability requires you to provide an >> `ObservableValue`, binding semantics should work with both observable >> and regular values. >> >> This is a powerful addition to the Property API, since it increases >> the expressiveness of the API in a natural way: >> >> // instead of: >> rect.setStyle("-fx-fill: red; -fx-width: 200"); >> >> // you can use strongly-typed Java code: >> rect.fillProperty().bindConstant(Color.RED); >> rect.widthProperty().bindConstant(200); >> >> Since the `bindConstant` method accepts a value of the property type, >> all features of the Java language can be used to fill in the value. >> >> >> Implementation >> -------------- >> The following method will be added to the `Property` interface: >> >> default void bindConstant(T value) { >> bind(ObjectConstant.valueOf(value)); >> } >> >> Specialized methods will be added to `BooleanProperty`, >> `DoubleProperty`, `FloatProperty`, `IntegerProperty`, and >> `LongProperty`, each with one of the preexisting constant wrappers >> that are already in the framework. >> Some wrappers can be deduplicated (we only ever need two wrapper >> instances for boolean values).
