diff options
author | eska <eska@eska.me> | 2017-01-12 14:14:40 +0100 |
---|---|---|
committer | eska <eska@eska.me> | 2017-01-14 14:53:40 +0100 |
commit | 1f7d4c4d0ee8eba0a1d8084019269a45dfa76be4 (patch) | |
tree | c9f552f3792945fbcdbca983a3fb369ca5291302 /platform | |
parent | 68422c5dd68ec0e3b326d3718035d6513142fe4c (diff) |
Improve usability and style in web export presentation
- Check for WebGL support, don't load if unsupported
- Check for IndexedDB support
- Make canvas support check message visible
- Colored debug output for warnings and errors
- Make it obvious status can be closed by clicking
- Don't use status to display non-critical errors
- Limit output message count
- Add clear output button
- Fix setting total memory
Diffstat (limited to 'platform')
-rw-r--r-- | platform/javascript/export/export.cpp | 2 | ||||
-rw-r--r-- | platform/javascript/godot_shell.html | 381 | ||||
-rw-r--r-- | platform/javascript/javascript_main.cpp | 42 | ||||
-rw-r--r-- | platform/javascript/os_javascript.cpp | 12 |
4 files changed, 249 insertions, 188 deletions
diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp index ab64ffbb45..fa84a1e67c 100644 --- a/platform/javascript/export/export.cpp +++ b/platform/javascript/export/export.cpp @@ -377,7 +377,7 @@ EditorExportPlatformJavaScript::EditorExportPlatformJavaScript() { logo->create_from_image(img); max_memory=3; html_title=""; - html_font_family="arial,sans-serif"; + html_font_family="'Droid Sans',arial,sans-serif"; html_controls_enabled=true; pack_mode=PACK_SINGLE_FILE; } diff --git a/platform/javascript/godot_shell.html b/platform/javascript/godot_shell.html index 3170d2bb9e..a8b9594935 100644 --- a/platform/javascript/godot_shell.html +++ b/platform/javascript/godot_shell.html @@ -3,14 +3,14 @@ <head> <meta charset="utf-8" /> <title>$GODOT_HEAD_TITLE</title> - $GODOT_HEAD_INCLUDE +$GODOT_HEAD_INCLUDE <style type="text/css"> body { margin: 0; border: 0 none; padding: 0; text-align: center; - background-color: black; + background-color: #222226; font-family: $GODOT_STYLE_FONT_FAMILY; } @@ -71,7 +71,7 @@ margin: 0; border: 0 none; padding: 0; - background-color: #111; + background-color: #0c0c0c; } #canvas { @@ -81,6 +81,7 @@ * calculate cursor coordinates correctly */ border: 0 none; padding: 0; + color: white; } @@ -101,6 +102,8 @@ } #status { + line-height: 1.3; + cursor: pointer; visibility: visible; padding: 4px 6px; } @@ -123,7 +126,7 @@ -ms-user-select: none; } - #container:hover > #controls { + :hover > #controls { opacity: 1.0; transition: opacity 60ms ease-in-out; } @@ -135,223 +138,289 @@ margin-right: 2px; } + #controls > label > input { + vertical-align: middle; + } + #controls > label > input[type="checkbox"] { /* override user agent style */ margin-left: 0; } - label > input { - vertical-align: middle; - } - - #display-output { display: none; } + #output-toggle { display: none; } /* Debug output * ============ */ - #output { + #output-panel { display: none; - margin: 6px auto; - border: 2px groove grey; - padding: 4px; - outline: none; + max-width: $GODOT_CANVAS_WIDTHpx; + font-size: small; + margin: 6px auto 0; + padding: 0 4px 4px; text-align: left; + line-height: 2.2; + } + + #output-header { + display: flex; + justify-content: space-between; + align-items: center; + } + + #output-container { + padding: 6px; + background-color: #2c2a32; + box-shadow: inset 0 0 1px 1px #232127; + color: #bbb; + } + + #output-scroll { + line-height: 1; + height: 12em; + overflow-y: scroll; white-space: pre-wrap; font-size: small; - color: #eee; - background-color: black; font-family: "Lucida Console", Monaco, monospace; } - /* Export style include - * ==================== */ +/* Export style include + * ==================== */ + +$GODOT_STYLE_INCLUDE - $GODOT_STYLE_INCLUDE </style> </head> <body> <div id="container"> <canvas id="canvas" width="$GODOT_CANVAS_WIDTH" height="$GODOT_CANVAS_HEIGHT" onclick="canvas.ownerDocument.defaultView.focus();" oncontextmenu="event.preventDefault();"> - HTML5 canvas appears to be unsupported in the current browser.<br />Please try updating or use a different browser. + HTML5 canvas appears to be unsupported in the current browser.<br /> + Please try updating or use a different browser. </canvas> <div id="status-container"> - <span id="status" class="godot" onclick="this.style.visibility='hidden';">Loading page...</span> + <span id="status" class="godot" onclick="this.style.visibility='hidden';">Downloading page...</span> </div> <div id="controls" class="godot"> - <label id="display-output"><input id="output-toggle" type="checkbox" autocomplete="off" onchange="Presentation.setOutputVisible(this.checked);" />display output</label> + <label id="output-toggle"><input type="checkbox" checked="checked" autocomplete="off" onchange="Presentation.setOutputVisible(this.checked);" />Display Output</label> <!-- hidden until implemented - <label><input id="lock-cursor" type="checkbox" autocomplete="off" />lock cursor</label> - <label><input id="resize-canvas" type="checkbox" autocomplete="off" />resize canvas</label> + <label><input class="postRun-enable" type="checkbox" disabled="disabled" autocomplete="off" />lock cursor</label> + <label><input class="postRun-enable" type="checkbox" disabled="disabled" autocomplete="off" onchange="Presentation.setCanvasMaximized(this.checked);" />maximize</label> --> - <button id="fullscreen" class="godot" type="button" disabled="disabled" autocomplete="off" onclick="Presentation.goFullscreen();">fullscreen</button> + <button id="fullscreen" class="godot postRun-enable" type="button" disabled="disabled" autocomplete="off" onclick="Presentation.requestFullscreen();">Fullscreen</button> </div> </div> - <!-- Firefox adds extra space to textarea, but shouldn't matter too much https://bugzilla.mozilla.org/show_bug.cgi?id=33654 --> - <textarea id="output" rows="10" cols="100" readonly="readonly" style="resize:none"></textarea> + <div id="output-panel" class="godot"> + <div id="output-header"> + Output: + <button class="godot" type="button" autocomplete="off" onclick="Presentation.clearOutput();">Clear</button> + </div> + <div id="output-container"><div id="output-scroll"></div></div> + </div> + <!-- Scripts --> <script type="text/javascript">//<![CDATA[ var Presentation = (function() { var statusElement = document.getElementById("status"); - var outputElement = document.getElementById("output"); - var doneLoading = false; - - function onLoaded() { - doneLoading = true; - var fullscreenButtonElement = document.getElementById("fullscreen"); - fullscreenButtonElement.disabled = false; - } + var canvasElement = document.getElementById("canvas"); var presentation = { - statusElement: statusElement, - outputElement: outputElement, - setOutputVisible: function setOutputVisible(visible) { - outputElement.style.display = (visible?"block":"none"); + postRun: [ + function() { + var elements = document.getElementsByClassName("postRun-enable"); + Array.prototype.slice.call(elements).forEach(function(element) { + element.disabled = false; + }); + } + ], + requestFullscreen: function requestFullscreen() { + if (typeof Module !== "undefined" && Module.requestFullscreen) { + Module.requestFullscreen(false, false); + } + }, + /* + requestPointerlock: function requestPointerlock() { + if (typeof Module !== "undefined" && Module.requestPointerlock) { + Module.requestPointerlock(false, false); + } }, + setCanvasMaximized: function setCanvasMaximized(enabled) { + if (typeof Module !== "undefined" && Module.setCanvasMaximized) { + Module.setCanvasMaximized(enabled); + } + }, + */ setStatusVisible: function setStatusVisible(visible) { statusElement.style.visibility = (visible?"visible":"hidden"); }, setStatus: function setStatus(text) { - if (!text || text.length === 0) { - Presentation.setStatusVisible(false); - onLoaded(); - } else { - Presentation.setStatusVisible(true); - statusElement.innerHTML = text; + if (text.length === 0) { + // emscripten sets empty string as status after "Running..." + // per timeout, but another status may have been set by then + if (Presentation.setStatus.lastText === "Running...") + Presentation.setStatusVisible(false); + return; } + Presentation.setStatus.lastText = text; + while (statusElement.lastChild) { + statusElement.removeChild(statusElement.lastChild); + } + var lines = text.split("\n"); + lines.forEach(function(line, index) { + statusElement.appendChild(document.createTextNode(line)); + statusElement.appendChild(document.createElement("br")); + }); + var closeNote = document.createElement("span"); + closeNote.style.fontSize = "small"; + closeNote.textContent = "click to close"; + statusElement.appendChild(closeNote); + Presentation.setStatusVisible(true); + }, + isWebGLAvailable: function isWebGLAvailable() { + var context; + try { + context = canvasElement.getContext("webgl") || canvasElement.getContext("experimental-webgl"); + } catch (e) {} + return !!context; }, - goFullscreen: function goFullscreen() { - if (doneLoading) Module.requestFullScreen(false, false); - } }; + window.onerror = function(event) { presentation.setStatus("Failure during start-up\nSee JavaScript console") }; + if ($GODOT_CONTROLS_ENABLED) { // controls enabled - (function() { - var controlsElement = document.getElementById("controls"); - controlsElement.style.visibility="visible"; - })(); + document.getElementById("controls").style.visibility="visible"; } if ($GODOT_DEBUG_ENABLED) { // debugging enabled - (function() { - var outputToggleLabel = document.getElementById("display-output"); - var outputToggle = document.getElementById("output-toggle"); - - outputElement.value = ""; // clear browser cache - outputElement.style.display = "block"; - outputToggle.checked = true; - outputToggleLabel.style.display = "inline"; - - presentation.print = function print(text) { - if (outputElement.value.length !== 0) - outputElement.value += "\n"; - outputElement.value += text; - outputElement.scrollTop = outputElement.scrollHeight; // focus on bottom - }; - })(); + var outputRoot = document.getElementById("output-panel"); + var outputElement = document.getElementById("output-scroll"); + var outputToggle = document.getElementById("output-toggle"); + const maxOutputMessages = 400; + + presentation.setOutputVisible = function setOutputVisible(visible) { + outputRoot.style.display = (visible?"block":"none"); + }; + presentation.clearOutput = function clearOutput() { + while (outputElement.firstChild) { + outputElement.firstChild.remove(); + } + }; + + presentation.setOutputVisible(true); + outputToggle.style.display = "inline"; + + presentation.print = function print(text) { + if (arguments.length > 1) { + text = Array.prototype.slice.call(arguments).join(" "); + } + if (text.length <= 0) return; + while (outputElement.childElementCount >= maxOutputMessages) { + outputElement.firstChild.remove(); + } + var msg = document.createElement("div"); + if (text.trim().startsWith("**ERROR**") + || text.startsWith("**EXCEPTION**")) { + msg.style.color = "#d44"; + } else if (text.trim().startsWith("**WARNING**")) { + msg.style.color = "#ccc000"; + } else if (text.trim().startsWith("**SCRIPT ERROR**")) { + msg.style.color = "#c6d"; + } + msg.textContent = text; + var scrollToBottom = outputElement.scrollHeight - (outputElement.clientHeight + outputElement.scrollTop) < 10; + outputElement.appendChild(msg); + if (scrollToBottom) { + outputElement.scrollTop = outputElement.scrollHeight; + } + }; + + presentation.postRun.push(function() { + window.onerror = function(event) { presentation.print("**EXCEPTION**:", event) }; + }); + + } else { + presentation.postRun.push(function() { window.onerror = null; }); } return presentation; })(); // Emscripten interface - var Module = (function() { - var print = (function() { - if (typeof Presentation.print === "function") { - return function print(text) { - if (arguments.length > 1) - text = Array.prototype.slice.call(arguments).join(" "); - console.log(text); - Presentation.print(text); - }; - } else { - return function print(text) { - if (arguments.length > 1) - text = Array.prototype.slice.call(arguments).join(" "); - console.log(text); - }; + var Module = { + TOTAL_MEMORY: $GODOT_TMEM, + postRun: (function() { + if (typeof Presentation !== "undefined" && Presentation.postRun instanceof Array) { + return Presentation.postRun; } - })(); - - var canvas = (function() { - var canvasElement = document.getElementById("canvas"); - + })(), + print: function print(text) { + if (arguments.length > 1) { + text = Array.prototype.slice.call(arguments).join(" "); + } + console.log(text); + if (typeof Presentation !== "undefined" && typeof Presentation.print === "function") { + Presentation.print(text); + } + }, + printErr: function printErr(text) { + if (arguments.length > 1) { + text = Array.prototype.slice.call(arguments).join(" "); + } + console.error(text); + if (typeof Presentation !== "undefined" && typeof Presentation.print === "function") { + Presentation.print("**ERROR**:", text) + } + }, + canvas: (function() { + var canvas = document.getElementById("canvas"); // As a default initial behavior, pop up an alert when WebGL context is lost. To make your // application robust, you may want to override this behavior before shipping! // See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2 - canvasElement.addEventListener("webglcontextlost", function(e) { alert("WebGL context lost. Plase reload the page."); e.preventDefault(); }, false); - - return canvasElement; - })(); - - var setStatus = (function() { - if (typeof Presentation.setStatus === "function") - return function setStatus(text) { - if (!Module.setStatus.last) - Module.setStatus.last = { time: Date.now(), text: "" }; - if (text === Module.setStatus.text) - return; - var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/); - var now = Date.now(); - if (m) { - if (now - Date.now() < 30) // if this is a progress update, skip it if too soon - return; - text = m[1]; - } - Presentation.setStatus(text); - }; - else - return function setStatus(text) { - if (!Module.setStatus.last) - Module.setStatus.last = { time: Date.now(), text: "" }; - if (text === Module.setStatus.text) - return; - var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/); - var now = Date.now(); - if (m) { - if (now - Date.now() < 30) // if this is a progress update, skip it if too soon - return; - text = m[1]; - } - }; - })(); - - return { - TOTAL_MEMORY: 268435456, - preRun: [], - postRun: [], - print: print, - printErr: function printErr(text) { - if (arguments.length > 1) - text = Array.prototype.slice.call(arguments).join(" "); - if (0) { // XXX disabled for safety `if (typeof dump == "function")` - dump(text + "\n"); // fast, straight to the real console - } else { - console.error(text); - } - }, - canvas: canvas, - setStatus: setStatus, - totalDependencies: 0, - monitorRunDependencies: function monitorRunDependencies(left) { - this.totalDependencies = Math.max(this.totalDependencies, left); - Module.setStatus(left ? "Preparing... (" + (this.totalDependencies-left) + "/" + this.totalDependencies + ")" : "All downloads complete."); + canvas.addEventListener("webglcontextlost", function(e) { alert("WebGL context lost. Plase reload the page."); e.preventDefault(); }, false); + return canvas; + + })(), + setStatus: function setStatus(text) { + var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/); + var now = Date.now(); + if (m) { + if (now - Date.now() < 30) // if this is a progress update, skip it if too soon + return; + text = m[1]; } - }; - })(); + if (typeof Presentation !== "undefined" && typeof Presentation.setStatus == "function") { + Presentation.setStatus(text); + } + } + }; - Presentation.setStatus("Downloading..."); + if (!Presentation.isWebGLAvailable()) { + Presentation.setStatus("WebGL appears to be unsupported in the current browser.\nPlease try updating or use a different browser."); + Presentation.preventLoading = true; + } else { + Presentation.setStatus("Downloading..."); + } - window.onerror = function(event) { - // TODO: do not warn on ok events like simulating an infinite loop or exitStatus - Module.setStatus("Exception thrown, see JavaScript console"); - Module.setStatus = function(text) { - if (text) Module.printErr("[post-exception status] " + text); - }; - }; + if (Presentation.preventLoading) { + // prevent *fs.js and Emscripten's SCRIPT placeholder from loading any files + Presentation._XHR_send = XMLHttpRequest.prototype.send; + XMLHttpRequest.prototype.send = function() {}; + Presentation._Node_appendChild = Node.prototype.appendChild; + Node.prototype.appendChild = function(node) { + if (!(node instanceof HTMLScriptElement)) { + return Presentation._Node_appendChild.call(this, node); + } + } + } //]]></script> <script type="text/javascript" src="$GODOT_BASEfs.js"></script> - {{{ SCRIPT }}} +{{{ SCRIPT }}} + <script type="text/javascript"> + if (Presentation.preventLoading) { + XMLHttpRequest.prototype.send = Presentation._XHR_send; + Node.prototype.appendChild = Presentation._Node_appendChild; + } + </script> </body> </html> diff --git a/platform/javascript/javascript_main.cpp b/platform/javascript/javascript_main.cpp index 586ccc9b4e..99b16b2b9b 100644 --- a/platform/javascript/javascript_main.cpp +++ b/platform/javascript/javascript_main.cpp @@ -140,10 +140,9 @@ static void _godot_draw(void) { extern "C" { -void main_after_fs_sync(int value) { +void main_after_fs_sync() { start_step=1; - printf("FS SYNCHED!\n"); } } @@ -178,26 +177,25 @@ int main(int argc, char *argv[]) { glutDisplayFunc(_godot_draw); //glutSpecialFunc(gears_special); - - - //mount persistent filesystem - EM_ASM( - FS.mkdir('/userfs'); - FS.mount(IDBFS, {}, '/userfs'); - - - - // sync from persisted state into memory and then - // run the 'test' function - FS.syncfs(true, function (err) { - assert(!err); - console.log("done syncinc!"); - _after_sync_cb = Module.cwrap('main_after_fs_sync', 'void',['number']); - _after_sync_cb(0); - - }); - - ); + //mount persistent file system + EM_ASM( + FS.mkdir('/userfs'); + FS.mount(IDBFS, {}, '/userfs'); + + // sync from persistent state into memory and then + // run the 'main_after_fs_sync' function + FS.syncfs(true, function(err) { + + if (err) { + Module.setStatus('Failed to load persistent data\nPlease allow (third-party) cookies'); + Module.printErr('Failed to populate IDB file system: ' + err.message); + Module.exit(); + } else { + Module.print('Successfully populated IDB file system'); + ccall('main_after_fs_sync', 'void', []); + } + }); + ); glutMainLoop(); diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index 14af9b3e37..aa728c768a 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -502,18 +502,12 @@ bool OS_JavaScript::main_loop_iterate() { time_to_save_sync-=elapsed; - print_line("elapsed "+itos(elapsed)+" tts "+itos(time_to_save_sync)); - if (time_to_save_sync<0) { //time to sync, for real - // run 'success' - print_line("DOING SYNCH!"); EM_ASM( - FS.syncfs(function (err) { - assert(!err); - console.log("Synched!"); - //ccall('success', 'v'); - }); + FS.syncfs(function(err) { + if (err) { Module.printErr('Failed to save IDB file system: ' + err.message); } + }); ); } |