Chromium Code Reviews| 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 |