On Wed, 21 Oct 2026 11:47:14 GMT, Marius Hanl <[email protected]> wrote:
>>> > I do have a solution for this, that I use for my own programs. They're
>>> > basically a different kind of property that allows the owner of the
>>> > property (ie. the control, table, whatever) to control when listeners
>>> > fire (something we see already in some areas of FX where properties are
>>> > "customized" to delay firing, but that's usually manual and not
>>> > coordinated across multiple properties).
>>>
>>> I'm all ears. A solution to this problem in JavaFX would be great. Yeah
>>> JavaFX already uses this mechanism for e.g. `focusProperty` and it makes
>>> sense.
>>>
>>> > For example, if I need to change 3 properties, but they're all related
>>> > (let's say x,y,z) I can change all three, and only at the end of my
>>> > changes have the listeners fire.
>>>
>>> This is exactly what we need.
>>
>> In my FX Flow library, I have a solution for externally read only properties
>> `UpdatableValue`, but general solutions can be built as well. It works like
>> this:
>>
>> // these are private inside some class (like a ReadOnlyPropertyWrapper)
>> private final UpdatableValue<Double> x = UpdatableValue.of();
>> private final UpdatableValue<Double> y = UpdatableValue.of();
>>
>> // UpdatableValue don't have a normal "set" or "setValue" method at all,
>> // instead manipulate using `UpdatableValue.set` method:
>> UpdatableValue.set(
>> x, 100, // these are all typed parameters, despite how it looks
>> y, 100
>> );
>>
>> // or using `batch` method:
>> UpdatableValue.batch(() -> {
>> UpdatableValue.set(x, 100);
>> UpdatableValue.set(y, 100);
>> // supports nesting, can start another batch here, listeners only
>> // fire at the end of the top most level batch
>> });
>>
>> // exposing as read only:
>> public final ObservableValue<Double> x() {
>> return x.asObservableValue();
>> }
>>
>> For more arbitrary property manipulation, I've also got a different system,
>> but it requires wrapping all properties that should participate in a
>> transaction. If we ever add something like this to FX (where we can change
>> the property code itself) the wrapping may not be needed. The
>> `UpdatableValue` system is more light-weight, and works much more like how
>> read only properties work.
>
>> For more arbitrary property manipulation, I've also got a different system,
>> but it requires wrapping all properties that should participate in a
>> transaction. If we ever add something like this to FX (where we can change
>> the property code itself) the wrapping may not be needed. The
>> `UpdatableValue` system is more light-weight, and works much more like how
>> read only properties work.
>
> This looks promising.
>
> I also thought about a potential system and what I came up with (untested,
> just a random shower thought really):
>
> var listenerCallback = property.setSilent(12);
> ...
> listenerCallback.run();
>
>
> Then, the same idea like your batch, adding all those callbacks to a `List`
> and then run them sequentially.
> This was based on how I would like to see the CSS code, because there is so
> much logic in for loops, that this was the only idea where we could first
> 'collect' callbacks and then later execute them sequentially.
>
> It is really the same concept as your idea, but with more overhead and the
> CSS stuff in mind.
> And your idea works like a single transaction, which I like more.
>
> Under the hood, do you still notify each listener sequentially, or do you
> have another logic in place for such a transaction?
> If we find a good way how we could use this with some kind of 'collect' phase
> / easy mechanism to track and then fire the transaction, I think this might
> solve all problems.
>
> Also does this solve the issues that were reported (also partly by you) where
> sometimes JavaFX methods are called in a re-entrant way, messing up the
> layout or internal state completely?
>
> I think cases like this: https://bugs.openjdk.org/browse/JDK-8387639 and
> https://bugs.openjdk.org/browse/JDK-8369617 are the usual result of this.
The code is here:
https://github.com/int4-org/FX/blob/master/fx-core/src/main/java/org/int4/fx/core/util/UpdatableValue.java
To answer your question, it uses a custom observable value internally that can
be asked to fire events manually and will never fire it automatically. All
changes are gathered in a thread local `Set`, and once a batch ends, it just
triggers the listeners on all of them sequentially indeed. Because it is a
`Set`, it won't send a notification twice for the same property.
If you listen on both `x` and `y` you will get notified twice still, but you
can't observe a wrong x/y pair. I also have something to avoid that on the
listener side of things:
Observe.values(x, y).subscribe((x, y) -> { ... });
That one will observe both properties, and call the subscriber when either `x`
or `y` changes and won't call it again if an invalidation listener on `x` or
`y` was called but there was no change. Combining both solutions you get atomic
manipulation of x/y on the writer side, and you get a single callback on the
reader side.
> Also does this solve the issues that were reported (also partly by you) where
> sometimes JavaFX methods are called in a re-entrant way, messing up the
> layout or internal state completely?
Well, I can't speak for all situations, and the layout system involves way more
than just a handful of properties in a single class, but generally **if** you
can delay when listeners fire, then there is no re-entrancy problem because you
can trigger the listeners after your code is done (it will still nest on the
stack, but you can control exactly where that happens).
Before #1081 I explored doing breadth first notification. This requires you to
delay nested changes (so you can't get re-entrancy, but requires recording
changes). The major problem with that approach was that the listener callbacks
would go out of sync with what `getValue` says -- if I delay a nested listener
notification, and just continue with the next one at the same level, that
listener would observe a `getValue` that isn't the same as its `newValue`. It
could work, but it's too late for FX to do this as it requires every listener
to realize that only `newValue` should be acted upon. There is just too much
reliance on depth first (nested) notifications.
> I think cases like this: https://bugs.openjdk.org/browse/JDK-8387639 and
> https://bugs.openjdk.org/browse/JDK-8369617 are the usual result of this.
Unless there's a good reproducer attached, a lot of these are likely the result
of doing something from a thread other than the FX thread. Re-entrancy problems
are deterministic, so there should be a reproducer.
-------------
PR Review Comment: https://git.openjdk.org/jfx/pull/2347#discussion_r4336578487