• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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