no message

This commit is contained in:
gem
2025-02-18 15:21:31 +08:00
commit 2d133e56d7
1980 changed files with 465595 additions and 0 deletions

View File

@@ -0,0 +1,118 @@
/****************************************************************************
Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "cocos/bindings/jswrapper/SeApi.h"
#include "cocos/bindings/manual/jsb_global.h"
#include "engine/EngineEvents.h"
#include "platform/java/jni/JniHelper.h"
#ifndef JCLS_EDITBOX
#define JCLS_EDITBOX "com/cocos/lib/CocosEditBoxActivity"
#endif
#ifndef ORG_EDITBOX_CLASS_NAME
#define ORG_EDITBOX_CLASS_NAME com_cocos_lib_CocosEditBoxActivity
#endif
#define JNI_EDITBOX(FUNC) JNI_METHOD1(ORG_EDITBOX_CLASS_NAME, FUNC)
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 ccstd::string &type, const ccstd::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);
}
} // namespace
namespace cc {
bool EditBox::_isShown = false; //NOLINT
void EditBox::show(const cc::EditBox::ShowInfo &showInfo) {
JniHelper::callStaticVoidMethod(JCLS_EDITBOX,
"showNative",
showInfo.defaultValue,
showInfo.maxLength,
showInfo.isMultiline,
showInfo.confirmHold,
showInfo.confirmType,
showInfo.inputType);
_isShown = true;
}
void EditBox::hide() {
JniHelper::callStaticVoidMethod(JCLS_EDITBOX, "hideNative");
_isShown = false;
}
bool EditBox::complete() {
if (!_isShown) {
return false;
}
EditBox::hide();
return true;
}
} // namespace cc
extern "C" {
JNIEXPORT void JNICALL JNI_EDITBOX(onKeyboardInputNative)(JNIEnv * /*env*/, jclass /*unused*/, jstring text) {
auto textStr = cc::JniHelper::jstring2string(text);
callJSFunc("input", textStr);
}
JNIEXPORT void JNICALL JNI_EDITBOX(onKeyboardCompleteNative)(JNIEnv * /*env*/, jclass /*unused*/, jstring text) {
auto textStr = cc::JniHelper::jstring2string(text);
callJSFunc("complete", textStr);
}
JNIEXPORT void JNICALL JNI_EDITBOX(onKeyboardConfirmNative)(JNIEnv * /*env*/, jclass /*unused*/, jstring text) {
auto textStr = cc::JniHelper::jstring2string(text);
callJSFunc("confirm", textStr);
}
}

View File

