no message
This commit is contained in:
118
cocos/ui/edit-box/EditBox-android.cpp
Normal file
118
cocos/ui/edit-box/EditBox-android.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
641
cocos/ui/edit-box/EditBox-ios.mm
Normal file
641
cocos/ui/edit-box/EditBox-ios.mm
Normal 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
|
||||
40
cocos/ui/edit-box/EditBox-linux.cpp
Normal file
40
cocos/ui/edit-box/EditBox-linux.cpp
Normal 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
|
||||
309
cocos/ui/edit-box/EditBox-mac.mm
Normal file
309
cocos/ui/edit-box/EditBox-mac.mm
Normal 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
|
||||
119
cocos/ui/edit-box/EditBox-ohos.cpp
Normal file
119
cocos/ui/edit-box/EditBox-ohos.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
107
cocos/ui/edit-box/EditBox-openharmony.cpp
Normal file
107
cocos/ui/edit-box/EditBox-openharmony.cpp
Normal 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
|
||||
|
||||
|
||||
47
cocos/ui/edit-box/EditBox-openharmony.h
Normal file
47
cocos/ui/edit-box/EditBox-openharmony.h
Normal 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
|
||||
|
||||
|
||||
283
cocos/ui/edit-box/EditBox-win32.cpp
Normal file
283
cocos/ui/edit-box/EditBox-win32.cpp
Normal 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
|
||||
73
cocos/ui/edit-box/EditBox.h
Normal file
73
cocos/ui/edit-box/EditBox.h
Normal 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
|
||||
474
cocos/ui/videoplayer/VideoPlayer-ios.mm
Normal file
474
cocos/ui/videoplayer/VideoPlayer-ios.mm
Normal 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
|
||||
227
cocos/ui/videoplayer/VideoPlayer-java.cpp
Normal file
227
cocos/ui/videoplayer/VideoPlayer-java.cpp
Normal 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
|
||||
194
cocos/ui/videoplayer/VideoPlayer.h
Normal file
194
cocos/ui/videoplayer/VideoPlayer.h
Normal 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
|
||||
208
cocos/ui/webview/WebView-inl.h
Normal file
208
cocos/ui/webview/WebView-inl.h
Normal 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
247
cocos/ui/webview/WebView.h
Normal 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
|
||||
300
cocos/ui/webview/WebViewImpl-android.cpp
Normal file
300
cocos/ui/webview/WebViewImpl-android.cpp
Normal 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
|
||||
92
cocos/ui/webview/WebViewImpl-ios.h
Normal file
92
cocos/ui/webview/WebViewImpl-ios.h
Normal 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_ */
|
||||
474
cocos/ui/webview/WebViewImpl-ios.mm
Normal file
474
cocos/ui/webview/WebViewImpl-ios.mm
Normal 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
|
||||
94
cocos/ui/webview/WebViewImpl-java.h
Normal file
94
cocos/ui/webview/WebViewImpl-java.h
Normal 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
|
||||
302
cocos/ui/webview/WebViewImpl-ohos.cpp
Normal file
302
cocos/ui/webview/WebViewImpl-ohos.cpp
Normal 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
|
||||
313
cocos/ui/webview/WebViewImpl-openharmony.cpp
Normal file
313
cocos/ui/webview/WebViewImpl-openharmony.cpp
Normal 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
|
||||
41
cocos/ui/webview/WebViewImpl-openharmony.h
Normal file
41
cocos/ui/webview/WebViewImpl-openharmony.h
Normal 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
|
||||
Reference in New Issue
Block a user