blob: e763c0cdb69bf8281dd0b981dab4a676bbf7487a [file] [log] [blame]
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ui/base/cocoa/hover_button.h"
@implementation HoverButton
@synthesize hoverState = hoverState_;
- (id)initWithFrame:(NSRect)frameRect {
if ((self = [super initWithFrame:frameRect])) {
[self setTrackingEnabled:YES];
hoverState_ = kHoverStateNone;
[self updateTrackingAreas];
}
return self;
}
- (void)awakeFromNib {
[self setTrackingEnabled:YES];
self.hoverState = kHoverStateNone;
[self updateTrackingAreas];
}
- (void)dealloc {
[self setTrackingEnabled:NO];
[super dealloc];
}
- (void)mouseEntered:(NSEvent*)theEvent {
if (trackingArea_.get())
self.hoverState = kHoverStateMouseOver;
}
- (void)mouseExited:(NSEvent*)theEvent {
if (trackingArea_.get())
self.hoverState = kHoverStateNone;
}
- (void)mouseMoved:(NSEvent*)theEvent {
[self checkImageState];
}
- (void)mouseDown:(NSEvent*)theEvent {
self.hoverState = kHoverStateMouseDown;
// The hover button needs to hold onto itself here for a bit. Otherwise,
// it can be freed while |super mouseDown:| is in its loop, and the
// |checkImageState| call will crash.
// http://crbug.com/28220
base::scoped_nsobject<HoverButton> myself([self retain]);
[super mouseDown:theEvent];
// We need to check the image state after the mouseDown event loop finishes.
// It's possible that we won't get a mouseExited event if the button was
// moved under the mouse during tab resize, instead of the mouse moving over
// the button.
// http://crbug.com/31279
[self checkImageState];
}
- (void)setAccessibilityTitle:(NSString*)accessibilityTitle {
NSCell* cell = [self cell];
[cell accessibilitySetOverrideValue:accessibilityTitle
forAttribute:NSAccessibilityTitleAttribute];
}
- (void)setTrackingEnabled:(BOOL)enabled {
if (enabled) {
trackingArea_.reset(
[[CrTrackingArea alloc] initWithRect:NSZeroRect
options:NSTrackingMouseEnteredAndExited |
NSTrackingMouseMoved |
NSTrackingActiveAlways |
NSTrackingInVisibleRect
owner:self
userInfo:nil]);
[self addTrackingArea:trackingArea_.get()];
// If you have a separate window that overlaps the close button, and you
// move the mouse directly over the close button without entering another
// part of the tab strip, we don't get any mouseEntered event since the
// tracking area was disabled when we entered.
// Done with a delay of 0 because sometimes an event appears to be missed
// between the activation of the tracking area and the call to
// checkImageState resulting in the button state being incorrect.
[self performSelector:@selector(checkImageState)
withObject:nil
afterDelay:0];
} else {
if (trackingArea_.get()) {
self.hoverState = kHoverStateNone;
[self removeTrackingArea:trackingArea_.get()];
trackingArea_.reset(nil);
}
}
}
- (void)updateTrackingAreas {
[super updateTrackingAreas];
[self checkImageState];
}
- (void)checkImageState {
if (!trackingArea_.get())
return;
// Update the button's state if the button has moved.
NSPoint mouseLoc = [[self window] mouseLocationOutsideOfEventStream];
mouseLoc = [self convertPoint:mouseLoc fromView:nil];
self.hoverState = NSPointInRect(mouseLoc, [self bounds]) ?
kHoverStateMouseOver : kHoverStateNone;
}
- (void)setHoverState:(HoverState)state {
BOOL stateChanged = (hoverState_ != state);
hoverState_ = state;
[self setNeedsDisplay:stateChanged];
}
@end