diff options
-rw-r--r-- | xbmc/dialogs/GUIDialogKeyboardTouch.cpp | 4 | ||||
-rw-r--r-- | xbmc/platform/darwin/ios-common/CMakeLists.txt | 12 | ||||
-rw-r--r-- | xbmc/platform/darwin/ios-common/DarwinEmbedKeyboard.h | 33 | ||||
-rw-r--r-- | xbmc/platform/darwin/ios-common/DarwinEmbedKeyboard.mm | 108 | ||||
-rw-r--r-- | xbmc/platform/darwin/ios-common/DarwinEmbedKeyboardView.h | 32 | ||||
-rw-r--r-- | xbmc/platform/darwin/ios-common/DarwinEmbedKeyboardView.mm | 161 | ||||
-rw-r--r-- | xbmc/platform/darwin/ios-common/IOSKeyboard.h | 29 | ||||
-rw-r--r-- | xbmc/platform/darwin/ios-common/IOSKeyboard.mm | 100 | ||||
-rw-r--r-- | xbmc/platform/darwin/ios-common/IOSKeyboardView.h | 40 | ||||
-rw-r--r-- | xbmc/platform/darwin/ios-common/IOSKeyboardView.mm | 351 | ||||
-rw-r--r-- | xbmc/platform/darwin/ios/CMakeLists.txt | 2 | ||||
-rw-r--r-- | xbmc/platform/darwin/ios/IOSKeyboardView.h | 17 | ||||
-rw-r--r-- | xbmc/platform/darwin/ios/IOSKeyboardView.mm | 146 |
13 files changed, 507 insertions, 528 deletions
diff --git a/xbmc/dialogs/GUIDialogKeyboardTouch.cpp b/xbmc/dialogs/GUIDialogKeyboardTouch.cpp index a05999d00d..e9511cec1a 100644 --- a/xbmc/dialogs/GUIDialogKeyboardTouch.cpp +++ b/xbmc/dialogs/GUIDialogKeyboardTouch.cpp @@ -8,7 +8,7 @@ #include "GUIDialogKeyboardTouch.h" #if defined(TARGET_DARWIN_EMBEDDED) -#include "platform/darwin/ios-common/IOSKeyboard.h" +#include "platform/darwin/ios-common/DarwinEmbedKeyboard.h" #endif CGUIDialogKeyboardTouch::CGUIDialogKeyboardTouch() @@ -22,7 +22,7 @@ CGUIDialogKeyboardTouch::CGUIDialogKeyboardTouch() bool CGUIDialogKeyboardTouch::ShowAndGetInput(char_callback_t pCallback, const std::string &initialString, std::string &typedString, const std::string &heading, bool bHiddenInput) { #if defined(TARGET_DARWIN_EMBEDDED) - m_keyboard.reset(new CIOSKeyboard()); + m_keyboard.reset(new CDarwinEmbedKeyboard()); #endif if (!m_keyboard) diff --git a/xbmc/platform/darwin/ios-common/CMakeLists.txt b/xbmc/platform/darwin/ios-common/CMakeLists.txt index 5c6533d5bc..1a1749c379 100644 --- a/xbmc/platform/darwin/ios-common/CMakeLists.txt +++ b/xbmc/platform/darwin/ios-common/CMakeLists.txt @@ -1,11 +1,11 @@ set(SOURCES AnnounceReceiver.mm - DarwinEmbedNowPlayingInfoManager.mm - IOSKeyboard.mm - IOSKeyboardView.mm) + DarwinEmbedKeyboard.mm + DarwinEmbedKeyboardView.mm + DarwinEmbedNowPlayingInfoManager.mm) set(HEADERS AnnounceReceiver.h - DarwinEmbedNowPlayingInfoManager.h - IOSKeyboard.h - IOSKeyboardView.h) + DarwinEmbedKeyboard.h + DarwinEmbedKeyboardView.h + DarwinEmbedNowPlayingInfoManager.h) core_add_library(platform_ios-common) diff --git a/xbmc/platform/darwin/ios-common/DarwinEmbedKeyboard.h b/xbmc/platform/darwin/ios-common/DarwinEmbedKeyboard.h new file mode 100644 index 0000000000..386f1bb42c --- /dev/null +++ b/xbmc/platform/darwin/ios-common/DarwinEmbedKeyboard.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2012-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "guilib/GUIKeyboard.h" + +struct CDarwinEmbedKeyboardImpl; + +class CDarwinEmbedKeyboard : public CGUIKeyboard +{ +public: + CDarwinEmbedKeyboard(); + bool ShowAndGetInput(char_callback_t pCallback, + const std::string& initialString, + std::string& typedString, + const std::string& heading, + bool bHiddenInput) override; + void Cancel() override; + void fireCallback(const std::string& str); + void invalidateCallback(); + bool SetTextToKeyboard(const std::string& text, bool closeKeyboard = false) override; + +private: + char_callback_t m_pCharCallback = nullptr; + bool m_canceled = false; + std::unique_ptr<CDarwinEmbedKeyboardImpl> m_impl; +}; diff --git a/xbmc/platform/darwin/ios-common/DarwinEmbedKeyboard.mm b/xbmc/platform/darwin/ios-common/DarwinEmbedKeyboard.mm new file mode 100644 index 0000000000..e0d61050ce --- /dev/null +++ b/xbmc/platform/darwin/ios-common/DarwinEmbedKeyboard.mm @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2012-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "DarwinEmbedKeyboard.h" + +#include "platform/darwin/ios-common/DarwinEmbedKeyboardView.h" +#include "platform/darwin/ios/IOSKeyboardView.h" +#include "platform/darwin/ios/XBMCController.h" + +struct CDarwinEmbedKeyboardImpl +{ + IOSKeyboardView* g_pKeyboard = nil; +}; + +CDarwinEmbedKeyboard::CDarwinEmbedKeyboard() + : CGUIKeyboard(), m_impl{std::make_unique<CDarwinEmbedKeyboardImpl>()} +{ +} + +bool CDarwinEmbedKeyboard::ShowAndGetInput(char_callback_t pCallback, + const std::string& initialString, + std::string& typedString, + const std::string& heading, + bool bHiddenInput) +{ + // we are in xbmc main thread or python module thread. + @autoreleasepool + { + @synchronized([KeyboardView class]) + { + // in case twice open keyboard. + if (m_impl->g_pKeyboard) + return false; + + //! @Todo generalise this block for platform + //create the keyboardview + IOSKeyboardView* __block keyboardView; + dispatch_sync(dispatch_get_main_queue(), ^{ + // assume we are only drawn on the mainscreen ever! + auto keyboardFrame = [g_xbmcController fullscreenSubviewFrame]; + keyboardView = [[IOSKeyboardView alloc] initWithFrame:keyboardFrame]; + }); + //////////////////////////////////////////// + if (!keyboardView) + return false; + m_impl->g_pKeyboard = keyboardView; + + // inform the controller that the native keyboard is active + // basically as long as m_impl->g_pKeyboard exists... + [g_xbmcController nativeKeyboardActive:true]; + } + + m_pCharCallback = pCallback; + + // init keyboard stuff + SetTextToKeyboard(initialString); + [m_impl->g_pKeyboard setHidden:bHiddenInput]; + [m_impl->g_pKeyboard setHeading:@(heading.c_str())]; + m_impl->g_pKeyboard.darwinEmbedKeyboard = this; // for calling back + bool confirmed = false; + if (!m_canceled) + { + [m_impl->g_pKeyboard setCancelFlag:&m_canceled]; + [m_impl->g_pKeyboard activate]; // blocks and shows keyboard + // user is done - get resulted text and confirmation + confirmed = [m_impl->g_pKeyboard isConfirmed]; + if (confirmed) + typedString = m_impl->g_pKeyboard.text.UTF8String; + } + @synchronized([KeyboardView class]) + { + m_impl->g_pKeyboard = nil; + [g_xbmcController nativeKeyboardActive:false]; + } + return confirmed; + } +} + +void CDarwinEmbedKeyboard::Cancel() +{ + m_canceled = true; +} + +bool CDarwinEmbedKeyboard::SetTextToKeyboard(const std::string& text, + bool closeKeyboard /* = false */) +{ + if (!m_impl->g_pKeyboard) + return false; + [m_impl->g_pKeyboard setKeyboardText:@(text.c_str()) closeKeyboard:closeKeyboard ? YES : NO]; + return true; +} + +//wrap our callback between objc and c++ +void CDarwinEmbedKeyboard::fireCallback(const std::string& str) +{ + if (m_pCharCallback) + m_pCharCallback(this, str); +} + +void CDarwinEmbedKeyboard::invalidateCallback() +{ + m_pCharCallback = nullptr; +} diff --git a/xbmc/platform/darwin/ios-common/DarwinEmbedKeyboardView.h b/xbmc/platform/darwin/ios-common/DarwinEmbedKeyboardView.h new file mode 100644 index 0000000000..a50b616d8d --- /dev/null +++ b/xbmc/platform/darwin/ios-common/DarwinEmbedKeyboardView.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2012-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "platform/darwin/ios-common/DarwinEmbedKeyboard.h" + +#import <UIKit/UIKit.h> + +@interface KeyboardView : UIView <UITextFieldDelegate> +{ + bool* m_canceled; + BOOL m_deactivated; + UITextField* __weak m_inputTextField; +} + +@property(getter=isConfirmed) BOOL confirmed; +@property(assign) CDarwinEmbedKeyboard* darwinEmbedKeyboard; +@property(nonatomic, readonly) NSString* text; + +- (void)setHeading:(NSString*)heading; +- (void)setHidden:(BOOL)hidden; +- (void)activate; +- (void)deactivate; +- (void)setKeyboardText:(NSString*)aText closeKeyboard:(BOOL)closeKeyboard; +- (void)textChanged:(NSNotification*)aNotification; +- (void)setCancelFlag:(bool*)cancelFlag; + +@end diff --git a/xbmc/platform/darwin/ios-common/DarwinEmbedKeyboardView.mm b/xbmc/platform/darwin/ios-common/DarwinEmbedKeyboardView.mm new file mode 100644 index 0000000000..c0365c614a --- /dev/null +++ b/xbmc/platform/darwin/ios-common/DarwinEmbedKeyboardView.mm @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2012-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#import "DarwinEmbedKeyboardView.h" + +#include "Application.h" +#include "guilib/GUIKeyboardFactory.h" +#include "threads/Event.h" +#include "utils/log.h" + +#import "platform/darwin/ios/XBMCController.h" + +static CEvent keyboardFinishedEvent; + +@implementation KeyboardView + +@synthesize confirmed = m_confirmed; +@synthesize darwinEmbedKeyboard = m_darwinEmbedKeyboard; + +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (!self) + return nil; + + m_canceled = nullptr; + m_deactivated = NO; + + auto textField = [UITextField new]; + textField.translatesAutoresizingMaskIntoConstraints = NO; + textField.clearButtonMode = UITextFieldViewModeAlways; + textField.borderStyle = UITextBorderStyleNone; + textField.returnKeyType = UIReturnKeyDone; + textField.autocapitalizationType = UITextAutocapitalizationTypeNone; + textField.backgroundColor = UIColor.whiteColor; + textField.delegate = self; + [self addSubview:textField]; + m_inputTextField = textField; + + self.userInteractionEnabled = YES; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(textChanged:) + name:UITextFieldTextDidChangeNotification + object:m_inputTextField]; + return self; +} + +- (void)textFieldDidEndEditing:(UITextField*)textField +{ + [self deactivate]; +} + +- (BOOL)textFieldShouldReturn:(UITextField*)textField +{ + m_confirmed = YES; + return YES; +} + +- (NSString*)text +{ + NSString __block* result; + dispatch_sync(dispatch_get_main_queue(), ^{ + result = m_inputTextField.text; + }); + return result; +} + +- (void)activate +{ + dispatch_sync(dispatch_get_main_queue(), ^{ + [g_xbmcController activateKeyboard:self]; + [self layoutIfNeeded]; + [m_inputTextField becomeFirstResponder]; + keyboardFinishedEvent.Reset(); + }); + + // we are waiting on the user finishing the keyboard + while (!keyboardFinishedEvent.WaitMSec(500)) + { + if (nullptr != m_canceled && *m_canceled) + { + [self deactivate]; + m_canceled = nullptr; + } + } +} + +- (void)deactivate +{ + m_deactivated = YES; + + // invalidate our callback object + if (m_darwinEmbedKeyboard) + { + m_darwinEmbedKeyboard->invalidateCallback(); + m_darwinEmbedKeyboard = nil; + } + // give back the control to whoever + [m_inputTextField resignFirstResponder]; + + // delay closing view until text field finishes resigning first responder + dispatch_async(dispatch_get_main_queue(), ^{ + // always called in the mainloop context + // detach the keyboard view from our main controller + [g_xbmcController deactivateKeyboard:self]; + + // no more notification we want to receive. + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + keyboardFinishedEvent.Set(); + }); +} + +- (void)setKeyboardText:(NSString*)aText closeKeyboard:(BOOL)closeKeyboard +{ + CLog::Log(LOGDEBUG, "{}: {}, {}", __PRETTY_FUNCTION__, aText.UTF8String, closeKeyboard); + + dispatch_sync(dispatch_get_main_queue(), ^{ + m_inputTextField.text = aText; + [self textChanged:nil]; + }); + + if (closeKeyboard) + { + m_confirmed = YES; + [self deactivate]; + } +} + +- (void)setHeading:(NSString*)heading +{ + dispatch_sync(dispatch_get_main_queue(), ^{ + m_inputTextField.placeholder = heading; + }); +} + +- (void)setHidden:(BOOL)hidden +{ + dispatch_sync(dispatch_get_main_queue(), ^{ + m_inputTextField.secureTextEntry = hidden; + }); +} + +- (void)textChanged:(NSNotification*)aNotification +{ + if (m_darwinEmbedKeyboard) + m_darwinEmbedKeyboard->fireCallback(m_inputTextField.text.UTF8String); +} + +- (void)setCancelFlag:(bool*)cancelFlag +{ + m_canceled = cancelFlag; +} + +@end diff --git a/xbmc/platform/darwin/ios-common/IOSKeyboard.h b/xbmc/platform/darwin/ios-common/IOSKeyboard.h deleted file mode 100644 index 74e5166bd7..0000000000 --- a/xbmc/platform/darwin/ios-common/IOSKeyboard.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2012-2018 Team Kodi - * This file is part of Kodi - https://kodi.tv - * - * SPDX-License-Identifier: GPL-2.0-or-later - * See LICENSES/README.md for more information. - */ - -#pragma once - -#include "guilib/GUIKeyboard.h" - -class CIOSKeyboardImpl; - -class CIOSKeyboard : public CGUIKeyboard -{ - public: - CIOSKeyboard(); - bool ShowAndGetInput(char_callback_t pCallback, const std::string& initialString, std::string& typedString, const std::string& heading, bool bHiddenInput) override; - void Cancel() override; - void fireCallback(const std::string &str); - void invalidateCallback() {m_pCharCallback = nullptr;} - bool SetTextToKeyboard(const std::string& text, bool closeKeyboard = false) override; - - private: - char_callback_t m_pCharCallback; - bool m_bCanceled; - std::unique_ptr<CIOSKeyboardImpl> m_impl; -}; diff --git a/xbmc/platform/darwin/ios-common/IOSKeyboard.mm b/xbmc/platform/darwin/ios-common/IOSKeyboard.mm deleted file mode 100644 index c76572ba5c..0000000000 --- a/xbmc/platform/darwin/ios-common/IOSKeyboard.mm +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2012-2018 Team Kodi - * This file is part of Kodi - https://kodi.tv - * - * SPDX-License-Identifier: GPL-2.0-or-later - * See LICENSES/README.md for more information. - */ - -#include "platform/darwin/ios-common/IOSKeyboard.h" - -#include "platform/darwin/NSLogDebugHelpers.h" -#include "platform/darwin/ios-common/IOSKeyboardView.h" -#include "platform/darwin/ios/XBMCController.h" - -class CIOSKeyboardImpl -{ -public: - KeyboardView* g_pIosKeyboard = nil; -}; - -CIOSKeyboard::CIOSKeyboard() - : CGUIKeyboard() - , m_pCharCallback{nullptr} - , m_bCanceled{false} - , m_impl{new CIOSKeyboardImpl} -{ -} - -bool CIOSKeyboard::ShowAndGetInput(char_callback_t pCallback, const std::string &initialString, std::string &typedString, const std::string &heading, bool bHiddenInput) -{ - // we are in xbmc main thread or python module thread. - @autoreleasepool - { - @synchronized([KeyboardView class]) - { - // in case twice open keyboard. - if (m_impl->g_pIosKeyboard) - return false; - - // assume we are only drawn on the mainscreen ever! - auto keyboardFrame = [g_xbmcController fullscreenSubviewFrame]; - - //create the keyboardview - m_impl->g_pIosKeyboard = [[KeyboardView alloc] initWithFrame:keyboardFrame]; - if (!m_impl->g_pIosKeyboard) - return false; - - // inform the controller that the native keyboard is active - // basically as long as m_impl->g_pIosKeyboard exists... - [g_xbmcController nativeKeyboardActive:true]; - } - - m_pCharCallback = pCallback; - - // init keyboard stuff - SetTextToKeyboard(initialString); - [m_impl->g_pIosKeyboard setHidden:bHiddenInput]; - [m_impl->g_pIosKeyboard setHeading:[NSString stringWithUTF8String:heading.c_str()]]; - [m_impl->g_pIosKeyboard registerKeyboard:this]; // for calling back - bool confirmed = false; - if (!m_bCanceled) - { - [m_impl->g_pIosKeyboard setCancelFlag:&m_bCanceled]; - [m_impl->g_pIosKeyboard activate]; // blocks and shows keyboard - // user is done - get resulted text and confirmation - confirmed = m_impl->g_pIosKeyboard.isConfirmed; - if (confirmed) - typedString = [m_impl->g_pIosKeyboard.text UTF8String]; - } - @synchronized([KeyboardView class]) - { - m_impl->g_pIosKeyboard = nil; - [g_xbmcController nativeKeyboardActive:false]; - } - return confirmed; - } -} - -void CIOSKeyboard::Cancel() -{ - m_bCanceled = true; -} - -bool CIOSKeyboard::SetTextToKeyboard(const std::string &text, bool closeKeyboard /* = false */) -{ - if (!m_impl->g_pIosKeyboard) - return false; - [m_impl->g_pIosKeyboard setKeyboardText:[NSString stringWithUTF8String:text.c_str()] - closeKeyboard:closeKeyboard ? YES : NO]; - return true; -} - -//wrap our callback between objc and c++ -void CIOSKeyboard::fireCallback(const std::string &str) -{ - if(m_pCharCallback) - { - m_pCharCallback(this, str); - } -} diff --git a/xbmc/platform/darwin/ios-common/IOSKeyboardView.h b/xbmc/platform/darwin/ios-common/IOSKeyboardView.h deleted file mode 100644 index 1ddec3ac32..0000000000 --- a/xbmc/platform/darwin/ios-common/IOSKeyboardView.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2012-2018 Team Kodi - * This file is part of Kodi - https://kodi.tv - * - * SPDX-License-Identifier: GPL-2.0-or-later - * See LICENSES/README.md for more information. - */ - -#include "platform/darwin/ios-common/IOSKeyboard.h" - -#import <UIKit/UIKit.h> - -@interface KeyboardView : UIView <UITextFieldDelegate> -{ - NSMutableString *text; - BOOL _confirmed; - CIOSKeyboard *_iosKeyboard; - bool *_canceled; - BOOL _deactivated; - UITextField *_textField; - UITextField *_heading; - int _keyboardIsShowing; // 0: not, 1: will show, 2: showing - CGRect _kbRect; -} - -@property(nonatomic, strong) NSMutableString* text; -@property (getter = isConfirmed) BOOL _confirmed; -@property (assign, setter = registerKeyboard:) CIOSKeyboard *_iosKeyboard; -@property CGRect _frame; - -- (void) setHeading:(NSString *)heading; -- (void) setHidden:(BOOL)hidden; -- (void) activate; -- (void) deactivate; -- (void) setKeyboardText:(NSString*)aText closeKeyboard:(BOOL)closeKeyboard; -- (void) textChanged:(NSNotification*)aNotification; -- (void) setCancelFlag:(bool *)cancelFlag; -- (void) doDeactivate:(NSDictionary *)dict; -- (id)initWithFrameInternal; -@end diff --git a/xbmc/platform/darwin/ios-common/IOSKeyboardView.mm b/xbmc/platform/darwin/ios-common/IOSKeyboardView.mm deleted file mode 100644 index 84d69325b1..0000000000 --- a/xbmc/platform/darwin/ios-common/IOSKeyboardView.mm +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Copyright (C) 2012-2018 Team Kodi - * This file is part of Kodi - https://kodi.tv - * - * SPDX-License-Identifier: GPL-2.0-or-later - * See LICENSES/README.md for more information. - */ - -#import "platform/darwin/ios-common/IOSKeyboardView.h" - -#include "Application.h" -#include "guilib/GUIKeyboardFactory.h" -#include "threads/Event.h" - -#import "platform/darwin/NSLogDebugHelpers.h" -#import "platform/darwin/ios-common/IOSKeyboard.h" -#import "platform/darwin/ios/IOSScreenManager.h" -#import "platform/darwin/ios/XBMCController.h" - -static CEvent keyboardFinishedEvent; - -#define INPUT_BOX_HEIGHT 30 -#define SPACE_BETWEEN_INPUT_AND_KEYBOARD 0 - -@implementation KeyboardView -@synthesize text; -@synthesize _confirmed; -@synthesize _iosKeyboard; -@synthesize _frame; - -- (id)initWithFrame:(CGRect)frame -{ - _frame = frame; - if([NSThread currentThread] != [NSThread mainThread]) - { - [self performSelectorOnMainThread:@selector(initWithFrameInternal) withObject:nil waitUntilDone:YES]; - } - else - { - self = [self initWithFrameInternal]; - } - return self; -} - -- (id)initWithFrameInternal -{ - CGRect frame = _frame; - self = [super initWithFrame:frame]; - if (self) - { - _iosKeyboard = nil; - _keyboardIsShowing = 0; - _confirmed = NO; - _canceled = NULL; - _deactivated = NO; - - self.text = [NSMutableString stringWithString:@""]; - - // default input box position above the half screen. - CGRect textFieldFrame = CGRectMake(frame.size.width/2, - frame.size.height/2-INPUT_BOX_HEIGHT-SPACE_BETWEEN_INPUT_AND_KEYBOARD, - frame.size.width/2, - INPUT_BOX_HEIGHT); - _textField = [[UITextField alloc] initWithFrame:textFieldFrame]; - _textField.clearButtonMode = UITextFieldViewModeAlways; - // UITextBorderStyleRoundedRect; - with round rect we can't control backgroundcolor - _textField.borderStyle = UITextBorderStyleNone; - _textField.returnKeyType = UIReturnKeyDone; - _textField.autocapitalizationType = UITextAutocapitalizationTypeNone; - _textField.backgroundColor = [UIColor whiteColor]; - _textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter; - _textField.delegate = self; - - CGRect labelFrame = textFieldFrame; - labelFrame.origin.x = 0; - _heading = [[UITextField alloc] initWithFrame:labelFrame]; - _heading.borderStyle = UITextBorderStyleNone; - _heading.backgroundColor = [UIColor whiteColor]; - _heading.adjustsFontSizeToFitWidth = YES; - _heading.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter; - _heading.enabled = NO; - - [self addSubview:_heading]; - [self addSubview:_textField]; - - self.userInteractionEnabled = YES; - - [self setAlpha:0.9]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(textChanged:) - name:UITextFieldTextDidChangeNotification - object:_textField]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(keyboardDidHide:) - name:UIKeyboardDidHideNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(keyboardDidChangeFrame:) - name:UIKeyboardDidChangeFrameNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(keyboardWillShow:) - name:UIKeyboardWillShowNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(keyboardDidShow:) - name:UIKeyboardDidShowNotification - object:nil]; - } - return self; -} - -- (void)layoutSubviews -{ - CGFloat headingW = 0; - if (_heading.text and _heading.text.length > 0) - { - CGSize headingSize = [_heading.text sizeWithAttributes: @{NSFontAttributeName: [UIFont systemFontOfSize:[UIFont systemFontSize]]}]; - headingW = MIN(self.bounds.size.width/2, headingSize.width+30); - } - - CGFloat y = _kbRect.origin.y - INPUT_BOX_HEIGHT - SPACE_BETWEEN_INPUT_AND_KEYBOARD; - - _heading.frame = CGRectMake(0, y, headingW, INPUT_BOX_HEIGHT); - _textField.frame = CGRectMake(headingW, y, self.bounds.size.width-headingW, INPUT_BOX_HEIGHT); -} - --(void)keyboardWillShow:(NSNotification *) notification{ - NSDictionary* info = [notification userInfo]; - CGRect kbRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; - LOG(@"keyboardWillShow: keyboard frame: %@", NSStringFromCGRect(kbRect)); - _kbRect = kbRect; - [self setNeedsLayout]; - _keyboardIsShowing = 1; -} - --(void)keyboardDidShow:(NSNotification *) notification{ - LOG(@"keyboardDidShow: deactivated: %d", _deactivated); - _keyboardIsShowing = 2; - if (_deactivated) - [self doDeactivate:nil]; -} - -- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event -{ - PRINT_SIGNATURE(); - [_textField resignFirstResponder]; -} - -- (BOOL)textFieldShouldEndEditing:(UITextField *)textField -{ - LOG(@"%s: keyboard IsShowing %d", __PRETTY_FUNCTION__, _keyboardIsShowing); - // Do not break the keyboard show up process, else we will lost - // keyboard did hide notification. - return _keyboardIsShowing != 1; -} - -- (void)textFieldDidEndEditing:(UITextField *)textField -{ - PRINT_SIGNATURE(); - [self deactivate]; -} - --(BOOL)textFieldShouldReturn:(UITextField *)textField{ - PRINT_SIGNATURE(); - _confirmed = YES; - [_textField resignFirstResponder]; - return YES; -} - -- (void)keyboardDidChangeFrame:(id)sender -{ -} - -- (void)keyboardDidHide:(id)sender -{ - PRINT_SIGNATURE(); - - _keyboardIsShowing = 0; - - if (_textField.editing) - { - LOG(@"kb hide when editing, it could be a language switch"); - return; - } - - [self deactivate]; -} - -- (void) doActivate:(NSDictionary *)dict -{ - PRINT_SIGNATURE(); - [g_xbmcController activateKeyboard:self]; - [_textField becomeFirstResponder]; - [self setNeedsLayout]; - keyboardFinishedEvent.Reset(); -} - -- (void)activate -{ - PRINT_SIGNATURE(); - if([NSThread currentThread] != [NSThread mainThread]) - { - [self performSelectorOnMainThread:@selector(doActivate:) withObject:nil waitUntilDone:YES]; - } - else - { - // this would be fatal! We never should be called from the ios mainthread - return; - } - - // we are waiting on the user finishing the keyboard - while(!keyboardFinishedEvent.WaitMSec(500)) - { - if (NULL != _canceled && *_canceled) - { - [self deactivate]; - _canceled = NULL; - } - } -} - -- (void) doDeactivate:(NSDictionary *)dict -{ - LOG(@"%s: keyboard IsShowing %d", __PRETTY_FUNCTION__, _keyboardIsShowing); - _deactivated = YES; - - // Do not break keyboard show up process, if so there's a bug of ios4 will not - // notify us keyboard hide. - if (_keyboardIsShowing == 1) - return; - - // invalidate our callback object - if(_iosKeyboard) - { - _iosKeyboard->invalidateCallback(); - _iosKeyboard = nil; - } - // give back the control to whoever - [_textField resignFirstResponder]; - - // delay closing view until text field finishes resigning first responder - dispatch_async(dispatch_get_main_queue(), ^{ - // always called in the mainloop context - // detach the keyboard view from our main controller - [g_xbmcController deactivateKeyboard:self]; - - // no more notification we want to receive. - [[NSNotificationCenter defaultCenter] removeObserver: self]; - - keyboardFinishedEvent.Set(); - }); -} - -- (void) deactivate -{ - PRINT_SIGNATURE(); - if([NSThread currentThread] != [NSThread mainThread]) - { - [self performSelectorOnMainThread:@selector(doDeactivate:) withObject:nil waitUntilDone:YES]; - } - else - { - [self doDeactivate:nil]; - } -} - -- (void) setKeyboardText:(NSString*)aText closeKeyboard:(BOOL)closeKeyboard -{ - LOG(@"%s: %@, %d", __PRETTY_FUNCTION__, aText, closeKeyboard); - if([NSThread currentThread] != [NSThread mainThread]) - { - [self performSelectorOnMainThread:@selector(setDefault:) withObject:aText waitUntilDone:YES]; - } - else - { - [self setDefault:aText]; - } - if (closeKeyboard) - { - _confirmed = YES; - [self deactivate]; - } -} - -- (void) setHeading:(NSString *)heading -{ - if([NSThread currentThread] != [NSThread mainThread]) - { - [self performSelectorOnMainThread:@selector(setHeadingInternal:) withObject:heading waitUntilDone:YES]; - } - else - { - [self setHeadingInternal:heading]; - } -} - -- (void) setHeadingInternal:(NSString *)heading -{ - if (heading && heading.length > 0) { - _heading.text = [NSString stringWithFormat:@" %@:", heading]; - } - else { - _heading.text = nil; - } -} - -- (void) setDefault:(NSString *)defaultText -{ - [_textField setText:defaultText]; - [self textChanged:nil]; -} - -- (void) setHiddenInternal:(NSNumber *)hidden -{ - BOOL hiddenBool = [hidden boolValue]; - [_textField setSecureTextEntry:hiddenBool]; -} - -- (void) setHidden:(BOOL)hidden -{ - NSNumber *passedValue = [NSNumber numberWithBool:hidden]; - - if([NSThread currentThread] != [NSThread mainThread]) - { - [self performSelectorOnMainThread:@selector(setHiddenInternal:) withObject:passedValue waitUntilDone:YES]; - } - else - { - [self setHiddenInternal:passedValue]; - } -} - -- (void) textChanged:(NSNotification*)aNotification -{ - if (![self.text isEqualToString:_textField.text]) - { - [self.text setString:_textField.text]; - if (_iosKeyboard) - { - _iosKeyboard->fireCallback([self.text UTF8String]); - } - } -} - -- (void) setCancelFlag:(bool *)cancelFlag -{ - _canceled = cancelFlag; -} - -@end diff --git a/xbmc/platform/darwin/ios/CMakeLists.txt b/xbmc/platform/darwin/ios/CMakeLists.txt index 6214b00bb7..516fe43d62 100644 --- a/xbmc/platform/darwin/ios/CMakeLists.txt +++ b/xbmc/platform/darwin/ios/CMakeLists.txt @@ -1,12 +1,14 @@ set(SOURCES CPUInfoIos.cpp IOSEAGLView.mm IOSExternalTouchController.mm + IOSKeyboardView.mm IOSScreenManager.mm XBMCController.mm) set(HEADERS CPUInfoIos.h IOSEAGLView.h IOSExternalTouchController.h + IOSKeyboardView.h IOSScreenManager.h XBMCApplication.h XBMCController.h) diff --git a/xbmc/platform/darwin/ios/IOSKeyboardView.h b/xbmc/platform/darwin/ios/IOSKeyboardView.h new file mode 100644 index 0000000000..f5e3a61591 --- /dev/null +++ b/xbmc/platform/darwin/ios/IOSKeyboardView.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2019- Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#import "platform/darwin/ios-common/DarwinEmbedKeyboardView.h" + +@interface IOSKeyboardView : KeyboardView + +// use -initWithFrame: +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +@end diff --git a/xbmc/platform/darwin/ios/IOSKeyboardView.mm b/xbmc/platform/darwin/ios/IOSKeyboardView.mm new file mode 100644 index 0000000000..74ec08a705 --- /dev/null +++ b/xbmc/platform/darwin/ios/IOSKeyboardView.mm @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2019- Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#import "IOSKeyboardView.h" + +#import "utils/log.h" + +static const CGFloat INPUT_BOX_HEIGHT = 30; + +@interface IOSKeyboardView () +@property(nonatomic, weak) UIView* textFieldContainer; +@property(nonatomic, weak) NSLayoutConstraint* containerBottomConstraint; +@property(nonatomic, assign, getter=isKeyboardVisible) bool keyboardVisible; +@end + +@implementation IOSKeyboardView + +@synthesize textFieldContainer = m_textFieldContainer; +@synthesize containerBottomConstraint = m_containerBottomConstraint; +@synthesize keyboardVisible = m_keyboardVisible; + +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (!self) + return nil; + + m_keyboardVisible = false; + + self.backgroundColor = [UIColor colorWithWhite:0 alpha:0.1]; + + auto notificationCenter = NSNotificationCenter.defaultCenter; + [notificationCenter addObserver:self + selector:@selector(keyboardDidHide:) + name:UIKeyboardDidHideNotification + object:nil]; + [notificationCenter addObserver:self + selector:@selector(keyboardDidChangeFrame:) + name:UIKeyboardDidChangeFrameNotification + object:nil]; + [notificationCenter addObserver:self + selector:@selector(keyboardWillShow:) + name:UIKeyboardWillShowNotification + object:nil]; + [notificationCenter addObserver:self + selector:@selector(keyboardDidShow:) + name:UIKeyboardDidShowNotification + object:nil]; + + [self addGestureRecognizer:[[UITapGestureRecognizer alloc] + initWithTarget:m_inputTextField + action:@selector(resignFirstResponder)]]; + + auto textFieldContainer = [UIView new]; + textFieldContainer.translatesAutoresizingMaskIntoConstraints = NO; + textFieldContainer.backgroundColor = UIColor.whiteColor; + [textFieldContainer addSubview:m_inputTextField]; + [self addSubview:textFieldContainer]; + m_textFieldContainer = textFieldContainer; + + auto bottomConstraint = [textFieldContainer.bottomAnchor constraintEqualToAnchor:self.topAnchor]; + m_containerBottomConstraint = bottomConstraint; + + [NSLayoutConstraint activateConstraints:@[ + bottomConstraint, + [textFieldContainer.leadingAnchor constraintEqualToAnchor:self.leadingAnchor], + [textFieldContainer.trailingAnchor constraintEqualToAnchor:self.trailingAnchor], + [textFieldContainer.heightAnchor constraintEqualToConstant:INPUT_BOX_HEIGHT], + + [m_inputTextField.widthAnchor constraintEqualToAnchor:textFieldContainer.widthAnchor + multiplier:0.5], + [m_inputTextField.centerXAnchor constraintEqualToAnchor:textFieldContainer.centerXAnchor], + [m_inputTextField.topAnchor constraintEqualToAnchor:textFieldContainer.topAnchor], + [m_inputTextField.bottomAnchor constraintEqualToAnchor:textFieldContainer.bottomAnchor], + ]]; + + return self; +} + +- (void)keyboardWillShow:(NSNotification*)notification +{ + CLog::Log(LOGDEBUG, "{} {}", __PRETTY_FUNCTION__, notification.userInfo.description.UTF8String); + if (!self.isKeyboardVisible) + self.textFieldContainer.hidden = YES; +} + +- (void)keyboardDidShow:(NSNotification*)notification +{ + CLog::Log(LOGDEBUG, "{} deactivated: {}, {}", __PRETTY_FUNCTION__, m_deactivated, + notification.userInfo.description.UTF8String); + self.keyboardVisible = true; + self.textFieldContainer.hidden = NO; + + if (m_deactivated) + [self deactivate]; +} + +- (BOOL)textFieldShouldEndEditing:(UITextField*)textField +{ + CLog::Log(LOGDEBUG, "{}: keyboard IsShowing {}", __PRETTY_FUNCTION__, self.isKeyboardVisible); + return YES; +} + +- (BOOL)textFieldShouldReturn:(UITextField*)textField +{ + auto result = [super textFieldShouldReturn:textField]; + if (result) + [textField resignFirstResponder]; + return result; +} + +- (void)keyboardDidChangeFrame:(NSNotification*)notification +{ + auto keyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; + // although converting isn't really necessary in our case, as the holder view occupies + // the whole screen, technically it's more correct + auto convertedFrame = [self convertRect:keyboardFrame + fromCoordinateSpace:UIScreen.mainScreen.coordinateSpace]; + self.containerBottomConstraint.constant = CGRectGetMinY(convertedFrame); + [self layoutIfNeeded]; +} + +- (void)keyboardDidHide:(id)sender +{ + if (m_inputTextField.editing) + { + CLog::Log(LOGDEBUG, "kb hide when editing, it could be a language switch"); + return; + } + + self.keyboardVisible = false; + [self deactivate]; +} + +- (void)deactivate +{ + CLog::Log(LOGDEBUG, "{}: keyboard IsShowing {}", __PRETTY_FUNCTION__, self.isKeyboardVisible); + [super deactivate]; +} + +@end |