初始化

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,296 @@
/****************************************************************************
Copyright (c) 2014 Chukong Technologies Inc.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "VideoPlayer.h"
#include "base/CCLog.h"
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include <unordered_map>
#include <stdlib.h>
#include <jni.h>
#include <string>
#include "platform/CCApplication.h"
#include "platform/android/jni/JniHelper.h"
#include "platform/CCFileUtils.h"
#include "renderer/gfx/Texture2D.h"
//-----------------------------------------------------------------------------------------------------------
static const std::string videoHelperClassName = "org/cocos2dx/lib/Cocos2dxVideoHelper";
USING_NS_CC;
static void executeVideoCallback(int index,int event);
#define QUIT_FULLSCREEN 1000
extern "C" {
void Java_org_cocos2dx_lib_Cocos2dxVideoHelper_nativeExecuteVideoCallback(JNIEnv * env, jobject obj, jint index,jint event) {
executeVideoCallback(index,event);
}
}
int createVideoWidgetJNI()
{
JniMethodInfo t;
int ret = -1;
if (JniHelper::getStaticMethodInfo(t, videoHelperClassName.c_str(), "createVideoWidget", "()I")) {
ret = t.env->CallStaticIntMethod(t.classID, t.methodID);
t.env->DeleteLocalRef(t.classID);
}
return ret;
}
//-----------------------------------------------------------------------------------------------------------
static std::unordered_map<int, VideoPlayer*> s_allVideoPlayers;
static jobject s_jobj = nullptr;
VideoPlayer::VideoPlayer()
: _videoPlayerIndex(-1)
, _fullScreenEnabled(false)
, _fullScreenDirty(false)
, _keepAspectRatioEnabled(false)
, _videoPixels(nullptr)
, _texDataSize(0)
, _maxDataLen(0)
{
_videoPlayerIndex = createVideoWidgetJNI();
s_allVideoPlayers[_videoPlayerIndex] = this;
#if CC_VIDEOPLAYER_DEBUG_DRAW
_debugDrawNode = DrawNode::create();
addChild(_debugDrawNode);
#endif
if(s_jobj == nullptr) {
s_jobj = JniHelper::getEnv()->NewGlobalRef(JniHelper::newObject(videoHelperClassName.c_str()));
}
}
VideoPlayer::~VideoPlayer()
{
s_allVideoPlayers.erase(_videoPlayerIndex);
JniHelper::callStaticVoidMethod(videoHelperClassName, "removeVideoWidget", _videoPlayerIndex);
if (_videoPixels != nullptr) free(_videoPixels);
}
void VideoPlayer::setURL(const std::string& videoUrl)
{
if (videoUrl.find("://") == std::string::npos)
{
_videoURL = FileUtils::getInstance()->fullPathForFilename(videoUrl);
_videoSource = VideoPlayer::Source::FILENAME;
}
else
{
_videoURL = videoUrl;
_videoSource = VideoPlayer::Source::URL;
}
JniHelper::callStaticVoidMethod(videoHelperClassName, "setVideoUrl", _videoPlayerIndex,
(int)_videoSource,_videoURL);
}
void VideoPlayer::VideoPlayer::setFrame(float x, float y, float width, float height)
{
JniHelper::callStaticVoidMethod(videoHelperClassName, "setVideoRect", _videoPlayerIndex,
(int)x, (int)y, (int)width, (int)height);
}
void VideoPlayer::setFullScreenEnabled(bool enabled)
{
if (_fullScreenEnabled != enabled)
{
_fullScreenEnabled = enabled;
JniHelper::callStaticVoidMethod(videoHelperClassName, "setFullScreenEnabled", _videoPlayerIndex, enabled);
}
}
void VideoPlayer::setKeepAspectRatioEnabled(bool enable)
{
if (_keepAspectRatioEnabled != enable)
{
_keepAspectRatioEnabled = enable;
JniHelper::callStaticVoidMethod(videoHelperClassName, "setVideoKeepRatioEnabled", _videoPlayerIndex, enable);
}
}
void VideoPlayer::play()
{
if (! _videoURL.empty())
{
JniHelper::callStaticVoidMethod(videoHelperClassName, "startVideo", _videoPlayerIndex);
}
}
void VideoPlayer::pause()
{
if (! _videoURL.empty())
{
JniHelper::callStaticVoidMethod(videoHelperClassName, "pauseVideo", _videoPlayerIndex);
}
}
void VideoPlayer::stop()
{
if (! _videoURL.empty())
{
JniHelper::callStaticVoidMethod(videoHelperClassName, "stopVideo", _videoPlayerIndex);
}
}
void VideoPlayer::seekTo(float sec)
{
if (! _videoURL.empty())
{
JniHelper::callStaticVoidMethod(videoHelperClassName, "seekVideoTo", _videoPlayerIndex, int(sec * 1000));
}
}
void VideoPlayer::setVisible(bool visible)
{
JniHelper::callStaticVoidMethod(videoHelperClassName, "setVideoVisible", _videoPlayerIndex, visible);
}
void VideoPlayer::addEventListener(const std::string& name, const VideoPlayer::ccVideoPlayerCallback& callback)
{
_eventCallback[name] = callback;
}
void VideoPlayer::onPlayEvent(int event)
{
if (event == QUIT_FULLSCREEN)
{
_fullScreenEnabled = false;
}
else
{
VideoPlayer::EventType videoEvent = (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 = s_allVideoPlayers.find(index);
if (it != s_allVideoPlayers.end())
{
s_allVideoPlayers[index]->onPlayEvent(event);
}
}
float VideoPlayer::currentTime() const
{
return JniHelper::callStaticFloatMethod(videoHelperClassName, "getCurrentTime", _videoPlayerIndex);
}
float VideoPlayer::duration() const
{
return JniHelper::callStaticFloatMethod(videoHelperClassName, "getDuration", _videoPlayerIndex);
}
void VideoPlayer::getFrame() {
jbyteArray arr = JniHelper::callObjectByteArrayMethod(s_jobj, videoHelperClassName, "getFrame", _videoPlayerIndex);
if (arr == nullptr) return;
jsize len = JniHelper::getEnv()->GetArrayLength(arr);
if (len == 0) return;
_texDataSize = len;
if (len > _maxDataLen) {
_maxDataLen = len;
if(_videoPixels != nullptr) free(_videoPixels);
_videoPixels = (unsigned char*)malloc(len * sizeof(unsigned char));
}
JniHelper::getEnv()->GetByteArrayRegion(arr, 0, len, reinterpret_cast<jbyte*>(_videoPixels));
JniHelper::getEnv()->DeleteLocalRef(arr);
}
int VideoPlayer::getFrameChannel() const {
return (int)JniHelper::callObjectFloatMethod(s_jobj, videoHelperClassName, "getFrameChannel", _videoPlayerIndex);
}
int VideoPlayer::getFrameWidth() const {
return (int)JniHelper::callObjectFloatMethod(s_jobj, videoHelperClassName, "getFrameWidth", _videoPlayerIndex);
}
int VideoPlayer::getFrameHeight() const {
return (int)JniHelper::callObjectFloatMethod(s_jobj, videoHelperClassName, "getFrameHeight", _videoPlayerIndex);
}
int VideoPlayer::getVideoTexDataSize() const {
return _texDataSize;
}
void VideoPlayer::pushFrameDataToTexture2D(cocos2d::renderer::Texture* tex) const {
if(tex == nullptr) log("Can't find texture!");
else {
if(_videoPixels != nullptr && getFrameWidth() > 0 && getFrameHeight() > 0) {
renderer::Texture::SubImageOption opt(0, 0, getFrameWidth(), getFrameHeight(), 0, false,
false);
opt.imageData = _videoPixels;
((cocos2d::renderer::Texture2D*)tex)->updateSubImage(opt);
}
}
}
void VideoPlayer::setShowRawFrame(bool show) const {
JniHelper::callObjectVoidMethod(s_jobj, videoHelperClassName, "setShowRawFrame", _videoPlayerIndex, show);
}
void VideoPlayer::update() {
//test
}
#endif

View File

@@ -0,0 +1,631 @@
/****************************************************************************
Copyright (c) 2014-2016 Chukong Technologies Inc.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "VideoPlayer.h"
USING_NS_CC;
// No Available on tvOS
#if CC_TARGET_PLATFORM == CC_PLATFORM_IOS && !defined(CC_TARGET_OS_TVOS)
//-------------------------------------------------------------------------------------
#import <AVKit/AVPlayerViewController.h>
#import <CoreMedia/CMTime.h>
#import <CoreVideo/CVPixelBuffer.h>
#include "platform/CCApplication.h"
#include "platform/ios/CCEAGLView-ios.h"
#include "platform/CCFileUtils.h"
#include "renderer/gfx/Texture2D.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 :(std::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) setFullScreenEnabled:(BOOL) enabled;
- (void) showPlaybackControls:(BOOL) value;
- (BOOL) isFullScreenEnabled;
- (void) cleanup;
-(id) init:(void*) videoPlayer;
- (unsigned char*) getFrame;
- (bool) copyFinished;
- (int) getFrameWidth;
- (int) getFrameHeight;
- (int) getFrameDataSize;
- (int) getBitCount;
- (void) setShowRaw:(BOOL) show;
- (void) clearBuffers;
-(void) videoFinished:(NSNotification*) notification;
@end
@implementation UIVideoViewWrapperIos
{
int _left;
int _top;
int _width;
int _height;
bool _keepRatioEnabled;
bool _fullscreen;
CGRect _restoreRect;
PlayerbackState _state;
VideoPlayer* _videoPlayer;
int _frameWidth;
int _frameHeight;
int _frameDataSize;
int _frameBitCount;
unsigned char* _videoPixels;
bool _finishCopy;
}
-(id)init:(void*)videoPlayer
{
if (self = [super init]) {
_keepRatioEnabled = 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;
_frameWidth = 0;
_frameHeight = 0;
_frameDataSize = 0;
_frameBitCount = 1;
_videoPixels = nullptr;
_finishCopy = false;
[self setShowRaw:YES];
}
-(void) dealloc
{
if(_videoPixels != nullptr) free(_videoPixels);
_videoPlayer = nullptr;
[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)];
}
-(unsigned char*) getFrame
{
UInt32 type = kCVPixelFormatType_32BGRA;
AVPlayerItem* item = self.playerController.player.currentItem;
if(item.outputs.count == 0) {
NSDictionary* settings = @{ (id)kCVPixelBufferPixelFormatTypeKey : @(type) };
AVPlayerItemVideoOutput* output = [[AVPlayerItemVideoOutput alloc] initWithPixelBufferAttributes:settings];
[item addOutput:output];
}
AVPlayerItemOutput* output = item.outputs[0];
CMTime time = self.playerController.player.currentTime;
CVPixelBufferRef pixelBuffer = [output copyPixelBufferForItemTime:time itemTimeForDisplay:nil];
_frameBitCount = 4;
_frameHeight = (int)CVPixelBufferGetHeight(pixelBuffer);
_frameWidth = (int)CVPixelBufferGetBytesPerRow(pixelBuffer) / _frameBitCount;
_frameDataSize = (int)CVPixelBufferGetDataSize(pixelBuffer);
if(_videoPixels == nullptr && _frameWidth > 0 && _frameHeight > 0) {
_videoPixels = (unsigned char*)calloc(_frameDataSize, 1);
}
CVPixelBufferLockBaseAddress(pixelBuffer, 0);
void* pixels = CVPixelBufferGetBaseAddress(pixelBuffer);
if(_videoPixels != nullptr && pixels != nullptr) {
memcpy(_videoPixels, pixels, _frameDataSize);
_finishCopy = true;
}
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
return _videoPixels;
}
-(bool) copyFinished
{
return _finishCopy;
}
-(int) getFrameWidth
{
return _frameWidth;
}
-(int) getFrameHeight
{
return _frameHeight;
}
-(int) getFrameDataSize
{
return _frameDataSize;
}
-(int) getBitCount
{
return _frameBitCount;
}
-(void) setShowRaw:(BOOL) show
{
if(show == YES) {
self.playerController.view.alpha = 1;
} else {
self.playerController.view.alpha = 0;
}
}
-(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 :(std::string &)videoUrl
{
[self clearBuffers];
[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) play
{
if (self.playerController.player && ![self isPlaying] ) {
[self.playerController.player play];
_state = PlayerbackStatePlaying;
_videoPlayer->onPlayEvent((int)VideoPlayer::EventType::PLAYING);
}
}
-(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;
// stop() will be invoked in dealloc, which is invoked by _videoPlayer's destructor,
// so do't send the message when _videoPlayer is being deleted.
if (_videoPlayer)
_videoPlayer->onPlayEvent((int)VideoPlayer::EventType::STOPPED);
}
}
// Private functions
-(void) clearBuffers
{
if(_videoPixels != nullptr) {
free(_videoPixels);
_videoPixels = nullptr;
}
AVPlayerItem* item = self.playerController.player.currentItem;
if(item.outputs.count > 0) {
AVPlayerItemOutput* output = item.outputs[0];
[item removeOutput:output];
[output dealloc];
}
}
-(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);
_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 eaglview = (CCEAGLView*)cocos2d::Application::getInstance()->getView();
[eaglview 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
_videoPixels = nullptr;
_texDataSize = 0;
_maxDataLen = 0;
}
VideoPlayer::~VideoPlayer()
{
if(_videoView)
{
[((UIVideoViewWrapperIos*)_videoView) dealloc];
}
}
void VideoPlayer::setURL(const std::string& videoUrl)
{
if (videoUrl.find("://") == std::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::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 std::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 eaglview = (CCEAGLView*)cocos2d::Application::getInstance()->getView();
auto scaleFactor = [eaglview contentScaleFactor];
[((UIVideoViewWrapperIos*)_videoView) setFrame:x/scaleFactor
:y/scaleFactor
:width/scaleFactor
:height/scaleFactor];
}
void VideoPlayer::getFrame()
{
_videoPixels = [((UIVideoViewWrapperIos*)_videoView) getFrame];
}
int VideoPlayer::getFrameChannel() const
{
int res = VideoPlayer::PX_BGRA;
return res;
}
int VideoPlayer::getFrameWidth() const
{
return [((UIVideoViewWrapperIos*)_videoView) getFrameWidth];
}
int VideoPlayer::getFrameHeight() const
{
return [((UIVideoViewWrapperIos*)_videoView) getFrameHeight];
}
int VideoPlayer::getVideoTexDataSize() const
{
return [((UIVideoViewWrapperIos*)_videoView) getFrameDataSize];
}
void VideoPlayer::update()
{
}
void VideoPlayer::pushFrameDataToTexture2D(cocos2d::renderer::Texture* tex) const
{
if(tex == nullptr) {
printf("Can't find texture!\n");
} else {
bool finshCopy = [((UIVideoViewWrapperIos*)_videoView) copyFinished];
if(_videoPixels != nullptr && getFrameWidth() > 0 && getFrameHeight() > 0 && finshCopy) {
renderer::Texture::SubImageOption opt(0, 0, getFrameWidth(), getFrameHeight(), 0, false, false);
opt.imageData = _videoPixels;
((cocos2d::renderer::Texture2D*)tex)->updateSubImage(opt);
}
}
}
void VideoPlayer::setShowRawFrame(bool show) const
{
if(show)
[((UIVideoViewWrapperIos*)_videoView) setShowRaw: YES];
else
[((UIVideoViewWrapperIos*)_videoView) setShowRaw: NO];
}
#endif

View File

@@ -0,0 +1,211 @@
/****************************************************************************
Copyright (c) 2014-2016 Chukong Technologies Inc.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#pragma once
#include "base/ccMacros.h"
#include "base/CCData.h"
#include "base/CCRef.h"
#include <functional>
#include <string>
#include <map>
#include "renderer/gfx/Texture2D.h"
#ifndef OBJC_CLASS
#ifdef __OBJC__
#define OBJC_CLASS(name) @class name
#else
#define OBJC_CLASS(name) class name
#endif
#endif // OBJC_CLASS
NS_CC_BEGIN
/**
* @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 : public Ref
{
public:
/**
* Videoplayer play event type.
*/
enum class EventType
{
PLAYING = 0,
PAUSED,
STOPPED,
COMPLETED,
META_LOADED,
CLICKED,
READY_TO_PLAY
};
VideoPlayer();
/**
* A callback which will be called after specific VideoPlayer event happens.
*/
typedef std::function<void()> ccVideoPlayerCallback;
/**
* Sets a URL as a video source for VideoPlayer.
*/
virtual void setURL(const std::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;
/**
* Get frame rgba pixel as bytes
*/
void getFrame();
int getFrameChannel() const;
int getFrameWidth() const;
int getFrameHeight() const;
int getVideoTexDataSize() const;
void update();
void pushFrameDataToTexture2D(cocos2d::renderer::Texture* tex) const;
void setShowRawFrame(bool show) 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 std::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);
protected:
virtual ~VideoPlayer();
protected:
enum class Source
{
FILENAME = 0,
URL
};
bool _isVisible;
bool _fullScreenDirty;
bool _fullScreenEnabled;
bool _keepAspectRatioEnabled;
std::string _videoURL;
Source _videoSource;
int _videoPlayerIndex;
std::map<std::string, ccVideoPlayerCallback> _eventCallback;
void *_videoView;
int _texDataSize;
private:
unsigned char* _videoPixels;
int _maxDataLen;
static const int PX_RGBA = 3;
static const int PX_RGB565 = 6;
static const int PX_BGRA = 7;
};
NS_CC_END

