src/video/uikit/SDL_uikitview.m
branchiOS-improvements
changeset 9532 318042c16b76
parent 9529 4bf9830d8153
child 9551 950a1a7b4c3c
equal deleted inserted replaced
9531:b7ad394978b1 9532:318042c16b76
    22 
    22 
    23 #if SDL_VIDEO_DRIVER_UIKIT
    23 #if SDL_VIDEO_DRIVER_UIKIT
    24 
    24 
    25 #include "SDL_uikitview.h"
    25 #include "SDL_uikitview.h"
    26 
    26 
    27 #include "../../events/SDL_keyboard_c.h"
       
    28 #include "../../events/SDL_mouse_c.h"
    27 #include "../../events/SDL_mouse_c.h"
    29 #include "../../events/SDL_touch_c.h"
    28 #include "../../events/SDL_touch_c.h"
       
    29 #include "../../events/SDL_events_c.h"
    30 
    30 
    31 #if SDL_IPHONE_KEYBOARD
    31 #import "SDL_uikitappdelegate.h"
    32 #include "keyinfotable.h"
    32 #import "SDL_uikitmodes.h"
    33 #endif
    33 #import "SDL_uikitwindow.h"
    34 #include "SDL_uikitappdelegate.h"
       
    35 #include "SDL_uikitmodes.h"
       
    36 #include "SDL_uikitwindow.h"
       
    37 
    34 
    38 @implementation SDL_uikitview {
    35 @implementation SDL_uikitview {
       
    36     SDL_Window *sdlwindow;
    39 
    37 
    40     SDL_TouchID touchId;
    38     SDL_TouchID touchId;
    41     UITouch *leftFingerDown;
    39     UITouch * __weak firstFingerDown;
    42 
       
    43 #if SDL_IPHONE_KEYBOARD
       
    44     UITextField *textField;
       
    45 #endif
       
    46 
       
    47 }
    40 }
    48 
    41 
    49 @synthesize sdlwindow;
    42 - (instancetype)initWithFrame:(CGRect)frame
    50 
       
    51 - (id)initWithFrame:(CGRect)frame
       
    52 {
    43 {
    53     if (self = [super initWithFrame:frame]) {
    44     if ((self = [super initWithFrame:frame])) {
    54 #if SDL_IPHONE_KEYBOARD
    45         self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    55         [self initKeyboard];
    46         self.autoresizesSubviews = YES;
    56 #endif
       
    57 
    47 
    58         self.multipleTouchEnabled = YES;
    48         self.multipleTouchEnabled = YES;
    59 
    49 
    60         touchId = 1;
    50         touchId = 1;
    61         SDL_AddTouch(touchId, "");
    51         SDL_AddTouch(touchId, "");
    62     }
    52     }
    63 
    53 
    64     return self;
    54     return self;
    65 
       
    66 }
    55 }
    67 
    56 
    68 - (void)dealloc
    57 - (void)setSDLWindow:(SDL_Window *)window
    69 {
    58 {
    70 #if SDL_IPHONE_KEYBOARD
    59     SDL_WindowData *data = nil;
    71     [self deinitKeyboard];
    60 
    72 #endif
    61     if (window == sdlwindow) {
       
    62         return;
       
    63     }
       
    64 
       
    65     if (sdlwindow) {
       
    66         SDL_uikitview *view = nil;
       
    67         data = (__bridge SDL_WindowData *) sdlwindow->driverdata;
       
    68 
       
    69         [data.views removeObject:self];
       
    70 
       
    71         [self removeFromSuperview];
       
    72 
       
    73         /* Restore the next-oldest view in the old window. */
       
    74         if (data.views.count > 0) {
       
    75             view = data.views[data.views.count - 1];
       
    76         }
       
    77 
       
    78         data.viewcontroller.view = view;
       
    79 
       
    80         if (data.uiwindow.rootViewController != data.viewcontroller) {
       
    81             data.uiwindow.rootViewController = data.viewcontroller;
       
    82         } else if (view) {
       
    83             [data.uiwindow addSubview:view];
       
    84         }
       
    85 
       
    86         [data.uiwindow layoutIfNeeded];
       
    87     }
       
    88 
       
    89     if (window) {
       
    90         data = (__bridge SDL_WindowData *) window->driverdata;
       
    91 
       
    92         /* Make sure the SDL window has a strong reference to this view. */
       
    93         [data.views addObject:self];
       
    94 
       
    95         /* Replace the view controller's old view with this one. */
       
    96         [data.viewcontroller.view removeFromSuperview];
       
    97         data.viewcontroller.view = self;
       
    98 
       
    99         if (data.uiwindow.rootViewController != data.viewcontroller) {
       
   100             /* The root view controller handles rotation and the status bar.
       
   101              * Assigning it also adds the controller's view to the window. */
       
   102             data.uiwindow.rootViewController = data.viewcontroller;
       
   103         } else {
       
   104             [data.uiwindow addSubview:self];
       
   105         }
       
   106 
       
   107         /* The view's bounds may not be correct until the next event cycle. That
       
   108          * might happen after the current dimensions are queried, so we force a
       
   109          * layout now to immediately update the bounds. */
       
   110         [data.uiwindow layoutIfNeeded];
       
   111     }
       
   112 
       
   113     sdlwindow = window;
    73 }
   114 }
    74 
   115 
    75 - (CGPoint)touchLocation:(UITouch *)touch shouldNormalize:(BOOL)normalize
   116 - (CGPoint)touchLocation:(UITouch *)touch shouldNormalize:(BOOL)normalize
    76 {
   117 {
    77     CGPoint point = [touch locationInView:self];
   118     CGPoint point = [touch locationInView:self];
    86 }
   127 }
    87 
   128 
    88 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
   129 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    89 {
   130 {
    90     for (UITouch *touch in touches) {
   131     for (UITouch *touch in touches) {
    91         if (!leftFingerDown) {
   132         if (!firstFingerDown) {
    92             CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO];
   133             CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO];
    93 
   134 
    94             /* send moved event */
   135             /* send mouse moved event */
    95             SDL_SendMouseMotion(sdlwindow, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y);
   136             SDL_SendMouseMotion(sdlwindow, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y);
    96 
   137 
    97             /* send mouse down event */
   138             /* send mouse down event */
    98             SDL_SendMouseButton(sdlwindow, SDL_TOUCH_MOUSEID, SDL_PRESSED, SDL_BUTTON_LEFT);
   139             SDL_SendMouseButton(sdlwindow, SDL_TOUCH_MOUSEID, SDL_PRESSED, SDL_BUTTON_LEFT);
    99 
   140 
   100             leftFingerDown = touch;
   141             firstFingerDown = touch;
   101         }
   142         }
   102 
   143 
   103         CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
   144         CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
   104         SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
   145         SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
   105                       SDL_TRUE, locationInView.x, locationInView.y, 1.0f);
   146                       SDL_TRUE, locationInView.x, locationInView.y, 1.0f);
   107 }
   148 }
   108 
   149 
   109 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
   150 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
   110 {
   151 {
   111     for (UITouch *touch in touches) {
   152     for (UITouch *touch in touches) {
   112         if (touch == leftFingerDown) {
   153         if (touch == firstFingerDown) {
   113             /* send mouse up */
   154             /* send mouse up */
   114             SDL_SendMouseButton(sdlwindow, SDL_TOUCH_MOUSEID, SDL_RELEASED, SDL_BUTTON_LEFT);
   155             SDL_SendMouseButton(sdlwindow, SDL_TOUCH_MOUSEID, SDL_RELEASED, SDL_BUTTON_LEFT);
   115             leftFingerDown = nil;
   156             firstFingerDown = nil;
   116         }
   157         }
   117 
   158 
   118         CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
   159         CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
   119         SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
   160         SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
   120                       SDL_FALSE, locationInView.x, locationInView.y, 1.0f);
   161                       SDL_FALSE, locationInView.x, locationInView.y, 1.0f);
   121     }
   162     }
   122 }
   163 }
   123 
   164 
   124 - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
   165 - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
   125 {
   166 {
   126     /*
       
   127         this can happen if the user puts more than 5 touches on the screen
       
   128         at once, or perhaps in other circumstances.  Usually (it seems)
       
   129         all active touches are canceled.
       
   130     */
       
   131     [self touchesEnded:touches withEvent:event];
   167     [self touchesEnded:touches withEvent:event];
   132 }
   168 }
   133 
   169 
   134 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
   170 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
   135 {
   171 {
   136     for (UITouch *touch in touches) {
   172     for (UITouch *touch in touches) {
   137         if (touch == leftFingerDown) {
   173         if (touch == firstFingerDown) {
   138             CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO];
   174             CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO];
   139 
   175 
   140             /* send moved event */
   176             /* send moved event */
   141             SDL_SendMouseMotion(sdlwindow, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y);
   177             SDL_SendMouseMotion(sdlwindow, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y);
   142         }
   178         }
   145         SDL_SendTouchMotion(touchId, (SDL_FingerID)((size_t)touch),
   181         SDL_SendTouchMotion(touchId, (SDL_FingerID)((size_t)touch),
   146                             locationInView.x, locationInView.y, 1.0f);
   182                             locationInView.x, locationInView.y, 1.0f);
   147     }
   183     }
   148 }
   184 }
   149 
   185 
   150 /*
       
   151     ---- Keyboard related functionality below this line ----
       
   152 */
       
   153 #if SDL_IPHONE_KEYBOARD
       
   154 
       
   155 @synthesize textInputRect;
       
   156 @synthesize keyboardHeight;
       
   157 @synthesize keyboardVisible;
       
   158 
       
   159 /* Set ourselves up as a UITextFieldDelegate */
       
   160 - (void)initKeyboard
       
   161 {
       
   162     textField = [[UITextField alloc] initWithFrame:CGRectZero];
       
   163     textField.delegate = self;
       
   164     /* placeholder so there is something to delete! */
       
   165     textField.text = @" ";
       
   166 
       
   167     /* set UITextInputTrait properties, mostly to defaults */
       
   168     textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
       
   169     textField.autocorrectionType = UITextAutocorrectionTypeNo;
       
   170     textField.enablesReturnKeyAutomatically = NO;
       
   171     textField.keyboardAppearance = UIKeyboardAppearanceDefault;
       
   172     textField.keyboardType = UIKeyboardTypeDefault;
       
   173     textField.returnKeyType = UIReturnKeyDefault;
       
   174     textField.secureTextEntry = NO;
       
   175 
       
   176     textField.hidden = YES;
       
   177     keyboardVisible = NO;
       
   178     /* add the UITextField (hidden) to our view */
       
   179     [self addSubview:textField];
       
   180 
       
   181     NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
       
   182     [center addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
       
   183     [center addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
       
   184 }
       
   185 
       
   186 - (void)deinitKeyboard
       
   187 {
       
   188     NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
       
   189     [center removeObserver:self name:UIKeyboardWillShowNotification object:nil];
       
   190     [center removeObserver:self name:UIKeyboardWillHideNotification object:nil];
       
   191 }
       
   192 
       
   193 /* reveal onscreen virtual keyboard */
       
   194 - (void)showKeyboard
       
   195 {
       
   196     keyboardVisible = YES;
       
   197     [textField becomeFirstResponder];
       
   198 }
       
   199 
       
   200 /* hide onscreen virtual keyboard */
       
   201 - (void)hideKeyboard
       
   202 {
       
   203     keyboardVisible = NO;
       
   204     [textField resignFirstResponder];
       
   205 }
       
   206 
       
   207 - (void)keyboardWillShow:(NSNotification *)notification
       
   208 {
       
   209     CGRect kbrect = [[notification userInfo][UIKeyboardFrameBeginUserInfoKey] CGRectValue];
       
   210     int height;
       
   211 
       
   212     /* The keyboard rect is in the coordinate space of the screen, but we want
       
   213      * its height in the view's coordinate space.
       
   214      */
       
   215 #ifdef __IPHONE_8_0
       
   216     if ([self respondsToSelector:@selector(convertRect:fromCoordinateSpace:)]) {
       
   217         UIScreen *screen = self.window.screen;
       
   218         kbrect = [self convertRect:kbrect fromCoordinateSpace:screen.coordinateSpace];
       
   219         height = kbrect.size.height;
       
   220     } else
       
   221 #endif
       
   222     {
       
   223         /* In iOS 7 and below, the screen's coordinate space is never rotated. */
       
   224         if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) {
       
   225             height = kbrect.size.width;
       
   226         } else {
       
   227             height = kbrect.size.height;
       
   228         }
       
   229     }
       
   230 
       
   231     [self setKeyboardHeight:height];
       
   232 }
       
   233 
       
   234  - (void)keyboardWillHide:(NSNotification *)notification
       
   235 {
       
   236     [self setKeyboardHeight:0];
       
   237 }
       
   238 
       
   239 - (void)updateKeyboard
       
   240 {
       
   241     SDL_Rect textrect = self.textInputRect;
       
   242     CGAffineTransform t = self.transform;
       
   243     CGPoint offset = CGPointMake(0.0, 0.0);
       
   244 
       
   245     if (self.keyboardHeight) {
       
   246         int rectbottom = textrect.y + textrect.h;
       
   247         int kbottom = self.bounds.size.height - self.keyboardHeight;
       
   248         if (kbottom < rectbottom) {
       
   249             offset.y = kbottom - rectbottom;
       
   250         }
       
   251     }
       
   252 
       
   253     /* Put the offset into the this view transform's coordinate space. */
       
   254     t.tx = 0.0;
       
   255     t.ty = 0.0;
       
   256     offset = CGPointApplyAffineTransform(offset, t);
       
   257 
       
   258     t.tx = offset.x;
       
   259     t.ty = offset.y;
       
   260 
       
   261     /* Move the view by applying the updated transform. */
       
   262     self.transform = t;
       
   263 }
       
   264 
       
   265 - (void)setKeyboardHeight:(int)height
       
   266 {
       
   267     keyboardVisible = height > 0;
       
   268     keyboardHeight = height;
       
   269     [self updateKeyboard];
       
   270 }
       
   271 
       
   272 /* UITextFieldDelegate method.  Invoked when user types something. */
       
   273 - (BOOL)textField:(UITextField *)_textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
       
   274 {
       
   275     NSUInteger len = string.length;
       
   276 
       
   277     if (len == 0) {
       
   278         /* it wants to replace text with nothing, ie a delete */
       
   279         SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_BACKSPACE);
       
   280         SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_BACKSPACE);
       
   281     } else {
       
   282         /* go through all the characters in the string we've been sent
       
   283            and convert them to key presses */
       
   284         for (int i = 0; i < len; i++) {
       
   285             unichar c = [string characterAtIndex:i];
       
   286             Uint16 mod = 0;
       
   287             SDL_Scancode code;
       
   288 
       
   289             if (c < 127) {
       
   290                 /* figure out the SDL_Scancode and SDL_keymod for this unichar */
       
   291                 code = unicharToUIKeyInfoTable[c].code;
       
   292                 mod  = unicharToUIKeyInfoTable[c].mod;
       
   293             }
       
   294             else {
       
   295                 /* we only deal with ASCII right now */
       
   296                 code = SDL_SCANCODE_UNKNOWN;
       
   297                 mod = 0;
       
   298             }
       
   299 
       
   300             if (mod & KMOD_SHIFT) {
       
   301                 /* If character uses shift, press shift down */
       
   302                 SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_LSHIFT);
       
   303             }
       
   304 
       
   305             /* send a keydown and keyup even for the character */
       
   306             SDL_SendKeyboardKey(SDL_PRESSED, code);
       
   307             SDL_SendKeyboardKey(SDL_RELEASED, code);
       
   308 
       
   309             if (mod & KMOD_SHIFT) {
       
   310                 /* If character uses shift, press shift back up */
       
   311                 SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LSHIFT);
       
   312             }
       
   313         }
       
   314 
       
   315         SDL_SendKeyboardText([string UTF8String]);
       
   316     }
       
   317 
       
   318     return NO; /* don't allow the edit! (keep placeholder text there) */
       
   319 }
       
   320 
       
   321 /* Terminates the editing session */
       
   322 - (BOOL)textFieldShouldReturn:(UITextField*)_textField
       
   323 {
       
   324     SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_RETURN);
       
   325     SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RETURN);
       
   326     SDL_StopTextInput();
       
   327     return YES;
       
   328 }
       
   329 
       
   330 #endif
       
   331 
       
   332 @end
   186 @end
   333 
       
   334 /* iPhone keyboard addition functions */
       
   335 #if SDL_IPHONE_KEYBOARD
       
   336 
       
   337 static SDL_uikitview *
       
   338 GetWindowView(SDL_Window * window)
       
   339 {
       
   340     if (window == NULL) {
       
   341         SDL_SetError("Window does not exist");
       
   342         return nil;
       
   343     }
       
   344 
       
   345     SDL_WindowData *data = (__bridge SDL_WindowData *)window->driverdata;
       
   346     SDL_uikitview *view = data != nil ? data.view : nil;
       
   347 
       
   348     if (view == nil) {
       
   349         SDL_SetError("Window has no view");
       
   350     }
       
   351 
       
   352     return view;
       
   353 }
       
   354 
       
   355 SDL_bool
       
   356 UIKit_HasScreenKeyboardSupport(_THIS)
       
   357 {
       
   358     return SDL_TRUE;
       
   359 }
       
   360 
       
   361 void
       
   362 UIKit_ShowScreenKeyboard(_THIS, SDL_Window *window)
       
   363 {
       
   364     @autoreleasepool {
       
   365         SDL_uikitview *view = GetWindowView(window);
       
   366         if (view != nil) {
       
   367             [view showKeyboard];
       
   368         }
       
   369     }
       
   370 }
       
   371 
       
   372 void
       
   373 UIKit_HideScreenKeyboard(_THIS, SDL_Window *window)
       
   374 {
       
   375     @autoreleasepool {
       
   376         SDL_uikitview *view = GetWindowView(window);
       
   377         if (view != nil) {
       
   378             [view hideKeyboard];
       
   379         }
       
   380     }
       
   381 }
       
   382 
       
   383 SDL_bool
       
   384 UIKit_IsScreenKeyboardShown(_THIS, SDL_Window *window)
       
   385 {
       
   386     @autoreleasepool {
       
   387         SDL_uikitview *view = GetWindowView(window);
       
   388         if (view != nil) {
       
   389             return view.isKeyboardVisible;
       
   390         }
       
   391         return 0;
       
   392     }
       
   393 }
       
   394 
       
   395 void
       
   396 UIKit_SetTextInputRect(_THIS, SDL_Rect *rect)
       
   397 {
       
   398     if (!rect) {
       
   399         SDL_InvalidParamError("rect");
       
   400         return;
       
   401     }
       
   402 
       
   403     @autoreleasepool {
       
   404         SDL_uikitview *view = GetWindowView(SDL_GetFocusWindow());
       
   405         if (view != nil) {
       
   406             view.textInputRect = *rect;
       
   407 
       
   408             if (view.keyboardVisible) {
       
   409                 [view updateKeyboard];
       
   410             }
       
   411         }
       
   412     }
       
   413 }
       
   414 
       
   415 
       
   416 #endif /* SDL_IPHONE_KEYBOARD */
       
   417 
   187 
   418 #endif /* SDL_VIDEO_DRIVER_UIKIT */
   188 #endif /* SDL_VIDEO_DRIVER_UIKIT */
   419 
   189 
   420 /* vi: set ts=4 sw=4 expandtab: */
   190 /* vi: set ts=4 sw=4 expandtab: */