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