@@ -0,0 +1,641 @@
/****************************************************************************
Copyright (c) 2018-2022 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
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.
****************************************************************************/
/************************************************************
TODO: New implementation of iOS inputbox
UI Structure:
==[| ][done]== >>> inputAccessoryView
= q w e r t y u i o p =
= a s d f g h j k l ; '' =
= virtual [ ] keyboard = >>> inputView
Further needs:
Customization of inputbox, developer DIY toolbar.
==[Camera][| ][done]== >>> inputoopeAccessoryView
= q w e r t y u i o p =
= a s d f g h j k l ; '' =
= virtual [ ] keyboard = >>> inputView
The principle idea is to set inputAccessoryView from JS where developer can bind selector with js callback.
JS API:
jsb.InputBox customizeIBox = new jsb.InputBox();
ibox.addComponent(sendBtn, (ClickEvent: event)=>{...}); automatically set as the last element.
ibox.addComponent(inputFld, (InputEvent: event)=>{...});
ibox.setLayout(sendBtn, inputFld);
JSB binding:
jsb_addComponent(se::Value){
...
inputBox.addComponent(cpt);
}
************************************************************/
#include "EditBox.h"
#include "cocos/bindings/jswrapper/SeApi.h"
#include "cocos/bindings/manual/jsb_global.h"
#include "engine/EngineEvents.h"
#import <UIKit/UIKit.h>
#include "cocos/platform/ios/WarpCocosContent.h"
#define ITEM_MARGIN_WIDTH 10
#define ITEM_MARGIN_HEIGHT 10
#define TEXT_LINE_HEIGHT 40
#define TEXT_VIEW_MAX_LINE_SHOWN 1.5
#define BUTTON_HEIGHT (TEXT_LINE_HEIGHT - ITEM_MARGIN_HEIGHT)
#define BUTTON_WIDTH 60
//TODO: change the proccedure of showing inputbox, possibly become a property
const bool INPUTBOX_HIDDEN = true; // Toggle if Inputbox is visible
/*************************************************************************
Inner class declarations.
************************************************************************/
@interface EditboxManager : NSObject
+ (instancetype)sharedInstance;
- (void) show:(const cc::EditBox::ShowInfo*)showInfo;
- (void) hide;
- (UIView*) getCurrentViewInUse;
- (NSString*) getCurrentText;
@end
@interface ButtonHandler : NSObject
- (IBAction) buttonTapped:(UIButton *)button;
@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> //Multiline
- (BOOL) textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
- (void) textViewDidChange:(UITextView *)textView;
@end
/*************************************************************************
Global variables and functions relative to script engine.
************************************************************************/
namespace {
static bool g_isMultiline{false};
static bool g_confirmHold{false};
static int g_maxLength{INT_MAX};
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 ccstd::string &type, const ccstd::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);
}
/*************************************************************************
Global functions as tools to set values
************************************************************************/
int getTextInputHeight(bool isMultiLine) {
if (isMultiLine)
return TEXT_LINE_HEIGHT * TEXT_VIEW_MAX_LINE_SHOWN;
else
return TEXT_LINE_HEIGHT;
}
void setTextFieldKeyboardType(UITextField *textField, const ccstd::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 ccstd::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 ccstd::string &returnType) {
NSString *titleKey = [NSString stringWithUTF8String:returnType.c_str()];
return NSLocalizedString(titleKey, nil); // get i18n string to be the title
}
//
CGRect getSafeAreaRect() {
//UIView *view = UIApplication.sharedApplication.delegate.window.rootViewController.view;
UIView *view = WarpCocosContent.shareInstance.renderView;
CGRect viewRect = view.frame;
// safeAreaInsets is avaible since iOS 11.
if (@available(iOS 11.0, *)) {
auto safeAreaInsets = view.safeAreaInsets;
UIInterfaceOrientation orient = [UIApplication sharedApplication].statusBarOrientation;
if (UIInterfaceOrientationLandscapeLeft == orient) {
viewRect.origin.x = 0;
viewRect.size.width -= safeAreaInsets.left;
viewRect.size.width -= safeAreaInsets.right;
} else {
viewRect.origin.x += safeAreaInsets.left;
viewRect.size.width -= safeAreaInsets.left;
viewRect.size.width -= safeAreaInsets.right;
}
}
return viewRect;
}
//TODO: Get hash with different type of showInfo, for example different inputAccessoryView
NSString* getTextInputType(const cc::EditBox::ShowInfo* showInfo) {
return showInfo->isMultiline?@"textView":@"textField";
}
void onParentViewTouched(const cc::CustomEvent &touchEvent){
[[EditboxManager sharedInstance] hide];
}
} // namespace
/*************************************************************************
Class implementations.
************************************************************************/
@implementation TextViewDelegate {
UITextView* tViewOnView;
UITextView* tViewOnToolbar;
}
- (id) initWithPairs:(UITextView*) inputOnView and:(UITextView*) inputOnToolbar {
if (self = [super init]) {
tViewOnView = inputOnView;
tViewOnToolbar = inputOnToolbar;
}
return self;
}
- (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) {
NSRange rangeIndex = [textView.text rangeOfComposedCharacterSequenceAtIndex:g_maxLength];
textView.text = [textView.text substringToIndex:rangeIndex.location];
}
tViewOnView.text = textView.text;
tViewOnToolbar.text = textView.text;
callJSFunc("input", [textView.text UTF8String]);
}
@end
@implementation TextFieldDelegate {
UITextField* textFieldOnView;
UITextField* textFieldOntoolbar;
}
- (id) initWithPairs:(UITextField*) inputOnView and:(UITextField*) inputOnToolbar {
if (self = [super init]) {
textFieldOnView = inputOnView;
textFieldOntoolbar = inputOnToolbar;
}
return self;
}
- (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];
textField.text = [textField.text substringToIndex:rangeIndex.location];
}
textFieldOnView.text = textField.text;
textFieldOntoolbar.text = textField.text;
callJSFunc("input", [textField.text UTF8String]);
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
cc::EditBox::complete();
return YES;
}
@end
static ButtonHandler* btnHandler = nil;
@interface InputBoxPair : NSObject
@property(readwrite) id inputOnView;
@property(readwrite) id inputOnToolbar;
@property(readwrite) id inputDelegate;
@end
@implementation InputBoxPair
- (void)dealloc {
[super dealloc];
[_inputOnView release];
[_inputOnToolbar release];
[_inputDelegate release];
}
@end
@implementation EditboxManager
{
//recently there'ill be only 2 elements
NSMutableDictionary<NSString*, InputBoxPair*>* textInputDictionnary;
InputBoxPair* curView;
cc::events::Resize::Listener resizeListener;
cc::events::Touch::Listener touchListener;
cc::events::Close::Listener closeListener;
}
static EditboxManager *instance = nil;
+ (instancetype) sharedInstance {
static dispatch_once_t pred = 0;
dispatch_once(&pred, ^{
instance = [[super allocWithZone:NULL] init];
if (instance == nil) {
CC_LOG_ERROR("Editbox manager init failed, plz check if you have enough space left");
}
});
return instance;
}
+ (id)allocWithZone:(struct _NSZone*)zone {
return [EditboxManager sharedInstance];
}
- (id)copyWithZone:(struct _NSZone*)zone {
return [EditboxManager sharedInstance];
}
- (void)onOrientationChanged{
cc::EditBox::complete();
}
- (id)init {
if (self = [super init]) {
textInputDictionnary = [NSMutableDictionary new];
if (textInputDictionnary == nil) {
[self release];
return nil;
}
resizeListener.bind([&](int /*width*/, int /*height*/ , uint32_t /*windowId*/) {
[[EditboxManager sharedInstance] onOrientationChanged];
});
//"onTouchStart" is a sub event for TouchEvent, so we can only add listener for this sub event rather than TouchEvent itself.
touchListener.bind([&](const cc::TouchEvent& event) {
if(event.type == cc::TouchEvent::Type::BEGAN) {
cc::EditBox::complete();
}
});
closeListener.bind([&]() {
[[EditboxManager sharedInstance] dealloc];
});
}
return self;
}
- (void)dealloc {
[textInputDictionnary release];
[super dealloc];
}
- (UIBarButtonItem*) setInputWidthOf: (UIToolbar*)toolbar{
CGFloat totalItemsWidth = ITEM_MARGIN_WIDTH;
UIBarButtonItem *textViewBarButtonItem;
UIView *view;
for (UIBarButtonItem *barButtonItem in toolbar.items) {
if ((view = [barButtonItem valueForKey:@"view"])) {
if ([view isKindOfClass:[UITextView class]] || [view isKindOfClass:[UITextField class]]) {
textViewBarButtonItem = barButtonItem;
} else if ([view isKindOfClass:[UIButton class]]) {
// Docs say width can be negative for variable size items
totalItemsWidth += BUTTON_WIDTH + ITEM_MARGIN_WIDTH;
}
} else {
totalItemsWidth += barButtonItem.width + ITEM_MARGIN_WIDTH;
}
totalItemsWidth += ITEM_MARGIN_WIDTH;
}
[[textViewBarButtonItem customView]
setFrame:CGRectMake(0,
0,
getSafeAreaRect().size.width - totalItemsWidth,
getTextInputHeight(g_isMultiline))];
return textViewBarButtonItem;
}
- (void) addInputAccessoryViewForTextView: (InputBoxPair*)inputbox
with:(const cc::EditBox::ShowInfo*)showInfo{
CGRect safeView = getSafeAreaRect();
UIToolbar* toolbar = [[UIToolbar alloc]
initWithFrame:CGRectMake(0,
0,
safeView.size.width,
getTextInputHeight(g_isMultiline) + ITEM_MARGIN_HEIGHT)];
[toolbar setBackgroundColor:[UIColor darkGrayColor]];
UITextView* textView = [[UITextView alloc] init];
textView.textColor = [UIColor blackColor];
textView.backgroundColor = [UIColor whiteColor];
textView.layer.cornerRadius = 5.0;
textView.clipsToBounds = YES;
textView.text = [NSString stringWithUTF8String:showInfo->defaultValue.c_str()];
TextViewDelegate* delegate = [[TextViewDelegate alloc] initWithPairs:[inputbox inputOnView] and:textView];
inputbox.inputDelegate = delegate;
textView.delegate = delegate;
UIBarButtonItem *textViewItem = [[UIBarButtonItem alloc] initWithCustomView:textView];
if (!btnHandler){
btnHandler = [[ButtonHandler alloc] init];
}
UIButton *confirmBtn = [[UIButton alloc]
initWithFrame:CGRectMake(0,
0,
BUTTON_WIDTH,
BUTTON_HEIGHT)];
[confirmBtn addTarget:btnHandler
action:@selector(buttonTapped:)
forControlEvents:UIControlEventTouchUpInside];
[confirmBtn setTitle:[NSString stringWithUTF8String:showInfo->confirmType.c_str()]
forState:UIControlStateNormal];
[confirmBtn setTitleColor:[UIColor systemBlueColor]
forState:UIControlStateNormal];
[confirmBtn setTitleColor:[UIColor darkTextColor]
forState:UIControlStateHighlighted]; // Hight light state triggered when the button is tapped.
UIBarButtonItem *confirm = [[UIBarButtonItem alloc]initWithCustomView:confirmBtn];
[toolbar setItems:@[textViewItem, confirm] animated:YES];
UIBarButtonItem* textViewBarButtonItem = [self setInputWidthOf:toolbar];
((UITextView*)[inputbox inputOnView]).inputAccessoryView = toolbar;
[inputbox setInputOnToolbar:textViewBarButtonItem.customView];
//release for NON ARC ENV
[toolbar release];
[textView release];
[confirmBtn release];
[textViewItem release];
[confirm release];
}
- (void) addInputAccessoryViewForTextField: (InputBoxPair*)inputbox
with: (const cc::EditBox::ShowInfo*)showInfo{
CGRect safeView = getSafeAreaRect();
UIToolbar* toolbar = [[UIToolbar alloc]
initWithFrame:CGRectMake(0,
0,
safeView.size.width,
TEXT_LINE_HEIGHT + ITEM_MARGIN_HEIGHT)];
[toolbar setBackgroundColor:[UIColor darkGrayColor]];
UITextField* textField = [[UITextField alloc] init];
textField.borderStyle = UITextBorderStyleRoundedRect;
textField.textColor = [UIColor blackColor];
textField.backgroundColor = [UIColor whiteColor];
textField.text = [NSString stringWithUTF8String:showInfo->defaultValue.c_str()];
TextFieldDelegate* delegate = [[TextFieldDelegate alloc] initWithPairs:[inputbox inputOnView] and:textField];
textField.delegate = delegate;
inputbox.inputDelegate = delegate;
[textField addTarget:delegate action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
UIBarButtonItem *textFieldItem = [[UIBarButtonItem alloc] initWithCustomView:textField];
if (!btnHandler){
btnHandler = [[ButtonHandler alloc] init];
}
UIButton *confirmBtn = [[UIButton alloc]
initWithFrame:CGRectMake(0,
0,
BUTTON_WIDTH,
BUTTON_HEIGHT)];
[confirmBtn addTarget:btnHandler
action:@selector(buttonTapped:)
forControlEvents:UIControlEventTouchUpInside];
[confirmBtn setTitle:[NSString stringWithUTF8String:showInfo->confirmType.c_str()]
forState:UIControlStateNormal];
[confirmBtn setTitleColor:[UIColor systemBlueColor]
forState:UIControlStateNormal];
[confirmBtn setTitleColor:[UIColor darkTextColor]
forState:UIControlStateHighlighted]; // Hight light state triggered when the button is tapped.
UIBarButtonItem *confirm = [[UIBarButtonItem alloc]initWithCustomView:confirmBtn];
[toolbar setItems:@[textFieldItem, confirm] animated:YES];
UIBarButtonItem* textFieldBarButtonItem = [self setInputWidthOf:toolbar];
((UITextField*)[inputbox inputOnView]).inputAccessoryView = toolbar;
[inputbox setInputOnToolbar:textFieldBarButtonItem.customView];
//release for NON ARC ENV
[toolbar release];
[textField release];
[confirmBtn release];
[textFieldItem release];
[confirm release];
}
- (id) createTextView: (const cc::EditBox::ShowInfo *)showInfo {
InputBoxPair* ret;
// Visible view rect size of phone
//CGRect viewRect = UIApplication.sharedApplication.delegate.window.rootViewController.view.frame;
CGRect viewRect = WarpCocosContent.shareInstance.renderView.frame;
//TODO: object for key with real hash value
NSString* inputType = getTextInputType(showInfo);
if ((ret = [textInputDictionnary objectForKey:inputType])) {
[[ret inputOnView] setFrame:CGRectMake(showInfo->x,
viewRect.size.height - showInfo->y - showInfo->height,
showInfo->width,
showInfo->height)];
CGRect safeArea = getSafeAreaRect();
[[[ret inputOnView] inputAccessoryView] setFrame:CGRectMake(0,
0,
safeArea.size.width,
getTextInputHeight(g_isMultiline) + ITEM_MARGIN_HEIGHT)];
[self setInputWidthOf:[[ret inputOnView] inputAccessoryView] ];
} else {
ret = [[InputBoxPair alloc] init];
[ret setInputOnView:[[UITextView alloc]
initWithFrame:CGRectMake(showInfo->x,
viewRect.size.height - showInfo->y - showInfo->height,
showInfo->width,
showInfo->height)]];
[textInputDictionnary setValue:ret forKey:inputType];
[self addInputAccessoryViewForTextView:ret with:showInfo];
}
((UITextView*)[ret inputOnToolbar]).text = [NSString stringWithUTF8String:showInfo->defaultValue.c_str()];
((UITextView*)[ret inputOnView]).text = [NSString stringWithUTF8String:showInfo->defaultValue.c_str()];
return ret;
}
- (id) createTextField: (const cc::EditBox::ShowInfo*)showInfo {
InputBoxPair* ret;
CGRect viewRect = UIApplication.sharedApplication.delegate.window.rootViewController.view.frame;
//TODO: object for key with real hash value
NSString* inputType = getTextInputType(showInfo);
if ((ret = [textInputDictionnary objectForKey:inputType])) {
[[ret inputOnView] setFrame:CGRectMake(showInfo->x,
viewRect.size.height - showInfo->y - showInfo->height,
showInfo->width,
showInfo->height)];
CGRect safeArea = getSafeAreaRect();
[[[ret inputOnView] inputAccessoryView] setFrame:CGRectMake(0,
0,
safeArea.size.width,
getTextInputHeight(g_isMultiline) + ITEM_MARGIN_HEIGHT)];
[self setInputWidthOf:[[ret inputOnView] inputAccessoryView] ];
} else {
ret = [[InputBoxPair alloc] init];
[ret setInputOnView:[[UITextField alloc]
initWithFrame:CGRectMake(showInfo->x,
viewRect.size.height - showInfo->y - showInfo->height,
showInfo->width,
showInfo->height)]];
[textInputDictionnary setValue:ret forKey:inputType];
[self addInputAccessoryViewForTextField:ret with:showInfo];
}
((UITextField*)[ret inputOnToolbar]).text = [NSString stringWithUTF8String:showInfo->defaultValue.c_str()];
((UITextField*)[ret inputOnView]).text = [NSString stringWithUTF8String:showInfo->defaultValue.c_str()];
setTextFieldReturnType((UITextField*)[ret inputOnToolbar], showInfo->confirmType);
setTextFieldReturnType((UITextField*)[ret inputOnView], showInfo->confirmType);
setTextFieldKeyboardType((UITextField*)[ret inputOnToolbar], showInfo->inputType);
setTextFieldKeyboardType((UITextField*)[ret inputOnView], showInfo->inputType);
return ret;
}
//TODO: show inputbox with width modified.
- (void) show: (const cc::EditBox::ShowInfo*)showInfo {
g_maxLength = showInfo->maxLength;
g_isMultiline = showInfo->isMultiline;
g_confirmHold = showInfo->confirmHold;
if (g_isMultiline) {
curView = [self createTextView:showInfo];
} else {
curView = [self createTextField:showInfo];
}
[[curView inputOnView] setHidden:INPUTBOX_HIDDEN];
//UIView *view = UIApplication.sharedApplication.delegate.window.rootViewController.view;
UIView *view = WarpCocosContent.shareInstance.renderView;
[view addSubview:[curView inputOnView]];
[[curView inputOnView] becomeFirstResponder];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if(![[curView inputOnToolbar] becomeFirstResponder]) {
CC_LOG_ERROR("inputOnToolbar becomeFirstResponder error!");
}
});
}
// Change the focus point to the TextField or TextView on the toolbar.
- (void) hide {
if ([[curView inputOnToolbar] isFirstResponder]) {
[[curView inputOnToolbar] resignFirstResponder];
}
if ([[curView inputOnView] isFirstResponder]) {
[[curView inputOnView] resignFirstResponder];
}
[[curView inputOnView] removeFromSuperview];
}
- (InputBoxPair*) getCurrentViewInUse {
return curView;
}
- (NSString*) getCurrentText {
if (g_isMultiline)
return [(UITextView*)[curView inputOnToolbar] text];
return [(UITextField*)[curView inputOnToolbar] text];
}
@end
@implementation ButtonHandler
- (IBAction)buttonTapped:(UIButton *)button {
const ccstd::string text([[[EditboxManager sharedInstance]getCurrentText] UTF8String]);
callJSFunc("confirm", text);
if (!g_confirmHold)
cc::EditBox::complete();
}
@end
/*************************************************************************
Implementation of EditBox.
************************************************************************/
// MARK: EditBox
namespace cc{
bool EditBox::_isShown = false;
void EditBox::show(const cc::EditBox::ShowInfo &showInfo) {
[[EditboxManager sharedInstance] show:&showInfo];
EditBox::_isShown = true;
}
void EditBox::hide() {
[[EditboxManager sharedInstance] hide];
EditBox::_isShown = false;
}
bool EditBox::complete() {
if(!EditBox::_isShown)
return false;
NSString *text = [[EditboxManager sharedInstance] getCurrentText];
callJSFunc("complete", [text UTF8String]);
EditBox::hide();
return true;
}
} // namespace cc

