Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(774)

Unified Diff: ui/events/cocoa/events_mac_unittest.mm

Issue 2193153002: MacViews: Send Mac scrollWheel NSEvents as ui::ET_SCROLL (ui::ScrollEvent). (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@20160728-MacViews-ScrollLayers
Patch Set: More idiomatic ObjC Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698