src/video/uikit/SDL_uikitview.m
author Alex Szpakowski <slime73@gmail.com>
Sun, 23 Nov 2014 23:23:47 -0400
branchiOS-improvements
changeset 9527 bbd9326ecacf
parent 9525 64e3f446d6d7
child 9529 4bf9830d8153
permissions -rw-r--r--
Cleaned up the iOS text input code, fixed the orientation of the view offset when the onscreen keyboard is shown in iOS 8+, and changed UIKit_SetTextInputRect to update the view offset if the keyboard is currently visible.
slouken@6079
     1
 /*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@8149
     3
  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
slouken@5262
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@5262
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@5262
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@5262
    20
*/
icculus@8093
    21
#include "../../SDL_internal.h"
slouken@6044
    22
slouken@6044
    23
#if SDL_VIDEO_DRIVER_UIKIT
slouken@2765
    24
slouken@6518
    25
#include "SDL_uikitview.h"
slouken@2765
    26
slouken@4490
    27
#include "../../events/SDL_keyboard_c.h"
slouken@4490
    28
#include "../../events/SDL_mouse_c.h"
jimtla@4677
    29
#include "../../events/SDL_touch_c.h"
slouken@4490
    30
slouken@2765
    31
#if SDL_IPHONE_KEYBOARD
slouken@6518
    32
#include "keyinfotable.h"
urkle@7111
    33
#endif
slouken@6518
    34
#include "SDL_uikitappdelegate.h"
slouken@6518
    35
#include "SDL_uikitmodes.h"
slouken@6518
    36
#include "SDL_uikitwindow.h"
slouken@2765
    37
slime73@9501
    38
@implementation SDL_uikitview {
slime73@9501
    39
slime73@9501
    40
    SDL_TouchID touchId;
slime73@9501
    41
    UITouch *leftFingerDown;
slime73@9501
    42
slime73@9501
    43
#if SDL_IPHONE_KEYBOARD
slime73@9501
    44
    UITextField *textField;
slime73@9501
    45
#endif
slime73@9501
    46
slime73@9501
    47
}
slouken@2765
    48
slime73@9510
    49
@synthesize viewcontroller;
slime73@9510
    50
kees@6003
    51
- (id)initWithFrame:(CGRect)frame
kees@6003
    52
{
slime73@9524
    53
    if (self = [super initWithFrame:frame]) {
slouken@2765
    54
#if SDL_IPHONE_KEYBOARD
slime73@9527
    55
        [self initKeyboard];
kees@6001
    56
#endif
slouken@2765
    57
slime73@9505
    58
        self.multipleTouchEnabled = YES;
slouken@5445
    59
slime73@9505
    60
        touchId = 1;
slime73@9505
    61
        SDL_AddTouch(touchId, "");
slime73@9505
    62
    }
slouken@4661
    63
slouken@5131
    64
    return self;
slouken@2765
    65
slouken@2765
    66
}
slouken@2765
    67
slime73@9527
    68
- (void)dealloc
slime73@9527
    69
{
slime73@9527
    70
#if SDL_IPHONE_KEYBOARD
slime73@9527
    71
    [self deinitKeyboard];
slime73@9527
    72
#endif
slime73@9527
    73
}
slime73@9527
    74
slouken@6436
    75
- (CGPoint)touchLocation:(UITouch *)touch shouldNormalize:(BOOL)normalize
slouken@6079
    76
{
slime73@9524
    77
    CGPoint point = [touch locationInView:self];
slouken@6436
    78
slouken@6436
    79
    if (normalize) {
slime73@9501
    80
        CGRect bounds = self.bounds;
slouken@6439
    81
        point.x /= bounds.size.width;
slouken@6439
    82
        point.y /= bounds.size.height;
slouken@6436
    83
    }
slime73@9488
    84
slouken@6079
    85
    return point;
slouken@6079
    86
}
slouken@6079
    87