View File

@@ -0,0 +1,40 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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"
namespace cc {
void EditBox::show(const ShowInfo &showInfo) {
return;
}
void EditBox::hide() {
return;
}
bool EditBox::complete() {
return true;
}
} // namespace cc

View File

@@ -0,0 +1,309 @@
/****************************************************************************
Copyright (c) 2018-2022 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
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 "cocos/bindings/jswrapper/SeApi.h"
#include "cocos/bindings/manual/jsb_global.h"
#include "engine/EngineEvents.h"
#include "platform/SDLHelper.h"
#import <AppKit/AppKit.h>
/*************************************************************************
Forward declaration of global functions.
************************************************************************/
namespace {
void callJSFunc(const ccstd::string &type, const ccstd::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;
} // namespace
/*************************************************************************
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;
}
- (void)textDidEndEditing:(NSNotification *)notification {
cc::EditBox::complete();
}
@end
/*************************************************************************
TextFieldDelegate
************************************************************************/
@interface TextFieldDelegate : NSObject <NSTextFieldDelegate>
@end
@implementation TextFieldDelegate
- (void)controlTextDidChange:(NSNotification *)notification {
NSTextField *textField = [notification object];
callJSFunc("input", [textField.stringValue UTF8String]);
}
- (void)controlTextDidEndEditing:(NSNotification *)obj {
cc::EditBox::complete();
}
@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 {
static cc::events::Resize::Listener resizeListener;
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 ccstd::string &type, const ccstd::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 cc::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;
NSWindow *nsWindow = NSApplication.sharedApplication.mainWindow;
[nsWindow.contentView addSubview:g_scrollView];
[nsWindow makeFirstResponder:g_scrollView];
}
void doInitTextField(NSTextField *textField, const CGRect &rect, const cc::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];
NSWindow *nsWindow = NSApplication.sharedApplication.mainWindow;
[nsWindow.contentView addSubview:textField];
[textField becomeFirstResponder];
}
void initTextField(const cc::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 cc::EditBox::ShowInfo &showInfo) {
if (showInfo.isMultiline)
initTextView(showInfo);
else
initTextField(showInfo);
resizeListener.bind([&](int /*width*/, int /*height*/ , uint32_t /*windowId*/) {
cc::EditBox::complete();
});
}
} // namespace
/*************************************************************************
Implementation of EditBox.
************************************************************************/
namespace cc {
bool EditBox::_isShown = false;
void EditBox::show(const ShowInfo &showInfo) {
g_isMultiline = showInfo.isMultiline;
g_maxLength = showInfo.maxLength;
g_isPassword = showInfo.inputType == "password";
init(showInfo);
EditBox::_isShown = 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];
}
EditBox::_isShown = false;
}
bool EditBox::complete() {
if (!_isShown)
return false;
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();
return true;
}
} // namespace cc

View File

@@ -0,0 +1,119 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "cocos/bindings/jswrapper/SeApi.h"
#include "cocos/bindings/manual/jsb_global.h"
#include "engine/EngineEvents.h"
//#include "platform/Application.h"
#include "platform/java/jni/JniHelper.h"
#ifndef JCLS_EDITBOX
#define JCLS_EDITBOX "com/cocos/lib/CocosEditBoxAbility"
#endif
#ifndef ORG_EDITBOX_CLASS_NAME
#define ORG_EDITBOX_CLASS_NAME com_cocos_lib_CocosEditBoxAbility
#endif
#define JNI_EDITBOX(FUNC) JNI_METHOD1(ORG_EDITBOX_CLASS_NAME, FUNC)
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 ccstd::string &type, const ccstd::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);
}
} // namespace
namespace cc {
bool EditBox::_isShown = false; //NOLINT
void EditBox::show(const cc::EditBox::ShowInfo &showInfo) {
JniHelper::callStaticVoidMethod(JCLS_EDITBOX,
"showNative",
showInfo.defaultValue,
showInfo.maxLength,
showInfo.isMultiline,
showInfo.confirmHold,
showInfo.confirmType,
showInfo.inputType);
_isShown = true;
}
void EditBox::hide() {
JniHelper::callStaticVoidMethod(JCLS_EDITBOX, "hideNative");
_isShown = false;
}
bool EditBox::complete() {
if (!_isShown) {
return false;
}
EditBox::hide();
return true;
}
} // namespace cc
extern "C" {
JNIEXPORT void JNICALL JNI_EDITBOX(onKeyboardInputNative)(JNIEnv * /*env*/, jclass /*unused*/, jstring text) {
auto textStr = cc::JniHelper::jstring2string(text);
callJSFunc("input", textStr);
}
JNIEXPORT void JNICALL JNI_EDITBOX(onKeyboardCompleteNative)(JNIEnv * /*env*/, jclass /*unused*/, jstring text) {
auto textStr = cc::JniHelper::jstring2string(text);
callJSFunc("complete", textStr);
}
JNIEXPORT void JNICALL JNI_EDITBOX(onKeyboardConfirmNative)(JNIEnv * /*env*/, jclass /*unused*/, jstring text) {
auto textStr = cc::JniHelper::jstring2string(text);
callJSFunc("confirm", textStr);
}
}

View File

@@ -0,0 +1,107 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
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 "EditBox-openharmony.h"
#include "application/ApplicationManager.h"
#include "platform/openharmony/napi/NapiHelper.h"
#include "bindings/jswrapper/SeApi.h"
namespace cc {
/*************************************************************************
Global variables and functions.
************************************************************************/
namespace {
se::Value g_textInputCallback;
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 ccstd::string &type, const ccstd::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);
}
} // namespace
/*************************************************************************
Implementation of EditBox.
************************************************************************/
void EditBox::show(const EditBox::ShowInfo &showInfo) {
NapiHelper::postMessageToUIThread("showEditBox", Napi::String::New(NapiHelper::getWorkerEnv(), showInfo.defaultValue));
}
void EditBox::hide() {
NapiHelper::postMessageToUIThread("hideEditBox", Napi::String::New(NapiHelper::getWorkerEnv(), ""));
}
bool EditBox::complete() {
callJSFunc("complete", "");
return true;
}
void OpenHarmonyEditBox::napiOnComplete(const Napi::CallbackInfo &info) {
EditBox::complete();
}
void OpenHarmonyEditBox::napiOnTextChange(const Napi::CallbackInfo &info) {
auto env = info.Env();
if (info.Length() != 1) {
Napi::Error::New(env, "napiOnTextChange, 1 argument expected").ThrowAsJavaScriptException();
return;
}
if (!info[0].IsString()) {
Napi::TypeError::New(env, "napiOnTextChange, string argument expected").ThrowAsJavaScriptException();
return;
}
ccstd::string buffer = info[0].As<Napi::String>().Utf8Value();
callJSFunc("input", buffer);
}
} // namespace cc

View File

@@ -0,0 +1,47 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
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 "cocos/application/ApplicationManager.h"
#include "platform/openharmony/napi/NapiHelper.h"
namespace cc {
class OpenHarmonyEditBox : public EditBox {
public:
static void napiOnComplete(const Napi::CallbackInfo &info);
static void napiOnTextChange(const Napi::CallbackInfo &info);
static napi_value show(const std::string& inputMessage);
static napi_value hide();
private:
static napi_ref showEditBoxFunction;
static napi_ref hideEditBoxFunction;
};
} // namespace cc

View File

@@ -0,0 +1,283 @@
/****************************************************************************
Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "cocos/application/ApplicationManager.h"
#include "cocos/bindings/jswrapper/SeApi.h"
#include "cocos/bindings/manual/jsb_global.h"
#include "cocos/platform/interfaces/modules/ISystemWindow.h"
#include "cocos/platform/interfaces/modules/ISystemWindowManager.h"
#include <stdlib.h>
#include <windows.h>
#include <codecvt>
#include <locale>
#include <memory>
#include "Richedit.h"
namespace cc {
/*************************************************************************
Global variables and functions.
************************************************************************/
namespace {
bool g_isMultiline = false;
HWND g_hwndEditBox = nullptr;
WNDPROC g_prevMainWindowProc = nullptr;
WNDPROC g_prevEditWindowProc = nullptr;
se::Value g_textInputCallback;
HWND getCurrentWindowHwnd() {
if (!CC_CURRENT_APPLICATION()) {
return nullptr;
}
ISystemWindow *systemWindowIntf = CC_GET_MAIN_SYSTEM_WINDOW();
if (!systemWindowIntf) {
return nullptr;
}
return reinterpret_cast<HWND>(systemWindowIntf->getWindowHandle());
}
int getCocosWindowHeight() {
// HWND parent = cc_get_application_view()->getWindowHandle();
HWND parent = getCurrentWindowHwnd();
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 ccstd::string &type, const ccstd::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);
}
ccstd::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;
ccstd::string ret(convert.to_bytes(str));
free(str);
return ret;
}
std::wstring str2ws(const ccstd::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(getCurrentWindowHwnd());
break;
case WM_COMMAND:
// EN_CHANGE => EN_UPDATE
if (EN_UPDATE == HIWORD(wParam)) {
callJSFunc("input", getText(g_hwndEditBox).c_str());
}
break;
default:
break;
}
return CallWindowProc(g_prevMainWindowProc, hwnd, msg, wParam, lParam);
}
LRESULT editWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_KEYUP:
if (wParam == VK_RETURN && !g_isMultiline) {
EditBox::complete();
EditBox::hide();
SetFocus(getCurrentWindowHwnd());
}
break;
default:
break;
}
return CallWindowProc(g_prevEditWindowProc, hwnd, msg, wParam, lParam);
}
} // namespace
/*************************************************************************
Implementation of EditBox.
************************************************************************/
void EditBox::show(const EditBox::ShowInfo &showInfo) {
int windowHeight = getCocosWindowHeight();
if (!g_hwndEditBox) {
HWND parent = getCurrentWindowHwnd();
UINT32 flags = WS_CHILD | showInfo.textAlignment | WS_TABSTOP | ES_AUTOHSCROLL;
g_isMultiline = showInfo.isMultiline;
if (g_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);*/
LoadLibrary(TEXT("Msftedit.dll"));
g_hwndEditBox = CreateWindowEx(
0,
MSFTEDIT_CLASS,
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;
CC_LOG_DEBUG("Can not create editbox: %s", convert.to_bytes(buffer).c_str());
return;
}
g_prevMainWindowProc = (WNDPROC)SetWindowLongPtr(parent, GWLP_WNDPROC, (LONG_PTR)mainWindowProc);
g_prevEditWindowProc = (WNDPROC)SetWindowLongPtr(g_hwndEditBox, GWLP_WNDPROC, (LONG_PTR)editWindowProc);
}
::SendMessageW(g_hwndEditBox, EM_LIMITTEXT, showInfo.maxLength, 0);
// SendMessage(g_hwndEditBox, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
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());
// SendMessage(g_hwndEditBox, EM_SETFONTSIZE, 1, 0);
SendMessage(g_hwndEditBox, EM_SETBKGNDCOLOR, 0, RGB((showInfo.backgroundColor & 0x000000ff), (showInfo.backgroundColor & 0x0000ff00) >> 8, (showInfo.backgroundColor & 0x00ff0000) >> 16));
::PostMessage(g_hwndEditBox, WM_ACTIVATE, 0, 0);
::ShowWindow(g_hwndEditBox, SW_SHOW);
/* Get current length of text in the box */
int index = GetWindowTextLength(g_hwndEditBox);
SetFocus(g_hwndEditBox);
SendMessage(g_hwndEditBox, EM_SETSEL, (WPARAM)0, (LPARAM)index);
// int height = CC_GET_MAIN_SYSTEM_WINDOW()->kheight;
CHARFORMAT2 cf;
RECT rect;
GetWindowRect(getCurrentWindowHwnd(), &rect);
float WindowRatio = (float)(rect.bottom - rect.top) / (float)CC_GET_MAIN_SYSTEM_WINDOW()->getViewSize().height;
float JsFontRatio = float(showInfo.fontSize) / 5;
/** A probale way to calculate the increase of font size
* OriginalSize + Increase = OriginalSize * Ratio_of_js_fontSize * Ratio_of_window
* Default value : OriginalSize = 8, Ratio_of_js_fontSize = showInfo.fontSize /5,
* Ratio_of_window = (float)height / (float)CC_GET_MAIN_SYSTEM_WINDOW()->getViewSize().height
* thus Increase was calculated.
*/
int fsize = (float)(JsFontRatio + 8) * WindowRatio - 8;
SendMessage(g_hwndEditBox, EM_SETFONTSIZE, fsize, 0);
/* Set the caret to the end of the text in the box */
SendMessage(g_hwndEditBox, EM_SETSEL, (WPARAM)index, (LPARAM)index);
cf.cbSize = sizeof(CHARFORMAT2);
cf.crTextColor = RGB((showInfo.fontColor & 0x000000ff), (showInfo.fontColor & 0x0000ff00) >> 8, (showInfo.fontColor & 0x00ff0000) >> 16);
cf.crBackColor = RGB((showInfo.backColor & 0x000000ff), (showInfo.backColor & 0x0000ff00) >> 8, (showInfo.backColor & 0x00ff0000) >> 16);
cf.dwMask = CFM_COLOR | CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE;
cf.dwEffects = (showInfo.isUnderline ? CFE_UNDERLINE : 0) | (showInfo.isBold ? CFE_BOLD : 0) | (showInfo.isItalic ? CFE_ITALIC : 0);
cf.bUnderlineColor = showInfo.underlineColor;
SendMessage(g_hwndEditBox, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
}
void EditBox::hide() {
DestroyWindow(g_hwndEditBox);
SetWindowLongPtr(getCurrentWindowHwnd(), GWLP_WNDPROC, (LONG_PTR)g_prevMainWindowProc);
SetWindowLongPtr(g_hwndEditBox, GWLP_WNDPROC, (LONG_PTR)g_prevEditWindowProc);
g_hwndEditBox = nullptr;
}
bool EditBox::complete() {
callJSFunc("complete", getText(g_hwndEditBox).c_str());
return true;
}
} // namespace cc

View File

@@ -0,0 +1,73 @@
/****************************************************************************
Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "base/Macros.h"
#include "base/std/container/string.h"
namespace cc {
class EditBox {
public:
enum TextAlignment {
LEFT,
CENTER,
RIGHT
};
struct ShowInfo {
ccstd::string defaultValue;
ccstd::string confirmType;
ccstd::string inputType;
int maxLength = 0;
int x = 0;
int y = 0;
int width = 0;
int height = 0;
bool confirmHold = false;
bool isMultiline = false;
//NEW PROPERTIES
uint32_t fontSize = 20;
uint32_t fontColor = 0x00000000;
uint32_t backColor = 0x00000000; //font back color
uint32_t backgroundColor = 0x00000000;
bool isBold = false;
bool isItalic = false;
bool isUnderline = false;
uint32_t underlineColor = 0x00000000;
uint32_t textAlignment = LEFT; //By default, override with left, center or right
};
static void show(const ShowInfo &showInfo);
static void hide();
// It is internally to send a complete message to JS.
// Don't call it by yourself untile you know the effect.
static bool complete();
private:
static bool _isShown; //NOLINT(readability-identifier-naming)
};
} // namespace cc