初始化

This commit is contained in:
SmallMain
2022-06-25 00:23:03 +08:00
commit ef0589e8e5
2264 changed files with 617829 additions and 0 deletions

View File

@@ -0,0 +1,115 @@
/****************************************************************************
Copyright (c) 2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "EditBox.h"
#include "platform/android/jni/JniHelper.h"
#include "cocos/scripting/js-bindings/jswrapper/SeApi.h"
#include "cocos/scripting/js-bindings/manual/jsb_global.h"
#ifndef JCLS_EDITBOX
#define JCLS_EDITBOX "org/cocos2dx/lib/Cocos2dxEditBox"
#endif
#ifndef ORG_EDITBOX_CLASS_NAME
#define ORG_EDITBOX_CLASS_NAME org_cocos2dx_lib_Cocos2dxEditBox
#endif
#define JNI_EDITBOX(FUNC) JNI_METHOD1(ORG_EDITBOX_CLASS_NAME,FUNC)
NS_CC_BEGIN
void EditBox::show(const cocos2d::EditBox::ShowInfo& showInfo)
{
JniHelper::callStaticVoidMethod(JCLS_EDITBOX,
"showNative",
showInfo.defaultValue,
showInfo.maxLength,
showInfo.isMultiline,
showInfo.confirmHold,
showInfo.confirmType,
showInfo.inputType);
}
void EditBox::hide()
{
JniHelper::callStaticVoidMethod(JCLS_EDITBOX, "hideNative");
}
void EditBox::updateRect(int x, int y, int width, int height)
{
// not supported ...
}
NS_CC_END
namespace
{
se::Value textInputCallback;
void getTextInputCallback()
{
if (! textInputCallback.isUndefined())
return;
auto global = se::ScriptEngine::getInstance()->getGlobalObject();
se::Value jsbVal;
if (global->getProperty("jsb", &jsbVal) && jsbVal.isObject())
{
jsbVal.toObject()->getProperty("onTextInput", &textInputCallback);
// free globle se::Value before ScriptEngine clean up
se::ScriptEngine::getInstance()->addBeforeCleanupHook([](){
textInputCallback.setUndefined();
});
}
}
void callJSFunc(const std::string& type, const jstring& text)
{
getTextInputCallback();
se::AutoHandleScope scope;
se::ValueArray args;
args.push_back(se::Value(type));
args.push_back(se::Value(cocos2d::JniHelper::jstring2string(text)));
textInputCallback.toObject()->call(args, nullptr);
}
}
extern "C"
{
JNIEXPORT void JNICALL JNI_EDITBOX(onKeyboardInputNative)(JNIEnv* env, jclass, jstring text)
{
callJSFunc("input", text);
}
JNIEXPORT void JNICALL JNI_EDITBOX(onKeyboardCompleteNative)(JNIEnv* env, jclass, jstring text)
{
callJSFunc("complete", text);
}
JNIEXPORT void JNICALL JNI_EDITBOX(onKeyboardConfirmNative)(JNIEnv* env, jclass, jstring text)
{
callJSFunc("confirm", text);
}
}

View File

@@ -0,0 +1,482 @@
/****************************************************************************
Copyright (c) 2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "EditBox.h"
#include "platform/CCApplication.h"
#include "platform/ios/CCEAGLView-ios.h"
#include "cocos/scripting/js-bindings/jswrapper/SeApi.h"
#include "cocos/scripting/js-bindings/manual/jsb_global.h"
#import <UIKit/UITextField.h>
#import <UIKit/UITextView.h>
#define TEXT_LINE_HEIGHT 40
#define TEXT_VIEW_MAX_LINE_SHOWN 3
#define BUTTON_HIGHT (TEXT_LINE_HEIGHT - 2)
#define BUTTON_WIDTH 60
#define TO_TEXT_VIEW(textinput) ((UITextView*)textinput)
#define TO_TEXT_FIELD(textinput) ((UITextField*)textinput)
/*************************************************************************
Inner class declarations.
************************************************************************/
// MARK: class declaration
@interface ButtonHandler : NSObject
-(IBAction) buttonTapped:(UIButton *)button;
@end
@interface KeyboardEventHandler : NSObject
-(void)keyboardWillShow: (NSNotification*) notification;
-(void)keyboardWillHide: (NSNotification*) notification;
@end
@interface TextFieldDelegate : NSObject<UITextFieldDelegate>
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string;
- (void)textFieldDidChange:(UITextField *)textField;
- (BOOL)textFieldShouldReturn:(UITextField *)textField;
@end
@interface TextViewDelegate : NSObject<UITextViewDelegate>
- (BOOL) textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
- (void) textViewDidChange:(UITextView *)textView;
@end
/*************************************************************************
Global variables and functions.
************************************************************************/
// MARK: global variables and functions
namespace
{
bool g_isMultiline = false;
bool g_confirmHold = false;
int g_maxLength = INT_MAX;
KeyboardEventHandler* g_keyboardHandler = nil;
// "#1fa014", a color of dark green, was used for confirm button background
static UIColor* g_darkGreen = [UIColor colorWithRed:31/255.0 green:160/255.0 blue:20/255.0 alpha:0.8];
UITextField* g_textField = nil;
TextFieldDelegate* g_textFieldDelegate = nil;
UIButton* g_textFieldConfirmButton = nil;
ButtonHandler* g_textFieldConfirmButtonHandler = nil;
UITextView* g_textView = nil;
TextViewDelegate* g_textViewDelegate = nil;
UIButton* g_textViewConfirmButton = nil;
ButtonHandler* g_textViewConfirmButtonHander = nil;
UIView* getCurrentView()
{
if (g_isMultiline)
return g_textView;
else
return g_textField;
}
NSString* getCurrentText()
{
if (g_isMultiline)
return g_textView.text;
else
return g_textField.text;
}
void setText(NSString* text)
{
if (g_isMultiline)
g_textView.text = text;
else
g_textField.text = text;
}
se::Value textInputCallback;
void getTextInputCallback()
{
if (! textInputCallback.isUndefined())
return;
auto global = se::ScriptEngine::getInstance()->getGlobalObject();
se::Value jsbVal;
if (global->getProperty("jsb", &jsbVal) && jsbVal.isObject())
{
jsbVal.toObject()->getProperty("onTextInput", &textInputCallback);
// free globle se::Value before ScriptEngine clean up
se::ScriptEngine::getInstance()->addBeforeCleanupHook([](){
textInputCallback.setUndefined();
});
}
}
void callJSFunc(const std::string& type, const std::string& text)
{
getTextInputCallback();
se::AutoHandleScope scope;
se::ValueArray args;
args.push_back(se::Value(type));
args.push_back(se::Value(text));
textInputCallback.toObject()->call(args, nullptr);
}
int getTextInputHeight()
{
if (g_isMultiline)
return TEXT_LINE_HEIGHT * TEXT_VIEW_MAX_LINE_SHOWN;
else
return TEXT_LINE_HEIGHT;
}
void createButton(UIButton** button, ButtonHandler** buttonHandler, const CGRect& viewRect, const std::string& title)
{
ButtonHandler *btnHandler = [[ButtonHandler alloc] init];
UIButton* btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btn addTarget:btnHandler action:@selector(buttonTapped:)
forControlEvents:UIControlEventTouchUpInside];
btn.frame = CGRectMake(0, 0, BUTTON_WIDTH, BUTTON_HIGHT);
btn.backgroundColor = g_darkGreen;
[btn setTitle: [NSString stringWithUTF8String:title.c_str()]
forState:UIControlStateNormal];
[btn setTitleColor: [UIColor whiteColor]
forState:UIControlStateNormal];
*button = btn;
*buttonHandler = btnHandler;
}
void setTexFiledKeyboardType(UITextField* textField, const std::string& inputType)
{
if (0 == inputType.compare("password"))
{
textField.secureTextEntry = TRUE;
textField.keyboardType = UIKeyboardTypeDefault;
}
else
{
textField.secureTextEntry = FALSE;
if (0 == inputType.compare("email"))
textField.keyboardType = UIKeyboardTypeEmailAddress;
else if (0 == inputType.compare("number"))
textField.keyboardType = UIKeyboardTypeDecimalPad;
else if (0 == inputType.compare("url"))
textField.keyboardType = UIKeyboardTypeURL;
else if (0 == inputType.compare("text"))
textField.keyboardType = UIKeyboardTypeDefault;
}
}
void setTextFieldReturnType(UITextField* textField, const std::string& returnType)
{
if (0 == returnType.compare("done"))
textField.returnKeyType = UIReturnKeyDone;
else if (0 == returnType.compare("next"))
textField.returnKeyType = UIReturnKeyNext;
else if (0 == returnType.compare("search"))
textField.returnKeyType = UIReturnKeySearch;
else if (0 == returnType.compare("go"))
textField.returnKeyType = UIReturnKeyGo;
else if (0 == returnType.compare("send"))
textField.returnKeyType = UIReturnKeySend;
}
NSString* getConfirmButtonTitle(const std::string& returnType)
{
NSString* titleKey = [NSString stringWithUTF8String: returnType.c_str()];
return NSLocalizedString(titleKey, nil); // get i18n string to be the title
}
void initTextField(const CGRect& rect, const cocos2d::EditBox::ShowInfo& showInfo)
{
if (! g_textField)
{
g_textField = [[UITextField alloc] initWithFrame:rect];
g_textField.textColor = [UIColor blackColor];
g_textField.backgroundColor = [UIColor whiteColor];
[g_textField setBorderStyle:UITextBorderStyleLine];
g_textField.backgroundColor = [UIColor whiteColor];
g_textFieldDelegate = [[TextFieldDelegate alloc] init];
g_textField.delegate = g_textFieldDelegate;
// Assign the overlay button to a stored text field
createButton(&g_textFieldConfirmButton, &g_textFieldConfirmButtonHandler, rect, showInfo.confirmType);
g_textField.rightView = g_textFieldConfirmButton;
g_textField.rightViewMode = UITextFieldViewModeAlways;
[g_textField addTarget:g_textFieldDelegate action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
}
g_textField.frame = rect;
setTextFieldReturnType(g_textField, showInfo.confirmType);
setTexFiledKeyboardType(g_textField, showInfo.inputType);
g_textField.text = [NSString stringWithUTF8String: showInfo.defaultValue.c_str()];
[g_textFieldConfirmButton setTitle:getConfirmButtonTitle(showInfo.confirmType) forState:UIControlStateNormal];
}
void initTextView(const CGRect& viewRect, const CGRect& btnRect, const cocos2d::EditBox::ShowInfo& showInfo)
{
if (!g_textView)
{
g_textView = [[UITextView alloc] initWithFrame:btnRect];
g_textView.textColor = [UIColor blackColor];
g_textView.backgroundColor = [UIColor whiteColor];
g_textViewDelegate = [[TextViewDelegate alloc] init];
g_textView.delegate = g_textViewDelegate;
createButton(&g_textViewConfirmButton, &g_textViewConfirmButtonHander, btnRect, showInfo.confirmType);
g_textViewConfirmButton.frame = CGRectMake(viewRect.size.width - BUTTON_WIDTH, 0, BUTTON_WIDTH, BUTTON_HIGHT);
[g_textView addSubview:g_textViewConfirmButton];
}
g_textView.frame = btnRect;
g_textView.text = [NSString stringWithUTF8String: showInfo.defaultValue.c_str()];
[g_textViewConfirmButton setTitle:getConfirmButtonTitle(showInfo.confirmType) forState:UIControlStateNormal];
}
CGRect getSafeAreaRect()
{
UIView* view = (UIView*)cocos2d::Application::getInstance()->getView();
CGRect viewRect = view.frame;
// safeAreaInsets is avaible since iOS 11.
if (@available(iOS 11.0, *))
{
auto safeAreaInsets = view.safeAreaInsets;
UIInterfaceOrientation sataus = [UIApplication sharedApplication].statusBarOrientation;
if (UIInterfaceOrientationLandscapeLeft == sataus) {
viewRect.origin.x = 0;
viewRect.size.width -= safeAreaInsets.right;
}
else {
viewRect.origin.x += safeAreaInsets.left;
viewRect.size.width -= safeAreaInsets.left;
}
}
return viewRect;
}
void addTextInput(const cocos2d::EditBox::ShowInfo& showInfo)
{
auto safeAreaRect = getSafeAreaRect();
int height = getTextInputHeight();
CGRect rect = CGRectMake(safeAreaRect.origin.x,
safeAreaRect.size.height - height,
safeAreaRect.size.width,
height);
if (showInfo.isMultiline)
initTextView(safeAreaRect, rect, showInfo);
else
initTextField(rect, showInfo);
UIView* textInput = getCurrentView();
UIView* view = (UIView*)cocos2d::Application::getInstance()->getView();
[view addSubview:textInput];
[textInput becomeFirstResponder];
}
void addKeyboardEventLisnters()
{
if (!g_keyboardHandler)
g_keyboardHandler = [[KeyboardEventHandler alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:g_keyboardHandler
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:g_keyboardHandler
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
void removeKeyboardEventLisnters()
{
if (!g_keyboardHandler)
return;
[[NSNotificationCenter defaultCenter] removeObserver:g_keyboardHandler];
}
}
/*************************************************************************
Class implementations.
************************************************************************/
// MARK: class implementation
@implementation KeyboardEventHandler
-(void)keyboardWillShow: (NSNotification*) notification
{
UIView* textView = getCurrentView();
if (!textView)
return;
NSDictionary* keyboardInfo = [notification userInfo];
NSValue* keyboardFrame = [keyboardInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
CGSize kbSize = [keyboardFrame CGRectValue].size;
int textHeight = getTextInputHeight();
CGRect screenRect = getSafeAreaRect();
textView.frame = CGRectMake(screenRect.origin.x,
screenRect.size.height - textHeight - kbSize.height,
screenRect.size.width,
textHeight);
}
-(void)keyboardWillHide: (NSNotification*) notification
{
NSDictionary *info = [notification userInfo];
CGRect beginKeyboardRect = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
CGRect endKeyboardRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGFloat yOffset = endKeyboardRect.origin.y - beginKeyboardRect.origin.y;
if (yOffset <= 0) {
cocos2d::EditBox::complete();
cocos2d::EditBox::hide();
}
}
@end
@implementation TextFieldDelegate
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
// REFINE: check length limit before text changed
return YES;
}
- (void)textFieldDidChange:(UITextField *)textField
{
if (textField.markedTextRange != nil)
return;
// check length limit after text changed, a little rude
if (textField.text.length > g_maxLength) {
NSRange rangeIndex = [textField.text rangeOfComposedCharacterSequenceAtIndex:g_maxLength];
auto newText = [textField.text substringToIndex:rangeIndex.location];
dispatch_async(dispatch_get_main_queue(), ^{
textField.text = newText;
});
}
callJSFunc("input", [textField.text UTF8String]);
}
-(BOOL) textFieldShouldReturn:(UITextField *)textField
{
cocos2d::EditBox::complete();
return YES;
}
@end
@implementation ButtonHandler
-(IBAction) buttonTapped:(UIButton *)button
{
const std::string text([getCurrentText() UTF8String]);
callJSFunc("confirm", text);
if (!g_confirmHold)
cocos2d::EditBox::complete();
}
@end
@implementation TextViewDelegate
- (BOOL) textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
// REFINE: check length limit before text changed
return YES;
}
- (void)textViewDidChange:(UITextView *)textView
{
if (textView.markedTextRange != nil)
return;
// check length limit after text changed, a little rude
if (textView.text.length > g_maxLength) {
auto newText = [textView.text substringToIndex:g_maxLength];
// fix undo crash
dispatch_async(dispatch_get_main_queue(), ^{
textView.text = newText;
});
}
callJSFunc("input", [textView.text UTF8String]);
}
@end
/*************************************************************************
Implementation of EditBox.
************************************************************************/
// MARK: EditBox
NS_CC_BEGIN
void EditBox::show(const cocos2d::EditBox::ShowInfo& showInfo)
{
// Should initialize them at first.
g_maxLength = showInfo.maxLength;
g_isMultiline = showInfo.isMultiline;
g_confirmHold = showInfo.confirmHold;
[(CCEAGLView*)cocos2d::Application::getInstance()->getView() setPreventTouchEvent:true];
addKeyboardEventLisnters();
addTextInput(showInfo);
}
void EditBox::hide()
{
removeKeyboardEventLisnters();
UIView* view = getCurrentView();
if (view)
{
[view removeFromSuperview];
[view resignFirstResponder];
}
[(CCEAGLView*)cocos2d::Application::getInstance()->getView() setPreventTouchEvent:false];
}
void EditBox::updateRect(int x, int y, int width, int height)
{
// not supported ...
}
void EditBox::complete()
{
NSString* text = getCurrentText();
callJSFunc("complete", [text UTF8String]);
EditBox::hide();
}
NS_CC_END

View File

@@ -0,0 +1,340 @@
/****************************************************************************
Copyright (c) 2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "EditBox.h"
#include "platform/desktop/CCGLView-desktop.h"
#include "platform/CCApplication.h"
#include "cocos/scripting/js-bindings/jswrapper/SeApi.h"
#include "cocos/scripting/js-bindings/manual/jsb_global.h"
/*************************************************************************
Forward declaration of global functions.
************************************************************************/
namespace
{
void callJSFunc(const std::string& type, const std::string& text);
}
/*************************************************************************
Global variables.
************************************************************************/
namespace
{
NSTextView* g_textView = nil;
NSScrollView* g_scrollView = nil;
NSTextField* g_textField = nil;
NSSecureTextField* g_secureTextField = nil;
bool g_isMultiline = false;
bool g_isPassword = false;
int g_maxLength = INT_MAX;
se::Value g_textInputCallback;
}
/*************************************************************************
TextViewDelegate
************************************************************************/
@interface TextViewDelegate : NSObject<NSTextViewDelegate>
@end
@implementation TextViewDelegate
// Get notification when something is input.
- (void) textDidChange:(NSNotification *)notification
{
callJSFunc("input", [[g_textView.textStorage string] UTF8String]);
}
// Max length limitaion
- (BOOL) textView:(NSTextView *)textView
shouldChangeTextInRange:(NSRange)affectedCharRange
replacementString:(NSString *)replacementString
{
NSUInteger newLength = [textView.string length] + [replacementString length] - affectedCharRange.length;
if (newLength > g_maxLength)
return FALSE;
if (!g_isMultiline && [replacementString containsString:@"\n"])
return FALSE;
return TRUE;
}
@end
/*************************************************************************
TextFieldDelegate
************************************************************************/
@interface TextFieldDelegate: NSObject<NSTextFieldDelegate>
@end
@implementation TextFieldDelegate
- (void)controlTextDidChange:(NSNotification *)notification
{
NSTextField *textField = [notification object];
callJSFunc("input", [textField.stringValue UTF8String]);
}
@end
/*************************************************************************
TextFieldFormatter: used for textfield length limitation.
************************************************************************/
@interface TextFieldFormatter : NSFormatter
{
int maxLength;
}
- (void)setMaximumLength:(int)len;
@end
@implementation TextFieldFormatter
- (id)init
{
if(self = [super init])
maxLength = INT_MAX;
return self;
}
- (void)setMaximumLength:(int)len
{
maxLength = len;
}
- (NSString *)stringForObjectValue:(id)object
{
return (NSString *)object;
}
- (BOOL)getObjectValue:(id *)object forString:(NSString *)string errorDescription:(NSString **)error
{
*object = string;
return YES;
}
- (BOOL)isPartialStringValid:(NSString **)partialStringPtr
proposedSelectedRange:(NSRangePointer)proposedSelRangePtr
originalString:(NSString *)origString
originalSelectedRange:(NSRange)origSelRange
errorDescription:(NSString **)error
{
if ([*partialStringPtr length] > maxLength)
return NO;
return YES;
}
- (NSAttributedString *)attributedStringForObjectValue:(id)anObject withDefaultAttributes:(NSDictionary *)attributes
{
return nil;
}
@end
/*************************************************************************
Implementation of global helper functions.
************************************************************************/
namespace
{
void getTextInputCallback()
{
if (! g_textInputCallback.isUndefined())
return;
auto global = se::ScriptEngine::getInstance()->getGlobalObject();
se::Value jsbVal;
if (global->getProperty("jsb", &jsbVal) && jsbVal.isObject())
{
jsbVal.toObject()->getProperty("onTextInput", &g_textInputCallback);
// free globle se::Value before ScriptEngine clean up
se::ScriptEngine::getInstance()->addBeforeCleanupHook([](){
g_textInputCallback.setUndefined();
});
}
}
void callJSFunc(const std::string& type, const std::string& text)
{
getTextInputCallback();
se::AutoHandleScope scope;
se::ValueArray args;
args.push_back(se::Value(type));
args.push_back(se::Value(text));
g_textInputCallback.toObject()->call(args, nullptr);
}
void initTextView(const cocos2d::EditBox::ShowInfo& showInfo)
{
CGRect rect = CGRectMake(showInfo.x, showInfo.y, showInfo.width, showInfo.height);
if (! g_textView)
{
g_textView = [[NSTextView alloc] initWithFrame:rect];
g_textView.textColor = [NSColor blackColor];
g_textView.backgroundColor = [NSColor whiteColor];
g_textView.editable = TRUE;
g_textView.hidden = FALSE;
g_textView.delegate = [[TextViewDelegate alloc] init];
g_scrollView = [[NSScrollView alloc] initWithFrame:rect];
[g_scrollView setBorderType:NSNoBorder];
[g_scrollView setHasVerticalScroller:TRUE];
[g_scrollView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
[g_scrollView setDocumentView:g_textView];
}
g_textView.string = [NSString stringWithUTF8String:showInfo.defaultValue.c_str()];
g_textView.frame = rect;
g_scrollView.frame = rect;
auto glfwWindow = ((cocos2d::GLView*)cocos2d::Application::getInstance()->getView())->getGLFWWindow();
NSWindow* nsWindow = glfwGetCocoaWindow(glfwWindow);
[nsWindow.contentView addSubview:g_scrollView];
[nsWindow makeFirstResponder:g_scrollView];
}
void doInitTextField(NSTextField* textField, const CGRect& rect, const cocos2d::EditBox::ShowInfo& showInfo)
{
textField.editable = TRUE;
textField.wantsLayer = TRUE;
textField.frame = rect;
textField.stringValue = [NSString stringWithUTF8String:showInfo.defaultValue.c_str()];
[(TextFieldFormatter*)textField.formatter setMaximumLength: showInfo.maxLength];
auto glfwWindow = ((cocos2d::GLView*)cocos2d::Application::getInstance()->getView())->getGLFWWindow();
NSWindow* nsWindow = glfwGetCocoaWindow(glfwWindow);
[nsWindow.contentView addSubview:textField];
[textField becomeFirstResponder];
}
void initTextField(const cocos2d::EditBox::ShowInfo& showInfo)
{
CGRect rect = CGRectMake(showInfo.x, showInfo.y, showInfo.width, showInfo.height);
// Use NSSecureTextField for password, use NSTextField for others.
if (g_isPassword)
{
if (! g_secureTextField)
{
g_secureTextField = [[NSSecureTextField alloc] init];
g_secureTextField.textColor = [NSColor blackColor];
g_secureTextField.backgroundColor = [NSColor whiteColor];
g_secureTextField.delegate = [[TextFieldDelegate alloc] init];
g_secureTextField.formatter = [[TextFieldFormatter alloc] init];
}
doInitTextField(g_secureTextField, rect, showInfo);
}
else
{
if (! g_textField)
{
g_textField = [[NSTextField alloc] init];
g_textField.textColor = [NSColor blackColor];
g_textField.backgroundColor = [NSColor whiteColor];
g_textField.delegate = [[TextFieldDelegate alloc] init];
g_textField.formatter = [[TextFieldFormatter alloc] init];
}
doInitTextField(g_textField, rect, showInfo);
}
}
void init(const cocos2d::EditBox::ShowInfo& showInfo)
{
if (showInfo.isMultiline)
initTextView(showInfo);
else
initTextField(showInfo);
}
}
/*************************************************************************
Implementation of EditBox.
************************************************************************/
NS_CC_BEGIN
void EditBox::show(const ShowInfo& showInfo)
{
g_isMultiline = showInfo.isMultiline;
g_maxLength = showInfo.maxLength;
g_isPassword = showInfo.inputType == "password";
init(showInfo);
((GLView*)Application::getInstance()->getView())->setIsEditboxEditing(true);
}
void EditBox::hide()
{
if (g_scrollView)
[g_scrollView removeFromSuperview];
if (g_textField)
{
[g_textField resignFirstResponder];
[g_textField removeFromSuperview];
}
if (g_secureTextField)
{
[g_secureTextField resignFirstResponder];
[g_secureTextField removeFromSuperview];
}
((GLView*)Application::getInstance()->getView())->setIsEditboxEditing(false);
}
void EditBox::complete()
{
if (g_isMultiline)
callJSFunc("complete", [[g_textView.textStorage string] UTF8String]);
else
{
if (g_isPassword)
callJSFunc("complete", [g_secureTextField.stringValue UTF8String]);
else
callJSFunc("complete", [g_textField.stringValue UTF8String]);
}
EditBox::hide();
}
void EditBox::updateRect(int x, int y, int width, int height) {
CGRect rect = CGRectMake(x, y, width, height);
auto glfwWindow = ((cocos2d::GLView*)cocos2d::Application::getInstance()->getView())->getGLFWWindow();
NSWindow* nsWindow = glfwGetCocoaWindow(glfwWindow);
const auto& subviews = [nsWindow.contentView subviews];
if (g_scrollView && [subviews containsObject:g_scrollView])
{
g_scrollView.frame = rect;
if (g_textView)
{
g_textView.frame = rect;
}
}
else if (g_textField && [subviews containsObject:g_textField])
{
g_textField.frame = rect;
}
else if (g_secureTextField && [subviews containsObject:g_secureTextField])
{
g_secureTextField.frame = rect;
}
}
NS_CC_END

View File

@@ -0,0 +1,220 @@
/****************************************************************************
Copyright (c) 2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "EditBox.h"
#include "platform/CCApplication.h"
#include "platform/desktop/CCGLView-desktop.h"
#include "cocos/scripting/js-bindings/jswrapper/SeApi.h"
#include "cocos/scripting/js-bindings/manual/jsb_global.h"
#include <windows.h>
#include <locale>
#include <codecvt>
#include <stdlib.h>
NS_CC_BEGIN
/*************************************************************************
Global variables and functions.
************************************************************************/
namespace
{
HWND g_hwndEditBox = nullptr;
WNDPROC g_prevMainWindowProc = nullptr;
se::Value g_textInputCallback;
HWND getCocosWindow()
{
auto glfwWindow = ((GLView*)Application::getInstance()->getView())->getGLFWWindow();
return glfwGetWin32Window(glfwWindow);
}
int getCocosWindowHeight()
{
HWND parent = getCocosWindow();
RECT rect;
GetClientRect(parent, &rect);
return (rect.bottom - rect.top);
}
void getTextInputCallback()
{
if (!g_textInputCallback.isUndefined())
return;
auto global = se::ScriptEngine::getInstance()->getGlobalObject();
se::Value jsbVal;
if (global->getProperty("jsb", &jsbVal) && jsbVal.isObject())
{
jsbVal.toObject()->getProperty("onTextInput", &g_textInputCallback);
// free globle se::Value before ScriptEngine clean up
se::ScriptEngine::getInstance()->addBeforeCleanupHook([](){
g_textInputCallback.setUndefined();
});
}
}
void callJSFunc(const std::string& type, const std::string& text)
{
getTextInputCallback();
se::AutoHandleScope scope;
se::ValueArray args;
args.push_back(se::Value(type));
args.push_back(se::Value(text));
g_textInputCallback.toObject()->call(args, nullptr);
}
std::string getText(HWND hwnd)
{
int length = GetWindowTextLength(hwnd);
LPWSTR str = (LPWSTR)malloc(sizeof(WCHAR) * (length+1));
GetWindowText(hwnd, str, length + 1);
std::wstring_convert<std::codecvt_utf8<wchar_t>> convert;
std::string ret(convert.to_bytes(str));
free(str);
return ret;
}
std::wstring str2ws(const std::string& text)
{
if (text.empty())
return std::wstring();
int sz = MultiByteToWideChar(CP_UTF8, 0, &text[0], (int)text.size(), 0, 0);
std::wstring res(sz, 0);
MultiByteToWideChar(CP_UTF8, 0, &text[0], (int)text.size(), &res[0], sz);
return res;
}
LRESULT mainWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_LBUTTONDOWN:
EditBox::complete();
EditBox::hide();
SetFocus(getCocosWindow());
break;
case WM_COMMAND:
if (EN_CHANGE == HIWORD(wParam))
callJSFunc("input", getText(g_hwndEditBox).c_str());
break;
default:
break;
}
return CallWindowProc(g_prevMainWindowProc, hwnd, msg, wParam, lParam);
}
}
/*************************************************************************
Implementation of EditBox.
************************************************************************/
void EditBox::show(const EditBox::ShowInfo& showInfo)
{
int windowHeight = getCocosWindowHeight();
if (! g_hwndEditBox)
{
HWND parent = getCocosWindow();
g_prevMainWindowProc = (WNDPROC)SetWindowLongPtr(parent, GWL_WNDPROC, (LONG_PTR)mainWindowProc);
UINT32 flags = WS_CHILD | ES_LEFT | WS_TABSTOP | ES_AUTOHSCROLL;
if (showInfo.isMultiline)
flags |= ES_MULTILINE;
if (showInfo.inputType == "password")
flags |= WS_EX_TRANSPARENT;
g_hwndEditBox = CreateWindowEx(
WS_EX_WINDOWEDGE,
L"EDIT",
NULL,
flags,
0,
0,
0,
0,
parent,
0,
NULL,
NULL);
if (! g_hwndEditBox)
{
wchar_t buffer[256] = { 0 };
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
buffer,
sizeof(buffer) / sizeof(wchar_t),
NULL);
std::wstring_convert<std::codecvt_utf8<wchar_t>> convert;
CCLOG("Can not create editbox: %s", convert.to_bytes(buffer).c_str());
}
}
::SendMessageW(g_hwndEditBox, EM_LIMITTEXT, showInfo.maxLength, 0);
SetWindowPos(g_hwndEditBox,
HWND_NOTOPMOST,
showInfo.x,
windowHeight - showInfo.y - showInfo.height,
showInfo.width,
showInfo.height,
SWP_NOZORDER);
::SetWindowTextW(g_hwndEditBox, str2ws(showInfo.defaultValue).c_str());
::PostMessage(g_hwndEditBox, WM_ACTIVATE, 0, 0);
::ShowWindow(g_hwndEditBox, SW_SHOW);
SetFocus(g_hwndEditBox);
}
void EditBox::hide()
{
DestroyWindow(g_hwndEditBox);
g_hwndEditBox = nullptr;
SetWindowLongPtr(getCocosWindow(), GWL_WNDPROC, (LONG_PTR)g_prevMainWindowProc);
}
void EditBox::updateRect(int x, int y, int width, int height)
{
int windowHeight = getCocosWindowHeight();
SetWindowPos(g_hwndEditBox,
HWND_NOTOPMOST,
x,
windowHeight - y - height,
width,
height,
SWP_NOZORDER);
}
void EditBox::complete()
{
callJSFunc("complete", getText(g_hwndEditBox).c_str());
}
NS_CC_END

View File

@@ -0,0 +1,57 @@
/****************************************************************************
Copyright (c) 2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#pragma once
#include <string>
#include "base/ccMacros.h"
NS_CC_BEGIN
class EditBox
{
public:
struct ShowInfo
{
std::string defaultValue = "";
std::string confirmType = "";
std::string inputType = "";
int maxLength = 0;
int x = 0;
int y = 0;
int width = 0;
int height = 0;
bool confirmHold = false;
bool isMultiline = false;
};
static void show(const ShowInfo& showInfo);
static void hide();
static void updateRect(int x, int y, int width, int height);
// It is internally to send a complete message to JS.
// Don't call it by yourself untile you know the effect.
static void complete();
};
NS_CC_END