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

View File

@@ -0,0 +1,474 @@
/****************************************************************************
Copyright (c) 2014-2016 Chukong Technologies Inc.
Copyright (c) 2016-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 "VideoPlayer.h"
using namespace cc;
#if CC_PLATFORM == CC_PLATFORM_IOS
//-------------------------------------------------------------------------------------
#import <AVKit/AVPlayerViewController.h>
#import <CoreMedia/CMTime.h>
#include "platform/FileUtils.h"
#include "platform/ios/WarpCocosContent.h"
@interface UIVideoViewWrapperIos : NSObject
typedef NS_ENUM(NSInteger, PlayerbackState) {
PlayerbackStateUnknown = 0,
PlayerbackStatePaused,
PlayerbackStopped,
PlayerbackStatePlaying,
PlayerbackStateCompleted
};
@property (assign, nonatomic) AVPlayerViewController *playerController;
- (void)setFrame:(int)left
:(int)top
:(int)width
:(int)height;
- (void)setURL:(int)videoSource :(ccstd::string &)videoUrl;
- (void)play;
- (void)pause;
- (void)resume;
- (void)stop;
- (BOOL)isPlaying;
- (void)seekTo:(float)sec;
- (float)currentTime;
- (float)duration;
- (void)setVisible:(BOOL)visible;
- (void)setKeepRatioEnabled:(BOOL)enabled;
- (void)setPlaybackRate:(float)value;
- (void)setMute:(BOOL)enabled;
- (void)setLoop:(BOOL)enabled;
- (void)setFullScreenEnabled:(BOOL)enabled;
- (void)showPlaybackControls:(BOOL)value;
- (BOOL)isFullScreenEnabled;
- (void)cleanup;
- (id)init:(void *)videoPlayer;
- (void)videoFinished:(NSNotification *)notification;
@end
@implementation UIVideoViewWrapperIos {
int _left;
int _top;
int _width;
int _height;
bool _keepRatioEnabled;
bool _keepLoopEnabled;
bool _fullscreen;
CGRect _restoreRect;
PlayerbackState _state;
VideoPlayer *_videoPlayer;
}
- (id)init:(void *)videoPlayer {
if (self = [super init]) {
_keepRatioEnabled = FALSE;
_keepLoopEnabled = FALSE;
_left = _top = _width = _height = 0;
[self initPlayerController];
_videoPlayer = (VideoPlayer *)videoPlayer;
}
return self;
}
- (void)initPlayerController {
self.playerController = [AVPlayerViewController new];
[self setFrame:_left:_top:_width:_height];
[self showPlaybackControls:TRUE];
[self setKeepRatioEnabled:_keepRatioEnabled];
_state = PlayerbackStateUnknown;
}
- (void)dealloc {
[self cleanup];
[super dealloc];
}
- (void)setFrame:(int)left
:(int)top
:(int)width
:(int)height {
if (_left == left && _width == width && _top == top && _height == height)
return;
_left = left;
_width = width;
_top = top;
_height = height;
[self.playerController.view setFrame:CGRectMake(left, top, width, height)];
}
- (void)setFullScreenEnabled:(BOOL)enabled {
// AVPlayerViewController doesn't provide API to enable fullscreen. But you can toggle
// fullsreen by the playback controllers.
}
- (BOOL)isFullScreenEnabled {
return false;
}
- (BOOL)isPlaying {
return (self.playerController.player && self.playerController.player.rate != 0);
}
- (void)setURL:(int)videoSource :(ccstd::string &)videoUrl {
[self cleanup];
[self initPlayerController];
if (videoSource == 1)
self.playerController.player = [[[AVPlayer alloc] initWithURL:[NSURL URLWithString:@(videoUrl.c_str())]] autorelease];
else
self.playerController.player = [[[AVPlayer alloc] initWithURL:[NSURL fileURLWithPath:@(videoUrl.c_str())]] autorelease];
[self registerPlayerEventListener];
}
- (void)seekTo:(float)sec {
if (self.playerController.player)
[self.playerController.player seekToTime:CMTimeMake(sec * 600, 600) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
}
- (float)currentTime {
if (self.playerController.player)
return CMTimeGetSeconds([self.playerController.player currentTime]);
return -1;
}
- (float)duration {
if (self.playerController.player)
return CMTimeGetSeconds(self.playerController.player.currentItem.asset.duration);
return -1;
;
}
- (void)setVisible:(BOOL)visible {
[self.playerController.view setHidden:!visible];
if (!visible)
[self pause];
}
- (void)setKeepRatioEnabled:(BOOL)enabled {
_keepRatioEnabled = enabled;
if (_keepRatioEnabled)
self.playerController.videoGravity = AVLayerVideoGravityResizeAspectFill;
else
self.playerController.videoGravity = AVLayerVideoGravityResize;
}
- (void)setMute:(BOOL)enabled {
[self.playerController.player setMuted:enabled];
}
- (void)setLoop:(BOOL)enabled {
_keepLoopEnabled = enabled;
if (_keepLoopEnabled) {
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(runLoopTheVideo:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.playerController.player.currentItem];
}
}
- (void)setPlaybackRate:(float)value {
if (self.playerController.player) {
[self.playerController.player playImmediatelyAtRate:value];
}
}
- (void)play {
if (self.playerController.player && ![self isPlaying]) {
[self.playerController.player play];
_state = PlayerbackStatePlaying;
_videoPlayer->onPlayEvent((int)VideoPlayer::EventType::PLAYING);
}
}
- (void)runLoopTheVideo:(NSNotification *)notification {
if (_keepLoopEnabled) {
AVPlayerItem *playerItem = notification.object;
[self seekTo:0];
[self.playerController.player play];
}
}
- (void)pause {
if ([self isPlaying]) {
[self.playerController.player pause];
_state = PlayerbackStatePaused;
_videoPlayer->onPlayEvent((int)VideoPlayer::EventType::PAUSED);
}
}
- (void)resume {
if (self.playerController.player && _state == PlayerbackStatePaused)
[self play];
}
- (void)stop {
// AVPlayer doesn't have `stop` method, so just pause it, and seek time to 0.
if (self.playerController.player && _state != PlayerbackStopped) {
[self seekTo:0];
[self.playerController.player pause];
_state = PlayerbackStopped;
_videoPlayer->onPlayEvent((int)VideoPlayer::EventType::STOPPED);
}
}
// Private functions
- (void)cleanup {
[self stop];
[self removePlayerEventListener];
[self.playerController.view removeFromSuperview];
[self.playerController release];
}
- (void)removePlayerEventListener {
if (self.playerController.player) {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:AVPlayerItemDidPlayToEndTimeNotification
object:self.playerController.player.currentItem];
[self.playerController.player removeObserver:self forKeyPath:@"status"];
}
}
- (void)registerPlayerEventListener {
if (self.playerController.player) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(videoFinished:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:self.playerController.player.currentItem];
[self.playerController.player addObserver:self forKeyPath:@"status" options:0 context:nil];
}
}
- (void)showPlaybackControls:(BOOL)value {
self.playerController.showsPlaybackControls = value;
}
- (void)videoFinished:(NSNotification *)notification {
if (_videoPlayer != nullptr) {
_videoPlayer->onPlayEvent((int)VideoPlayer::EventType::COMPLETED);
_state = PlayerbackStateCompleted;
// Seek to 0 to make it playable again.
[self seekTo:0];
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
auto player = self.playerController.player;
if (object == player && [keyPath isEqualToString:@"status"]) {
if (player.status == AVPlayerStatusReadyToPlay) {
[self addPlayerControllerSubView];
_videoPlayer->onPlayEvent((int)VideoPlayer::EventType::META_LOADED);
if (![self isPlaying]) { //User may start playing after getting the META_LOADED message
_videoPlayer->onPlayEvent((int)VideoPlayer::EventType::READY_TO_PLAY);
}
} else if (player.status == AVPlayerStatusFailed) {
// something went wrong. player.error should contain some information
NSLog(@"Failed to load video");
}
}
}
- (void)addPlayerControllerSubView {
//auto rootView = UIApplication.sharedApplication.delegate.window.rootViewController.view;
auto rootView = WarpCocosContent.shareInstance.renderView;
[rootView addSubview:self.playerController.view];
}
@end
//------------------------------------------------------------------------------------------------------------
VideoPlayer::VideoPlayer()
: _videoPlayerIndex(-1),
_fullScreenEnabled(false),
_fullScreenDirty(false),
_keepAspectRatioEnabled(false) {
_videoView = [[UIVideoViewWrapperIos alloc] init:this];
#if CC_VIDEOPLAYER_DEBUG_DRAW
_debugDrawNode = DrawNode::create();
addChild(_debugDrawNode);
#endif
}
VideoPlayer::~VideoPlayer() {
destroy();
}
void VideoPlayer::destroy() {
if (_videoView != nil) {
[((UIVideoViewWrapperIos *)_videoView) release];
_videoView = nil;
}
}
void VideoPlayer::setURL(const ccstd::string &videoUrl) {
if (videoUrl.find("://") == ccstd::string::npos) {
_videoURL = FileUtils::getInstance()->fullPathForFilename(videoUrl);
_videoSource = VideoPlayer::Source::FILENAME;
} else {
_videoURL = videoUrl;
_videoSource = VideoPlayer::Source::URL;
}
[((UIVideoViewWrapperIos *)_videoView) setURL:(int) _videoSource:_videoURL];
}
void VideoPlayer::setFullScreenEnabled(bool enabled) {
[((UIVideoViewWrapperIos *)_videoView) setFullScreenEnabled:enabled];
}
void VideoPlayer::setKeepAspectRatioEnabled(bool enable) {
if (_keepAspectRatioEnabled != enable) {
_keepAspectRatioEnabled = enable;
[((UIVideoViewWrapperIos *)_videoView) setKeepRatioEnabled:enable];
}
}
void VideoPlayer::setMute(bool enable) {
if (!_videoURL.empty()) {
if (enable) {
[((UIVideoViewWrapperIos *)_videoView) setMute:YES];
} else {
[((UIVideoViewWrapperIos *)_videoView) setMute:NO];
}
}
}
void VideoPlayer::setLoop(bool enable) {
if (!_videoURL.empty()) {
if (enable) {
[((UIVideoViewWrapperIos *)_videoView) setLoop:YES];
}
else {
[((UIVideoViewWrapperIos *)_videoView) setLoop:NO];
}
}
}
void VideoPlayer::setPlaybackRate(float value) {
if (!_videoURL.empty()) {
[((UIVideoViewWrapperIos *)_videoView) setPlaybackRate:value];
}
}
void VideoPlayer::play() {
if (!_videoURL.empty() && _isVisible) {
[((UIVideoViewWrapperIos *)_videoView) play];
}
}
void VideoPlayer::pause() {
if (!_videoURL.empty()) {
[((UIVideoViewWrapperIos *)_videoView) pause];
}
}
void VideoPlayer::stop() {
if (!_videoURL.empty()) {
[((UIVideoViewWrapperIos *)_videoView) stop];
}
}
void VideoPlayer::seekTo(float sec) {
if (!_videoURL.empty()) {
[((UIVideoViewWrapperIos *)_videoView) seekTo:sec];
}
}
float VideoPlayer::currentTime() const {
return [((UIVideoViewWrapperIos *)_videoView) currentTime];
}
float VideoPlayer::duration() const {
return [((UIVideoViewWrapperIos *)_videoView) duration];
}
void VideoPlayer::setVisible(bool visible) {
_isVisible = visible;
if (!visible) {
[((UIVideoViewWrapperIos *)_videoView) setVisible:NO];
} else {
[((UIVideoViewWrapperIos *)_videoView) setVisible:YES];
}
}
void VideoPlayer::addEventListener(const ccstd::string &name, const VideoPlayer::ccVideoPlayerCallback &callback) {
_eventCallback[name] = callback;
}
void VideoPlayer::onPlayEvent(int event) {
switch ((EventType)event) {
case EventType::PLAYING:
_eventCallback["play"]();
break;
case EventType::PAUSED:
_eventCallback["pause"]();
break;
case EventType::STOPPED:
_eventCallback["stoped"]();
break;
case EventType::COMPLETED:
_eventCallback["ended"]();
break;
case EventType::META_LOADED:
_eventCallback["loadedmetadata"]();
break;
case EventType::CLICKED:
_eventCallback["click"]();
break;
case EventType::READY_TO_PLAY:
_eventCallback["suspend"]();
break;
default:
break;
}
}
void VideoPlayer::setFrame(float x, float y, float width, float height) {
auto scaleFactor = [[UIScreen mainScreen] nativeScale];
[((UIVideoViewWrapperIos *)_videoView) setFrame:x / scaleFactor:y / scaleFactor:width / scaleFactor:height / scaleFactor];
}
#endif

View File

@@ -0,0 +1,227 @@
/****************************************************************************
Copyright (c) 2014-2017 Chukong Technologies Inc.
Copyright (c) 2017-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 "VideoPlayer.h"
#if (CC_PLATFORM == CC_PLATFORM_ANDROID || CC_PLATFORM == CC_PLATFORM_OHOS)
#include <jni.h>
#include <cstdlib>
#include "base/std/container/string.h"
#include "base/std/container/unordered_map.h"
#include "platform/FileUtils.h"
#include "platform/java/jni/JniHelper.h"
//-----------------------------------------------------------------------------------------------------------
static const ccstd::string VIDEO_HELPER_CLASS_NAME = "com/cocos/lib/CocosVideoHelper";
using namespace cc; //NOLINT
static void executeVideoCallback(int index, int event);
#define QUIT_FULLSCREEN 1000
extern "C" {
JNIEXPORT
//NOLINTNEXTLINE
void Java_com_cocos_lib_CocosVideoHelper_nativeExecuteVideoCallback(JNIEnv *env, jobject obj, jint index, jint event) {
executeVideoCallback(index, event);
}
}
int createVideoWidgetJNI() {
JniMethodInfo t;
int ret = -1;
if (JniHelper::getStaticMethodInfo(t, VIDEO_HELPER_CLASS_NAME.c_str(), "createVideoWidget", "()I")) {
ret = t.env->CallStaticIntMethod(t.classID, t.methodID);
ccDeleteLocalRef(t.env, t.classID);
}
return ret;
}
//-----------------------------------------------------------------------------------------------------------
static ccstd::unordered_map<int, VideoPlayer *> sAllVideoPlayers;
VideoPlayer::VideoPlayer()
: _videoPlayerIndex(-1),
_fullScreenEnabled(false),
_fullScreenDirty(false),
_keepAspectRatioEnabled(false) {
_videoPlayerIndex = createVideoWidgetJNI();
sAllVideoPlayers[_videoPlayerIndex] = this;
#if CC_VIDEOPLAYER_DEBUG_DRAW
_debugDrawNode = DrawNode::create();
addChild(_debugDrawNode);
#endif
}
VideoPlayer::~VideoPlayer() {
destroy();
}
void VideoPlayer::destroy() {
if (_videoPlayerIndex != -1) {
auto iter = sAllVideoPlayers.find(_videoPlayerIndex);
if (iter != sAllVideoPlayers.end()) {
sAllVideoPlayers.erase(iter);
}
JniHelper::callStaticVoidMethod(VIDEO_HELPER_CLASS_NAME, "removeVideoWidget",
_videoPlayerIndex);
_videoPlayerIndex = -1;
}
}
void VideoPlayer::setURL(const ccstd::string &videoUrl) {
if (videoUrl.find("://") == ccstd::string::npos) {
_videoURL = FileUtils::getInstance()->fullPathForFilename(videoUrl);
_videoSource = VideoPlayer::Source::FILENAME;
} else {
_videoURL = videoUrl;
_videoSource = VideoPlayer::Source::URL;
}
JniHelper::callStaticVoidMethod(VIDEO_HELPER_CLASS_NAME, "setVideoUrl", _videoPlayerIndex,
static_cast<int>(_videoSource), _videoURL);
}
void VideoPlayer::setFrame(float x, float y, float width, float height) {
JniHelper::callStaticVoidMethod(VIDEO_HELPER_CLASS_NAME, "setVideoRect", _videoPlayerIndex,
static_cast<int>(x), static_cast<int>(y), static_cast<int>(width), static_cast<int>(height));
}
void VideoPlayer::setPlaybackRate(float value) {
JniHelper::callStaticVoidMethod(VIDEO_HELPER_CLASS_NAME, "setPlaybackRate", _videoPlayerIndex, value);
}
void VideoPlayer::setMute(bool enable) {
JniHelper::callStaticVoidMethod(VIDEO_HELPER_CLASS_NAME, "setMute", _videoPlayerIndex, enable);
}
void VideoPlayer::setLoop(bool enable) {
JniHelper::callStaticVoidMethod(VIDEO_HELPER_CLASS_NAME, "setLoop", _videoPlayerIndex, enable);
}
void VideoPlayer::setFullScreenEnabled(bool fullscreen) {
if (_fullScreenEnabled != fullscreen) {
_fullScreenEnabled = fullscreen;
JniHelper::callStaticVoidMethod(VIDEO_HELPER_CLASS_NAME, "setFullScreenEnabled", _videoPlayerIndex, fullscreen);
}
}
void VideoPlayer::setKeepAspectRatioEnabled(bool enable) {
if (_keepAspectRatioEnabled != enable) {
_keepAspectRatioEnabled = enable;
JniHelper::callStaticVoidMethod(VIDEO_HELPER_CLASS_NAME, "setVideoKeepRatioEnabled", _videoPlayerIndex, enable);
}
}
void VideoPlayer::play() {
if (!_videoURL.empty()) {
JniHelper::callStaticVoidMethod(VIDEO_HELPER_CLASS_NAME, "startVideo", _videoPlayerIndex);
}
}
void VideoPlayer::pause() {
if (!_videoURL.empty()) {
JniHelper::callStaticVoidMethod(VIDEO_HELPER_CLASS_NAME, "pauseVideo", _videoPlayerIndex);
}
}
void VideoPlayer::stop() {
if (!_videoURL.empty()) {
JniHelper::callStaticVoidMethod(VIDEO_HELPER_CLASS_NAME, "stopVideo", _videoPlayerIndex);
}
}
void VideoPlayer::seekTo(float sec) {
if (!_videoURL.empty()) {
JniHelper::callStaticVoidMethod(VIDEO_HELPER_CLASS_NAME, "seekVideoTo", _videoPlayerIndex, int(sec * 1000));
}
}
void VideoPlayer::setVisible(bool visible) {
JniHelper::callStaticVoidMethod(VIDEO_HELPER_CLASS_NAME, "setVideoVisible", _videoPlayerIndex, visible);
}
void VideoPlayer::addEventListener(const ccstd::string &name, const VideoPlayer::ccVideoPlayerCallback &callback) {
_eventCallback[name] = callback;
}
void VideoPlayer::onPlayEvent(int event) {
if (event == QUIT_FULLSCREEN) {
_fullScreenEnabled = false;
} else {
auto videoEvent = static_cast<VideoPlayer::EventType>(event);
switch (videoEvent) {
case EventType::PLAYING:
_eventCallback["play"]();
break;
case EventType::PAUSED:
_eventCallback["pause"]();
break;
case EventType::STOPPED:
_eventCallback["stoped"]();
break;
case EventType::COMPLETED:
_eventCallback["ended"]();
break;
case EventType::META_LOADED:
_eventCallback["loadedmetadata"]();
break;
case EventType::CLICKED:
_eventCallback["click"]();
break;
case EventType::READY_TO_PLAY:
_eventCallback["suspend"]();
break;
default:
break;
}
}
}
void executeVideoCallback(int index, int event) {
auto it = sAllVideoPlayers.find(index);
if (it != sAllVideoPlayers.end()) {
sAllVideoPlayers[index]->onPlayEvent(event);
}
}
float VideoPlayer::currentTime() const {
return JniHelper::callStaticFloatMethod(VIDEO_HELPER_CLASS_NAME, "getCurrentTime", _videoPlayerIndex);
}
float VideoPlayer::duration() const {
return JniHelper::callStaticFloatMethod(VIDEO_HELPER_CLASS_NAME, "getDuration", _videoPlayerIndex);
}
#endif

View File

@@ -0,0 +1,194 @@
/****************************************************************************
Copyright (c) 2014-2016 Chukong Technologies Inc.
Copyright (c) 2016-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 <functional>
#include "base/Macros.h"
#include "base/RefCounted.h"
#include "base/std/container/string.h"
#include "base/std/container/unordered_map.h"
#ifndef OBJC_CLASS
#ifdef __OBJC__
#define OBJC_CLASS(name) @class name
#else
#define OBJC_CLASS(name) class name
#endif
#endif // OBJC_CLASS
namespace cc {
/**
* @class VideoPlayer
* @brief Displays a video file.
*
* @note VideoPlayer displays a video file base on system widget.
* It's mean VideoPlayer displays a video file above all graphical elements of cocos2d-x.
* @js NA
*/
class VideoPlayer final {
public:
/**
* Videoplayer play event type.
*/
enum class EventType {
PLAYING = 0,
PAUSED,
STOPPED,
COMPLETED,
META_LOADED,
CLICKED,
READY_TO_PLAY,
UPDATE,
};
VideoPlayer();
~VideoPlayer();
/**
* Destroy VideoPlayer, remove it from parent
*/
void destroy();
/**
* A callback which will be called after specific VideoPlayer event happens.
*/
using ccVideoPlayerCallback = std::function<void()>;
/**
* Sets a URL as a video source for VideoPlayer.
*/
virtual void setURL(const ccstd::string &videoURL);
/**
* Starts playback.
*/
virtual void play();
/**
* Pauses playback.
*/
virtual void pause();
/**
* Stops playback.
*/
virtual void stop();
/**
* Seeks to specified time position.
*
* @param sec The offset in seconds from the start to seek to.
*/
virtual void seekTo(float sec);
/**
* Get the current play time, measure in seconds.
*/
float currentTime() const;
float duration() const;
/**
* Causes the video player to keep aspect ratio or no when displaying the video.
*
* @param enable Specify true to keep aspect ratio or false to scale the video until
* both dimensions fit the visible bounds of the view exactly.
*/
virtual void setKeepAspectRatioEnabled(bool enable);
/**
* Indicates whether the video player keep aspect ratio when displaying the video.
*/
virtual bool isKeepAspectRatioEnabled() const { return _keepAspectRatioEnabled; }
/**
* Causes the video player to enter or exit full-screen mode.
*
* @param fullscreen Specify true to enter full-screen mode or false to exit full-screen mode.
*/
virtual void setFullScreenEnabled(bool fullscreen);
/**
* Register a callback to be invoked when the video state is updated.
*
* @param callback The callback that will be run.
*/
virtual void addEventListener(const ccstd::string &name, const VideoPlayer::ccVideoPlayerCallback &callback);
/**
* @brief A function which will be called when video is playing.
*
* @param event @see VideoPlayer::EventType.
*/
virtual void onPlayEvent(int event);
/**
* Toggle visibility of VideoPlayer.
*/
virtual void setVisible(bool visible);
/**
* Set the rect of VideoPlayer.
*/
virtual void setFrame(float x, float y, float width, float height);
/**
* Set playback rate of VideoPlayer.
*/
virtual void setPlaybackRate(float value);
/**
* Set mute of VideoPlayer.
*/
virtual void setMute(bool enable);
/**
* Set loop of VideoPlayer.
*/
virtual void setLoop(bool enable);
protected:
enum class Source {
FILENAME = 0,
URL
};
bool _isVisible;
bool _fullScreenDirty;
bool _fullScreenEnabled;
bool _keepAspectRatioEnabled;
ccstd::string _videoURL;
Source _videoSource;
int _videoPlayerIndex;
ccstd::unordered_map<ccstd::string, ccVideoPlayerCallback> _eventCallback;
void *_videoView;
};
} // namespace cc

