Skip to content
This repository was archived by the owner on Apr 29, 2021. It is now read-only.

[Feature] UIWidgetsRaycastablePanel #366

Merged
merged 6 commits into from
Dec 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions Runtime/engine/UIWidgetsPanel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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()) {
Expand Down
8 changes: 8 additions & 0 deletions Runtime/engine/raycastable.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

125 changes: 125 additions & 0 deletions Runtime/engine/raycastable/RaycastManager.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
11 changes: 11 additions & 0 deletions Runtime/engine/raycastable/RaycastManager.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

106 changes: 106 additions & 0 deletions Runtime/engine/raycastable/RaycastableContainer.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
11 changes: 11 additions & 0 deletions Runtime/engine/raycastable/RaycastableContainer.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 40 additions & 0 deletions Runtime/engine/raycastable/UIWidgetsRaycastablePanel.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
11 changes: 11 additions & 0 deletions Runtime/engine/raycastable/UIWidgetsRaycastablePanel.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.