Changeset 21764
- Timestamp:
- 12/01/2007 01:07:31 AM (1 year ago)
- Files:
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
branches/tooltip-fixes/Frameworks/AIUtilities Framework/Source/AISmoothTooltipTracker.h
r21762 r21764 46 46 47 47 NSPoint lastMouseLocation; //Last known location of the mouse, used for movement tracking 48 NSTimer *tooltipMouseLocationTimer; //Checks for mouse movement49 48 NSPoint tooltipLocation; //Last tooltip location we told our delegate about 50 NSTracking RectTag tooltipTrackingTag; //Tag for our tracking rect49 NSTrackingArea *trackingArea; //Tracking area to watch the view for mouse entry, movement, and exit 51 50 NSTimer *tooltipDelayTimer; //Used to determine how long before a tooltip appears 52 53 void *mouseMovedHandlerUPP; //By void *, I mean EventHandlerUPP.54 void *mouseMovedHandler; //Carbon Event handler for mouse-moved events; only exists during tracking. By void *, I mean EventHandlerRef.55 51 } 56 52 branches/tooltip-fixes/Frameworks/AIUtilities Framework/Source/AISmoothTooltipTracker.m
r21763 r21764 27 27 - (void)resetCursorTracking; 28 28 29 - (void)_startTrackingMouse;30 - (void)_stopTrackingMouse;31 29 - (void)_hideTooltip; 32 30 33 //10.4: We use these to access our instance variables from the Carbon Event handler without making them @public. When we switch to NSTrackingArea, they should go away along with the handler. 34 - (void)getPrivateVariablesTooltipDelayTimer:(out NSTimer **)outTooltipDelayTimer 35 lastMouseLocation:(out NSPoint *)outLastMouseLocation 36 tooltipLocation:(out NSPoint *)outTooltipLocation 37 delegate:(out id *)outDelegate; 38 - (void)setPrivateVariablesLastMouseLocation:(NSPoint)newLastMouseLocation 39 tooltipLocation:(NSPoint)newTooltipLocation 40 delegate:(id)newDelegate; 31 - (void)mouseEntered:(NSEvent *)event; 32 - (void)mouseMoved:(NSEvent *)event; 33 - (void)mouseExited:(NSEvent *)event; 34 41 35 @end 42 43 //10.4: This handler responds to kEventMouseMoved. For Leopard-only, switch to NSTrackingArea.44 static OSStatus handleMouseMovedCarbonEvent(EventHandlerCallRef nextHandler, EventRef event, void *refcon);45 36 46 37 @implementation AISmoothTooltipTracker … … 56 47 view = [inView retain]; 57 48 delegate = inDelegate; 58 tooltipTrackingTag = -1;59 49 tooltipLocation = NSZeroPoint; 60 50 … … 70 60 71 61 [self installCursorRect]; 72 73 mouseMovedHandlerUPP = NewEventHandlerUPP(handleMouseMovedCarbonEvent);74 62 } 75 63 … … 83 71 #endif 84 72 85 RemoveEventHandler(mouseMovedHandler);86 DisposeEventHandlerUPP(mouseMovedHandlerUPP);87 88 73 [[NSNotificationCenter defaultCenter] removeObserver:self]; 89 74 90 75 [self removeCursorRect]; 91 [self _stopTrackingMouse];92 76 93 77 [view release]; view = nil; … … 99 83 { 100 84 if (delegate != inDelegate) { 101 [self _stopTrackingMouse];102 103 85 delegate = inDelegate; 104 86 } … … 122 104 123 105 [self removeCursorRect]; 124 [self _stopTrackingMouse];125 106 } 126 107 … … 143 124 - (void)installCursorRect 144 125 { 145 if ( tooltipTrackingTag == -1) {126 if (!trackingArea) { 146 127 NSRect trackingRect; 128 NSPoint mouseLocation = [[view window] mouseLocationOutsideOfEventStream]; 147 129 BOOL mouseInside; 148 130 … … 151 133 trackingRect.origin = NSMakePoint(0,0); 152 134 153 mouseInside = NSPointInRect([view convertPoint: [[view window] mouseLocationOutsideOfEventStream]fromView:[[view window] contentView]],135 mouseInside = NSPointInRect([view convertPoint:mouseLocation fromView:[[view window] contentView]], 154 136 trackingRect); 137 138 trackingArea = [[NSTrackingArea alloc] initWithRect:trackingRect 139 options:(NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved) | NSTrackingActiveAlways | NSTrackingInVisibleRect 140 owner:self 141 userInfo:nil]; 142 [view addTrackingArea:trackingArea]; 143 155 144 #if LOG_TRACKING_INFO 156 145 NSLog(@"%s: mouse location: %@; trackingRect: %@; mouseInside: %@", __PRETTY_FUNCTION__, NSStringFromPoint([view convertPoint:[[view window] mouseLocationOutsideOfEventStream] fromView:[[view window] contentView]]), NSStringFromRect(trackingRect), mouseInside ? @"YES" : @"NO"); 157 146 #endif 158 tooltipTrackingTag = [view addTrackingRect:trackingRect owner:self userData:nil assumeInside:mouseInside]; 159 160 #if LOG_TRACKING_INFO 161 NSLog(@"[%@ installCursorRect] addTrackingRect %@ on %@ in %@: tag = %i",self,NSStringFromRect(trackingRect),view,[view window],tooltipTrackingTag); 162 #endif 163 //If the mouse is already inside, begin tracking the mouse immediately 164 if ([[view window] isVisible] && mouseInside) [self _startTrackingMouse]; 147 148 //If the mouse is already inside, NSTrackingArea won't send us a mouse-entered event, so we need to forge one. 149 if ([[view window] isVisible] && mouseInside) { 150 struct UnsignedWide microsecondsSinceStartup; 151 Microseconds(µsecondsSinceStartup); 152 153 NSEvent *event = [NSEvent mouseEventWithType:NSMouseEntered 154 location:mouseLocation 155 modifierFlags:0 156 timestamp:UnsignedWideToUInt64(microsecondsSinceStartup) 157 windowNumber:[[view window] windowNumber] 158 context:[NSGraphicsContext currentContext] 159 eventNumber:0 160 clickCount:0 161 pressure:0.0f]; 162 [self mouseEntered:event]; 163 } 165 164 } 166 165 } … … 170 169 { 171 170 #if LOG_TRACKING_INFO 172 if (t ooltipTrackingTag != -1) {173 NSLog(@"[%@ removeCursorRect] Remove rect from %@ in %@ : t ag = %i",self,view,[view window],tooltipTrackingTag);171 if (trackingArea) { 172 NSLog(@"[%@ removeCursorRect] Remove rect from %@ in %@ : tracking area = %i",self,view,[view window], trackingArea); 174 173 } else { 175 174 NSLog(@"[%@ removeCursorRect] No rect to remove",self); … … 177 176 #endif 178 177 179 if (tooltipTrackingTag != -1) { 180 [view removeTrackingRect:tooltipTrackingTag]; 181 tooltipTrackingTag = -1; 182 [self _stopTrackingMouse]; 178 if (trackingArea) { 179 [view removeTrackingArea:trackingArea]; 180 trackingArea = nil; 183 181 } 184 182 } … … 201 199 // - mouseMoved: events do not work when Adium is in the background 202 200 #pragma mark Tooltips (Cursor movement) 203 //Mouse entered our list, begin tracking it's movement 201 202 //Mouse entered our list. Start the delay timer that will show the tooltip. 204 203 - (void)mouseEntered:(NSEvent *)theEvent 205 204 { … … 207 206 NSLog(@"+++ [%@: mouseEntered]", self); 208 207 #endif 209 [self _startTrackingMouse]; 210 } 211 212 //Mouse left our list, cease tracking 213 - (void)mouseExited:(NSEvent *)theEvent 214 { 215 #if LOG_TRACKING_INFO 216 NSLog(@"--- [%@: mouseExited]", self); 217 #endif 218 [self _stopTrackingMouse]; 219 } 220 221 //Start tracking mouse movement 222 - (void)_startTrackingMouse 223 { 224 #if LOG_TRACKING_INFO 225 NSLog(@"%s called; mouseMovedHandler is %p", __PRETTY_FUNCTION__, mouseMovedHandler); 226 #endif 227 228 if (!mouseMovedHandler) { 229 enum { numTypeSpecs = 1 }; 230 struct EventTypeSpec typeSpecs[numTypeSpecs] = { 231 { kEventClassMouse, kEventMouseMoved } 232 }; 233 WindowRef theWindow = [[view window] windowRef]; 234 235 OSStatus err = InstallWindowEventHandler(theWindow, mouseMovedHandlerUPP, numTypeSpecs, typeSpecs, /*refcon*/ (void *)self, (EventHandlerRef *)&mouseMovedHandler); 236 NSAssert3(err == noErr, @"%s: InstallWindowEventHandler returned %i (%s)", __PRETTY_FUNCTION__, err, GetMacOSStatusCommentString(err)); 237 #if LOG_TRACKING_INFO 238 NSLog(@"%s: Installed window event handler on %p (%p)", __PRETTY_FUNCTION__, theWindow, [[view window] windowRef]); 239 #endif 240 241 NSValue *initialMouseLocation = [NSValue valueWithPoint:[NSEvent mouseLocation]]; 242 tooltipDelayTimer = [[NSTimer scheduledTimerWithTimeInterval:TOOL_TIP_DELAY 243 target:self 244 selector:@selector(delayedShowTooltip:) 245 userInfo:[NSMutableDictionary dictionaryWithObject:initialMouseLocation forKey:MOUSE_LOCATION_KEY] 246 repeats:NO] retain]; 247 #if LOG_TRACKING_INFO 248 NSLog(@"%s: Scheduled timer %@ for %f seconds from now", __PRETTY_FUNCTION__, tooltipDelayTimer, TOOL_TIP_DELAY); 249 #endif 250 } 251 } 252 253 //Stop tracking mouse movement 254 - (void)_stopTrackingMouse 255 { 256 #if LOG_TRACKING_INFO 257 NSLog(@"%s called; mouseMovedHandler is %p", __PRETTY_FUNCTION__, mouseMovedHandler); 258 #endif 259 260 //Invalidate tracking 261 if (mouseMovedHandler) { 262 #if LOG_TRACKING_INFO 263 NSLog(@"%s: Hiding tooltip! delegate is %@", __PRETTY_FUNCTION__, delegate); 264 #endif 265 //Hide the tooltip before releasing the timer, as the timer may be the last object retaining self 266 //and we want to communicate with the delegate before a potential call to dealloc. 267 [self _hideTooltip]; 268 269 #if LOG_TRACKING_INFO 270 NSLog(@"%s: Removing event handler!", __PRETTY_FUNCTION__); 271 #endif 272 RemoveEventHandler((EventHandlerRef)mouseMovedHandler); 273 274 #if LOG_TRACKING_INFO 275 NSLog(@"%s: Invalidating and releasing tooltip delay timer %@!", __PRETTY_FUNCTION__, tooltipDelayTimer); 276 #endif 277 [tooltipDelayTimer invalidate]; 278 [tooltipDelayTimer release]; 279 tooltipDelayTimer = nil; 280 281 #if LOG_TRACKING_INFO 282 NSLog(@"%s: Using up surplus exclamation marks!", __PRETTY_FUNCTION__); 283 #endif 284 } 285 } 286 287 - (void)delayedShowTooltip:(NSTimer *)timer 288 { 289 #if LOG_TRACKING_INFO 290 NSLog(@"%s: Timer fired! Showing tooltip at %@", __PRETTY_FUNCTION__, NSStringFromPoint([[[timer userInfo] objectForKey:MOUSE_LOCATION_KEY] pointValue])); 291 #endif 292 [delegate showTooltipAtPoint:[[[timer userInfo] objectForKey:MOUSE_LOCATION_KEY] pointValue]]; 293 294 /* 295 #if LOG_TRACKING_INFO 296 NSLog(@"%s: Removing event handler %p", __PRETTY_FUNCTION__, mouseMovedHandler); 297 #endif 298 RemoveEventHandler((EventHandlerRef)mouseMovedHandler); 299 */ 300 301 #if LOG_TRACKING_INFO 302 NSLog(@"%s: Invalidating and releasing timer %@", __PRETTY_FUNCTION__, tooltipDelayTimer); 303 #endif 304 [tooltipDelayTimer invalidate]; 305 [tooltipDelayTimer release]; 306 tooltipDelayTimer = nil; 307 } 308 309 - (void)_hideTooltip 310 { 311 #if LOG_TRACKING_INFO 312 NSLog(@"%s: tooltipLocation is %@; delegate is %@", __PRETTY_FUNCTION__, NSStringFromPoint(tooltipLocation), delegate); 313 #endif 314 //If the tooltip was being shown before, hide it 315 if (!NSEqualPoints(tooltipLocation,NSZeroPoint)) { 316 lastMouseLocation = NSZeroPoint; 317 tooltipLocation = NSZeroPoint; 318 319 //Hide tooltip 320 [delegate hideTooltip]; 321 } 322 } 323 324 - (void)getPrivateVariablesTooltipDelayTimer:(out NSTimer **)outTooltipDelayTimer 325 lastMouseLocation:(out NSPoint *)outLastMouseLocation 326 tooltipLocation:(out NSPoint *)outTooltipLocation 327 delegate:(out id *)outDelegate 328 { 329 if (outTooltipDelayTimer) *outTooltipDelayTimer = tooltipDelayTimer; 330 if (outLastMouseLocation) *outLastMouseLocation = lastMouseLocation; 331 if (outTooltipLocation) *outTooltipLocation = tooltipLocation; 332 if (outDelegate) *outDelegate = delegate; 333 } 334 - (void)setPrivateVariablesLastMouseLocation:(NSPoint)newLastMouseLocation 335 tooltipLocation:(NSPoint)newTooltipLocation 336 delegate:(id)newDelegate 337 { 338 lastMouseLocation = newLastMouseLocation; 339 tooltipLocation = newTooltipLocation; 340 delegate = newDelegate; 341 } 342 343 @end 344 345 static OSStatus handleMouseMovedCarbonEvent(EventHandlerCallRef nextHandler, EventRef event, void *refcon) { 346 #if LOG_TRACKING_INFO 347 NSLog(@"%s called!", __PRETTY_FUNCTION__); 348 #endif 349 OSStatus err; 350 351 AISmoothTooltipTracker *self = (id)refcon; 352 NSView *view = [self view]; 353 NSWindow *theWindow = [view window]; 354 355 id delegate; 356 NSTimer *tooltipDelayTimer; 357 NSPoint lastMouseLocation; 358 NSPoint tooltipLocation; 359 [self getPrivateVariablesTooltipDelayTimer:&tooltipDelayTimer 360 lastMouseLocation:&lastMouseLocation 361 tooltipLocation:&tooltipLocation 362 delegate:&delegate]; 363 364 //Check whether kEventParamWindowRef is the window we're tracking. 365 WindowRef eventWindow = NULL; 366 err = GetEventParameter(event, kEventParamWindowRef, typeWindowRef, /*outActualType*/ NULL, sizeof(eventWindow), /*outActualSize*/ NULL, &eventWindow); 367 #if LOG_TRACKING_INFO 368 NSLog(@"%s: GetEventParameter, for kEventParamWindowRef, returned window %p and error %i (%s)", __PRETTY_FUNCTION__, eventWindow, err, GetMacOSStatusCommentString(err)); 369 #endif 370 NSCAssert3(err == noErr, @"%s: GetEventParameter, retrieving kEventParamWindowRef, returned error %i (%s)", __PRETTY_FUNCTION__, err, GetMacOSStatusCommentString(err)); 371 if (eventWindow != [theWindow windowRef]) { 372 return eventNotHandledErr; 373 } 374 375 //Check whether kEventParamWindowMouseLocation is within the frame of the view we're tracking. 376 HIPoint mouseLocationInWindow; 377 err = GetEventParameter(event, kEventParamWindowMouseLocation, typeHIPoint, /*outActualType*/ NULL, sizeof(mouseLocationInWindow), /*outActualSize*/ NULL, &mouseLocationInWindow); 378 #if LOG_TRACKING_INFO 379 NSLog(@"%s: GetEventParameter, for kEventParamWindowMouseLocation, returned point %@ and error %i (%s)", __PRETTY_FUNCTION__, NSStringFromPoint(NSPointFromCGPoint(mouseLocationInWindow)), err, GetMacOSStatusCommentString(err)); 380 #endif 381 NSCAssert3(err == noErr, @"%s: GetEventParameter, retrieving kEventParamWindowMouseLocation, returned error %i (%s)", __PRETTY_FUNCTION__, err, GetMacOSStatusCommentString(err)); 382 383 //Convert from HIToolbox's top-left origin to Cocoa's bottom-left origin. 384 NSPoint mouseLocation = { mouseLocationInWindow.x, (mouseLocationInWindow.y + [theWindow frame].size.height) * -1.0 }; 385 386 #if LOG_TRACKING_INFO 387 NSLog(@"%@: Visible: %i ; Point %@ in %@ = %i", self, 388 [theWindow isVisible], 389 NSStringFromPoint(mouseLocation), 390 NSStringFromRect([view frame]), 391 NSPointInRect(mouseLocation, [view frame])); 392 #endif 393 208 209 NSValue *initialMouseLocation = [NSValue valueWithPoint:[NSEvent mouseLocation]]; 210 tooltipDelayTimer = [[NSTimer scheduledTimerWithTimeInterval:TOOL_TIP_DELAY 211 target:self 212 selector:@selector(delayedShowTooltip:) 213 userInfo:[NSMutableDictionary dictionaryWithObject:initialMouseLocation forKey:MOUSE_LOCATION_KEY] 214 repeats:NO] retain]; 215 #if LOG_TRACKING_INFO 216 NSLog(@"%s: Scheduled timer %@ for %f seconds from now", __PRETTY_FUNCTION__, tooltipDelayTimer, TOOL_TIP_DELAY); 217 #endif 218 } 219 220 - (void)mouseMoved:(NSEvent *)event 221 { 222 NSPoint mouseLocation = [event locationInWindow]; 223 NSWindow *theWindow = [event window]; 224 225 #warning Assumes that (a) view is the content view of this window and (b) content-relative co-ordinates are the same as frame-relative co-ordinates. 394 226 if ([theWindow isVisible] && 395 227 NSPointInRect(mouseLocation, [view frame]) 396 228 ) { 229 //Convert our mouse location from window-relative (for comparison) to screen-relative (for real use). 230 mouseLocation = [theWindow convertBaseToScreen:mouseLocation]; 231 397 232 //If the tooltip is not yet on screen, and the mouse has moved, then reset the delay. 398 233 if (tooltipDelayTimer) { … … 427 262 [self _hideTooltip]; 428 263 } 429 430 [self setPrivateVariablesLastMouseLocation:lastMouseLocation 431 tooltipLocation:tooltipLocation 432 delegate:delegate]; 433 434 return err; 435 } 264 } 265 266 //Mouse left our list. Hide the tooltip. 267 - (void)mouseExited:(NSEvent *)theEvent 268 { 269 #if LOG_TRACKING_INFO 270 NSLog(@"--- [%@: mouseExited]", self); 271 #endif 272 273 [self _hideTooltip]; 274 } 275 276 - (void)delayedShowTooltip:(NSTimer *)timer 277 { 278 #if LOG_TRACKING_INFO 279 NSLog(@"%s: Timer fired! Showing tooltip at %@", __PRETTY_FUNCTION__, NSStringFromPoint([[[timer userInfo] objectForKey:MOUSE_LOCATION_KEY] pointValue])); 280 #endif 281 [delegate showTooltipAtPoint:[[[timer userInfo] objectForKey:MOUSE_LOCATION_KEY] pointValue]]; 282 283 /* 284 #if LOG_TRACKING_INFO 285 NSLog(@"%s: Removing event handler %p", __PRETTY_FUNCTION__, mouseMovedHandler); 286 #endif 287 RemoveEventHandler((EventHandlerRef)mouseMovedHandler); 288 */ 289 290 #if LOG_TRACKING_INFO 291 NSLog(@"%s: Invalidating and releasing timer %@", __PRETTY_FUNCTION__, tooltipDelayTimer); 292 #endif 293 [tooltipDelayTimer invalidate]; 294 [tooltipDelayTimer release]; 295 tooltipDelayTimer = nil; 296 } 297 298 - (void)_hideTooltip 299 { 300 #if LOG_TRACKING_INFO 301 NSLog(@"%s: tooltipLocation is %@; delegate is %@", __PRETTY_FUNCTION__, NSStringFromPoint(tooltipLocation), delegate); 302 #endif 303 //If the tooltip was being shown before, hide it 304 if (!NSEqualPoints(tooltipLocation,NSZeroPoint)) { 305 lastMouseLocation = NSZeroPoint; 306 tooltipLocation = NSZeroPoint; 307 308 //Hide tooltip 309 [delegate hideTooltip]; 310 } 311 } 312 313 @end