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. ///