ScrollBar: support smooth scrolling via keyboard

This commit is contained in:
Karl Tauber
2020-08-12 16:13:09 +02:00
parent 1f2622819a
commit 3573188025
2 changed files with 77 additions and 14 deletions

View File

@@ -477,6 +477,7 @@ public class FlatScrollBarUI
if( useValueIsAdjusting )
scrollbar.setValueIsAdjusting( true );
// remember current scrollbar value so that we can start scroll animation from there
int oldValue = scrollbar.getValue();
// if invoked while animation is running, calculation of new value
@@ -488,31 +489,51 @@ public class FlatScrollBarUI
// do not use animation if started dragging thumb
if( isDragging ) {
// do not clear valueIsAdjusting here
inRunAndSetValueAnimated = false;
return;
}
int newValue = scrollbar.getValue();
if( newValue != oldValue ) {
scrollbar.setValue( oldValue );
setValueAnimated( newValue );
} else if( useValueIsAdjusting )
// start scroll animation if value has changed
setValueAnimated( oldValue, newValue );
} else {
// clear valueIsAdjusting if value has not changed
if( useValueIsAdjusting )
scrollbar.setValueIsAdjusting( false );
}
inRunAndSetValueAnimated = false;
}
private boolean inRunAndSetValueAnimated;
private Animator animator;
private int startValue = Integer.MIN_VALUE;
private int targetValue = Integer.MIN_VALUE;
private int delta;
private boolean useValueIsAdjusting = true;
public void setValueAnimated( int value ) {
public void setValueAnimated( int initialValue, int value ) {
// do some check if animation already running
if( animator != null && animator.isRunning() && targetValue != Integer.MIN_VALUE ) {
// ignore requests if animation still running and scroll direction is the same
// and new value is within currently running animation
// (this may occur when repeat-scrolling via keyboard)
if( value == targetValue ||
(value > startValue && value < targetValue) || // scroll down/right
(value < startValue && value > targetValue) ) // scroll up/left
return;
}
if( useValueIsAdjusting )
scrollbar.setValueIsAdjusting( true );
// set scrollbar value to initial value
scrollbar.setValue( initialValue );
startValue = initialValue;
targetValue = value;
// create animator
if( animator == null ) {
int duration = FlatUIUtils.getUIInt( "ScrollPane.smoothScrolling.duration", 200 );
@@ -521,7 +542,7 @@ public class FlatScrollBarUI
animator = new Animator( duration, fraction -> {
if( scrollbar == null || !scrollbar.isShowing() ) {
animator.cancel();
animator.stop();
return;
}
@@ -530,10 +551,11 @@ public class FlatScrollBarUI
if( useValueIsAdjusting && !scrollbar.getValueIsAdjusting() )
scrollbar.setValueIsAdjusting( true );
scrollbar.setValue( targetValue - delta + Math.round( delta * fraction ) );
scrollbar.setValue( startValue + Math.round( (targetValue - startValue) * fraction ) );
}, () -> {
targetValue = Integer.MIN_VALUE;
if( useValueIsAdjusting )
startValue = targetValue = Integer.MIN_VALUE;
if( useValueIsAdjusting && scrollbar != null )
scrollbar.setValueIsAdjusting( false );
});
@@ -543,9 +565,7 @@ public class FlatScrollBarUI
: new CubicBezierEasing( 0.5f, 0.5f, 0.5f, 1 ) );
}
targetValue = value;
delta = targetValue - scrollbar.getValue();
// restart animator
animator.cancel();
animator.start();
}

View File

@@ -453,6 +453,49 @@ public class FlatScrollPaneUI
return false;
}
@Override
protected void syncScrollPaneWithViewport() {
if( isSmoothScrollingEnabled() ) {
runAndSyncScrollBarValueAnimated( scrollpane.getVerticalScrollBar(), 0, () -> {
runAndSyncScrollBarValueAnimated( scrollpane.getHorizontalScrollBar(), 1, () -> {
super.syncScrollPaneWithViewport();
} );
} );
} else
super.syncScrollPaneWithViewport();
}
private void runAndSyncScrollBarValueAnimated( JScrollBar sb, int i, Runnable r ) {
if( inRunAndSyncValueAnimated[i] || sb == null ) {
r.run();
return;
}
inRunAndSyncValueAnimated[i] = true;
int oldValue = sb.getValue();
int oldVisibleAmount = sb.getVisibleAmount();
int oldMinimum = sb.getMinimum();
int oldMaximum = sb.getMaximum();
r.run();
int newValue = sb.getValue();
if( newValue != oldValue &&
sb.getVisibleAmount() == oldVisibleAmount &&
sb.getMinimum() == oldMinimum &&
sb.getMaximum() == oldMaximum &&
sb.getUI() instanceof FlatScrollBarUI )
{
((FlatScrollBarUI)sb.getUI()).setValueAnimated( oldValue, newValue );
}
inRunAndSyncValueAnimated[i] = false;
}
private final boolean[] inRunAndSyncValueAnimated = new boolean[2];
//---- class Handler ------------------------------------------------------
/**