X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=OSX%2FSaverRunner.m;h=80031e775f6456b895cda3c6da664462e5a4317c;hp=e2cd11c5808b521a57e9417583fb8d4f28ca9ba1;hb=d5186197bc394e10a4402f7f6d23fbb14103bc50;hpb=6afd6db0ae9396cd7ff897ade597cd5483f49b0e diff --git a/OSX/SaverRunner.m b/OSX/SaverRunner.m index e2cd11c5..80031e77 100644 --- a/OSX/SaverRunner.m +++ b/OSX/SaverRunner.m @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 2006-2013 Jamie Zawinski +/* xscreensaver, Copyright (c) 2006-2014 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -38,13 +38,40 @@ #ifdef USE_IPHONE @interface RotateyViewController : UINavigationController +{ + BOOL allowRotation; +} @end @implementation RotateyViewController + +/* This subclass exists so that we can ask that the SaverListController and + preferences panels be auto-rotated by the system. Note that the + XScreenSaverView is not auto-rotated because it is on a different UIWindow. + */ + +- (id)initWithRotation:(BOOL)rotatep +{ + self = [super init]; + allowRotation = rotatep; + return self; +} + - (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)o { - return YES; + return allowRotation; /* Deprecated in iOS 6 */ } + +- (BOOL)shouldAutorotate /* Added in iOS 6 */ +{ + return allowRotation; +} + +- (NSUInteger)supportedInterfaceOrientations /* Added in iOS 6 */ +{ + return UIInterfaceOrientationMaskAll; +} + @end #endif // USE_IPHONE @@ -354,7 +381,7 @@ relabel_menus (NSObject *v, NSString *old_str, NSString *new_str) [prefs setObject:saver forKey:@"selectedSaverName"]; [prefs synchronize]; - [rootViewController pushViewController: [saverView configureView] + [rotating_nav pushViewController: [saverView configureView] animated:YES]; } @@ -431,15 +458,6 @@ relabel_menus (NSObject *v, NSString *old_str, NSString *new_str) saverName = name; - if (! backgroundView) { - // This view is the parent of the XScreenSaverView, and exists only - // so that there is a black background behind it. Without this, when - // rotation is in progress, the scrolling-list window's corners show - // through in the corners. - backgroundView = [[[NSView class] alloc] initWithFrame:[window frame]]; - [backgroundView setBackgroundColor:[NSColor blackColor]]; - } - if (saverView) { if ([saverView isAnimating]) [saverView stopAnimation]; @@ -449,8 +467,27 @@ relabel_menus (NSObject *v, NSString *old_str, NSString *new_str) [saverView release]; } - NSSize size = [window frame].size; - saverView = [self makeSaverView:name withSize: size]; + /* We can't just use [window bounds] because that is the *rotated* rectangle + and we need the *unrotated* rectangle, so that the view is always created + in portrait orientation. Without that, the initial rotation event that + takes us from unknown->landscape will be out of step with reality. + */ + UIScreen *screen = [UIScreen mainScreen]; +# ifndef __IPHONE_8_0 // iOS 7 SDK + NSSize size = [screen bounds].size; + int ss = [screen scale]; +# else // iOS 8 SDK + NSSize size = ([screen respondsToSelector:@selector(nativeBounds)] + ? [screen nativeBounds].size // iOS 8 + : [screen bounds].size); // iOS 7 + int ss = ([screen respondsToSelector:@selector(nativeScale)] + ? [screen nativeScale] // iOS 8 + : [screen scale]); // iOS 7 +# endif // iOS 8 SDK + + size.width /= ss; + size.height /= ss; + saverView = [self makeSaverView:name withSize:size]; if (! saverView) { [[[UIAlertView alloc] initWithTitle: name @@ -462,20 +499,41 @@ relabel_menus (NSObject *v, NSString *old_str, NSString *new_str) return; } - [saverView setFrame: [window frame]]; [[NSNotificationCenter defaultCenter] addObserver:saverView selector:@selector(didRotate:) name:UIDeviceOrientationDidChangeNotification object:nil]; LAUNCH: + if (launch) { [self saveScreenshot]; - [window addSubview: backgroundView]; + NSRect f = [saverWindow bounds]; + [backgroundView setFrame:f]; + [saverView setFrame:f]; + [saverWindow addSubview: backgroundView]; [backgroundView addSubview: saverView]; - [saverView becomeFirstResponder]; + [saverView setBackgroundColor:[NSColor blackColor]]; + + /* WTF! Without creating and keying this window, we get no events + delivered on the saverView/saverWindow! Bad craziness. + */ + { + UIWindow *dummy = [[UIWindow alloc] initWithFrame:CGRectMake(0,0,0,0)]; + [dummy setRootViewController: nonrotating_nav]; // Must be this one. + [dummy setHidden:NO]; // required + [dummy setHidden:YES]; + [dummy release]; + } + + [saverWindow setHidden:NO]; + [saverWindow makeKeyAndVisible]; [saverView startAnimation]; [self aboutPanel:nil]; + + // Doing this makes savers cut back to the list instead of fading, + // even though [XScreenSaverView stopAndClose] does setHidden:NO first. + // [window setHidden:YES]; } # endif // USE_IPHONE } @@ -521,16 +579,37 @@ relabel_menus (NSObject *v, NSString *old_str, NSString *new_str) CGFloat pt2 = 14; UIFont *font1 = [UIFont boldSystemFontOfSize: pt1]; UIFont *font2 = [UIFont italicSystemFontOfSize:pt2]; + +# ifdef __IPHONE_7_0 + CGSize s = CGSizeMake(frame.size.width, frame.size.height); + CGSize tsize1 = [[[NSAttributedString alloc] + initWithString: name + attributes:@{ NSFontAttributeName: font1 }] + boundingRectWithSize: s + options: NSStringDrawingUsesLineFragmentOrigin + context: nil].size; + CGSize tsize2 = [[[NSAttributedString alloc] + initWithString: name + attributes:@{ NSFontAttributeName: font2 }] + boundingRectWithSize: s + options: NSStringDrawingUsesLineFragmentOrigin + context: nil].size; +# else // iOS 6 or Cocoa CGSize tsize1 = [name sizeWithFont:font1 constrainedToSize:CGSizeMake(frame.size.width, frame.size.height)]; CGSize tsize2 = [year sizeWithFont:font2 constrainedToSize:CGSizeMake(frame.size.width, frame.size.height)]; +#endif + CGSize tsize = CGSizeMake (tsize1.width > tsize2.width ? tsize1.width : tsize2.width, tsize1.height + tsize2.height); + tsize.width = ceilf(tsize.width); + tsize.height = ceilf(tsize.height); + // Don't know how to find inner margin of UITextView. CGFloat margin = 10; tsize.width += margin * 4; @@ -541,10 +620,7 @@ relabel_menus (NSObject *v, NSString *old_str, NSString *new_str) frame = CGRectMake (0, 0, tsize.width, tsize.height); - UIInterfaceOrientation orient = - // Why are both of these wrong when starting up rotated?? - [[UIDevice currentDevice] orientation]; - // [rootViewController interfaceOrientation]; + UIInterfaceOrientation orient = [rotating_nav interfaceOrientation]; /* Get the text oriented properly, and move it to the bottom of the screen, since many savers have action in the middle. @@ -604,7 +680,7 @@ relabel_menus (NSObject *v, NSString *old_str, NSString *new_str) textview = [[UITextView alloc] initWithFrame:frame]; textview.font = (j == 0 ? font1 : font2); textview.text = (j == 0 ? name : year); - textview.textAlignment = UITextAlignmentCenter; + textview.textAlignment = NSTextAlignmentCenter; textview.showsHorizontalScrollIndicator = NO; textview.showsVerticalScrollIndicator = NO; textview.scrollEnabled = NO; @@ -881,14 +957,23 @@ relabel_menus (NSObject *v, NSString *old_str, NSString *new_str) } // Delete everything after the first blank line. + // r = [desc rangeOfString:@"\n\n" options:0]; if (r.length > 0) desc = [desc substringToIndex: r.location]; - // Truncate really long ones. - int max = 140; - if ([desc length] > max) - desc = [desc substringToIndex: max]; + // Unwrap lines and compress whitespace. + { + NSString *result = @""; + for (NSString *s in [desc componentsSeparatedByCharactersInSet: + [NSCharacterSet whitespaceAndNewlineCharacterSet]]) { + if ([result length] == 0) + result = s; + else if ([s length] > 0) + result = [NSString stringWithFormat: @"%@ %@", result, s]; + desc = result; + } + } if (year) desc = [year stringByAppendingString: @@ -1112,23 +1197,63 @@ FAIL: # undef ya_rand_init ya_rand_init (0); // Now's a good time. - rootViewController = [[[RotateyViewController alloc] init] retain]; - [window setRootViewController: rootViewController]; + rotating_nav = [[[RotateyViewController alloc] initWithRotation:YES] + retain]; + [window setRootViewController: rotating_nav]; + [window setAutoresizesSubviews:YES]; + [window setAutoresizingMask: + (UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleHeight)]; + + nonrotating_nav = [[[RotateyViewController alloc] initWithRotation:NO] + retain]; + [nonrotating_nav setNavigationBarHidden:YES animated:NO]; + + /* We run the saver on a different UIWindow than the one the + SaverListController and preferences panels run on, because that's + the only way to make rotation work right. We want the system to + handle rotation of the UI stuff, but we want it to keep its hands + off of rotation of the savers. As of iOS 8, this seems to be the + only way to accomplish that. + + Also, we need to create saverWindow with a portrait rectangle, always. + Note that [UIScreen bounds] returns rotated and scaled values. + */ + UIScreen *screen = [UIScreen mainScreen]; +# ifndef __IPHONE_8_0 // iOS 7 SDK + NSRect frame = [screen bounds]; + int ss = [screen scale]; +# else // iOS 8 SDK + NSRect frame = ([screen respondsToSelector:@selector(nativeBounds)] + ? [screen nativeBounds] // iOS 8 + : [screen bounds]); // iOS 7 + int ss = ([screen respondsToSelector:@selector(nativeScale)] + ? [screen nativeScale] // iOS 8 + : [screen scale]); // iOS 7 +# endif // iOS 8 SDK + frame.size.width /= ss; + frame.size.height /= ss; + saverWindow = [[UIWindow alloc] initWithFrame:frame]; + [saverWindow setRootViewController: nonrotating_nav]; + [saverWindow setHidden:YES]; + + /* This view is the parent of the XScreenSaverView, and exists only + so that there is a black background behind it. Without this, when + rotation is in progress, the scrolling-list window's corners show + through in the corners. + */ + backgroundView = [[[NSView class] alloc] initWithFrame:[saverWindow frame]]; + [backgroundView setBackgroundColor:[NSColor blackColor]]; SaverListController *menu = [[SaverListController alloc] initWithNames:saverNames descriptions:[self makeDescTable]]; - [rootViewController pushViewController:menu animated:YES]; + [rotating_nav pushViewController:menu animated:YES]; [menu becomeFirstResponder]; - [window makeKeyAndVisible]; - [window setAutoresizesSubviews:YES]; - [window setAutoresizingMask: - (UIViewAutoresizingFlexibleWidth | - UIViewAutoresizingFlexibleHeight)]; - application.applicationSupportsShakeToEdit = YES; + # endif // USE_IPHONE NSString *forced = 0; @@ -1183,6 +1308,12 @@ FAIL: # endif [self selectedSaverDidChange:nil]; +// [NSTimer scheduledTimerWithTimeInterval: 0 +// target:self +// selector:@selector(selectedSaverDidChange:) +// userInfo:nil +// repeats:NO]; + # ifndef USE_IPHONE