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. | |
Ben Goodger (Google)
2014/07/16 15:05:14
i would just call this file event_generator_delega
tapted
2014/07/17 03:42:34
Done (former).
| |
4 | |
5 #import <Cocoa/Cocoa.h> | |
6 | |
7 #include "ui/events/event_processor.h" | |
8 #include "ui/events/event_target.h" | |
9 #include "ui/events/event_target_iterator.h" | |
10 #include "ui/events/event_targeter.h" | |
11 #include "ui/events/test/event_generator.h" | |
12 #import "ui/events/test/cocoa_test_event_utils.h" | |
13 #include "ui/gfx/mac/coordinate_conversion.h" | |
14 | |
15 namespace { | |
16 | |
17 // Singleton to provide state for swizzled Objective C methods. | |
18 ui::test::EventGenerator* g_active_generator = NULL; | |
19 | |
20 } // namespace | |
21 | |
22 @interface NSEventDonor : NSObject | |
23 @end | |
24 | |
25 @implementation NSEventDonor | |
26 | |
27 // Donate +[NSEvent pressedMouseButtons] by retrieving the flags from the | |
28 // active generator. | |
29 + (NSUInteger)pressedMouseButtons { | |
30 int flags = g_active_generator->flags(); | |
31 NSUInteger bitmask = 0; | |
32 if (flags & ui::EF_LEFT_MOUSE_BUTTON) | |
33 bitmask |= 1; | |
34 if (flags & ui::EF_RIGHT_MOUSE_BUTTON) | |
35 bitmask |= 1 << 1; | |
36 if (flags & ui::EF_MIDDLE_MOUSE_BUTTON) | |
37 bitmask |= 1 << 2; | |
38 return bitmask; | |
39 } | |
40 | |
41 @end | |
42 | |
43 namespace { | |
44 | |
45 NSPoint ConvertRootPointToTarget(NSWindow* target, | |
46 const gfx::Point& point_in_root) { | |
47 // Normally this would do [NSWindow convertScreenToBase:]. However, Cocoa can | |
48 // reposition the window on screen and make things flaky. Initially, just | |
49 // assume that the contentRect of |target| is at the top-left corner of the | |
50 // screen. | |
51 NSRect content_rect = [target contentRectForFrameRect:[target frame]]; | |
52 return NSMakePoint(point_in_root.x(), | |
53 NSHeight(content_rect) - point_in_root.y()); | |
54 } | |
55 | |
56 // Inverse of ui::EventFlagsFromModifiers(). | |
57 NSUInteger EventFlagsToModifiers(int flags) { | |
58 NSUInteger modifiers = 0; | |
59 modifiers |= (flags & ui::EF_CAPS_LOCK_DOWN) ? NSAlphaShiftKeyMask : 0; | |
60 modifiers |= (flags & ui::EF_SHIFT_DOWN) ? NSShiftKeyMask : 0; | |
61 modifiers |= (flags & ui::EF_CONTROL_DOWN) ? NSControlKeyMask : 0; | |
62 modifiers |= (flags & ui::EF_ALT_DOWN) ? NSAlternateKeyMask : 0; | |
63 modifiers |= (flags & ui::EF_COMMAND_DOWN) ? NSCommandKeyMask : 0; | |
64 // ui::EF_*_MOUSE_BUTTON not handled here. | |
65 // NSFunctionKeyMask, NSNumericPadKeyMask and NSHelpKeyMask not mapped. | |
66 return modifiers; | |
67 } | |
68 | |
69 // Picks the corresponding mouse event type for the buttons set in |flags|. | |
70 NSEventType PickMouseEventType(int flags, | |
71 NSEventType left, | |
72 NSEventType right, | |
73 NSEventType other) { | |
74 if (flags & ui::EF_LEFT_MOUSE_BUTTON) | |
75 return left; | |
76 if (flags & ui::EF_RIGHT_MOUSE_BUTTON) | |
77 return right; | |
78 return other; | |
79 } | |
80 | |
81 // Inverse of ui::EventTypeFromNative(). If non-null |modifiers| will be set | |
82 // using the inverse of ui::EventFlagsFromNSEventWithModifiers(). | |
83 NSEventType EventTypeToNative(ui::EventType ui_event_type, | |
84 int flags, | |
85 NSUInteger* modifiers) { | |
86 if (modifiers) | |
87 *modifiers = EventFlagsToModifiers(flags); | |
88 switch (ui_event_type) { | |
89 case ui::ET_UNKNOWN: | |
90 return 0; | |
91 case ui::ET_KEY_PRESSED: | |
92 return NSKeyDown; | |
93 case ui::ET_KEY_RELEASED: | |
94 return NSKeyUp; | |
95 case ui::ET_MOUSE_PRESSED: | |
96 return PickMouseEventType(flags, | |
97 NSLeftMouseDown, | |
98 NSRightMouseDown, | |
99 NSOtherMouseDown); | |
100 case ui::ET_MOUSE_RELEASED: | |
101 return PickMouseEventType(flags, | |
102 NSLeftMouseUp, | |
103 NSRightMouseUp, | |
104 NSOtherMouseUp); | |
105 case ui::ET_MOUSE_DRAGGED: | |
106 return PickMouseEventType(flags, | |
107 NSLeftMouseDragged, | |
108 NSRightMouseDragged, | |
109 NSOtherMouseDragged); | |
110 case ui::ET_MOUSE_MOVED: | |
111 return NSMouseMoved; | |
112 case ui::ET_MOUSEWHEEL: | |
113 return NSScrollWheel; | |
114 case ui::ET_MOUSE_ENTERED: | |
115 return NSMouseEntered; | |
116 case ui::ET_MOUSE_EXITED: | |
117 return NSMouseExited; | |
118 case ui::ET_SCROLL_FLING_START: | |
119 return NSEventTypeSwipe; | |
120 default: | |
121 NOTREACHED(); | |
122 return 0; | |
123 } | |
124 } | |
125 | |
126 // Emulate the dispatching that would be performed by -[NSWindow sendEvent:]. | |
127 // sendEvent is a black box which (among other things) will try to peek at the | |
128 // event queue and can block indefinitely. | |
129 void EmulateSendEvent(NSWindow* window, NSEvent* event) { | |
130 NSResponder* responder = [window firstResponder]; | |
131 switch ([event type]) { | |
132 case NSKeyDown: | |
133 [responder keyDown:event]; | |
134 return; | |
135 case NSKeyUp: | |
136 [responder keyUp:event]; | |
137 return; | |
138 } | |
139 | |
140 // For mouse events, NSWindow will use -[NSView hitTest:] for the initial | |
141 // mouseDown, and then keep track of the NSView returned. The toolkit-views | |
142 // RootView does this too. So, for tests, assume tracking will be done there, | |
143 // and the NSWindow's contentView is wrapping a views::internal::RootView. | |
144 responder = [window contentView]; | |
145 switch ([event type]) { | |
146 case NSLeftMouseDown: | |
147 [responder mouseDown:event]; | |
148 break; | |
149 case NSRightMouseDown: | |
150 [responder rightMouseDown:event]; | |
151 break; | |
152 case NSOtherMouseDown: | |
153 [responder otherMouseDown:event]; | |
154 break; | |
155 case NSLeftMouseUp: | |
156 [responder mouseUp:event]; | |
157 break; | |
158 case NSRightMouseUp: | |
159 [responder rightMouseUp:event]; | |
160 break; | |
161 case NSOtherMouseUp: | |
162 [responder otherMouseUp:event]; | |
163 break; | |
164 case NSLeftMouseDragged: | |
165 [responder mouseDragged:event]; | |
166 break; | |
167 case NSRightMouseDragged: | |
168 [responder rightMouseDragged:event]; | |
169 break; | |
170 case NSOtherMouseDragged: | |
171 [responder otherMouseDragged:event]; | |
172 break; | |
173 case NSMouseMoved: | |
174 // Assumes [NSWindow acceptsMouseMovedEvents] would return YES, and that | |
175 // NSTrackingAreas have been appropriately installed on |responder|. | |
176 [responder mouseMoved:event]; | |
177 break; | |
178 case NSScrollWheel: | |
179 [responder scrollWheel:event]; | |
180 break; | |
181 case NSMouseEntered: | |
182 case NSMouseExited: | |
183 // With the assumptions in NSMouseMoved, it doesn't make sense for the | |
184 // generator to handle entered/exited separately. It's the responsibility | |
185 // of views::internal::RootView to convert the moved events into entered | |
186 // and exited events for the individual views. | |
187 NOTREACHED(); | |
188 break; | |
189 case NSEventTypeSwipe: | |
190 // NSEventTypeSwipe events can't be generated using public interfaces on | |
191 // NSEvent, so this will need to be handled at a higher level. | |
192 NOTREACHED(); | |
193 break; | |
194 default: | |
195 NOTREACHED(); | |
196 } | |
197 } | |
198 | |
199 void DispatchMouseEventInWindow(NSWindow* window, | |
200 ui::EventType event_type, | |
201 const gfx::Point& point_in_root, | |
202 int flags) { | |
203 NSUInteger click_count = 0; | |
204 if (event_type == ui::ET_MOUSE_PRESSED || | |
205 event_type == ui::ET_MOUSE_RELEASED) { | |
206 if (flags & ui::EF_IS_TRIPLE_CLICK) | |
207 click_count = 3; | |
208 else if (flags & ui::EF_IS_DOUBLE_CLICK) | |
209 click_count = 2; | |
210 else | |
211 click_count = 1; | |
212 } | |
213 NSPoint point = ConvertRootPointToTarget(window, point_in_root); | |
214 NSUInteger modifiers = 0; | |
215 NSEventType type = EventTypeToNative(event_type, flags, &modifiers); | |
216 NSEvent* event = [NSEvent mouseEventWithType:type | |
217 location:point | |
218 modifierFlags:modifiers | |
219 timestamp:0 | |
220 windowNumber:[window windowNumber] | |
221 context:nil | |
222 eventNumber:0 | |
223 clickCount:click_count | |
224 pressure:1.0]; | |
225 | |
226 // Typically events go through NSApplication. For tests, dispatch the event | |
227 // directly to make things more predicatble. | |
228 EmulateSendEvent(window, event); | |
229 } | |
230 | |
231 // Implementation of ui::test::EventGeneratorDelegate for Mac. Everything | |
232 // defined inline is just a stub. Interesting overrides are defined below the | |
233 // class. | |
234 class EventGeneratorDelegateMac : public ui::EventTarget, | |
235 public ui::EventSource, | |
236 public ui::EventProcessor, | |
237 public ui::EventTargeter, | |
238 public ui::test::EventGeneratorDelegate { | |
239 public: | |
240 EventGeneratorDelegateMac(ui::test::EventGenerator* owner, NSWindow* window); | |
241 virtual ~EventGeneratorDelegateMac() {} | |
242 | |
243 // Overridden from ui::EventTarget: | |
244 virtual bool CanAcceptEvent(const ui::Event& event) OVERRIDE { return true; } | |
245 virtual ui::EventTarget* GetParentTarget() OVERRIDE { return NULL; } | |
246 virtual scoped_ptr<ui::EventTargetIterator> GetChildIterator() const OVERRIDE; | |
247 virtual ui::EventTargeter* GetEventTargeter() OVERRIDE { | |
248 return this; | |
249 } | |
250 | |
251 // Overridden from ui::EventHandler (via ui::EventTarget): | |
252 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE; | |
253 | |
254 // Overridden from ui::EventSource: | |
255 virtual ui::EventProcessor* GetEventProcessor() OVERRIDE { return this; } | |
256 | |
257 // Overridden from ui::EventProcessor: | |
258 virtual ui::EventTarget* GetRootTarget() OVERRIDE { return this; } | |
259 | |
260 // Overridden from ui::EventDispatcherDelegate (via ui::EventProcessor): | |
261 virtual bool CanDispatchToTarget(EventTarget* target) OVERRIDE { | |
262 return true; | |
263 } | |
264 | |
265 // Overridden from ui::test::EventGeneratorDelegate: | |
266 virtual ui::EventTarget* GetTargetAt(const gfx::Point& location) OVERRIDE { | |
267 return this; | |
268 } | |
269 virtual ui::EventSource* GetEventSource(ui::EventTarget* target) OVERRIDE { | |
270 return this; | |
271 } | |
272 virtual gfx::Point CenterOfTarget( | |
273 const ui::EventTarget* target) const OVERRIDE; | |
274 virtual gfx::Point CenterOfWindow(gfx::NativeWindow window) const OVERRIDE; | |
275 | |
276 virtual void ConvertPointFromTarget(const ui::EventTarget* target, | |
277 gfx::Point* point) const OVERRIDE {} | |
278 virtual void ConvertPointToTarget(const ui::EventTarget* target, | |
279 gfx::Point* point) const OVERRIDE {} | |
280 virtual void ConvertPointFromHost(const ui::EventTarget* hosted_target, | |
281 gfx::Point* point) const OVERRIDE {} | |
282 | |
283 private: | |
284 ui::test::EventGenerator* owner_; | |
285 NSWindow* window_; | |
286 ScopedClassSwizzler swizzle_pressed_; | |
287 | |
288 DISALLOW_COPY_AND_ASSIGN(EventGeneratorDelegateMac); | |
289 }; | |
290 | |
291 EventGeneratorDelegateMac::EventGeneratorDelegateMac( | |
292 ui::test::EventGenerator* owner, NSWindow* window) | |
293 : owner_(owner), | |
294 window_(window), | |
295 swizzle_pressed_([NSEvent class], | |
296 [NSEventDonor class], | |
297 @selector(pressedMouseButtons)) { | |
298 } | |
299 | |
300 scoped_ptr<ui::EventTargetIterator> | |
301 EventGeneratorDelegateMac::GetChildIterator() const { | |
302 // Return NULL to dispatch all events to the result of GetRootTarget(). | |
303 return scoped_ptr<ui::EventTargetIterator>(); | |
304 } | |
305 | |
306 void EventGeneratorDelegateMac::OnMouseEvent(ui::MouseEvent* event) { | |
307 // For mouse drag events, ensure the swizzled methods return the right flags. | |
308 base::AutoReset<ui::test::EventGenerator*> reset(&g_active_generator, owner_); | |
309 DispatchMouseEventInWindow(window_, | |
310 event->type(), | |
311 event->location(), | |
312 event->changed_button_flags()); | |
313 } | |
314 | |
315 gfx::Point EventGeneratorDelegateMac::CenterOfTarget( | |
316 const ui::EventTarget* target) const { | |
317 DCHECK_EQ(target, this); | |
318 return CenterOfWindow(window_); | |
319 } | |
320 | |
321 gfx::Point EventGeneratorDelegateMac::CenterOfWindow( | |
322 gfx::NativeWindow window) const { | |
323 DCHECK_EQ(window, window_); | |
324 return gfx::ScreenRectFromNSRect([window frame]).CenterPoint(); | |
325 } | |
326 | |
327 } // namespace | |
328 | |
329 namespace ui { | |
330 namespace test { | |
331 | |
332 // static | |
333 EventGeneratorDelegate* EventGenerator::CreateDefaultPlatformDelegate( | |
334 EventGenerator* owner, | |
335 gfx::NativeWindow root_window, | |
336 gfx::NativeWindow window) { | |
337 return new EventGeneratorDelegateMac(owner, window); | |
338 } | |
339 | |
340 } // namespace ui | |
341 } // namespace test | |
OLD | NEW |