Files
RobustToolbox/Robust.Client/UserInterface/Controls/PopupContainer.cs
TayrtahnandGitHub d2792926e5 Remove [Virtual] attributes from abstract/sealed classes (#6482)
Removed [Virtual] attributes that shouldn't be there
2026-03-24 23:41:58 -04:00

159 lines
5.7 KiB
C#

using System.Numerics;
using Robust.Shared.Maths;
namespace Robust.Client.UserInterface.Controls
{
/// <summary>
/// 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.
/// </summary>
public sealed class PopupContainer : Control
{
/// <summary>
/// The origin that the container tries to place the child at.
/// </summary>
public static readonly AttachedProperty PopupOriginProperty = AttachedProperty.Create("PopupOrigin",
typeof(PopupContainer), typeof(Vector2), changed: PopupOriginChangedCallback);
/// <summary>
/// Alternative position to right-align the popup if <see cref="PopupOriginProperty"/>
/// would put it off-screen horizontally.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public static readonly AttachedProperty AltOriginProperty = AttachedProperty.Create("AltOrigin",
typeof(PopupContainer), typeof(Vector2?), changed: PopupOriginChangedCallback);
/// <summary>
/// Alternative position to bottom-left-align the popup if <see cref="PopupOriginProperty"/>
/// would put it off-screen vertically.
/// </summary>
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<Vector2>(PopupOriginProperty);
}
public static Vector2? GetAltOrigin(Control control)
{
return control.GetValue<Vector2?>(AltOriginProperty);
}
public static Vector2? GetAltOriginUp(Control control)
{
return control.GetValue<Vector2?>(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<Vector2>(PopupOriginProperty);
var altPos = child.GetValue<Vector2?>(AltOriginProperty);
var altPosUp = child.GetValue<Vector2?>(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();
}
}
}