WebViewPrefab.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. // Copyright (c) 2022 Vuplex Inc. All rights reserved.
  2. //
  3. // Licensed under the Vuplex Commercial Software Library License, you may
  4. // not use this file except in compliance with the License. You may obtain
  5. // a copy of the License at
  6. //
  7. // https://vuplex.com/commercial-library-license
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. using System;
  15. using UnityEngine;
  16. using UnityEngine.EventSystems;
  17. using UnityEngine.Serialization;
  18. using Vuplex.WebView.Internal;
  19. namespace Vuplex.WebView {
  20. /// <summary>
  21. /// WebViewPrefab is a prefab that makes it easy to view and interact with an IWebView in 3D world space.
  22. /// It takes care of creating an IWebView, displaying its texture, and handling pointer interactions
  23. /// from the user, like clicking, dragging, and scrolling. So, all you need to do is specify a URL or HTML to load,
  24. /// and then the user can view and interact with it. For use in a Canvas, see CanvasWebViewPrefab instead.
  25. /// </summary>
  26. /// <remarks>
  27. /// There are two ways to create a WebViewPrefab:
  28. /// <list type="number">
  29. /// <item>
  30. /// By dragging the WebViewPrefab.prefab file into your scene via the editor and setting its "Initial URL" property.
  31. /// </item>
  32. /// <item>
  33. /// Or by creating an instance programmatically with WebViewPrefab.Instantiate(), waiting for
  34. /// it to initialize, and then calling methods on its WebView property, like LoadUrl().
  35. /// </item>
  36. /// </list>
  37. /// <para>
  38. /// If your use case requires a high degree of customization, you can instead create an IWebView
  39. /// outside of the prefab with Web.CreateWebView().
  40. /// </para>
  41. /// See also:
  42. /// <list type="bullet">
  43. /// <item>CanvasWebViewPrefab: https://developer.vuplex.com/webview/CanvasWebViewPrefab</item>
  44. /// <item>How clicking and scrolling works: https://support.vuplex.com/articles/clicking</item>
  45. /// <item>IWebView: https://developer.vuplex.com/webview/IWebView</item>
  46. /// <item>Web (static methods): https://developer.vuplex.com/webview/Web</item>
  47. /// </list>
  48. /// </remarks>
  49. [HelpURL("https://developer.vuplex.com/webview/WebViewPrefab")]
  50. public partial class WebViewPrefab : BaseWebViewPrefab {
  51. /// <summary>
  52. /// Gets the prefab's collider.
  53. /// </summary>
  54. public Collider Collider { get { return _view.GetComponent<Collider>(); }}
  55. /// <summary>
  56. /// Determines whether the operating system's native on-screen keyboard is
  57. /// automatically shown when a text input in the webview is focused. The default for
  58. /// WebViewPrefab is `false`.
  59. /// </summary>
  60. /// <remarks>
  61. /// The native on-screen keyboard is only supported for the following packages:
  62. /// <list type="bullet">
  63. /// <item>3D WebView for Android (non-Gecko)</item>
  64. /// <item>3D WebView for iOS</item>
  65. /// </list>
  66. /// </remarks>
  67. /// <remarks>
  68. /// On iOS, disabling the keyboard for one webview disables it for all webviews.
  69. /// </remarks>
  70. [Label("Native On-Screen Keyboard (Android and iOS only)")]
  71. [Header("Platform-specific")]
  72. [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")]
  73. public bool NativeOnScreenKeyboardEnabled;
  74. /// <summary>
  75. /// Gets or sets the prefab's resolution in pixels per Unity unit.
  76. /// You can change the resolution to make web content appear larger or smaller.
  77. /// The default resolution for WebViewPrefab is `1300`.
  78. /// </summary>
  79. /// <remarks>
  80. /// Setting a lower resolution decreases the pixel density, but has the effect
  81. /// of making web content appear larger. Setting a higher resolution increases
  82. /// the pixel density, but has the effect of making content appear smaller.
  83. /// For more information on scaling web content, see
  84. /// [this support article](https://support.vuplex.com/articles/how-to-scale-web-content).
  85. /// </remarks>
  86. /// <example>
  87. /// <code>
  88. /// // Set the resolution to 1800px per Unity unit.
  89. /// webViewPrefab.Resolution = 1800;
  90. /// </code>
  91. /// </example>
  92. [Label("Resolution (px / Unity unit)")]
  93. [Tooltip("You can change this to make web content appear larger or smaller.")]
  94. [HideInInspector]
  95. [FormerlySerializedAs("InitialResolution")]
  96. public float Resolution = 1300;
  97. /// <summary>
  98. /// Determines the scroll sensitivity. The default sensitivity for WebViewPrefab is `0.005`.
  99. /// </summary>
  100. [HideInInspector]
  101. public float ScrollingSensitivity = 0.005f;
  102. /// <summary>
  103. /// Converts the given world point to a normalized point in the webview.
  104. /// </summary>
  105. public Vector2 WorldToNormalized(Vector3 worldPoint) {
  106. var localPoint = _viewResizer.transform.InverseTransformPoint(worldPoint);
  107. return new Vector2(1 - localPoint.x, -1 * localPoint.y);
  108. }
  109. /// <summary>
  110. /// Creates a new instance with the given dimensions in Unity units.
  111. /// </summary>
  112. /// <remarks>
  113. /// The WebView property is available after initialization completes,
  114. /// which is indicated by WaitUntilInitialized() method.
  115. /// A WebViewPrefab's default resolution is 1300px per Unity unit but can be
  116. /// changed by setting Resolution property.
  117. /// </remarks>
  118. /// <example>
  119. /// <code>
  120. /// // Create a 0.5 x 0.5 instance
  121. /// var webViewPrefab = WebViewPrefab.Instantiate(0.5f, 0.5f);
  122. /// // Position the prefab how we want it
  123. /// webViewPrefab.transform.parent = transform;
  124. /// webViewPrefab.transform.localPosition = new Vector3(0, 0f, 0.5f);
  125. /// webViewPrefab.transform.LookAt(transform);
  126. /// // Load a URL once the prefab finishes initializing
  127. /// await webViewPrefab.WaitUntilInitialized();
  128. /// webViewPrefab.WebView.LoadUrl("https://vuplex.com");
  129. /// </code>
  130. /// </example>
  131. public static WebViewPrefab Instantiate(float width, float height) {
  132. return Instantiate(width, height, new WebViewOptions());
  133. }
  134. /// <summary>
  135. /// Like Instantiate(float, float), except it also accepts an object
  136. /// of options flags that can be used to alter the generated webview's behavior.
  137. /// </summary>
  138. public static WebViewPrefab Instantiate(float width, float height, WebViewOptions options) {
  139. var prefabPrototype = (GameObject) Resources.Load("WebViewPrefab");
  140. var gameObject = (GameObject) Instantiate(prefabPrototype);
  141. var webViewPrefab = gameObject.GetComponent<WebViewPrefab>();
  142. webViewPrefab._sizeForInitialization = new Vector2(width, height);
  143. webViewPrefab._options = options;
  144. return webViewPrefab;
  145. }
  146. /// <summary>
  147. /// Like Instantiate(float, float), except it initializes the instance with an existing, initialized
  148. /// IWebView instance. This causes the WebViewPrefab to use the existing
  149. /// IWebView instance instead of creating a new one.
  150. /// </summary>
  151. public static WebViewPrefab Instantiate(IWebView webView) {
  152. var prefabPrototype = (GameObject) Resources.Load("WebViewPrefab");
  153. var gameObject = (GameObject) Instantiate(prefabPrototype);
  154. var webViewPrefab = gameObject.GetComponent<WebViewPrefab>();
  155. webViewPrefab.SetWebViewForInitialization(webView);
  156. return webViewPrefab;
  157. }
  158. /// <summary>
  159. /// Resizes the prefab mesh and webview to the given dimensions in Unity units.
  160. /// </summary>
  161. /// <example>
  162. /// <c>webViewPrefab.Resize(1.2f, 0.5f);</c>
  163. /// </example>
  164. /// <seealso cref="Resolution"/>
  165. public void Resize(float width, float height) {
  166. if (width <= 0f || height <= 0f) {
  167. throw new ArgumentException($"Invalid dimensions: ({width.ToString("n4")}, {height.ToString("n4")})");
  168. }
  169. _sizeInUnityUnits = new Vector2(width, height);
  170. _resizeWebViewIfNeeded();
  171. _setViewSize(width, height);
  172. }
  173. #region Non-public members
  174. Vector2 _sizeForInitialization = Vector2.zero;
  175. [SerializeField]
  176. [HideInInspector]
  177. Transform _videoRectPositioner;
  178. [SerializeField]
  179. [HideInInspector]
  180. protected Transform _viewResizer;
  181. // Partial method implemented by various 3D WebView packages
  182. // to provide platform-specific warnings.
  183. partial void OnInit();
  184. protected override float _getResolution() {
  185. if (Resolution > 0f) {
  186. return Resolution;
  187. }
  188. WebViewLogger.LogError("Invalid value set for WebViewPrefab.Resolution: " + Resolution);
  189. return 1300;
  190. }
  191. protected override float _getScrollingSensitivity() => ScrollingSensitivity;
  192. protected override bool _getNativeOnScreenKeyboardEnabled() => NativeOnScreenKeyboardEnabled;
  193. protected override ViewportMaterialView _getVideoLayer() {
  194. if (_videoRectPositioner == null) {
  195. return null;
  196. }
  197. return _videoRectPositioner.GetComponentInChildren<ViewportMaterialView>();
  198. }
  199. protected override ViewportMaterialView _getView() {
  200. return transform.Find("WebViewPrefabResizer/WebViewPrefabView").GetComponent<ViewportMaterialView>();
  201. }
  202. void _initWebViewPrefab() {
  203. OnInit();
  204. #if VUPLEX_XR_INTERACTION_TOOLKIT
  205. 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>.");
  206. #endif
  207. #if UNITY_ANDROID && UNITY_2018_2_OR_NEWER
  208. if (UnityEngine.Rendering.GraphicsSettings.useScriptableRenderPipelineBatching) {
  209. WebViewLogger.LogError("URP settings error: \"SRP Batcher\" is enabled in Universal Render Pipeline (URP) settings, but URP for Android has an issue that prevents 3D WebView's textures from showing up outside of a Canvas. Please either go to \"UniversalRenderPipelineAsset\" -> \"Advanced\" and disable SRP Batcher or switch to using CanvasWebViewPrefab.");
  210. }
  211. #endif
  212. if (_sizeForInitialization == Vector2.zero) {
  213. if (_webViewForInitialization != null) {
  214. _sizeForInitialization = (Vector2)_webViewForInitialization.Size / Resolution;
  215. } else {
  216. // The size was set via the editor instead of through arguments to Instantiate().
  217. _sizeForInitialization = transform.localScale;
  218. _resetLocalScale();
  219. }
  220. }
  221. _viewResizer = transform.GetChild(0);
  222. _videoRectPositioner = _viewResizer.Find("VideoRectPositioner");
  223. _setViewSize(_sizeForInitialization.x, _sizeForInitialization.y);
  224. _initBase(new Rect(Vector2.zero, _sizeForInitialization));
  225. }
  226. /// <summary>
  227. /// The top-level WebViewPrefab object's scale must be (1, 1),
  228. /// so the scale that was set via the editor is transferred from WebViewPrefab
  229. /// to WebViewPrefabResizer, and WebViewPrefab is moved to compensate
  230. /// for how WebViewPrefabResizer is moved in _setViewSize.
  231. /// </summary>
  232. void _resetLocalScale() {
  233. var localScale = transform.localScale;
  234. var localPosition = transform.localPosition;
  235. transform.localScale = new Vector3(1, 1, localScale.z);
  236. var offsetMagnitude = 0.5f * localScale.x;
  237. transform.localPosition = transform.localPosition + Quaternion.Euler(transform.localEulerAngles) * new Vector3(offsetMagnitude, 0, 0);
  238. }
  239. protected override void _setVideoLayerPosition(Rect videoRect) {
  240. // The origins of the prefab and the video rect are in their top-right
  241. // corners instead of their top-left corners.
  242. _videoRectPositioner.localPosition = new Vector3(
  243. 1 - (videoRect.x + videoRect.width),
  244. -1 * videoRect.y,
  245. _videoRectPositioner.localPosition.z
  246. );
  247. _videoRectPositioner.localScale = new Vector3(videoRect.width, videoRect.height, _videoRectPositioner.localScale.z);
  248. }
  249. void _setViewSize(float width, float height) {
  250. var depth = _viewResizer.localScale.z;
  251. _viewResizer.localScale = new Vector3(width, height, depth);
  252. var localPosition = _viewResizer.localPosition;
  253. // Set the view resizer so that its middle aligns with the middle of this parent class's gameobject.
  254. localPosition.x = width * -0.5f;
  255. _viewResizer.localPosition = localPosition;
  256. }
  257. void Start() => _initWebViewPrefab();
  258. #endregion
  259. #region Obsolete APIs
  260. // Renamed in v4.2.
  261. [Obsolete("WebViewPrefab.ConvertToScreenPoint() has been renamed to WebViewPrefab.WorldToNormalized(). Please switch to WorldToNormalized().")]
  262. public Vector2 ConvertToScreenPoint(Vector3 worldPoint) => WorldToNormalized(worldPoint);
  263. // Added in v1.0, removed in v3.12.
  264. [Obsolete("WebViewPrefab.Init() has been removed. The WebViewPrefab script now initializes itself automatically, so Init() no longer needs to be called.", true)]
  265. public void Init() {}
  266. // Added in v1.0, removed in v3.12.
  267. [Obsolete("WebViewPrefab.Init() has been removed. The WebViewPrefab script now initializes itself automatically, so Init() no longer needs to be called.", true)]
  268. public void Init(float width, float height) {}
  269. // Added in v1.0, removed in v3.12.
  270. [Obsolete("WebViewPrefab.Init() has been removed. The WebViewPrefab script now initializes itself automatically, so Init() no longer needs to be called.", true)]
  271. public void Init(float width, float height, WebViewOptions options) {}
  272. // Added in v3.8, removed in v3.12.
  273. [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)]
  274. public void Init(IWebView webView) {}
  275. // Deprecated in v4.0.
  276. [Obsolete("WebViewPrefab.InitialResolution is now deprecated. Please use WebViewPrefab.Resolution instead.")]
  277. public float InitialResolution {
  278. get { return Resolution; }
  279. set { Resolution = value; }
  280. }
  281. // Added in v2.3.3, removed in v3.5.
  282. [Obsolete("The static WebViewPrefab.ScrollSensitivity property has been removed. Please use the ScrollingSensitivity instance property instead: https://developer.vuplex.com/webview/WebViewPrefab#ScrollingSensitivity", true)]
  283. public static float ScrollSensitivity;
  284. #endregion
  285. }
  286. }