diff --git a/Runtime/engine/UIWidgetsPanel.cs b/Runtime/engine/UIWidgetsPanel.cs index a72ffabf..c886fea3 100644 --- a/Runtime/engine/UIWidgetsPanel.cs +++ b/Runtime/engine/UIWidgetsPanel.cs @@ -138,6 +138,13 @@ void _handleViewMetricsChanged(string method, List<JSONNode> args) { this._displayMetrics.onViewMetricsChanged(); } + protected virtual void InitWindowAdapter() { + D.assert(this._windowAdapter == null); + this._windowAdapter = new UIWidgetWindowAdapter(this); + + this._windowAdapter.OnEnable(); + } + protected override void OnEnable() { base.OnEnable(); @@ -153,10 +160,7 @@ protected override void OnEnable() { _repaintEvent = new Event {type = EventType.Repaint}; } - D.assert(this._windowAdapter == null); - this._windowAdapter = new UIWidgetWindowAdapter(this); - - this._windowAdapter.OnEnable(); + this.InitWindowAdapter(); Widget root; using (this._windowAdapter.getScope()) { diff --git a/Runtime/engine/raycastable.meta b/Runtime/engine/raycastable.meta new file mode 100644 index 00000000..4b305bd7 --- /dev/null +++ b/Runtime/engine/raycastable.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d8e15e1221dae40e68226ec6d8ed8ddb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/engine/raycastable/RaycastManager.cs b/Runtime/engine/raycastable/RaycastManager.cs new file mode 100644 index 00000000..f9c7aca8 --- /dev/null +++ b/Runtime/engine/raycastable/RaycastManager.cs @@ -0,0 +1,125 @@ +using System.Collections.Generic; +using Unity.UIWidgets.foundation; +using Unity.UIWidgets.ui; +using UnityEngine; + +namespace Unity.UIWidgets.engine.raycast { + public class RaycastableRect { + bool _isDirty = true; + + public bool isDirty { + get { return this._isDirty; } + } + + public float left; + public float right; + public float top; + public float bottom; + + public void MarkDirty() { + this._isDirty = true; + } + + public void UnmarkDirty() { + this._isDirty = false; + } + + public void UpdateRect(float left, float top, float width, float height) { + this.left = left; + this.right = left + width; + this.top = top; + this.bottom = top + height; + } + + public bool CheckInRect(Vector2 pos) { + return pos.x >= this.left && + pos.x < this.right && + pos.y >= this.top && + pos.y < this.bottom; + } + } + + public class RaycastManager { + static RaycastManager _instance; + + public static RaycastManager instance { + get { + if (_instance == null) { + _instance = new RaycastManager(); + } + + return _instance; + } + } + + public readonly Dictionary<int, Dictionary<int, RaycastableRect>> raycastHandlerMap = + new Dictionary<int, Dictionary<int, RaycastableRect>>(); + + public static void NewWindow(int windowHashCode) { + if (!instance.raycastHandlerMap.ContainsKey(windowHashCode)) { + instance.raycastHandlerMap.Add(windowHashCode, new Dictionary<int, RaycastableRect>()); + } + } + + public static void DisposeWindow(int windowHashCode) { + if (instance.raycastHandlerMap.ContainsKey(windowHashCode)) { + instance.raycastHandlerMap.Remove(windowHashCode); + } + } + + public static void AddToList(int widgetHashCode, int windowHashCode) { + D.assert(instance.raycastHandlerMap.ContainsKey(windowHashCode), () => + $"Raycast Handler Map doesn't contain Window {windowHashCode}, " + + $"Make sure using UIWidgetsRaycastablePanel instead of UIWidgetsPanel " + + $"while using RaycastableContainer."); + D.assert(!instance.raycastHandlerMap[windowHashCode].ContainsKey(widgetHashCode), () => + $"Raycast Handler Map already contains Widget {widgetHashCode} at Window {windowHashCode}"); + + instance.raycastHandlerMap[windowHashCode][widgetHashCode] = new RaycastableRect(); + } + + public static void MarkDirty(int widgetHashCode, int windowHashCode) { + D.assert(instance.raycastHandlerMap.ContainsKey(windowHashCode), () => + $"Raycast Handler Map doesn't contain Window {windowHashCode}"); + D.assert(instance.raycastHandlerMap[windowHashCode].ContainsKey(widgetHashCode), () => + $"Raycast Handler Map doesn't contain Widget {widgetHashCode} at Window {windowHashCode}"); + + instance.raycastHandlerMap[windowHashCode][widgetHashCode].MarkDirty(); + } + + public static void UpdateSizeOffset(int widgetHashCode, int windowHashCode, Size size, Offset offset) { + D.assert(instance.raycastHandlerMap.ContainsKey(windowHashCode), () => + $"Raycast Handler Map doesn't contain Window {windowHashCode}"); + D.assert(instance.raycastHandlerMap[windowHashCode].ContainsKey(widgetHashCode), () => + $"Raycast Handler Map doesn't contain Widget {widgetHashCode} at Window {windowHashCode}"); + + if (instance.raycastHandlerMap[windowHashCode][widgetHashCode].isDirty) { + instance.raycastHandlerMap[windowHashCode][widgetHashCode] + .UpdateRect(offset.dx, offset.dy, size.width, size.height); + instance.raycastHandlerMap[windowHashCode][widgetHashCode].UnmarkDirty(); + } + } + + public static void RemoveFromList(int widgetHashCode, int windowHashCode) { + D.assert(instance.raycastHandlerMap.ContainsKey(windowHashCode), () => + $"Raycast Handler Map doesn't contain Window {windowHashCode}"); + D.assert(instance.raycastHandlerMap[windowHashCode].ContainsKey(widgetHashCode), () => + $"Raycast Handler Map doesn't contain Widget {widgetHashCode} at Window {windowHashCode}"); + + instance.raycastHandlerMap[windowHashCode].Remove(widgetHashCode); + } + + public static bool CheckCastThrough(int windowHashCode, Vector2 pos) { + D.assert(instance.raycastHandlerMap.ContainsKey(windowHashCode), () => + $"Raycast Handler Map doesn't contain Window {windowHashCode}"); + + foreach (var item in instance.raycastHandlerMap[windowHashCode]) { + if (item.Value.CheckInRect(pos)) { + return false; + } + } + + return true; + } + } +} \ No newline at end of file diff --git a/Runtime/engine/raycastable/RaycastManager.cs.meta b/Runtime/engine/raycastable/RaycastManager.cs.meta new file mode 100644 index 00000000..74519d43 --- /dev/null +++ b/Runtime/engine/raycastable/RaycastManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9574f12b230354e6f87fc5fc0c98c96e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/engine/raycastable/RaycastableContainer.cs b/Runtime/engine/raycastable/RaycastableContainer.cs new file mode 100644 index 00000000..429d6524 --- /dev/null +++ b/Runtime/engine/raycastable/RaycastableContainer.cs @@ -0,0 +1,106 @@ +using Unity.UIWidgets.foundation; +using Unity.UIWidgets.rendering; +using Unity.UIWidgets.ui; +using Unity.UIWidgets.widgets; + +namespace Unity.UIWidgets.engine.raycast { + class RaycastableBox : SingleChildRenderObjectWidget { + public RaycastableBox( + Key key = null, + Widget child = null + ) : base(key, child) { + this.windowHashCode = Window.instance.GetHashCode(); + } + + readonly int windowHashCode; + + public override RenderObject createRenderObject(BuildContext context) { + return new RenderRaycastableBox( + windowHashCode: this.windowHashCode, + widget: this + ); + } + + public override Element createElement() { + return new _RaycastableBoxRenderElement(windowHashCode: this.windowHashCode, widget: this); + } + } + + class RenderRaycastableBox : RenderProxyBox { + public RenderRaycastableBox( + int windowHashCode, + RaycastableBox widget + ) { + this.widgetHashCode = widget.GetHashCode(); + this.windowHashCode = windowHashCode; + } + + readonly int widgetHashCode; + readonly int windowHashCode; + + public override void paint(PaintingContext context, Offset offset) { + RaycastManager.UpdateSizeOffset(this.widgetHashCode, this.windowHashCode, this.size, offset); + + base.paint(context, offset); + } + } + + class _RaycastableBoxRenderElement : SingleChildRenderObjectElement { + public _RaycastableBoxRenderElement( + int windowHashCode, + RaycastableBox widget + ) : base(widget) { + this.windowHashCode = windowHashCode; + } + + public new RaycastableBox widget { + get { return base.widget as RaycastableBox; } + } + + int widgetHashCode; + int windowHashCode; + + public override void mount(Element parent, object newSlot) { + this.widgetHashCode = this.widget.GetHashCode(); + RaycastManager.AddToList(this.widgetHashCode, this.windowHashCode); + base.mount(parent, newSlot); + } + + public override void update(Widget newWidget) { + RaycastManager.MarkDirty(this.widgetHashCode, this.windowHashCode); + base.update(newWidget); + } + + public override void unmount() { + RaycastManager.RemoveFromList(this.widgetHashCode, this.windowHashCode); + base.unmount(); + } + } + + public class RaycastableContainer : StatelessWidget { + public RaycastableContainer( + Widget child = null, + Key key = null + ) : base(key) { + this.child = child; + } + + public readonly Widget child; + + public override Widget build(BuildContext context) { + Widget current = this.child; + + if (this.child == null) { + current = new LimitedBox( + maxWidth: 0.0f, + maxHeight: 0.0f, + child: new ConstrainedBox(constraints: BoxConstraints.expand()) + ); + } + + current = new RaycastableBox(child: current); + + return current; + } + } +} \ No newline at end of file diff --git a/Runtime/engine/raycastable/RaycastableContainer.cs.meta b/Runtime/engine/raycastable/RaycastableContainer.cs.meta new file mode 100644 index 00000000..3bb75ef0 --- /dev/null +++ b/Runtime/engine/raycastable/RaycastableContainer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9af0c7d6aab134f5ba187ff34acf2377 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/engine/raycastable/UIWidgetsRaycastablePanel.cs b/Runtime/engine/raycastable/UIWidgetsRaycastablePanel.cs new file mode 100644 index 00000000..2fc70de1 --- /dev/null +++ b/Runtime/engine/raycastable/UIWidgetsRaycastablePanel.cs @@ -0,0 +1,40 @@ +using Unity.UIWidgets.engine; +using UnityEngine; + +namespace Unity.UIWidgets.engine.raycast { + [RequireComponent(typeof(RectTransform))] + public class UIWidgetsRaycastablePanel : UIWidgetsPanel, ICanvasRaycastFilter { + int windowHashCode; + + protected override void InitWindowAdapter() { + base.InitWindowAdapter(); + this.windowHashCode = this.window.GetHashCode(); + RaycastManager.NewWindow(this.windowHashCode); + } + + protected override void OnDisable() { + base.OnDisable(); + RaycastManager.DisposeWindow(this.windowHashCode); + } + + public bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera) { + if (!this.enabled) { + return true; + } + + Vector2 local; + RectTransformUtility.ScreenPointToLocalPointInRectangle(this.rectTransform, screenPoint, eventCamera, + out local); + + Rect rect = this.rectTransform.rect; + + // Convert top left corner as reference origin point. + local.x += this.rectTransform.pivot.x * rect.width; + local.y -= this.rectTransform.pivot.y * rect.height; + local.x = local.x / this.devicePixelRatio; + local.y = -local.y / this.devicePixelRatio; + + return !RaycastManager.CheckCastThrough(this.windowHashCode, local); + } + } +} \ No newline at end of file diff --git a/Runtime/engine/raycastable/UIWidgetsRaycastablePanel.cs.meta b/Runtime/engine/raycastable/UIWidgetsRaycastablePanel.cs.meta new file mode 100644 index 00000000..f5ff1f78 --- /dev/null +++ b/Runtime/engine/raycastable/UIWidgetsRaycastablePanel.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e5265d12f7193408b90993bdf987f058 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: