| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/ui/cocoa/gradient_button_cell.h" | 5 #include "chrome/browser/ui/cocoa/gradient_button_cell.h" |
| 6 | 6 |
| 7 #include <cmath> | 7 #include <cmath> |
| 8 | 8 |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #import "base/mac/scoped_nsobject.h" | 10 #import "base/mac/scoped_nsobject.h" |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 43 | 43 |
| 44 // Note: due to a bug (?), drawWithFrame:inView: does not call | 44 // Note: due to a bug (?), drawWithFrame:inView: does not call |
| 45 // drawBorderAndFillForTheme::::: unless the mouse is inside. The net | 45 // drawBorderAndFillForTheme::::: unless the mouse is inside. The net |
| 46 // effect is that our "fade out" when the mouse leaves becaumes | 46 // effect is that our "fade out" when the mouse leaves becaumes |
| 47 // instantaneous. When I "fixed" it things looked horrible; the | 47 // instantaneous. When I "fixed" it things looked horrible; the |
| 48 // hover-overed bookmark button would stay highlit for 0.4 seconds | 48 // hover-overed bookmark button would stay highlit for 0.4 seconds |
| 49 // which felt like latency/lag. I'm leaving the "bug" in place for | 49 // which felt like latency/lag. I'm leaving the "bug" in place for |
| 50 // now so we don't suck. -jrg | 50 // now so we don't suck. -jrg |
| 51 static const NSTimeInterval kAnimationHideDuration = 0.4; | 51 static const NSTimeInterval kAnimationHideDuration = 0.4; |
| 52 | 52 |
| 53 static const NSTimeInterval kAnimationContinuousCycleDuration = 0.4; | |
| 54 | |
| 55 @implementation GradientButtonCell | 53 @implementation GradientButtonCell |
| 56 | 54 |
| 57 @synthesize hoverAlpha = hoverAlpha_; | 55 @synthesize hoverAlpha = hoverAlpha_; |
| 58 | 56 |
| 59 // For nib instantiations | 57 // For nib instantiations |
| 60 - (id)initWithCoder:(NSCoder*)decoder { | 58 - (id)initWithCoder:(NSCoder*)decoder { |
| 61 if ((self = [super initWithCoder:decoder])) { | 59 if ((self = [super initWithCoder:decoder])) { |
| 62 [self sharedInit]; | 60 [self sharedInit]; |
| 63 } | 61 } |
| 64 return self; | 62 return self; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 76 if (trackingArea_) { | 74 if (trackingArea_) { |
| 77 [[self controlView] removeTrackingArea:trackingArea_]; | 75 [[self controlView] removeTrackingArea:trackingArea_]; |
| 78 trackingArea_.reset(); | 76 trackingArea_.reset(); |
| 79 } | 77 } |
| 80 [super dealloc]; | 78 [super dealloc]; |
| 81 } | 79 } |
| 82 | 80 |
| 83 // Return YES if we are pulsing (towards another state or continuously). | 81 // Return YES if we are pulsing (towards another state or continuously). |
| 84 - (BOOL)pulsing { | 82 - (BOOL)pulsing { |
| 85 if ((pulseState_ == gradient_button_cell::kPulsingOn) || | 83 if ((pulseState_ == gradient_button_cell::kPulsingOn) || |
| 86 (pulseState_ == gradient_button_cell::kPulsingOff) || | 84 (pulseState_ == gradient_button_cell::kPulsingOff)) |
| 87 (pulseState_ == gradient_button_cell::kPulsingContinuous)) | |
| 88 return YES; | 85 return YES; |
| 89 return NO; | 86 return NO; |
| 90 } | 87 } |
| 91 | 88 |
| 92 // Perform one pulse step when animating a pulse. | 89 // Perform one pulse step when animating a pulse. |
| 93 - (void)performOnePulseStep { | 90 - (void)performOnePulseStep { |
| 94 NSTimeInterval thisUpdate = [NSDate timeIntervalSinceReferenceDate]; | 91 NSTimeInterval thisUpdate = [NSDate timeIntervalSinceReferenceDate]; |
| 95 NSTimeInterval elapsed = thisUpdate - lastHoverUpdate_; | 92 NSTimeInterval elapsed = thisUpdate - lastHoverUpdate_; |
| 96 CGFloat opacity = [self hoverAlpha]; | 93 CGFloat opacity = [self hoverAlpha]; |
| 97 | 94 |
| 98 // Update opacity based on state. | 95 // Update opacity based on state. |
| 99 // Adjust state if we have finished. | 96 // Adjust state if we have finished. |
| 100 switch (pulseState_) { | 97 switch (pulseState_) { |
| 101 case gradient_button_cell::kPulsingOn: | 98 case gradient_button_cell::kPulsingOn: |
| 102 opacity += elapsed / kAnimationShowDuration; | 99 opacity += elapsed / kAnimationShowDuration; |
| 103 if (opacity > 1.0) { | 100 if (opacity > 1.0) { |
| 104 [self setPulseState:gradient_button_cell::kPulsedOn]; | 101 [self setPulseState:gradient_button_cell::kPulsedOn]; |
| 105 return; | 102 return; |
| 106 } | 103 } |
| 107 break; | 104 break; |
| 108 case gradient_button_cell::kPulsingOff: | 105 case gradient_button_cell::kPulsingOff: |
| 109 opacity -= elapsed / kAnimationHideDuration; | 106 opacity -= elapsed / kAnimationHideDuration; |
| 110 if (opacity < 0.0) { | 107 if (opacity < 0.0) { |
| 111 [self setPulseState:gradient_button_cell::kPulsedOff]; | 108 [self setPulseState:gradient_button_cell::kPulsedOff]; |
| 112 return; | 109 return; |
| 113 } | 110 } |
| 114 break; | 111 break; |
| 115 case gradient_button_cell::kPulsingContinuous: | 112 case gradient_button_cell::kPulsingStuckOn: |
| 116 opacity += elapsed / kAnimationContinuousCycleDuration * pulseMultiplier_; | 113 outerStrokeAlphaMult_ = 1.0; |
| 117 if (opacity > 1.0) { | |
| 118 opacity = 1.0; | |
| 119 pulseMultiplier_ *= -1.0; | |
| 120 } else if (opacity < 0.0) { | |
| 121 opacity = 0.0; | |
| 122 pulseMultiplier_ *= -1.0; | |
| 123 } | |
| 124 outerStrokeAlphaMult_ = opacity; | |
| 125 break; | 114 break; |
| 126 default: | 115 default: |
| 127 NOTREACHED() << "unknown pulse state"; | 116 NOTREACHED() << "unknown pulse state"; |
| 128 } | 117 } |
| 129 | 118 |
| 130 // Update our control. | 119 // Update our control. |
| 131 lastHoverUpdate_ = thisUpdate; | 120 lastHoverUpdate_ = thisUpdate; |
| 132 [self setHoverAlpha:opacity]; | 121 [self setHoverAlpha:opacity]; |
| 133 [[self controlView] setNeedsDisplay:YES]; | 122 [[self controlView] setNeedsDisplay:YES]; |
| 134 | 123 |
| 135 // If our state needs it, keep going. | 124 // If our state needs it, keep going. |
| 136 if ([self pulsing]) { | 125 if ([self pulsing]) { |
| 137 [self performSelector:_cmd withObject:nil afterDelay:0.02]; | 126 [self performSelector:_cmd withObject:nil afterDelay:0.02]; |
| 138 } | 127 } |
| 139 } | 128 } |
| 140 | 129 |
| 141 - (gradient_button_cell::PulseState)pulseState { | 130 - (gradient_button_cell::PulseState)pulseState { |
| 142 return pulseState_; | 131 return pulseState_; |
| 143 } | 132 } |
| 144 | 133 |
| 145 // Set the pulsing state. This can either set the pulse to on or off | 134 // Set the pulsing state. This can either set the pulse to on or off |
| 146 // immediately (e.g. kPulsedOn, kPulsedOff) or initiate an animated | 135 // immediately (e.g. kPulsedOn, kPulsedOff) or initiate an animated |
| 147 // state change. | 136 // state change. |
| 148 - (void)setPulseState:(gradient_button_cell::PulseState)pstate { | 137 - (void)setPulseState:(gradient_button_cell::PulseState)pstate { |
| 149 pulseState_ = pstate; | 138 pulseState_ = pstate; |
| 150 pulseMultiplier_ = 0.0; | |
| 151 [NSObject cancelPreviousPerformRequestsWithTarget:self]; | 139 [NSObject cancelPreviousPerformRequestsWithTarget:self]; |
| 152 lastHoverUpdate_ = [NSDate timeIntervalSinceReferenceDate]; | 140 lastHoverUpdate_ = [NSDate timeIntervalSinceReferenceDate]; |
| 153 | 141 |
| 154 switch (pstate) { | 142 switch (pstate) { |
| 155 case gradient_button_cell::kPulsedOn: | 143 case gradient_button_cell::kPulsedOn: |
| 156 case gradient_button_cell::kPulsedOff: | 144 case gradient_button_cell::kPulsedOff: |
| 157 outerStrokeAlphaMult_ = 1.0; | 145 outerStrokeAlphaMult_ = 1.0; |
| 158 [self setHoverAlpha:((pulseState_ == gradient_button_cell::kPulsedOn) ? | 146 [self setHoverAlpha:((pulseState_ == gradient_button_cell::kPulsedOn) ? |
| 159 1.0 : 0.0)]; | 147 1.0 : 0.0)]; |
| 160 [[self controlView] setNeedsDisplay:YES]; | 148 [[self controlView] setNeedsDisplay:YES]; |
| 161 break; | 149 break; |
| 162 case gradient_button_cell::kPulsingOn: | 150 case gradient_button_cell::kPulsingOn: |
| 163 case gradient_button_cell::kPulsingOff: | 151 case gradient_button_cell::kPulsingOff: |
| 164 outerStrokeAlphaMult_ = 1.0; | 152 outerStrokeAlphaMult_ = 1.0; |
| 165 // Set initial value then engage timer. | 153 // Set initial value then engage timer. |
| 166 [self setHoverAlpha:((pulseState_ == gradient_button_cell::kPulsingOn) ? | 154 [self setHoverAlpha:((pulseState_ == gradient_button_cell::kPulsingOn) ? |
| 167 0.0 : 1.0)]; | 155 0.0 : 1.0)]; |
| 168 [self performOnePulseStep]; | 156 [self performOnePulseStep]; |
| 169 break; | 157 break; |
| 170 case gradient_button_cell::kPulsingContinuous: | 158 case gradient_button_cell::kPulsingStuckOn: |
| 171 // Semantics of continuous pulsing are that we pulse independent | 159 // Semantics of continuous pulsing are that we pulse independent |
| 172 // of mouse position. | 160 // of mouse position. |
| 173 pulseMultiplier_ = 1.0; | |
| 174 [self performOnePulseStep]; | 161 [self performOnePulseStep]; |
| 175 break; | 162 break; |
| 176 default: | 163 default: |
| 177 CHECK(0); | 164 CHECK(0); |
| 178 break; | 165 break; |
| 179 } | 166 } |
| 180 } | 167 } |
| 181 | 168 |
| 182 - (void)safelyStopPulsing { | 169 - (void)safelyStopPulsing { |
| 183 [NSObject cancelPreviousPerformRequestsWithTarget:self]; | 170 [NSObject cancelPreviousPerformRequestsWithTarget:self]; |
| 184 } | 171 } |
| 185 | 172 |
| 186 - (void)setIsContinuousPulsing:(BOOL)continuous { | 173 - (void)setPulseIsStuckOn:(BOOL)on { |
| 187 if (!continuous && pulseState_ != gradient_button_cell::kPulsingContinuous) | 174 if (!on && pulseState_ != gradient_button_cell::kPulsingStuckOn) |
| 188 return; | 175 return; |
| 189 if (continuous) { | 176 if (on) { |
| 190 [self setPulseState:gradient_button_cell::kPulsingContinuous]; | 177 [self setPulseState:gradient_button_cell::kPulsingStuckOn]; |
| 191 } else { | 178 } else { |
| 192 [self setPulseState:(isMouseInside_ ? gradient_button_cell::kPulsedOn : | 179 [self setPulseState:(isMouseInside_ ? gradient_button_cell::kPulsedOn : |
| 193 gradient_button_cell::kPulsedOff)]; | 180 gradient_button_cell::kPulsedOff)]; |
| 194 } | 181 } |
| 195 } | 182 } |
| 196 | 183 |
| 197 - (BOOL)isContinuousPulsing { | 184 - (BOOL)isPulseStuckOn { |
| 198 return (pulseState_ == gradient_button_cell::kPulsingContinuous) ? | 185 return (pulseState_ == gradient_button_cell::kPulsingStuckOn) ? |
| 199 YES : NO; | 186 YES : NO; |
| 200 } | 187 } |
| 201 | 188 |
| 202 #if 1 | 189 #if 1 |
| 203 // If we are not continuously pulsing, perform a pulse animation to | 190 // If we are not continuously pulsing, perform a pulse animation to |
| 204 // reflect our new state. | 191 // reflect our new state. |
| 205 - (void)setMouseInside:(BOOL)flag animate:(BOOL)animated { | 192 - (void)setMouseInside:(BOOL)flag animate:(BOOL)animated { |
| 206 isMouseInside_ = flag; | 193 isMouseInside_ = flag; |
| 207 if (pulseState_ != gradient_button_cell::kPulsingContinuous) { | 194 if (pulseState_ != gradient_button_cell::kPulsingStuckOn) { |
| 208 if (animated) { | 195 if (animated) { |
| 209 // In Material Design, if the button is already fully on, don't pulse it | 196 // In Material Design, if the button is already fully on, don't pulse it |
| 210 // on again if the mouse is within its bounds. | 197 // on again if the mouse is within its bounds. |
| 211 if ([self tag] == [self isMaterialDesignButtonType] && | 198 if ([self tag] == [self isMaterialDesignButtonType] && |
| 212 isMouseInside_ && pulseState_ == gradient_button_cell::kPulsedOn) { | 199 isMouseInside_ && pulseState_ == gradient_button_cell::kPulsedOn) { |
| 213 return; | 200 return; |
| 214 } | 201 } |
| 215 | 202 |
| 216 [self setPulseState:(isMouseInside_ ? gradient_button_cell::kPulsingOn : | 203 [self setPulseState:(isMouseInside_ ? gradient_button_cell::kPulsingOn : |
| 217 gradient_button_cell::kPulsingOff)]; | 204 gradient_button_cell::kPulsingOff)]; |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 283 NSGradient* gradient = [[NSGradient alloc] initWithColorsAndLocations: | 270 NSGradient* gradient = [[NSGradient alloc] initWithColorsAndLocations: |
| 284 startColor, hoverAlpha * 0.33, | 271 startColor, hoverAlpha * 0.33, |
| 285 endColor, 1.0, nil]; | 272 endColor, 1.0, nil]; |
| 286 | 273 |
| 287 return [gradient autorelease]; | 274 return [gradient autorelease]; |
| 288 } | 275 } |
| 289 | 276 |
| 290 - (void)sharedInit { | 277 - (void)sharedInit { |
| 291 shouldTheme_ = YES; | 278 shouldTheme_ = YES; |
| 292 pulseState_ = gradient_button_cell::kPulsedOff; | 279 pulseState_ = gradient_button_cell::kPulsedOff; |
| 293 pulseMultiplier_ = 1.0; | |
| 294 outerStrokeAlphaMult_ = 1.0; | 280 outerStrokeAlphaMult_ = 1.0; |
| 295 gradient_.reset([[self gradientForHoverAlpha:0.0 isThemed:NO] retain]); | 281 gradient_.reset([[self gradientForHoverAlpha:0.0 isThemed:NO] retain]); |
| 296 } | 282 } |
| 297 | 283 |
| 298 - (void)setShouldTheme:(BOOL)shouldTheme { | 284 - (void)setShouldTheme:(BOOL)shouldTheme { |
| 299 shouldTheme_ = shouldTheme; | 285 shouldTheme_ = shouldTheme; |
| 300 } | 286 } |
| 301 | 287 |
| 302 - (NSImage*)overlayImage { | 288 - (NSImage*)overlayImage { |
| 303 return overlayImage_.get(); | 289 return overlayImage_.get(); |
| (...skipping 292 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 596 // Stroke the borders and appropriate fill gradient. If we're borderless, the | 582 // Stroke the borders and appropriate fill gradient. If we're borderless, the |
| 597 // only time we want to draw the inner gradient is if we're highlighted or if | 583 // only time we want to draw the inner gradient is if we're highlighted or if |
| 598 // we're drawing the focus ring manually. In Material Design, the "border" is | 584 // we're drawing the focus ring manually. In Material Design, the "border" is |
| 599 // actually a highlight, which should be drawn if | 585 // actually a highlight, which should be drawn if |
| 600 // |showsBorderOnlyWhileMouseInside| is true. | 586 // |showsBorderOnlyWhileMouseInside| is true. |
| 601 BOOL hasMaterialHighlight = | 587 BOOL hasMaterialHighlight = |
| 602 [self isMaterialDesignButtonType] && | 588 [self isMaterialDesignButtonType] && |
| 603 ![self showsBorderOnlyWhileMouseInside] && | 589 ![self showsBorderOnlyWhileMouseInside] && |
| 604 enabled; | 590 enabled; |
| 605 if (([self isBordered] && ![self showsBorderOnlyWhileMouseInside]) || | 591 if (([self isBordered] && ![self showsBorderOnlyWhileMouseInside]) || |
| 606 pressed || [self isMouseInside] || [self isContinuousPulsing] || | 592 pressed || [self isMouseInside] || [self isPulseStuckOn] || |
| 607 hasMaterialHighlight) { | 593 hasMaterialHighlight) { |
| 608 // When pulsing we want the bookmark to stand out a little more. | 594 // When pulsing we want the bookmark to stand out a little more. |
| 609 BOOL showClickedGradient = pressed || | 595 BOOL showClickedGradient = pressed || |
| 610 (pulseState_ == gradient_button_cell::kPulsingContinuous); | 596 (pulseState_ == gradient_button_cell::kPulsingStuckOn); |
| 611 BOOL showHighlightGradient = [self isHighlighted] || hasMaterialHighlight; | 597 BOOL showHighlightGradient = [self isHighlighted] || hasMaterialHighlight; |
| 612 | 598 |
| 613 [self drawBorderAndFillForTheme:themeProvider | 599 [self drawBorderAndFillForTheme:themeProvider |
| 614 controlView:controlView | 600 controlView:controlView |
| 615 innerPath:innerPath | 601 innerPath:innerPath |
| 616 showClickedGradient:showClickedGradient | 602 showClickedGradient:showClickedGradient |
| 617 showHighlightGradient:showHighlightGradient | 603 showHighlightGradient:showHighlightGradient |
| 618 hoverAlpha:[self hoverAlpha] | 604 hoverAlpha:[self hoverAlpha] |
| 619 active:active | 605 active:active |
| 620 cellFrame:cellFrame | 606 cellFrame:cellFrame |
| (...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 859 options:options | 845 options:options |
| 860 owner:self | 846 owner:self |
| 861 userInfo:nil]); | 847 userInfo:nil]); |
| 862 if (isMouseInside_ != mouseInView) { | 848 if (isMouseInside_ != mouseInView) { |
| 863 [self setMouseInside:mouseInView animate:NO]; | 849 [self setMouseInside:mouseInView animate:NO]; |
| 864 [controlView setNeedsDisplay:YES]; | 850 [controlView setNeedsDisplay:YES]; |
| 865 } | 851 } |
| 866 } | 852 } |
| 867 | 853 |
| 868 @end | 854 @end |
| OLD | NEW |