1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

show SDL's textfield above the keyboard for game chat, extract handling to separate class

kambala-decapitator/vcmi#4 kambala-decapitator/vcmi#31
This commit is contained in:
Andrey Filipenkov 2021-04-19 02:28:29 +03:00
parent 217c83a7e7
commit f97ff108a7
4 changed files with 154 additions and 51 deletions

View File

@ -147,11 +147,13 @@ set(client_HEADERS
if(APPLE_IOS)
set(client_SRCS ${client_SRCS}
SDL_uikit_main.mm
CFocusableHelper.cpp
ios/GameChatKeyboardHanlder.m
ios/SDL_uikit_main.mm
)
set(client_HEADERS ${client_HEADERS}
CFocusableHelper.h
ios/GameChatKeyboardHanlder.h
)
endif()

View File

@ -0,0 +1,23 @@
/*
* GameChatKeyboardHanlder.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface GameChatKeyboardHanlder : NSObject
@property (nonatomic, weak) UITextField * textFieldSDL;
- (void)triggerInput;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,107 @@
/*
* GameChatKeyboardHanlder.m, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#import "GameChatKeyboardHanlder.h"
#include <SDL_events.h>
static int watchReturnKey(void * userdata, SDL_Event * event);
static void sendKeyEvent(SDL_KeyCode keyCode)
{
SDL_Event keyEvent;
keyEvent.key = (SDL_KeyboardEvent){
.type = SDL_KEYDOWN,
.keysym.sym = keyCode,
};
SDL_PushEvent(&keyEvent);
}
static CGRect keyboardFrame(NSNotification * n, NSString * userInfoKey)
{
return [n.userInfo[userInfoKey] CGRectValue];
}
static CGRect keyboardFrameBegin(NSNotification * n) { return keyboardFrame(n, UIKeyboardFrameBeginUserInfoKey); }
static CGRect keyboardFrameEnd (NSNotification * n) { return keyboardFrame(n, UIKeyboardFrameEndUserInfoKey); }
@interface GameChatKeyboardHanlder ()
@property (nonatomic) BOOL wasChatMessageSent;
@end
@implementation GameChatKeyboardHanlder
- (void)triggerInput {
__auto_type notificationCenter = NSNotificationCenter.defaultCenter;
[notificationCenter addObserver:self selector:@selector(textDidBeginEditing:) name:UITextFieldTextDidBeginEditingNotification object:nil];
[notificationCenter addObserver:self selector:@selector(textDidEndEditing:) name:UITextFieldTextDidEndEditingNotification object:nil];
[notificationCenter addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
[notificationCenter addObserver:self selector:@selector(keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nil];
self.wasChatMessageSent = NO;
sendKeyEvent(SDLK_TAB);
}
- (void)positionTextFieldAboveKeyboardRect:(CGRect)kbFrame {
__auto_type r = kbFrame;
r.size.height = CGRectGetHeight(self.textFieldSDL.frame);
r.origin.y -= r.size.height;
self.textFieldSDL.frame = r;
}
#pragma mark - Notifications
- (void)textDidBeginEditing:(NSNotification *)n {
self.textFieldSDL.hidden = NO;
self.textFieldSDL.text = nil;
// watch for pressing Return to ignore sending Escape key after keyboard is closed
SDL_AddEventWatch(watchReturnKey, (__bridge void *)self);
}
- (void)textDidEndEditing:(NSNotification *)n {
[NSNotificationCenter.defaultCenter removeObserver:self];
self.textFieldSDL.hidden = YES;
// discard chat message
if(!self.wasChatMessageSent)
sendKeyEvent(SDLK_ESCAPE);
}
- (void)keyboardWillChangeFrame:(NSNotification *)n {
// animate textfield together with keyboard
[UIView performWithoutAnimation:^{
[self positionTextFieldAboveKeyboardRect:keyboardFrameBegin(n)];
}];
NSTimeInterval kbAnimationDuration = [n.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
NSUInteger kbAnimationCurve = [n.userInfo[UIKeyboardAnimationCurveUserInfoKey] unsignedIntegerValue];
[UIView animateWithDuration:kbAnimationDuration delay:0 options:(kbAnimationCurve << 16) animations:^{
[self positionTextFieldAboveKeyboardRect:keyboardFrameEnd(n)];
} completion:nil];
}
- (void)keyboardDidChangeFrame:(NSNotification *)n {
[self positionTextFieldAboveKeyboardRect:keyboardFrameEnd(n)];
}
@end
static int watchReturnKey(void * userdata, SDL_Event * event)
{
if(event->type == SDL_KEYDOWN && event->key.keysym.scancode == SDL_SCANCODE_RETURN)
{
__auto_type self = (__bridge GameChatKeyboardHanlder *)userdata;
self.wasChatMessageSent = YES;
SDL_DelEventWatch(watchReturnKey, userdata);
}
return 1;
}

View File

@ -13,35 +13,38 @@
#include "CServerHandler.h"
#include "CFocusableHelper.h"
#import "GameChatKeyboardHanlder.h"
#import <UIKit/UIKit.h>
double ios_screenScale() { return UIScreen.mainScreen.nativeScale; }
static int watchReturnKey(void * userdata, SDL_Event * event);
static void sendKeyEvent(SDL_KeyCode keyCode)
{
SDL_Event keyEvent;
keyEvent.key = (SDL_KeyboardEvent){
.type = SDL_KEYDOWN,
.keysym.sym = keyCode,
};
SDL_PushEvent(&keyEvent);
}
@interface SDLViewObserver : NSObject <UIGestureRecognizerDelegate>
@property (nonatomic) bool wasChatMessageSent;
@property (nonatomic, strong) GameChatKeyboardHanlder * gameChatHandler;
@end
@implementation SDLViewObserver
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
UIView * view = [object valueForKey:keyPath];
UIView * view = [object valueForKeyPath:keyPath];
UITextField * textField;
for (UIView * v in view.subviews) {
if ([v isKindOfClass:[UITextField class]]) {
textField = (UITextField *)v;
break;
}
}
auto r = textField.frame;
r.size.height = 40;
textField.frame = r;
textField.backgroundColor = UIColor.whiteColor;
self.gameChatHandler.textFieldSDL = textField;
auto longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
longPress.minimumPressDuration = 0.1;
longPress.minimumPressDuration = 0.2;
[view addGestureRecognizer:longPress];
auto pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)];
@ -102,29 +105,7 @@ static void sendKeyEvent(SDL_KeyCode keyCode)
- (void)handlePinch:(UIGestureRecognizer *)gesture {
if(gesture.state != UIGestureRecognizerStateBegan || CSH->state != EClientState::GAMEPLAY)
return;
// Tab triggers chat message input
self.wasChatMessageSent = false;
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(handleKeyboardDidShowForGameChat:) name:UIKeyboardDidShowNotification object:nil];
sendKeyEvent(SDLK_TAB);
}
#pragma mark - Notifications
- (void)handleKeyboardDidShowForGameChat:(NSNotification *)n {
[NSNotificationCenter.defaultCenter removeObserver:self name:n.name object:nil];
// watch for pressing Return to ignore sending Escape key after keyboard is closed
SDL_AddEventWatch(watchReturnKey, (__bridge void *)self);
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(handleKeyboardDidHideForGameChat:) name:UIKeyboardDidHideNotification object:nil];
}
- (void)handleKeyboardDidHideForGameChat:(NSNotification *)n {
[NSNotificationCenter.defaultCenter removeObserver:self name:n.name object:nil];
// discard chat message
if(!self.wasChatMessageSent)
sendKeyEvent(SDLK_ESCAPE);
[self.gameChatHandler triggerInput];
}
#pragma mark - UIGestureRecognizerDelegate
@ -146,6 +127,7 @@ main(int argc, char *argv[])
@autoreleasepool
{
auto observer = [SDLViewObserver new];
observer.gameChatHandler = [GameChatKeyboardHanlder new];
[NSNotificationCenter.defaultCenter addObserverForName:UIWindowDidBecomeKeyNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
[UIApplication.sharedApplication.keyWindow.rootViewController addObserver:observer forKeyPath:NSStringFromSelector(@selector(view)) options:NSKeyValueObservingOptionNew context:NULL];
}];
@ -155,14 +137,3 @@ main(int argc, char *argv[])
return SDL_UIKitRunApp(argc, argv, SDL_main);
}
}
static int watchReturnKey(void * userdata, SDL_Event * event)
{
if(event->type == SDL_KEYDOWN && event->key.keysym.scancode == SDL_SCANCODE_RETURN)
{
auto self = (__bridge SDLViewObserver *)userdata;
self.wasChatMessageSent = true;
SDL_DelEventWatch(watchReturnKey, userdata);
}
return 1;
}