kees@6003
    88
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
kees@6003
    89
{
slime73@9487
    90
    for (UITouch *touch in touches) {
slouken@6597
    91
        if (!leftFingerDown) {
slouken@6597
    92
            CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO];
kees@6001
    93
slouken@6597
    94
            /* send moved event */
slime73@9510
    95
            SDL_SendMouseMotion(self.viewcontroller.window, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y);
slouken@4488
    96
slouken@6597
    97
            /* send mouse down event */
slime73@9510
    98
            SDL_SendMouseButton(self.viewcontroller.window, SDL_TOUCH_MOUSEID, SDL_PRESSED, SDL_BUTTON_LEFT);
jim@4660
    99
icculus@7050
   100
            leftFingerDown = touch;
slouken@6597
   101
        }
slouken@6597
   102
slouken@6436
   103
        CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
icculus@7050
   104
        SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
slouken@6951
   105
                      SDL_TRUE, locationInView.x, locationInView.y, 1.0f);
slouken@5131
   106
    }
slouken@2765
   107
}
slouken@2765
   108
kees@6003
   109
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
kees@6003
   110
{
slime73@9487
   111
    for (UITouch *touch in touches) {
icculus@7050
   112
        if (touch == leftFingerDown) {
slouken@6597
   113
            /* send mouse up */
slime73@9510
   114
            SDL_SendMouseButton(self.viewcontroller.window, SDL_TOUCH_MOUSEID, SDL_RELEASED, SDL_BUTTON_LEFT);
icculus@7050
   115
            leftFingerDown = nil;
slouken@6597
   116
        }
slouken@4661
   117
slouken@6436
   118
        CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
slime73@9501
   119
        SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
slouken@6951
   120
                      SDL_FALSE, locationInView.x, locationInView.y, 1.0f);
slouken@5131
   121
    }
slouken@2765
   122
}
slouken@2765
   123
kees@6003
   124
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
kees@6003
   125
{
slouken@5131
   126
    /*
slouken@5131
   127
        this can happen if the user puts more than 5 touches on the screen
slouken@5131
   128
        at once, or perhaps in other circumstances.  Usually (it seems)
slouken@5131
   129
        all active touches are canceled.
slouken@5131
   130
    */
slime73@9524
   131
    [self touchesEnded:touches withEvent:event];
slouken@2765
   132
}
slouken@2765
   133
kees@6003
   134
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
kees@6003
   135
{
slime73@9487
   136
    for (UITouch *touch in touches) {
icculus@7050
   137
        if (touch == leftFingerDown) {
slouken@6597
   138
            CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO];
slouken@4488
   139
slouken@6597
   140
            /* send moved event */
slime73@9510
   141
            SDL_SendMouseMotion(self.viewcontroller.window, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y);
slouken@6597
   142
        }
jim@4660
   143
slouken@6436
   144
        CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
slime73@9501
   145
        SDL_SendTouchMotion(touchId, (SDL_FingerID)((size_t)touch),
slouken@6951
   146
                            locationInView.x, locationInView.y, 1.0f);
slouken@5131
   147
    }
slouken@2765
   148
}
slouken@2765
   149
slouken@2765
   150
/*
slouken@5131
   151
    ---- Keyboard related functionality below this line ----
slouken@2765
   152
*/
slouken@2765
   153
#if SDL_IPHONE_KEYBOARD
slouken@2765
   154
slime73@9501
   155
@synthesize textInputRect;
slime73@9501
   156
@synthesize keyboardHeight;
slime73@9501
   157
@synthesize keyboardVisible;
slouken@2765
   158
slouken@2765
   159
/* Set ourselves up as a UITextFieldDelegate */
slime73@9527
   160
- (void)initKeyboard
kees@6003
   161
{
slime73@9524
   162
    textField = [[UITextField alloc] initWithFrame:CGRectZero];
slouken@5131
   163
    textField.delegate = self;
slouken@5131
   164
    /* placeholder so there is something to delete! */
kees@6001
   165
    textField.text = @" ";
kees@6001
   166
slouken@5131
   167
    /* set UITextInputTrait properties, mostly to defaults */
slouken@5131
   168
    textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
slouken@5131
   169
    textField.autocorrectionType = UITextAutocorrectionTypeNo;
slouken@5131
   170
    textField.enablesReturnKeyAutomatically = NO;
slouken@5131
   171
    textField.keyboardAppearance = UIKeyboardAppearanceDefault;
slouken@5131
   172
    textField.keyboardType = UIKeyboardTypeDefault;
slouken@5131
   173
    textField.returnKeyType = UIReturnKeyDefault;
kees@6001
   174
    textField.secureTextEntry = NO;
kees@6001
   175
slouken@5131
   176
    textField.hidden = YES;
slouken@5131
   177
    keyboardVisible = NO;
slouken@5131
   178
    /* add the UITextField (hidden) to our view */
slime73@9524
   179
    [self addSubview:textField];
slime73@9527
   180
slime73@9527
   181
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
slime73@9527
   182
    [center addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
slime73@9527
   183
    [center addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
slime73@9527
   184
}
slime73@9527
   185
slime73@9527
   186
- (void)deinitKeyboard
slime73@9527
   187
{
slime73@9527
   188
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
slime73@9527
   189
    [center removeObserver:self name:UIKeyboardWillShowNotification object:nil];
slime73@9527
   190
    [center removeObserver:self name:UIKeyboardWillHideNotification object:nil];
slouken@2765
   191
}
slouken@2765
   192
slouken@2765
   193
/* reveal onscreen virtual keyboard */
kees@6003
   194
- (void)showKeyboard
kees@6003
   195
{
slouken@5131
   196
    keyboardVisible = YES;
slouken@5131
   197
    [textField becomeFirstResponder];
slouken@2765
   198
}
slouken@2765
   199
slouken@2765
   200
/* hide onscreen virtual keyboard */
kees@6003
   201
- (void)hideKeyboard
kees@6003
   202
{
slouken@5131
   203
    keyboardVisible = NO;
slouken@5131
   204
    [textField resignFirstResponder];
slouken@2765
   205
}
slouken@2765
   206
slime73@9527
   207
- (void)keyboardWillShow:(NSNotification *)notification
slime73@9527
   208
{
slime73@9527
   209
    CGRect kbrect = [[notification userInfo][UIKeyboardFrameBeginUserInfoKey] CGRectValue];
slime73@9527
   210
    int height;
slime73@9527
   211
slime73@9527
   212
    /* The keyboard rect is in the coordinate space of the screen, but we want
slime73@9527
   213
     * its height in the view's coordinate space.
slime73@9527
   214
     */
slime73@9527
   215
#ifdef __IPHONE_8_0
slime73@9527
   216
    if ([self respondsToSelector:@selector(convertRect:fromCoordinateSpace:)]) {
slime73@9527
   217
        UIScreen *screen = self.window.screen;
slime73@9527
   218
        kbrect = [self convertRect:kbrect fromCoordinateSpace:screen.coordinateSpace];
slime73@9527
   219
        height = kbrect.size.height;
slime73@9527
   220
    } else
slime73@9527
   221
#endif
slime73@9527
   222
    {
slime73@9527
   223
        /* In iOS 7 and below, the screen's coordinate space is never rotated. */
slime73@9527
   224
        if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) {
slime73@9527
   225
            height = kbrect.size.width;
slime73@9527
   226
        } else {
slime73@9527
   227
            height = kbrect.size.height;
slime73@9527
   228
        }
slime73@9527
   229
    }
slime73@9527
   230
slime73@9527
   231
    [self setKeyboardHeight:height];
slime73@9527
   232
}
slime73@9527
   233
slime73@9527
   234
 - (void)keyboardWillHide:(NSNotification *)notification
slime73@9527
   235
{
slime73@9527
   236
    [self setKeyboardHeight:0];
slime73@9527
   237
}
slime73@9527
   238
slime73@9527
   239
- (void)updateKeyboard
slime73@9527
   240
{
slime73@9527
   241
    SDL_Rect textrect = self.textInputRect;
slime73@9527
   242
    CGAffineTransform t = self.transform;
slime73@9527
   243
    CGPoint offset = CGPointMake(0.0, 0.0);
slime73@9527
   244
slime73@9527
   245
    if (self.keyboardHeight) {
slime73@9527
   246
        int rectbottom = textrect.y + textrect.h;
slime73@9527
   247
        int kbottom = self.bounds.size.height - self.keyboardHeight;
slime73@9527
   248
        if (kbottom < rectbottom) {
slime73@9527
   249
            offset.y = kbottom - rectbottom;
slime73@9527
   250
        }
slime73@9527
   251
    }
slime73@9527
   252
slime73@9527
   253
    /* Put the offset into the this view transform's coordinate space. */
slime73@9527
   254
    t.tx = 0.0;
slime73@9527
   255
    t.ty = 0.0;
slime73@9527
   256
    offset = CGPointApplyAffineTransform(offset, t);
slime73@9527
   257
slime73@9527
   258
    t.tx = offset.x;
slime73@9527
   259
    t.ty = offset.y;
slime73@9527
   260
slime73@9527
   261
    /* Move the view by applying the updated transform. */
slime73@9527
   262
    self.transform = t;
slime73@9527
   263
}
slime73@9527
   264
slime73@9527
   265
- (void)setKeyboardHeight:(int)height
slime73@9527
   266
{
slime73@9527
   267
    keyboardVisible = height > 0;
slime73@9527
   268
    keyboardHeight = height;
slime73@9527
   269
    [self updateKeyboard];
slime73@9527
   270
}
slime73@9527
   271
slouken@2765
   272
/* UITextFieldDelegate method.  Invoked when user types something. */
kees@6003
   273
- (BOOL)textField:(UITextField *)_textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
kees@6003
   274
{
slime73@9525
   275
    NSUInteger len = string.length;
slime73@9525
   276
slime73@9525
   277
    if (len == 0) {
slouken@5131
   278
        /* it wants to replace text with nothing, ie a delete */
icculus@7774
   279
        SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_BACKSPACE);
icculus@7774
   280
        SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_BACKSPACE);
slime73@9525
   281
    } else {
slouken@5131
   282
        /* go through all the characters in the string we've been sent
slouken@5131
   283
           and convert them to key presses */
slime73@9525
   284
        for (int i = 0; i < len; i++) {
slime73@9524
   285
            unichar c = [string characterAtIndex:i];
slouken@5131
   286
            Uint16 mod = 0;
slouken@5218
   287
            SDL_Scancode code;
kees@6001
   288
slouken@5131
   289
            if (c < 127) {
slouken@5218
   290
                /* figure out the SDL_Scancode and SDL_keymod for this unichar */
slouken@5131
   291
                code = unicharToUIKeyInfoTable[c].code;
slouken@5131
   292
                mod  = unicharToUIKeyInfoTable[c].mod;
slouken@5131
   293
            }
slouken@5131
   294
            else {
slouken@5131
   295
                /* we only deal with ASCII right now */
slouken@5131
   296
                code = SDL_SCANCODE_UNKNOWN;
slouken@5131
   297
                mod = 0;
slouken@5131
   298
            }
kees@6001
   299
slouken@5131
   300
            if (mod & KMOD_SHIFT) {
slouken@5131
   301
                /* If character uses shift, press shift down */
slouken@5131
   302
                SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_LSHIFT);
slouken@5131
   303
            }
slime73@9525
   304
slouken@5131
   305
            /* send a keydown and keyup even for the character */
slouken@5131
   306
            SDL_SendKeyboardKey(SDL_PRESSED, code);
slouken@5131
   307
            SDL_SendKeyboardKey(SDL_RELEASED, code);
slime73@9525
   308
slouken@5131
   309
            if (mod & KMOD_SHIFT) {
slouken@5131
   310
                /* If character uses shift, press shift back up */
slouken@5131
   311
                SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LSHIFT);
kees@6001
   312
            }
