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

Side by Side Diff: ui/views/test/widget_event_generator_mac.mm

Issue 322893005: MacViews: Add WidgetEventGenerator to abstract platform-specific event generation for tests. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: cull orthogonal stuff, add WidgetTest.MouseEventTypesViaGenerator Created 6 years, 6 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/views/test/widget_event_generator.h"
6
7 #import <Cocoa/Cocoa.h>
8
9 #import "base/test/mock_chrome_application_mac.h"
10 #import "ui/events/test/cocoa_test_event_utils.h"
11 #include "ui/gfx/vector2d_conversions.h"
12 #include "ui/views/widget/widget.h"
13
14 namespace {
15
16 // Singleton to provide state for swizzled Objective C methods.
17 views::test::WidgetEventGeneratorMac* g_active_generator = NULL;
18
19 } // namespace
20
21 @interface NSEventDonor : NSObject
22 @end
23
24 @implementation NSEventDonor
25
26 // Donate +[NSEvent pressedMouseButtons] by retrieving the flags from the
27 // active generator.
28 + (NSUInteger)pressedMouseButtons {
29 int flags = g_active_generator->flags();
30 NSUInteger bitmask = 0;
31 if (flags & ui::EF_LEFT_MOUSE_BUTTON)
32 bitmask |= 1;
33 if (flags & ui::EF_RIGHT_MOUSE_BUTTON)
34 bitmask |= 1 << 1;
35 if (flags & ui::EF_MIDDLE_MOUSE_BUTTON)
36 bitmask |= 1 << 2;
37 return bitmask;
38 }
39
40 @end
41
42 namespace views {
43 namespace test {
44
45 class WidgetEventGeneratorMac::Impl {
46 public:
47 Impl();
48 ~Impl() {}
49
50 private:
51 ScopedClassSwizzler swizzle_pressed_;
52
53 DISALLOW_COPY_AND_ASSIGN(Impl);
54 };
55
56 WidgetEventGeneratorMac::Impl::Impl()
57 : swizzle_pressed_([NSEvent class],
58 [NSEventDonor class],
59 @selector(pressedMouseButtons)) {
60 }
61
62 namespace {
63
64 NSPoint ConvertScreenPointToTarget(NSWindow* target,
65 const gfx::Point& point_in_screen) {
66 // Normally this would do [NSWindow convertScreenToBase:]. However, Cocoa can
67 // reposition the window on screen and make things flaky. Initially, just
68 // assume that the contentRect of |target| is at the top-left corner of the
69 // screen.
70 NSRect content_rect = [target contentRectForFrameRect:[target frame]];
71 return NSMakePoint(point_in_screen.x(),
72 NSHeight(content_rect) - point_in_screen.y());
73 }
74
75 // Inverse of ui::EventFlagsFromModifiers().
76 NSUInteger EventFlagsToModifiers(int flags) {
77 NSUInteger modifiers = 0;
78 modifiers |= (flags & ui::EF_CAPS_LOCK_DOWN) ? NSAlphaShiftKeyMask : 0;
79 modifiers |= (flags & ui::EF_SHIFT_DOWN) ? NSShiftKeyMask : 0;
80 modifiers |= (flags & ui::EF_CONTROL_DOWN) ? NSControlKeyMask : 0;
81 modifiers |= (flags & ui::EF_ALT_DOWN) ? NSAlternateKeyMask : 0;
82 modifiers |= (flags & ui::EF_COMMAND_DOWN) ? NSCommandKeyMask : 0;
83 // ui::EF_*_MOUSE_BUTTON not handled here.
84 // NSFunctionKeyMask, NSNumericPadKeyMask and NSHelpKeyMask not mapped.
85 return modifiers;
86 }
87
88 // Picks the corresponding mouse event type for the buttons set in |flags|.
89 NSEventType PickMouseEventType(int flags,
90 NSEventType left,
91 NSEventType right,
92 NSEventType other) {
93 if (flags & ui::EF_MIDDLE_MOUSE_BUTTON)
94 return other;
95 if (flags & ui::EF_RIGHT_MOUSE_BUTTON)
96 return right;
97 return left;
98 }
99
100 // Inverse of ui::EventTypeFromNative(). If non-null |modifiers| will be set
101 // using the inverse of ui::EventFlagsFromNSEventWithModifiers().
102 NSEventType EventTypeToNative(ui::EventType ui_event_type,
103 int flags,
104 NSUInteger* modifiers) {
105 if (modifiers)
106 *modifiers = EventFlagsToModifiers(flags);
107 switch (ui_event_type) {
108 case ui::ET_UNKNOWN:
109 return 0;
110 case ui::ET_KEY_PRESSED:
111 return NSKeyDown;
112 case ui::ET_KEY_RELEASED:
113 return NSKeyUp;
114 case ui::ET_MOUSE_PRESSED:
115 return PickMouseEventType(flags,
116 NSLeftMouseDown,
117 NSRightMouseDown,
118 NSOtherMouseDown);
119 case ui::ET_MOUSE_RELEASED:
120 return PickMouseEventType(flags,
121 NSLeftMouseUp,
122 NSRightMouseUp,
123 NSOtherMouseUp);
124 case ui::ET_MOUSE_DRAGGED:
125 return PickMouseEventType(flags,
126 NSLeftMouseDragged,
127 NSRightMouseDragged,
128 NSOtherMouseDragged);
129 case ui::ET_MOUSE_MOVED:
130 return NSMouseMoved;
131 case ui::ET_MOUSEWHEEL:
132 return NSScrollWheel;
133 case ui::ET_MOUSE_ENTERED:
134 return NSMouseEntered;
135 case ui::ET_MOUSE_EXITED:
136 return NSMouseExited;
137 case ui::ET_SCROLL_FLING_START:
138 return NSEventTypeSwipe;
139 default:
140 NOTREACHED();
141 return 0;
142 }
143 }
144
145 // Emulate the dispatching that would be performed by -[NSWindow sendEvent:].
146 // sendEvent is a black box which (among other things) will try to peek at the
147 // event queue and can block indefinitely.
148 void EmulateSendEvent(NSWindow* window, NSEvent* event) {
149 NSResponder* responder = [window firstResponder];
150 switch ([event type]) {
151 case NSKeyDown:
152 [responder keyDown:event];
153 break;
154 case NSKeyUp:
155 [responder keyUp:event];
156 break;
157 case NSLeftMouseDown:
158 [responder mouseDown:event];
Andre 2014/06/17 21:17:56 The target of mouse events should be the view unde
tapted 2014/06/18 08:28:06 Hm good point. Although hitTest is just for moused
159 break;
160 case NSRightMouseDown:
161 [responder rightMouseDown:event];
162 break;
163 case NSOtherMouseDown:
164 [responder otherMouseDown:event];
165 break;
166 case NSLeftMouseUp:
167 [responder mouseUp:event];
168 break;
169 case NSRightMouseUp:
170 [responder rightMouseUp:event];
171 break;
172 case NSOtherMouseUp:
173 [responder otherMouseUp:event];
174 break;
175 case NSLeftMouseDragged:
176 [responder mouseDragged:event];
177 break;
178 case NSRightMouseDragged:
179 [responder rightMouseDragged:event];
180 break;
181 case NSOtherMouseDragged:
182 [responder otherMouseDragged:event];
183 break;
184 case NSMouseMoved:
185 // Assumes [NSWindow acceptsMouseMovedEvents] would return YES.
186 [responder mouseMoved:event];
187 break;
188 case NSScrollWheel:
189 [responder scrollWheel:event];
190 break;
191 case NSMouseEntered:
192 [responder mouseEntered:event];
193 break;
194 case NSMouseExited:
195 [responder mouseExited:event];
196 break;
197 case NSEventTypeSwipe:
198 [responder swipeWithEvent:event];
199 break;
200 default:
201 NOTREACHED();
202 }
203 }
204
205 void DispatchMouseEventInWindow(NSWindow* window,
206 ui::EventType event_type,
207 const gfx::Point& point_in_screen,
208 int flags) {
209 NSUInteger click_count = 0;
210 if (event_type == ui::ET_MOUSE_PRESSED ||
211 event_type == ui::ET_MOUSE_RELEASED) {
212 if (flags & ui::EF_IS_TRIPLE_CLICK)
213 click_count = 3;
214 else if (flags & ui::EF_IS_DOUBLE_CLICK)
215 click_count = 2;
216 else
217 click_count = 1;
218 }
219 NSPoint point = ConvertScreenPointToTarget(window, point_in_screen);
220 NSUInteger modifiers = 0;
221 NSEventType type = EventTypeToNative(event_type, flags, &modifiers);
222 NSEvent* event = [NSEvent mouseEventWithType:type
223 location:point
224 modifierFlags:modifiers
225 timestamp:0
226 windowNumber:[window windowNumber]
227 context:nil
228 eventNumber:0
229 clickCount:click_count
230 pressure:1.0];
231
232 // Typically events go through NSApplication. For tests, dispatch the event
233 // directly to make things more predicatble.
234 EmulateSendEvent(window, event);
235 }
236
237 } // namespace
238
239 WidgetEventGenerator::WidgetEventGenerator(Widget* widget)
240 : WidgetEventGeneratorMac(widget->GetNativeWindow()) {
241 }
242
243 WidgetEventGenerator::WidgetEventGenerator(gfx::NativeView context)
244 : WidgetEventGeneratorMac([context window]) {
245 }
246
247 WidgetEventGenerator::WidgetEventGenerator(
248 gfx::NativeView context,
249 Widget* window_for_initial_location)
250 : WidgetEventGeneratorMac([context window]) {
251 set_current_location(
252 window_for_initial_location->GetRestoredBounds().CenterPoint());
253 }
254
255 WidgetEventGenerator::~WidgetEventGenerator() {
256 }
257
258 // static
259 void WidgetEventGenerator::SimulateNativeDestroy(Widget* widget) {
260 [widget->GetNativeWindow() release];
261 }
262
263 WidgetEventGeneratorMac::WidgetEventGeneratorMac(NSWindow* ns_window)
264 : impl_(new Impl), ns_window_(ns_window), flags_(0) {
265 DCHECK(!g_active_generator);
266 g_active_generator = this;
267 }
268
269 WidgetEventGeneratorMac::~WidgetEventGeneratorMac() {
270 DCHECK_EQ(this, g_active_generator);
271 g_active_generator = NULL;
272 }
273
274 void WidgetEventGeneratorMac::PressLeftButton() {
275 PressButton(ui::EF_LEFT_MOUSE_BUTTON);
276 }
277
278 void WidgetEventGeneratorMac::ReleaseLeftButton() {
279 ReleaseButton(ui::EF_LEFT_MOUSE_BUTTON);
280 }
281
282 void WidgetEventGeneratorMac::ClickLeftButton() {
283 PressLeftButton();
284 ReleaseLeftButton();
285 }
286
287 void WidgetEventGeneratorMac::PressRightButton() {
288 PressButton(ui::EF_RIGHT_MOUSE_BUTTON);
289 }
290
291 void WidgetEventGeneratorMac::ReleaseRightButton() {
292 ReleaseButton(ui::EF_RIGHT_MOUSE_BUTTON);
293 }
294
295 void WidgetEventGeneratorMac::GestureTapAt(const gfx::Point& point) {
296 NOTIMPLEMENTED();
297 }
298
299 void WidgetEventGeneratorMac::GestureScrollSequence(
300 const gfx::Point& start,
301 const gfx::Point& end,
302 const base::TimeDelta& duration,
303 int steps) {
304 NOTIMPLEMENTED();
305 }
306
307 void WidgetEventGeneratorMac::GestureMultiFingerScroll(
308 int count,
309 const gfx::Point start[],
310 int event_separation_time_ms,
311 int steps,
312 int move_x,
313 int move_y) {
314 NOTIMPLEMENTED();
315 }
316
317 void WidgetEventGeneratorMac::MoveMouseTo(const gfx::Point& point_in_screen,
318 int count) {
319 DCHECK_GT(count, 0);
320 const ui::EventType event_type = (flags_ & ui::EF_LEFT_MOUSE_BUTTON) ?
321 ui::ET_MOUSE_DRAGGED : ui::ET_MOUSE_MOVED;
322
323 gfx::Vector2dF diff(point_in_screen - current_location_);
324 for (float i = 1; i <= count; i++) {
325 gfx::Vector2dF step(diff);
326 step.Scale(i / count);
327 gfx::Point move_point = current_location_ + gfx::ToRoundedVector2d(step);
328 // TOOD(tapted): Handle moving between windows.
329 DispatchMouseEventInWindow(ns_window_, event_type, move_point, flags_);
330 }
331 current_location_ = point_in_screen;
332 }
333
334 void WidgetEventGeneratorMac::DragMouseTo(const gfx::Point& point) {
335 PressLeftButton();
336 MoveMouseTo(point);
337 ReleaseLeftButton();
338 }
339
340 void WidgetEventGeneratorMac::PressButton(int flag) {
341 flags_ |= flag;
342 DispatchMouseEventInWindow(ns_window_,
343 ui::ET_MOUSE_PRESSED,
344 current_location_,
345 flag);
346 }
347
348 void WidgetEventGeneratorMac::ReleaseButton(int flag) {
349 DCHECK(flags_ & flag);
350 flags_ &= ~flag;
351 DispatchMouseEventInWindow(ns_window_,
352 ui::ET_MOUSE_RELEASED,
353 current_location_,
354 flag);
355 }
356
357 } // namespace views
358 } // namespace test
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698