diff --git a/Source/Editor/GUI/ContextMenu/ContextMenu.cs b/Source/Editor/GUI/ContextMenu/ContextMenu.cs
index 072b01678..9a5e40b27 100644
--- a/Source/Editor/GUI/ContextMenu/ContextMenu.cs
+++ b/Source/Editor/GUI/ContextMenu/ContextMenu.cs
@@ -360,15 +360,15 @@ namespace FlaxEditor.GUI.ContextMenu
}
///
- public override bool ContainsPoint(ref Float2 location)
+ public override bool ContainsPoint(ref Float2 location, bool precise)
{
- if (base.ContainsPoint(ref location))
+ if (base.ContainsPoint(ref location, precise))
return true;
var cLocation = location - Location;
for (int i = 0; i < _panel.Children.Count; i++)
{
- if (_panel.Children[i].ContainsPoint(ref cLocation))
+ if (_panel.Children[i].ContainsPoint(ref cLocation, precise))
return true;
}
diff --git a/Source/Editor/Surface/SurfaceComment.cs b/Source/Editor/Surface/SurfaceComment.cs
index aad45190e..678b3351f 100644
--- a/Source/Editor/Surface/SurfaceComment.cs
+++ b/Source/Editor/Surface/SurfaceComment.cs
@@ -231,7 +231,7 @@ namespace FlaxEditor.Surface
}
///
- public override bool ContainsPoint(ref Float2 location)
+ public override bool ContainsPoint(ref Float2 location, bool precise)
{
return _headerRect.Contains(ref location) || _resizeButtonRect.Contains(ref location);
}
diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs
index 09fe2e6b5..9d04ced5f 100644
--- a/Source/Editor/Windows/GameWindow.cs
+++ b/Source/Editor/Windows/GameWindow.cs
@@ -148,6 +148,18 @@ namespace FlaxEditor.Windows
{
public bool EnableEvents => !Time.GamePaused;
+ public override bool RayCast(ref Float2 location, out Control hit)
+ {
+ return RayCastChildren(ref location, out hit);
+ }
+
+ public override bool ContainsPoint(ref Float2 location, bool precise = false)
+ {
+ if (precise)
+ return false;
+ return base.ContainsPoint(ref location, precise);
+ }
+
public override bool OnCharInput(char c)
{
if (!EnableEvents)
diff --git a/Source/Engine/UI/GUI/CanvasContainer.cs b/Source/Engine/UI/GUI/CanvasContainer.cs
index 084a22503..7bdd8bf25 100644
--- a/Source/Engine/UI/GUI/CanvasContainer.cs
+++ b/Source/Engine/UI/GUI/CanvasContainer.cs
@@ -60,6 +60,21 @@ namespace FlaxEngine.GUI
return ((CanvasRootControl)child).Is2D && base.IntersectsChildContent(child, location, out childSpaceLocation);
}
+ ///
+ public override bool RayCast(ref Float2 location, out Control hit)
+ {
+ // Ignore self
+ return RayCastChildren(ref location, out hit);
+ }
+
+ ///
+ public override bool ContainsPoint(ref Float2 location, bool precise = false)
+ {
+ if (precise) // Ignore as utility-only element
+ return false;
+ return base.ContainsPoint(ref location, precise);
+ }
+
///
public override void OnMouseEnter(Float2 location)
{
diff --git a/Source/Engine/UI/GUI/CanvasRootControl.cs b/Source/Engine/UI/GUI/CanvasRootControl.cs
index 54ad75295..02317ebf9 100644
--- a/Source/Engine/UI/GUI/CanvasRootControl.cs
+++ b/Source/Engine/UI/GUI/CanvasRootControl.cs
@@ -45,8 +45,9 @@ namespace FlaxEngine.GUI
///
/// The input ray to test (in world-space).
/// Output canvas-space local position.
+ /// True if perform precise intersection test against the control content (eg. with hit mask or transparency threshold). Otherwise, only simple bounds-check will be performed.
/// True if canvas intersects with that point, otherwise false.
- public bool Intersects3D(ref Ray ray, out Float2 canvasLocation)
+ public bool Intersects3D(ref Ray ray, out Float2 canvasLocation, bool precise = false)
{
// Inline bounds calculations (it will reuse world matrix)
var bounds = new OrientedBoundingBox
@@ -67,7 +68,7 @@ namespace FlaxEngine.GUI
Vector3.Transform(ref hitPoint, ref world, out Vector3 localHitPoint);
canvasLocation = new Float2(localHitPoint);
- return ContainsPoint(ref canvasLocation);
+ return ContainsPoint(ref canvasLocation, precise);
}
canvasLocation = Float2.Zero;
@@ -189,9 +190,9 @@ namespace FlaxEngine.GUI
}
///
- public override bool ContainsPoint(ref Float2 location)
+ public override bool ContainsPoint(ref Float2 location, bool precise)
{
- return base.ContainsPoint(ref location)
+ return base.ContainsPoint(ref location, precise)
&& (_canvas.TestCanvasIntersection == null || _canvas.TestCanvasIntersection(ref location));
}
diff --git a/Source/Engine/UI/GUI/CanvasScaler.cs b/Source/Engine/UI/GUI/CanvasScaler.cs
index 684583c32..18e5f0c38 100644
--- a/Source/Engine/UI/GUI/CanvasScaler.cs
+++ b/Source/Engine/UI/GUI/CanvasScaler.cs
@@ -429,6 +429,23 @@ namespace FlaxEngine.GUI
return ContainsPoint(ref location);
}
+ ///
+ public override bool ContainsPoint(ref Float2 location, bool precise = false)
+ {
+ if (precise) // Ignore as utility-only element
+ return false;
+ return base.ContainsPoint(ref location, precise);
+ }
+
+ ///
+ public override bool RayCast(ref Float2 location, out Control hit)
+ {
+ var p = location / _scale;
+ if (RayCastChildren(ref p, out hit))
+ return true;
+ return base.RayCast(ref location, out hit);
+ }
+
///
public override Float2 PointToParent(ref Float2 location)
{
diff --git a/Source/Engine/UI/GUI/Common/Border.cs b/Source/Engine/UI/GUI/Common/Border.cs
index 6ed94dbaf..dd673d703 100644
--- a/Source/Engine/UI/GUI/Common/Border.cs
+++ b/Source/Engine/UI/GUI/Common/Border.cs
@@ -36,5 +36,13 @@ namespace FlaxEngine.GUI
Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), BorderColor, BorderWidth);
}
+
+ ///
+ public override bool ContainsPoint(ref Float2 location, bool precise = false)
+ {
+ if (precise) // Ignore border
+ return false;
+ return base.ContainsPoint(ref location, precise);
+ }
}
}
diff --git a/Source/Engine/UI/GUI/Common/CheckBox.cs b/Source/Engine/UI/GUI/Common/CheckBox.cs
index 2128c0311..1893998cb 100644
--- a/Source/Engine/UI/GUI/Common/CheckBox.cs
+++ b/Source/Engine/UI/GUI/Common/CheckBox.cs
@@ -257,6 +257,14 @@ namespace FlaxEngine.GUI
}
}
+ ///
+ public override bool ContainsPoint(ref Float2 location, bool precise = false)
+ {
+ if (precise) // Precise check for checkbox element
+ return _box.Contains(ref location);
+ return base.ContainsPoint(ref location, precise);
+ }
+
///
public override void OnMouseMove(Float2 location)
{
diff --git a/Source/Engine/UI/GUI/Common/Spacer.cs b/Source/Engine/UI/GUI/Common/Spacer.cs
index f558dd1e2..0edfa3222 100644
--- a/Source/Engine/UI/GUI/Common/Spacer.cs
+++ b/Source/Engine/UI/GUI/Common/Spacer.cs
@@ -26,5 +26,13 @@ namespace FlaxEngine.GUI
{
AutoFocus = false;
}
+
+ ///
+ public override bool ContainsPoint(ref Float2 location, bool precise = false)
+ {
+ if (precise) // Ignore as visual-only element
+ return false;
+ return base.ContainsPoint(ref location, precise);
+ }
}
}
diff --git a/Source/Engine/UI/GUI/ContainerControl.cs b/Source/Engine/UI/GUI/ContainerControl.cs
index e5b92430f..9e3dd0722 100644
--- a/Source/Engine/UI/GUI/ContainerControl.cs
+++ b/Source/Engine/UI/GUI/ContainerControl.cs
@@ -879,6 +879,30 @@ namespace FlaxEngine.GUI
UnlockChildrenRecursive();
}
+ ///
+ public override bool RayCast(ref Float2 location, out Control hit)
+ {
+ if (RayCastChildren(ref location, out hit))
+ return true;
+ return base.RayCast(ref location, out hit);
+ }
+
+ internal bool RayCastChildren(ref Float2 location, out Control hit)
+ {
+ for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
+ {
+ var child = _children[i];
+ if (child.Visible)
+ {
+ IntersectsChildContent(child, location, out var childLocation);
+ if (child.RayCast(ref childLocation, out hit))
+ return true;
+ }
+ }
+ hit = null;
+ return false;
+ }
+
///
public override void OnMouseEnter(Float2 location)
{
diff --git a/Source/Engine/UI/GUI/Control.cs b/Source/Engine/UI/GUI/Control.cs
index 5cb9501c0..6733b09fa 100644
--- a/Source/Engine/UI/GUI/Control.cs
+++ b/Source/Engine/UI/GUI/Control.cs
@@ -1088,6 +1088,23 @@ namespace FlaxEngine.GUI
#region Helper Functions
+ ///
+ /// Performs a raycast against UI controls hierarchy to find any intersecting control content. Uses with precise check (skips transparent surfaces and empty panels).
+ ///
+ /// The position to intersect UI with.
+ /// The result control that intersects with the raycast.
+ /// True if ray hits any matching control, otherwise false.
+ public virtual bool RayCast(ref Float2 location, out Control hit)
+ {
+ if (ContainsPoint(ref location, true))
+ {
+ hit = this;
+ return true;
+ }
+ hit = null;
+ return false;
+ }
+
///
/// Checks if given location point in Parent Space intersects with the control content and calculates local position.
///
@@ -1101,11 +1118,12 @@ namespace FlaxEngine.GUI
}
///
- /// Checks if control contains given point in local Control Space.
+ /// Checks if this control contains given point in local Control Space.
///
/// Point location in Control Space to check
+ /// True if perform precise intersection test against the control content (eg. with hit mask or transparency threshold). Otherwise, only simple bounds-check will be performed.
/// True if point is inside control's area, otherwise false.
- public virtual bool ContainsPoint(ref Float2 location)
+ public virtual bool ContainsPoint(ref Float2 location, bool precise = false)
{
return location.X >= 0 &&
location.Y >= 0 &&
@@ -1123,12 +1141,10 @@ namespace FlaxEngine.GUI
{
if (parent == null)
throw new ArgumentNullException();
-
Control c = this;
while (c != null)
{
location = c.PointToParent(ref location);
-
c = c.Parent;
if (c == parent)
break;
diff --git a/Source/Engine/UI/GUI/Panels/AlphaPanel.cs b/Source/Engine/UI/GUI/Panels/AlphaPanel.cs
index b3159af4b..b694390d4 100644
--- a/Source/Engine/UI/GUI/Panels/AlphaPanel.cs
+++ b/Source/Engine/UI/GUI/Panels/AlphaPanel.cs
@@ -46,5 +46,13 @@ namespace FlaxEngine.GUI
Render2D.PopTint();
}
+
+ ///
+ public override bool ContainsPoint(ref Float2 location, bool precise = false)
+ {
+ if (precise) // Ignore as visual-only element
+ return false;
+ return base.ContainsPoint(ref location, precise);
+ }
}
}
diff --git a/Source/Engine/UI/GUI/Panels/BlurPanel.cs b/Source/Engine/UI/GUI/Panels/BlurPanel.cs
index 055535aa8..1a6a2e945 100644
--- a/Source/Engine/UI/GUI/Panels/BlurPanel.cs
+++ b/Source/Engine/UI/GUI/Panels/BlurPanel.cs
@@ -41,5 +41,13 @@ namespace FlaxEngine.GUI
Render2D.DrawBlur(new Rectangle(Float2.Zero, size), strength);
}
}
+
+ ///
+ public override bool ContainsPoint(ref Float2 location, bool precise = false)
+ {
+ if (precise) // Ignore as visual-only element
+ return false;
+ return base.ContainsPoint(ref location, precise);
+ }
}
}
diff --git a/Source/Engine/UI/GUI/Panels/Panel.cs b/Source/Engine/UI/GUI/Panels/Panel.cs
index 05cb9bd80..677045697 100644
--- a/Source/Engine/UI/GUI/Panels/Panel.cs
+++ b/Source/Engine/UI/GUI/Panels/Panel.cs
@@ -371,6 +371,14 @@ namespace FlaxEngine.GUI
}
}
+ ///
+ public override bool ContainsPoint(ref Float2 location, bool precise = false)
+ {
+ if (precise && BackgroundColor.A <= 0.0f) // Go through transparency
+ return false;
+ return base.ContainsPoint(ref location, precise);
+ }
+
///
public override bool IntersectsChildContent(Control child, Float2 location, out Float2 childSpaceLocation)
{
diff --git a/Source/Engine/UI/GUI/Panels/PanelWithMargins.cs b/Source/Engine/UI/GUI/Panels/PanelWithMargins.cs
index 68265a05a..b3e250477 100644
--- a/Source/Engine/UI/GUI/Panels/PanelWithMargins.cs
+++ b/Source/Engine/UI/GUI/Panels/PanelWithMargins.cs
@@ -180,5 +180,13 @@ namespace FlaxEngine.GUI
PerformLayout();
}
+
+ ///
+ public override bool ContainsPoint(ref Float2 location, bool precise = false)
+ {
+ if (precise && BackgroundColor.A <= 0.0f) // Go through transparency
+ return false;
+ return base.ContainsPoint(ref location, precise);
+ }
}
}
diff --git a/Source/Engine/UI/GUI/RootControl.cs b/Source/Engine/UI/GUI/RootControl.cs
index e2605dce8..a2ef271a5 100644
--- a/Source/Engine/UI/GUI/RootControl.cs
+++ b/Source/Engine/UI/GUI/RootControl.cs
@@ -159,6 +159,21 @@ namespace FlaxEngine.GUI
}
}
+ ///
+ public override bool RayCast(ref Float2 location, out Control hit)
+ {
+ // Ignore self
+ return RayCastChildren(ref location, out hit);
+ }
+
+ ///
+ public override bool ContainsPoint(ref Float2 location, bool precise = false)
+ {
+ if (precise) // Ignore as utility-only element
+ return false;
+ return base.ContainsPoint(ref location, precise);
+ }
+
///
/// Starts the mouse tracking. Used by the scrollbars, splitters, etc.
///