diff --git a/README.md b/README.md index 66a0878..c0b1eb8 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,9 @@ https://github.com/defold/extension-webview/archive/master.zip We recommend using a link to a zip file of a [specific release](https://github.com/defold/extension-webview/releases). +## Usage +Full documentation: https://www.defold.com/extension-webview + ## API reference https://defold.com/extension-webview/api/ diff --git a/docs/index.md b/docs/index.md index d0f6876..8296c38 100644 --- a/docs/index.md +++ b/docs/index.md @@ -10,9 +10,9 @@ The WebView extension provides a unified API to load and display web pages as ov ## Installing the extension To use this library in your Defold project, add the following URL to your `game.project` dependencies: -https://github.com/defold/extension-websocket/archive/master.zip +https://github.com/defold/extension-webview/archive/master.zip -We recommend using a link to a zip file of a [specific release](https://github.com/defold/extension-websocket/releases). +We recommend using a link to a zip file of a [specific release](https://github.com/defold/extension-webview/releases). ## Opening a WebView diff --git a/main/main.gui b/main/main.gui index 1fa1cee..dcfded5 100644 --- a/main/main.gui +++ b/main/main.gui @@ -8,7 +8,7 @@ background_color { nodes { position { x: 320.0 - y: 125.0 + y: 195.0 z: 0.0 w: 1.0 } @@ -169,7 +169,7 @@ nodes { nodes { position { x: 215.0 - y: 55.0 + y: 125.0 z: 0.0 w: 1.0 } @@ -330,7 +330,7 @@ nodes { nodes { position { x: 425.0 - y: 55.0 + y: 125.0 z: 0.0 w: 1.0 } @@ -491,7 +491,7 @@ nodes { nodes { position { x: 320.0 - y: 265.0 + y: 335.0 z: 0.0 w: 1.0 } @@ -652,7 +652,7 @@ nodes { nodes { position { x: 320.0 - y: 195.0 + y: 265.0 z: 0.0 w: 1.0 } @@ -810,6 +810,328 @@ nodes { text_leading: 1.0 text_tracking: 0.0 } +nodes { + position { + x: 215.0 + y: 55.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.5 + y: 0.5 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEMPLATE + id: "button_create" + layer: "" + inherit_alpha: true + alpha: 1.0 + template: "/dirtylarry/button.gui" + template_node_child: false +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 380.0 + y: 120.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "button/button_normal" + id: "button_create/larrybutton" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "button_create" + layer: "" + inherit_alpha: true + slice9 { + x: 32.0 + y: 32.0 + z: 32.0 + w: 32.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + overridden_fields: 4 + template_node_child: true + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.5 + y: 1.5 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Create" + font: "larryfont" + id: "button_create/larrylabel" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "button_create/larrybutton" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 0.0 + shadow_alpha: 1.0 + overridden_fields: 3 + overridden_fields: 8 + overridden_fields: 31 + template_node_child: true + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 425.0 + y: 55.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.5 + y: 0.5 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEMPLATE + id: "button_destroy" + layer: "" + inherit_alpha: true + alpha: 1.0 + template: "/dirtylarry/button.gui" + template_node_child: false +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 380.0 + y: 120.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "button/button_normal" + id: "button_destroy/larrybutton" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "button_destroy" + layer: "" + inherit_alpha: true + slice9 { + x: 32.0 + y: 32.0 + z: 32.0 + w: 32.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + overridden_fields: 4 + template_node_child: true + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.5 + y: 1.5 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Destroy" + font: "larryfont" + id: "button_destroy/larrylabel" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "button_destroy/larrybutton" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 0.0 + shadow_alpha: 1.0 + overridden_fields: 3 + overridden_fields: 8 + overridden_fields: 31 + template_node_child: true + text_leading: 1.0 + text_tracking: 0.0 +} material: "/builtins/materials/gui.material" adjust_reference: ADJUST_REFERENCE_PARENT max_nodes: 512 diff --git a/main/main.gui_script b/main/main.gui_script index 81c468b..f54c76b 100644 --- a/main/main.gui_script +++ b/main/main.gui_script @@ -50,6 +50,17 @@ local function webview_available() return true end +local function webview_exists(self) + if not webview_available() then + return false + end + if not self.webview_id then + print("WebView has been destroyed. Create it again before calling this") + return false + end + return true +end + local function window_callback(self, event, data) if event == window.WINDOW_EVENT_RESIZED then self.width = data.width @@ -74,32 +85,50 @@ end function on_input(self, action_id, action) dirtylarry:button("button_open", action_id, action, function() - if not webview_available() then return end + if not webview_exists(self) then return end webview.open(self.webview_id, "https://www.google.com") end) dirtylarry:button("button_eval", action_id, action, function() - if not webview_available() then return end + if not webview_exists(self) then return end webview.eval(self.webview_id, "1+1") end) dirtylarry:button("button_html", action_id, action, function() - if not webview_available() then return end + if not webview_exists(self) then return end webview.open_raw(self.webview_id, HTML) end) dirtylarry:button("button_show", action_id, action, function() - if not webview_available() then return end + if not webview_exists(self) then return end webview.set_visible(self.webview_id, 1) end) dirtylarry:button("button_hide", action_id, action, function() - if not webview_available() then return end + if not webview_exists(self) then return end webview.set_visible(self.webview_id, 0) end) + + dirtylarry:button("button_create", action_id, action, function() + if not webview_available() then return end + if self.webview_id then + print("WebView already created") + return + end + + self.webview_id = webview.create(webview_callback) + webview.set_position(self.webview_id, 0, 0, -1, 300) + end) + + dirtylarry:button("button_destroy", action_id, action, function() + if not webview_exists(self) then return end + + webview.destroy(self.webview_id) + self.webview_id = nil + end) end diff --git a/webview/ext.manifest b/webview/ext.manifest index d60d69a..b306a4b 100644 --- a/webview/ext.manifest +++ b/webview/ext.manifest @@ -10,5 +10,9 @@ platforms: frameworks: ["WebKit"] armv7-ios: + context: + frameworks: ["WebKit"] + + x86_64-ios: context: frameworks: ["WebKit"] \ No newline at end of file diff --git a/webview/src/java/com/defold/webview/JSONValue.java b/webview/src/java/com/defold/webview/JSONValue.java new file mode 100644 index 0000000..6b825aa --- /dev/null +++ b/webview/src/java/com/defold/webview/JSONValue.java @@ -0,0 +1,81 @@ +/* + * $Id: JSONValue.java,v 1.1 2006/04/15 14:37:04 platform Exp $ + * Created on 2006-4-15 + */ + + /* + * Code from the json-simple project, modified to extract only the escape() function. + * https://github.com/fangyidong/json-simple/blob/351aa583745c6de31eed3ccd921c7589f27e2413/src/main/java/org/json/simple/JSONValue.java + * + * Original code licensed under the terms of Apache License 2.0: + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package com.defold.webview; + +/** + * @author FangYidong + */ +public class JSONValue { + /** + * Escape quotes, \, /, \r, \n, \b, \f, \t and other control characters (U+0000 through U+001F). + * @param s + * @return + */ + public static String escape(String s){ + if(s==null) + return null; + StringBuffer sb = new StringBuffer(); + escape(s, sb); + return sb.toString(); + } + + /** + * @param s - Must not be null. + * @param sb + */ + static void escape(String s, StringBuffer sb) { + final int len = s.length(); + for(int i=0;i='\u0000' && ch<='\u001F') || (ch>='\u007F' && ch<='\u009F') || (ch>='\u2000' && ch<='\u20FF')){ + String ss=Integer.toHexString(ch); + sb.append("\\u"); + for(int k=0;k<4-ss.length();k++){ + sb.append('0'); + } + sb.append(ss.toUpperCase()); + } + else{ + sb.append(ch); + } + } + }//for + } +} \ No newline at end of file diff --git a/webview/src/java/com/defold/webview/WebViewJNI.java b/webview/src/java/com/defold/webview/WebViewJNI.java index bf972f4..4602bfd 100644 --- a/webview/src/java/com/defold/webview/WebViewJNI.java +++ b/webview/src/java/com/defold/webview/WebViewJNI.java @@ -411,7 +411,7 @@ public void eval(final String code, final int webview_id, final int request_id) public void run() { WebViewJNI.this.infos[webview_id].webviewClient.reset(request_id); WebViewJNI.this.infos[webview_id].webviewChromeClient.reset(request_id); - String javascript = String.format("javascript:%s.returnResultToJava(eval(\"%s\"))", JS_NAMESPACE, code); + String javascript = String.format("javascript:%s.returnResultToJava(eval(\"%s\"))", JS_NAMESPACE, JSONValue.escape(code)); WebViewJNI.this.infos[webview_id].webview.loadUrl(javascript); } }); diff --git a/webview/src/webview_darwin.mm b/webview/src/webview_darwin.mm index 3e55f88..9c4320e 100644 --- a/webview/src/webview_darwin.mm +++ b/webview/src/webview_darwin.mm @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -175,10 +176,10 @@ int Platform_Create(lua_State* L, dmWebView::WebViewInfo* _info) WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init]; WKWebView *view = [[WKWebView alloc] initWithFrame:screen.bounds configuration:configuration]; #elif defined(DM_PLATFORM_OSX) - NSScreen* screen = [NSScreen mainScreen]; + CGRect gameFrame = [dmGraphics::GetNativeOSXNSView() frame]; WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init]; - WKWebView *view = [[WKWebView alloc] initWithFrame:screen.visibleFrame configuration:configuration]; + WKWebView *view = [[WKWebView alloc] initWithFrame:gameFrame configuration:configuration]; #endif WebViewDelegate* navigationDelegate = [WebViewDelegate alloc]; navigationDelegate->m_WebViewID = webview_id; @@ -190,7 +191,7 @@ int Platform_Create(lua_State* L, dmWebView::WebViewInfo* _info) g_WebView.m_WebViews[webview_id] = view; g_WebView.m_WebViewDelegates[webview_id] = navigationDelegate; #if defined(DM_PLATFORM_IOS) - UIView * topView = [[[[UIApplication sharedApplication] keyWindow] subviews] lastObject]; + UIView * topView = [[[[UIApplication sharedApplication] keyWindow] rootViewController] view]; #elif defined(DM_PLATFORM_OSX) NSView * topView = [[[NSApplication sharedApplication] keyWindow] contentView]; #endif @@ -205,8 +206,16 @@ int Platform_Create(lua_State* L, dmWebView::WebViewInfo* _info) static void DestroyWebView(int webview_id) { ClearWebViewInfo(&g_WebView.m_Info[webview_id]); - [g_WebView.m_WebViews[webview_id] removeFromSuperview]; - [g_WebView.m_WebViews[webview_id] release]; + WKWebView *view = g_WebView.m_WebViews[webview_id]; + #if defined(DM_PLATFORM_OSX) + NSWindow *window = dmGraphics::GetNativeOSXNSWindow(); + if ([window firstResponder] == view) { + [window makeFirstResponder:dmGraphics::GetNativeOSXNSView()]; + } + #endif + [view removeFromSuperview]; + [view release]; + g_WebView.m_WebViews[webview_id] = NULL; } int Platform_Destroy(lua_State* L, int webview_id) @@ -298,9 +307,22 @@ int Platform_SetPosition(lua_State* L, int webview_id, int x, int y, int width, #if defined(DM_PLATFORM_IOS) CGRect screenRect = [[UIScreen mainScreen] bounds]; #elif defined(DM_PLATFORM_OSX) - CGRect screenRect = [[NSScreen mainScreen] visibleFrame]; + CGRect screenRect = [dmGraphics::GetNativeOSXNSView() frame]; #endif - g_WebView.m_WebViews[webview_id].frame = CGRectMake(x, y, width >= 0 ? width : screenRect.size.width, height >= 0 ? height : screenRect.size.height); + + CGFloat frameWidth = width >= 0 ? width : screenRect.size.width; + CGFloat frameHeight = height >= 0 ? height : screenRect.size.height; + CGRect frame = CGRectMake( + screenRect.origin.x + x, + #if defined(DM_PLATFORM_OSX) + screenRect.origin.y + screenRect.size.height - frameHeight - y, + #else + screenRect.origin.y + y, + #endif + frameWidth, + frameHeight + ); + g_WebView.m_WebViews[webview_id].frame = frame; return 0; }