123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359 |
- // Copyright (c) 2024 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.EventSystems;
- using UnityEngine.Serialization;
- using Vuplex.WebView.Internal;
- namespace Vuplex.WebView {
- /// <summary>
- /// WebViewPrefab is a prefab that makes it easy to view and interact with an IWebView in 3D world space.
- /// 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 in a Canvas, see CanvasWebViewPrefab instead.
- /// </summary>
- /// <remarks>
- /// There are two ways to create a WebViewPrefab:
- /// <list type="number">
- /// <item>
- /// By dragging the WebViewPrefab.prefab file into your scene via the editor and setting its "Initial URL" property.
- /// </item>
- /// <item>
- /// Or by creating an instance programmatically with WebViewPrefab.Instantiate(), waiting for
- /// it to initialize, and then calling methods on its WebView property, like LoadUrl().
- /// </item>
- /// </list>
- /// <para>
- /// If your use case requires a high degree of customization, you can instead create an IWebView
- /// outside of the prefab with Web.CreateWebView().
- /// </para>
- /// See also:
- /// <list type="bullet">
- /// <item>CanvasWebViewPrefab: https://developer.vuplex.com/webview/CanvasWebViewPrefab</item>
- /// <item>How clicking and scrolling works: https://support.vuplex.com/articles/clicking</item>
- /// <item>IWebView: https://developer.vuplex.com/webview/IWebView</item>
- /// <item>Web (static methods): https://developer.vuplex.com/webview/Web</item>
- /// </list>
- /// </remarks>
- [HelpURL("https://developer.vuplex.com/webview/WebViewPrefab")]
- public partial class WebViewPrefab : BaseWebViewPrefab {
- /// <summary>
- /// Gets the prefab's collider.
- /// </summary>
- public Collider Collider { get => _view.GetComponent<Collider>(); }
- /// <summary>
- /// 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
- /// WebViewPrefab is `false`.
- /// </summary>
- /// <seealso cref="IWithNativeOnScreenKeyboard"/>
- /// <remarks>
- /// The native on-screen keyboard is only supported for the following packages:
- /// <list type="bullet">
- /// <item>3D WebView for Android (non-Gecko)</item>
- /// <item>3D WebView for iOS</item>
- /// </list>
- /// </remarks>
- /// <remarks>
- /// 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).
- /// </remarks>
- /// <remarks>
- /// On iOS, disabling the keyboard for one webview disables it for all webviews.
- /// </remarks>
- /// <seealso cref="IWithNativeOnScreenKeyboard"/>
- /// <seealso cref="KeyboardEnabled"/>
- [Label("Native On-Screen Keyboard (Android and iOS only)")]
- [Header("Platform-specific")]
- [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")]
- public bool NativeOnScreenKeyboardEnabled;
- /// <summary>
- /// 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 WebViewPrefab is `1300`.
- /// </summary>
- /// <remarks>
- /// 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).
- /// </remarks>
- /// <example>
- /// <code>
- /// // Set the resolution to 1800px per Unity unit.
- /// webViewPrefab.Resolution = 1800;
- /// </code>
- /// </example>
- [Label("Resolution (px / Unity unit)")]
- [Tooltip("You can change this to make web content appear larger or smaller.")]
- [HideInInspector]
- [FormerlySerializedAs("InitialResolution")]
- public float Resolution = 1300;
- /// <summary>
- /// Determines the scroll sensitivity. The default sensitivity for WebViewPrefab is `0.005`.
- /// </summary>
- [HideInInspector]
- public float ScrollingSensitivity = 0.005f;
- public override Vector2 BrowserToScreenPoint(int xInPixels, int yInPixels) {
- if (WebView == null || Camera.main == null) {
- 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);
- var worldPoint = _viewResizer.TransformPoint(new Vector3(1 - clampedNormalizedX, -1 * clampedNormalizedY, 0));
- var screenPoint = Camera.main.WorldToScreenPoint(worldPoint);
- return new Vector2(screenPoint.x, Screen.height - screenPoint.y);
- }
- /// <summary>
- /// Creates a new instance with the given dimensions in Unity units.
- /// </summary>
- /// <remarks>
- /// The WebView property is available after initialization completes,
- /// which is indicated by WaitUntilInitialized() method.
- /// A WebViewPrefab's default resolution is 1300px per Unity unit but can be
- /// changed by setting Resolution property.
- /// </remarks>
- /// <example>
- /// <code>
- /// // Create a 0.5 x 0.5 instance
- /// var webViewPrefab = WebViewPrefab.Instantiate(0.5f, 0.5f);
- /// // Position the prefab how we want it
- /// webViewPrefab.transform.parent = transform;
- /// webViewPrefab.transform.localPosition = new Vector3(0, 0f, 0.5f);
- /// webViewPrefab.transform.LookAt(transform);
- /// // Load a URL once the prefab finishes initializing
- /// await webViewPrefab.WaitUntilInitialized();
- /// webViewPrefab.WebView.LoadUrl("https://vuplex.com");
- /// </code>
- /// </example>
- public static WebViewPrefab Instantiate(float width, float height) {
- return Instantiate(width, height, new WebViewOptions());
- }
- /// <summary>
- /// Like Instantiate(float, float), except it also accepts an object
- /// of options flags that can be used to alter the generated webview's behavior.
- /// </summary>
- public static WebViewPrefab Instantiate(float width, float height, WebViewOptions options) {
- var prefabPrototype = (GameObject)Resources.Load("WebViewPrefab");
- var gameObject = (GameObject)Instantiate(prefabPrototype);
- var webViewPrefab = gameObject.GetComponent<WebViewPrefab>();
- webViewPrefab._sizeForInitialization = new Vector2(width, height);
- webViewPrefab._options = options;
- return webViewPrefab;
- }
- /// <summary>
- /// Like Instantiate(float, float), except it initializes the instance with an existing, initialized
- /// IWebView instance. This causes the WebViewPrefab 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.
- /// </summary>
- /// <example>
- /// <code>
- /// await firstWebViewPrefab.WaitUntilInitialized();
- /// var secondWebViewPrefab = WebViewPrefab.Instantiate(firstWebViewPrefab.WebView);
- /// // TODO: Position secondWebViewPrefab to the location where you want to display it.
- /// </code>
- /// </example>
- public static WebViewPrefab Instantiate(IWebView webView) {
- var prefabPrototype = (GameObject)Resources.Load("WebViewPrefab");
- var gameObject = (GameObject)Instantiate(prefabPrototype);
- var webViewPrefab = gameObject.GetComponent<WebViewPrefab>();
- webViewPrefab.SetWebViewForInitialization(webView);
- return webViewPrefab;
- }
- /// <summary>
- /// Resizes the prefab mesh and webview to the given dimensions in Unity units.
- /// </summary>
- /// <example>
- /// <c>webViewPrefab.Resize(1.2f, 0.5f);</c>
- /// </example>
- /// <seealso cref="Resolution"/>
- public void Resize(float width, float height) {
- if (width <= 0f || height <= 0f) {
- throw new ArgumentException($"Invalid dimensions: ({width.ToString("n4")}, {height.ToString("n4")})");
- }
- _sizeInUnityUnits = new Vector2(width, height);
- _resizeWebViewIfNeeded();
- _setViewSize(width, height);
- }
- /// <summary>
- /// Converts the given world point to a normalized point in the webview.
- /// </summary>
- public Vector2 WorldToNormalized(Vector3 worldPoint) {
- var localPoint = _viewResizer.transform.InverseTransformPoint(worldPoint);
- return new Vector2(1 - localPoint.x, -1 * localPoint.y);
- }
- #region Non-public members
- Vector2 _sizeForInitialization = Vector2.zero;
- [SerializeField]
- [HideInInspector]
- Transform _videoRectPositioner;
- [SerializeField]
- [HideInInspector]
- protected Transform _viewResizer;
- // Partial method implemented by various 3D WebView packages
- // to provide platform-specific warnings.
- partial void OnInit();
- protected override float _getResolution() {
- if (Resolution > 0f) {
- return Resolution;
- }
- WebViewLogger.LogError("Invalid value set for WebViewPrefab.Resolution: " + Resolution);
- return 1300;
- }
- protected override float _getScrollingSensitivity() => ScrollingSensitivity;
- protected override bool _getNativeOnScreenKeyboardEnabled() => NativeOnScreenKeyboardEnabled;
- protected override ViewportMaterialView _getVideoLayer() {
- if (_videoRectPositioner == null) {
- return null;
- }
- return _videoRectPositioner.GetComponentInChildren<ViewportMaterialView>();
- }
- protected override ViewportMaterialView _getView() {
- return transform.Find("WebViewPrefabResizer/WebViewPrefabView").GetComponent<ViewportMaterialView>();
- }
- async void _initWebViewPrefab() {
- try {
- OnInit();
- #if VUPLEX_XR_INTERACTION_TOOLKIT
- WebViewLogger.LogWarning("It looks like you're using a WebViewPrefab with XR Interaction Toolkit. Please use a CanvasWebViewPrefab inside a world space Canvas instead. For more information, please see <em>https://support.vuplex.com/articles/xr-interaction-toolkit</em>.");
- #endif
- if (_sizeForInitialization == Vector2.zero) {
- if (_webViewForInitialization != null) {
- _sizeForInitialization = (Vector2)_webViewForInitialization.Size / Resolution;
- } else {
- // The size was set via the editor instead of through arguments to Instantiate().
- _sizeForInitialization = transform.localScale;
- _resetLocalScale();
- }
- }
- _viewResizer = transform.GetChild(0);
- _videoRectPositioner = _viewResizer.Find("VideoRectPositioner");
- _setViewSize(_sizeForInitialization.x, _sizeForInitialization.y);
- await _initBase(new Rect(Vector2.zero, _sizeForInitialization));
- } catch (Exception exception) {
- // Catch any exceptions that occur during initialization because
- // some applications terminate the application on uncaught exceptions.
- Debug.LogException(exception);
- }
- }
- /// <summary>
- /// The top-level WebViewPrefab object's scale must be (1, 1),
- /// so the scale that was set via the editor is transferred from WebViewPrefab
- /// to WebViewPrefabResizer, and WebViewPrefab is moved to compensate
- /// for how WebViewPrefabResizer is moved in _setViewSize.
- /// </summary>
- void _resetLocalScale() {
- var localScale = transform.localScale;
- var localPosition = transform.localPosition;
- transform.localScale = new Vector3(1, 1, localScale.z);
- var offsetMagnitude = 0.5f * localScale.x;
- transform.localPosition = transform.localPosition + Quaternion.Euler(transform.localEulerAngles) * new Vector3(offsetMagnitude, 0, 0);
- }
- protected override void _setVideoLayerPosition(Rect videoRect) {
- // The origins of the prefab and the video rect are in their top-right
- // corners instead of their top-left corners.
- _videoRectPositioner.localPosition = new Vector3(
- 1 - (videoRect.x + videoRect.width),
- -1 * videoRect.y,
- _videoRectPositioner.localPosition.z
- );
- _videoRectPositioner.localScale = new Vector3(videoRect.width, videoRect.height, _videoRectPositioner.localScale.z);
- }
- void _setViewSize(float width, float height) {
- var depth = _viewResizer.localScale.z;
- _viewResizer.localScale = new Vector3(width, height, depth);
- var localPosition = _viewResizer.localPosition;
- // Set the view resizer so that its middle aligns with the middle of this parent class's gameobject.
- localPosition.x = width * -0.5f;
- _viewResizer.localPosition = localPosition;
- }
- void Start() => _initWebViewPrefab();
- #endregion
- #region Obsolete APIs
- // Renamed in v4.2.
- [Obsolete("WebViewPrefab.ConvertToScreenPoint() has been renamed to WebViewPrefab.WorldToNormalized(). Please switch to WorldToNormalized().")]
- public Vector2 ConvertToScreenPoint(Vector3 worldPoint) => WorldToNormalized(worldPoint);
- // Added in v1.0, removed in v3.12.
- [Obsolete("WebViewPrefab.Init() has been removed. The WebViewPrefab script now initializes itself automatically, so Init() no longer needs to be called.", true)]
- public void Init() {}
- // Added in v1.0, removed in v3.12.
- [Obsolete("WebViewPrefab.Init() has been removed. The WebViewPrefab script now initializes itself automatically, so Init() no longer needs to be called.", true)]
- public void Init(float width, float height) {}
- // Added in v1.0, removed in v3.12.
- [Obsolete("WebViewPrefab.Init() has been removed. The WebViewPrefab script now initializes itself automatically, so Init() no longer needs to be called.", true)]
- public void Init(float width, float height, WebViewOptions options) {}
- // Added in v3.8, removed in v3.12.
- [Obsolete("WebViewPrefab.Init() has been removed. The WebViewPrefab script now initializes itself automatically, so Init() no longer needs to be called. Please use WebViewPrefab.SetWebViewForInitialization(IWebView) instead.", true)]
- public void Init(IWebView webView) {}
- // Deprecated in v4.0.
- [Obsolete("WebViewPrefab.InitialResolution is now deprecated. Please use WebViewPrefab.Resolution instead.")]
- public float InitialResolution {
- get => Resolution;
- set => Resolution = value;
- }
- // Added in v2.3.3, removed in v3.5.
- [Obsolete("The static WebViewPrefab.ScrollSensitivity property has been removed. Please use the ScrollingSensitivity instance property instead: https://developer.vuplex.com/webview/WebViewPrefab#ScrollingSensitivity", true)]
- public static float ScrollSensitivity;
- #endregion
- }
- }
|