View File

@@ -0,0 +1,208 @@
/****************************************************************************
Copyright (c) 2014-2016 Chukong Technologies Inc.
Copyright (c) 2017-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 "WebView.h"
#include "base/memory/Memory.h"
#include "platform/FileUtils.h"
#if CC_PLATFORM == CC_PLATFORM_IOS
#include "WebViewImpl-ios.h"
#elif CC_PLATFORM == CC_PLATFORM_ANDROID || CC_PLATFORM == CC_PLATFORM_OHOS || CC_PLATFORM == CC_PLATFORM_OPENHARMONY
#include "WebViewImpl-java.h"
#else
static_assert(false, "WebView only supported on iOS & Android");
#endif
namespace cc {
WebView::WebView()
: _impl(ccnew WebViewImpl(this)),
_onJSCallback(nullptr),
_onShouldStartLoading(nullptr),
_onDidFinishLoading(nullptr),
_onDidFailLoading(nullptr) {
}
WebView::~WebView() {
CC_SAFE_DELETE(_impl);
}
WebView *WebView::create() {
auto webView = ccnew WebView();
if (webView) {
return webView;
}
return nullptr;
}
void WebView::destroy() {
CC_SAFE_DESTROY(_impl);
}
void WebView::setJavascriptInterfaceScheme(const ccstd::string &scheme) {
if (_impl != nullptr) {
_impl->setJavascriptInterfaceScheme(scheme);
}
}
void WebView::loadData(const cc::Data &data,
const ccstd::string &MIMEType,
const ccstd::string &encoding,
const ccstd::string &baseURL) {
if (_impl != nullptr) {
_impl->loadData(data, MIMEType, encoding, baseURL);
}
}
void WebView::loadHTMLString(const ccstd::string &string, const ccstd::string &baseURL) {
if (_impl != nullptr) {
_impl->loadHTMLString(string, baseURL);
}
}
void WebView::loadURL(const ccstd::string &url) {
if (_impl != nullptr) {
_impl->loadURL(url);
}
}
void WebView::loadFile(const ccstd::string &fileName) {
if (_impl != nullptr) {
_impl->loadFile(fileName);
}
}
void WebView::stopLoading() {
if (_impl != nullptr) {
_impl->stopLoading();
}
}
void WebView::reload() {
if (_impl != nullptr) {
_impl->reload();
}
}
bool WebView::canGoBack() {
if (_impl != nullptr) {
return _impl->canGoBack();
}
return false;
}
bool WebView::canGoForward() {
if (_impl != nullptr) {
return _impl->canGoForward();
}
return false;
}
void WebView::goBack() {
if (_impl != nullptr) {
_impl->goBack();
}
}
void WebView::goForward() {
if (_impl != nullptr) {
_impl->goForward();
}
}
void WebView::evaluateJS(const ccstd::string &js) {
if (_impl != nullptr) {
_impl->evaluateJS(js);
}
}
void WebView::setScalesPageToFit(bool scalesPageToFit) {
if (_impl != nullptr) {
_impl->setScalesPageToFit(scalesPageToFit);
}
}
void WebView::setVisible(bool visible) {
if (_impl != nullptr) {
_impl->setVisible(visible);
}
}
void WebView::setFrame(float x, float y, float width, float height) {
if (_impl != nullptr) {
_impl->setFrame(x, y, width, height);
}
}
void WebView::setBounces(bool bounces) {
if (_impl != nullptr) {
_impl->setBounces(bounces);
}
}
void WebView::setBackgroundTransparent(bool isTransparent) {
if (_impl != nullptr) {
_impl->setBackgroundTransparent(isTransparent);
}
}
void WebView::setOnDidFailLoading(const ccWebViewCallback &callback) {
_onDidFailLoading = callback;
}
void WebView::setOnDidFinishLoading(const ccWebViewCallback &callback) {
_onDidFinishLoading = callback;
}
void WebView::setOnShouldStartLoading(
const std::function<bool(WebView *sender, const ccstd::string &url)> &callback) {
_onShouldStartLoading = callback;
}
void WebView::setOnJSCallback(const ccWebViewCallback &callback) {
_onJSCallback = callback;
}
std::function<bool(WebView
*sender,
const ccstd::string &url)>
WebView::getOnShouldStartLoading() const {
return _onShouldStartLoading;
}
WebView::ccWebViewCallback WebView::getOnDidFailLoading() const {
return _onDidFailLoading;
}
WebView::ccWebViewCallback WebView::getOnDidFinishLoading() const {
return _onDidFinishLoading;
}
WebView::ccWebViewCallback WebView::getOnJSCallback() const {
return _onJSCallback;
}
} //namespace cc

247
cocos/ui/webview/WebView.h Normal file
View File

@@ -0,0 +1,247 @@
/****************************************************************************
Copyright (c) 2014-2016 Chukong Technologies Inc.
Copyright (c) 2017-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 <functional>
#include "base/Data.h"
#include "base/Macros.h"
#include "base/RefCounted.h"
#include "base/std/container/string.h"
/**
* @addtogroup ui
* @{
*/
namespace cc {
class WebViewImpl;
/**
* @brief A View that displays web pages.
*
* @note WebView displays web pages base on system widget.
* It's mean WebView displays web pages above all graphical elements of cocos2d-x.
* @js NA
*/
class WebView final {
public:
/**
* Allocates and initializes a WebView.
*/
static WebView *create();
/**
* Destroy webview, remove it from its parent
*/
void destroy();
/**
* Set javascript interface scheme.
*
* @see WebView::setOnJSCallback()
*/
void setJavascriptInterfaceScheme(const ccstd::string &scheme);
/**
* Sets the main page contents, MIME type, content encoding, and base URL.
*
* @param data The content for the main page.
* @param mimeType The MIME type of the data.
* @param encoding The encoding of the data.
* @param baseURL The base URL for the content.
*/
void loadData(const cc::Data &data,
const ccstd::string &mimeType,
const ccstd::string &encoding,
const ccstd::string &baseURL);
/**
* Sets the main page content and base URL.
*
* @param string The content for the main page.
* @param baseURL The base URL for the content.
*/
void loadHTMLString(const ccstd::string &string, const ccstd::string &baseURL = "");
/**
* Loads the given URL.
*
* @param url Content URL.
*/
void loadURL(const ccstd::string &url);
/**
* Loads the given fileName.
*
* @param fileName Content fileName.
*/
void loadFile(const ccstd::string &fileName);
/**
* Stops the current load.
*/
void stopLoading();
/**
* Reloads the current URL.
*/
void reload();
/**
* Gets whether this WebView has a back history item.
*
* @return WebView has a back history item.
*/
bool canGoBack();
/**
* Gets whether this WebView has a forward history item.
*
* @return WebView has a forward history item.
*/
bool canGoForward();
/**
* Goes back in the history.
*/
void goBack();
/**
* Goes forward in the history.
*/
void goForward();
/**
* Evaluates JavaScript in the context of the currently displayed page.
*/
void evaluateJS(const ccstd::string &js);
/**
* Set WebView should support zooming. The default value is false.
*/
void setScalesPageToFit(bool scalesPageToFit);
/**
* Call before a web view begins loading.
*
* @param callback The web view that is about to load new content.
* @return YES if the web view should begin loading content; otherwise, NO.
*/
void setOnShouldStartLoading(
const std::function<bool(WebView *sender, const ccstd::string &url)> &callback);
/**
* A callback which will be called when a WebView event happens.
*/
using ccWebViewCallback = std::function<void(WebView *, const ccstd::string &)>;
/**
* Call after a web view finishes loading.
*
* @param callback The web view that has finished loading.
*/
void setOnDidFinishLoading(const ccWebViewCallback &callback);
/**
* Call if a web view failed to load content.
*
* @param callback The web view that has failed loading.
*/
void setOnDidFailLoading(const ccWebViewCallback &callback);
/**
* This callback called when load URL that start with javascript interface scheme.
*/
void setOnJSCallback(const ccWebViewCallback &callback);
/**
* Get the callback when WebView is about to start.
*/
std::function<bool(WebView *sender, const ccstd::string &url)>
getOnShouldStartLoading() const;
/**
* Get the callback when WebView has finished loading.
*/
ccWebViewCallback getOnDidFinishLoading() const;
/**
* Get the callback when WebView has failed loading.
*/
ccWebViewCallback getOnDidFailLoading() const;
/**
*Get the Javascript callback.
*/
ccWebViewCallback getOnJSCallback() const;
/**
* Set whether the webview bounces at end of scroll of WebView.
*/
void setBounces(bool bounce);
/**
* Toggle visibility of WebView.
*/
virtual void setVisible(bool visible);
/**
* Set the rect of WebView.
*/
virtual void setFrame(float x, float y, float width, float height);
/**
* Set the background transparent
*/
virtual void setBackgroundTransparent(bool isTransparent);
protected:
std::function<bool(WebView *sender, const ccstd::string &url)> _onShouldStartLoading;
ccWebViewCallback _onDidFinishLoading;
ccWebViewCallback _onDidFailLoading;
ccWebViewCallback _onJSCallback;
/**
* Default constructor.
*/
WebView();
/**
* Default destructor.
*/
~WebView();
private:
WebViewImpl *_impl;
friend class WebViewImpl;
};
} // namespace cc

