blob: 857e9d4fb4625542be03c05d61ae8f27d47f8e0d [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "chrome/browser/ui/cocoa/tabs/tab_spinner_view.h"
#include "base/mac/scoped_cftyperef.h"
#include "chrome/browser/themes/theme_properties.h"
#include "skia/ext/skia_utils_mac.h"
#include "ui/base/theme_provider.h"
#include "ui/gfx/geometry/angle_conversions.h"
#include "ui/native_theme/native_theme.h"
namespace {
constexpr CGFloat kDegrees90 = gfx::DegToRad(90.0f);
constexpr CGFloat kDegrees180 = gfx::DegToRad(180.0f);
constexpr CGFloat kDegrees270 = gfx::DegToRad(270.0f);
constexpr CGFloat kDegrees360 = gfx::DegToRad(360.0f);
constexpr CGFloat kWaitingStrokeAlpha = 0.5;
} // namespace
@implementation TabSpinnerView {
BOOL spinReverse_;
}
- (NSColor*)spinnerColor {
BOOL hasDarkTheme =
[[self window] respondsToSelector:@selector(hasDarkTheme)] &&
[[self window] hasDarkTheme];
if (hasDarkTheme) {
return spinReverse_ ? [[NSColor whiteColor]
colorWithAlphaComponent:kWaitingStrokeAlpha]
: [NSColor whiteColor];
}
const ui::ThemeProvider* theme = nullptr;
if ([[self window] respondsToSelector:@selector(themeProvider)]) {
theme = [[self window] themeProvider];
}
if (spinReverse_) {
if (theme) {
return theme->GetNSColor(ThemeProperties::COLOR_TAB_THROBBER_WAITING);
}
SkColor skWaitingColor =
ui::NativeTheme::GetInstanceForNativeUi()->GetSystemColor(
ui::NativeTheme::kColorId_ThrobberWaitingColor);
return skia::SkColorToSRGBNSColor(skWaitingColor);
}
return theme ? theme->GetNSColor(ThemeProperties::COLOR_TAB_THROBBER_SPINNING)
: [super spinnerColor];
}
- (CGFloat)arcStartAngle {
return spinReverse_ ? kDegrees270 : [super arcStartAngle];
}
- (CGFloat)arcEndAngleDelta {
return spinReverse_ ? kDegrees180 : [super arcEndAngleDelta];
}
- (CGFloat)arcLength {
// The reverse arc spans 90 degrees of circumference.
static CGFloat reverseArcLength =
kDegrees90 * ([SpinnerView arcUnitRadius] * 2);
return spinReverse_ ? reverseArcLength : [super arcLength];
}
- (void)initializeAnimation {
if (!spinReverse_) {
return [super initializeAnimation];
}
const CGFloat forwardArcRotationTime = [SpinnerView arcRotationTime];
const CGFloat reverseArcAnimationTime = forwardArcRotationTime / 2.0;
// Create the arc animation.
base::scoped_nsobject<CAKeyframeAnimation> arcAnimation(
[[CAKeyframeAnimation animationWithKeyPath:@"lineDashPhase"] retain]);
[arcAnimation
setTimingFunction:[CAMediaTimingFunction
functionWithName:kCAMediaTimingFunctionLinear]];
CGFloat scaleFactor = [self scaleFactor];
NSArray* animationValues = @[ @(-[self arcLength] * scaleFactor), @(0.0) ];
[arcAnimation setValues:animationValues];
NSArray* keyTimes = @[ @(0.0), @(1.0) ];
[arcAnimation setKeyTimes:keyTimes];
[arcAnimation setDuration:reverseArcAnimationTime];
[arcAnimation setFillMode:kCAFillModeForwards];
CAAnimationGroup* group = [CAAnimationGroup animation];
[group setDuration:reverseArcAnimationTime];
[group setFillMode:kCAFillModeForwards];
[group setAnimations:@[ arcAnimation ]];
[self setSpinnerAnimation:group];
// Finally, create an animation that rotates the entire spinner layer.
CABasicAnimation* rotationAnimation =
[CABasicAnimation animationWithKeyPath:@"transform.rotation"];
[rotationAnimation setFromValue:@0];
[rotationAnimation setToValue:@(kDegrees360)];
// Start the rotation once the stroke animation has completed.
[rotationAnimation
setBeginTime:CACurrentMediaTime() + reverseArcAnimationTime];
[rotationAnimation setDuration:forwardArcRotationTime];
[rotationAnimation setFillMode:kCAFillModeForwards];
[rotationAnimation setRepeatCount:HUGE_VALF];
[self setRotationAnimation:rotationAnimation];
}
- (void)setSpinDirection:(SpinDirection)newSpinDirection {
BOOL spinReverse = (newSpinDirection == SpinDirection::REVERSE);
if (spinReverse == spinReverse_) {
return;
}
spinReverse_ = spinReverse;
[self restartAnimation];
}
// ThemedWindowDrawing implementation.
- (void)windowDidChangeTheme {
// Make sure the spinner colors matches the current theme.
[self updateSpinnerColor];
}
- (void)windowDidChangeActive {
}
@end