diff options
author | rsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-17 22:39:59 +0000 |
---|---|---|
committer | rsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-17 22:39:59 +0000 |
commit | 44717e30838708f2fd5c7d7b06450071a758d329 (patch) | |
tree | bc9962cb90091fdb28376e545772e292c3515ea0 /chrome | |
parent | 5d27d4fbd417ecfb707e95727b2a4e172898e3b2 (diff) | |
download | chromium_src-44717e30838708f2fd5c7d7b06450071a758d329.zip chromium_src-44717e30838708f2fd5c7d7b06450071a758d329.tar.gz chromium_src-44717e30838708f2fd5c7d7b06450071a758d329.tar.bz2 |
[Mac] Implement highlight-on-hover for the Wrench menu buttons.
R=pinkerton, shess, mark
BUG=51643
TEST=Open Wrench menu in sticky and non-sticky mode. Hover over buttons and they highlight.
Review URL: http://codereview.chromium.org/3137013
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@56430 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/cocoa/menu_tracked_button.h | 13 | ||||
-rw-r--r-- | chrome/browser/cocoa/menu_tracked_button.mm | 85 |
2 files changed, 97 insertions, 1 deletions
diff --git a/chrome/browser/cocoa/menu_tracked_button.h b/chrome/browser/cocoa/menu_tracked_button.h index df42255..00dc78a 100644 --- a/chrome/browser/cocoa/menu_tracked_button.h +++ b/chrome/browser/cocoa/menu_tracked_button.h @@ -12,6 +12,15 @@ // the custom view of an NSMenuItem. If the user opens the menu in a non-sticky // fashion (i.e. clicks, holds, and drags) and then releases the mouse over // a MenuTrackedButton, it will |-performClick:| itself. +// +// To create the hover state effects, there are two code paths. When the menu +// is opened sticky, a tracking rect produces mouse entered/exit events that +// allow for setting the cell's highlight property. When in a drag cycle, +// however, the only event received is |-mouseDragged:|. Therefore, a +// delayed selector is scheduled to poll the mouse location after each drag +// event. This checks if the user is still over the button after the drag +// events stop being sent, indicating either the user is hovering without +// movement or that the mouse is no longer over the receiver. @interface MenuTrackedButton : NSButton { @private // If the button received a |-mouseEntered:| event. This short-circuits the @@ -21,6 +30,10 @@ // Whether or not the user is in a click-drag-release event sequence. If so // and this receives a |-mouseUp:|, then this will click itself. BOOL tracking_; + + // In order to get hover effects when the menu is sticky-opened, a tracking + // rect needs to be installed on the button. + NSTrackingRectTag trackingTag_; } @end diff --git a/chrome/browser/cocoa/menu_tracked_button.mm b/chrome/browser/cocoa/menu_tracked_button.mm index 654e850..aabef67 100644 --- a/chrome/browser/cocoa/menu_tracked_button.mm +++ b/chrome/browser/cocoa/menu_tracked_button.mm @@ -4,29 +4,112 @@ #import "chrome/browser/cocoa/menu_tracked_button.h" +#include <cmath> + +@interface MenuTrackedButton (Private) +- (void)doHighlight:(BOOL)highlight; +- (void)checkMouseInRect; +- (NSRect)insetBounds; +- (BOOL)shouldHighlightOnHover; +@end + @implementation MenuTrackedButton +- (void)updateTrackingAreas { + [super updateTrackingAreas]; + [self removeTrackingRect:trackingTag_]; + trackingTag_ = [self addTrackingRect:NSInsetRect([self bounds], 1, 1) + owner:self + userData:NULL + assumeInside:NO]; +} + +- (void)viewDidMoveToWindow { + [self updateTrackingAreas]; + [self doHighlight:NO]; +} + - (void)mouseEntered:(NSEvent*)theEvent { - didEnter_ = YES; + if (!tracking_) { + didEnter_ = YES; + } + [self doHighlight:YES]; [super mouseEntered:theEvent]; } - (void)mouseExited:(NSEvent*)theEvent { didEnter_ = NO; tracking_ = NO; + [self doHighlight:NO]; [super mouseExited:theEvent]; } - (void)mouseDragged:(NSEvent*)theEvent { tracking_ = !didEnter_; + + NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil]; + BOOL highlight = NSPointInRect(point, [self insetBounds]); + [self doHighlight:highlight]; + + // If tracking in non-sticky mode, poll the mouse cursor to see if it is still + // over the button and thus needs to be highlighted. The delay is the + // smallest that still produces the effect while minimizing jank. Smaller + // values make the selector fire too close to immediately/now for the mouse to + // have moved off the receiver, and larger values produce lag. + if (tracking_ && [self shouldHighlightOnHover]) { + [self performSelector:@selector(checkMouseInRect) + withObject:nil + afterDelay:0.05 + inModes:[NSArray arrayWithObject:NSEventTrackingRunLoopMode]]; + } [super mouseDragged:theEvent]; } - (void)mouseUp:(NSEvent*)theEvent { + [self doHighlight:NO]; if (!tracking_) { return [super mouseUp:theEvent]; } [self performClick:self]; } +- (void)doHighlight:(BOOL)highlight { + if (![self shouldHighlightOnHover]) { + return; + } + [[self cell] setHighlighted:highlight]; + [self setNeedsDisplay]; +} + +// Checks if the user's current mouse location is over this button. If it is, +// the user is merely hovering here. If it is not, then disable the highlight. +// If the menu is opened in non-sticky mode, the button does not receive enter/ +// exit mouse events and thus polling is necessary. +- (void)checkMouseInRect { + NSPoint point = [NSEvent mouseLocation]; + point = [[self window] convertScreenToBase:point]; + point = [self convertPoint:point fromView:nil]; + if (!NSPointInRect(point, [self insetBounds])) { + [self doHighlight:NO]; + } +} + +// Returns the bounds of the receiver slightly inset to avoid highlighting both +// buttons in a pair that overlap. +- (NSRect)insetBounds { + return NSInsetRect([self bounds], 2, 1); +} + +- (BOOL)shouldHighlightOnHover { + // Apple does not define NSAppKitVersionNumber10_5 when using the 10.5 SDK. + // The Internets have come up with this solution. + #ifndef NSAppKitVersionNumber10_5 + #define NSAppKitVersionNumber10_5 949 + #endif + + // There's a cell drawing bug in 10.5 that was fixed on 10.6. Hover states + // look terrible due to this, so disable highlighting on 10.5. + return std::floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_5; +} + @end |