提交 8f47c3fd 编写于 作者: A alexsch

8166591: [macos 10.12] Trackpad scrolling of text on OS X 10.12 Sierra is very...

8166591: [macos 10.12] Trackpad scrolling of text on OS X 10.12 Sierra is very fast (Trackpad, Retina only)
Reviewed-by: malenkov, serb
上级 feb5c899
...@@ -91,7 +91,7 @@ public class CEmbeddedFrame extends EmbeddedFrame { ...@@ -91,7 +91,7 @@ public class CEmbeddedFrame extends EmbeddedFrame {
int x = (int)pluginX; int x = (int)pluginX;
int y = (int)pluginY; int y = (int)pluginY;
responder.handleScrollEvent(x, y, modifierFlags, deltaX, deltaY); responder.handleScrollEvent(x, y, modifierFlags, deltaX, deltaY, NSEvent.SCROLL_PHASE_UNSUPPORTED);
} }
public void handleKeyEvent(int eventType, int modifierFlags, String characters, public void handleKeyEvent(int eventType, int modifierFlags, String characters,
......
...@@ -44,6 +44,8 @@ final class CPlatformResponder { ...@@ -44,6 +44,8 @@ final class CPlatformResponder {
private final PlatformEventNotifier eventNotifier; private final PlatformEventNotifier eventNotifier;
private final boolean isNpapiCallback; private final boolean isNpapiCallback;
private int lastKeyPressCode = KeyEvent.VK_UNDEFINED; private int lastKeyPressCode = KeyEvent.VK_UNDEFINED;
private final DeltaAccumulator deltaAccumulatorX = new DeltaAccumulator();
private final DeltaAccumulator deltaAccumulatorY = new DeltaAccumulator();
CPlatformResponder(final PlatformEventNotifier eventNotifier, CPlatformResponder(final PlatformEventNotifier eventNotifier,
final boolean isNpapiCallback) { final boolean isNpapiCallback) {
...@@ -90,37 +92,38 @@ final class CPlatformResponder { ...@@ -90,37 +92,38 @@ final class CPlatformResponder {
* Handles scroll events. * Handles scroll events.
*/ */
void handleScrollEvent(final int x, final int y, final int modifierFlags, void handleScrollEvent(final int x, final int y, final int modifierFlags,
final double deltaX, final double deltaY) { final double deltaX, final double deltaY,
final int scrollPhase) {
final int buttonNumber = CocoaConstants.kCGMouseButtonCenter; final int buttonNumber = CocoaConstants.kCGMouseButtonCenter;
int jmodifiers = NSEvent.nsToJavaMouseModifiers(buttonNumber, int jmodifiers = NSEvent.nsToJavaMouseModifiers(buttonNumber,
modifierFlags); modifierFlags);
final boolean isShift = (jmodifiers & InputEvent.SHIFT_DOWN_MASK) != 0; final boolean isShift = (jmodifiers & InputEvent.SHIFT_DOWN_MASK) != 0;
int roundDeltaX = deltaAccumulatorX.getRoundedDelta(deltaX, scrollPhase);
int roundDeltaY = deltaAccumulatorY.getRoundedDelta(deltaY, scrollPhase);
// Vertical scroll. // Vertical scroll.
if (!isShift && deltaY != 0.0) { if (!isShift && (deltaY != 0.0 || roundDeltaY != 0)) {
dispatchScrollEvent(x, y, jmodifiers, deltaY); dispatchScrollEvent(x, y, jmodifiers, roundDeltaY, deltaY);
} }
// Horizontal scroll or shirt+vertical scroll. // Horizontal scroll or shirt+vertical scroll.
final double delta = isShift && deltaY != 0.0 ? deltaY : deltaX; final double delta = isShift && deltaY != 0.0 ? deltaY : deltaX;
if (delta != 0.0) { final int roundDelta = isShift && roundDeltaY != 0 ? roundDeltaY : roundDeltaX;
if (delta != 0.0 || roundDelta != 0) {
jmodifiers |= InputEvent.SHIFT_DOWN_MASK; jmodifiers |= InputEvent.SHIFT_DOWN_MASK;
dispatchScrollEvent(x, y, jmodifiers, delta); dispatchScrollEvent(x, y, jmodifiers, roundDelta, delta);
} }
} }
private void dispatchScrollEvent(final int x, final int y, private void dispatchScrollEvent(final int x, final int y,
final int modifiers, final double delta) { final int modifiers,
final int roundDelta, final double delta) {
final long when = System.currentTimeMillis(); final long when = System.currentTimeMillis();
final int scrollType = MouseWheelEvent.WHEEL_UNIT_SCROLL; final int scrollType = MouseWheelEvent.WHEEL_UNIT_SCROLL;
final int scrollAmount = 1; final int scrollAmount = 1;
int wheelRotation = (int) delta;
int signum = (int) Math.signum(delta);
if (signum * delta < 1) {
wheelRotation = signum;
}
// invert the wheelRotation for the peer // invert the wheelRotation for the peer
eventNotifier.notifyMouseWheelEvent(when, x, y, modifiers, scrollType, eventNotifier.notifyMouseWheelEvent(when, x, y, modifiers, scrollType,
scrollAmount, -wheelRotation, -delta, null); scrollAmount, -roundDelta, -delta, null);
} }
/** /**
...@@ -256,4 +259,46 @@ final class CPlatformResponder { ...@@ -256,4 +259,46 @@ final class CPlatformResponder {
void handleWindowFocusEvent(boolean gained, LWWindowPeer opposite) { void handleWindowFocusEvent(boolean gained, LWWindowPeer opposite) {
eventNotifier.notifyActivation(gained, opposite); eventNotifier.notifyActivation(gained, opposite);
} }
static class DeltaAccumulator {
static final double MIN_THRESHOLD = 0.1;
static final double MAX_THRESHOLD = 0.5;
double accumulatedDelta;
int getRoundedDelta(double delta, int scrollPhase) {
int roundDelta = (int) Math.round(delta);
if (scrollPhase == NSEvent.SCROLL_PHASE_UNSUPPORTED) { // mouse wheel
if (roundDelta == 0 && delta != 0) {
roundDelta = delta > 0 ? 1 : -1;
}
} else { // trackpad
boolean begin = scrollPhase == NSEvent.SCROLL_PHASE_BEGAN;
boolean end = scrollPhase == NSEvent.SCROLL_MASK_PHASE_ENDED
|| scrollPhase == NSEvent.SCROLL_MASK_PHASE_CANCELLED;
if (begin) {
accumulatedDelta = 0;
}
accumulatedDelta += delta;
double absAccumulatedDelta = Math.abs(accumulatedDelta);
if (absAccumulatedDelta > MAX_THRESHOLD) {
roundDelta = (int) Math.round(accumulatedDelta);
accumulatedDelta -= roundDelta;
}
if (end) {
if (roundDelta == 0 && absAccumulatedDelta > MIN_THRESHOLD) {
roundDelta = accumulatedDelta > 0 ? 1 : -1;
}
}
}
return roundDelta;
}
}
} }
...@@ -192,7 +192,8 @@ public class CPlatformView extends CFRetainedResource { ...@@ -192,7 +192,8 @@ public class CPlatformView extends CFRetainedResource {
if (event.getType() == CocoaConstants.NSScrollWheel) { if (event.getType() == CocoaConstants.NSScrollWheel) {
responder.handleScrollEvent(x, y, event.getModifierFlags(), responder.handleScrollEvent(x, y, event.getModifierFlags(),
event.getScrollDeltaX(), event.getScrollDeltaY()); event.getScrollDeltaX(), event.getScrollDeltaY(),
event.getScrollPhase());
} else { } else {
responder.handleMouseEvent(event.getType(), event.getModifierFlags(), event.getButtonNumber(), responder.handleMouseEvent(event.getType(), event.getModifierFlags(), event.getButtonNumber(),
event.getClickCount(), x, y, event.getAbsX(), event.getAbsY()); event.getClickCount(), x, y, event.getAbsX(), event.getAbsY());
......
...@@ -32,6 +32,13 @@ import java.awt.event.*; ...@@ -32,6 +32,13 @@ import java.awt.event.*;
* JDK functionality. * JDK functionality.
*/ */
final class NSEvent { final class NSEvent {
static final int SCROLL_PHASE_UNSUPPORTED = 1;
static final int SCROLL_PHASE_BEGAN = 2;
static final int SCROLL_PHASE_CONTINUED = 3;
static final int SCROLL_MASK_PHASE_CANCELLED = 4;
static final int SCROLL_MASK_PHASE_ENDED = 5;
private int type; private int type;
private int modifierFlags; private int modifierFlags;
...@@ -42,6 +49,7 @@ final class NSEvent { ...@@ -42,6 +49,7 @@ final class NSEvent {
private int y; private int y;
private double scrollDeltaY; private double scrollDeltaY;
private double scrollDeltaX; private double scrollDeltaX;
private int scrollPhase;
private int absX; private int absX;
private int absY; private int absY;
...@@ -62,7 +70,7 @@ final class NSEvent { ...@@ -62,7 +70,7 @@ final class NSEvent {
// Called from native // Called from native
NSEvent(int type, int modifierFlags, int clickCount, int buttonNumber, NSEvent(int type, int modifierFlags, int clickCount, int buttonNumber,
int x, int y, int absX, int absY, int x, int y, int absX, int absY,
double scrollDeltaY, double scrollDeltaX) { double scrollDeltaY, double scrollDeltaX, int scrollPhase) {
this.type = type; this.type = type;
this.modifierFlags = modifierFlags; this.modifierFlags = modifierFlags;
this.clickCount = clickCount; this.clickCount = clickCount;
...@@ -73,6 +81,7 @@ final class NSEvent { ...@@ -73,6 +81,7 @@ final class NSEvent {
this.absY = absY; this.absY = absY;
this.scrollDeltaY = scrollDeltaY; this.scrollDeltaY = scrollDeltaY;
this.scrollDeltaX = scrollDeltaX; this.scrollDeltaX = scrollDeltaX;
this.scrollPhase = scrollPhase;
} }
int getType() { int getType() {
...@@ -107,6 +116,10 @@ final class NSEvent { ...@@ -107,6 +116,10 @@ final class NSEvent {
return scrollDeltaX; return scrollDeltaX;
} }
int getScrollPhase() {
return scrollPhase;
}
int getAbsX() { int getAbsX() {
return absX; return absX;
} }
......
...@@ -386,7 +386,7 @@ AWT_ASSERT_APPKIT_THREAD; ...@@ -386,7 +386,7 @@ AWT_ASSERT_APPKIT_THREAD;
} }
static JNF_CLASS_CACHE(jc_NSEvent, "sun/lwawt/macosx/NSEvent"); static JNF_CLASS_CACHE(jc_NSEvent, "sun/lwawt/macosx/NSEvent");
static JNF_CTOR_CACHE(jctor_NSEvent, jc_NSEvent, "(IIIIIIIIDD)V"); static JNF_CTOR_CACHE(jctor_NSEvent, jc_NSEvent, "(IIIIIIIIDDI)V");
jobject jEvent = JNFNewObject(env, jctor_NSEvent, jobject jEvent = JNFNewObject(env, jctor_NSEvent,
[event type], [event type],
[event modifierFlags], [event modifierFlags],
...@@ -395,7 +395,8 @@ AWT_ASSERT_APPKIT_THREAD; ...@@ -395,7 +395,8 @@ AWT_ASSERT_APPKIT_THREAD;
(jint)localPoint.x, (jint)localPoint.y, (jint)localPoint.x, (jint)localPoint.y,
(jint)absP.x, (jint)absP.y, (jint)absP.x, (jint)absP.y,
[event deltaY], [event deltaY],
[event deltaX]); [event deltaX],
[AWTToolkit scrollStateWithEvent: event]);
if (jEvent == nil) { if (jEvent == nil) {
// Unable to create event by some reason. // Unable to create event by some reason.
return; return;
......
...@@ -136,7 +136,7 @@ static NSSize ScaledImageSizeForStatusBar(NSSize imageSize) { ...@@ -136,7 +136,7 @@ static NSSize ScaledImageSizeForStatusBar(NSSize imageSize) {
clickCount = [event clickCount]; clickCount = [event clickCount];
static JNF_CLASS_CACHE(jc_NSEvent, "sun/lwawt/macosx/NSEvent"); static JNF_CLASS_CACHE(jc_NSEvent, "sun/lwawt/macosx/NSEvent");
static JNF_CTOR_CACHE(jctor_NSEvent, jc_NSEvent, "(IIIIIIIIDD)V"); static JNF_CTOR_CACHE(jctor_NSEvent, jc_NSEvent, "(IIIIIIIIDDI)V");
jobject jEvent = JNFNewObject(env, jctor_NSEvent, jobject jEvent = JNFNewObject(env, jctor_NSEvent,
[event type], [event type],
[event modifierFlags], [event modifierFlags],
...@@ -145,7 +145,8 @@ static NSSize ScaledImageSizeForStatusBar(NSSize imageSize) { ...@@ -145,7 +145,8 @@ static NSSize ScaledImageSizeForStatusBar(NSSize imageSize) {
(jint)localPoint.x, (jint)localPoint.y, (jint)localPoint.x, (jint)localPoint.y,
(jint)absP.x, (jint)absP.y, (jint)absP.x, (jint)absP.y,
[event deltaY], [event deltaY],
[event deltaX]); [event deltaX],
[AWTToolkit scrollStateWithEvent: event]);
if (jEvent == nil) { if (jEvent == nil) {
// Unable to create event by some reason. // Unable to create event by some reason.
return; return;
......
...@@ -40,6 +40,7 @@ extern jint* gButtonDownMasks; ...@@ -40,6 +40,7 @@ extern jint* gButtonDownMasks;
@interface AWTToolkit : NSObject { } @interface AWTToolkit : NSObject { }
+ (long) getEventCount; + (long) getEventCount;
+ (void) eventCountPlusPlus; + (void) eventCountPlusPlus;
+ (jint) scrollStateWithEvent: (NSEvent*) event;
@end @end
/* /*
......
...@@ -39,6 +39,13 @@ ...@@ -39,6 +39,13 @@
#import "sizecalc.h" #import "sizecalc.h"
// SCROLL PHASE STATE
#define SCROLL_PHASE_UNSUPPORTED 1
#define SCROLL_PHASE_BEGAN 2
#define SCROLL_PHASE_CONTINUED 3
#define SCROLL_PHASE_CANCELLED 4
#define SCROLL_PHASE_ENDED 5
int gNumberOfButtons; int gNumberOfButtons;
jint* gButtonDownMasks; jint* gButtonDownMasks;
...@@ -54,6 +61,23 @@ static long eventCount; ...@@ -54,6 +61,23 @@ static long eventCount;
eventCount++; eventCount++;
} }
+ (jint) scrollStateWithEvent: (NSEvent*) event {
if ([event type] != NSScrollWheel) {
return 0;
}
NSEventPhase phase = [event phase];
NSEventPhase momentumPhase = [event momentumPhase];
if (!phase && !momentumPhase) return SCROLL_PHASE_UNSUPPORTED;
switch (phase) {
case NSEventPhaseBegan: return SCROLL_PHASE_BEGAN;
case NSEventPhaseCancelled: return SCROLL_PHASE_CANCELLED;
case NSEventPhaseEnded: return SCROLL_PHASE_ENDED;
}
return SCROLL_PHASE_CONTINUED;
}
@end @end
......
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
/*
* @test
* @bug 8166591
* @summary [macos 10.12] Trackpad scrolling of text on OS X 10.12 Sierra
* is very fast (Trackpad, Retina only)
* @run main/manual/othervm TooMuchWheelRotationEventsTest
*/
public class TooMuchWheelRotationEventsTest {
private static volatile boolean testResult = false;
private static volatile CountDownLatch countDownLatch;
private static final String INSTRUCTIONS = "INSTRUCTIONS:\n"
+ "Try to check the issue on Mac OS X 10.12 Sierra with trackpad"
+ " on Retina display.\n"
+ "\n"
+ "If the trackpad is not supported, press PASS\n"
+ "\n"
+ "Use the trackpad to slightly scroll the JTextArea horizontally and vertically.\n"
+ "If the text area is scrolled too fast press FAIL, else press PASS.";
public static void main(String args[]) throws Exception {
countDownLatch = new CountDownLatch(1);
SwingUtilities.invokeLater(TooMuchWheelRotationEventsTest::createUI);
countDownLatch.await(15, TimeUnit.MINUTES);
if (!testResult) {
throw new RuntimeException("Test fails!");
}
}
private static void createUI() {
final JFrame mainFrame = new JFrame("Trackpad scrolling test");
GridBagLayout layout = new GridBagLayout();
JPanel mainControlPanel = new JPanel(layout);
JPanel resultButtonPanel = new JPanel(layout);
GridBagConstraints gbc = new GridBagConstraints();
JPanel testPanel = createTestPanel();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
mainControlPanel.add(testPanel, gbc);
JTextArea instructionTextArea = new JTextArea();
instructionTextArea.setText(INSTRUCTIONS);
instructionTextArea.setEditable(false);
instructionTextArea.setBackground(Color.white);
gbc.gridx = 0;
gbc.gridy = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
mainControlPanel.add(instructionTextArea, gbc);
JButton passButton = new JButton("Pass");
passButton.setActionCommand("Pass");
passButton.addActionListener((ActionEvent e) -> {
testResult = true;
mainFrame.dispose();
countDownLatch.countDown();
});
JButton failButton = new JButton("Fail");
failButton.setActionCommand("Fail");
failButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
mainFrame.dispose();
countDownLatch.countDown();
}
});
gbc.gridx = 0;
gbc.gridy = 0;
resultButtonPanel.add(passButton, gbc);
gbc.gridx = 1;
gbc.gridy = 0;
resultButtonPanel.add(failButton, gbc);
gbc.gridx = 0;
gbc.gridy = 2;
mainControlPanel.add(resultButtonPanel, gbc);
mainFrame.add(mainControlPanel);
mainFrame.pack();
mainFrame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
mainFrame.dispose();
countDownLatch.countDown();
}
});
mainFrame.setVisible(true);
}
private static JPanel createTestPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
JTextArea textArea = new JTextArea(20, 20);
textArea.setText(getLongString());
JScrollPane scrollPane = new JScrollPane(textArea);
panel.add(scrollPane);
return panel;
}
private static String getLongString() {
String lowCaseString = getLongString('a', 'z');
String upperCaseString = getLongString('A', 'Z');
String digitsString = getLongString('0', '9');
int repeat = 30;
StringBuilder lowCaseBuilder = new StringBuilder();
StringBuilder upperCaseBuilder = new StringBuilder();
StringBuilder digitsBuilder = new StringBuilder();
for (int i = 0; i < repeat; i++) {
lowCaseBuilder.append(lowCaseString).append(' ');
upperCaseBuilder.append(upperCaseString).append(' ');
digitsBuilder.append(digitsString).append(' ');
}
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 200; i++) {
builder.append(upperCaseBuilder).append('\n')
.append(lowCaseBuilder).append('\n')
.append(digitsBuilder).append("\n\n\n");
}
return builder.toString();
}
private static String getLongString(char c1, char c2) {
char[] chars = new char[c2 - c1 + 1];
for (char i = c1; i <= c2; i++) {
chars[i - c1] = i;
}
return new String(chars);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册