View File

@@ -0,0 +1,173 @@
/****************************************************************************
Copyright (c) 2014-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "WebView.h"
#include "platform/CCFileUtils.h"
#include "platform/CCPlatformConfig.h"
#if CC_TARGET_PLATFORM == CC_PLATFORM_IOS && !defined(CC_PLATFORM_OS_TVOS)
#include "WebViewImpl-ios.h"
#elif CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
#include "WebViewImpl-android.h"
#else
static_assert(false, "WebView only supported on iOS & Android");
#endif
NS_CC_BEGIN
WebView::WebView()
: _impl(new WebViewImpl(this)),
_onJSCallback(nullptr),
_onShouldStartLoading(nullptr),
_onDidFinishLoading(nullptr),
_onDidFailLoading(nullptr) {
}
WebView::~WebView() {
CC_SAFE_DELETE(_impl);
}
WebView *WebView::create() {
auto webView = new(std::nothrow) WebView();
if (webView) {
webView->autorelease();
return webView;
}
CC_SAFE_DELETE(webView);
return nullptr;
}
void WebView::setJavascriptInterfaceScheme(const std::string &scheme) {
_impl->setJavascriptInterfaceScheme(scheme);
}
void WebView::loadData(const cocos2d::Data &data,
const std::string &MIMEType,
const std::string &encoding,
const std::string &baseURL) {
_impl->loadData(data, MIMEType, encoding, baseURL);
}
void WebView::loadHTMLString(const std::string &string, const std::string &baseURL) {
_impl->loadHTMLString(string, baseURL);
}
void WebView::loadURL(const std::string &url) {
_impl->loadURL(url);
}
void WebView::loadFile(const std::string &fileName) {
_impl->loadFile(fileName);
}
void WebView::stopLoading() {
_impl->stopLoading();
}
void WebView::reload() {
_impl->reload();
}
bool WebView::canGoBack() {
return _impl->canGoBack();
}
bool WebView::canGoForward() {
return _impl->canGoForward();
}
void WebView::goBack() {
_impl->goBack();
}
void WebView::goForward() {
_impl->goForward();
}
void WebView::evaluateJS(const std::string &js) {
_impl->evaluateJS(js);
}
void WebView::setScalesPageToFit(bool const scalesPageToFit) {
_impl->setScalesPageToFit(scalesPageToFit);
}
void WebView::setVisible(bool visible) {
_impl->setVisible(visible);
}
void WebView::setFrame(float x, float y, float width, float height) {
_impl->setFrame(x, y, width, height);
}
void WebView::setBounces(bool bounces) {
_impl->setBounces(bounces);
}
void WebView::setBackgroundTransparent(bool isTransparent) {
_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 std::string &url)> &callback) {
_onShouldStartLoading = callback;
}
void WebView::setOnJSCallback(const ccWebViewCallback &callback) {
_onJSCallback = callback;
}
std::function<bool(WebView
*sender,
const std::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 cocos2d
/// @endcond

View File

@@ -0,0 +1,246 @@
/****************************************************************************
Copyright (c) 2014-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#pragma once
#include <functional>
#include "base/ccMacros.h"
#include "base/CCRef.h"
#include "base/CCData.h"
#include "base/ccConfig.h"
/**
* @addtogroup ui
* @{
*/
NS_CC_BEGIN
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 : public Ref {
public:
/**
* Allocates and initializes a WebView.
*/
static WebView *create();
/**
* Set javascript interface scheme.
*
* @see WebView::setOnJSCallback()
*/
void setJavascriptInterfaceScheme(const std::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 cocos2d::Data &data,
const std::string &MIMEType,
const std::string &encoding,
const std::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 std::string &string, const std::string &baseURL = "");
/**
* Loads the given URL.
*
* @param url Content URL.
*/
void loadURL(const std::string &url);
/**
* Loads the given fileName.
*
* @param fileName Content fileName.
*/
void loadFile(const std::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 std::string &js);
/**
* Set WebView should support zooming. The default value is false.
*/
void setScalesPageToFit(const 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 std::string &url)> &callback);
/**
* A callback which will be called when a WebView event happens.
*/
typedef std::function<void(WebView *sender, const std::string &url)> ccWebViewCallback;
/**
* 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 std::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 std::string &url)> _onShouldStartLoading;
ccWebViewCallback _onDidFinishLoading;
ccWebViewCallback _onDidFailLoading;
ccWebViewCallback _onJSCallback;
CC_CONSTRUCTOR_ACCESS:
/**
* Default constructor.
*/
WebView();
/**
* Default destructor.
*/
virtual ~WebView();
private:
WebViewImpl *_impl;
friend class WebViewImpl;
};
NS_CC_END

