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

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: No inheritance Created 6 years, 5 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.
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698