Control and ScrollPane in places do floating point math without re-snapping the final result. This can cause such values to be off by a few ulps. ScrollPaneSkin determines scroll bar visibility by comparing the size of the ScrollPane (minus its insets) to the size of the content to be scrolled (pseudo code using names from ScrollPaneSkin):
boolean showVerticalScrollBar = nodeHeight > contentHeight;
(Note the variable names are confusing here, nodeHeight = the size of the content node, contentHeight = the size of the ScrollPane itself)
These often have slight rounding errors like:
638.6666666666666 > 638.6666666666664
In the above case, the scroll bar would be shown, as it determined that the content to show wouldn't fit without a scroll bar -- obviously that's due to a rounding error, the comparison should have been with two equal numbers:
638.6666666666666 > 638.6666666666666
These subtle rounding errors are coming from two locations:
1) in the compute methods of ScrollPaneSkin, several sizes are added together without re-snapping (insets + scroll bar size + content size for example).
2) In the layoutChildren method of Control, the insets are subtracted from the control size without re-snapping.
Each of these calculations can cause subtle errors that would trigger a scroll bar to appear when not needed.
This can be especially visible when the scroll pane contains nodes that animate (like collapsible title panes in an accordion, or stand alone), as during the animation the scroll bar appears/disappears multiple times. The final state is a wash. Sometimes the final calculation happens to round correctly and the scroll bar is gone, sometimes it just shows a scroll bar without good reason.
Suggested Fix
============
In both Control and ScrollPaneSkin, use snapSpace (which rounds to the nearest pixel) to "fix" subtle rounding errors. Do not use snapSize as it will amplify slight errors by ceiling them if they are 1 ulp over the desired value.
boolean showVerticalScrollBar = nodeHeight > contentHeight;
(Note the variable names are confusing here, nodeHeight = the size of the content node, contentHeight = the size of the ScrollPane itself)
These often have slight rounding errors like:
638.6666666666666 > 638.6666666666664
In the above case, the scroll bar would be shown, as it determined that the content to show wouldn't fit without a scroll bar -- obviously that's due to a rounding error, the comparison should have been with two equal numbers:
638.6666666666666 > 638.6666666666666
These subtle rounding errors are coming from two locations:
1) in the compute methods of ScrollPaneSkin, several sizes are added together without re-snapping (insets + scroll bar size + content size for example).
2) In the layoutChildren method of Control, the insets are subtracted from the control size without re-snapping.
Each of these calculations can cause subtle errors that would trigger a scroll bar to appear when not needed.
This can be especially visible when the scroll pane contains nodes that animate (like collapsible title panes in an accordion, or stand alone), as during the animation the scroll bar appears/disappears multiple times. The final state is a wash. Sometimes the final calculation happens to round correctly and the scroll bar is gone, sometimes it just shows a scroll bar without good reason.
Suggested Fix
============
In both Control and ScrollPaneSkin, use snapSpace (which rounds to the nearest pixel) to "fix" subtle rounding errors. Do not use snapSize as it will amplify slight errors by ceiling them if they are 1 ulp over the desired value.
- links to
-
Review(master)
openjdk/jfx/1948