ScrollPane: smooth scrolling:

- scroll at least one pixel to avoid "hanging"
- limit scroll increment to visible width/height
- no longer use block increment because had width/height of view (IOW was too large and had no effect)

(issue #27)
This commit is contained in:
Karl Tauber
2021-01-08 11:20:55 +01:00
parent 1315d847b9
commit 2fe1b9e726

View File

@@ -14,12 +14,6 @@
* limitations under the License. * limitations under the License.
*/ */
/*
* Smooth scrolling code partly based on code from IntelliJ IDEA Community Edition,
* which is licensed under the Apache 2.0 license. Copyright 2000-2016 JetBrains s.r.o.
* See: https://github.com/JetBrains/intellij-community/blob/31e1b5a8e43219b9571951bab6457cfb3012e3ef/platform/platform-api/src/com/intellij/ui/components/SmoothScrollPane.java#L141-L185
*
*/
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.awt.Component; import java.awt.Component;
@@ -138,8 +132,6 @@ public class FlatScrollPaneUI
return UIManager.getBoolean( "ScrollPane.smoothScrolling" ); return UIManager.getBoolean( "ScrollPane.smoothScrolling" );
} }
private static final double EPSILON = 1e-5d;
private void mouseWheelMovedSmooth( MouseWheelEvent e ) { private void mouseWheelMovedSmooth( MouseWheelEvent e ) {
// return if there is no viewport // return if there is no viewport
JViewport viewport = scrollpane.getViewport(); JViewport viewport = scrollpane.getViewport();
@@ -160,24 +152,22 @@ public class FlatScrollPaneUI
// get precise wheel rotation // get precise wheel rotation
double rotation = e.getPreciseWheelRotation(); double rotation = e.getPreciseWheelRotation();
// get unit and block increment // get unit increment
int unitIncrement; int unitIncrement;
int blockIncrement;
int orientation = scrollbar.getOrientation(); int orientation = scrollbar.getOrientation();
Component view = viewport.getView(); Component view = viewport.getView();
if( view instanceof Scrollable ) { if( view instanceof Scrollable ) {
Scrollable scrollable = (Scrollable) view; Scrollable scrollable = (Scrollable) view;
// Use (0, 0) view position to obtain constant unit increment of first item // Use (0, 0) view position to obtain a constant unit increment of first item.
// (which might otherwise be variable on smaller-than-unit scrolling). // Unit increment may be different for each item.
Rectangle visibleRect = new Rectangle( viewport.getViewSize() ); Rectangle visibleRect = new Rectangle( viewport.getViewSize() );
unitIncrement = scrollable.getScrollableUnitIncrement( visibleRect, orientation, 1 ); unitIncrement = scrollable.getScrollableUnitIncrement( visibleRect, orientation, 1 );
blockIncrement = scrollable.getScrollableBlockIncrement( visibleRect, orientation, 1 );
if( unitIncrement > 0 ) { if( unitIncrement > 0 ) {
// For the case that the first item (e.g. in a list) is larger // For the case that the first item (e.g. in a list) is larger
// than the other items, get the unit increment of the second item // than the other items (e.g. themes list in FlatLaf Demo),
// and use the smaller one. // get the unit increment of the second item and use the smaller one.
if( orientation == SwingConstants.VERTICAL ) { if( orientation == SwingConstants.VERTICAL ) {
visibleRect.y += unitIncrement; visibleRect.y += unitIncrement;
visibleRect.height -= unitIncrement; visibleRect.height -= unitIncrement;
@@ -192,52 +182,58 @@ public class FlatScrollPaneUI
} else { } else {
int direction = rotation < 0 ? -1 : 1; int direction = rotation < 0 ? -1 : 1;
unitIncrement = scrollbar.getUnitIncrement( direction ); unitIncrement = scrollbar.getUnitIncrement( direction );
blockIncrement = scrollbar.getBlockIncrement( direction );
} }
// limit scroll amount (number of units to scroll) for small viewports // get viewport width/height (the visible width/height)
// (e.g. vertical scrolling in file chooser)
int scrollAmount = e.getScrollAmount();
int viewportWH = (orientation == SwingConstants.VERTICAL) int viewportWH = (orientation == SwingConstants.VERTICAL)
? viewport.getHeight() ? viewport.getHeight()
: viewport.getWidth(); : viewport.getWidth();
if( unitIncrement * scrollAmount > viewportWH )
scrollAmount = Math.max( viewportWH / unitIncrement, 1 ); // limit scroll increment to viewport width/height
// - if scroll amount is set to a large value in OS settings
// - for large unit increments in small viewports (e.g. horizontal scrolling in file chooser)
int scrollIncrement = Math.min( unitIncrement * e.getScrollAmount(), viewportWH );
// compute relative delta // compute relative delta
double delta = rotation * scrollAmount * unitIncrement; double delta = rotation * scrollIncrement;
boolean adjustDelta = Math.abs( rotation ) < (1.0 + EPSILON); int idelta = (int) Math.round( delta );
double adjustedDelta = adjustDelta
? Math.max( -blockIncrement, Math.min( delta, blockIncrement ) ) // scroll at least one pixel to avoid "hanging"
: delta; // - for "super-low-speed" scrolling (move fingers very slowly on trackpad)
// - if unit increment is very small (e.g. 1 if scroll view does not implement
// javax.swing.Scrollable interface)
if( idelta == 0 ) {
if( rotation > 0 )
idelta = 1;
else if( rotation < 0 )
idelta = -1;
}
// compute new value // compute new value
int value = scrollbar.getValue(); int value = scrollbar.getValue();
double minDelta = scrollbar.getMinimum() - value; int minValue = scrollbar.getMinimum();
double maxDelta = scrollbar.getMaximum() - scrollbar.getModel().getExtent() - value; int maxValue = scrollbar.getMaximum() - scrollbar.getModel().getExtent();
double boundedDelta = Math.max( minDelta, Math.min( adjustedDelta, maxDelta ) ); int newValue = Math.max( minValue, Math.min( value + idelta, maxValue ) );
int newValue = value + (int) Math.round( boundedDelta );
// set new value // set new value
if( newValue != value ) if( newValue != value )
scrollbar.setValue( newValue ); scrollbar.setValue( newValue );
/*debug /*debug
System.out.println( String.format( "%4d %9f / %4d %4d / %12f %5s %12f / %4d %4d %4d / %12f %12f %12f / %4d", System.out.println( String.format( "%s %4d %9f / %3d * %d = %3d [%3d] / %8.2f %5d / %4d --> %4d [%d, %d]",
(orientation == SwingConstants.VERTICAL) ? "V" : "H",
e.getWheelRotation(), e.getWheelRotation(),
e.getPreciseWheelRotation(), e.getPreciseWheelRotation(),
unitIncrement, unitIncrement,
blockIncrement, e.getScrollAmount(),
scrollIncrement,
viewportWH,
delta, delta,
adjustDelta, idelta,
adjustedDelta,
value, value,
scrollbar.getMinimum(), newValue,
scrollbar.getMaximum(), minValue,
minDelta, maxValue ) );
maxDelta,
boundedDelta,
newValue ) );
*/ */
} }