1 // Copyright (c) 2010 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 #import "chrome/browser/ui/cocoa/hover_button.h" 6 7 @implementation HoverButton 8 9 - (id)initWithFrame:(NSRect)frameRect { 10 if ((self = [super initWithFrame:frameRect])) { 11 [self setTrackingEnabled:YES]; 12 hoverState_ = kHoverStateNone; 13 [self updateTrackingAreas]; 14 } 15 return self; 16 } 17 18 - (void)awakeFromNib { 19 [self setTrackingEnabled:YES]; 20 hoverState_ = kHoverStateNone; 21 [self updateTrackingAreas]; 22 } 23 24 - (void)dealloc { 25 [self setTrackingEnabled:NO]; 26 [super dealloc]; 27 } 28 29 - (void)mouseEntered:(NSEvent*)theEvent { 30 hoverState_ = kHoverStateMouseOver; 31 [self setNeedsDisplay:YES]; 32 } 33 34 - (void)mouseExited:(NSEvent*)theEvent { 35 hoverState_ = kHoverStateNone; 36 [self setNeedsDisplay:YES]; 37 } 38 39 - (void)mouseDown:(NSEvent*)theEvent { 40 hoverState_ = kHoverStateMouseDown; 41 [self setNeedsDisplay:YES]; 42 // The hover button needs to hold onto itself here for a bit. Otherwise, 43 // it can be freed while |super mouseDown:| is in it's loop, and the 44 // |checkImageState| call will crash. 45 // http://crbug.com/28220 46 scoped_nsobject<HoverButton> myself([self retain]); 47 48 [super mouseDown:theEvent]; 49 // We need to check the image state after the mouseDown event loop finishes. 50 // It's possible that we won't get a mouseExited event if the button was 51 // moved under the mouse during tab resize, instead of the mouse moving over 52 // the button. 53 // http://crbug.com/31279 54 [self checkImageState]; 55 } 56 57 - (void)setTrackingEnabled:(BOOL)enabled { 58 if (enabled) { 59 trackingArea_.reset( 60 [[NSTrackingArea alloc] initWithRect:[self bounds] 61 options:NSTrackingMouseEnteredAndExited | 62 NSTrackingActiveAlways 63 owner:self 64 userInfo:nil]); 65 [self addTrackingArea:trackingArea_.get()]; 66 67 // If you have a separate window that overlaps the close button, and you 68 // move the mouse directly over the close button without entering another 69 // part of the tab strip, we don't get any mouseEntered event since the 70 // tracking area was disabled when we entered. 71 [self checkImageState]; 72 } else { 73 if (trackingArea_.get()) { 74 [self removeTrackingArea:trackingArea_.get()]; 75 trackingArea_.reset(nil); 76 } 77 } 78 } 79 80 - (void)updateTrackingAreas { 81 [super updateTrackingAreas]; 82 [self checkImageState]; 83 } 84 85 - (void)checkImageState { 86 if (!trackingArea_.get()) 87 return; 88 89 // Update the button's state if the button has moved. 90 NSPoint mouseLoc = [[self window] mouseLocationOutsideOfEventStream]; 91 mouseLoc = [self convertPoint:mouseLoc fromView:nil]; 92 hoverState_ = NSPointInRect(mouseLoc, [self bounds]) ? 93 kHoverStateMouseOver : kHoverStateNone; 94 [self setNeedsDisplay:YES]; 95 } 96 97 @end 98