240 lines
12 KiB
C#
240 lines
12 KiB
C#
// Copyright (c) 2025 Vuplex Inc. All rights reserved.
|
|
//
|
|
// Licensed under the Vuplex Commercial Software Library License, you may
|
|
// not use this file except in compliance with the License. You may obtain
|
|
// a copy of the License at
|
|
//
|
|
// https://vuplex.com/commercial-library-license
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
using System;
|
|
using System.Threading.Tasks;
|
|
using UnityEngine;
|
|
using Vuplex.WebView;
|
|
|
|
namespace Vuplex.Demos {
|
|
|
|
/// <summary>
|
|
/// Sets up the AdvancedWebViewDemo scene, which displays web content in a main
|
|
/// world-space WebViewPrefab and renders a UI in a second webview to display the current URL
|
|
/// and provide back / forward navigation controls.<br/><br/>
|
|
///
|
|
/// <b>Note:</b> The address bar currently only displays the current URL and is not an input.
|
|
/// I plan to add a dedicated browser prefab in the future that will include
|
|
/// a functional address bar. In the meantime, you can edit the CONTROLS_HTML field
|
|
/// below to implement a URL input.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This scene demonstrates the following: <br/>
|
|
/// - Programmatically instantiating WebViewPrefabs at runtime <br/>
|
|
/// - Programmatically instantiating an on-screen Keyboard prefab <br/>
|
|
/// - Using IWebView methods like LoadUrl(), LoadHtml(), GoBack(), and GoForward() <br/>
|
|
/// - Attaching handlers to the IWebView.UrlChanged and MessageEmitted events <br/>
|
|
/// - Sending messages from JavaScript to C# and vice versa <br/>
|
|
/// - Creating a transparent webview using the transparent meta tag <br/><br/>
|
|
///
|
|
/// Links: <br/>
|
|
/// - WebViewPrefab docs: https://developer.vuplex.com/webview/WebViewPrefab <br/>
|
|
/// - Sending messages from JavaScript to C# and vice versa: https://support.vuplex.com/articles/how-to-send-messages-from-javascript-to-c-sharp <br/>
|
|
/// - How to make a transparent webview: https://support.vuplex.com/articles/how-to-make-a-webview-transparent <br/>
|
|
/// - How clicking works: https://support.vuplex.com/articles/clicking <br/>
|
|
/// - Other examples: https://developer.vuplex.com/webview/overview#examples <br/>
|
|
/// </remarks>
|
|
class AdvancedWebViewDemo : MonoBehaviour {
|
|
|
|
WebViewPrefab controlsWebViewPrefab;
|
|
WebViewPrefab mainWebViewPrefab;
|
|
|
|
async void Start() {
|
|
|
|
Debug.Log("[AdvancedWebViewDemo] Just a heads-up: this scene's address bar currently only displays the current URL and is not an input. For more info, please see the comments in AdvancedWebViewDemo.cs.");
|
|
|
|
// Use a desktop User-Agent to request the desktop versions of websites.
|
|
// https://developer.vuplex.com/webview/Web#SetUserAgent
|
|
Web.SetUserAgent(false);
|
|
|
|
// Instantiate a 0.6 x 0.3 webview for the main web content.
|
|
// https://developer.vuplex.com/webview/WebViewPrefab#Instantiate
|
|
mainWebViewPrefab = WebViewPrefab.Instantiate(0.6f, 0.3f);
|
|
mainWebViewPrefab.PixelDensity = 2;
|
|
mainWebViewPrefab.transform.parent = transform;
|
|
mainWebViewPrefab.transform.localPosition = new Vector3(0, -0.05f, 0.4f);
|
|
mainWebViewPrefab.transform.localEulerAngles = new Vector3(0, 180, 0);
|
|
|
|
// Instantiate a second webview above the first to show a UI that
|
|
// displays the current URL and provides back / forward navigation buttons.
|
|
controlsWebViewPrefab = WebViewPrefab.Instantiate(0.6f, 0.05f);
|
|
controlsWebViewPrefab.KeyboardEnabled = false;
|
|
controlsWebViewPrefab.transform.parent = mainWebViewPrefab.transform;
|
|
controlsWebViewPrefab.transform.localPosition = new Vector3(0, 0.06f, 0);
|
|
controlsWebViewPrefab.transform.localEulerAngles = Vector3.zero;
|
|
|
|
// Add an on-screen keyboard under the main webview.
|
|
// https://developer.vuplex.com/webview/Keyboard
|
|
var keyboard = Keyboard.Instantiate();
|
|
keyboard.transform.SetParent(mainWebViewPrefab.transform, false);
|
|
keyboard.transform.localPosition = new Vector3(0, -0.31f, 0);
|
|
keyboard.transform.localEulerAngles = Vector3.zero;
|
|
|
|
// Wait for the prefabs to initialize because the WebView property of each is null until then.
|
|
// https://developer.vuplex.com/webview/WebViewPrefab#WaitUntilInitialized
|
|
await Task.WhenAll(new Task[] {
|
|
mainWebViewPrefab.WaitUntilInitialized(),
|
|
controlsWebViewPrefab.WaitUntilInitialized()
|
|
});
|
|
|
|
// Now that the WebViewPrefabs are initialized, we can use the IWebView APIs via its WebView property.
|
|
// https://developer.vuplex.com/webview/IWebView
|
|
mainWebViewPrefab.WebView.UrlChanged += (sender, eventArgs) => {
|
|
_setDisplayedUrl(eventArgs.Url);
|
|
// Refresh the back / forward button state after 1 second.
|
|
Invoke("_refreshBackForwardState", 1);
|
|
};
|
|
mainWebViewPrefab.WebView.LoadUrl("https://www.google.com");
|
|
|
|
controlsWebViewPrefab.WebView.MessageEmitted += Controls_MessageEmitted;
|
|
controlsWebViewPrefab.WebView.LoadHtml(CONTROLS_HTML);
|
|
await controlsWebViewPrefab.WebView.WaitForNextPageLoadToFinish();
|
|
_setDisplayedUrl(mainWebViewPrefab.WebView.Url);
|
|
|
|
// Android Gecko and UWP w/ XR enabled don't support transparent webviews, so as a workaround,
|
|
// configure the shader to turn black pixels transparent.
|
|
var pluginType = controlsWebViewPrefab.WebView.PluginType;
|
|
if (pluginType == WebPluginType.AndroidGecko || pluginType == WebPluginType.UniversalWindowsPlatform) {
|
|
controlsWebViewPrefab.SetRenderBlackAsTransparent(true);
|
|
}
|
|
}
|
|
|
|
async void _refreshBackForwardState() {
|
|
|
|
// Get the main webview's back / forward state and then post a message
|
|
// to the controls UI to update its buttons' state.
|
|
var canGoBack = await mainWebViewPrefab.WebView.CanGoBack();
|
|
var canGoForward = await mainWebViewPrefab.WebView.CanGoForward();
|
|
var serializedMessage = $"{{ \"type\": \"SET_BUTTONS\", \"canGoBack\": {canGoBack.ToString().ToLowerInvariant()}, \"canGoForward\": {canGoForward.ToString().ToLowerInvariant()} }}";
|
|
controlsWebViewPrefab.WebView.PostMessage(serializedMessage);
|
|
}
|
|
|
|
void Controls_MessageEmitted(object sender, EventArgs<string> eventArgs) {
|
|
|
|
var message = eventArgs.Value;
|
|
if (message == "GO_BACK") {
|
|
mainWebViewPrefab.WebView.GoBack();
|
|
} else if (message == "GO_FORWARD") {
|
|
mainWebViewPrefab.WebView.GoForward();
|
|
}
|
|
}
|
|
|
|
void _setDisplayedUrl(string url) {
|
|
|
|
if (controlsWebViewPrefab.WebView != null) {
|
|
var serializedMessage = $"{{ \"type\": \"SET_URL\", \"url\": \"{url}\" }}";
|
|
controlsWebViewPrefab.WebView.PostMessage(serializedMessage);
|
|
}
|
|
}
|
|
|
|
const string CONTROLS_HTML = @"
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<!-- This transparent meta tag instructs 3D WebView to allow the page to be transparent. -->
|
|
<meta name='transparent' content='true'>
|
|
<meta charset='UTF-8'>
|
|
<style>
|
|
body {
|
|
font-family: Helvetica, Arial, Sans-Serif;
|
|
margin: 0;
|
|
height: 100vh;
|
|
color: white;
|
|
}
|
|
.controls {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
height: 100%;
|
|
}
|
|
.controls > div {
|
|
background-color: #283237;
|
|
border-radius: 8px;
|
|
height: 100%;
|
|
}
|
|
.url-display {
|
|
flex: 0 0 75%;
|
|
width: 75%;
|
|
display: flex;
|
|
align-items: center;
|
|
overflow: hidden;
|
|
cursor: default;
|
|
}
|
|
#url {
|
|
width: 100%;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
padding: 0 15px;
|
|
font-size: 18px;
|
|
}
|
|
.buttons {
|
|
flex: 0 0 20%;
|
|
width: 20%;
|
|
display: flex;
|
|
justify-content: space-around;
|
|
align-items: center;
|
|
}
|
|
.buttons > button {
|
|
font-size: 40px;
|
|
background: none;
|
|
border: none;
|
|
outline: none;
|
|
color: white;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
.buttons > button:disabled {
|
|
color: rgba(255, 255, 255, 0.3);
|
|
}
|
|
.buttons > button:last-child {
|
|
transform: scaleX(-1);
|
|
}
|
|
/* For Gecko only, set the background color
|
|
to black so that the shader's cutout rect
|
|
can translate the black pixels to transparent.*/
|
|
@supports (-moz-appearance:none) {
|
|
body {
|
|
background-color: black;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class='controls'>
|
|
<div class='url-display'>
|
|
<div id='url'></div>
|
|
</div>
|
|
<div class='buttons'>
|
|
<button id='back-button' disabled='true' onclick='vuplex.postMessage(""GO_BACK"")'>←</button>
|
|
<button id='forward-button' disabled='true' onclick='vuplex.postMessage(""GO_FORWARD"")'>←</button>
|
|
</div>
|
|
</div>
|
|
<script>
|
|
// Handle messages sent from C#
|
|
window.addEventListener('vuplexmessage', event => {
|
|
var data = JSON.parse(event.value);
|
|
if (data.type === 'SET_URL') {
|
|
document.getElementById('url').innerText = data.url;
|
|
} else if (data.type === 'SET_BUTTONS') {
|
|
document.getElementById('back-button').disabled = !data.canGoBack;
|
|
document.getElementById('forward-button').disabled = !data.canGoForward;
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|
|
";
|
|
}
|
|
}
|