View File

@@ -0,0 +1,290 @@
/****************************************************************************
Copyright (c) 2014-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include <cstdlib>
#include <string>
#include <unordered_map>
#include "WebView-inl.h"
#include "platform/CCFileUtils.h"
#include "platform/android/jni/JniHelper.h"
static const std::string className = "org/cocos2dx/lib/Cocos2dxWebViewHelper";
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,"",__VA_ARGS__)
static const std::string s_defaultBaseUrl = "file:///android_asset/";
static const std::string s_sdRootBaseUrl = "file://";
static std::string getFixedBaseUrl(const std::string &baseUrl) {
std::string fixedBaseUrl;
if (baseUrl.empty()) {
fixedBaseUrl = s_defaultBaseUrl;
} else if (baseUrl.find(s_sdRootBaseUrl) != std::string::npos) {
fixedBaseUrl = baseUrl;
} else if (baseUrl.c_str()[0] != '/') {
if (baseUrl.find("assets/") == 0) {
fixedBaseUrl = s_defaultBaseUrl + baseUrl.c_str()[7];
} else {
fixedBaseUrl = s_defaultBaseUrl + baseUrl;
}
} else {
fixedBaseUrl = s_sdRootBaseUrl + baseUrl;
}
if (fixedBaseUrl.c_str()[fixedBaseUrl.length() - 1] != '/') {
fixedBaseUrl += "/";
}
return fixedBaseUrl;
}
extern "C" {
/*
* Class: org_cocos2dx_lib_Cocos2dxWebViewHelper
* Method: shouldStartLoading
* Signature: (ILjava/lang/String;)Z
*/
JNIEXPORT jboolean JNICALL
Java_org_cocos2dx_lib_Cocos2dxWebViewHelper_shouldStartLoading(JNIEnv *env, jclass, jint index,
jstring jurl) {
auto charUrl = env->GetStringUTFChars(jurl, NULL);
std::string url = charUrl;
env->ReleaseStringUTFChars(jurl, charUrl);
return cocos2d::WebViewImpl::shouldStartLoading(index, url);
}
/*
* Class: org_cocos2dx_lib_Cocos2dxWebViewHelper
* Method: didFinishLoading
* Signature: (ILjava/lang/String;)V
*/
JNIEXPORT void JNICALL
Java_org_cocos2dx_lib_Cocos2dxWebViewHelper_didFinishLoading(JNIEnv *env, jclass, jint index,
jstring jurl) {
// LOGD("didFinishLoading");
auto charUrl = env->GetStringUTFChars(jurl, NULL);
std::string url = charUrl;
env->ReleaseStringUTFChars(jurl, charUrl);
cocos2d::WebViewImpl::didFinishLoading(index, url);
}
/*
* Class: org_cocos2dx_lib_Cocos2dxWebViewHelper
* Method: didFailLoading
* Signature: (ILjava/lang/String;)V
*/
JNIEXPORT void JNICALL
Java_org_cocos2dx_lib_Cocos2dxWebViewHelper_didFailLoading(JNIEnv *env, jclass, jint index,
jstring jurl) {
// LOGD("didFailLoading");
auto charUrl = env->GetStringUTFChars(jurl, NULL);
std::string url = charUrl;
env->ReleaseStringUTFChars(jurl, charUrl);
cocos2d::WebViewImpl::didFailLoading(index, url);
}
/*
* Class: org_cocos2dx_lib_Cocos2dxWebViewHelper
* Method: onJsCallback
* Signature: (ILjava/lang/String;)V
*/
JNIEXPORT void JNICALL
Java_org_cocos2dx_lib_Cocos2dxWebViewHelper_onJsCallback(JNIEnv *env, jclass, jint index,
jstring jmessage) {
// LOGD("jsCallback");
auto charMessage = env->GetStringUTFChars(jmessage, NULL);
std::string message = charMessage;
env->ReleaseStringUTFChars(jmessage, charMessage);
cocos2d::WebViewImpl::onJsCallback(index, message);
}
}
namespace {
int createWebViewJNI() {
cocos2d::JniMethodInfo t;
if (cocos2d::JniHelper::getStaticMethodInfo(t, className.c_str(), "createWebView", "()I")) {
// LOGD("error: %s,%d",__func__,__LINE__);
jint viewTag = t.env->CallStaticIntMethod(t.classID, t.methodID);
t.env->DeleteLocalRef(t.classID);
return viewTag;
}
return -1;
}
std::string getUrlStringByFileName(const std::string &fileName) {
// LOGD("error: %s,%d",__func__,__LINE__);
const std::string basePath("file:///android_asset/");
std::string fullPath = cocos2d::FileUtils::getInstance()->fullPathForFilename(fileName);
const std::string assetsPath("assets/");
std::string urlString;
if (fullPath.find(assetsPath) != std::string::npos) {
urlString = fullPath.replace(fullPath.find_first_of(assetsPath), assetsPath.length(),
basePath);
} else {
urlString = fullPath;
}
return urlString;
}
} // namespace
namespace cocos2d {
static std::unordered_map<int, WebViewImpl *> s_WebViewImpls;
WebViewImpl::WebViewImpl(WebView *webView) : _viewTag(-1), _webView(webView) {
_viewTag = createWebViewJNI();
s_WebViewImpls[_viewTag] = this;
}
WebViewImpl::~WebViewImpl() {
JniHelper::callStaticVoidMethod(className, "removeWebView", _viewTag);
s_WebViewImpls.erase(_viewTag);
}
void WebViewImpl::loadData(const Data &data, const std::string &MIMEType,
const std::string &encoding, const std::string &baseURL) {
std::string dataString(reinterpret_cast<char *>(data.getBytes()),
static_cast<unsigned int>(data.getSize()));
JniHelper::callStaticVoidMethod(className, "setJavascriptInterfaceScheme", _viewTag,
dataString, MIMEType, encoding, baseURL);
}
void WebViewImpl::loadHTMLString(const std::string &string, const std::string &baseURL) {
JniHelper::callStaticVoidMethod(className, "loadHTMLString", _viewTag, string, baseURL);
}
void WebViewImpl::loadURL(const std::string &url) {
JniHelper::callStaticVoidMethod(className, "loadUrl", _viewTag, url);
}
void WebViewImpl::loadFile(const std::string &fileName) {
auto fullPath = getUrlStringByFileName(fileName);
JniHelper::callStaticVoidMethod(className, "loadFile", _viewTag, fullPath);
}
void WebViewImpl::stopLoading() {
JniHelper::callStaticVoidMethod(className, "stopLoading", _viewTag);
}
void WebViewImpl::reload() {
JniHelper::callStaticVoidMethod(className, "reload", _viewTag);
}
bool WebViewImpl::canGoBack() {
return JniHelper::callStaticBooleanMethod(className, "canGoBack", _viewTag);
}
bool WebViewImpl::canGoForward() {
return JniHelper::callStaticBooleanMethod(className, "canGoForward", _viewTag);
}
void WebViewImpl::goBack() {
JniHelper::callStaticVoidMethod(className, "goBack", _viewTag);
}
void WebViewImpl::goForward() {
JniHelper::callStaticVoidMethod(className, "goForward", _viewTag);
}
void WebViewImpl::setJavascriptInterfaceScheme(const std::string &scheme) {
JniHelper::callStaticVoidMethod(className, "setJavascriptInterfaceScheme", _viewTag,
scheme);
}
void WebViewImpl::evaluateJS(const std::string &js) {
JniHelper::callStaticVoidMethod(className, "evaluateJS", _viewTag, js);
}
void WebViewImpl::setScalesPageToFit(const bool scalesPageToFit) {
JniHelper::callStaticVoidMethod(className, "setScalesPageToFit", _viewTag, scalesPageToFit);
}
bool WebViewImpl::shouldStartLoading(const int viewTag, const std::string &url) {
bool allowLoad = true;
auto it = s_WebViewImpls.find(viewTag);
if (it != s_WebViewImpls.end()) {
auto webView = it->second->_webView;
if (webView->_onShouldStartLoading) {
allowLoad = webView->_onShouldStartLoading(webView, url);
}
}
return allowLoad;
}
void WebViewImpl::didFinishLoading(const int viewTag, const std::string &url) {
auto it = s_WebViewImpls.find(viewTag);
if (it != s_WebViewImpls.end()) {
auto webView = it->second->_webView;
if (webView->_onDidFinishLoading) {
webView->_onDidFinishLoading(webView, url);
}
}
}
void WebViewImpl::didFailLoading(const int viewTag, const std::string &url) {
auto it = s_WebViewImpls.find(viewTag);
if (it != s_WebViewImpls.end()) {
auto webView = it->second->_webView;
if (webView->_onDidFailLoading) {
webView->_onDidFailLoading(webView, url);
}
}
}
void WebViewImpl::onJsCallback(const int viewTag, const std::string &message) {
auto it = s_WebViewImpls.find(viewTag);
if (it != s_WebViewImpls.end()) {
auto webView = it->second->_webView;
if (webView->_onJSCallback) {
webView->_onJSCallback(webView, message);
}
}
}
void WebViewImpl::setVisible(bool visible) {
JniHelper::callStaticVoidMethod(className, "setVisible", _viewTag, visible);
}
void WebViewImpl::setFrame(float x, float y, float width, float height) {
JniHelper::callStaticVoidMethod(className, "setWebViewRect", _viewTag,
(int) x, (int) y, (int) width, (int) height);
}
void WebViewImpl::setBounces(bool bounces) {
// empty function as this was mainly a fix for iOS
}
void WebViewImpl::setBackgroundTransparent(bool isTransparent) {
JniHelper::callStaticVoidMethod(className, "setBackgroundTransparent", _viewTag,
isTransparent);
}
} //namespace cocos2d

