Index: chrome/browser/cocoa/gradient_button_cell.mm |
=================================================================== |
--- chrome/browser/cocoa/gradient_button_cell.mm (revision 53704) |
+++ chrome/browser/cocoa/gradient_button_cell.mm (working copy) |
@@ -27,51 +27,19 @@ |
innerFrame:(NSRect*)returnInnerFrame |
innerPath:(NSBezierPath**)returnInnerPath |
clipPath:(NSBezierPath**)returnClipPath; |
+ |
+ |
@end |
+ |
static const NSTimeInterval kAnimationShowDuration = 0.2; |
static const NSTimeInterval kAnimationHideDuration = 0.4; |
+static const NSTimeInterval kAnimationContinuousCycleDuration = 0.4; |
@implementation GradientButtonCell |
+ |
@synthesize hoverAlpha = hoverAlpha_; |
-- (void)adjustHoverValue { |
- NSTimeInterval thisUpdate = [NSDate timeIntervalSinceReferenceDate]; |
- |
- NSTimeInterval elapsed = thisUpdate - lastHoverUpdate_; |
- |
- CGFloat opacity = [self hoverAlpha]; |
- if (isMouseInside_) { |
- opacity += elapsed / kAnimationShowDuration; |
- } else { |
- opacity -= elapsed / kAnimationHideDuration; |
- } |
- |
- if (!isMouseInside_ && opacity < 0) { |
- opacity = 0; |
- } else if (isMouseInside_ && opacity > 1) { |
- opacity = 1; |
- } else { |
- [self performSelector:_cmd withObject:nil afterDelay:0.02]; |
- } |
- lastHoverUpdate_ = thisUpdate; |
- [self setHoverAlpha:opacity]; |
- |
- [[self controlView] setNeedsDisplay:YES]; |
-} |
- |
-- (void)setMouseInside:(BOOL)flag animate:(BOOL)animated { |
- isMouseInside_ = flag; |
- if (animated) { |
- lastHoverUpdate_ = [NSDate timeIntervalSinceReferenceDate]; |
- [self adjustHoverValue]; |
- } else { |
- [NSObject cancelPreviousPerformRequestsWithTarget:self]; |
- [self setHoverAlpha:flag ? 1.0 : 0.0]; |
- } |
- [[self controlView] setNeedsDisplay:YES]; |
-} |
- |
// For nib instantiations |
- (id)initWithCoder:(NSCoder*)decoder { |
if ((self = [super initWithCoder:decoder])) { |
@@ -96,6 +64,140 @@ |
[super dealloc]; |
} |
+// Return YES if we are pulsing (towards another state or continuously). |
+- (BOOL)pulsing { |
+ if ((pulseState_ == gradient_button_cell::kPulsingOn) || |
+ (pulseState_ == gradient_button_cell::kPulsingOff) || |
+ (pulseState_ == gradient_button_cell::kPulsingContinuous)) |
+ return YES; |
+ return NO; |
+} |
+ |
+// Perform one pulse step when animating a pulse. |
+- (void)performOnePulseStep { |
+ NSTimeInterval thisUpdate = [NSDate timeIntervalSinceReferenceDate]; |
+ NSTimeInterval elapsed = thisUpdate - lastHoverUpdate_; |
+ CGFloat opacity = [self hoverAlpha]; |
+ |
+ // Update opacity based on state. |
+ // Adjust state if we have finished. |
+ switch (pulseState_) { |
+ case gradient_button_cell::kPulsingOn: |
+ opacity += elapsed / kAnimationShowDuration; |
+ if (opacity > 1.0) { |
+ [self setPulseState:gradient_button_cell::kPulsedOn]; |
+ return; |
+ } |
+ break; |
+ case gradient_button_cell::kPulsingOff: |
+ opacity -= elapsed / kAnimationHideDuration; |
+ if (opacity < 0.0) { |
+ [self setPulseState:gradient_button_cell::kPulsedOff]; |
+ return; |
+ } |
+ break; |
+ case gradient_button_cell::kPulsingContinuous: |
+ opacity += elapsed / kAnimationContinuousCycleDuration * pulseMultiplier_; |
+ if (opacity > 1.0) { |
+ opacity = 1.0; |
+ pulseMultiplier_ *= -1.0; |
+ } else if (opacity < 0.0) { |
+ opacity = 0.0; |
+ pulseMultiplier_ *= -1.0; |
+ } |
+ outerStrokeAlphaMult_ = opacity; |
+ break; |
+ default: |
+ NOTREACHED() << "unknown pulse state"; |
+ } |
+ |
+ // Update our control. |
+ lastHoverUpdate_ = thisUpdate; |
+ [self setHoverAlpha:opacity]; |
+ [[self controlView] setNeedsDisplay:YES]; |
+ |
+ // If our state needs it, keep going. |
+ if ([self pulsing]) { |
+ [self performSelector:_cmd withObject:nil afterDelay:0.02]; |
+ } |
+} |
+ |
+- (gradient_button_cell::PulseState)pulseState { |
+ return pulseState_; |
+} |
+ |
+// Set the pulsing state. This can either set the pulse to on or off |
+// immediately (e.g. kPulsedOn, kPulsedOff) or initiate an animated |
+// state change. |
+- (void)setPulseState:(gradient_button_cell::PulseState)pstate { |
+ pulseState_ = pstate; |
+ pulseMultiplier_ = 0.0; |
+ [NSObject cancelPreviousPerformRequestsWithTarget:self]; |
+ lastHoverUpdate_ = [NSDate timeIntervalSinceReferenceDate]; |
+ |
+ switch (pstate) { |
+ case gradient_button_cell::kPulsedOn: |
+ case gradient_button_cell::kPulsedOff: |
+ outerStrokeAlphaMult_ = 1.0; |
+ [self setHoverAlpha:((pulseState_ == gradient_button_cell::kPulsedOn) ? |
+ 1.0 : 0.0)]; |
+ [[self controlView] setNeedsDisplay:YES]; |
+ break; |
+ case gradient_button_cell::kPulsingOn: |
+ case gradient_button_cell::kPulsingOff: |
+ outerStrokeAlphaMult_ = 1.0; |
+ // Set initial value then engage timer. |
+ [self setHoverAlpha:((pulseState_ == gradient_button_cell::kPulsingOn) ? |
+ 0.0 : 1.0)]; |
+ [self performOnePulseStep]; |
+ break; |
+ case gradient_button_cell::kPulsingContinuous: |
+ // Semantics of continuous pulsing are that we pulse independent |
+ // of mouse position. |
+ pulseMultiplier_ = 1.0; |
+ [self performOnePulseStep]; |
+ break; |
+ default: |
+ CHECK(0); |
+ break; |
+ } |
+} |
+ |
+- (void)safelyStopPulsing { |
+ [NSObject cancelPreviousPerformRequestsWithTarget:self]; |
+} |
+ |
+- (void)setIsContinuousPulsing:(BOOL)continuous { |
+ if (!continuous && pulseState_ != gradient_button_cell::kPulsingContinuous) |
+ return; |
+ if (continuous) { |
+ [self setPulseState:gradient_button_cell::kPulsingContinuous]; |
+ } else { |
+ [self setPulseState:(isMouseInside_ ? gradient_button_cell::kPulsedOn : |
+ gradient_button_cell::kPulsedOff)]; |
+ } |
+} |
+ |
+- (BOOL)isContinuousPulsing { |
+ return (pulseState_ == gradient_button_cell::kPulsingContinuous) ? |
+ YES : NO; |
+} |
+ |
+// If we are not continuously pulsing, perform a pulse animation to |
+// reflect our new state. |
+- (void)setMouseInside:(BOOL)flag animate:(BOOL)animated { |
+ isMouseInside_ = flag; |
+ if (pulseState_ != gradient_button_cell::kPulsingContinuous) { |
+ if (animated) { |
+ [self setPulseState:(isMouseInside_ ? gradient_button_cell::kPulsingOn : |
+ gradient_button_cell::kPulsingOff)]; |
+ } else { |
+ [self setPulseState:(isMouseInside_ ? gradient_button_cell::kPulsedOn : |
+ gradient_button_cell::kPulsedOff)]; |
+ } |
+ } |
+} |
+ |
- (NSGradient*)gradientForHoverAlpha:(CGFloat)hoverAlpha |
isThemed:(BOOL)themed { |
CGFloat startAlpha = 0.6 + 0.3 * hoverAlpha; |
@@ -121,6 +223,9 @@ |
- (void)sharedInit { |
shouldTheme_ = YES; |
+ pulseState_ = gradient_button_cell::kPulsedOff; |
+ pulseMultiplier_ = 1.0; |
+ outerStrokeAlphaMult_ = 1.0; |
gradient_.reset([[self gradientForHoverAlpha:0.0 isThemed:NO] retain]); |
} |
@@ -283,12 +388,15 @@ |
// Draw the outer stroke. |
NSColor* strokeColor = nil; |
if (showClickedGradient) { |
- strokeColor = [NSColor colorWithCalibratedWhite:0.0 alpha:0.3]; |
+ strokeColor = [NSColor |
+ colorWithCalibratedWhite:0.0 |
+ alpha:0.3 * outerStrokeAlphaMult_]; |
} else { |
strokeColor = themeProvider ? themeProvider->GetNSColor( |
active ? BrowserThemeProvider::COLOR_TOOLBAR_BUTTON_STROKE : |
BrowserThemeProvider::COLOR_TOOLBAR_BUTTON_STROKE_INACTIVE, |
- true) : [NSColor colorWithCalibratedWhite:0.0 alpha:0.6]; |
+ true) : [NSColor colorWithCalibratedWhite:0.0 |
+ alpha:0.6 * outerStrokeAlphaMult_]; |
} |
[strokeColor setStroke]; |
@@ -366,12 +474,17 @@ |
// the only time we want to draw the inner gradient is if we're highlighted. |
if (([self isBordered] && ![self showsBorderOnlyWhileMouseInside]) || |
pressed || |
- [self isMouseInside]) { |
+ [self isMouseInside] || |
+ [self pulsing]) { |
+ // When pulsing we want the bookmark to stand out a little more. |
+ BOOL showClickedGradient = pressed || |
+ (pulseState_ == gradient_button_cell::kPulsingContinuous); |
+ |
[self drawBorderAndFillForTheme:themeProvider |
controlView:controlView |
innerPath:innerPath |
- showClickedGradient:pressed |
+ showClickedGradient:showClickedGradient |
showHighlightGradient:[self isHighlighted] |
hoverAlpha:[self hoverAlpha] |
active:active |