Removing the use of private CoreGraphics APIs to draw shadows, and replacing with...
[ITKit.git] / ITCoreImageWindowEffect.m
1 #import "ITCoreImageWindowEffect.h"
2 #import "ITTransientStatusWindow.h"
3 #import "ITCoreGraphicsHacks.h"
4 #import "ITCoreImageView.h"
5
6 @interface ITCoreImageWindowEffect (Private)
7 - (void)performAppearFromProgress:(float)progress effectTime:(float)time;
8 - (void)appearStep;
9 - (void)appearFinish;
10 - (void)performVanishFromProgress:(float)progress effectTime:(float)time;
11 - (void)vanishStep;
12 - (void)vanishFinish;
13
14 - (void)setupEffect;
15 @end
16
17 static BOOL _running = NO;
18
19 @implementation ITCoreImageWindowEffect
20
21 + (NSString *)effectName
22 {
23     return @"Ripple";
24 }
25
26 + (NSDictionary *)supportedPositions
27 {
28     return [NSDictionary dictionaryWithObjectsAndKeys:
29         [NSDictionary dictionaryWithObjectsAndKeys:
30             [NSNumber numberWithBool:YES], @"Left",
31             [NSNumber numberWithBool:YES], @"Center",
32             [NSNumber numberWithBool:YES], @"Right", nil] , @"Top" ,
33         [NSDictionary dictionaryWithObjectsAndKeys:
34             [NSNumber numberWithBool:YES], @"Left",
35             [NSNumber numberWithBool:YES], @"Center",
36             [NSNumber numberWithBool:YES], @"Right", nil] , @"Middle" ,
37         [NSDictionary dictionaryWithObjectsAndKeys:
38             [NSNumber numberWithBool:YES], @"Left",
39             [NSNumber numberWithBool:YES], @"Center",
40             [NSNumber numberWithBool:YES], @"Right", nil] , @"Bottom" , nil];
41 }
42
43 + (unsigned int)listOrder
44 {
45     return 800;
46 }
47
48 /*************************************************************************/
49 #pragma mark -
50 #pragma mark APPEAR METHODS
51 /*************************************************************************/
52
53 - (void)performAppear
54 {
55     __idle = NO;
56         
57     [self setWindowVisibility:ITWindowAppearingState];
58     [self performAppearFromProgress:0.0 effectTime:_effectTime];
59 }
60
61 - (void)performAppearFromProgress:(float)progress effectTime:(float)time
62 {
63     [_window setEffectProgress:progress];
64     _effectSpeed = (1.0 / (EFFECT_FPS * time));
65
66     if ( progress == 0.0 ) {
67         [_window setAlphaValue:0.0];
68     }
69         
70         [_window orderFront:self];
71         
72     _effectTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0 / EFFECT_FPS)
73                                                     target:self
74                                                   selector:@selector(appearStep)
75                                                   userInfo:nil
76                                                    repeats:YES];
77         [self setupEffect];
78 }
79
80 - (void)appearStep
81 {
82     float interFade = 0.0;
83     [_window setEffectProgress:([_window effectProgress] + _effectSpeed)];
84     [_window setEffectProgress:( ([_window effectProgress] < 1.0) ? [_window effectProgress] : 1.0)];
85     interFade = (( sin(([_window effectProgress] * pi) - (pi / 2)) + 1 ) / 2);
86     [_window setAlphaValue:interFade];
87
88     if ( [_window effectProgress] >= 1.0 ) {
89         [self appearFinish];
90     }
91 }
92
93 - (void)appearFinish
94 {
95     [_effectTimer invalidate];
96     _effectTimer = nil;
97
98     [self setWindowVisibility:ITWindowVisibleState];
99
100     __idle = YES;
101         
102     if ( __shouldReleaseWhenIdle ) {
103         [self release];
104     }
105 }
106
107 - (void)cancelAppear
108 {
109     [self setWindowVisibility:ITWindowVanishingState];
110
111     [_effectTimer invalidate];
112     _effectTimer = nil;
113
114     [self performVanishFromProgress:[_window effectProgress] effectTime:(_effectTime / 3.5)];
115 }
116
117
118 /*************************************************************************/
119 #pragma mark -
120 #pragma mark VANISH METHODS
121 /*************************************************************************/
122
123 - (void)performVanish
124 {
125         __idle = NO;
126         
127         [self setWindowVisibility:ITWindowVanishingState];
128         [self performVanishFromProgress:1.0 effectTime:_effectTime];
129 }
130
131 - (void)performVanishFromProgress:(float)progress effectTime:(float)time
132 {
133     [_window setEffectProgress:progress];
134     _effectSpeed = (1.0 / (EFFECT_FPS * time));
135     if ( progress == 1.0 ) {
136         [_window setAlphaValue:1.0];
137     }
138
139     [_window orderFront:self];
140         
141     _effectTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0 / EFFECT_FPS)
142                                                     target:self
143                                                   selector:@selector(vanishStep)
144                                                   userInfo:nil
145                                                    repeats:YES];
146         [self setupEffect];
147 }
148
149 - (void)vanishStep
150 {
151     float interFade = 1.0;
152     [_window setEffectProgress:([_window effectProgress] - _effectSpeed)];
153     [_window setEffectProgress:( ([_window effectProgress] > 0.0) ? [_window effectProgress] : 0.0)];
154     interFade = (( sin(([_window effectProgress] * pi) - (pi / 2)) + 1 ) / 2);
155     [_window setAlphaValue:interFade];
156
157     if ( [_window effectProgress] <= 0.0 ) {
158         [self vanishFinish];
159     }
160 }
161
162 - (void)vanishFinish
163 {
164     [_effectTimer invalidate];
165     _effectTimer = nil;
166     [_window orderOut:self];
167     [_window setAlphaValue:1.0];
168         
169     [self setWindowVisibility:ITWindowHiddenState];
170
171     __idle = YES;
172         
173     if ( __shouldReleaseWhenIdle ) {
174         [self release];
175     }
176 }
177
178 - (void)cancelVanish
179 {
180     [self setWindowVisibility:ITWindowVanishingState];
181
182     [_effectTimer invalidate];
183     _effectTimer = nil;
184
185     [self performAppearFromProgress:[_window effectProgress] effectTime:(_effectTime / 3.5)];
186 }
187
188 /*************************************************************************/
189 #pragma mark -
190 #pragma mark CORE IMAGE METHODS
191 /*************************************************************************/
192
193 - (void)setupEffect
194 {
195         NSRect rippleRect = [_window frame];
196         NSRect screenRect = [[_window screen] frame];
197         
198         if (_running) {
199                 //Short-circuit to avoid layering effects and crashing
200                 return;
201         }
202         
203         _running = YES;
204         _ripple = YES;
205         
206     rippleRect.origin.y = - (NSMaxY(rippleRect) - screenRect.size.height);
207         
208         _effectWindow = [[NSWindow alloc] initWithContentRect:_ripple ? NSInsetRect([_window frame], -200, -200) : [_window frame] styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
209         [_effectWindow setBackgroundColor:[NSColor clearColor]];
210         [_effectWindow setAlphaValue:1.0];
211         [_effectWindow setOpaque:NO];
212         [_effectWindow setHasShadow:NO];
213         [_effectWindow setContentView:[[ITCoreImageView alloc] initWithFrame:[_window frame]]];
214         [_effectWindow setLevel:[_window level]];
215         
216         [_effectWindow orderFrontRegardless];
217     [_window orderWindow:NSWindowAbove relativeTo:[_effectWindow windowNumber]];
218         
219         if (_ripple) {
220                 _effectFilter = [[CIFilter filterWithName:@"CIShapedWaterRipple"] retain];
221                 [_effectFilter setDefaults];
222                 [_effectFilter setValue:[NSNumber numberWithFloat:50.0] forKey:@"inputCornerRadius"];
223                 [_effectFilter setValue:[CIVector vectorWithX:rippleRect.origin.x Y:rippleRect.origin.y] forKey:@"inputPoint0"];
224                 [_effectFilter setValue:[CIVector vectorWithX:(rippleRect.origin.x + rippleRect.size.width) Y:(rippleRect.origin.y + rippleRect.size.height)] forKey:@"inputPoint1"];
225                 [_effectFilter setValue:[NSNumber numberWithFloat:0.0] forKey:@"inputPhase"];
226         } else {
227                 _effectFilter = [[CIFilter filterWithName:@"CIZoomBlur"] retain];
228                 [_effectFilter setDefaults];
229                 [_effectFilter setValue:[CIVector vectorWithX:(rippleRect.origin.x + rippleRect.size.width / 2) Y:(rippleRect.origin.y + rippleRect.size.height / 2)] forKey:@"inputCenter"];
230         }
231         
232         _windowFilter = [[CICGSFilter filterWithFilter:_effectFilter connectionID:[NSApp contextID]] retain];
233         [_windowFilter addToWindow:(CGSWindowID)[_effectWindow windowNumber] flags:1];
234         
235         [NSThread detachNewThreadSelector:@selector(runFilterAnimation:) toTarget:self withObject:self];
236 }
237
238 - (void)runFilterAnimation:(id)sender
239 {
240         NSAutoreleasePool *pool;
241     CICGSFilter *oldFilter;
242     CFAbsoluteTime startTime, time;
243         CGSWindowID windowNumber = (CGSWindowID)[_effectWindow windowNumber];
244     
245     pool = [[NSAutoreleasePool alloc] init];
246         
247     startTime = CFAbsoluteTimeGetCurrent();
248     time = CFAbsoluteTimeGetCurrent();
249         
250         while (time < (startTime + 2.5) && (time >= startTime)) {
251                 oldFilter = _windowFilter;
252                 if (_ripple) {
253                         [_effectFilter setValue:[NSNumber numberWithFloat:160*(time - startTime)] forKey:@"inputPhase"];
254                 } else {
255                         [_effectFilter setValue:[NSNumber numberWithFloat:5 * (time - startTime)] forKey:@"inputAmount"];
256                 }
257         _windowFilter = [[CICGSFilter filterWithFilter:_effectFilter connectionID:[NSApp contextID]] retain];
258         [_windowFilter addToWindow:windowNumber flags:1];
259                 [oldFilter removeFromWindow:windowNumber];
260                 [oldFilter release];
261         time = CFAbsoluteTimeGetCurrent();
262         }
263         
264         [_windowFilter removeFromWindow:windowNumber];
265         [_windowFilter release];
266     [_effectFilter release];
267     [_effectWindow orderOut:self];
268         [[_effectWindow contentView] release];
269     [_effectWindow release];
270     [pool release];
271         
272         _running = NO;
273 }
274
275 @end