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)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]);
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
54 // http://crbug.com/31279
55 [self checkImageState];
58 - (void)setTrackingEnabled:(BOOL)enabled {
61 [[CrTrackingArea alloc] initWithRect:NSZeroRect
62 options:NSTrackingMouseEnteredAndExited |
63 NSTrackingActiveAlways |
64 NSTrackingInVisibleRect
67 [self addTrackingArea:trackingArea_.get()];
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)
80 if (trackingArea_.get()) {
81 self.hoverState = kHoverStateNone;
82 [self removeTrackingArea:trackingArea_.get()];
83 trackingArea_.reset(nil);
88 - (void)updateTrackingAreas {
89 [super updateTrackingAreas];
90 [self checkImageState];
93 - (void)checkImageState {
94 if (!trackingArea_.get())
97 // Update the button's state if the button has moved.
98 NSPoint mouseLoc = [[self window] mouseLocationOutsideOfEventStream];
99 mouseLoc = [self convertPoint:mouseLoc fromView:nil];
100 self.hoverState = NSPointInRect(mouseLoc, [self bounds]) ?
101 kHoverStateMouseOver : kHoverStateNone;
104 - (void)setHoverState:(HoverState)state {
106 [self setNeedsDisplay:YES];