Virtual Flow enhancements
Dirk Lemmermann
dlemmermann at gmail.com
Wed Sep 14 10:18:00 UTC 2022
Hi,
for me the most important aspect regarding the future work on VirtualFlow right now is to get back a piece of functionality that I require for the FlexGanttFX framework and and I am sure others also need. It is the ability to synchronize the scrolling of two VirtualFlows with each other. In the case of FlexGanttFX it is the VirtualFlow of the left-hand side of the GanttChart control (a TreeTableView) and the right-hand side of the same control (a ListView).
FlexGanttFX used to make this work via bidirectional bindings of the properties of the vertical scrollbars of both VirtualFlows. With the latest fixes to the VirtualFlow the assumption that two identically populated VirtualFlows would provide identical values to the ScrollBar properties is no longer true. The attempt to bind the “position” property also failed and a work-around that Johan provided also has not been successful, yet (a customer of mine is still evaluating it).
For the FlexGanttFX framework it would be super important to have a proper / recommended way to get the behaviour / the ability back to synchronise scrolling (backed up by public API).
Dirk
P.S.: at the moment I am asking my customers to stay on JavaFX 16, but some of them are starting to ask for newer versions because of fixes made in those.
> On 13 Sep 2022, at 20:16, John Hendrikx <john.hendrikx at gmail.com> wrote:
>
> I think it would be very good to add high level tests for those general use cases -- I've always found the VirtualFlow code quite complicated, but most of that comes from the need to allow different heights.
>
> Not sure if this is relevant, but perhaps it is one of those "common sense" cases: one thing I missed since the inception of these controls is a way to position them in a such a way that they (pixel perfectly) show a certain state, tracking a cell or position, to allow me to show the control exactly the same as it was last time, or that the control tracks the position of a cell that may still be in flux (due to background loading of data).
>
> For example, I scrolled my list so that the first Item is half visible (at the top), and the fifth item is a quarter visible (at the bottom) and I double click the 3rd item to go to a new screen (destroying the original list view in the process), I find there is no way to recreate the list view in the exact same state as it was before.
>
>
> [ The part below turned out to be a bit of ramble... I could perhaps turn it into a ticket if others agree -- I think it also shows that there may not be one correct way to handle the scroll / positioning behavior of a list view, and that more user control could be warranted ]:
>
>
> Preferably, I'd be able to tell the view, show item X (the one I double clicked) at the same vertical position as it was before. If the list meanwhile changed and there are other items before/after it, then those are allowed to be different, but the item I clicked should be in the exact same position. The only exception is if there are insufficient items before item X now, then it is acceptable to show X in a higher position. The scrollTo function doesn't quite capture that (and has been generally unreliable IIRC requiring `Platform.runLater` wrapping for example).
>
> Perhaps support something like an anchor position, which is updated **only** when there is user interaction (selecting, scrolling, using scroll keys). The only other way to change it would be programmatically. The anchor would indicate the preferred cell or part of the view to keep in an exact stable position and is never changed by the view itself unless there was some interaction (ie. size changes of cells and the view do not change the anchor, and neither do changes in the underlying list of items). Not even a removal of the anchored index or item should update the anchor -- instead the view should fall back to a secondary anchor of its own choosing. If the index or item later is available again, it will start using the primary anchor again.
>
> The anchor should support I think three use cases:
>
> - Anchoring an index
> - Anchoring an item T
> - Anchoring a scroll percentage
>
> Anchoring an index would allow you to simply keep whatever item is at a certain index at a stable position. This could be the first item or the current last item, regardless of size changes or new items being added/removed. Using an index of Integer.MAX_VALUE could be supported here as an alternative to anchoring the bottom scroll position to the bottom of the view.
>
> Similarly, anchoring a specific item T would keep that item in a stable position, even if the list is sorted, items around it are added or removed or the item itself changes size.
>
> Anchoring a scroll percentage allows you to keep the view at a fixed position. Anchoring it at the bottom for example would be suitable for lists where the latest added items should be kept in view at all times.
>
> The anchor is changed by the view code when the user selects an item to that specific item in that specific position (Item X should appear at 25% of the control height). The anchor is also changed when the user uses the scroll bar, fixing it at percentage 0 or 100 if the bar was moved all the way to the top or bottom, and on the top item otherwise (this behavior may need to be selectable). Similar changes of the anchor are allowed for navigation keys, up, down, pg up, pg down, home, end.
>
> To show the 42nd item in the exact center of the view do (and keep it there regardless of items appearing/disappearing/growing/shrinking):
>
> setAnchoredIndex(42, 0.5, 0.5); // item 42's center is shown at the view center
>
> To show the 42nd item's top exactly at 25% of the view:
>
> setAnchoredIndex(42.0, 0.0, 0.25); // item 42's top is shown at 25% of the views height
>
> Item anchors:
>
> setAnchoredItem(itemX, 0.5, 0.5); // itemX's center is shown at the exact center
>
> Scroll anchors:
>
> setAnchoredPosition(1.0, 1.0); // keep bottom of list at all times at bottom of view
>
> Note that you could also ensure there is 50% empty space with an anchor at the top or bottom, like this:
>
> setAnchoredPosition(1.0, 0.5); // keep bottom of list at all times at 50% of the view
>
> For a list with images for example, which are often lazily loaded and have various sizes, the anchor would allow a user to easily say which item should be visible and where it is should appear relatively in the view, even if the item is not yet fully loaded; ie. setting the anchor with `setAnchoredItem(myLazyImageItem, 0.5, 0.5)` might initially display a small place holder cell in the exact center of the view, but once the image is loaded it might be much bigger and the view would jump to allow this; with the anchor, the image would remain in the exact center. Only if the image is higher than the view itself it would be scrolled a bit to show its top.
>
> --John
>
>
> On 13/09/2022 11:02, Johan Vos wrote:
>> Hi,
>>
>> OpenJFX 17 contains a fix for JDK-8089589 [1] ([ListView] ScrollBar content moves toward-backward during scrolling.) Before this fix, the scrollbar in the VirtualFlow backing List/Table/TreeTableView controls was treating every cell of equal height. That generated an awkward scrolling experience, especially when there are major differences in sizes in the backing list.
>>
>> The original fix introduced some new issues, and most of these are meanwhile fixed, and tests are added (thanks to e.g. Florian Kirmaier who created a good amount of tests).
>>
>> However, people have in the past been relying on undocumented behavior while creating apps and libraries, and that behavior is not guaranteed anymore with the fix for JDK-8089589 that went into OpenJFX 17.
>> Most of these cases are related to
>> (1) assuming that IndexedCell.updateItem() is only called when an item is (about to be) shown and
>> (2) assuming that the value returned by the positionProperty of the scrollbar thumb is linearly related to the index of the item in the backinglist being shown.
>>
>> Both of these assumptions are invalid, but were (more or less) happening before JavaFX 17.
>> I have seen a fair amount of creative implementations that somehow get and process information about the position of elements in the controls. Some of these relied on the false assumptions above, and are obviously no longer working.
>> Instead of providing fixes per case, I think it would be good to capture the common use cases, and make it easier for developers to achieve what they want to achieve. I didn't yet encounter a use case that can not be implemented with the current approach (using public API's only), but granted, implementations often require too much boilerplate and wiring code.
>>
>> Crucial in this exercise are tests. There are a fair amount of tests for the list/table/treetableview controls, but only few of them are focused on external access and behavior.
>> For all the fixes done so far in VirtualFlow, additional tests have been written which are now excellent regression tests. The last thing we want is that a fix for usecase A is not working anymore after a fix for usecase B.
>>
>> Hence, I'd like us to do 2 things:
>> 1) for issues that are clearly bugs (*violating the JavaDoc*), please create JBS issues as usual (with failing test code).
>> 2) for issues that are not violating the specs now, but are "common sense", please create a description and test scenario with expected versus actual outcome.
>>
>> I am not 100% sure what the best approach is to tackle issues that fall in the second category, but I suggest this: if you have a rather vague, abstract issue (e.g. "listview should always scroll to the bottom of the list IF it was at the bottom before and a new element is added") , it probably makes sense to first discuss it here. If on the other hand you have a very clear issue with a clear failing test, it can be a JBS issue as well -- but we need to keep in mind then that it might be in the category of "this fixes usescase A but blocks usecase B"
>>
>> Thanks,
>>
>> - Johan
>>
>> [1] https://bugs.openjdk.org/browse/JDK-8089589
More information about the openjfx-dev
mailing list