View File

@@ -0,0 +1,300 @@
/****************************************************************************
Copyright (c) 2014-2016 Chukong Technologies Inc.
Copyright (c) 2017-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 <cstdlib>
#include "base/std/container/string.h"
#include "base/std/container/unordered_map.h"
#include "WebView-inl.h"
#include "platform/FileUtils.h"
#include "platform/java/jni/JniHelper.h"
static const ccstd::string CLASS_NAME = "com/cocos/lib/CocosWebViewHelper";
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "", __VA_ARGS__)
static const ccstd::string S_DEFAULT_BASE_URL = "file:///android_asset/";
static const ccstd::string S_SD_ROOT_BASE_URL = "file://";
static ccstd::string getFixedBaseUrl(const ccstd::string &baseUrl) {
ccstd::string fixedBaseUrl;
if (baseUrl.empty()) {
fixedBaseUrl = S_DEFAULT_BASE_URL;
} else if (baseUrl.find(S_SD_ROOT_BASE_URL) != ccstd::string::npos) {
fixedBaseUrl = baseUrl;
} else if (baseUrl.c_str()[0] != '/') {
if (baseUrl.find("assets/") == 0) {
fixedBaseUrl = S_DEFAULT_BASE_URL + baseUrl.c_str()[7];
} else {
fixedBaseUrl = S_DEFAULT_BASE_URL + baseUrl;
}
} else {
fixedBaseUrl = S_SD_ROOT_BASE_URL + baseUrl;
}
if (fixedBaseUrl.c_str()[fixedBaseUrl.length() - 1] != '/') {
fixedBaseUrl += "/";
}
return fixedBaseUrl;
}
extern "C" {
/*
* Class: com_cocos_lib_CocosWebViewHelper
* Method: shouldStartLoading
* Signature: (ILjava/lang/String;)Z
*/
JNIEXPORT jboolean JNICALL
Java_com_cocos_lib_CocosWebViewHelper_shouldStartLoading(JNIEnv *env, jclass, jint index, //NOLINT
jstring jurl) {
const auto *charUrl = env->GetStringUTFChars(jurl, nullptr);
ccstd::string url = charUrl;
env->ReleaseStringUTFChars(jurl, charUrl);
return cc::WebViewImpl::shouldStartLoading(index, url);
}
/*
* Class: com_cocos_lib_CocosWebViewHelper
* Method: didFinishLoading
* Signature: (ILjava/lang/String;)V
*/
JNIEXPORT void JNICALL
Java_com_cocos_lib_CocosWebViewHelper_didFinishLoading(JNIEnv *env, jclass, jint index, //NOLINT
jstring jurl) {
// LOGD("didFinishLoading");
const auto *charUrl = env->GetStringUTFChars(jurl, nullptr);
ccstd::string url = charUrl;
env->ReleaseStringUTFChars(jurl, charUrl);
cc::WebViewImpl::didFinishLoading(index, url);
}
/*
* Class: com_cocos_lib_CocosWebViewHelper
* Method: didFailLoading
* Signature: (ILjava/lang/String;)V
*/
JNIEXPORT void JNICALL
Java_com_cocos_lib_CocosWebViewHelper_didFailLoading(JNIEnv *env, jclass, jint index, //NOLINT
jstring jurl) {
// LOGD("didFailLoading");
const auto *charUrl = env->GetStringUTFChars(jurl, nullptr);
ccstd::string url = charUrl;
env->ReleaseStringUTFChars(jurl, charUrl);
cc::WebViewImpl::didFailLoading(index, url);
}
/*
* Class: com_cocos_lib_CocosWebViewHelper
* Method: onJsCallback
* Signature: (ILjava/lang/String;)V
*/
JNIEXPORT void JNICALL
Java_com_cocos_lib_CocosWebViewHelper_onJsCallback(JNIEnv *env, jclass, jint index, //NOLINT
jstring jmessage) {
// LOGD("jsCallback");
const auto *charMessage = env->GetStringUTFChars(jmessage, nullptr);
ccstd::string message = charMessage;
env->ReleaseStringUTFChars(jmessage, charMessage);
cc::WebViewImpl::onJsCallback(index, message);
}
}
namespace {
int createWebViewJNI() {
cc::JniMethodInfo t;
if (cc::JniHelper::getStaticMethodInfo(t, CLASS_NAME.c_str(), "createWebView", "()I")) {
// LOGD("error: %s,%d",__func__,__LINE__);
jint viewTag = t.env->CallStaticIntMethod(t.classID, t.methodID);
ccDeleteLocalRef(t.env, t.classID);
return viewTag;
}
return -1;
}
ccstd::string getUrlStringByFileName(const ccstd::string &fileName) {
// LOGD("error: %s,%d",__func__,__LINE__);
const ccstd::string basePath("file:///android_asset/");
ccstd::string fullPath = cc::FileUtils::getInstance()->fullPathForFilename(fileName);
const ccstd::string assetsPath("assets/");
ccstd::string urlString;
if (fullPath.find(assetsPath) != ccstd::string::npos) {
urlString = fullPath.replace(fullPath.find_first_of(assetsPath), assetsPath.length(),
basePath);
} else {
urlString = fullPath;
}
return urlString;
}
} // namespace
namespace cc {
static ccstd::unordered_map<int, WebViewImpl *> sWebViewImpls;
WebViewImpl::WebViewImpl(WebView *webView) : _viewTag(-1),
_webView(webView) {
_viewTag = createWebViewJNI();
sWebViewImpls[_viewTag] = this;
}
WebViewImpl::~WebViewImpl() {
destroy();
}
void WebViewImpl::destroy() {
if (_viewTag != -1) {
JniHelper::callStaticVoidMethod(CLASS_NAME, "removeWebView", _viewTag);
auto iter = sWebViewImpls.find(_viewTag);
if (iter != sWebViewImpls.end()) {
sWebViewImpls.erase(iter);
}
_viewTag = -1;
}
}
void WebViewImpl::loadData(const Data &data, const ccstd::string &mimeType, // NOLINT
const ccstd::string &encoding, const ccstd::string &baseURL) { // NOLINT
ccstd::string dataString(reinterpret_cast<char *>(data.getBytes()),
static_cast<unsigned int>(data.getSize()));
JniHelper::callStaticVoidMethod(CLASS_NAME, "setJavascriptInterfaceScheme", _viewTag,
dataString, mimeType, encoding, baseURL);
}
void WebViewImpl::loadHTMLString(const ccstd::string &string, const ccstd::string &baseURL) { // NOLINT
JniHelper::callStaticVoidMethod(CLASS_NAME, "loadHTMLString", _viewTag, string, baseURL);
}
void WebViewImpl::loadURL(const ccstd::string &url) { // NOLINT
JniHelper::callStaticVoidMethod(CLASS_NAME, "loadUrl", _viewTag, url);
}
void WebViewImpl::loadFile(const ccstd::string &fileName) { // NOLINT
auto fullPath = getUrlStringByFileName(fileName);
JniHelper::callStaticVoidMethod(CLASS_NAME, "loadFile", _viewTag, fullPath);
}
void WebViewImpl::stopLoading() { // NOLINT
JniHelper::callStaticVoidMethod(CLASS_NAME, "stopLoading", _viewTag);
}
void WebViewImpl::reload() { // NOLINT
JniHelper::callStaticVoidMethod(CLASS_NAME, "reload", _viewTag);
}
bool WebViewImpl::canGoBack() { // NOLINT
return JniHelper::callStaticBooleanMethod(CLASS_NAME, "canGoBack", _viewTag);
}
bool WebViewImpl::canGoForward() { // NOLINT
return JniHelper::callStaticBooleanMethod(CLASS_NAME, "canGoForward", _viewTag);
}
void WebViewImpl::goBack() { // NOLINT
JniHelper::callStaticVoidMethod(CLASS_NAME, "goBack", _viewTag);
}
void WebViewImpl::goForward() { // NOLINT
JniHelper::callStaticVoidMethod(CLASS_NAME, "goForward", _viewTag);
}
void WebViewImpl::setJavascriptInterfaceScheme(const ccstd::string &scheme) { // NOLINT
JniHelper::callStaticVoidMethod(CLASS_NAME, "setJavascriptInterfaceScheme", _viewTag,
scheme);
}
void WebViewImpl::evaluateJS(const ccstd::string &js) { // NOLINT
JniHelper::callStaticVoidMethod(CLASS_NAME, "evaluateJS", _viewTag, js);
}
void WebViewImpl::setScalesPageToFit(bool scalesPageToFit) { // NOLINT
JniHelper::callStaticVoidMethod(CLASS_NAME, "setScalesPageToFit", _viewTag, scalesPageToFit);
}
bool WebViewImpl::shouldStartLoading(int viewTag, const ccstd::string &url) {
bool allowLoad = true;
auto it = sWebViewImpls.find(viewTag);
if (it != sWebViewImpls.end()) {
auto *webView = it->second->_webView;
if (webView->_onShouldStartLoading) {
allowLoad = webView->_onShouldStartLoading(webView, url);
}
}
return allowLoad;
}
void WebViewImpl::didFinishLoading(int viewTag, const ccstd::string &url) {
auto it = sWebViewImpls.find(viewTag);
if (it != sWebViewImpls.end()) {
auto *webView = it->second->_webView;
if (webView->_onDidFinishLoading) {
webView->_onDidFinishLoading(webView, url);
}
}
}
void WebViewImpl::didFailLoading(int viewTag, const ccstd::string &url) {
auto it = sWebViewImpls.find(viewTag);
if (it != sWebViewImpls.end()) {
auto *webView = it->second->_webView;
if (webView->_onDidFailLoading) {
webView->_onDidFailLoading(webView, url);
}
}
}
void WebViewImpl::onJsCallback(int viewTag, const ccstd::string &message) {
auto it = sWebViewImpls.find(viewTag);
if (it != sWebViewImpls.end()) {
auto *webView = it->second->_webView;
if (webView->_onJSCallback) {
webView->_onJSCallback(webView, message);
}
}
}
void WebViewImpl::setVisible(bool visible) { // NOLINT
JniHelper::callStaticVoidMethod(CLASS_NAME, "setVisible", _viewTag, visible);
}
void WebViewImpl::setFrame(float x, float y, float width, float height) { // NOLINT
JniHelper::callStaticVoidMethod(CLASS_NAME, "setWebViewRect", _viewTag,
static_cast<int>(x), static_cast<int>(y), static_cast<int>(width), static_cast<int>(height));
}
void WebViewImpl::setBounces(bool bounces) {
// empty function as this was mainly a fix for iOS
}
void WebViewImpl::setBackgroundTransparent(bool isTransparent) { // NOLINT
JniHelper::callStaticVoidMethod(CLASS_NAME, "setBackgroundTransparent", _viewTag,
isTransparent);
}
} //namespace cc

