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.
5 #import "ui/base/cocoa/hover_button.h"
7 @implementation HoverButton
9 @synthesize hoverState = hoverState_;
11 - (id)initWithFrame:(NSRect)frameRect {
12 if ((self = [super initWithFrame:frameRect])) {
13 [self setTrackingEnabled:YES];
14 hoverState_ = kHoverStateNone;
15 [self updateTrackingAreas];
20 - (void)awakeFromNib {
21 [self setTrackingEnabled:YES];
22 self.hoverState = kHoverStateNone;
23 [self updateTrackingAreas];
27 [self setTrackingEnabled:NO];
31 - (void)mouseEntered:(NSEvent*)theEvent {
32 if (trackingArea_.get())
33 self.hoverState = kHoverStateMouseOver;
36 - (void)mouseExited:(NSEvent*)theEvent {
37 if (trackingArea_.get())
38 self.hoverState = kHoverStateNone;
41 - (void)mouseMoved:(NSEvent*)theEvent {
42 [self checkImageState];
45 - (void)mouseDown:(NSEvent*)theEvent {
46 self.hoverState = kHoverStateMouseDown;
47 // The hover button needs to hold onto itself here for a bit. Otherwise,
48 // it can be freed while |super mouseDown:| is in its loop, and the
49 // |checkImageState| call will crash.
50 // http://crbug.com/28220
51 base::scoped_nsobject<HoverButton> myself([self retain]);
53 [super mouseDown:theEvent];
54 // We need to check the image state after the mouseDown event loop finishes.
55 // It's possible that we won't get a mouseExited event if the button was
56 // moved under the mouse during tab resize, instead of the mouse moving over
58 // http://crbug.com/31279
59 [self checkImageState];
62 - (void)setAccessibilityTitle:(NSString*)accessibilityTitle {
63 NSCell* cell = [self cell];
64 [cell accessibilitySetOverrideValue:accessibilityTitle
65 forAttribute:NSAccessibilityTitleAttribute];
68 - (void)setTrackingEnabled:(BOOL)enabled {
71 [[CrTrackingArea alloc] initWithRect:NSZeroRect
72 options:NSTrackingMouseEnteredAndExited |
73 NSTrackingMouseMoved |
74 NSTrackingActiveAlways |
75 NSTrackingInVisibleRect
78 [self addTrackingArea:trackingArea_.get()];
80 // If you have a separate window that overlaps the close button, and you
81 // move the mouse directly over the close button without entering another
82 // part of the tab strip, we don't get any mouseEntered event since the
83 // tracking area was disabled when we entered.
84 // Done with a delay of 0 because sometimes an event appears to be missed
85 // between the activation of the tracking area and the call to
86 // checkImageState resulting in the button state being incorrect.
87 [self performSelector:@selector(checkImageState)
91 if (trackingArea_.get()) {
92 self.hoverState = kHoverStateNone;
93 [self removeTrackingArea:trackingArea_.get()];
94 trackingArea_.reset(nil);
99 - (void)updateTrackingAreas {
100 [super updateTrackingAreas];
101 [self checkImageState];
104 - (void)checkImageState {
105 if (!trackingArea_.get())
108 // Update the button's state if the button has moved.
109 NSPoint mouseLoc = [[self window] mouseLocationOutsideOfEventStream];
110 mouseLoc = [self convertPoint:mouseLoc fromView:nil];
111 self.hoverState = NSPointInRect(mouseLoc, [self bounds]) ?
112 kHoverStateMouseOver : kHoverStateNone;
115 - (void)setHoverState:(HoverState)state {
116 BOOL stateChanged = (hoverState_ != state);
118 [self setNeedsDisplay:stateChanged];