View File

@@ -0,0 +1,94 @@
/****************************************************************************
Copyright (c) 2014-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#pragma once
#include <iosfwd>
#include <stdint.h>
#include "base/ccMacros.h"
#include "base/CCData.h"
NS_CC_BEGIN
class WebView;
class WebViewImpl {
public:
WebViewImpl(cocos2d::WebView *webView);
virtual ~WebViewImpl();
void setJavascriptInterfaceScheme(const std::string &scheme);
void loadData(const cocos2d::Data &data, const std::string &MIMEType,
const std::string &encoding, const std::string &baseURL);
void loadHTMLString(const std::string &string, const std::string &baseURL);
void loadURL(const std::string &url);
void loadFile(const std::string &fileName);
void stopLoading();
void reload();
bool canGoBack();
bool canGoForward();
void goBack();
void goForward();
void evaluateJS(const std::string &js);
void setScalesPageToFit(const bool scalesPageToFit);
virtual void setVisible(bool visible);
virtual void setFrame(float x, float y, float width, float height);
void setBounces(bool bounces);
void setBackgroundTransparent(bool isTransparent);
static bool shouldStartLoading(const int viewTag, const std::string &url);
static void didFinishLoading(const int viewTag, const std::string &url);
static void didFailLoading(const int viewTag, const std::string &url);
static void onJsCallback(const int viewTag, const std::string &message);
private:
int _viewTag;
WebView *_webView;
};
NS_CC_END

View File

@@ -0,0 +1,90 @@
/****************************************************************************
Copyright (c) 2014-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#ifndef __COCOS2D_UI_WEBVIEWIMPL_IOS_H_
#define __COCOS2D_UI_WEBVIEWIMPL_IOS_H_
/// @cond DO_NOT_SHOW
#include <iosfwd>
@class UIWebViewWrapper;
namespace cocos2d {
class Data;
class WebView;
class WebViewImpl {
public:
WebViewImpl(WebView *webView);
virtual ~WebViewImpl();
void setJavascriptInterfaceScheme(const std::string &scheme);
void loadData(const cocos2d::Data &data,
const std::string &MIMEType,
const std::string &encoding,
const std::string &baseURL);
void loadHTMLString(const std::string &string, const std::string &baseURL);
void loadURL(const std::string &url);
void loadFile(const std::string &fileName);
void stopLoading();
void reload();
bool canGoBack();
bool canGoForward();
void goBack();
void goForward();
void evaluateJS(const std::string &js);
void setScalesPageToFit(const bool scalesPageToFit);
virtual void setVisible(bool visible);
virtual void setFrame(float x, float y, float width, float height);
void setBounces(bool bounces);
void setBackgroundTransparent(bool isTransparent);
private:
UIWebViewWrapper *_uiWebViewWrapper;
WebView *_webView;
};
}//namespace cocos2d
/// @endcond
#endif /* __COCOS2D_UI_WEBVIEWIMPL_IOS_H_ */