View File

@@ -0,0 +1,92 @@
/****************************************************************************
Copyright (c) 2014-2016 Chukong Technologies Inc.
Copyright (c) 2017-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.
****************************************************************************/
#ifndef __COCOS2D_UI_WEBVIEWIMPL_IOS_H_
#define __COCOS2D_UI_WEBVIEWIMPL_IOS_H_
/// @cond DO_NOT_SHOW
#include <iosfwd>
@class UIWebViewWrapper;
namespace cc {
class Data;
class WebView;
class WebViewImpl final {
public:
explicit WebViewImpl(WebView *webView);
~WebViewImpl();
void destroy();
void setJavascriptInterfaceScheme(const ccstd::string &scheme);
void loadData(const cc::Data &data,
const ccstd::string &MIMEType,
const ccstd::string &encoding,
const ccstd::string &baseURL);
void loadHTMLString(const ccstd::string &string, const ccstd::string &baseURL);
void loadURL(const ccstd::string &url);
void loadFile(const ccstd::string &fileName);
void stopLoading();
void reload();
bool canGoBack();
bool canGoForward();
void goBack();
void goForward();
void evaluateJS(const ccstd::string &js);
void setScalesPageToFit(const bool scalesPageToFit);
void setVisible(bool visible);
void setFrame(float x, float y, float width, float height);
void setBounces(bool bounces);
void setBackgroundTransparent(bool isTransparent);
private:
UIWebViewWrapper *_uiWebViewWrapper;
WebView *_webView;
};
} //namespace cc
/// @endcond
#endif /* __COCOS2D_UI_WEBVIEWIMPL_IOS_H_ */

