1// Copyright (c) 2011 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/base/cocoa/base_view.h" 6 7#include "base/mac/mac_util.h" 8 9NSString* kViewDidBecomeFirstResponder = 10 @"Chromium.kViewDidBecomeFirstResponder"; 11NSString* kSelectionDirection = @"Chromium.kSelectionDirection"; 12 13@implementation BaseView 14 15- (instancetype)initWithFrame:(NSRect)frame { 16 if ((self = [super initWithFrame:frame])) { 17 [self enableTracking]; 18 } 19 return self; 20} 21 22- (instancetype)initWithCoder:(NSCoder*)decoder { 23 if ((self = [super initWithCoder:decoder])) { 24 [self enableTracking]; 25 } 26 return self; 27} 28 29- (void)dealloc { 30 [self disableTracking]; 31 [super dealloc]; 32} 33 34- (void)enableTracking { 35 if (_trackingArea.get()) 36 return; 37 38 NSTrackingAreaOptions trackingOptions = NSTrackingMouseEnteredAndExited | 39 NSTrackingMouseMoved | 40 NSTrackingActiveAlways | 41 NSTrackingInVisibleRect; 42 _trackingArea.reset([[CrTrackingArea alloc] initWithRect:NSZeroRect 43 options:trackingOptions 44 owner:self 45 userInfo:nil]); 46 [self addTrackingArea:_trackingArea.get()]; 47} 48 49- (void)disableTracking { 50 if (_trackingArea.get()) { 51 [self removeTrackingArea:_trackingArea.get()]; 52 _trackingArea.reset(); 53 } 54} 55 56- (void)updateTrackingAreas { 57 [super updateTrackingAreas]; 58 59 // NSTrackingInVisibleRect doesn't work correctly with Lion's window 60 // resizing (See https://crbug.com/176725 and 61 // http://openradar.appspot.com/radar?id=2773401). It also doesn't work 62 // correctly when the window enters fullscreen 63 // (See https://crbug.com/170058). 64 // 65 // Work around it by reinstalling the tracking area after the window resizes 66 // or enters fullscreen. This AppKit bug is fixed on High Sierra, so we only 67 // apply this workaround on 10.12 or earlier. 68 if (base::mac::IsAtMostOS10_12()) { 69 [self disableTracking]; 70 [self enableTracking]; 71 } 72} 73 74- (void)handleLeftMouseUp:(NSEvent*)theEvent { 75 DCHECK_EQ([theEvent type], NSLeftMouseUp); 76 _dragging = NO; 77 if (!_pendingExitEvent) 78 return; 79 80 NSEvent* exitEvent = 81 [NSEvent enterExitEventWithType:NSMouseExited 82 location:[theEvent locationInWindow] 83 modifierFlags:[theEvent modifierFlags] 84 timestamp:[theEvent timestamp] 85 windowNumber:[theEvent windowNumber] 86 context:[theEvent context] 87 eventNumber:[_pendingExitEvent eventNumber] 88 trackingNumber:[_pendingExitEvent trackingNumber] 89 userData:[_pendingExitEvent userData]]; 90 [self mouseEvent:exitEvent]; 91 _pendingExitEvent.reset(); 92} 93 94- (void)mouseEvent:(NSEvent*)theEvent { 95 // This method left intentionally blank. 96} 97 98- (EventHandled)keyEvent:(NSEvent*)theEvent { 99 // The default implementation of this method does not handle any key events. 100 // Derived classes should return kEventHandled if they handled an event, 101 // otherwise it will be forwarded on to |super|. 102 return kEventNotHandled; 103} 104 105- (void)forceTouchEvent:(NSEvent*)theEvent { 106 // This method left intentionally blank. 107} 108 109- (void)tabletEvent:(NSEvent*)theEvent { 110 // This method left intentionally blank. 111} 112 113- (void)mouseDown:(NSEvent*)theEvent { 114 _dragging = YES; 115 [self mouseEvent:theEvent]; 116} 117 118- (void)rightMouseDown:(NSEvent*)theEvent { 119 [self mouseEvent:theEvent]; 120} 121 122- (void)otherMouseDown:(NSEvent*)theEvent { 123 [self mouseEvent:theEvent]; 124} 125 126- (void)mouseUp:(NSEvent*)theEvent { 127 [self mouseEvent:theEvent]; 128 [self handleLeftMouseUp:theEvent]; 129} 130 131- (void)rightMouseUp:(NSEvent*)theEvent { 132 [self mouseEvent:theEvent]; 133} 134 135- (void)otherMouseUp:(NSEvent*)theEvent { 136 [self mouseEvent:theEvent]; 137} 138 139- (void)mouseMoved:(NSEvent*)theEvent { 140 [self mouseEvent:theEvent]; 141} 142 143- (void)mouseDragged:(NSEvent*)theEvent { 144 [self mouseEvent:theEvent]; 145} 146 147- (void)rightMouseDragged:(NSEvent*)theEvent { 148 [self mouseEvent:theEvent]; 149} 150 151- (void)otherMouseDragged:(NSEvent*)theEvent { 152 [self mouseEvent:theEvent]; 153} 154 155- (void)mouseEntered:(NSEvent*)theEvent { 156 if (_pendingExitEvent) { 157 _pendingExitEvent.reset(); 158 return; 159 } 160 161 [self mouseEvent:theEvent]; 162} 163 164- (void)mouseExited:(NSEvent*)theEvent { 165 // The tracking area will send an exit event even during a drag, which isn't 166 // how the event flow for drags should work. This stores the exit event, and 167 // sends it when the drag completes instead. 168 if (_dragging) { 169 _pendingExitEvent.reset([theEvent retain]); 170 return; 171 } 172 173 [self mouseEvent:theEvent]; 174} 175 176- (void)keyDown:(NSEvent*)theEvent { 177 if ([self keyEvent:theEvent] != kEventHandled) 178 [super keyDown:theEvent]; 179} 180 181- (void)keyUp:(NSEvent*)theEvent { 182 if ([self keyEvent:theEvent] != kEventHandled) 183 [super keyUp:theEvent]; 184} 185 186- (void)pressureChangeWithEvent:(NSEvent*)theEvent { 187 NSInteger newStage = [theEvent stage]; 188 if (_pressureEventStage == newStage) 189 return; 190 191 // Call the force touch event when the stage reaches 2, which is the value 192 // for force touch. 193 if (newStage == 2) { 194 [self forceTouchEvent:theEvent]; 195 } 196 _pressureEventStage = newStage; 197} 198 199- (void)flagsChanged:(NSEvent*)theEvent { 200 if ([self keyEvent:theEvent] != kEventHandled) 201 [super flagsChanged:theEvent]; 202} 203 204- (gfx::Rect)flipNSRectToRect:(NSRect)rect { 205 gfx::Rect new_rect(NSRectToCGRect(rect)); 206 new_rect.set_y(NSHeight([self bounds]) - new_rect.bottom()); 207 return new_rect; 208} 209 210- (NSRect)flipRectToNSRect:(gfx::Rect)rect { 211 NSRect new_rect(NSRectFromCGRect(rect.ToCGRect())); 212 new_rect.origin.y = NSHeight([self bounds]) - NSMaxY(new_rect); 213 return new_rect; 214} 215 216- (void)tabletProximity:(NSEvent*)theEvent { 217 [self tabletEvent:theEvent]; 218} 219 220@end 221