Binding properties to constant values
Scott Palmer
swpalmer at gmail.com
Tue Jan 30 16:32:33 UTC 2024
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 <john.hendrikx at gmail.com> 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).
More information about the openjfx-dev
mailing list