View File

@@ -0,0 +1,474 @@
/****************************************************************************
Copyright (c) 2014-2016 Chukong Technologies Inc.
Copyright (c) 2017-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.
****************************************************************************/
#import <WebKit/WKWebView.h>
#import <WebKit/WKUIDelegate.h>
#import <WebKit/WKNavigationDelegate.h>
#include "WebView-inl.h"
#include "platform/FileUtils.h"
#include "platform/ios/WarpCocosContent.h"
@interface UIWebViewWrapper : NSObject
@property (nonatomic) std::function<bool(ccstd::string url)> shouldStartLoading;
@property (nonatomic) std::function<void(ccstd::string url)> didFinishLoading;
@property (nonatomic) std::function<void(ccstd::string url)> didFailLoading;
@property (nonatomic) std::function<void(ccstd::string url)> onJsCallback;
@property (nonatomic, readonly, getter=canGoBack) BOOL canGoBack;
@property (nonatomic, readonly, getter=canGoForward) BOOL canGoForward;
+ (instancetype)webViewWrapper;
- (void)setVisible:(bool)visible;
- (void)setBounces:(bool)bounces;
- (void)setFrame:(float)x y:(float)y width:(float)width height:(float)height;
- (void)setJavascriptInterfaceScheme:(const ccstd::string &)scheme;
- (void)loadData:(const ccstd::string &)data MIMEType:(const ccstd::string &)MIMEType textEncodingName:(const ccstd::string &)encodingName baseURL:(const ccstd::string &)baseURL;
- (void)loadHTMLString:(const ccstd::string &)string baseURL:(const ccstd::string &)baseURL;
- (void)loadUrl:(const ccstd::string &)urlString;
- (void)loadFile:(const ccstd::string &)filePath;
- (void)stopLoading;
- (void)reload;
- (void)evaluateJS:(const ccstd::string &)js;
- (void)goBack;
- (void)goForward;
- (void)setScalesPageToFit:(const bool)scalesPageToFit;
- (void)setBackgroundTransparent:(const bool)isTransparent;
@end
@interface UIWebViewWrapper () <WKUIDelegate, WKNavigationDelegate>
@property (nonatomic, assign) WKWebView *uiWebView;
@property (nonatomic, copy) NSString *jsScheme;
@end
@implementation UIWebViewWrapper {
}
+ (instancetype)webViewWrapper {
return [[[self alloc] init] autorelease];
}
- (instancetype)init {
self = [super init];
if (self) {
self.uiWebView = nil;
self.shouldStartLoading = nullptr;
self.didFinishLoading = nullptr;
self.didFailLoading = nullptr;
}
return self;
}
- (void)dealloc {
self.uiWebView.UIDelegate = nil;
[self.uiWebView removeFromSuperview];
[self.uiWebView release];
self.jsScheme = nil;
[super dealloc];
}
- (void)setupWebView {
if (!self.uiWebView) {
self.uiWebView = [[WKWebView alloc] init];
self.uiWebView.UIDelegate = self;
self.uiWebView.navigationDelegate = self;
}
if (!self.uiWebView.superview) {
//UIView *eaglview = UIApplication.sharedApplication.delegate.window.rootViewController.view;
UIView *eaglview = WarpCocosContent.shareInstance.renderView;
[eaglview addSubview:self.uiWebView];
}
}
- (void)setVisible:(bool)visible {
self.uiWebView.hidden = !visible;
}
- (void)setBounces:(bool)bounces {
self.uiWebView.scrollView.bounces = bounces;
}
- (void)setFrame:(float)x y:(float)y width:(float)width height:(float)height {
if (!self.uiWebView) {
[self setupWebView];
}
CGRect newFrame = CGRectMake(x, y, width, height);
if (!CGRectEqualToRect(self.uiWebView.frame, newFrame)) {
self.uiWebView.frame = newFrame;
}
}
- (void)setJavascriptInterfaceScheme:(const ccstd::string &)scheme {
self.jsScheme = @(scheme.c_str());
}
- (void)loadData:(const ccstd::string &)data MIMEType:(const ccstd::string &)MIMEType textEncodingName:(const ccstd::string &)encodingName baseURL:(const ccstd::string &)baseURL {
auto path = [[NSBundle mainBundle] resourcePath];
path = [path stringByAppendingPathComponent:@(baseURL.c_str())];
auto url = [NSURL fileURLWithPath:path];
[self.uiWebView loadData:[NSData dataWithBytes:data.c_str() length:data.length()]
MIMEType:@(MIMEType.c_str())
characterEncodingName:@(encodingName.c_str())
baseURL:url];
}
- (void)loadHTMLString:(const ccstd::string &)string baseURL:(const ccstd::string &)baseURL {
if (!self.uiWebView) {
[self setupWebView];
}
auto path = [[NSBundle mainBundle] resourcePath];
path = [path stringByAppendingPathComponent:@(baseURL.c_str())];
auto url = [NSURL fileURLWithPath:path];
[self.uiWebView loadHTMLString:@(string.c_str()) baseURL:url];
}
- (void)loadUrl:(const ccstd::string &)urlString {
if (!self.uiWebView) {
[self setupWebView];
}
NSURL *url = [NSURL URLWithString:@(urlString.c_str())];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self.uiWebView loadRequest:request];
}
- (void)loadFile:(const ccstd::string &)filePath {
if (!self.uiWebView) {
[self setupWebView];
}
NSURL *url = [NSURL fileURLWithPath:@(filePath.c_str())];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self.uiWebView loadRequest:request];
}
- (void)stopLoading {
[self.uiWebView stopLoading];
}
- (void)reload {
[self.uiWebView reload];
}
- (BOOL)canGoForward {
return self.uiWebView.canGoForward;
}
- (BOOL)canGoBack {
return self.uiWebView.canGoBack;
}
- (void)goBack {
[self.uiWebView goBack];
}
- (void)goForward {
[self.uiWebView goForward];
}
- (void)evaluateJS:(const ccstd::string &)js {
if (!self.uiWebView) {
[self setupWebView];
}
[self.uiWebView evaluateJavaScript:@(js.c_str()) completionHandler:nil];
}
- (void)setScalesPageToFit:(const bool)scalesPageToFit {
// TODO: there is not corresponding API in WK.
// https://stackoverflow.com/questions/26295277/wkwebview-equivalent-for-uiwebviews-scalespagetofit/43048514 seems has a solution,
// but it doesn't support setting it dynamically. If we want to set this feature dynamically, then it will be too complex.
}
- (void)setBackgroundTransparent:(const bool)isTransparent {
if (!self.uiWebView) {
[self setupWebView];
}
[self.uiWebView setOpaque:isTransparent ? NO : YES];
[self.uiWebView setBackgroundColor:isTransparent ? [UIColor clearColor] : [UIColor whiteColor]];
}
#pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSString *url = [[navigationAction request].URL.absoluteString stringByRemovingPercentEncoding];
NSString *scheme = [navigationAction request].URL.scheme;
if ([scheme isEqualToString:self.jsScheme]) {
self.onJsCallback(url.UTF8String);
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
if (self.shouldStartLoading && url) {
if (self.shouldStartLoading(url.UTF8String))
decisionHandler(WKNavigationActionPolicyAllow);
else
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
decisionHandler(WKNavigationActionPolicyAllow);
}
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
if (self.didFinishLoading) {
NSString *url = [webView.URL absoluteString];
self.didFinishLoading([url UTF8String]);
}
}
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
if (self.didFailLoading) {
NSString *errorInfo = error.userInfo[NSURLErrorFailingURLStringErrorKey];
if (errorInfo) {
self.didFailLoading([errorInfo UTF8String]);
}
}
}
#pragma WKUIDelegate
// Implement js alert function.
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)())completionHandler {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:message
message:nil
preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:[UIAlertAction actionWithTitle:@"Ok"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action) {
completionHandler();
}]];
//auto rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
auto rootViewController = WarpCocosContent.shareInstance.gameVCBlock();
[rootViewController presentViewController:alertController
animated:YES
completion:^{
}];
}
@end
namespace cc {
WebViewImpl::WebViewImpl(WebView *webView)
: _uiWebViewWrapper([UIWebViewWrapper webViewWrapper]),
_webView(webView) {
[_uiWebViewWrapper retain];
_uiWebViewWrapper.shouldStartLoading = [this](ccstd::string url) {
if (this->_webView->_onShouldStartLoading) {
return this->_webView->_onShouldStartLoading(this->_webView, url);
}
return true;
};
_uiWebViewWrapper.didFinishLoading = [this](ccstd::string url) {
if (this->_webView->_onDidFinishLoading) {
this->_webView->_onDidFinishLoading(this->_webView, url);
}
};
_uiWebViewWrapper.didFailLoading = [this](ccstd::string url) {
if (this->_webView->_onDidFailLoading) {
this->_webView->_onDidFailLoading(this->_webView, url);
}
};
_uiWebViewWrapper.onJsCallback = [this](ccstd::string url) {
if (this->_webView->_onJSCallback) {
this->_webView->_onJSCallback(this->_webView, url);
}
};
}
WebViewImpl::~WebViewImpl() {
destroy();
}
void WebViewImpl::destroy() {
if (_uiWebViewWrapper != nil) {
[_uiWebViewWrapper release];
_uiWebViewWrapper = nil;
}
}
void WebViewImpl::setJavascriptInterfaceScheme(const ccstd::string &scheme) {
if (_uiWebViewWrapper == nil) {
return;
}
[_uiWebViewWrapper setJavascriptInterfaceScheme:scheme];
}
void WebViewImpl::loadData(const Data &data,
const ccstd::string &MIMEType,
const ccstd::string &encoding,
const ccstd::string &baseURL) {
if (_uiWebViewWrapper == nil) {
return;
}
ccstd::string dataString(reinterpret_cast<char *>(data.getBytes()), static_cast<unsigned int>(data.getSize()));
[_uiWebViewWrapper loadData:dataString MIMEType:MIMEType textEncodingName:encoding baseURL:baseURL];
}
void WebViewImpl::loadHTMLString(const ccstd::string &string, const ccstd::string &baseURL) {
if (_uiWebViewWrapper == nil) {
return;
}
[_uiWebViewWrapper loadHTMLString:string baseURL:baseURL];
}
void WebViewImpl::loadURL(const ccstd::string &url) {
if (_uiWebViewWrapper == nil) {
return;
}
[_uiWebViewWrapper loadUrl:url];
}
void WebViewImpl::loadFile(const ccstd::string &fileName) {
if (_uiWebViewWrapper == nil) {
return;
}
auto fullPath = cc::FileUtils::getInstance()->fullPathForFilename(fileName);
[_uiWebViewWrapper loadFile:fullPath];
}
void WebViewImpl::stopLoading() {
if (_uiWebViewWrapper == nil) {
return;
}
[_uiWebViewWrapper stopLoading];
}
void WebViewImpl::reload() {
if (_uiWebViewWrapper == nil) {
return;
}
[_uiWebViewWrapper reload];
}
bool WebViewImpl::canGoBack() {
if (_uiWebViewWrapper == nil) {
return false;
}
return _uiWebViewWrapper.canGoBack;
}
bool WebViewImpl::canGoForward() {
if (_uiWebViewWrapper == nil) {
return false;
}
return _uiWebViewWrapper.canGoForward;
}
void WebViewImpl::goBack() {
if (_uiWebViewWrapper == nil) {
return;
}
[_uiWebViewWrapper goBack];
}
void WebViewImpl::goForward() {
if (_uiWebViewWrapper == nil) {
return;
}
[_uiWebViewWrapper goForward];
}
void WebViewImpl::evaluateJS(const ccstd::string &js) {
if (_uiWebViewWrapper == nil) {
return;
}
[_uiWebViewWrapper evaluateJS:js];
}
void WebViewImpl::setBounces(bool bounces) {
if (_uiWebViewWrapper == nil) {
return;
}
[_uiWebViewWrapper setBounces:bounces];
}
void WebViewImpl::setScalesPageToFit(bool scalesPageToFit) {
if (_uiWebViewWrapper == nil) {
return;
}
[_uiWebViewWrapper setScalesPageToFit:scalesPageToFit];
}
void WebViewImpl::setVisible(bool visible) {
if (_uiWebViewWrapper == nil) {
return;
}
[_uiWebViewWrapper setVisible:visible];
}
void WebViewImpl::setFrame(float x, float y, float width, float height) {
if (_uiWebViewWrapper == nil) {
return;
}
auto scaleFactor = [[UIScreen mainScreen] nativeScale];
[_uiWebViewWrapper setFrame:x / scaleFactor
y:y / scaleFactor
width:width / scaleFactor
height:height / scaleFactor];
}
void WebViewImpl::setBackgroundTransparent(bool isTransparent) {
if (_uiWebViewWrapper == nil) {
return;
}
[_uiWebViewWrapper setBackgroundTransparent:isTransparent];
}
} //namespace cc

