// 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 UnityEngine;
using UnityEngine.UI;
using UnityEngine.Serialization;
using Vuplex.WebView.Internal;
namespace Vuplex.WebView {
///
/// CanvasWebViewPrefab is a prefab that makes it easy to view and interact with an IWebView in a 2D Canvas.
/// It takes care of creating an IWebView, displaying its texture, and handling pointer interactions
/// from the user, like clicking, dragging, and scrolling. So, all you need to do is specify a URL or HTML to load,
/// and then the user can view and interact with it. For use outside of a Canvas, see WebViewPrefab instead.
///
///
/// There are two ways to create a CanvasWebViewPrefab:
///
/// -
/// By dragging the CanvasWebViewPrefab.prefab file into your scene via the editor and setting its "Initial URL" property.
/// -
/// Or by creating an instance programmatically with CanvasWebViewPrefab.Instantiate(), waiting for
/// it to initialize, and then calling methods on its WebView property, like LoadUrl().
///
///
///
/// If your use case requires a high degree of customization, you can instead create an IWebView
/// outside of the prefab with Web.CreateWebView().
///
/// See also:
///
/// - WebViewPrefab: https://developer.vuplex.com/webview/WebViewPrefab
/// - How clicking and scrolling works: https://support.vuplex.com/articles/clicking
/// - IWebView: https://developer.vuplex.com/webview/IWebView
/// - Web (static methods): https://developer.vuplex.com/webview/Web
///
///
[HelpURL("https://developer.vuplex.com/webview/CanvasWebViewPrefab")]
public partial class CanvasWebViewPrefab : BaseWebViewPrefab {
public override event EventHandler Clicked {
add {
if (_native2DModeActive) {
_logNative2DModeWarning("The CanvasWebViewPrefab.Clicked event is not supported in Native 2D Mode.");
}
base.Clicked += value;
}
remove {
base.Clicked -= value;
}
}
public override event EventHandler Scrolled {
add {
if (_native2DModeActive) {
_logNative2DModeWarning("The CanvasWebViewPrefab.Scrolled event is not supported in Native 2D Mode.");
}
base.Scrolled += value;
}
remove {
base.Scrolled -= value;
}
}
///
/// Enables or disables [Native 2D Mode](https://support.vuplex.com/articles/native-2d-mode/),
/// which makes it so that 3D WebView positions a native 2D webview in front of the Unity game view
/// instead of displaying web content as a texture in the Unity scene. The default is `false`. If set to `true` and the 3D WebView package
/// in use doesn't support Native 2D Mode, then the default rendering mode is used instead. This field can only be set prior to
/// initialization (i.e. prior to when Unity calls Start() for the CanvasWebViewPrefab). Native 2D Mode cannot be enabled or disabled
/// after the webview has been initialized, so changing this field's value after initialization has no effect.
///
///
/// Important notes:
///
/// -
/// Native 2D Mode is only supported for 3D WebView for Android (non-Gecko) and 3D WebView for iOS.
/// For other packages, the default render mode is used instead.
///
/// - Native 2D Mode requires that the canvas's render mode be set to "Screen Space - Overlay".
///
///
[Label("Native 2D Mode (Android, iOS, WebGL, & UWP only)")]
[Tooltip("Native 2D Mode positions a native 2D webview in front of the Unity game view instead of rendering web content as a texture in the Unity scene. Native 2D Mode provides better performance on iOS and UWP, because the default mode of rendering web content to a texture is slower. \n\nImportant notes:\n• Native 2D Mode is only supported for Android (non-Gecko), iOS, WebGL, and UWP. For the other 3D WebView packages, the default render mode is used instead.\n• Native 2D Mode requires that the canvas's render mode be set to \"Screen Space - Overlay\".")]
[HideInInspector]
[Header("Platform-specific")]
public bool Native2DModeEnabled;
///
/// Determines whether the operating system's native on-screen keyboard is
/// automatically shown when a text input in the webview is focused. The default for
/// CanvasWebViewPrefab is `true`.
///
///
///
/// The native on-screen keyboard is only supported for the following packages:
///
/// - 3D WebView for Android (non-Gecko)
/// - 3D WebView for iOS
/// - 3D WebView for visionOS
///
///
///
/// 3D WebView for Android with Gecko Engine doesn't support automatically showing the native on-screen keyboard,
/// but you can use Unity's [TouchScreenKeyboard](https://docs.unity3d.com/ScriptReference/TouchScreenKeyboard.html)
/// API to show the keyboard and then send typed characters to the webview like described in [this article](https://support.vuplex.com/articles/how-to-use-a-third-party-keyboard).
///
///
///
[Label("Native On-Screen Keyboard (Android, iOS, & visionOS only)")]
[Tooltip("Determines whether the operating system's native on-screen keyboard is automatically shown when a text input in the webview is focused. The native on-screen keyboard is only supported for the following packages:\n• 3D WebView for Android (non-Gecko)\n• 3D WebView for iOS\n• 3D WebView for visionOS")]
public bool NativeOnScreenKeyboardEnabled = true;
///
/// Gets or sets the prefab's resolution in pixels per Unity unit.
/// You can change the resolution to make web content appear larger or smaller.
/// The default resolution for CanvasWebViewPrefab is `1`.
///
///
///
/// Setting a lower resolution decreases the pixel density, but has the effect
/// of making web content appear larger. Setting a higher resolution increases
/// the pixel density, but has the effect of making content appear smaller.
/// For more information on scaling web content, see
/// [this support article](https://support.vuplex.com/articles/how-to-scale-web-content).
///
///
/// When running in [Native 2D Mode](https://support.vuplex.com/articles/native-2d-mode), the Resolution field
/// isn't used because the device's native resolution is used instead. So, the Resolution field's value is inaccurate and changes to it are ignored.
///
///
///
///
/// // Set the resolution to 2.5px per Unity unit.
/// webViewPrefab.Resolution = 2.5f;
///
///
[Label("Resolution (px / Unity unit)")]
[Tooltip("You can change this to make web content appear larger or smaller. Note that This property is ignored when running in Native 2D Mode.")]
[HideInInspector]
[FormerlySerializedAs("InitialResolution")]
public float Resolution = 1;
///
/// Determines the scroll sensitivity. The default sensitivity for CanvasWebViewPrefab is `15`.
///
///
/// This property is ignored when running in [Native 2D Mode](https://support.vuplex.com/articles/native-2d-mode).
///
[HideInInspector]
[Tooltip("Determines the scroll sensitivity. Note that This property is ignored when running in Native 2D Mode.")]
public float ScrollingSensitivity = 15;
public override bool Visible {
get {
var native2DWebView = _getNative2DWebViewIfActive();
if (native2DWebView != null) {
return native2DWebView.Visible;
}
return base.Visible;
}
set {
var native2DWebView = _getNative2DWebViewIfActive();
if (native2DWebView != null) {
native2DWebView.SetVisible(value);
return;
}
base.Visible = value;
}
}
public override Vector2 BrowserToScreenPoint(int xInPixels, int yInPixels) {
if (WebView == null) {
return Vector2.zero;
}
var rect = _getScreenSpaceRect();
if (rect == Rect.zero) {
return Vector2.zero;
}
var normalizedPoint = WebView.PointToNormalized(xInPixels, yInPixels);
// Clamp x and y to the range [0, WebView.Size].
var clampedNormalizedX = Math.Min(Math.Max(normalizedPoint.x, 0), 1);
var clampedNormalizedY = Math.Min(Math.Max(normalizedPoint.y, 0), 1);
return new Vector2(
rect.x + rect.width * clampedNormalizedX,
rect.y + rect.height * clampedNormalizedY
);
}
///
/// Creates a new instance.
///
///
/// The WebView property is available after initialization completes,
/// which is indicated by WaitUntilInitialized().
///
///
///
/// // Create a CanvasWebViewPrefab
/// var canvasWebViewPrefab = CanvasWebViewPrefab.Instantiate();
/// // Position the prefab how we want it
/// var canvas = GameObject.Find("Canvas");
/// canvasWebViewPrefab.transform.parent = canvas.transform;
/// var rectTransform = canvasWebViewPrefab.transform as RectTransform;
/// rectTransform.anchoredPosition3D = Vector3.zero;
/// rectTransform.offsetMin = Vector2.zero;
/// rectTransform.offsetMax = Vector2.zero;
/// canvasWebViewPrefab.transform.localScale = Vector3.one;
/// // Load a URL once the prefab finishes initializing
/// await canvasWebViewPrefab.WaitUntilInitialized();
/// canvasWebViewPrefab.WebView.LoadUrl("https://vuplex.com");
///
///
public static CanvasWebViewPrefab Instantiate() {
return Instantiate(new WebViewOptions());
}
///
/// Like Instantiate(), except it also accepts an object
/// of options flags that can be used to alter the generated webview's behavior.
///
public static CanvasWebViewPrefab Instantiate(WebViewOptions options) {
var prefabPrototype = (GameObject)Resources.Load("CanvasWebViewPrefab");
var gameObject = (GameObject)Instantiate(prefabPrototype);
var canvasWebViewPrefab = gameObject.GetComponent();
canvasWebViewPrefab._options = options;
return canvasWebViewPrefab;
}
///
/// Like Instantiate(float, float), except it initializes the instance with an existing, initialized
/// IWebView instance. This causes the CanvasWebViewPrefab to use the existing
/// IWebView instance instead of creating a new one. This can be used, for example, to create multiple
/// WebViewPrefabs that are connected to the same IWebView, or to create a prefab for an IWebView
/// created by IWithPopups.PopupRequested.
///
///
///
/// await firstWebViewPrefab.WaitUntilInitialized();
/// var secondWebViewPrefab = CanvasWebViewPrefab.Instantiate(firstWebViewPrefab.WebView);
/// // TODO: Position secondWebViewPrefab to the location where you want to display it.
///
///
public static CanvasWebViewPrefab Instantiate(IWebView webView) {
var prefabPrototype = (GameObject)Resources.Load("CanvasWebViewPrefab");
var gameObject = (GameObject)Instantiate(prefabPrototype);
var canvasWebViewPrefab = gameObject.GetComponent();
canvasWebViewPrefab.SetWebViewForInitialization(webView);
return canvasWebViewPrefab;
}
#region Non-public members
RectTransform _cachedRectTransform;
Canvas _canvas {
get {
if (_canvasGetter == null) {
_canvasGetter = new CachingGetter