blob: bfdf4bba6b323894dd4a8756d3cef46ff560598e (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
// Copyright (c) 2009 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/cocoa/throbber_view.h"
#include "base/logging.h"
static const float kAnimationIntervalSeconds = 0.03; // 30ms, same as windows
@interface ThrobberView(PrivateMethods)
- (void)animate;
@end
// A very simple object that is the target for the animation timer so that
// the view isn't. We do this to avoid retain cycles as the timer
// retains its target.
@interface TimerTarget : NSObject {
@private
ThrobberView* throbber_; // Weak, owns us
}
- (id)initWithThrobber:(ThrobberView*)view;
@end
@implementation TimerTarget
- (id)initWithThrobber:(ThrobberView*)view {
if ((self = [super init])) {
throbber_ = view;
}
return self;
}
- (void)animate:(NSTimer*)timer {
[throbber_ animate];
}
@end
@implementation ThrobberView
- (id)initWithFrame:(NSRect)frame image:(NSImage*)image {
if ((self = [super initWithFrame:frame])) {
[self setImage:image];
}
return self;
}
- (void)dealloc {
[timer_ invalidate];
[super dealloc];
}
- (void)removeFromSuperview {
[timer_ invalidate];
timer_ = nil;
[super removeFromSuperview];
}
// Called when the TimerTarget gets tickled by our timer. Increment the frame
// counter and mark as needing display.
- (void)animate {
animationFrame_ = ++animationFrame_ % numFrames_;
[self setNeedsDisplay:YES];
}
// Overridden to draw the appropriate frame in the image strip.
- (void)drawRect:(NSRect)rect {
float imageDimension = [image_ extent].size.height;
float xOffset = animationFrame_ * imageDimension;
NSRect sourceImageRect =
NSMakeRect(xOffset, 0, imageDimension, imageDimension);
[image_ drawInRect:[self bounds]
fromRect:sourceImageRect
operation:NSCompositeSourceOver
fraction:1.0];
}
// Stores the internal representation of the image from |image|. We use
// CoreImage for speed (though this doesn't seem to help perf issues). We
// validate that the image is of the appropriate ratio. If the image has more
// than one frame, restarts the timer.
- (void)setImage:(NSImage*)image {
// Reset the animation counter so there's no chance we are off the end.
animationFrame_ = 0;
[timer_ invalidate];
timer_ = nil;
// Ensure that the height divides evenly into the width. Cache the
// number of frames in the animation for later.
NSSize imageSize = [image size];
DCHECK(imageSize.height && imageSize.width);
if (!imageSize.height)
return;
DCHECK((int)imageSize.width % (int)imageSize.height == 0);
numFrames_ = (int)imageSize.width / (int)imageSize.height;
DCHECK(numFrames_);
// First check if we have a bitmap image rep and use it, otherwise fall
// back to creating one.
NSBitmapImageRep* rep = [[image representations] objectAtIndex:0];
if (![rep isKindOfClass:[NSBitmapImageRep class]]) {
[image lockFocus];
NSRect imageRect = NSMakeRect(0, 0, imageSize.width, imageSize.height);
rep = [[[NSBitmapImageRep alloc] initWithFocusedViewRect:imageRect]
autorelease];
[image unlockFocus];
}
image_.reset([[CIImage alloc] initWithBitmapImageRep:rep]);
if (numFrames_ > 1) {
// Start a timer for the animation frames.
target_.reset([[TimerTarget alloc] initWithThrobber:self]);
timer_ =
[NSTimer scheduledTimerWithTimeInterval:kAnimationIntervalSeconds
target:target_.get()
selector:@selector(animate:)
userInfo:nil
repeats:YES];
}
}
@end
|