View File

@@ -0,0 +1,94 @@
/****************************************************************************
Copyright (c) 2014-2016 Chukong Technologies Inc.
Copyright (c) 2017-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 <cstdint>
#include <iosfwd>
#include "base/Data.h"
#include "base/Macros.h"
namespace cc {
class WebView;
class WebViewImpl final {
public:
explicit WebViewImpl(WebView *webView);
~WebViewImpl();
void destroy();
void setJavascriptInterfaceScheme(const ccstd::string &scheme);
void loadData(const cc::Data &data, const ccstd::string &mimeType,
const ccstd::string &encoding, const ccstd::string &baseURL);
void loadHTMLString(const ccstd::string &string, const ccstd::string &baseURL);
void loadURL(const ccstd::string &url);
void loadFile(const ccstd::string &fileName);
void stopLoading();
void reload();
bool canGoBack();
bool canGoForward();
void goBack();
void goForward();
void evaluateJS(const ccstd::string &js);
void setScalesPageToFit(bool scalesPageToFit);
void setVisible(bool visible);
void setFrame(float x, float y, float width, float height);
void setBounces(bool bounces);
void setBackgroundTransparent(bool isTransparent);
static bool shouldStartLoading(int viewTag, const ccstd::string &url);
static void didFinishLoading(int viewTag, const ccstd::string &url);
static void didFailLoading(int viewTag, const ccstd::string &url);
static void onJsCallback(int viewTag, const ccstd::string &message);
private:
int _viewTag;
WebView *_webView;
};
} // namespace cc

View File

@@ -0,0 +1,302 @@
/****************************************************************************
Copyright (c) 2014-2016 Chukong Technologies Inc.
Copyright (c) 2017-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 <cstdlib>
#include "base/std/container/string.h"
#include "base/std/container/unordered_map.h"
#include "WebView-inl.h"
#include "platform/FileUtils.h"
#include "platform/java/jni/JniHelper.h"
#include "cocos/base/Log.h"
static const ccstd::string CLASS_NAME = "com/cocos/lib/CocosWebViewHelper";
#define LOGD CC_LOG_DEBUG
static const ccstd::string S_DEFAULT_BASE_URL = "file:///android_asset/";
static const ccstd::string S_SD_ROOT_BASE_URL = "file://";
static ccstd::string getFixedBaseUrl(const ccstd::string &baseUrl) {
ccstd::string fixedBaseUrl;
if (baseUrl.empty()) {
fixedBaseUrl = S_DEFAULT_BASE_URL;
} else if (baseUrl.find(S_SD_ROOT_BASE_URL) != ccstd::string::npos) {
fixedBaseUrl = baseUrl;
} else if (baseUrl.c_str()[0] != '/') {
if (baseUrl.find("assets/") == 0) {
fixedBaseUrl = S_DEFAULT_BASE_URL + baseUrl.c_str()[7];
} else {
fixedBaseUrl = S_DEFAULT_BASE_URL + baseUrl;
}
} else {
fixedBaseUrl = S_SD_ROOT_BASE_URL + baseUrl;
}
if (fixedBaseUrl.c_str()[fixedBaseUrl.length() - 1] != '/') {
fixedBaseUrl += "/";
}
return fixedBaseUrl;
}
extern "C" {
/*
* Class: com_cocos_lib_CocosWebViewHelper
* Method: shouldStartLoading
* Signature: (ILjava/lang/String;)Z
*/
JNIEXPORT jboolean JNICALL
Java_com_cocos_lib_CocosWebViewHelper_shouldStartLoading(JNIEnv *env, jclass, jint index, //NOLINT
jstring jurl) {
auto charUrl = env->GetStringUTFChars(jurl, nullptr);
ccstd::string url = charUrl;
env->ReleaseStringUTFChars(jurl, charUrl);
return cc::WebViewImpl::shouldStartLoading(index, url);
}
/*
* Class: com_cocos_lib_CocosWebViewHelper
* Method: didFinishLoading
* Signature: (ILjava/lang/String;)V
*/
JNIEXPORT void JNICALL
Java_com_cocos_lib_CocosWebViewHelper_didFinishLoading(JNIEnv *env, jclass, jint index, //NOLINT
jstring jurl) {
// LOGD("didFinishLoading");
auto charUrl = env->GetStringUTFChars(jurl, nullptr);
ccstd::string url = charUrl;
env->ReleaseStringUTFChars(jurl, charUrl);
cc::WebViewImpl::didFinishLoading(index, url);
}
/*
* Class: com_cocos_lib_CocosWebViewHelper
* Method: didFailLoading
* Signature: (ILjava/lang/String;)V
*/
JNIEXPORT void JNICALL
Java_com_cocos_lib_CocosWebViewHelper_didFailLoading(JNIEnv *env, jclass, jint index, //NOLINT
jstring jurl) {
// LOGD("didFailLoading");
auto charUrl = env->GetStringUTFChars(jurl, nullptr);
ccstd::string url = charUrl;
env->ReleaseStringUTFChars(jurl, charUrl);
cc::WebViewImpl::didFailLoading(index, url);
}
/*
* Class: com_cocos_lib_CocosWebViewHelper
* Method: onJsCallback
* Signature: (ILjava/lang/String;)V
*/
JNIEXPORT void JNICALL
Java_com_cocos_lib_CocosWebViewHelper_onJsCallback(JNIEnv *env, jclass, jint index, //NOLINT
jstring jmessage) {
// LOGD("jsCallback");
auto charMessage = env->GetStringUTFChars(jmessage, nullptr);
ccstd::string message = charMessage;
env->ReleaseStringUTFChars(jmessage, charMessage);
cc::WebViewImpl::onJsCallback(index, message);
}
}
namespace {
int createWebViewJNI() {
cc::JniMethodInfo t;
if (cc::JniHelper::getStaticMethodInfo(t, CLASS_NAME.c_str(), "createWebView", "()I")) {
// LOGD("error: %s,%d",__func__,__LINE__);
jint viewTag = t.env->CallStaticIntMethod(t.classID, t.methodID);
ccDeleteLocalRef(t.env, t.classID);
return viewTag;
}
return -1;
}
ccstd::string getUrlStringByFileName(const ccstd::string &fileName) {
// LOGD("error: %s,%d",__func__,__LINE__);
const ccstd::string basePath("file:///android_asset/");
ccstd::string fullPath = cc::FileUtils::getInstance()->fullPathForFilename(fileName);
const ccstd::string assetsPath("assets/");
ccstd::string urlString;
if (fullPath.find(assetsPath) != ccstd::string::npos) {
urlString = fullPath.replace(fullPath.find_first_of(assetsPath), assetsPath.length(),
basePath);
} else {
urlString = fullPath;
}
return urlString;
}
} // namespace
namespace cc {
static ccstd::unordered_map<int, WebViewImpl *> sWebViewImpls;
WebViewImpl::WebViewImpl(WebView *webView) : _viewTag(-1),
_webView(webView) {
_viewTag = createWebViewJNI();
sWebViewImpls[_viewTag] = this;
}
WebViewImpl::~WebViewImpl() {
destroy();
}
void WebViewImpl::destroy() {
if (_viewTag != -1) {
JniHelper::callStaticVoidMethod(CLASS_NAME, "removeWebView", _viewTag);
auto iter = sWebViewImpls.find(_viewTag);
if (iter != sWebViewImpls.end()) {
sWebViewImpls.erase(iter);
}
_viewTag = -1;
}
}
void WebViewImpl::loadData(const Data &data, const ccstd::string &mimeType,
const ccstd::string &encoding, const ccstd::string &baseURL) {
ccstd::string dataString(reinterpret_cast<char *>(data.getBytes()),
static_cast<unsigned int>(data.getSize()));
JniHelper::callStaticVoidMethod(CLASS_NAME, "setJavascriptInterfaceScheme", _viewTag,
dataString, mimeType, encoding, baseURL);
}
void WebViewImpl::loadHTMLString(const ccstd::string &string, const ccstd::string &baseURL) {
JniHelper::callStaticVoidMethod(CLASS_NAME, "loadHTMLString", _viewTag, string, baseURL);
}
void WebViewImpl::loadURL(const ccstd::string &url) {
JniHelper::callStaticVoidMethod(CLASS_NAME, "loadUrl", _viewTag, url);
}
void WebViewImpl::loadFile(const ccstd::string &fileName) {
auto fullPath = getUrlStringByFileName(fileName);
JniHelper::callStaticVoidMethod(CLASS_NAME, "loadFile", _viewTag, fullPath);
}
void WebViewImpl::stopLoading() {
JniHelper::callStaticVoidMethod(CLASS_NAME, "stopLoading", _viewTag);
}
void WebViewImpl::reload() {
JniHelper::callStaticVoidMethod(CLASS_NAME, "reload", _viewTag);
}
bool WebViewImpl::canGoBack() {
return JniHelper::callStaticBooleanMethod(CLASS_NAME, "canGoBack", _viewTag);
}
bool WebViewImpl::canGoForward() {
return JniHelper::callStaticBooleanMethod(CLASS_NAME, "canGoForward", _viewTag);
}
void WebViewImpl::goBack() {
JniHelper::callStaticVoidMethod(CLASS_NAME, "goBack", _viewTag);
}
void WebViewImpl::goForward() {
JniHelper::callStaticVoidMethod(CLASS_NAME, "goForward", _viewTag);
}
void WebViewImpl::setJavascriptInterfaceScheme(const ccstd::string &scheme) {
JniHelper::callStaticVoidMethod(CLASS_NAME, "setJavascriptInterfaceScheme", _viewTag,
scheme);
}
void WebViewImpl::evaluateJS(const ccstd::string &js) {
JniHelper::callStaticVoidMethod(CLASS_NAME, "evaluateJS", _viewTag, js);
}
void WebViewImpl::setScalesPageToFit(bool scalesPageToFit) {
JniHelper::callStaticVoidMethod(CLASS_NAME, "setScalesPageToFit", _viewTag, scalesPageToFit);
}
bool WebViewImpl::shouldStartLoading(int viewTag, const ccstd::string &url) {
bool allowLoad = true;
auto it = sWebViewImpls.find(viewTag);
if (it != sWebViewImpls.end()) {
auto webView = it->second->_webView;
if (webView->_onShouldStartLoading) {
allowLoad = webView->_onShouldStartLoading(webView, url);
}
}
return allowLoad;
}
void WebViewImpl::didFinishLoading(int viewTag, const ccstd::string &url) {
auto it = sWebViewImpls.find(viewTag);
if (it != sWebViewImpls.end()) {
auto webView = it->second->_webView;
if (webView->_onDidFinishLoading) {
webView->_onDidFinishLoading(webView, url);
}
}
}
void WebViewImpl::didFailLoading(int viewTag, const ccstd::string &url) {
auto it = sWebViewImpls.find(viewTag);
if (it != sWebViewImpls.end()) {
auto webView = it->second->_webView;
if (webView->_onDidFailLoading) {
webView->_onDidFailLoading(webView, url);
}
}
}
void WebViewImpl::onJsCallback(int viewTag, const ccstd::string &message) {
auto it = sWebViewImpls.find(viewTag);
if (it != sWebViewImpls.end()) {
auto webView = it->second->_webView;
if (webView->_onJSCallback) {
webView->_onJSCallback(webView, message);
}
}
}
void WebViewImpl::setVisible(bool visible) {
JniHelper::callStaticVoidMethod(CLASS_NAME, "setVisible", _viewTag, visible);
}
void WebViewImpl::setFrame(float x, float y, float width, float height) {
JniHelper::callStaticVoidMethod(CLASS_NAME, "setWebViewRect", _viewTag,
static_cast<int>(x), static_cast<int>(y), static_cast<int>(width), static_cast<int>(height));
}
void WebViewImpl::setBounces(bool bounces) {
// empty function as this was mainly a fix for iOS
}
void WebViewImpl::setBackgroundTransparent(bool isTransparent) {
JniHelper::callStaticVoidMethod(CLASS_NAME, "setBackgroundTransparent", _viewTag,
isTransparent);
}
} //namespace cc

