OLD | NEW |
---|---|
(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 | |
OLD | NEW |