Files
FlaxEngine/Source/Engine/UI/GUI/CanvasContainer.cs
W2.Wizard 694b20148d Cleanup 5
2021-02-21 11:50:30 +01:00

284 lines
9.5 KiB
C#

// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
namespace FlaxEngine.GUI
{
/// <summary>
/// The root container control used to sort and manage child UICanvas controls. Helps with sending input events.
/// </summary>
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
[HideInEditor]
internal sealed class CanvasContainer : ContainerControl
{
internal CanvasContainer()
{
AnchorPreset = AnchorPresets.StretchAll;
Offsets = Margin.Zero;
AutoFocus = false;
}
/// <summary>
/// Sorts the canvases by order.
/// </summary>
public void SortCanvases()
{
Children.Sort(SortCanvas);
}
private int SortCanvas(Control a, Control b)
{
return ((CanvasRootControl)a).Canvas.Order - ((CanvasRootControl)b).Canvas.Order;
}
private bool IntersectsChildContent(CanvasRootControl child, ref Ray ray, out Vector2 childSpaceLocation)
{
// Inline bounds calculations (it will reuse world matrix)
OrientedBoundingBox bounds = new OrientedBoundingBox
{
Extents = new Vector3(child.Size * 0.5f, Mathf.Epsilon)
};
child.Canvas.GetWorldMatrix(out var world);
Matrix.Translation(bounds.Extents.X, bounds.Extents.Y, 0, out var offset);
Matrix.Multiply(ref offset, ref world, out bounds.Transformation);
// Hit test
if (bounds.Intersects(ref ray, out Vector3 hitPoint))
{
// Transform world-space hit point to canvas local-space
world.Invert();
Vector3.Transform(ref hitPoint, ref world, out Vector3 localHitPoint);
childSpaceLocation = new Vector2(localHitPoint);
return child.ContainsPoint(ref childSpaceLocation);
}
childSpaceLocation = Vector2.Zero;
return false;
}
/// <inheritdoc />
public override void OnChildrenChanged()
{
SortCanvases();
base.OnChildrenChanged();
}
/// <inheritdoc />
protected override void DrawChildren()
{
// Draw all screen space canvases
for (int i = 0; i < _children.Count; i++)
{
var child = (CanvasRootControl)_children[i];
if (child.Visible && child.Is2D)
{
child.Draw();
}
}
}
/// <inheritdoc />
public override bool IntersectsChildContent(Control child, Vector2 location, out Vector2 childSpaceLocation)
{
childSpaceLocation = Vector2.Zero;
return ((CanvasRootControl)child).Is2D && base.IntersectsChildContent(child, location, out childSpaceLocation);
}
/// <inheritdoc />
public override void OnMouseEnter(Vector2 location)
{
// 2D GUI first
base.OnMouseEnter(location);
// Calculate 3D mouse ray
UICanvas.CalculateRay(ref location, out Ray ray);
// Test 3D
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
{
var child = (CanvasRootControl)_children[i];
if (child.Visible && child.Enabled && child.Is3D)
{
if (IntersectsChildContent(child, ref ray, out var childLocation))
{
child.OnMouseEnter(childLocation);
return;
}
}
}
}
/// <inheritdoc />
public override void OnMouseMove(Vector2 location)
{
// Calculate 3D mouse ray
UICanvas.CalculateRay(ref location, out Ray ray);
// Check all children collisions with mouse and fire events for them
bool isFirst3DHandled = false;
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
{
var child = (CanvasRootControl)_children[i];
if (child.Visible && child.Enabled)
{
// Fire events
if (child.Is2D)
{
if (IntersectsChildContent(child, location, out var childLocation))
{
if (child.IsMouseOver)
{
// Move
child.OnMouseMove(childLocation);
}
else
{
// Enter
child.OnMouseEnter(childLocation);
}
}
else if (child.IsMouseOver)
{
// Leave
child.OnMouseLeave();
}
}
else
{
if (!isFirst3DHandled && IntersectsChildContent(child, ref ray, out var childLocation))
{
isFirst3DHandled = true;
if (child.IsMouseOver)
{
// Move
child.OnMouseMove(childLocation);
}
else
{
// Enter
child.OnMouseEnter(childLocation);
}
}
else if (child.IsMouseOver)
{
// Leave
child.OnMouseLeave();
}
}
}
}
}
/// <inheritdoc />
public override bool OnMouseWheel(Vector2 location, float delta)
{
// 2D GUI first
if (base.OnMouseWheel(location, delta))
return true;
// Calculate 3D mouse ray
UICanvas.CalculateRay(ref location, out Ray ray);
// Test 3D
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
{
var child = (CanvasRootControl)_children[i];
if (child.Visible && child.Enabled && child.Is3D)
{
if (IntersectsChildContent(child, ref ray, out var childLocation))
{
child.OnMouseWheel(childLocation, delta);
return true;
}
}
}
return false;
}
/// <inheritdoc />
public override bool OnMouseDown(Vector2 location, MouseButton button)
{
// 2D GUI first
if (base.OnMouseDown(location, button))
return true;
// Calculate 3D mouse ray
UICanvas.CalculateRay(ref location, out Ray ray);
// Test 3D
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
{
var child = (CanvasRootControl)_children[i];
if (child.Visible && child.Enabled && child.Is3D)
{
if (IntersectsChildContent(child, ref ray, out var childLocation))
{
child.OnMouseDown(childLocation, button);
return true;
}
}
}
return false;
}
/// <inheritdoc />
public override bool OnMouseUp(Vector2 location, MouseButton button)
{
// 2D GUI first
if (base.OnMouseUp(location, button))
return true;
// Calculate 3D mouse ray
UICanvas.CalculateRay(ref location, out Ray ray);
// Test 3D
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
{
var child = (CanvasRootControl)_children[i];
if (child.Visible && child.Enabled && child.Is3D)
{
if (IntersectsChildContent(child, ref ray, out var childLocation))
{
child.OnMouseUp(childLocation, button);
return true;
}
}
}
return false;
}
/// <inheritdoc />
public override bool OnMouseDoubleClick(Vector2 location, MouseButton button)
{
// 2D GUI first
if (base.OnMouseDoubleClick(location, button))
return true;
// Calculate 3D mouse ray
UICanvas.CalculateRay(ref location, out Ray ray);
// Test 3D
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
{
var child = (CanvasRootControl)_children[i];
if (child.Visible && child.Enabled && child.Is3D)
{
if (IntersectsChildContent(child, ref ray, out var childLocation))
{
child.OnMouseDoubleClick(childLocation, button);
return true;
}
}
}
return false;
}
}
}