Home | History | Annotate | Download | only in cocoa
      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