I like the idea.

I wonder if it is possible to reduce the amount of boilerplate code?  For 
example, a CssMetaData can have a

setGetter(Function<S, StyleableProperty<V>> getter)

method which supplies the property reference?  This way 
CssMetaData.isSettable(Node) and CssMetaData.getStyleableProperty(Node) can be 
implemented in the base class (there are more complicated cases, so perhaps 
setIsSettable(Predicate<Node>) would also be required).

Example:

CssMetaData.<ControlExample,Font>of("-fx-font", Font.getDefault(), (n) -> 
n.font)

Just a thought.  What do you think?

-andy


From: openjfx-dev <openjfx-dev-r...@openjdk.org> on behalf of Michael Strauß 
<michaelstr...@gmail.com>
Date: Sunday, December 3, 2023 at 22:02
To: openjfx-dev <openjfx-dev@openjdk.org>
Subject: Reflective discovery of styleable properties
Following up the discussion around the CssMetaData API, I'd like to
chime in with yet another idea. To recap, here's Nir's summary of the
current API [0]:

"Let's look at what implementation is required from a user who wants
to write their own styleable control:
1. Create styleable properties.
2. Create a list of these properties to be passed on.
3. Create a public static method that returns the concatenation of
this list with the one of its parent. (This method happens to be
poorly documented, as mstr said.)
4. Create a public non-static method that calls the static method in a
forced-override pattern because otherwise you will be calling the
wrong static method. (This method's docs seem to be just wrong because
you don't always want to delegate to Node's list.)"


I think this could reasonably be replaced with the following
implementation requirements:
1. Create styleable properties.
2. That's it.

Let's look at what we're actually trying to do: create a list of
CSS-styleable property metadata of a class. But we can easily do that
without all of the boilerplate code.

When ´Node.getCssMetaData()` is invoked, all public methods of the
class are reflectively enumerated, and metadata is retrieved from
`Property` and `StyleableProperty` getters. This is a price that's
only paid once for any particular class (i.e. not for every instance).
The resulting metadata list is cached and reused for all instances of
that particular class.

As a further optimization, metadata lists are also cached and
deduplicated for Control/Skin combinations (currently every Control
instance has its own copy of the metadata list).

Another benefit of this approach is that the CssMetaData can now be
co-located with the property implementation, and not be kept around in
other parts of the source code file. Here's how that looks like when a
new "myValue" property is added to MyClass:

    StyleableDoubleProperty myValue =
            new SimpleStyleableDoubleProperty(this, "myValue") {

        static final CssMetaData<MyClass, Number> METADATA =
            new CssMetaData<MyClass, Number>(
                "-fx-my-value",
                SizeConverter.getInstance(),
                USE_COMPUTED_SIZE) {
            @Override
            public boolean isSettable(MyClass node) {
                return !node.myValue.isBound();
            }

            @Override
            public StyleableProperty getStyleableProperty(
                    MyClass node) {
                return node.myValue;
            }
        };

        @Override
        public CssMetaData getCssMetaData() {
            return METADATA;
        }
    };

    public final DoubleProperty myValueProperty() {
        return myValue;
    }

It is not required to override the `getCssMetaData()` method, nor is
it required to redeclare a new static `getClassCssMetaData()` method.
It is also not required to manually keep the list of styleable
properties in sync with the list of CSS metadata.

I've prototyped this concept for the `Node`, `Region` and `Control` classes [1].

[0] https://mail.openjdk.org/pipermail/openjfx-dev/2023-December/044046.html
[1] https://github.com/openjdk/jfx/pull/1299

Reply via email to