slouken@5131
   313
        }
slime73@9525
   314
slouken@5461
   315
        SDL_SendKeyboardText([string UTF8String]);
slouken@5131
   316
    }
slime73@9525
   317
slouken@5131
   318
    return NO; /* don't allow the edit! (keep placeholder text there) */
slouken@2765
   319
}
slouken@2765
   320
slouken@2765
   321
/* Terminates the editing session */
kees@6003
   322
- (BOOL)textFieldShouldReturn:(UITextField*)_textField
kees@6003
   323
{
slouken@5134
   324
    SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_RETURN);
slouken@6054
   325
    SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RETURN);
slouken@6654
   326
    SDL_StopTextInput();
slouken@5131
   327
    return YES;
slouken@2765
   328
}
slouken@2765
   329
slouken@2765
   330
#endif
slouken@2765
   331
slouken@2765
   332
@end
slouken@2765
   333
slouken@2765
   334
/* iPhone keyboard addition functions */
slouken@2765
   335
#if SDL_IPHONE_KEYBOARD
slouken@2765
   336
slime73@9527
   337
static SDL_uikitview *
slime73@9527
   338
GetWindowView(SDL_Window * window)
kees@6010
   339
{
kees@6010
   340
    if (window == NULL) {
kees@6010
   341
        SDL_SetError("Window does not exist");
kees@6010
   342
        return nil;
kees@6010
   343
    }
kees@6010
   344
slime73@9510
   345
    SDL_WindowData *data = (__bridge SDL_WindowData *)window->driverdata;
slime73@9510
   346
    SDL_uikitview *view = data != nil ? data.view : nil;
kees@6010
   347
kees@6010
   348
    if (view == nil) {
kees@6010
   349
        SDL_SetError("Window has no view");
kees@6010
   350
    }
kees@6010
   351
kees@6010
   352
    return view;
kees@6010
   353
}
kees@6010
   354
