ScrollPane: use smooth scrolling when rotating the mouse wheel (issue #50)

This commit is contained in:
Karl Tauber
2020-07-25 13:11:52 +02:00
parent 7f226a2742
commit b67b701d1e
2 changed files with 47 additions and 53 deletions

View File

@@ -28,8 +28,6 @@ import java.beans.PropertyChangeListener;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import javax.swing.BoundedRangeModel;
import javax.swing.DefaultBoundedRangeModel;
import javax.swing.InputMap; import javax.swing.InputMap;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JComponent; import javax.swing.JComponent;
@@ -37,7 +35,6 @@ import javax.swing.JScrollBar;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicScrollBarUI; import javax.swing.plaf.basic.BasicScrollBarUI;
import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatClientProperties;
@@ -450,7 +447,11 @@ public class FlatScrollBarUI
} ); } );
} }
protected void runAndSetValueAnimated( Runnable r ) { /**
* Runs the given runnable, which should modify the scroll bar value,
* and then animate scroll bar value from old value to new value.
*/
public void runAndSetValueAnimated( Runnable r ) {
if( !isSmoothScrollingEnabled() ) { if( !isSmoothScrollingEnabled() ) {
r.run(); r.run();
return; return;
@@ -459,30 +460,26 @@ public class FlatScrollBarUI
if( animator != null ) if( animator != null )
animator.cancel(); animator.cancel();
int[] newValue = new int[1]; // if invoked while animation is running, calculation of new value
// should start at the previous target value
if( targetValue != Integer.MIN_VALUE )
scrollbar.setValue( targetValue );
runWithoutValueChangeEvents( scrollbar, () -> { int oldValue = scrollbar.getValue();
// if invoked while animation is running, calculation of new value
// should start at the previous target value
if( targetValue != Integer.MIN_VALUE )
scrollbar.setValue( targetValue );
int oldValue = scrollbar.getValue(); r.run();
r.run(); int newValue = scrollbar.getValue();
scrollbar.setValue( oldValue );
newValue[0] = scrollbar.getValue(); setValueAnimated( newValue );
scrollbar.setValue( oldValue );
} );
setValueAnimated( newValue[0] );
} }
private Animator animator; private Animator animator;
private int targetValue = Integer.MIN_VALUE; private int targetValue = Integer.MIN_VALUE;
private int delta; private int delta;
protected void setValueAnimated( int value ) { public void setValueAnimated( int value ) {
// create animator // create animator
if( animator == null ) { if( animator == null ) {
int duration = FlatUIUtils.getUIInt( "ScrollPane.smoothScrolling.duration", 200 ); int duration = FlatUIUtils.getUIInt( "ScrollPane.smoothScrolling.duration", 200 );
@@ -527,29 +524,6 @@ public class FlatScrollBarUI
return UIManager.getBoolean( "ScrollPane.smoothScrolling" ); return UIManager.getBoolean( "ScrollPane.smoothScrolling" );
} }
protected static void runWithoutValueChangeEvents( JScrollBar scrollBar, Runnable r ) {
BoundedRangeModel model = scrollBar.getModel();
if( !(model instanceof DefaultBoundedRangeModel) ) {
r.run();
return;
}
DefaultBoundedRangeModel m = (DefaultBoundedRangeModel) model;
// remove all listeners
ChangeListener[] changeListeners = m.getChangeListeners();
for( ChangeListener l : changeListeners )
m.removeChangeListener( l );
try {
r.run();
} finally {
// add all listeners
for( ChangeListener l : changeListeners )
m.addChangeListener( l );
}
}
//---- class ScrollBarHoverListener --------------------------------------- //---- class ScrollBarHoverListener ---------------------------------------
// using static field to disabling hover for other scroll bars // using static field to disabling hover for other scroll bars

View File

@@ -135,12 +135,25 @@ public class FlatScrollPaneUI
MouseWheelListener superListener = super.createMouseWheelListener(); MouseWheelListener superListener = super.createMouseWheelListener();
return e -> { return e -> {
if( isSmoothScrollingEnabled() && if( isSmoothScrollingEnabled() &&
scrollpane.isWheelScrollingEnabled() && scrollpane.isWheelScrollingEnabled() )
e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL &&
e.getPreciseWheelRotation() != 0 &&
e.getPreciseWheelRotation() != e.getWheelRotation() )
{ {
mouseWheelMovedSmooth( e ); if( e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL &&
e.getPreciseWheelRotation() != 0 &&
e.getPreciseWheelRotation() != e.getWheelRotation() )
{
// precise scrolling
mouseWheelMovedPrecise( e );
} else {
// smooth scrolling
JScrollBar scrollBar = findScrollBarToScroll( e );
if( scrollBar != null && scrollBar.getUI() instanceof FlatScrollBarUI ) {
FlatScrollBarUI ui = (FlatScrollBarUI) scrollBar.getUI();
ui.runAndSetValueAnimated( () -> {
superListener.mouseWheelMoved( e );
} );
} else
superListener.mouseWheelMoved( e );
}
} else } else
superListener.mouseWheelMoved( e ); superListener.mouseWheelMoved( e );
}; };
@@ -157,19 +170,16 @@ public class FlatScrollPaneUI
return UIManager.getBoolean( "ScrollPane.smoothScrolling" ); return UIManager.getBoolean( "ScrollPane.smoothScrolling" );
} }
private void mouseWheelMovedSmooth( MouseWheelEvent e ) { private void mouseWheelMovedPrecise( MouseWheelEvent e ) {
// return if there is no viewport // return if there is no viewport
JViewport viewport = scrollpane.getViewport(); JViewport viewport = scrollpane.getViewport();
if( viewport == null ) if( viewport == null )
return; return;
// find scrollbar to scroll // find scrollbar to scroll
JScrollBar scrollbar = scrollpane.getVerticalScrollBar(); JScrollBar scrollbar = findScrollBarToScroll( e );
if( scrollbar == null || !scrollbar.isVisible() || e.isShiftDown() ) { if( scrollbar == null )
scrollbar = scrollpane.getHorizontalScrollBar(); return;
if( scrollbar == null || !scrollbar.isVisible() )
return;
}
// consume event // consume event
e.consume(); e.consume();
@@ -262,6 +272,16 @@ public class FlatScrollPaneUI
*/ */
} }
private JScrollBar findScrollBarToScroll( MouseWheelEvent e ) {
JScrollBar scrollBar = scrollpane.getVerticalScrollBar();
if( scrollBar == null || !scrollBar.isVisible() || e.isShiftDown() ) {
scrollBar = scrollpane.getHorizontalScrollBar();
if( scrollBar == null || !scrollBar.isVisible() )
return null;
}
return scrollBar;
}
@Override @Override
protected PropertyChangeListener createPropertyChangeListener() { protected PropertyChangeListener createPropertyChangeListener() {
PropertyChangeListener superListener = super.createPropertyChangeListener(); PropertyChangeListener superListener = super.createPropertyChangeListener();