Some more iOS orientation rotation fixes.
authorRyan C. Gordon <icculus@icculus.org>
Mon, 04 Apr 2011 23:38:15 -0400
changeset 5529 8c0d15077360
parent 5528 15c9c03a80cc
child 5530 4e46a7b6773d
Some more iOS orientation rotation fixes. - Always use a UIViewController, even if window is not resizable. - Let non-resizable windows still flip over, so user can hold device with the correct orientation, but upside down, if that's more comfortable. - Don't set the UIScreen unless we're forced to, as it resets some state. - Minor correction with conventions for -[self init] tapdance.
src/video/uikit/SDL_uikitwindow.m
--- a/src/video/uikit/SDL_uikitwindow.m	Mon Apr 04 09:29:13 2011 -0700
+++ b/src/video/uikit/SDL_uikitwindow.m	Mon Apr 04 23:38:15 2011 -0400
@@ -41,13 +41,35 @@
 @implementation SDL_uikitviewcontroller
 
 - (id)initWithSDLWindow:(SDL_Window *)_window {
-    [self init];
+    if ((self = [self init]) == nil) {
+        return nil;
+    }
     self->window = _window;
     return self;
 }
 
 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orient {
-    return YES;
+    if (self->window->flags & SDL_WINDOW_RESIZABLE) {
+        return YES;  // any orientation is okay.
+    }
+
+    // If not resizable, allow device to orient to other matching sizes
+    //  (that is, let the user turn the device upside down...same screen
+    //   dimensions, but it lets the user place the device where it's most
+    //   comfortable in relation to its physical buttons, headphone jack, etc).
+    switch (orient) {
+        case UIInterfaceOrientationLandscapeLeft:
+        case UIInterfaceOrientationLandscapeRight:
+            return (self->window->w >= self->window->h);
+
+        case UIInterfaceOrientationPortrait:
+        case UIInterfaceOrientationPortraitUpsideDown:
+            return (self->window->h >= self->window->w);
+
+        default: break;
+    }
+
+    return NO;  // Nothing else is acceptable.
 }
 
 - (void)loadView  {
@@ -56,10 +78,16 @@
 
 // Send a resized event when the orientation changes.
 - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
+    if ((self->window->flags & SDL_WINDOW_RESIZABLE) == 0) {
+        return;   // don't care, we're just flipping over in this case.
+    }
+
     const UIInterfaceOrientation toInterfaceOrientation = [self interfaceOrientation];
     SDL_WindowData *data = self->window->driverdata;
     UIWindow *uiwindow = data->uiwindow;
-    CGRect frame = [uiwindow frame];
+    UIScreen *uiscreen = [uiwindow screen];
+    const int noborder = self->window->flags & SDL_WINDOW_BORDERLESS;
+    CGRect frame = noborder ? [uiscreen bounds] : [uiscreen applicationFrame];
     const CGSize size = frame.size;
     int w, h;
 
@@ -83,6 +111,9 @@
 
     frame.size.width = w;
     frame.size.height = h;
+    frame.origin.x = 0;
+    frame.origin.y = 0;
+
     [uiwindow setFrame:frame];
     [data->view updateFrame];
     SDL_SendWindowEvent(self->window, SDL_WINDOWEVENT_RESIZED, w, h);
@@ -139,31 +170,20 @@
             [UIApplication sharedApplication].statusBarHidden = NO;
         }
 
-        const CGSize uisize = [[uiscreen currentMode] size];
         const UIDeviceOrientation o = [[UIDevice currentDevice] orientation];
         const BOOL landscape = (o == UIDeviceOrientationLandscapeLeft) ||
                                    (o == UIDeviceOrientationLandscapeRight);
         const BOOL rotate = ( ((window->w > window->h) && (!landscape)) ||
                               ((window->w < window->h) && (landscape)) );
 
-        if (window->flags & SDL_WINDOW_RESIZABLE) {
-            // The View Controller will handle rotating the view when the
-            //  device orientation changes. We expose these as resize events.
-            SDL_uikitviewcontroller *controller;
-            controller = [SDL_uikitviewcontroller alloc];
-            data->viewcontroller = [controller initWithSDLWindow:window];
-            [data->viewcontroller setTitle:@"SDL App"];  // !!! FIXME: hook up SDL_SetWindowTitle()
-            // !!! FIXME: if (rotate), force a "resize" right at the start
-        } else {
-            // Rotate the view if we have to, but only on the main screen
-            //  (presumably, an external display doesn't report orientation).
-            if (rotate) {
-                #define D2R(x) (M_PI * (x) / 180.0)   // degrees to radians.
-                [uiwindow setTransform:CGAffineTransformIdentity];
-                [uiwindow setTransform:CGAffineTransformMakeRotation(D2R(90))];
-                #undef D2R
-            }
-        }
+        // The View Controller will handle rotating the view when the
+        //  device orientation changes. This will trigger resize events, if
+        //  appropriate.
+        SDL_uikitviewcontroller *controller;
+        controller = [SDL_uikitviewcontroller alloc];
+        data->viewcontroller = [controller initWithSDLWindow:window];
+        [data->viewcontroller setTitle:@"SDL App"];  // !!! FIXME: hook up SDL_SetWindowTitle()
+        // !!! FIXME: if (rotate), force a "resize" right at the start
     }
 
     return 0;
@@ -174,6 +194,7 @@
 {
     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
     UIScreen *uiscreen = (UIScreen *) display->driverdata;
+    const BOOL external = ([UIScreen mainScreen] != uiscreen);
 
     // SDL currently puts this window at the start of display's linked list. We rely on this.
     SDL_assert(_this->windows == window);
@@ -187,7 +208,7 @@
     // Non-mainscreen windows must be force to borderless, as there's no
     //  status bar there, and we want to get the right dimensions later in
     //  this function.
-    if ([UIScreen mainScreen] != uiscreen) {
+    if (external) {
         window->flags |= SDL_WINDOW_BORDERLESS;
     }
 
@@ -225,8 +246,12 @@
         uiwindow = [uiwindow initWithFrame:[uiscreen bounds]];
     else
         uiwindow = [uiwindow initWithFrame:[uiscreen applicationFrame]];
-
-    if (SDL_UIKit_supports_multiple_displays) {
+    
+    // put the window on an external display if appropriate. This implicitly
+    //  does [uiwindow setframe:[uiscreen bounds]], so don't do it on the
+    //  main display, where we land by default, as that would eat the
+    //  status bar real estate.
+    if (external) {
         [uiwindow setScreen:uiscreen];
     }