View File

@@ -0,0 +1,313 @@
/****************************************************************************
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 <cstdlib>
#include "base/std/container/string.h"
#include "base/std/container/unordered_map.h"
#include "WebView-inl.h"
#include "platform/FileUtils.h"
#include "platform/openharmony/napi/NapiHelper.h"
#include "WebViewImpl-openharmony.h"
#include "cocos/base/Log.h"
static const ccstd::string S_DEFAULT_BASE_URL = "file:///openharmony_asset/";
static const ccstd::string S_SD_ROOT_BASE_URL = "file://";
static ccstd::string getFixedBaseUrl(const ccstd::string &baseUrl) {
ccstd::string fixedBaseUrl;
if (baseUrl.empty()) {
fixedBaseUrl = S_DEFAULT_BASE_URL;
} else if (baseUrl.find(S_SD_ROOT_BASE_URL) != ccstd::string::npos) {
fixedBaseUrl = baseUrl;
} else if (baseUrl.c_str()[0] != '/') {
if (baseUrl.find("assets/") == 0) {
fixedBaseUrl = S_DEFAULT_BASE_URL + baseUrl.c_str()[7];
} else {
fixedBaseUrl = S_DEFAULT_BASE_URL + baseUrl;
}
} else {
fixedBaseUrl = S_SD_ROOT_BASE_URL + baseUrl;
}
if (fixedBaseUrl.c_str()[fixedBaseUrl.length() - 1] != '/') {
fixedBaseUrl += "/";
}
return fixedBaseUrl;
}
ccstd::string getUrlStringByFileName(const ccstd::string &fileName) {
// LOGD("error: %s,%d",__func__,__LINE__);
const ccstd::string basePath(S_DEFAULT_BASE_URL);
ccstd::string fullPath = cc::FileUtils::getInstance()->fullPathForFilename(fileName);
const ccstd::string assetsPath("assets/");
ccstd::string urlString;
if (fullPath.find(assetsPath) != ccstd::string::npos) {
urlString = fullPath.replace(fullPath.find_first_of(assetsPath), assetsPath.length(),
basePath);
} else {
urlString = fullPath;
}
return urlString;
}
namespace cc {
static int32_t kWebViewTag = 0;
static ccstd::unordered_map<int, WebViewImpl *> sWebViewImpls;
WebViewImpl::WebViewImpl(WebView *webView) : _viewTag(-1),
_webView(webView) {
_viewTag = kWebViewTag++;
NapiHelper::postMessageToUIThread("createWebView", Napi::Number::New(NapiHelper::getWorkerEnv(), static_cast<double>(_viewTag)));
sWebViewImpls[_viewTag] = this;
}
WebViewImpl::~WebViewImpl() {
destroy();
}
void WebViewImpl::destroy() {
if (_viewTag != -1) {
NapiHelper::postMessageToUIThread("removeWebView", Napi::Number::New(NapiHelper::getWorkerEnv(), static_cast<double>(_viewTag)));
auto iter = sWebViewImpls.find(_viewTag);
if (iter != sWebViewImpls.end()) {
sWebViewImpls.erase(iter);
}
_viewTag = -1;
}
}
void WebViewImpl::loadData(const Data &data, const ccstd::string &mimeType,
const ccstd::string &encoding, const ccstd::string &baseURL) {
ccstd::string dataString(reinterpret_cast<char *>(data.getBytes()),
static_cast<unsigned int>(data.getSize()));
auto env = NapiHelper::getWorkerEnv();
auto args = Napi::Object::New(env);
args["tag"] = Napi::Number::New(env, static_cast<double>(_viewTag));
args["contents"] = Napi::String::New(env, dataString);
args["mimeType"] = Napi::String::New(env, mimeType);
args["encoding"] = Napi::String::New(env, encoding);
args["baseUrl"] = Napi::String::New(env, baseURL);
NapiHelper::postMessageToUIThread("loadData", args);
}
void WebViewImpl::loadHTMLString(const ccstd::string &string, const ccstd::string &baseURL) {
auto env = NapiHelper::getWorkerEnv();
auto args = Napi::Object::New(env);
args["tag"] = Napi::Number::New(env, static_cast<double>(_viewTag));
args["contents"] = Napi::String::New(env, string);
args["baseUrl"] = Napi::String::New(env, baseURL);
NapiHelper::postMessageToUIThread("loadHTMLString", args);
}
void WebViewImpl::loadURL(const ccstd::string &url) {
auto env = NapiHelper::getWorkerEnv();
auto args = Napi::Object::New(env);
args["tag"] = Napi::Number::New(env, static_cast<double>(_viewTag));
args["url"] = Napi::String::New(env, url);
NapiHelper::postMessageToUIThread("loadUrl", args);
}
void WebViewImpl::loadFile(const ccstd::string &fileName) {
auto fullPath = getUrlStringByFileName(fileName);
auto env = NapiHelper::getWorkerEnv();
auto args = Napi::Object::New(env);
args["tag"] = Napi::Number::New(env, static_cast<double>(_viewTag));
args["url"] = Napi::String::New(env, fullPath);
NapiHelper::postMessageToUIThread("loadUrl", args);
}
void WebViewImpl::stopLoading() {
NapiHelper::postMessageToUIThread("stopLoading", Napi::Number::New(NapiHelper::getWorkerEnv(), static_cast<double>(_viewTag)));
}
void WebViewImpl::reload() {
NapiHelper::postMessageToUIThread("reload", Napi::Number::New(NapiHelper::getWorkerEnv(), static_cast<double>(_viewTag)));
}
bool WebViewImpl::canGoBack() {
// TODO(qgh):OpenHarmony does not support this interface.
return true;
}
bool WebViewImpl::canGoForward() {
// TODO(qgh):OpenHarmony does not support this interface.
return true;
}
void WebViewImpl::goBack() {
NapiHelper::postMessageToUIThread("goBack", Napi::Number::New(NapiHelper::getWorkerEnv(), static_cast<double>(_viewTag)));
}
void WebViewImpl::goForward() {
NapiHelper::postMessageToUIThread("goForward", Napi::Number::New(NapiHelper::getWorkerEnv(), static_cast<double>(_viewTag)));
}
void WebViewImpl::setJavascriptInterfaceScheme(const ccstd::string &scheme) {
// TODO(qgh):OpenHarmony does not support this interface.
}
void WebViewImpl::evaluateJS(const ccstd::string &js) {
auto env = NapiHelper::getWorkerEnv();
auto args = Napi::Object::New(env);
args["tag"] = Napi::Number::New(env, static_cast<double>(_viewTag));
args["jsContents"] = Napi::String::New(env, js);
NapiHelper::postMessageToUIThread("evaluateJS", args);
}
void WebViewImpl::setScalesPageToFit(bool scalesPageToFit) {
NapiHelper::postMessageToUIThread("setScalesPageToFit", Napi::Number::New(NapiHelper::getWorkerEnv(), static_cast<double>(_viewTag)));
}
bool WebViewImpl::shouldStartLoading(int viewTag, const ccstd::string &url) {
bool allowLoad = true;
auto it = sWebViewImpls.find(viewTag);
if (it != sWebViewImpls.end()) {
auto webView = it->second->_webView;
if (webView->_onShouldStartLoading) {
allowLoad = webView->_onShouldStartLoading(webView, url);
}
}
return allowLoad;
}
void WebViewImpl::didFinishLoading(int viewTag, const ccstd::string &url) {
auto it = sWebViewImpls.find(viewTag);
if (it != sWebViewImpls.end()) {
auto webView = it->second->_webView;
if (webView->_onDidFinishLoading) {
webView->_onDidFinishLoading(webView, url);
}
}
}
void WebViewImpl::didFailLoading(int viewTag, const ccstd::string &url) {
auto it = sWebViewImpls.find(viewTag);
if (it != sWebViewImpls.end()) {
auto webView = it->second->_webView;
if (webView->_onDidFailLoading) {
webView->_onDidFailLoading(webView, url);
}
}
}
void WebViewImpl::onJsCallback(int viewTag, const ccstd::string &message) {
auto it = sWebViewImpls.find(viewTag);
if (it != sWebViewImpls.end()) {
auto webView = it->second->_webView;
if (webView->_onJSCallback) {
webView->_onJSCallback(webView, message);
}
}
}
void WebViewImpl::setVisible(bool visible) {
auto env = NapiHelper::getWorkerEnv();
auto args = Napi::Object::New(env);
args["tag"] = Napi::Number::New(env, static_cast<double>(_viewTag));
args["visible"] = Napi::Boolean::New(env, visible);
NapiHelper::postMessageToUIThread("setVisible", args);
}
void WebViewImpl::setFrame(float x, float y, float width, float height) {
auto env = NapiHelper::getWorkerEnv();
auto args = Napi::Object::New(env);
args["tag"] = Napi::Number::New(env, static_cast<double>(_viewTag));
args["x"] = Napi::Number::New(env, x);
args["y"] = Napi::Number::New(env, y);
args["w"] = Napi::Number::New(env, width);
args["h"] = Napi::Number::New(env, height);
NapiHelper::postMessageToUIThread("setWebViewRect", args);
}
void WebViewImpl::setBounces(bool bounces) {
// empty function as this was mainly a fix for iOS
}
void WebViewImpl::setBackgroundTransparent(bool isTransparent) {
// TODO(qgh):OpenHarmony is not supported at this time
}
static void getViewTagAndUrlFromCallbackInfo(const Napi::CallbackInfo &info, int32_t *viewTag, ccstd::string *url) {
auto env = info.Env();
if (info.Length() != 2) {
Napi::Error::New(env, "napiShouldStartLoading, 2 arguments expected").ThrowAsJavaScriptException();
return;
}
auto arg0 = info[0].As<Napi::Number>();
auto arg1 = info[1].As<Napi::String>();
if (env.IsExceptionPending()) {
return;
}
*viewTag = arg0.Int32Value();
*url = arg1.Utf8Value();
}
void OpenHarmonyWebView::napiShouldStartLoading(const Napi::CallbackInfo &info) {
int32_t viewTag = 0;
ccstd::string url;
getViewTagAndUrlFromCallbackInfo(info, &viewTag, &url);
WebViewImpl::shouldStartLoading(viewTag, url);
}
void OpenHarmonyWebView::napiFinishLoading(const Napi::CallbackInfo &info) {
int32_t viewTag = 0;
ccstd::string url;
getViewTagAndUrlFromCallbackInfo(info, &viewTag, &url);
WebViewImpl::didFinishLoading(viewTag, url);
}
void OpenHarmonyWebView::napiFailLoading(const Napi::CallbackInfo &info) {
int32_t viewTag = 0;
ccstd::string url;
getViewTagAndUrlFromCallbackInfo(info, &viewTag, &url);
WebViewImpl::didFailLoading(viewTag, url);
}
void OpenHarmonyWebView::napiJsCallback(const Napi::CallbackInfo &info) {
int32_t viewTag = 0;
ccstd::string url;
getViewTagAndUrlFromCallbackInfo(info, &viewTag, &url);
WebViewImpl::onJsCallback(viewTag, url);
}
} //namespace cc

View File

@@ -0,0 +1,41 @@
/****************************************************************************
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.
****************************************************************************/
#pragma once
#include "platform/openharmony/napi/NapiHelper.h"
#include "ui/webview/WebViewImpl-java.h"
namespace cc {
class OpenHarmonyWebView {
public:
static void napiShouldStartLoading(const Napi::CallbackInfo &info);
static void napiFinishLoading(const Napi::CallbackInfo &info);
static void napiFailLoading(const Napi::CallbackInfo &info);
static void napiJsCallback(const Napi::CallbackInfo &info);
};
} //namespace cc