Index: ui/events/cocoa/events_mac_unittest.mm |
diff --git a/ui/events/cocoa/events_mac_unittest.mm b/ui/events/cocoa/events_mac_unittest.mm |
index e30202f301fe8a8ef376f0b86e1db3f66b8aa57a..0c46a98b1b840304458c7937d275a68ec12287a3 100644 |
--- a/ui/events/cocoa/events_mac_unittest.mm |
+++ b/ui/events/cocoa/events_mac_unittest.mm |
@@ -11,6 +11,7 @@ |
#include "base/mac/sdk_forward_declarations.h" |
#include "base/macros.h" |
#include "testing/gtest/include/gtest/gtest.h" |
+#import "ui/events/cocoa/cocoa_event_utils.h" |
#include "ui/events/event_constants.h" |
#include "ui/events/event_utils.h" |
#import "ui/events/test/cocoa_test_event_utils.h" |
@@ -61,14 +62,87 @@ class EventsMacTest : public CocoaTest { |
NSEvent* TestScrollEvent(const gfx::Point& window_location, |
int32_t delta_x, |
int32_t delta_y) { |
+ bool precise = false; |
return cocoa_test_event_utils::TestScrollEvent( |
- Flip(window_location).ToCGPoint(), test_window(), delta_x, delta_y); |
+ Flip(window_location).ToCGPoint(), test_window(), delta_x, delta_y, |
+ precise, NSEventPhaseNone, NSEventPhaseNone); |
} |
+ // Creates the sequence of events generated by a trackpad scroll. |
+ // |initial_rest| indicates whether there is a pause before scrolling starts. |
+ // |delta_y| is the portion to scroll without momentum (fingers on the |
+ // trackpad). |momentum_delta_y| is the momentum portion. A zero delta skips |
+ // that phase (if both are zero, the |initial_rest| is cancelled). |
+ NSArray* TrackpadScrollSequence(bool initial_rest, |
+ int32_t delta_y, |
+ int32_t momentum_delta_y); |
+ |
+ protected: |
+ const gfx::Point default_location_ = gfx::Point(10, 20); |
+ |
private: |
DISALLOW_COPY_AND_ASSIGN(EventsMacTest); |
}; |
+// Trackpad scroll sequences below determined empirically on OSX 10.11 (linking |
+// to 10.10 SDK), and dumping out with NSLog in -[NSView scrollWheel:]. First |
+// created using a Magic Trackpad 2 on a MacPro. See the Trackpad* test cases |
+// below for example event streams. |
+NSArray* EventsMacTest::TrackpadScrollSequence(bool initial_rest, |
+ int32_t delta_y, |
+ int32_t momentum_delta_y) { |
+ int32_t delta_x = 0; // Just test vertical scrolling for now. |
+ base::scoped_nsobject<NSMutableArray> events([[NSMutableArray alloc] init]); |
+ |
+ // Resting part. |
+ if (initial_rest) { |
+ // MayBegin always has a zero delta. |
+ [events addObject:cocoa_test_event_utils::TestScrollEvent( |
+ Flip(default_location_).ToCGPoint(), test_window(), delta_x, 0, true, |
+ NSEventPhaseMayBegin, NSEventPhaseNone)]; |
+ if (delta_y == 0) { |
+ // Rest and release: event gets cancelled. |
+ DCHECK_EQ(0, momentum_delta_y); // Pretty sure this is impossible. |
+ [events addObject:cocoa_test_event_utils::TestScrollEvent( |
+ Flip(default_location_).ToCGPoint(), test_window(), delta_x, 0, true, |
+ NSEventPhaseCancelled, NSEventPhaseNone)]; |
+ return events.autorelease(); |
+ } |
+ } |
+ |
+ // With or without a rest, a begin is sent. It can have a non-zero |
+ // deviceDeltaY but regular deltaY is always 0. |
+ [events addObject:cocoa_test_event_utils::TestScrollEvent( |
+ Flip(default_location_).ToCGPoint(), test_window(), delta_x, 0, true, |
+ NSEventPhaseBegan, NSEventPhaseNone)]; |
+ |
+ [events addObject:cocoa_test_event_utils::TestScrollEvent( |
+ Flip(default_location_).ToCGPoint(), test_window(), delta_x, delta_y, |
+ true, NSEventPhaseChanged, NSEventPhaseNone)]; |
+ |
+ // With or without momentum, an end is sent for the non-momentum part. |
+ [events addObject:cocoa_test_event_utils::TestScrollEvent( |
+ Flip(default_location_).ToCGPoint(), test_window(), delta_x, 0, true, |
+ NSEventPhaseEnded, NSEventPhaseNone)]; |
+ |
+ if (momentum_delta_y == 0) |
+ return events.autorelease(); |
+ |
+ // Flick part. Basically the same, but with phase and momentumPhase swapped. |
+ [events addObject:cocoa_test_event_utils::TestScrollEvent( |
+ Flip(default_location_).ToCGPoint(), test_window(), delta_x, 0, true, |
+ NSEventPhaseNone, NSEventPhaseBegan)]; |
+ |
+ [events addObject:cocoa_test_event_utils::TestScrollEvent( |
+ Flip(default_location_).ToCGPoint(), test_window(), delta_x, |
+ momentum_delta_y, true, NSEventPhaseNone, NSEventPhaseChanged)]; |
+ |
+ [events addObject:cocoa_test_event_utils::TestScrollEvent( |
+ Flip(default_location_).ToCGPoint(), test_window(), delta_x, 0, true, |
+ NSEventPhaseNone, NSEventPhaseEnded)]; |
+ return events.autorelease(); |
+} |
+ |
} // namespace |
TEST_F(EventsMacTest, EventFlagsFromNative) { |
@@ -155,7 +229,7 @@ TEST_F(EventsMacTest, ButtonEvents) { |
// Scroll up. |
event = TestScrollEvent(location, 0, 1); |
- EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(event)); |
+ EXPECT_EQ(ui::ET_SCROLL, ui::EventTypeFromNative(event)); |
EXPECT_EQ(0, ui::EventFlagsFromNative(event)); |
EXPECT_EQ(location.ToString(), ui::EventLocationFromNative(event).ToString()); |
offset = ui::GetMouseWheelOffset(event); |
@@ -164,7 +238,7 @@ TEST_F(EventsMacTest, ButtonEvents) { |
// Scroll down. |
event = TestScrollEvent(location, 0, -1); |
- EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(event)); |
+ EXPECT_EQ(ui::ET_SCROLL, ui::EventTypeFromNative(event)); |
EXPECT_EQ(0, ui::EventFlagsFromNative(event)); |
EXPECT_EQ(location, ui::EventLocationFromNative(event)); |
offset = ui::GetMouseWheelOffset(event); |
@@ -173,7 +247,7 @@ TEST_F(EventsMacTest, ButtonEvents) { |
// Scroll left. |
event = TestScrollEvent(location, 1, 0); |
- EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(event)); |
+ EXPECT_EQ(ui::ET_SCROLL, ui::EventTypeFromNative(event)); |
EXPECT_EQ(0, ui::EventFlagsFromNative(event)); |
EXPECT_EQ(location, ui::EventLocationFromNative(event)); |
offset = ui::GetMouseWheelOffset(event); |
@@ -182,7 +256,7 @@ TEST_F(EventsMacTest, ButtonEvents) { |
// Scroll right. |
event = TestScrollEvent(location, -1, 0); |
- EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(event)); |
+ EXPECT_EQ(ui::ET_SCROLL, ui::EventTypeFromNative(event)); |
EXPECT_EQ(0, ui::EventFlagsFromNative(event)); |
EXPECT_EQ(location, ui::EventLocationFromNative(event)); |
offset = ui::GetMouseWheelOffset(event); |
@@ -261,4 +335,166 @@ TEST_F(EventsMacTest, EventTypeFromNative) { |
EXPECT_EQ(ui::ET_MOUSE_EXITED, ui::EventTypeFromNative(event)); |
} |
+// Verify that a mouse wheel scroll event is correctly lacking phase data. |
+TEST_F(EventsMacTest, MouseWheelScroll) { |
+ int32_t wheel_delta_y = 2; |
+ |
+ NSEvent* ns_wheel = TestScrollEvent(default_location_, 0, wheel_delta_y); |
+ EXPECT_FALSE([ns_wheel hasPreciseScrollingDeltas]); |
+ ui::ScrollEvent wheel(ns_wheel); |
+ EXPECT_EQ(ui::ET_SCROLL, wheel.type()); |
+ |
+ // Currently wheel events still say two for finger count, but this may change. |
+ EXPECT_EQ(2, wheel.finger_count()); |
+ |
+ // Note the phase is "end" for wheel events, not "none". There is always an |
+ // "end" when no more events are expected. |
+ EXPECT_EQ(ui::EventMomentumPhase::END, wheel.momentum_phase()); |
+ EXPECT_EQ(default_location_, wheel.location()); |
+ |
+ float pixel_delta_y = wheel_delta_y * ui::kScrollbarPixelsPerCocoaTick; |
+ EXPECT_EQ(pixel_delta_y, wheel.y_offset_ordinal()); |
+ EXPECT_EQ(0, wheel.x_offset_ordinal()); |
+} |
+ |
+// Test the event flow for a trackpad "rest" that doesn't result in scrolling |
+// nor momentum. Also check the boring stuff like type, finger count and |
+// location, which isn't phase-specific. |
+// Sequence: |
+// (1) NSEvent: type=ScrollWheel loc=(780,41) time=14909.3 flags=0x100 win=<set> |
+// {deviceD,d}elta{X,Y,Z}=0 count:0 phase=MayBegin momentumPhase=None |
+// (2) NSEvent: type=ScrollWheel loc=(780,41) time=14912.9 flags=0x100 win=<set> |
+// {deviceD,d}elta{X,Y,Z}=0 count:0 phase=Cancelled momentumPhase=None. |
+TEST_F(EventsMacTest, TrackpadRestRelease) { |
+ NSArray* ns_events = TrackpadScrollSequence(true, 0, 0); |
+ ASSERT_EQ(2u, [ns_events count]); |
+ EXPECT_TRUE([ns_events[0] hasPreciseScrollingDeltas]); |
+ |
+ ui::ScrollEvent rest(ns_events[0]); |
+ EXPECT_EQ(ui::ET_SCROLL, rest.type()); |
+ EXPECT_EQ(2, rest.finger_count()); |
+ EXPECT_EQ(ui::EventMomentumPhase::MAY_BEGIN, rest.momentum_phase()); |
+ EXPECT_EQ(0, rest.y_offset_ordinal()); |
+ EXPECT_EQ(default_location_, rest.location()); |
+ |
+ ui::ScrollEvent cancel(ns_events[1]); |
+ EXPECT_EQ(ui::ET_SCROLL, cancel.type()); |
+ EXPECT_EQ(2, cancel.finger_count()); |
+ EXPECT_EQ(ui::EventMomentumPhase::END, cancel.momentum_phase()); |
+ EXPECT_EQ(0, cancel.y_offset_ordinal()); |
+ EXPECT_EQ(default_location_, cancel.location()); |
+} |
+ |
+// Test the event flow for touching the trackpad while "in motion" already, then |
+// pausing so that a flick is not generated. deltaX and deltaZ are always zero. |
+// Note, deviceDeltaX may take on an integer value even though deltaX is zero. |
+// Sequence: |
+// (1) NSEvent: type=ScrollWheel loc=(780,41) time=15263.2 flags=0x100 win=<set> |
+// deltaY=0.000000 deviceDeltaY=1.000000 phase=Began momentumPhase=None |
+// (n) NSEvent: type=ScrollWheel loc=(780,41) time=15263.2 flags=0x100 win=<set> |
+// deltaY=0.400024 deviceDeltaY=3.000000 phase=Changed momentumPhase=None |
+// (3) NSEvent: type=ScrollWheel loc=(780,41) time=15264.2 flags=0x100 win=<set> |
+// deltaY=0.000000 deviceDeltaY=0.000000 phase=Ended momentumPhase=None. |
+TEST_F(EventsMacTest, TrackpadScrollThenRest) { |
+ int32_t delta_y = 21; |
+ |
+ NSArray* ns_events = TrackpadScrollSequence(false, delta_y, 0); |
+ ASSERT_EQ(3u, [ns_events count]); |
+ |
+ ui::ScrollEvent begin(ns_events[0]); |
+ EXPECT_EQ(ui::EventMomentumPhase::MAY_BEGIN, begin.momentum_phase()); |
+ EXPECT_EQ(0, begin.y_offset_ordinal()); |
+ |
+ ui::ScrollEvent update(ns_events[1]); |
+ // There's no momentum yet, so phase is none. |
+ EXPECT_EQ(ui::EventMomentumPhase::NONE, update.momentum_phase()); |
+ // Note: No pixel conversion for "precise" deltas. |
+ EXPECT_EQ(delta_y, update.y_offset_ordinal()); |
+ |
+ ui::ScrollEvent end(ns_events[2]); |
+ EXPECT_EQ(ui::EventMomentumPhase::END, end.momentum_phase()); |
+ EXPECT_EQ(0, end.y_offset_ordinal()); |
+} |
+ |
+// Same as the above, but with an initial rest, which is not cancelled. This |
+// results in multiple MAY_BEGIN phases. |
+TEST_F(EventsMacTest, TrackpadRestThenScrollThenRest) { |
+ int32_t delta_y = 21; |
+ |
+ NSArray* ns_events = TrackpadScrollSequence(true, delta_y, 0); |
+ ASSERT_EQ(4u, [ns_events count]); |
+ |
+ ui::ScrollEvent rest(ns_events[0]); |
+ EXPECT_EQ(ui::EventMomentumPhase::MAY_BEGIN, rest.momentum_phase()); |
+ EXPECT_EQ(0, rest.y_offset_ordinal()); |
+ |
+ ui::ScrollEvent begin(ns_events[1]); |
+ EXPECT_EQ(ui::EventMomentumPhase::MAY_BEGIN, begin.momentum_phase()); |
+ EXPECT_EQ(0, begin.y_offset_ordinal()); |
+ |
+ ui::ScrollEvent update(ns_events[2]); |
+ EXPECT_EQ(ui::EventMomentumPhase::NONE, update.momentum_phase()); |
+ EXPECT_EQ(delta_y, update.y_offset_ordinal()); |
+ |
+ ui::ScrollEvent end(ns_events[3]); |
+ EXPECT_EQ(ui::EventMomentumPhase::END, end.momentum_phase()); |
+ EXPECT_EQ(0, end.y_offset_ordinal()); |
+} |
+ |
+// Test the event flows that lead to momentum, with and without an initial rest. |
+// Example sequence (no initial rest): |
+// (1) NSEvent: type=ScrollWheel loc=(780,41) time=15187.5 flags=0x100 win=<set> |
+// deltaY=0.000000 deviceDeltaY=1.000000 phase=Began momentumPhase=None |
+// (n) NSEvent: type=ScrollWheel loc=(780,41) time=15187.5 flags=0x100 win=<set> |
+// deltaY=0.500031 deviceDeltaY=4.000000 phase=Changed momentumPhase=None |
+// (3) NSEvent: type=ScrollWheel loc=(780,41) time=15187.6 flags=0x100 win=<set> |
+// deltaY=0.000000 deviceDeltaY=0.000000 phase=Ended momentumPhase=None |
+// (4) NSEvent: type=ScrollWheel loc=(780,41) time=15187.6 flags=0x100 win=<set> |
+// deltaY=0.900055 deviceDeltaY=3.000000 phase=None momentumPhase=Began |
+// (n) NSEvent: type=ScrollWheel loc=(780,41) time=15187.6 flags=0x100 win=<set> |
+// deltaY=0.300018 deviceDeltaY=3.000000 phase=None momentumPhase=Changed |
+// (6) NSEvent: type=ScrollWheel loc=(780,41) time=15188.0 flags=0x100 win=<set> |
+// deltaY=0.000000 deviceDeltaY=0.000000 phase=None momentumPhase=Ended. |
+TEST_F(EventsMacTest, TrackpadScrollThenFlick) { |
+ int32_t delta_y = 21; |
+ int32_t momentum_delta_y = 33; |
+ |
+ NSArray* ns_events = TrackpadScrollSequence(false, delta_y, momentum_delta_y); |
+ ASSERT_EQ(6u, [ns_events count]); |
+ |
+ // Non-momentum part. |
+ { |
+ ui::ScrollEvent begin(ns_events[0]); |
+ EXPECT_EQ(ui::EventMomentumPhase::MAY_BEGIN, begin.momentum_phase()); |
+ EXPECT_EQ(0, begin.y_offset_ordinal()); |
+ |
+ ui::ScrollEvent update(ns_events[1]); |
+ EXPECT_EQ(ui::EventMomentumPhase::NONE, update.momentum_phase()); |
+ EXPECT_EQ(delta_y, update.y_offset_ordinal()); |
+ |
+ ui::ScrollEvent end(ns_events[2]); |
+ // Even though the event stream continues, AppKit doesn't provide a way to |
+ // know this without peeking at future events. So this "end" mid-stream is |
+ // unavoidable. |
+ EXPECT_EQ(ui::EventMomentumPhase::END, end.momentum_phase()); |
+ EXPECT_EQ(0, end.y_offset_ordinal()); |
+ } |
+ // Momentum part. |
+ { |
+ ui::ScrollEvent begin(ns_events[3]); |
+ // Since a momentum "begin" is really a continuation of the stream, it's |
+ // currently treated as an update, but the offsets should always be zero. |
+ EXPECT_EQ(ui::EventMomentumPhase::INERTIAL_UPDATE, begin.momentum_phase()); |
+ EXPECT_EQ(0, begin.y_offset_ordinal()); |
+ |
+ ui::ScrollEvent update(ns_events[4]); |
+ EXPECT_EQ(ui::EventMomentumPhase::INERTIAL_UPDATE, update.momentum_phase()); |
+ EXPECT_EQ(momentum_delta_y, update.y_offset_ordinal()); |
+ |
+ ui::ScrollEvent end(ns_events[5]); |
+ EXPECT_EQ(ui::EventMomentumPhase::END, end.momentum_phase()); |
+ EXPECT_EQ(0, end.y_offset_ordinal()); |
+ } |
+} |
+ |
} // namespace ui |