New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
8323511: Scrollbar Click jumps inconsistent amount of pixels #1326
base: master
Are you sure you want to change the base?
8323511: Scrollbar Click jumps inconsistent amount of pixels #1326
Conversation
Making the scrolled height consistent, when abvoe/below the scrollbar is clicked.
👋 Welcome back fkirmaier! A progress list of the required criteria for merging this PR into |
Webrevs
|
Reviewers: @johanvos @andy-goryachev-oracle /reviewers 2 |
@kevinrushforth |
Thanks for the PR!
I'll look into this more detailed. |
For what it's worth, I just wanted to mention an alternative algorithm that was used in another project:
Please let me know if you want to see the code. |
Tested on macOS 14.1.2 with ListView, fixed and variable cell height, using MonkeyTester I do see two problems:
It seems to happen when the cell height exceeds the viewport height AND the cell is positioned exactly at the top of the viewport. Can you see it? |
@@ -1545,25 +1545,37 @@ public void scrollTo(int index) { | |||
} | |||
|
|||
// will return true if scroll is successful | |||
private boolean tryScrollOneCell(int targetIndex, boolean downOrRight) { | |||
private boolean tryScrollOneCell(int targetIndex, boolean downOrRight) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please revert indentation change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed it!
This is one of the major difficulties with the VirtualFlow. The word "expect" is very context-dependent. Different scenario's have different expectations. As long as there is nothing specified in the JavaDoc, I don't think one can expect anything.
The location of the scrollbar thumb does not depend on previous actions, but on the current estimated position in the total sheet, which may vary for a number of reasons. We have the implicit restriction that even though the estimated position may change intermediately, we will not reposition the scrollbar thumb unless there is an explicit user action (scrolling or jumping etc). For some time, we have been going back and forth to patch VirtualFlow in order to match particular expectations. By making a change, it is often very likely that the expectations in some scenario are violated -- although no contract is violated, hence no bug is introduced. |
reverted accidental indentation chang
cells.addLast(cell); | ||
scrollPixels(getCellLength(cell)); | ||
if (targetIndex < 0) { | ||
T cell = getCell(targetIndex); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason you call getCell
here, but getAvailableCell
below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I was also involved in fixing this error, I can answer.
The getAvailableCell()
method creates a new cell if none exists for the specified index (-1).
The getCell()
returns the accumCell
in this case, which is completely sufficient for our case, because we only need the dimensions of the cell here. The cell itself will not be added to the scene-graph.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds right.
@Maran23 does this answer your question?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. But is it possible to scroll to an index that is negative? (<0) And what do we estimate then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But is it possible to scroll to an index that is negative?
Yes, both the negative value (-1
) and the positive value, which is greater than the items count (to be precise last item index + 1
), are possible and indicate the direction in which the scrolling takes place.
The last item index + 1
is already handled correctly. We only need to fix the handling of -1
index.
Can you explain your second question in more detail?
@FlorianKirmaier This pull request has been inactive for more than 4 weeks and will be automatically closed if another 4 weeks passes without any activity. To avoid this, simply add a new comment to the pull request. Feel free to ask for assistance if you need help with progressing this pull request towards integration! |
@FlorianKirmaier I still think this would be a good addition. I believe there is only one open question (from Marius) so it would be great if you can answer that. |
@johanvos |
❗ This change is not yet ready to be integrated. |
@@ -139,11 +139,10 @@ public final BooleanProperty virtualProperty() { | |||
if (firstVisibleCell != null && firstVisibleCell == lastVisibleCell) { | |||
int index = firstVisibleCell.getIndex(); | |||
if (newValue < oldValue) { | |||
index = Math.max(0, index - 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are the boundary checks (Math.max/min) removed here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Math.max(0, index - 1)
was introduced to fix an IndesOutOfBoundsException (JDK-8311983) for mouse clicks above the thumb, but it also introduced this bug (different scroll amount for clicks above and below thumb).
This change fixes the handling of -1 cell indexes in a different way, but for that we have to pass it to VirtualFlow.scrollTo(int index)
and then to VirtualFlow.tryScrollOneCell(int targetIndex, boolean downOrRight)
.
Therefore it is necessary to remove this Math.max(..)
call.
Are there any open questions or objections? Can this pull request be merged? |
I plan to do an in-depth review later this week. |
Intuitively, the added test seems to be the right thing to do. It indeed fails before and succeeds after the patch. The suggested patch changes the conceptual idea of The way the I believe the first thing that needs to be done, is to extend the "specification" that is currently in comments in the VirtualScrollBar:
What does this mean if there is no cell further in the direction the user has clicked? Should we them scroll to the top of the current cell (current behavior), or should we use a more gradual scroll (which is the new behavior, which I believe is better indeed)? If the latter is the preferred case, this looks to a behavior that is more similar to the Event that is received when the mousewheel is used (and which invokes |
Yes, this is exactly what I mean and where I do not know if this is the right approach.
Agree, it sounds somewhat weird to me that when
I completely agree. This sounds like we may should call |
I understand your objections. I will create a new pull request. We can then discuss which solution we use. But I already have a few questions about it. In the new pull request, the public class VirtualScrollBar extends ScrollBar {
@Override
public void adjustValue(double pos) {
if (isVirtual()) {
IndexedCell<?> cell = flow.getCell(-1);
double pixels = flow.getFixedCellSize() > 0
? flow.getFixedCellSize()
: flow.isVertical()
? cell.getLayoutBounds().getHeight()
: cell.getLayoutBounds().getWidth();
if (pos > getValue()) {
flow.scrollPixels(pixels);
} else {
flow.scrollPixels(-pixels);
}
} else {
super.adjustValue(pos);
}
}
} The main question is how far should we scroll when the user clicks on the scrollbar-track? For backwards compatibility reasons, I have used the same logic to calculate the delta pixels as is currently used. Should we move the entire delta pixel calculation logic into a new method |
That's a good question. Since there are a number of scenario's that change the current position (offset) of the VirtualFlow, we have to be careful that those scenario's are not conflicting, and are using the same code when appropriate. Hence, duplicating code between VirtualFlow and VirtualScrollBar sounds dangerous (what if code is changed on one place but not on the other?) Rather than shifting some logic to VirtualScrollBar, I believe it would be safer to have VirtualScrollBar calling into VirtualFlow. For mouse-scrollwheel events, this is already the case so it might be a good idea to do something similar here? |
…y one cell is visible
I added a new method Now, if only one cell is visible, the |
@@ -1800,6 +1800,16 @@ public T getCell(int index) { | |||
return accumCell; | |||
} | |||
|
|||
/** | |||
* The amount of pixels by which to adjust the position when the the scroll bar track is clicked. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should it be the height of the viewport, so to speak?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that would also be my favorite choice for the click on the scrollbar track as well as for the PageUp/PageDown.
But for reasons of backwards compatibility, I didn't want to change it here.
*/ | ||
public double getBlockIncrement() { | ||
// For reasons of backward compatibility, we use the cell length of the empty cell (cell index -1) | ||
return getCellLength(-1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How would that work when cell heights are different for each cell?
What if the cell height is much larger than the viewport height (as in, 10 times larger?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like that either, but as I said I didn't want to change it because of backwards compatibility.
I would like to make it configurable or even make the complete scrolling behavior interchangeable.
Because, the actual scrolling behavior is still not consistent.
If multiple cells are visible, the cells are currently aligned (when clicking on the scrollbar) so that they are either exactly at the top or exactly at the bottom. If only one cell is visible, the scrolling behavior changes to the applying of pixel delta.
For example, if you have two large cells and a small ViewPort so that only one cell is visible.
- If you click on the thumb below the scroll bar, the ListView is scrolled by a pixel delta so that two cells can become visible.
- If you click below scroll thumb again, the ListView scrolls so that the second cell appears at the top.
- If you click above the scroll thumb, the ListView scrolls by pixel delta, as the second cell is also larger than the viewport.
Now the ListView is in a different state than before the click (2).
These two behaviors are each useful for their own situation and should not be mixed.
- Align the cell at the top or bottom if you have cells of the same size that are smaller than the viewport.
- Scroll by pixel delta if you have cells of different sizes or if the cells are larger than the viewport.
* | ||
* @return the value in pixels | ||
*/ | ||
public double getBlockIncrement() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather not add that method but instead make getViewportLength()
public. That would benefit other use cases, and it is more behavior-agnostic. The "viewportLength" is easier to define in a contract than a "blockIncrement" and since this is exactly what you return, it sounds like a reasonable thing to do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good suggestion. I have made the necessary changes.
…tBlockIncrement()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eduardsdv I left a couple comments on the API docs for the new public method.
Regarding the CSR, if you don't have a JBS account, perhaps you can ask @FlorianKirmaier or @johanvos to create the CSR for you.
And I agree with @andy-goryachev-oracle that you should be added a contributor. @FlorianKirmaier will need to do that, since only the author of the PR can add contributors.
double getViewportLength() { | ||
|
||
/** | ||
* The length of the viewport portion of the VirtualFlow as computed during the layout pass. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: use code case, {@code VirtualFlow}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
* For a vertical flow, this corresponds to the height and for a horizontal flow to the width of the clip view, | ||
* but it does not necessarily have to be the same value. | ||
* | ||
* @return the viewport length in pixel |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That should be "in pixels" (plural).
Also, please add an @since 23
tag after the return tag.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
I ask @FlorianKirmaier to create a CSR ticket. |
I did a number of tests with the new approach. It works as expected, and I don't see scenarios that could lead to a performance issue. |
could you please point to these places? I think this change is inline with the modern UIs, at least macOS and Windows. As Eduard correctly pointed out, it makes sense to align on the cell top boundary when changing selection, but mouse navigation via scroll bar should use the view port height. In other words, going back and forward with the mouse should revert the control to the same state (unless it hit the rail). |
/contributor add @eduardsdv |
@FlorianKirmaier Syntax:
User names can only be used for users in the census associated with this repository. For other contributors you need to supply the full name and email address. |
/contributor add @eduardsdv |
@FlorianKirmaier Syntax:
User names can only be used for users in the census associated with this repository. For other contributors you need to supply the full name and email address. |
/contributor add Eduard Sedov eduard.sdv@web.de |
@FlorianKirmaier |
I've added eduard as a contributor! CSR |
Thanks!
Yes, the titles of the JBS bug, the associated CSR, and the PR must all match. And speaking of the CSR, based on Johan's comment about the behavior change, that should also be documented in the CSR. We don't currently document the behavior of most controls in the spec, but significant behavioral changes should still be listed in the CSR when they change. |
Ok, I've updated the CSR accrodingly together with Eduard. |
I'm not sure I understand the question, as this is exactly what the PR is doing. The scrollToTop/scrollToBottom is replaced with a scroll of X pixels. |
I thought there were specific comments somewhere in the code. Never mind then. Thank you for responding! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The API changes look good. I left one minor wording suggestion on the API docs.
The new behavior feels more consistent to me, so I have no concerns. I'll leave it to @johanvos and @andy-goryachev-oracle to formally review it.
double getViewportLength() { | ||
|
||
/** | ||
* The length of the viewport portion of the {@code VirtualFlow} as computed during the layout pass. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggested change:
"Returns the length of the viewport ..."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
@FlorianKirmaier or @eduardsdv can one of you merge in the latest upstream master? The source branch for this PR is a few months (and 134 commits) behind master. |
I have merged the master into this branch. |
* For a vertical flow, this corresponds to the height and for a horizontal flow to the width of the clip view, | ||
* but it does not necessarily have to be the same value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@johanvos added a question in the CSR about this last part:
I understand and agree with the goal behind this.
I'm a bit confused though about the following: "...but it does not necessarily have to be the same value." -> can you elaborate a bit about this?
I share this concern. I think that removing that last clause and putting a period after "clip view" is probably the best.
Johan: what do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@johanvos added a question in the CSR about this last part:
I understand and agree with the goal behind this.
I'm a bit confused though about the following: "...but it does not necessarily have to be the same value." -> can you elaborate a bit about this?I share this concern. I think that removing that last clause and putting a period after "clip view" is probably the best.
Johan: what do you think?
I agree that removing that clause is probably best to avoid confusion. Having open-ended suggestions in javadoc can lead to broad speculation, so I think it either should be explained (like you did in the CSR issue with the rubberband effect example) or removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably also avoid the word "corresponds".
/**
* Returns the length of the viewport portion of the {@code VirtualFlow} as computed during the layout pass.
* For a vertical flow this is based on the height and for a horizontal flow on the width of the clip view.
*
* @return the viewport length in pixels
* @since 23
*/
The text explains that depending on the orientation of the view height or width is used in the calculation and the word "based" makes it clear that the value can differ from the respective size of the view.
This version looks good to me. If it is fine for you too, I would check it in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm happy with this wording.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The latest version looks good.
As seen in the unit test of the PR, when we click on the area above/below the scrollbar the position jumps - but the jump is now not always consistent.
In the current version on the last cell - the UI always jumps to the top. In the other cases, the assumed default cell height is used.
With this PR, always the default cell height is used, to determine how much is scrolled.
This makes the behavior more consistent.
Especially from the unit-test, it's clear that with this PR the behavior is much more consistent.
This is also related to the following PR: #1194
Progress
Issues
Reviewers
Contributors
<eduard.sdv@web.de>
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/jfx.git pull/1326/head:pull/1326
$ git checkout pull/1326
Update a local copy of the PR:
$ git checkout pull/1326
$ git pull https://git.openjdk.org/jfx.git pull/1326/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 1326
View PR using the GUI difftool:
$ git pr show -t 1326
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jfx/pull/1326.diff
Webrev
Link to Webrev Comment