using System.Numerics;
using Robust.Shared.Maths;
namespace Robust.Client.UserInterface.Controls
{
///
/// Container that tries to put controls at their specified origin,
/// moving them around if necessary to avoid them going outside of the bounds of the container.
///
public sealed class PopupContainer : Control
{
///
/// The origin that the container tries to place the child at.
///
public static readonly AttachedProperty PopupOriginProperty = AttachedProperty.Create("PopupOrigin",
typeof(PopupContainer), typeof(Vector2), changed: PopupOriginChangedCallback);
///
/// Alternative position to right-align the popup if
/// would put it off-screen horizontally.
///
///
/// You know how right click menus with sub menus put the submenu on the left
/// if it's too close to the right of the screen? Yeah that.
///
public static readonly AttachedProperty AltOriginProperty = AttachedProperty.Create("AltOrigin",
typeof(PopupContainer), typeof(Vector2?), changed: PopupOriginChangedCallback);
///
/// Alternative position to bottom-left-align the popup if
/// would put it off-screen vertically.
///
public static readonly AttachedProperty AltOriginUpProperty = AttachedProperty.Create("AltOriginUp",
typeof(PopupContainer), typeof(Vector2?), changed: PopupOriginChangedCallback);
public PopupContainer()
{
RectClipContent = true;
}
public static void SetPopupOrigin(Control control, Vector2 origin)
{
control.SetValue(PopupOriginProperty, origin);
}
public static Vector2 GetPopupOrigin(Control control)
{
return control.GetValue(PopupOriginProperty);
}
public static Vector2? GetAltOrigin(Control control)
{
return control.GetValue(AltOriginProperty);
}
public static Vector2? GetAltOriginUp(Control control)
{
return control.GetValue(AltOriginUpProperty);
}
public static void SetAltOrigin(Control control, Vector2? origin)
{
control.SetValue(AltOriginProperty, origin);
}
public static void SetAltOriginUp(Control control, Vector2? origin)
{
control.SetValue(AltOriginUpProperty, origin);
}
private static void PopupOriginChangedCallback(Control owner, AttachedPropertyChangedEventArgs eventArgs)
{
if (owner.Parent is PopupContainer container)
{
container.InvalidateArrange();
}
}
protected override Vector2 ArrangeOverride(Vector2 finalSize)
{
foreach (var child in Children)
{
var size = child.DesiredSize;
var offset = child.GetValue(PopupOriginProperty);
var altPos = child.GetValue(AltOriginProperty);
var altPosUp = child.GetValue(AltOriginUpProperty);
var box = UIBox2.FromDimensions(offset, size);
var isAltPos = false;
var isAltPosUp = false;
// Clamp the right edge.
if (box.Right > Width)
{
// Try to position at alt pos.
if (altPos != null && altPos.Value.X - size.X > 0)
{
// There is horizontal room at the alt pos so there we go.
isAltPos = true;
box = UIBox2.FromDimensions(new Vector2(altPos.Value.X - size.X, altPos.Value.Y), size);
}
else
{
box = box.Translated(new Vector2(-(box.Right - Width), 0));
}
}
// Clamp the bottom edge.
if (box.Bottom > Height)
{
// Try to position at alt pos.
if (altPosUp != null && altPosUp.Value.Y - size.Y > 0)
{
// There is vertical room at the alt pos so there we go.
isAltPosUp = true;
box = UIBox2.FromDimensions(new Vector2(altPosUp.Value.X, altPosUp.Value.Y - size.Y), size);
}
else
{
box = box.Translated(new Vector2(0, -(box.Bottom - Height)));
}
}
// Try to clamp the left edge.
if (box.Left < 0 && !isAltPos)
{
box = box.Translated(new Vector2(-offset.X, 0));
}
// Try to clamp the top edge.
if (box.Top < 0 && !isAltPosUp)
{
box = box.Translated(new Vector2(0, -offset.Y));
}
child.Arrange(box);
}
return finalSize;
}
protected override Vector2 MeasureOverride(Vector2 availableSize)
{
// Measure to availableSize so that child controls never get too large to fit the whole screen.
base.MeasureOverride(availableSize);
return availableSize;
}
protected override void Resized()
{
base.Resized();
InvalidateArrange();
}
}
}