On Tue, 11 Oct 2022 18:50:54 GMT, Andy Goryachev <ango...@openjdk.org> wrote:

>>> You are right, @kleopatra - this executeOnceWhenPropertyIsNonNull() is 
>>> installing a listener. perhaps we should add a similar functionality to 
>>> LambdaMultiplePropertyChangeListenerHandler and actually install a 
>>> WeakListener instead.
>> 
>> - yes, we need weakListeners (to wait for a not-null value to add the event 
>> handler) _and_ remove both the weakListener and the event handlers in dispose
>> - adding functionality to the LambdaHandler sounds like a good idea, but 
>> needs some thinking and should be done in a separate issue which also adds 
>> delegate methods to expose the new functionality in SkinBase for use in sub 
>> classes
>> 
>> For this issue, you might simply inline the utility method and register the 
>> listeners using skinbase api (didn't try, it's your issue :), see 
>> TableRowSkin for a similar pattern 
>> 
>> This conditional adding of handlers/listeners is needed if we have to 
>> support path properties (also f.i. when listening to selectedXX in any of 
>> the skins with selectionModels as properties). Which basically should be 
>> supported by base - but probably without any chance to ever get them ;)
>
> ListenerHelper - replacement for LambdaMultiplePropertyChangeListenerHandler .
> Please take a look at https://github.com/openjdk/jfx/pull/908

> Sure, but I think this should be done with a clear vision of where we're 
> going, and not just as a collection of ad-hoc helper methods. There are even 
> more problems that a new API should at least consider: Some skins change 
> their behavior depending on the configuration of their associated control at 
> the time when the skin is installed. For example, `MenuButtonSkinBase` adds a 
> `MOUSE_PRESSED` handler only if the control didn't specify one for its 
> onMousePressed property:
> 
> ```
> if (control.getOnMousePressed() == null) {
>     control.addEventHandler(MouseEvent.MOUSE_PRESSED, ...
> ```
> 
> But what happens if the `onMousePressed` property is set after the skin is 
> installed? Rather than a one-time check, cases like these should probably be 
> a dynamic expression:
> 
> ```
> when control.onMousePressedProperty().isNotNull() and skin.isInstalled()
>     control.addEventHandler
> otherwise
>     control.removeEventHandler
> ```

I think you meant `isNull()` instead of `isNotNull()`:

    when control.onMousePressedProperty().isNull() and skin.isInstalled()
        control.addEventHandler
    otherwise
        control.removeEventHandler

So... I was curious, and was checking to see if this can be done with fluent 
bindings these days, and I can get quite close. First assume we have a 
convenient property in all skins (or everything that is `SkinBase`) that 
reflects whether or not the skin is currently installed or active:

        BooleanProperty installed = new SimpleBooleanProperty();

You can then create a condition:

        ObservableValue<Boolean> noMouseAndInstalled = installed
            .flatMap(b -> b ? control.onMousePressedProperty() : installed)
            .map(x -> false)  // ---+ use case for isNotEmpty
            .orElse(true);    // --/

Or (if `isNotEmpty` already exists): 

        ObservableValue<Boolean> noMouseAndInstalled = installed
            .flatMap(b -> b ? control.onMousePressedProperty() : installed)
            .isNotEmpty();

It's then a simple matter of adding a listener to this result to either add or 
remove the event handler:

         installed
            .flatMap(b -> b ? control.onMousePressedProperty() : installed)
            .isNotEmpty();
            .addListener((obs, old, active) -> {
               if(active) control.addEventHandler(...);
               else control.removeEventHandler(...);
            });

One of the nice things here is that when `installed` is `false`, the listener 
on `control.onMousePressedProperty()` is automatically removed.  This is 
because the `flatMap` will switch to listening to `installed`.  This should 
mean that it will automatically be eligible for GC when `installed` becomes 
`false`.

The second nice thing is that you can do this in the constructor already, no 
need to do this in the `install` callback.  If you do it in the callback, then 
make sure to do it before `installed` is set to `true` as otherwise the change 
listener won't fire for the first change (another reason to have value 
listeners...)

If this becomes a common idiom, we could introduce a short circuiting `and` 
construct so the `flatMap` can be expressed easier (the short circuiting part 
will ensure the onMousePressedProperty won't be observed if installed is 
`false`):

      installed
          .and(control.onMousePressedProperty().isNotEmpty())
          .addListener( ... );

-------------

PR: https://git.openjdk.org/jfx/pull/906

Reply via email to