|
1 /* |
|
2 * Copyright (C) 2009 Apple Inc. All rights reserved. |
|
3 * |
|
4 * Redistribution and use in source and binary forms, with or without |
|
5 * modification, are permitted provided that the following conditions |
|
6 * are met: |
|
7 * 1. Redistributions of source code must retain the above copyright |
|
8 * notice, this list of conditions and the following disclaimer. |
|
9 * 2. Redistributions in binary form must reproduce the above copyright |
|
10 * notice, this list of conditions and the following disclaimer in the |
|
11 * documentation and/or other materials provided with the distribution. |
|
12 * |
|
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
|
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
|
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
|
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
|
23 * THE POSSIBILITY OF SUCH DAMAGE. |
|
24 */ |
|
25 |
|
26 #if ENABLE(VIDEO) |
|
27 |
|
28 #import "WebVideoFullscreenController.h" |
|
29 |
|
30 #import "WebTypesInternal.h" |
|
31 #import "WebVideoFullscreenHUDWindowController.h" |
|
32 #import "WebWindowAnimation.h" |
|
33 #import <IOKit/pwr_mgt/IOPMLib.h> |
|
34 #import <QTKit/QTKit.h> |
|
35 #import <WebCore/HTMLMediaElement.h> |
|
36 #import <WebCore/SoftLinking.h> |
|
37 #import <objc/objc-runtime.h> |
|
38 #import <wtf/UnusedParam.h> |
|
39 |
|
40 SOFT_LINK_FRAMEWORK(QTKit) |
|
41 SOFT_LINK_CLASS(QTKit, QTMovieLayer) |
|
42 |
|
43 SOFT_LINK_POINTER(QTKit, QTMovieRateDidChangeNotification, NSString *) |
|
44 |
|
45 #define QTMovieRateDidChangeNotification getQTMovieRateDidChangeNotification() |
|
46 |
|
47 @interface WebVideoFullscreenWindow : NSWindow |
|
48 #if !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_TIGER) |
|
49 <NSAnimationDelegate> |
|
50 #endif |
|
51 { |
|
52 SEL _controllerActionOnAnimationEnd; |
|
53 WebWindowScaleAnimation *_fullscreenAnimation; // (retain) |
|
54 } |
|
55 - (void)animateFromRect:(NSRect)startRect toRect:(NSRect)endRect withSubAnimation:(NSAnimation *)subAnimation controllerAction:(SEL)controllerAction; |
|
56 @end |
|
57 |
|
58 @interface WebVideoFullscreenController(HUDWindowControllerDelegate) <WebVideoFullscreenHUDWindowControllerDelegate> |
|
59 - (void)requestExitFullscreenWithAnimation:(BOOL)animation; |
|
60 - (void)updateMenuAndDockForFullscreen; |
|
61 - (void)updatePowerAssertions; |
|
62 @end |
|
63 |
|
64 @interface NSWindow(IsOnActiveSpaceAdditionForTigerAndLeopard) |
|
65 - (BOOL)isOnActiveSpace; |
|
66 @end |
|
67 |
|
68 @implementation WebVideoFullscreenController |
|
69 - (id)init |
|
70 { |
|
71 // Do not defer window creation, to make sure -windowNumber is created (needed by WebWindowScaleAnimation). |
|
72 NSWindow *window = [[WebVideoFullscreenWindow alloc] initWithContentRect:NSZeroRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; |
|
73 self = [super initWithWindow:window]; |
|
74 [window release]; |
|
75 if (!self) |
|
76 return nil; |
|
77 [self windowDidLoad]; |
|
78 return self; |
|
79 |
|
80 } |
|
81 - (void)dealloc |
|
82 { |
|
83 ASSERT(!_backgroundFullscreenWindow); |
|
84 ASSERT(!_fadeAnimation); |
|
85 [[NSNotificationCenter defaultCenter] removeObserver:self]; |
|
86 [super dealloc]; |
|
87 } |
|
88 |
|
89 - (WebVideoFullscreenWindow *)fullscreenWindow |
|
90 { |
|
91 return (WebVideoFullscreenWindow *)[super window]; |
|
92 } |
|
93 |
|
94 - (void)windowDidLoad |
|
95 { |
|
96 #ifdef BUILDING_ON_TIGER |
|
97 // WebVideoFullscreenController is not supported on Tiger: |
|
98 ASSERT_NOT_REACHED(); |
|
99 #else |
|
100 WebVideoFullscreenWindow *window = [self fullscreenWindow]; |
|
101 QTMovieLayer *layer = [[getQTMovieLayerClass() alloc] init]; |
|
102 [[window contentView] setLayer:layer]; |
|
103 [[window contentView] setWantsLayer:YES]; |
|
104 if (_mediaElement && _mediaElement->platformMedia().type == WebCore::PlatformMedia::QTMovieType) |
|
105 [layer setMovie:_mediaElement->platformMedia().media.qtMovie]; |
|
106 [window setHasShadow:YES]; // This is nicer with a shadow. |
|
107 [window setLevel:NSPopUpMenuWindowLevel-1]; |
|
108 [layer release]; |
|
109 |
|
110 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidResignActive:) name:NSApplicationDidResignActiveNotification object:NSApp]; |
|
111 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidChangeScreenParameters:) name:NSApplicationDidChangeScreenParametersNotification object:NSApp]; |
|
112 #endif |
|
113 } |
|
114 |
|
115 - (WebCore::HTMLMediaElement*)mediaElement |
|
116 { |
|
117 return _mediaElement.get(); |
|
118 } |
|
119 |
|
120 - (void)setMediaElement:(WebCore::HTMLMediaElement*)mediaElement |
|
121 { |
|
122 #ifdef BUILDING_ON_TIGER |
|
123 // WebVideoFullscreenController is not supported on Tiger: |
|
124 ASSERT_NOT_REACHED(); |
|
125 #else |
|
126 _mediaElement = mediaElement; |
|
127 if ([self isWindowLoaded]) { |
|
128 QTMovie *movie = _mediaElement->platformMedia().type == WebCore::PlatformMedia::QTMovieType ? _mediaElement->platformMedia().media.qtMovie : 0; |
|
129 QTMovieLayer *movieLayer = (QTMovieLayer *)[[[self fullscreenWindow] contentView] layer]; |
|
130 |
|
131 ASSERT(movieLayer && [movieLayer isKindOfClass:[getQTMovieLayerClass() class]]); |
|
132 ASSERT(movie); |
|
133 [movieLayer setMovie:movie]; |
|
134 [[NSNotificationCenter defaultCenter] addObserver:self |
|
135 selector:@selector(rateChanged:) |
|
136 name:QTMovieRateDidChangeNotification |
|
137 object:movie]; |
|
138 } |
|
139 #endif |
|
140 } |
|
141 |
|
142 - (id <WebVideoFullscreenControllerDelegate>)delegate |
|
143 { |
|
144 return _delegate; |
|
145 } |
|
146 |
|
147 - (void)setDelegate:(id <WebVideoFullscreenControllerDelegate>)delegate |
|
148 { |
|
149 _delegate = delegate; |
|
150 } |
|
151 |
|
152 - (CGFloat)clearFadeAnimation |
|
153 { |
|
154 [_fadeAnimation stopAnimation]; |
|
155 CGFloat previousAlpha = [_fadeAnimation currentAlpha]; |
|
156 [_fadeAnimation setWindow:nil]; |
|
157 [_fadeAnimation release]; |
|
158 _fadeAnimation = nil; |
|
159 return previousAlpha; |
|
160 } |
|
161 |
|
162 - (void)windowDidExitFullscreen |
|
163 { |
|
164 [self clearFadeAnimation]; |
|
165 [[self window] close]; |
|
166 [self setWindow:nil]; |
|
167 [self updateMenuAndDockForFullscreen]; |
|
168 [self updatePowerAssertions]; |
|
169 [_hudController setDelegate:nil]; |
|
170 [_hudController release]; |
|
171 _hudController = nil; |
|
172 [_backgroundFullscreenWindow close]; |
|
173 [_backgroundFullscreenWindow release]; |
|
174 _backgroundFullscreenWindow = nil; |
|
175 |
|
176 [self autorelease]; // Associated -retain is in -exitFullscreen. |
|
177 _isEndingFullscreen = NO; |
|
178 } |
|
179 |
|
180 - (void)windowDidEnterFullscreen |
|
181 { |
|
182 [self clearFadeAnimation]; |
|
183 |
|
184 ASSERT(!_hudController); |
|
185 _hudController = [[WebVideoFullscreenHUDWindowController alloc] init]; |
|
186 [_hudController setDelegate:self]; |
|
187 |
|
188 [self updateMenuAndDockForFullscreen]; |
|
189 [self updatePowerAssertions]; |
|
190 [NSCursor setHiddenUntilMouseMoves:YES]; |
|
191 |
|
192 // Give the HUD keyboard focus initially |
|
193 [_hudController fadeWindowIn]; |
|
194 } |
|
195 |
|
196 - (NSRect)mediaElementRect |
|
197 { |
|
198 return _mediaElement->screenRect(); |
|
199 } |
|
200 |
|
201 - (void)applicationDidResignActive:(NSNotification*)notification |
|
202 { |
|
203 // Check to see if the fullscreenWindow is on the active space; this function is available |
|
204 // on 10.6 and later, so default to YES if the function is not available: |
|
205 NSWindow* fullscreenWindow = [self fullscreenWindow]; |
|
206 BOOL isOnActiveSpace = ([fullscreenWindow respondsToSelector:@selector(isOnActiveSpace)] ? [fullscreenWindow isOnActiveSpace] : YES); |
|
207 |
|
208 // Replicate the QuickTime Player (X) behavior when losing active application status: |
|
209 // Is the fullscreen screen the main screen? (Note: this covers the case where only a |
|
210 // single screen is available.) Is the fullscreen screen on the current space? IFF so, |
|
211 // then exit fullscreen mode. |
|
212 if ([fullscreenWindow screen] == [[NSScreen screens] objectAtIndex:0] && isOnActiveSpace) |
|
213 [self requestExitFullscreenWithAnimation:NO]; |
|
214 } |
|
215 |
|
216 |
|
217 #pragma mark - |
|
218 #pragma mark Exposed Interface |
|
219 |
|
220 static void constrainFrameToRatioOfFrame(NSRect *frameToConstrain, const NSRect *frame) |
|
221 { |
|
222 // Keep a constrained aspect ratio for the destination window |
|
223 double originalRatio = frame->size.width / frame->size.height; |
|
224 double newRatio = frameToConstrain->size.width / frameToConstrain->size.height; |
|
225 if (newRatio > originalRatio) { |
|
226 double newWidth = originalRatio * frameToConstrain->size.height; |
|
227 double diff = frameToConstrain->size.width - newWidth; |
|
228 frameToConstrain->size.width = newWidth; |
|
229 frameToConstrain->origin.x += diff / 2; |
|
230 } else { |
|
231 double newHeight = frameToConstrain->size.width / originalRatio; |
|
232 double diff = frameToConstrain->size.height - newHeight; |
|
233 frameToConstrain->size.height = newHeight; |
|
234 frameToConstrain->origin.y += diff / 2; |
|
235 } |
|
236 } |
|
237 |
|
238 static NSWindow *createBackgroundFullscreenWindow(NSRect frame, int level) |
|
239 { |
|
240 NSWindow *window = [[NSWindow alloc] initWithContentRect:frame styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; |
|
241 [window setOpaque:YES]; |
|
242 [window setBackgroundColor:[NSColor blackColor]]; |
|
243 [window setLevel:level]; |
|
244 [window setHidesOnDeactivate:YES]; |
|
245 [window setReleasedWhenClosed:NO]; |
|
246 return window; |
|
247 } |
|
248 |
|
249 - (void)setupFadeAnimationIfNeededAndFadeIn:(BOOL)fadeIn |
|
250 { |
|
251 CGFloat initialAlpha = fadeIn ? 0 : 1; |
|
252 if (_fadeAnimation) { |
|
253 // Make sure we support queuing animation if the previous one isn't over yet |
|
254 initialAlpha = [self clearFadeAnimation]; |
|
255 } |
|
256 if (!_forceDisableAnimation) |
|
257 _fadeAnimation = [[WebWindowFadeAnimation alloc] initWithDuration:0.2 window:_backgroundFullscreenWindow initialAlpha:initialAlpha finalAlpha:fadeIn ? 1 : 0]; |
|
258 } |
|
259 |
|
260 - (void)enterFullscreen:(NSScreen *)screen |
|
261 { |
|
262 if (!screen) |
|
263 screen = [NSScreen mainScreen]; |
|
264 |
|
265 NSRect frame = [self mediaElementRect]; |
|
266 NSRect endFrame = [screen frame]; |
|
267 constrainFrameToRatioOfFrame(&endFrame, &frame); |
|
268 |
|
269 // Create a black window if needed |
|
270 if (!_backgroundFullscreenWindow) |
|
271 _backgroundFullscreenWindow = createBackgroundFullscreenWindow([screen frame], [[self window] level]-1); |
|
272 else |
|
273 [_backgroundFullscreenWindow setFrame:[screen frame] display:NO]; |
|
274 |
|
275 [self setupFadeAnimationIfNeededAndFadeIn:YES]; |
|
276 if (_forceDisableAnimation) { |
|
277 // This will disable scale animation |
|
278 frame = NSZeroRect; |
|
279 } |
|
280 [[self fullscreenWindow] animateFromRect:frame toRect:endFrame withSubAnimation:_fadeAnimation controllerAction:@selector(windowDidEnterFullscreen)]; |
|
281 |
|
282 [_backgroundFullscreenWindow orderWindow:NSWindowBelow relativeTo:[[self fullscreenWindow] windowNumber]]; |
|
283 } |
|
284 |
|
285 - (void)exitFullscreen |
|
286 { |
|
287 if (_isEndingFullscreen) |
|
288 return; |
|
289 _isEndingFullscreen = YES; |
|
290 [_hudController closeWindow]; |
|
291 |
|
292 NSRect endFrame = [self mediaElementRect]; |
|
293 |
|
294 [self setupFadeAnimationIfNeededAndFadeIn:NO]; |
|
295 if (_forceDisableAnimation) { |
|
296 // This will disable scale animation |
|
297 endFrame = NSZeroRect; |
|
298 } |
|
299 |
|
300 // We have to retain ourselves because we want to be alive for the end of the animation. |
|
301 // If our owner releases us we could crash if this is not the case. |
|
302 // Balanced in windowDidExitFullscreen |
|
303 [self retain]; |
|
304 |
|
305 [[self fullscreenWindow] animateFromRect:[[self window] frame] toRect:endFrame withSubAnimation:_fadeAnimation controllerAction:@selector(windowDidExitFullscreen)]; |
|
306 } |
|
307 |
|
308 - (void)applicationDidChangeScreenParameters:(NSNotification*)notification |
|
309 { |
|
310 // The user may have changed the main screen by moving the menu bar, or they may have changed |
|
311 // the Dock's size or location, or they may have changed the fullscreen screen's dimensions. |
|
312 // Update our presentation parameters, and ensure that the full screen window occupies the |
|
313 // entire screen: |
|
314 [self updateMenuAndDockForFullscreen]; |
|
315 [[self window] setFrame:[[[self window] screen] frame] display:YES]; |
|
316 } |
|
317 |
|
318 - (void)updateMenuAndDockForFullscreen |
|
319 { |
|
320 // NSApplicationPresentationOptions is available on > 10.6 only: |
|
321 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) |
|
322 NSApplicationPresentationOptions options = NSApplicationPresentationDefault; |
|
323 NSScreen* fullscreenScreen = [[self window] screen]; |
|
324 |
|
325 if (!_isEndingFullscreen) { |
|
326 // Auto-hide the menu bar if the fullscreenScreen contains the menu bar: |
|
327 // NOTE: if the fullscreenScreen contains the menu bar but not the dock, we must still |
|
328 // auto-hide the dock, or an exception will be thrown. |
|
329 if ([[NSScreen screens] objectAtIndex:0] == fullscreenScreen) |
|
330 options |= (NSApplicationPresentationAutoHideMenuBar | NSApplicationPresentationAutoHideDock); |
|
331 // Check if the current screen contains the dock by comparing the screen's frame to its |
|
332 // visibleFrame; if a dock is present, the visibleFrame will differ. If the current screen |
|
333 // contains the dock, hide it. |
|
334 else if (!NSEqualRects([fullscreenScreen frame], [fullscreenScreen visibleFrame])) |
|
335 options |= NSApplicationPresentationAutoHideDock; |
|
336 } |
|
337 |
|
338 if ([NSApp respondsToSelector:@selector(setPresentationOptions:)]) |
|
339 [NSApp setPresentationOptions:options]; |
|
340 else |
|
341 #endif |
|
342 SetSystemUIMode(_isEndingFullscreen ? kUIModeNormal : kUIModeAllHidden, 0); |
|
343 } |
|
344 |
|
345 #if !defined(BUILDING_ON_TIGER) // IOPMAssertionCreateWithName not defined on < 10.5 |
|
346 - (void)_disableIdleDisplaySleep |
|
347 { |
|
348 if (_idleDisplaySleepAssertion == kIOPMNullAssertionID) |
|
349 #if defined(BUILDING_ON_LEOPARD) // IOPMAssertionCreateWithName is not defined in the 10.5 SDK |
|
350 IOPMAssertionCreate(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, &_idleDisplaySleepAssertion); |
|
351 #else // IOPMAssertionCreate is depreciated in > 10.5 |
|
352 IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, CFSTR("WebKit playing a video fullscreen."), &_idleDisplaySleepAssertion); |
|
353 #endif |
|
354 } |
|
355 |
|
356 - (void)_enableIdleDisplaySleep |
|
357 { |
|
358 if (_idleDisplaySleepAssertion != kIOPMNullAssertionID) { |
|
359 IOPMAssertionRelease(_idleDisplaySleepAssertion); |
|
360 _idleDisplaySleepAssertion = kIOPMNullAssertionID; |
|
361 } |
|
362 } |
|
363 |
|
364 - (void)_disableIdleSystemSleep |
|
365 { |
|
366 if (_idleSystemSleepAssertion == kIOPMNullAssertionID) |
|
367 #if defined(BUILDING_ON_LEOPARD) // IOPMAssertionCreateWithName is not defined in the 10.5 SDK |
|
368 IOPMAssertionCreate(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, &_idleSystemSleepAssertion); |
|
369 #else // IOPMAssertionCreate is depreciated in > 10.5 |
|
370 IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, CFSTR("WebKit playing a video fullscreen."), &_idleSystemSleepAssertion); |
|
371 #endif |
|
372 } |
|
373 |
|
374 - (void)_enableIdleSystemSleep |
|
375 { |
|
376 if (_idleSystemSleepAssertion != kIOPMNullAssertionID) { |
|
377 IOPMAssertionRelease(_idleSystemSleepAssertion); |
|
378 _idleSystemSleepAssertion = kIOPMNullAssertionID; |
|
379 } |
|
380 } |
|
381 #endif |
|
382 |
|
383 - (void)updatePowerAssertions |
|
384 { |
|
385 #if !defined(BUILDING_ON_TIGER) |
|
386 float rate = 0; |
|
387 if (_mediaElement && _mediaElement->platformMedia().type == WebCore::PlatformMedia::QTMovieType) |
|
388 rate = [_mediaElement->platformMedia().media.qtMovie rate]; |
|
389 |
|
390 if (rate && !_isEndingFullscreen) { |
|
391 [self _disableIdleSystemSleep]; |
|
392 [self _disableIdleDisplaySleep]; |
|
393 } else { |
|
394 [self _enableIdleSystemSleep]; |
|
395 [self _enableIdleDisplaySleep]; |
|
396 } |
|
397 #endif |
|
398 } |
|
399 |
|
400 #pragma mark - |
|
401 #pragma mark Window callback |
|
402 |
|
403 - (void)_requestExit |
|
404 { |
|
405 if (_mediaElement) |
|
406 _mediaElement->exitFullscreen(); |
|
407 _forceDisableAnimation = NO; |
|
408 } |
|
409 |
|
410 - (void)requestExitFullscreenWithAnimation:(BOOL)animation |
|
411 { |
|
412 if (_isEndingFullscreen) |
|
413 return; |
|
414 |
|
415 _forceDisableAnimation = !animation; |
|
416 [self performSelector:@selector(_requestExit) withObject:nil afterDelay:0]; |
|
417 |
|
418 } |
|
419 |
|
420 - (void)requestExitFullscreen |
|
421 { |
|
422 [self requestExitFullscreenWithAnimation:YES]; |
|
423 } |
|
424 |
|
425 - (void)fadeHUDIn |
|
426 { |
|
427 [_hudController fadeWindowIn]; |
|
428 } |
|
429 |
|
430 #pragma mark - |
|
431 #pragma mark QTMovie callbacks |
|
432 |
|
433 - (void)rateChanged:(NSNotification *)unusedNotification |
|
434 { |
|
435 UNUSED_PARAM(unusedNotification); |
|
436 [_hudController updateRate]; |
|
437 [self updatePowerAssertions]; |
|
438 } |
|
439 |
|
440 @end |
|
441 |
|
442 @implementation WebVideoFullscreenWindow |
|
443 |
|
444 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag |
|
445 { |
|
446 UNUSED_PARAM(aStyle); |
|
447 self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:bufferingType defer:flag]; |
|
448 if (!self) |
|
449 return nil; |
|
450 [self setOpaque:NO]; |
|
451 [self setBackgroundColor:[NSColor clearColor]]; |
|
452 [self setHidesOnDeactivate:YES]; |
|
453 [self setIgnoresMouseEvents:NO]; |
|
454 [self setAcceptsMouseMovedEvents:YES]; |
|
455 return self; |
|
456 } |
|
457 |
|
458 - (void)dealloc |
|
459 { |
|
460 ASSERT(!_fullscreenAnimation); |
|
461 [super dealloc]; |
|
462 } |
|
463 |
|
464 - (BOOL)resignFirstResponder |
|
465 { |
|
466 return NO; |
|
467 } |
|
468 |
|
469 - (BOOL)canBecomeKeyWindow |
|
470 { |
|
471 return NO; |
|
472 } |
|
473 |
|
474 - (void)mouseDown:(NSEvent *)theEvent |
|
475 { |
|
476 UNUSED_PARAM(theEvent); |
|
477 } |
|
478 |
|
479 - (void)cancelOperation:(id)sender |
|
480 { |
|
481 UNUSED_PARAM(sender); |
|
482 [[self windowController] requestExitFullscreen]; |
|
483 } |
|
484 |
|
485 - (void)animatedResizeDidEnd |
|
486 { |
|
487 // Call our windowController. |
|
488 if (_controllerActionOnAnimationEnd) |
|
489 [[self windowController] performSelector:_controllerActionOnAnimationEnd]; |
|
490 _controllerActionOnAnimationEnd = NULL; |
|
491 } |
|
492 |
|
493 // |
|
494 // This function will animate a change of frame rectangle |
|
495 // We support queuing animation, that means that we'll correctly |
|
496 // interrupt the running animation, and queue the next one. |
|
497 // |
|
498 - (void)animateFromRect:(NSRect)startRect toRect:(NSRect)endRect withSubAnimation:(NSAnimation *)subAnimation controllerAction:(SEL)controllerAction |
|
499 { |
|
500 _controllerActionOnAnimationEnd = controllerAction; |
|
501 |
|
502 BOOL wasAnimating = NO; |
|
503 if (_fullscreenAnimation) { |
|
504 wasAnimating = YES; |
|
505 |
|
506 // Interrupt any running animation. |
|
507 [_fullscreenAnimation stopAnimation]; |
|
508 |
|
509 // Save the current rect to ensure a smooth transition. |
|
510 startRect = [_fullscreenAnimation currentFrame]; |
|
511 [_fullscreenAnimation release]; |
|
512 _fullscreenAnimation = nil; |
|
513 } |
|
514 |
|
515 if (NSIsEmptyRect(startRect) || NSIsEmptyRect(endRect)) { |
|
516 // Fakely end the subanimation. |
|
517 [subAnimation setCurrentProgress:1.0]; |
|
518 // And remove the weak link to the window. |
|
519 [subAnimation stopAnimation]; |
|
520 |
|
521 [self setFrame:endRect display:NO]; |
|
522 [self makeKeyAndOrderFront:self]; |
|
523 [self animatedResizeDidEnd]; |
|
524 return; |
|
525 } |
|
526 |
|
527 if (!wasAnimating) { |
|
528 // We'll downscale the window during the animation based on the higher resolution rect |
|
529 BOOL higherResolutionIsEndRect = startRect.size.width < endRect.size.width && startRect.size.height < endRect.size.height; |
|
530 [self setFrame:higherResolutionIsEndRect ? endRect : startRect display:NO]; |
|
531 } |
|
532 |
|
533 ASSERT(!_fullscreenAnimation); |
|
534 _fullscreenAnimation = [[WebWindowScaleAnimation alloc] initWithHintedDuration:0.2 window:self initalFrame:startRect finalFrame:endRect]; |
|
535 [_fullscreenAnimation setSubAnimation:subAnimation]; |
|
536 [_fullscreenAnimation setDelegate:self]; |
|
537 |
|
538 // Make sure the animation has scaled the window before showing it. |
|
539 [_fullscreenAnimation setCurrentProgress:0]; |
|
540 [self makeKeyAndOrderFront:self]; |
|
541 |
|
542 [_fullscreenAnimation startAnimation]; |
|
543 } |
|
544 |
|
545 - (void)animationDidEnd:(NSAnimation *)animation |
|
546 { |
|
547 #if !defined(BUILDING_ON_TIGER) // Animations are never threaded on Tiger. |
|
548 if (![NSThread isMainThread]) { |
|
549 [self performSelectorOnMainThread:@selector(animationDidEnd:) withObject:animation waitUntilDone:NO]; |
|
550 return; |
|
551 } |
|
552 #endif |
|
553 if (animation != _fullscreenAnimation) |
|
554 return; |
|
555 |
|
556 // The animation is not really over and was interrupted |
|
557 // Don't send completion events. |
|
558 if ([animation currentProgress] < 1.0) |
|
559 return; |
|
560 |
|
561 // Ensure that animation (and subanimation) don't keep |
|
562 // the weak reference to the window ivar that may be destroyed from |
|
563 // now on. |
|
564 [_fullscreenAnimation setWindow:nil]; |
|
565 |
|
566 [_fullscreenAnimation autorelease]; |
|
567 _fullscreenAnimation = nil; |
|
568 |
|
569 [self animatedResizeDidEnd]; |
|
570 } |
|
571 |
|
572 - (void)mouseMoved:(NSEvent *)theEvent |
|
573 { |
|
574 [[self windowController] fadeHUDIn]; |
|
575 } |
|
576 |
|
577 @end |
|
578 |
|
579 #endif /* ENABLE(VIDEO) */ |