Sfoglia il codice sorgente

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

kambala-decapitator/vcmi#4 kambala-decapitator/vcmi#31
Andrey Filipenkov 4 anni fa
parent
commit
f97ff108a7

+ 3 - 1
client/CMakeLists.txt

@@ -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()
 

+ 23 - 0
client/ios/GameChatKeyboardHanlder.h

@@ -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

+ 107 - 0
client/ios/GameChatKeyboardHanlder.m

@@ -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;
+}

+ 21 - 50
client/SDL_uikit_main.mm → client/ios/SDL_uikit_main.mm

@@ -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;
-}