1 /*****************************************************************************
2 * VLCBrowsableVideoView.h: VideoView subclasses that allow fullScreen
4 *****************************************************************************
5 * Copyright (C) 2007 Pierre d'Herbemont
6 * Copyright (C) 2007, 2009 the VideoLAN team
9 * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /* DisableScreenUpdates, SetSystemUIMode, ... */
27 #import <QuickTime/QuickTime.h>
29 #import "VLCBrowsableVideoView.h"
30 #import "VLCAppAdditions.h"
31 #import "VLCMediaListLayer.h"
32 #import "VLCMainWindowController.h"
34 /* TODO: We may want to clean up the private functions a bit... */
36 @interface VLCBrowsableVideoView ()
38 @property (readwrite, retain) id selectedObject;
41 @interface VLCBrowsableVideoView (Private)
44 + (CAScrollLayer *)menuLayer;
45 + (CALayer *)backLayer;
47 - (void)loadItemsAtIndexPath:(NSIndexPath *)path inLayer:(CALayer *)layer;
48 - (void)changeSelectedIndex:(NSInteger)i;
49 - (void)changeSelectedPath:(NSIndexPath *)newPath withSelectedIndex:(NSUInteger)newIndex;
51 - (void)displayEmptyView;
55 @interface VLCBrowsableVideoView (FullScreenTransition)
56 - (void)hasEndedFullScreen;
57 - (void)hasBecomeFullScreen;
59 - (void)enterFullScreen:(NSScreen *)screen;
60 - (void)leaveFullScreen;
61 - (void)leaveFullScreenAndFadeOut: (BOOL)fadeout;
66 /******************************************************************************
67 * VLCBrowsableVideoView
69 @implementation VLCBrowsableVideoView
72 @synthesize nodeKeyPath;
73 @synthesize contentKeyPath;
74 @synthesize selectedObject;
77 @synthesize videoLayer;
79 - (NSArray *)itemsTree {
83 - (void)setItemsTree:(NSArray *)newItemsTree
86 itemsTree = [newItemsTree retain];
87 [self changeSelectedPath:[[[NSIndexPath alloc] init] autorelease] withSelectedIndex:0];
92 return [self isInFullScreenMode];
95 - (void)setFullScreen:(BOOL)newFullScreen
97 if( newFullScreen == self.fullScreen )
102 [self enterFullScreenMode:[[self window] screen] withOptions:
103 [NSDictionary dictionaryWithObject: [NSNumber numberWithInt:1]
104 forKey: NSFullScreenModeWindowLevel]];
108 [self exitFullScreenModeWithOptions:nil];
114 return videoLayer.hasVideo;
117 /* Binded to VideoLayer's hasVideo */
118 - (void)setHasVideo:(BOOL)hasVideo
122 [CATransaction begin];
123 [videoLayer removeFromSuperlayer];
124 [self.layer addSublayer:videoLayer];
125 videoLayer.frame = [self layer].bounds;
126 [videoLayer setAutoresizingMask:kCALayerWidthSizable|kCALayerHeightSizable];
127 [mediaListLayer removeFromSuperlayer];
128 [CATransaction commit];
132 [CATransaction begin];
133 [mediaListLayer removeFromSuperlayer];
134 [self.layer addSublayer:mediaListLayer];
135 mediaListLayer.frame = [self layer].bounds;
136 [mediaListLayer setAutoresizingMask:kCALayerWidthSizable|kCALayerHeightSizable];
137 [videoLayer removeFromSuperlayer];
138 [CATransaction commit];
141 [[self layer] setNeedsDisplay];
142 [self setNeedsDisplay:YES];
148 // FIXME: do that in -initWithFrame:
149 [self setWantsLayer:YES];
151 displayedItems = NSMakeRange( -1, 0 );
153 selectionLayer = backLayer = nil;
155 selectedPath = [[NSIndexPath alloc] init];
156 tempFullScreenView = [[NSView alloc] init];
159 videoLayer = [[VLCVideoLayer layer] retain];
160 [videoLayer addObserver:self forKeyPath:@"hasVideo" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
162 [videoLayer setDelegate:self];
163 NSAssert( mainWindowController, @"No mainWindowController" );
164 [mainWindowController.mediaPlayer setVideoLayer: videoLayer];
165 mediaListLayer = [[VLCMediaListLayer layerWithMediaArrayController:mainWindowController.mediaArrayController] retain];
166 [self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSURLPboardType, @"VLCMediaURLType", nil]];
167 [mainWindowController.mediaArrayController setSelectsInsertedObjects:YES];
168 [mainWindowController.mediaArrayController setAvoidsEmptySelection:YES];
169 [[self layer] addSublayer:mediaListLayer];
170 mediaListLayer.frame = [self layer].bounds;
171 [mediaListLayer setAutoresizingMask:kCALayerWidthSizable|kCALayerHeightSizable];
173 [[self layer] setNeedsDisplay];
177 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
179 if([keyPath isEqualToString:@"hasVideo"])
181 [self setHasVideo:[object hasVideo]];
184 [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
189 /* Previously registered in */
190 [videoLayer removeObserver:self forKeyPath:@"hasVideo"];
192 [mediaListLayer release];
193 [videoLayer release];
194 [tempFullScreenView release];
195 [selectedPath release];
202 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
204 NSPasteboard *pboard;
206 pboard = [sender draggingPasteboard];
208 if ( [[pboard types] containsObject:NSFilenamesPboardType] &&
209 ![mainWindowController.mediaArrayController.contentMediaList isReadOnly] )
211 self.layer.borderColor = CGColorCreateGenericGray(0.5, 0.5);
212 self.layer.cornerRadius = 10.f;
213 self.layer.borderWidth = 10.0;
214 return NSDragOperationCopy;
216 return NSDragOperationNone;
219 - (void)draggingEnded:(id < NSDraggingInfo >)sender
221 [CATransaction begin];
222 [CATransaction setValue:[NSNumber numberWithFloat:0.1] forKey:kCATransactionAnimationDuration];
223 self.layer.borderWidth = 0.;
224 [CATransaction commit];
225 [CATransaction begin];
226 [mainWindowController.mediaArrayController setFilterPredicate:nil];
227 [mainWindowController.mediaArrayController setSelectionIndex:[mainWindowController.mediaArrayController.contentMediaList count] - 1];
228 [CATransaction commit];
231 - (void)draggingExited:(id < NSDraggingInfo >)sender
233 self.layer.borderWidth = 0.;
236 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
238 NSPasteboard *pboard;
239 NSDragOperation sourceDragMask;
241 sourceDragMask = [sender draggingSourceOperationMask];
242 pboard = [sender draggingPasteboard];
244 if ( [[pboard types] containsObject:NSFilenamesPboardType] )
246 NSArray *files = [pboard propertyListForType:NSFilenamesPboardType];
247 VLCMediaList * mediaList = mainWindowController.mediaArrayController.contentMediaList;
248 if( [mediaList isReadOnly] )
251 [CATransaction begin];
252 for( NSString * filePath in files )
253 [mediaList addMedia:[VLCMedia mediaWithPath:filePath]];
254 [CATransaction commit];
265 /* Hiding/Displaying the menu */
270 return; /* Nothing to do */
272 [menuLayer removeFromSuperlayer];
273 [selectionLayer removeFromSuperlayer];
274 [backLayer removeFromSuperlayer];
275 //[menuLayer autorelease]; /* Need gc for that */
276 //[selectionLayer autorelease];
277 //[backLayer autorelease];
278 selectionLayer = backLayer = nil;
281 [self setNeedsDisplay:YES];
286 if( menuDisplayed || !self.itemsTree )
287 return; /* Nothing to do */
291 CALayer * rootLayer = [self layer];
292 rootLayer.backgroundColor = CGColorGetConstantColor(kCGColorBlack);
293 rootLayer.layoutManager = [CAConstraintLayoutManager layoutManager];
294 menuLayer = [VLCBrowsableVideoView menuLayer];
295 [self loadItemsAtIndexPath: selectedPath inLayer: menuLayer];
299 backLayer = [[VLCBrowsableVideoView backLayer] retain];
301 [[self layer] addSublayer:backLayer];
302 [[self layer] addSublayer:menuLayer];
304 [[self layer] setNeedsLayout];
305 [[self layer] setNeedsDisplay];
308 [self changeSelectedPath:selectedPath withSelectedIndex:selectedIndex];
319 - (IBAction)backToMediaListView:(id)sender
321 [mainWindowController.mediaPlayer stop];
322 [self setHasVideo: NO];
328 - (void)drawRect:(NSRect)rect
330 if( [self hasVideo] )
332 [[NSColor blackColor] set];
336 NSColor * topGradient = [NSColor colorWithCalibratedWhite:.0f alpha:1.0];
337 NSColor * bottomGradient = [NSColor colorWithCalibratedWhite:0.35f alpha:1.0];
338 NSGradient * gradient = [[NSGradient alloc] initWithColorsAndLocations:bottomGradient, 0.f, topGradient, 0.65f, topGradient, 1.f, nil];
339 [gradient drawInRect:self.bounds angle:100.0];
345 - (BOOL)acceptsFirstResponder
350 -(void)moveUp:(id)sender
352 [self changeSelectedIndex:selectedIndex-1];
355 -(void)moveDown:(id)sender
357 [self changeSelectedIndex:selectedIndex+1];
360 - (void)mouseDown:(NSEvent *)theEvent
362 if([theEvent clickCount] == 1)
364 NSRect rect1 = [self bounds];
365 NSRect rect2 = [self bounds];
366 rect1.origin.x += [self bounds].size.width * 4./5.;
367 rect1.size.width /= 5.;
368 rect2.size.width /= 5.;
369 if(NSPointInRect([self convertPoint:[theEvent locationInWindow] fromView:nil], rect1))
371 [mainWindowController.mediaArrayController selectNext:self];
373 else if(NSPointInRect([self convertPoint:[theEvent locationInWindow] fromView:nil], rect2))
375 [mainWindowController.mediaArrayController selectPrevious:self];
379 if([theEvent clickCount] == 2)
381 [mainWindowController mediaListViewItemDoubleClicked:self];
384 if([theEvent clickCount] == 3)
386 self.fullScreen = !self.fullScreen;
390 - (void)keyDown:(NSEvent *)theEvent
392 if(([[theEvent charactersIgnoringModifiers] characterAtIndex:0] == 13) && menuDisplayed)
394 [self changeSelectedPath:[selectedPath indexPathByAddingIndex:selectedIndex] withSelectedIndex:0];
396 else if([[theEvent charactersIgnoringModifiers] characterAtIndex:0] == NSLeftArrowFunctionKey && menuDisplayed)
398 if( [selectedPath length] > 0 )
399 [self changeSelectedPath:[selectedPath indexPathByRemovingLastIndex] withSelectedIndex:[selectedPath lastIndex]];
403 else if(!menuDisplayed && [[theEvent charactersIgnoringModifiers] characterAtIndex:0] == NSRightArrowFunctionKey)
408 [super keyDown: theEvent];
412 - (void)enterFullScreenMode:(NSScreen *)screen withOptions:(NSDictionary *)options
414 [self enterFullScreen: screen];
417 - (void)exitFullScreenModeWithOptions:(NSDictionary *)options
419 [self leaveFullScreen];
423 - (BOOL)isInFullScreenMode
431 /******************************************************************************
432 * VLCBrowsableVideoView (Private)
435 @implementation VLCBrowsableVideoView (Private)
436 + (CAScrollLayer *)menuLayer
438 CAScrollLayer * layer = [CAScrollLayer layer];
439 layer.scrollMode = kCAScrollVertically;
441 [layer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxY
442 relativeTo:@"superlayer" attribute:kCAConstraintMaxY]];
443 [layer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxX
444 relativeTo:@"superlayer" attribute:kCAConstraintMaxX]];
445 [layer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinX
446 relativeTo:@"superlayer" attribute:kCAConstraintMinX]];
447 [layer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinY
448 relativeTo:@"superlayer" attribute:kCAConstraintMinY]];
452 + (CALayer *)backLayer
454 CALayer * layer = [CALayer layer];
456 [layer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxY
457 relativeTo:@"superlayer" attribute:kCAConstraintMaxY]];
458 [layer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxX
459 relativeTo:@"superlayer" attribute:kCAConstraintMaxX]];
460 [layer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinX
461 relativeTo:@"superlayer" attribute:kCAConstraintMinX]];
462 [layer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinY
463 relativeTo:@"superlayer" attribute:kCAConstraintMinY]];
466 layer.backgroundColor = CGColorCreateGenericRGB(0., 0., 0., .5);
471 - (void)loadItemsAtIndexPath:(NSIndexPath *)path inLayer:(CALayer *)layer
473 const CGFloat height=70.0;
474 const CGFloat fontSize=48.0;
475 NSArray * items = [self.itemsTree objectAtIndexPath:path withNodeKeyPath:self.nodeKeyPath];
478 for( i = 0; i < [items count]; i++ )
480 CATextLayer *menuItemLayer=[CATextLayer layer];
481 id item = [items objectAtIndex: i];
482 menuItemLayer.string = self.contentKeyPath ? [item valueForKeyPath:self.contentKeyPath] : @"No content Key path set";
483 menuItemLayer.font = @"BankGothic-Light";
484 menuItemLayer.fontSize = fontSize;
485 menuItemLayer.foregroundColor = CGColorCreateGenericRGB(1.0,1.0,1.0,1.0);
486 menuItemLayer.shadowColor = CGColorCreateGenericRGB(0.0,0.0,0.0,1.0);
487 menuItemLayer.shadowOpacity = 0.7;
488 menuItemLayer.shadowRadius = 2.0;
490 menuItemLayer.frame = CGRectMake( 40., height*(-i) + layer.visibleRect.size.height, 500.0f,70.);
491 [layer addSublayer: menuItemLayer];
494 /* for(i=0; i < [[layer sublayers] count]; i++)
495 NSLog(@"%d, %@", i, [[[layer sublayers] objectAtIndex: i] string]);
499 - (void)changeSelectedIndex:(NSInteger)i
501 BOOL justCreatedSelectionLayer = NO;
508 if( !selectionLayer )
510 justCreatedSelectionLayer = YES;
511 /* Rip-off from Apple's Sample code */
512 selectionLayer=[[CALayer layer] retain];
514 selectionLayer.borderWidth=2.0;
515 selectionLayer.borderColor=CGColorCreateGenericRGB(1.0f,1.0f,1.0f,1.0f);
516 selectionLayer.backgroundColor=CGColorCreateGenericRGB(.9f,1.0f,1.0f,.1f);
518 CIFilter *filter = [CIFilter filterWithName:@"CIBloom"];
519 [filter setDefaults];
520 [filter setValue:[NSNumber numberWithFloat:5.0] forKey:@"inputRadius"];
522 [filter setName:@"pulseFilter"];
524 [selectionLayer setFilters:[NSArray arrayWithObject:filter]];
526 CABasicAnimation* pulseAnimation = [CABasicAnimation animation];
528 pulseAnimation.keyPath = @"filters.pulseFilter.inputIntensity";
530 pulseAnimation.fromValue = [NSNumber numberWithFloat: 0.0];
531 pulseAnimation.toValue = [NSNumber numberWithFloat: 3.0];
533 pulseAnimation.duration = 2.0;
534 pulseAnimation.repeatCount = 1e100f;
535 pulseAnimation.autoreverses = YES;
537 pulseAnimation.timingFunction = [CAMediaTimingFunction functionWithName:
538 kCAMediaTimingFunctionEaseInEaseOut];
540 [selectionLayer addAnimation:pulseAnimation forKey:@"pulseAnimation"];
541 [[self layer] addSublayer:selectionLayer];
543 NSArray * items = [self.itemsTree objectAtIndexPath:selectedPath withNodeKeyPath:self.nodeKeyPath];
544 if( [items count] <= 0 )
546 if( i >= [items count] ) i = [items count] - 1;
549 CALayer * layer = [[menuLayer sublayers] objectAtIndex: i];
550 CGRect frame = layer.frame;
553 frame.origin.y -= [self layer].bounds.size.height - frame.size.height;
554 frame.size.height = [self layer].bounds.size.height;
556 [(CAScrollLayer*)menuLayer scrollToRect:frame];
558 if( !justCreatedSelectionLayer ) /* Get around an artifact on first launch */
559 [CATransaction flush]; /* Make sure we get the "right" layer.frame */
561 frame = [[self layer] convertRect:layer.frame fromLayer:[layer superlayer]];
562 frame.size.width += 200.;
563 frame.origin.x -= 100.f;
564 selectionLayer.frame = frame;
566 selectionLayer.cornerRadius = selectionLayer.bounds.size.height / 2.;
570 - (void)changeSelectedPath:(NSIndexPath *)newPath withSelectedIndex:(NSUInteger)newIndex
574 id object = [itemsTree objectAtIndexPath:newPath withNodeKeyPath:nodeKeyPath];
575 /* Make sure we are in a node */
576 if( ![object isKindOfClass:[NSArray class]] )
578 self.selectedObject = object;
579 if( !self.target || !self.action )
581 [NSException raise:@"VLCBrowsableVideoViewNoActionSpecified" format:@"*** Exception [%@]: No action specified.", [self class]];
584 void (*method)(id, SEL, id) = (void (*)(id, SEL, id))[self.target methodForSelector: self.action];
586 method( self.target, self.action, self);
592 /* Make sure the node isn't empty */
593 if( ![object count] )
595 [self displayEmptyView];
599 CALayer * newMenuLayer = [VLCBrowsableVideoView menuLayer];
601 newMenuLayer.bounds = menuLayer.bounds; /* Get around some artifacts */
602 [self loadItemsAtIndexPath:newPath inLayer:newMenuLayer];
604 [[self layer] replaceSublayer:menuLayer with:newMenuLayer];
606 [[self layer] addSublayer:newMenuLayer];
607 //[menuLayer autorelease]; /* warn: we need gc for that */
608 menuLayer = [newMenuLayer retain];
611 [selectedPath release];
612 selectedPath = [newPath retain];
613 [self changeSelectedIndex:newIndex];
616 - (void)displayEmptyView
618 CALayer * layer = [CALayer layer];
619 layer.layoutManager = [CAConstraintLayoutManager layoutManager];
620 [layer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxY
621 relativeTo:@"superlayer" attribute:kCAConstraintMaxY]];
622 [layer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxX
623 relativeTo:@"superlayer" attribute:kCAConstraintMaxX]];
624 [layer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinX
625 relativeTo:@"superlayer" attribute:kCAConstraintMinX]];
626 [layer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinY
627 relativeTo:@"superlayer" attribute:kCAConstraintMinY]];
629 CATextLayer *menuItemLayer=[CATextLayer layer];
630 menuItemLayer.string = @"Empty";
631 menuItemLayer.font = @"BankGothic-Light";
632 menuItemLayer.fontSize = 48.f;
633 menuItemLayer.foregroundColor = CGColorCreateGenericRGB(1.0,1.0,1.0,1.0);
634 menuItemLayer.shadowColor = CGColorCreateGenericRGB(0.0,0.0,0.0,1.0);
635 menuItemLayer.shadowOpacity = 0.7;
636 menuItemLayer.shadowRadius = 2.0;
638 [menuItemLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidX
639 relativeTo:@"superlayer" attribute:kCAConstraintMidX]];
640 [menuItemLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidY
641 relativeTo:@"superlayer" attribute:kCAConstraintMidY]];
642 [layer addSublayer:menuItemLayer];
645 [[self layer] replaceSublayer:menuLayer with:layer];
647 [[self layer] addSublayer:layer];
648 [selectionLayer removeFromSuperlayer];
649 //[selectionLayer autorelease] /* need gc */
650 //[menuLayer autorelease] /* need gc */
652 selectionLayer = nil;
660 @implementation VLCBrowsableVideoView (FullScreenTransition)
662 - (void)enterFullScreen:(NSScreen *)screen
664 NSMutableDictionary *dict1,*dict2;
668 screenRect = [screen frame];
670 [NSCursor setHiddenUntilMouseMoves: YES];
672 /* Only create the o_fullScreen_window if we are not in the middle of the zooming animation */
673 if (!fullScreenWindow)
675 /* We can't change the styleMask of an already created NSWindow, so we create an other window, and do eye catching stuff */
677 aRect = [[self superview] convertRect: [self frame] toView: nil]; /* Convert to Window base coord */
678 aRect.origin.x += [[self window] frame].origin.x;
679 aRect.origin.y += [[self window] frame].origin.y;
680 fullScreenWindow = [[VLCWindow alloc] initWithContentRect:aRect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
681 [fullScreenWindow setBackgroundColor: [NSColor blackColor]];
682 [fullScreenWindow setCanBecomeKeyWindow: YES];
684 if (![[self window] isVisible] || [[self window] alphaValue] == 0.0 || [self isHiddenOrHasHiddenAncestor] )
686 /* We don't animate if we are not visible, instead we
687 * simply fade the display */
688 CGDisplayFadeReservationToken token;
690 [fullScreenWindow setFrame:screenRect display:NO];
692 CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &token);
693 CGDisplayFade( token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
695 if ([screen isMainScreen])
696 SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
699 [[self superview] replaceSubview:self with:tempFullScreenView];
700 [tempFullScreenView setFrame:[self frame]];
701 [fullScreenWindow setContentView:self];
702 [fullScreenWindow makeKeyAndOrderFront:self];
704 [[tempFullScreenView window] orderOut: self];
706 CGDisplayFade( token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
707 CGReleaseDisplayFadeReservation( token);
709 [self hasBecomeFullScreen];
714 /* Make sure we don't see the o_view disappearing of the screen during this operation */
715 DisableScreenUpdates();
716 [self retain]; /* Removing from a view, make sure we won't be released */
717 /* Make sure our layer won't disappear */
718 CALayer * layer = [[self layer] retain];
719 id alayoutManager = layer.layoutManager;
720 [[self superview] replaceSubview:self with:tempFullScreenView];
721 [tempFullScreenView setFrame:[self frame]];
722 [fullScreenWindow setContentView:self];
723 [self setWantsLayer:YES];
724 [self setLayer:layer];
725 layer.layoutManager = alayoutManager;
727 [fullScreenWindow makeKeyAndOrderFront:self];
728 EnableScreenUpdates();
731 /* We are in fullScreen (and no animation is running) */
734 /* Make sure we are hidden */
735 [[tempFullScreenView window] orderOut: self];
741 [fullScreenAnim1 stopAnimation];
742 [fullScreenAnim1 release];
746 [fullScreenAnim2 stopAnimation];
747 [fullScreenAnim2 release];
750 if ([screen isMainScreen])
751 SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
753 dict1 = [[NSMutableDictionary alloc] initWithCapacity:2];
754 dict2 = [[NSMutableDictionary alloc] initWithCapacity:3];
756 [dict1 setObject:[tempFullScreenView window] forKey:NSViewAnimationTargetKey];
757 [dict1 setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
759 [dict2 setObject:fullScreenWindow forKey:NSViewAnimationTargetKey];
760 [dict2 setObject:[NSValue valueWithRect:[fullScreenWindow frame]] forKey:NSViewAnimationStartFrameKey];
761 [dict2 setObject:[NSValue valueWithRect:screenRect] forKey:NSViewAnimationEndFrameKey];
763 /* Strategy with NSAnimation allocation:
764 - Keep at most 2 animation at a time
765 - leaveFullScreen/enterFullScreen are the only responsible for releasing and alloc-ing
767 fullScreenAnim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict1, nil]];
768 fullScreenAnim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict2, nil]];
773 [fullScreenAnim1 setAnimationBlockingMode: NSAnimationNonblocking];
774 [fullScreenAnim1 setDuration: 0.3];
775 [fullScreenAnim1 setFrameRate: 30];
776 [fullScreenAnim2 setAnimationBlockingMode: NSAnimationNonblocking];
777 [fullScreenAnim2 setDuration: 0.3];
778 [fullScreenAnim2 setFrameRate: 30];
780 [fullScreenAnim2 setDelegate: self];
781 [fullScreenAnim2 startWhenAnimation: fullScreenAnim1 reachesProgress: 1.0];
783 [fullScreenAnim1 startAnimation];
786 - (void)hasBecomeFullScreen
788 [fullScreenWindow makeFirstResponder: self];
790 [fullScreenWindow makeKeyWindow];
791 [fullScreenWindow setAcceptsMouseMovedEvents: TRUE];
793 [[tempFullScreenView window] orderOut: self];
794 [self willChangeValueForKey:@"fullScreen"];
796 [self didChangeValueForKey:@"fullScreen"];
799 - (void)leaveFullScreen
801 [self leaveFullScreenAndFadeOut: NO];
804 - (void)leaveFullScreenAndFadeOut: (BOOL)fadeout
806 NSMutableDictionary *dict1, *dict2;
809 [self willChangeValueForKey:@"fullScreen"];
811 [self didChangeValueForKey:@"fullScreen"];
813 /* Don't do anything if o_fullScreen_window is already closed */
814 if (!fullScreenWindow)
817 if (fadeout || [tempFullScreenView isHiddenOrHasHiddenAncestor])
819 /* We don't animate if we are not visible, instead we
820 * simply fade the display */
821 CGDisplayFadeReservationToken token;
823 CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &token);
824 CGDisplayFade( token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
826 SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
828 [self hasEndedFullScreen];
830 CGDisplayFade( token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
831 CGReleaseDisplayFadeReservation( token);
835 [[tempFullScreenView window] setAlphaValue: 0.0];
836 [[tempFullScreenView window] orderFront: self];
838 SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
842 [fullScreenAnim1 stopAnimation];
843 [fullScreenAnim1 release];
847 [fullScreenAnim2 stopAnimation];
848 [fullScreenAnim2 release];
851 frame = [[tempFullScreenView superview] convertRect: [tempFullScreenView frame] toView: nil]; /* Convert to Window base coord */
852 frame.origin.x += [tempFullScreenView window].frame.origin.x;
853 frame.origin.y += [tempFullScreenView window].frame.origin.y;
855 dict2 = [[NSMutableDictionary alloc] initWithCapacity:2];
856 [dict2 setObject:[tempFullScreenView window] forKey:NSViewAnimationTargetKey];
857 [dict2 setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
859 fullScreenAnim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict2, nil]];
862 [fullScreenAnim2 setAnimationBlockingMode: NSAnimationNonblocking];
863 [fullScreenAnim2 setDuration: 0.3];
864 [fullScreenAnim2 setFrameRate: 30];
866 [fullScreenAnim2 setDelegate: self];
868 dict1 = [[NSMutableDictionary alloc] initWithCapacity:3];
870 [dict1 setObject:fullScreenWindow forKey:NSViewAnimationTargetKey];
871 [dict1 setObject:[NSValue valueWithRect:[fullScreenWindow frame]] forKey:NSViewAnimationStartFrameKey];
872 [dict1 setObject:[NSValue valueWithRect:frame] forKey:NSViewAnimationEndFrameKey];
874 fullScreenAnim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict1, nil]];
877 [fullScreenAnim1 setAnimationBlockingMode: NSAnimationNonblocking];
878 [fullScreenAnim1 setDuration: 0.2];
879 [fullScreenAnim1 setFrameRate: 30];
880 [fullScreenAnim2 startWhenAnimation: fullScreenAnim1 reachesProgress: 1.0];
882 /* Make sure o_fullScreen_window is the frontmost window */
883 [fullScreenWindow orderFront: self];
885 [fullScreenAnim1 startAnimation];
888 - (void)hasEndedFullScreen
890 /* This function is private and should be only triggered at the end of the fullScreen change animation */
891 /* Make sure we don't see the o_view disappearing of the screen during this operation */
892 DisableScreenUpdates();
894 /* Make sure we don't loose the layer */
895 CALayer * layer = [[self layer] retain];
896 id alayoutManager = layer.layoutManager;
897 [self removeFromSuperviewWithoutNeedingDisplay];
898 [[tempFullScreenView superview] replaceSubview:tempFullScreenView with:self];
900 [self setWantsLayer:YES];
901 [self setLayer:layer];
902 layer.layoutManager = alayoutManager;
904 [self setFrame:[tempFullScreenView frame]];
905 [[self window] makeFirstResponder: self];
906 if ([[self window] isVisible])
907 [[self window] makeKeyAndOrderFront:self];
908 [fullScreenWindow orderOut: self];
909 EnableScreenUpdates();
911 [fullScreenWindow release];
912 fullScreenWindow = nil;
915 - (void)animationDidEnd:(NSAnimation*)animation
917 NSArray *viewAnimations;
919 if ([animation currentValue] < 1.0)
922 /* FullScreen ended or started (we are a delegate only for leaveFullScreen's/enterFullscren's anim2) */
923 viewAnimations = [fullScreenAnim2 viewAnimations];
924 if ([viewAnimations count] >=1 &&
925 [[[viewAnimations objectAtIndex: 0] objectForKey: NSViewAnimationEffectKey] isEqualToString:NSViewAnimationFadeInEffect])
927 /* FullScreen ended */
928 [self hasEndedFullScreen];
932 /* FullScreen started */
933 [self hasBecomeFullScreen];