[Metrics] Make MetricsStateManager take a callback param to check if UMA is enabled.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / sprite_view.mm
blobc4614219c784493efeb9a37a73bd5594c2ea4f44
1 // Copyright 2014 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.
5 #import "chrome/browser/ui/cocoa/sprite_view.h"
7 #import <QuartzCore/CAAnimation.h>
8 #import <QuartzCore/CATransaction.h>
10 #include "base/logging.h"
11 #include "ui/base/cocoa/animation_utils.h"
13 static const CGFloat kFrameDuration = 0.03;  // 30ms for each animation frame.
15 @implementation SpriteView
17 - (instancetype)initWithFrame:(NSRect)frame {
18   if (self = [super initWithFrame:frame]) {
19     // A layer-hosting view.
20     CALayer* layer = [CALayer layer];
21     [layer setDelegate:self];
22     [self setLayer:layer];
23     [self setWantsLayer:YES];
24   }
25   return self;
28 - (void)dealloc {
29   [[NSNotificationCenter defaultCenter] removeObserver:self];
30   [super dealloc];
33 - (void)viewWillMoveToWindow:(NSWindow*)newWindow {
34   if ([self window]) {
35     [[NSNotificationCenter defaultCenter]
36         removeObserver:self
37                   name:NSWindowWillMiniaturizeNotification
38                 object:[self window]];
39     [[NSNotificationCenter defaultCenter]
40         removeObserver:self
41                   name:NSWindowDidDeminiaturizeNotification
42                 object:[self window]];
43   }
45   if (newWindow) {
46     [[NSNotificationCenter defaultCenter]
47         addObserver:self
48            selector:@selector(updateAnimation:)
49                name:NSWindowWillMiniaturizeNotification
50              object:newWindow];
51     [[NSNotificationCenter defaultCenter]
52         addObserver:self
53            selector:@selector(updateAnimation:)
54                name:NSWindowDidDeminiaturizeNotification
55              object:newWindow];
56   }
59 - (void)viewDidMoveToWindow {
60   [self updateAnimation:nil];
63 - (void)updateAnimation:(NSNotification*)notification {
64   if (spriteAnimation_.get()) {
65     // Only animate the sprites if we are attached to a window, and that window
66     // is not currently minimized or in the middle of a minimize animation.
67     // http://crbug.com/350329
68     CALayer* layer = [self layer];
69     if ([self window] && ![[self window] isMiniaturized]) {
70       if ([layer animationForKey:[spriteAnimation_ keyPath]] == nil)
71         [layer addAnimation:spriteAnimation_.get()
72                      forKey:[spriteAnimation_ keyPath]];
73     } else {
74       [layer removeAnimationForKey:[spriteAnimation_ keyPath]];
75     }
76   }
79 - (void)setImage:(NSImage*)image {
80   ScopedCAActionDisabler disabler;
81   CALayer* layer = [self layer];
83   if (spriteAnimation_.get()) {
84     [layer removeAnimationForKey:[spriteAnimation_ keyPath]];
85     spriteAnimation_.reset();
86   }
88   [layer setContents:image];
90   if (image != nil) {
91     NSSize imageSize = [image size];
92     NSSize spriteSize = NSMakeSize(imageSize.height, imageSize.height);
93     [self setFrameSize:spriteSize];
95     const NSUInteger spriteCount = imageSize.width / spriteSize.width;
96     const CGFloat unitWidth = 1.0 / spriteCount;
98     // Show the first (leftmost) sprite.
99     [layer setContentsRect:CGRectMake(0, 0, unitWidth, 1.0)];
101     if (spriteCount > 1) {
102       // Animate the sprite offsets, we use a keyframe animation with discrete
103       // calculation mode to prevent interpolation.
104       NSMutableArray* xOffsets = [NSMutableArray arrayWithCapacity:spriteCount];
105       for (NSUInteger i = 0; i < spriteCount; ++i) {
106         [xOffsets addObject:@(i * unitWidth)];
107       }
108       CAKeyframeAnimation* animation =
109           [CAKeyframeAnimation animationWithKeyPath:@"contentsRect.origin.x"];
110       [animation setValues:xOffsets];
111       [animation setCalculationMode:kCAAnimationDiscrete];
112       [animation setRepeatCount:HUGE_VALF];
113       [animation setDuration:kFrameDuration * [xOffsets count]];
114       spriteAnimation_.reset([animation retain]);
116       [self updateAnimation:nil];
117     }
118   }
121 - (void)setImage:(NSImage*)image withToastAnimation:(BOOL)animate {
122   CALayer* layer = [self layer];
123   if (!animate || [layer contents] == nil) {
124     [self setImage:image];
125   } else {
126     // Animate away the icon.
127     CABasicAnimation* animation =
128         [CABasicAnimation animationWithKeyPath:@"position.y"];
129     CGFloat height = CGRectGetHeight([layer bounds]);
130     [animation setToValue:@(-height)];
131     [animation setDuration:kFrameDuration * height];
133     // Don't remove on completion to prevent the presentation layer from
134     // snapping back to the model layer's value.
135     // It will instead be removed when we add the return animation because they
136     // have the same key.
137     [animation setRemovedOnCompletion:NO];
138     [animation setFillMode:kCAFillModeForwards];
140     [CATransaction begin];
141     [CATransaction setCompletionBlock:^{
142         // At the end of the animation, change to the new image and animate
143         // it back to position.
144         [self setImage:image];
146         CABasicAnimation* reverseAnimation =
147             [CABasicAnimation animationWithKeyPath:[animation keyPath]];
148         [reverseAnimation setFromValue:[animation toValue]];
149         [reverseAnimation setToValue:[animation fromValue]];
150         [reverseAnimation setDuration:[animation duration]];
151         [layer addAnimation:reverseAnimation forKey:@"position"];
152     }];
153     [layer addAnimation:animation forKey:@"position"];
154     [CATransaction commit];
155   }
158 - (BOOL)layer:(CALayer*)layer
159     shouldInheritContentsScale:(CGFloat)scale
160                     fromWindow:(NSWindow*)window {
161   return YES;
164 @end