RFR: 8196065: ListChangeListener getRemoved() returns items that were not removed. [v6]
Michael Strauß
mstrauss at openjdk.java.net
Sun May 23 15:10:04 UTC 2021
On Sun, 23 May 2021 10:13:48 GMT, Jeanette Winzenburg <fastegal at openjdk.org> wrote:
>> Michael Strauß has updated the pull request with a new target base due to a merge or a rebase. The pull request now contains 10 commits:
>>
>> - Merge branch 'master' into fixes/JDK-8196065
>>
>> # Conflicts:
>> # modules/javafx.controls/src/test/java/test/javafx/scene/control/MultipleSelectionModelImplTest.java
>> - Merge branch 'master' into fixes/JDK-8196065
>> - Merge branch 'master' into fixes/JDK-8196065
>>
>> # Conflicts:
>> # modules/javafx.controls/src/main/java/javafx/scene/control/ControlUtils.java
>> - Cleanup
>> - Fixed clear-and-select change notification
>> - Add failing tests
>> - Cleanup
>> - Added tests
>> - Fix incorrect index when multiple remove changes are handled in SelectedItemsReadOnlyObservableList
>> - Add failing test
>
> modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/SelectedItemsReadOnlyObservableList.java line 54:
>
>> 52: while (c.next()) {
>> 53: if (c.wasReplaced()) {
>> 54: List<E> removed = getRemovedElements(c, totalRemovedSize);
>
> hmm, don't quite understand all the fuss (but might be overlooking something, has been a while since I did a deeper dig into the selection issues ;)
>
> - we have a copy of the items (itemsRefs) with the same coordinates we receive from the change
> - this list is in sync with the indices, that is it contains the items at the respective selectedIndices
> - currently, that's done in a bulk setting at the end of the listener code
>
> Why not sync our copy while working through the change? Doing so, would automatically collect the state of our own change, or what am I missing?
This was my first thought, too. However, it doesn't work because `SelectedItemsReadOnlyObservableList` doesn't truthfully report its changes, so we can't simply replay its reported changes onto our copy of the items list. For example, consider the following simple piece of code:
ListView<String> listView = new ListView<>(FXCollections.observableArrayList("foo", "bar"));
MultipleSelectionModel<String> model = listView.getSelectionModel();
model.setSelectionMode(SelectionMode.SINGLE);
model.getSelectedIndices().addListener((ListChangeListener<? super Integer>)System.out::println);
model.select(0);
model.select(1);
You'd expect the following output:
{ [0] added at 0 }
{ [0] replaced by [1] at 0 }
However, the actual output is:
{ [0] added at 0 }
{ [1] added at 0 }
While that is a bug, it almost seems like it is a bug _by design_. `MultipleSelectionModelBase` actually contains lots of code to suppress change notifications. In this particular example, a crucial change notification is suppressed in `MultipleSelectionModelBase:405`:
if (getSelectionMode() == SINGLE) {
startAtomic();
quietClearSelection();
stopAtomic();
}
selectedIndices.set(row);
The reason why `MultipleSelectionModelBase` interferes with `selectedIndices` change notifications is to prevent surfacing all kinds of invalid intermediate selection states.
I tried to fix this by making `selectedIndices` changes transactional, where each of the `MultipleSelectionModel` operations would be a transaction that silently accumulates changes (clear, set, and so on), and at the end of a transaction, the changes would be distilled down into a single change notification.
However, in doing so, I ended up rewriting large parts of `MultipleSelectionModelBase`. This seemed like too big of a change. Also, I noticed that at one point you prototyped a new implementation of `SelectionModel`... maybe starting from scratch is better than adding transactions on top of the existing implementation.
> modules/javafx.controls/src/test/java/test/com/sun/javafx/scene/control/SelectedItemsReadOnlyObservableListTest.java line 120:
>
>> 118: assertEquals(change(replaced(0, range("foo"), range("bar"))), changes.get(0));
>> 119: }
>> 120:
>
> Another thingy I don't quite understand, sry ;)
>
> What do you want to test here? We have:
>
> selectedItems before: [foo, bar]
> selectedItems after: [bar, foo]
>
> so we need a change (the "should be" snippet in the description of this method)
>
> replaced [foo, bar] by [bar, foo] at 0
>
> we get:
>
> replaced [foo] by [bar] at 0
>
> which is an incorrect notification (because it's incomplete, doesn't notify at all about the replace at 1) covered by a passing test .. feels fishy. The other way round: if selectedItems cannot react to such a use-case with a correct notification, it might be broken?
Yes, `selectedItems` is broken. I agree that having an objectively incorrect test pass is fishy, but the alternative would be to not have the test at all, and lose the "free" heads-up if at some point in the future the underlying issue is fixed. Do you think it's better to have the correct test, but ignore it?
-------------
PR: https://git.openjdk.java.net/jfx/pull/478
More information about the openjfx-dev
mailing list