OK, I think you've answered my first and third questions. The broader question then is whether this fills a general need, and if so, is allowing for a dynamic user agent style on a Control the best way to provide this feature? I'd like to hear from some of the other developers on this list.

As for the second question, I don't think the API you propose for dynamic user agent style-sheets is feasible in a way that maintains compatibility. The current API model is that the control itself provides its userAgentStylesheet by overriding the get method, returning a String specific to that control (or possibly a specific instance of that control, if the designer of that control wants to add such logic for some reason). You can't compatibly turn this API into a standard property. Our properties have final set/get/property methods for a reason. Namely, to satisfy the invariant that calling the set or get method is identical to calling the property method (this is key to allowing bindings and listeners to work in a consistent and predictable manner). Also, since existing controls that override the getter, they will know nothing about the setter. Applications may wonder why setUserAgentStylesheet(sheet) does not imply sheet.equals(getUserAgentStyleSheet()).

Off the top of my head I can think of three ways to resolve this depending on what app developers and custom controls developers would prefer to see:

A. Provide a property with some other name (i.e., not userAgentStylesheet). If the value of this new property is non-null it takes precedence over the userAgentStylesheet. When it changes, it causes the necessary reevaluation to happen. This is the closest to what you propose, but preserves backward compatibility. This add some additional complexity, which may or may not be justified.

B. Provide an "updateUserAgentStylesheet(String)" method that acts "like" a setter, but a control would be free to ignore (and a control that overrides the "getter" today would certainly ignore). This seems messy and has its own consistency issues. And it still doesn't solve the question of how the control indicates that it has change its user agent stylesheet.

C. Leave it up to the control to decide when and how to change the value of its userAgentStylesheet. Provide a boolean "dynamicUserAgentStylesheet" property indicating whether the userAgentStylesheet is dynamic (most likely likely the control would set it for itself). There might also need to be a way for a control to indicate that its value has changed. Given your use cases, this seems like a good fit.

There may be more options - e.g., a hybrid of B and C if setting this under app control is a thing many apps want to do (although in that case, why not just go with option A)? Maybe there something else I'm not thinking of right now that would work.

A follow-up question is whether and how this would interact with the proposed theming API that is also under discussion (maybe that discussion could be revived)?

Before we spend too much more time on the API discussion, I want to hear from other developers on the list as to the value proposition of doing this and how they would like to see it done.

-- Kevin


On 7/16/2021 4:15 AM, Alessandro Parisi wrote:
The next steps would be to send an email (you can reply to this thread),
with a clear description of your proposal.

The focus should be on what problem you are solving and on the proposed
public API and behavioral changes. I suggest not (yet) referring to code
changes in your PR except for illustrative purposes. Start with what the
current behavior is, what problem that is causing, and what your
proposed solution is. By way of a counter-example, the "computeIfAbsent"
code fragment you included in your email and PR description is an
implementation detail; the concept that should be discussed is the
presumed stability of a Region's userAgentStyleSheet. This presumed
stability is documented in the spec, so start there, not with the code.

If there is general agreement that this feature is a good thing to add
to JavaFX as public API, meaning it is something that would benefit
multiple app developers, the next step would be to review the proposed
API and behavioral changes. This can be done in the context of reviewing
the PR. The focus during this phase of review would be mainly on the
specification changes that would need to go into the API docs and be
included in the CSR.

The above is good general guidance for getting any new non-trivial
feature added to the JavaFX API.

As for the specifics of what you are proposing, the main things I would
like to see addressed during the initial discussion are:

* When and why do custom controls typically want to provide a dynamic
userAgentStylesheet?
* How should a control indicate that its userAgentStyleSheet is dynamic?
By a separate property (read-only?) that it provides as an override?
Should it be dynamic by default (I am skeptical, myself)?
* Does an application that uses a custom control need to know whether or
not that control uses a dynamic userAgentStylesheet? Can it affect the
answer (e.g., you mentioned theming, can you be more specific)?

-- Kevin

So, without talking about code, I'm going to explain what I want to achieve
with this change.

As of now, custom controls  can specify their userAgentStylesheet by
overriding the getter inherited from Region. The documentation says:

An implementation may specify its own user-agent styles for this Region,
and its children, by overriding this method. Subclasses overriding this
method should not assume any particular implementation approach as to the
number and frequency with which it is called. For this reason, attempting
any kind of dynamic implementation (i.e. returning different user agent
stylesheet values) based on some state change is highly discouraged, as
there is no guarantee when, or even if, this method will be called.

The issue there is that the current implementation is not dynamic.
To answer your first question:

When and why do custom controls typically want to provide a dynamic
userAgentStylesheet?

In my library, I currently have two custom controls. For each of them I
have three stylesheets and I want the user  to choose the style they
prefer. So, the control is one, but the styles are three, and I can only
provide one userAgentStylesheet because it is not dynamic. The solution
would be to create three different controls and each of them uses one of
the three stylesheets. However, this would lead to code duplication for
just a stylesheet change.

To answer the third question:

Does an application that uses a custom control need to know whether or
not that control uses a dynamic userAgentStylesheet? Can it affect the
answer (e.g., you mentioned theming, can you be more specific)?

Keeping in mind what I said above, let's suppose the user is developing an
app with dark theme as default. With a dynamic implementation of the
userAgentStylesheet I could specify two or more stylesheets for my custom
controls, one or more for light and dark styles, the user would just need
to change the style. That's what I mean by theming. In my custom controls
then I could specify a way to change the style (an enumerator for example).
It's up to the user then to decide which style to use.
So the control needs to specify a way to change the style and that's up to
the library developer to decide how (like I said an enumerator would be a
way to do it), and JavaFX must allow the control to change the userAgent
dynamically and update the control on change.

To answer your second question:

How should a control indicate that its userAgentStyleSheet is dynamic?

Using a property. Rather than just having a getter, we could introduce a
property to store the userAgent.
When the user changes the style, the property changes too. On change, the
StyleManager should also be notified about the change so that the CSS can
be recomputed

Reply via email to