slime73@9527
   355
SDL_bool
slime73@9527
   356
UIKit_HasScreenKeyboardSupport(_THIS)
slouken@6392
   357
{
slouken@6392
   358
    return SDL_TRUE;
slouken@6392
   359
}
slouken@6392
   360
slime73@9527
   361
void
slime73@9527
   362
UIKit_ShowScreenKeyboard(_THIS, SDL_Window *window)
kees@6003
   363
{
slime73@9506
   364
    @autoreleasepool {
slime73@9527
   365
        SDL_uikitview *view = GetWindowView(window);
slime73@9506
   366
        if (view != nil) {
slime73@9506
   367
            [view showKeyboard];
slime73@9506
   368
        }
slouken@5131
   369
    }
slouken@2765
   370
}
slouken@2765
   371
slime73@9527
   372
void
slime73@9527
   373
UIKit_HideScreenKeyboard(_THIS, SDL_Window *window)
kees@6003
   374
{
slime73@9506
   375
    @autoreleasepool {
slime73@9527
   376
        SDL_uikitview *view = GetWindowView(window);
slime73@9506
   377
        if (view != nil) {
slime73@9506
   378
            [view hideKeyboard];
slime73@9506
   379
        }
kees@6001
   380
    }
slouken@2765
   381
}
slouken@2765
   382
slime73@9527
   383
SDL_bool
slime73@9527
   384
UIKit_IsScreenKeyboardShown(_THIS, SDL_Window *window)
kees@6003
   385
{
slime73@9506
   386
    @autoreleasepool {
slime73@9527
   387
        SDL_uikitview *view = GetWindowView(window);
slime73@9527
   388
        if (view != nil) {
slime73@9527
   389
            return view.isKeyboardVisible;
slime73@9506
   390
        }
slime73@9527
   391
        return 0;
kees@6001
   392
    }
slouken@2765
   393
}
slouken@2765
   394
slouken@7962
   395
void
slouken@7962
   396
UIKit_SetTextInputRect(_THIS, SDL_Rect *rect)
slouken@7962
   397
{
slouken@7962
   398
    if (!rect) {
slouken@7962
   399
        SDL_InvalidParamError("rect");
slouken@7962
   400
        return;
slouken@7962
   401
    }
slime73@9506
   402
slime73@9506
   403
    @autoreleasepool {
slime73@9527
   404
        SDL_uikitview *view = GetWindowView(SDL_GetFocusWindow());
slime73@9527
   405
        if (view != nil) {
slime73@9527
   406
            view.textInputRect = *rect;
slime73@9527
   407
slime73@9527
   408
            if (view.keyboardVisible) {
slime73@9527
   409
                [view updateKeyboard];
slime73@9527
   410
            }
slime73@9506
   411
        }
slouken@7962
   412
    }
slouken@7962
   413
}
slouken@7962
   414
slouken@7962
   415
slouken@5132
   416
#endif /* SDL_IPHONE_KEYBOARD */
slouken@2765
   417
slouken@6044
   418
#endif /* SDL_VIDEO_DRIVER_UIKIT */
slouken@6044
   419
slouken@5132
   420
/* vi: set ts=4 sw=4 expandtab: */