View File

@@ -0,0 +1,395 @@
/****************************************************************************
Copyright (c) 2014-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "platform/CCPlatformConfig.h"
// Webview not available on tvOS
#if (USE_WEB_VIEW > 0) && (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) && !defined(CC_TARGET_OS_TVOS)
#import <WebKit/WKWebView.h>
#import <WebKit/WKUIDelegate.h>
#import <WebKit/WKNavigationDelegate.h>
#include "WebView-inl.h"
#include "platform/CCApplication.h"
#include "platform/ios/CCEAGLView-ios.h"
#include "platform/CCFileUtils.h"
@interface UIWebViewWrapper : NSObject
@property (nonatomic) std::function<bool(std::string url)> shouldStartLoading;
@property (nonatomic) std::function<void(std::string url)> didFinishLoading;
@property (nonatomic) std::function<void(std::string url)> didFailLoading;
@property (nonatomic) std::function<void(std::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)setFrameWithX:(float)x y:(float)y width:(float)width height:(float)height;
- (void)setJavascriptInterfaceScheme:(const std::string &)scheme;
- (void)loadData:(const std::string &)data MIMEType:(const std::string &)MIMEType textEncodingName:(const std::string &)encodingName baseURL:(const std::string &)baseURL;
- (void)loadHTMLString:(const std::string &)string baseURL:(const std::string &)baseURL;
- (void)loadUrl:(const std::string &)urlString;
- (void)loadFile:(const std::string &)filePath;
- (void)stopLoading;
- (void)reload;
- (void)evaluateJS:(const std::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) {
auto eaglview = (CCEAGLView*)cocos2d::Application::getInstance()->getView();
[eaglview addSubview:self.uiWebView];
}
}
- (void)setVisible:(bool)visible {
self.uiWebView.hidden = !visible;
}
- (void)setBounces:(bool)bounces {
self.uiWebView.scrollView.bounces = bounces;
}
- (void)setFrameWithX:(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 = CGRectMake(x, y, width, height);
}
}
- (void)setJavascriptInterfaceScheme:(const std::string &)scheme {
self.jsScheme = @(scheme.c_str());
}
- (void)loadData:(const std::string &)data MIMEType:(const std::string &)MIMEType textEncodingName:(const std::string &)encodingName baseURL:(const std::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 std::string &)string baseURL:(const std::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 std::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 std::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 std::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;
[rootViewController presentViewController:alertController animated:YES completion:^{}];
}
@end
namespace cocos2d {
WebViewImpl::WebViewImpl(WebView *webView)
: _uiWebViewWrapper([UIWebViewWrapper webViewWrapper]),
_webView(webView) {
[_uiWebViewWrapper retain];
_uiWebViewWrapper.shouldStartLoading = [this](std::string url) {
if (this->_webView->_onShouldStartLoading) {
return this->_webView->_onShouldStartLoading(this->_webView, url);
}
return true;
};
_uiWebViewWrapper.didFinishLoading = [this](std::string url) {
if (this->_webView->_onDidFinishLoading) {
this->_webView->_onDidFinishLoading(this->_webView, url);
}
};
_uiWebViewWrapper.didFailLoading = [this](std::string url) {
if (this->_webView->_onDidFailLoading) {
this->_webView->_onDidFailLoading(this->_webView, url);
}
};
_uiWebViewWrapper.onJsCallback = [this](std::string url) {
if (this->_webView->_onJSCallback) {
this->_webView->_onJSCallback(this->_webView, url);
}
};
}
WebViewImpl::~WebViewImpl(){
[_uiWebViewWrapper release];
_uiWebViewWrapper = nullptr;
}
void WebViewImpl::setJavascriptInterfaceScheme(const std::string &scheme) {
[_uiWebViewWrapper setJavascriptInterfaceScheme:scheme];
}
void WebViewImpl::loadData(const Data &data,
const std::string &MIMEType,
const std::string &encoding,
const std::string &baseURL) {
std::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 std::string &string, const std::string &baseURL) {
[_uiWebViewWrapper loadHTMLString:string baseURL:baseURL];
}
void WebViewImpl::loadURL(const std::string &url) {
[_uiWebViewWrapper loadUrl:url];
}
void WebViewImpl::loadFile(const std::string &fileName) {
auto fullPath = cocos2d::FileUtils::getInstance()->fullPathForFilename(fileName);
[_uiWebViewWrapper loadFile:fullPath];
}
void WebViewImpl::stopLoading() {
[_uiWebViewWrapper stopLoading];
}
void WebViewImpl::reload() {
[_uiWebViewWrapper reload];
}
bool WebViewImpl::canGoBack() {
return _uiWebViewWrapper.canGoBack;
}
bool WebViewImpl::canGoForward() {
return _uiWebViewWrapper.canGoForward;
}
void WebViewImpl::goBack() {
[_uiWebViewWrapper goBack];
}
void WebViewImpl::goForward() {
[_uiWebViewWrapper goForward];
}
void WebViewImpl::evaluateJS(const std::string &js) {
[_uiWebViewWrapper evaluateJS:js];
}
void WebViewImpl::setBounces(bool bounces) {
[_uiWebViewWrapper setBounces:bounces];
}
void WebViewImpl::setScalesPageToFit(const bool scalesPageToFit) {
[_uiWebViewWrapper setScalesPageToFit:scalesPageToFit];
}
void WebViewImpl::setVisible(bool visible){
[_uiWebViewWrapper setVisible:visible];
}
void WebViewImpl::setFrame(float x, float y, float width, float height){
auto eaglview = (CCEAGLView*)cocos2d::Application::getInstance()->getView();
auto scaleFactor = [eaglview contentScaleFactor];
[_uiWebViewWrapper setFrameWithX:x/scaleFactor
y:y/scaleFactor
width:width/scaleFactor
height:height/scaleFactor];
}
void WebViewImpl::setBackgroundTransparent(bool isTransparent){
[_uiWebViewWrapper setBackgroundTransparent:isTransparent];
}
} //namespace cocos2d
#endif // (USE_WEB_VIEW > 0) && (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) && !defined(CC_TARGET_OS_TVOS)