FULL PRODUCT VERSION :
java version "1.8.0_66"
Java(TM) SE Runtime Environment (build 1.8.0_66-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
MacOS X 10.9.5
EXTRA RELEVANT SYSTEM CONFIGURATION :
Tested on a 13-inch Mid 2012 MacBook Air, non-retina, with or without external monitor.
A DESCRIPTION OF THE PROBLEM :
In recent Java versions (not sure since exactly which version), trackpad scrolling in any JScrollPane has become extremely jittery, as demonstrated in the following video: https://vimeo.com/151843642 .The source code for a simple JScrollPane app exhibiting the bug is attached.
After discussing on the macosx-port-dev mailing list, the underlying reason seems clear: At some point, a feature was added that lets the user scroll up by scrolling to the right in any JScrollPane that does not permit horizontal scrolling. This does not work well with the MacOS trackpad, since the user will often move their fingers slightly diagonally while executing a two-fingers-to-scroll gesture, effectively scrolling both up and down at the same time. This is what creates the jitter effect in question.
The "scroll right to scroll up" feature additionally interacts badly with the momentum-like behavior of the Mac trackpad; see for instance the video above at 1:13, where the viewport is jittering up and down _after_ my fingers have left the trackpad.
The "scroll right to scroll up" feature is not how native apps work on MacOS, so my guess would be it could simply be disabled on MacOS.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1) Use a MacBook or other Mac computer with a trackpad.
2) Go to "System Preferences->Trackpad->Scroll & Zoom" and ensure "Scroll direction: natural" is checked. (The bug will still appear if this is off, but the gesture described in step 4 will be reversed in the vertical direction.)
3) Run the attached sample app, which opens a JFrame with a JScrollPane in it.
4) Using a two-finger gesture on the trackpad, move your fingers upwards with a slight slant to the right (i.e. towards 1 o'clock). This will scroll down, but with a significant jitter.
5) You can also try flicking the two fingers in the same direction as in step 4 to scroll down using the momentum-like feature of the trackpad. The JScrollPane will scroll down but may jitter even after your fingers have left the trackpad (may require multiple attempts).
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The scroll pane should scroll down without jitter, despite the user not moving their fingers exactly vertically on the trackpad.
Note also that when scrolling with the trackpad in native MacOS apps, the OS makes a decision as to whether the scroll is vertical or horizontal, and then scrolls in that direction only. The cut-off is 45 degrees, giving the user plenty of wiggle room.
ACTUAL -
The scroll pane scrolled down, but jittered up again several times on its journey. The jitter happens even when the scroll pane is already scrolled all the way down.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
public final class ScrollpaneTrackpadJitterBugExhibit extends JFrame {
/* Note that the "implements Scrollable" business is not necessary to trigger
the bug, I just did it to set the getScrollableBlockIncrement to something
higher than 1 so that you'd see the jitter clearly on a video. */
private static final class ViewportPanel extends JPanel implements Scrollable {
public ViewportPanel() {
setLayout(new java.awt.GridLayout(2, 0, 0, 800));
add(new JLabel("Top of Viewport"));
JLabel bottomLabel = new JLabel(
"<html>Line 1<br>Line 2<br>Line 3<br>Line 4<br>Line 5<br>Line 6<br>Bottom of Viewport</html>");
bottomLabel.setFont(new Font("Dialog", Font.PLAIN, 18));
add(bottomLabel);
}
@Override
public Dimension getPreferredScrollableViewportSize() {
return getPreferredSize();
}
@Override
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
return 30;
}
@Override
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
return 30;
}
@Override
public boolean getScrollableTracksViewportWidth() {
return true;
}
@Override
public boolean getScrollableTracksViewportHeight() {
return false;
}
}
public ScrollpaneTrackpadJitterBugExhibit() {
JScrollPane scrollPane = new JScrollPane(new ViewportPanel());
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
getContentPane().add(scrollPane, java.awt.BorderLayout.CENTER);
pack();
setSize(300, 400);
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new ScrollpaneTrackpadJitterBugExhibit().setVisible(true);
}
});
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
None found. I tried adding a MouseWheelListener to the JScrollPane and consuming horizontal mouse wheel events, but this did not seem to have an effect.