mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 11:40:52 +01:00
689 lines
27 KiB
C#
689 lines
27 KiB
C#
using System;
|
|
using JetBrains.Annotations;
|
|
using Robust.Client.Graphics;
|
|
using Robust.Shared.Maths;
|
|
using Robust.Shared.ViewVariables;
|
|
|
|
namespace Robust.Client.UserInterface.Controls
|
|
{
|
|
[PublicAPI]
|
|
public class LayoutContainer : Container
|
|
{
|
|
/// <summary>
|
|
/// The value of an anchor that is exactly on the begin of the parent control.
|
|
/// </summary>
|
|
public const float AnchorBegin = 0;
|
|
|
|
/// <summary>
|
|
/// The value of an anchor that is exactly on the end of the parent control.
|
|
/// </summary>
|
|
public const float AnchorEnd = 1;
|
|
|
|
[ViewVariables(VVAccess.ReadWrite)] public bool Debug { get; set; }
|
|
|
|
public static readonly AttachedProperty MarginLeftProperty = AttachedProperty.Create("MarginLeft",
|
|
typeof(LayoutContainer), typeof(float), changed: LayoutPropertyChangedCallback);
|
|
|
|
public static readonly AttachedProperty MarginTopProperty = AttachedProperty.Create("MarginTop",
|
|
typeof(LayoutContainer), typeof(float), changed: LayoutPropertyChangedCallback);
|
|
|
|
public static readonly AttachedProperty MarginRightProperty = AttachedProperty.Create("MarginRight",
|
|
typeof(LayoutContainer), typeof(float), changed: LayoutPropertyChangedCallback);
|
|
|
|
public static readonly AttachedProperty MarginBottomProperty = AttachedProperty.Create("MarginBottom",
|
|
typeof(LayoutContainer), typeof(float), changed: LayoutPropertyChangedCallback);
|
|
|
|
public static readonly AttachedProperty AnchorLeftProperty = AttachedProperty.Create("AnchorLeft",
|
|
typeof(LayoutContainer), typeof(float), changed: LayoutPropertyChangedCallback);
|
|
|
|
public static readonly AttachedProperty AnchorTopProperty = AttachedProperty.Create("AnchorTop",
|
|
typeof(LayoutContainer), typeof(float), changed: LayoutPropertyChangedCallback);
|
|
|
|
public static readonly AttachedProperty AnchorRightProperty = AttachedProperty.Create("AnchorRight",
|
|
typeof(LayoutContainer), typeof(float), changed: LayoutPropertyChangedCallback);
|
|
|
|
public static readonly AttachedProperty AnchorBottomProperty = AttachedProperty.Create("AnchorBottom",
|
|
typeof(LayoutContainer), typeof(float), changed: LayoutPropertyChangedCallback);
|
|
|
|
public static readonly AttachedProperty GrowHorizontalProperty = AttachedProperty.Create("GrowHorizontal",
|
|
typeof(LayoutContainer), typeof(GrowDirection), changed: LayoutPropertyChangedCallback);
|
|
|
|
public static readonly AttachedProperty GrowVerticalProperty = AttachedProperty.Create("GrowVertical",
|
|
typeof(LayoutContainer), typeof(GrowDirection), changed: LayoutPropertyChangedCallback);
|
|
|
|
public static readonly AttachedProperty<bool> DebugProperty = AttachedProperty<bool>.Create("Debug",
|
|
typeof(LayoutContainer));
|
|
|
|
|
|
public static void SetMarginLeft(Control control, float value)
|
|
{
|
|
control.SetValue(MarginLeftProperty, value);
|
|
}
|
|
|
|
public static void SetMarginTop(Control control, float value)
|
|
{
|
|
control.SetValue(MarginTopProperty, value);
|
|
}
|
|
|
|
public static void SetMarginRight(Control control, float value)
|
|
{
|
|
control.SetValue(MarginRightProperty, value);
|
|
}
|
|
|
|
public static void SetMarginBottom(Control control, float value)
|
|
{
|
|
control.SetValue(MarginBottomProperty, value);
|
|
}
|
|
|
|
public static void SetAnchorLeft(Control control, float value)
|
|
{
|
|
control.SetValue(AnchorLeftProperty, value);
|
|
}
|
|
|
|
public static void SetAnchorTop(Control control, float value)
|
|
{
|
|
control.SetValue(AnchorTopProperty, value);
|
|
}
|
|
|
|
public static void SetAnchorRight(Control control, float value)
|
|
{
|
|
control.SetValue(AnchorRightProperty, value);
|
|
}
|
|
|
|
public static void SetAnchorBottom(Control control, float value)
|
|
{
|
|
control.SetValue(AnchorBottomProperty, value);
|
|
}
|
|
|
|
public static void SetGrowHorizontal(Control control, GrowDirection value)
|
|
{
|
|
control.SetValue(GrowHorizontalProperty, value);
|
|
}
|
|
|
|
public static void SetGrowVertical(Control control, GrowDirection value)
|
|
{
|
|
control.SetValue(GrowVerticalProperty, value);
|
|
}
|
|
|
|
public static void SetPosition(Control control, Vector2 position)
|
|
{
|
|
var (diffX, diffY) = position - control.Position;
|
|
|
|
// This is just to make subsequent set calls work correctly.
|
|
// It should get reset to this exact value next update either way.
|
|
control.Position = position;
|
|
|
|
SetMarginLeft(control, diffX + control.GetValue<float>(MarginLeftProperty));
|
|
SetMarginTop(control, diffY + control.GetValue<float>(MarginTopProperty));
|
|
SetMarginRight(control, diffX + control.GetValue<float>(MarginRightProperty));
|
|
SetMarginBottom(control, diffY + control.GetValue<float>(MarginBottomProperty));
|
|
}
|
|
|
|
[Obsolete("Change SetSize on the control instead.")]
|
|
public new static void SetSize(Control control, Vector2 size)
|
|
{
|
|
control.SetSize = size;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets an anchor AND a margin preset. This is most likely the method you want.
|
|
///
|
|
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Note that the current size and minimum size of the control affects how
|
|
/// each of the margins will be set, so if your control needs to shrink beyond its
|
|
/// current size / min size, you should either not call this method or only call it when your
|
|
/// control has a size of (0, 0). Otherwise your control's size will never be able
|
|
/// to go below the size implied by the margins set in this method.
|
|
/// </remarks>
|
|
public static void SetAnchorAndMarginPreset(Control control, LayoutPreset preset,
|
|
LayoutPresetMode mode = LayoutPresetMode.MinSize,
|
|
int margin = 0)
|
|
{
|
|
SetAnchorPreset(control, preset);
|
|
SetMarginsPreset(control, preset, mode, margin);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Changes all the anchors of a node at once to common presets.
|
|
/// The result is that the anchors are laid out to be suitable for a preset.
|
|
/// </summary>
|
|
/// <param name="preset">
|
|
/// The preset to apply to the anchors.
|
|
/// </param>
|
|
/// <param name="keepMargin">
|
|
/// If this is true, the control margin values themselves will not be changed,
|
|
/// and the control position and size will change according to the new anchor parameters.
|
|
/// If false, the control margins will adjust so that the control position and size remains the same relative to its parent.
|
|
/// </param>
|
|
/// <exception cref="ArgumentOutOfRangeException">
|
|
/// Thrown if <paramref name="preset" /> isn't a valid preset value.
|
|
/// </exception>
|
|
public static void SetAnchorPreset(Control control, LayoutPreset preset, bool keepMargin = false)
|
|
{
|
|
// TODO: Implement keepMargin.
|
|
|
|
// Left Anchor.
|
|
switch (preset)
|
|
{
|
|
case LayoutPreset.TopLeft:
|
|
case LayoutPreset.BottomLeft:
|
|
case LayoutPreset.CenterLeft:
|
|
case LayoutPreset.LeftWide:
|
|
case LayoutPreset.HorizontalCenterWide:
|
|
case LayoutPreset.Wide:
|
|
case LayoutPreset.TopWide:
|
|
case LayoutPreset.BottomWide:
|
|
control.SetValue(AnchorLeftProperty, 0f);
|
|
break;
|
|
case LayoutPreset.CenterTop:
|
|
case LayoutPreset.CenterBottom:
|
|
case LayoutPreset.Center:
|
|
case LayoutPreset.VerticalCenterWide:
|
|
control.SetValue(AnchorLeftProperty, 0.5f);
|
|
break;
|
|
case LayoutPreset.TopRight:
|
|
case LayoutPreset.BottomRight:
|
|
case LayoutPreset.CenterRight:
|
|
case LayoutPreset.RightWide:
|
|
control.SetValue(AnchorLeftProperty, 1f);
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(preset), preset, null);
|
|
}
|
|
|
|
// Top Anchor.
|
|
switch (preset)
|
|
{
|
|
case LayoutPreset.TopLeft:
|
|
case LayoutPreset.TopRight:
|
|
case LayoutPreset.LeftWide:
|
|
case LayoutPreset.TopWide:
|
|
case LayoutPreset.Wide:
|
|
case LayoutPreset.RightWide:
|
|
case LayoutPreset.CenterTop:
|
|
case LayoutPreset.VerticalCenterWide:
|
|
control.SetValue(AnchorTopProperty, 0f);
|
|
break;
|
|
case LayoutPreset.CenterLeft:
|
|
case LayoutPreset.CenterRight:
|
|
case LayoutPreset.HorizontalCenterWide:
|
|
case LayoutPreset.Center:
|
|
control.SetValue(AnchorTopProperty, 0.5f);
|
|
break;
|
|
case LayoutPreset.CenterBottom:
|
|
case LayoutPreset.BottomLeft:
|
|
case LayoutPreset.BottomRight:
|
|
case LayoutPreset.BottomWide:
|
|
control.SetValue(AnchorTopProperty, 1f);
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(preset), preset, null);
|
|
}
|
|
|
|
// Right Anchor.
|
|
switch (preset)
|
|
{
|
|
case LayoutPreset.TopLeft:
|
|
case LayoutPreset.CenterLeft:
|
|
case LayoutPreset.BottomLeft:
|
|
case LayoutPreset.LeftWide:
|
|
control.SetValue(AnchorRightProperty, 0f);
|
|
break;
|
|
case LayoutPreset.CenterTop:
|
|
case LayoutPreset.CenterBottom:
|
|
case LayoutPreset.Center:
|
|
case LayoutPreset.VerticalCenterWide:
|
|
control.SetValue(AnchorRightProperty, 0.5f);
|
|
break;
|
|
case LayoutPreset.CenterRight:
|
|
case LayoutPreset.TopRight:
|
|
case LayoutPreset.Wide:
|
|
case LayoutPreset.HorizontalCenterWide:
|
|
case LayoutPreset.TopWide:
|
|
case LayoutPreset.BottomWide:
|
|
case LayoutPreset.RightWide:
|
|
case LayoutPreset.BottomRight:
|
|
control.SetValue(AnchorRightProperty, 1f);
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(preset), preset, null);
|
|
}
|
|
|
|
// Bottom Anchor.
|
|
switch (preset)
|
|
{
|
|
case LayoutPreset.TopWide:
|
|
case LayoutPreset.TopLeft:
|
|
case LayoutPreset.TopRight:
|
|
case LayoutPreset.CenterTop:
|
|
control.SetValue(AnchorBottomProperty, 0f);
|
|
break;
|
|
case LayoutPreset.CenterLeft:
|
|
case LayoutPreset.CenterRight:
|
|
case LayoutPreset.Center:
|
|
case LayoutPreset.HorizontalCenterWide:
|
|
control.SetValue(AnchorBottomProperty, 0.5f);
|
|
break;
|
|
case LayoutPreset.CenterBottom:
|
|
case LayoutPreset.BottomLeft:
|
|
case LayoutPreset.BottomRight:
|
|
case LayoutPreset.LeftWide:
|
|
case LayoutPreset.Wide:
|
|
case LayoutPreset.RightWide:
|
|
case LayoutPreset.VerticalCenterWide:
|
|
case LayoutPreset.BottomWide:
|
|
control.SetValue(AnchorBottomProperty, 1f);
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(preset), preset, null);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Changes all the margins of a control at once to common presets.
|
|
/// The result is that the control is laid out as specified by the preset.
|
|
///
|
|
/// Note that the current size and minimum size of the control affects how
|
|
/// each of the margins will be set, so if your control needs to shrink beyond its
|
|
/// current size / min size, you should either not call this method or only call it when your
|
|
/// control has a size of (0, 0). Otherwise your control's size will never be able
|
|
/// to go below the size implied by the margins set in this method.
|
|
/// </summary>
|
|
/// <param name="preset"></param>
|
|
/// <param name="resizeMode"></param>
|
|
/// <param name="margin">Some extra margin to add depending on the preset chosen.</param>
|
|
public static void SetMarginsPreset(Control control, LayoutPreset preset,
|
|
LayoutPresetMode resizeMode = LayoutPresetMode.MinSize,
|
|
int margin = 0)
|
|
{
|
|
control.Measure(Vector2.Infinity);
|
|
var newSize = control.Size;
|
|
var minSize = control.DesiredSize;
|
|
if ((resizeMode & LayoutPresetMode.KeepWidth) == 0)
|
|
{
|
|
newSize = new Vector2(minSize.X, newSize.Y);
|
|
}
|
|
|
|
if ((resizeMode & LayoutPresetMode.KeepHeight) == 0)
|
|
{
|
|
newSize = new Vector2(newSize.X, minSize.Y);
|
|
}
|
|
|
|
var parentSize = control.Parent?.Size ?? Vector2.Zero;
|
|
|
|
var anchorLeft = control.GetValue<float>(AnchorLeftProperty);
|
|
var anchorTop = control.GetValue<float>(AnchorBottomProperty);
|
|
var anchorRight = control.GetValue<float>(AnchorRightProperty);
|
|
var anchorBottom = control.GetValue<float>(AnchorBottomProperty);
|
|
|
|
float marginLeft;
|
|
float marginTop;
|
|
float marginRight;
|
|
float marginBottom;
|
|
|
|
// Left Margin.
|
|
switch (preset)
|
|
{
|
|
case LayoutPreset.TopLeft:
|
|
case LayoutPreset.BottomLeft:
|
|
case LayoutPreset.CenterLeft:
|
|
case LayoutPreset.LeftWide:
|
|
case LayoutPreset.HorizontalCenterWide:
|
|
case LayoutPreset.Wide:
|
|
case LayoutPreset.TopWide:
|
|
case LayoutPreset.BottomWide:
|
|
// The AnchorLeft bit is to reverse the effect of anchors,
|
|
// So that the preset result is the same no matter what margins are set.
|
|
marginLeft = parentSize.X * (0 - anchorLeft) + margin;
|
|
break;
|
|
case LayoutPreset.CenterTop:
|
|
case LayoutPreset.CenterBottom:
|
|
case LayoutPreset.Center:
|
|
case LayoutPreset.VerticalCenterWide:
|
|
marginLeft = parentSize.X * (0.5f - anchorLeft) - newSize.X / 2;
|
|
break;
|
|
case LayoutPreset.TopRight:
|
|
case LayoutPreset.BottomRight:
|
|
case LayoutPreset.CenterRight:
|
|
case LayoutPreset.RightWide:
|
|
marginLeft = parentSize.X * (1 - anchorLeft) - newSize.X - margin;
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(preset), preset, null);
|
|
}
|
|
|
|
// Top Anchor.
|
|
switch (preset)
|
|
{
|
|
case LayoutPreset.TopLeft:
|
|
case LayoutPreset.TopRight:
|
|
case LayoutPreset.LeftWide:
|
|
case LayoutPreset.TopWide:
|
|
case LayoutPreset.Wide:
|
|
case LayoutPreset.RightWide:
|
|
case LayoutPreset.CenterTop:
|
|
case LayoutPreset.VerticalCenterWide:
|
|
marginTop = parentSize.Y * (0 - anchorTop) + margin;
|
|
break;
|
|
case LayoutPreset.CenterLeft:
|
|
case LayoutPreset.CenterRight:
|
|
case LayoutPreset.HorizontalCenterWide:
|
|
case LayoutPreset.Center:
|
|
marginTop = parentSize.Y * (0.5f - anchorTop) - newSize.Y / 2;
|
|
break;
|
|
case LayoutPreset.CenterBottom:
|
|
case LayoutPreset.BottomLeft:
|
|
case LayoutPreset.BottomRight:
|
|
case LayoutPreset.BottomWide:
|
|
marginTop = parentSize.Y * (1 - anchorTop) - newSize.Y - margin;
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(preset), preset, null);
|
|
}
|
|
|
|
// Right Anchor.
|
|
switch (preset)
|
|
{
|
|
case LayoutPreset.TopLeft:
|
|
case LayoutPreset.CenterLeft:
|
|
case LayoutPreset.BottomLeft:
|
|
case LayoutPreset.LeftWide:
|
|
marginRight = parentSize.X * (0 - anchorRight) + newSize.X + margin;
|
|
break;
|
|
case LayoutPreset.CenterTop:
|
|
case LayoutPreset.CenterBottom:
|
|
case LayoutPreset.Center:
|
|
case LayoutPreset.VerticalCenterWide:
|
|
marginRight = parentSize.X * (0.5f - anchorRight) + newSize.X;
|
|
break;
|
|
case LayoutPreset.CenterRight:
|
|
case LayoutPreset.TopRight:
|
|
case LayoutPreset.Wide:
|
|
case LayoutPreset.HorizontalCenterWide:
|
|
case LayoutPreset.TopWide:
|
|
case LayoutPreset.BottomWide:
|
|
case LayoutPreset.RightWide:
|
|
case LayoutPreset.BottomRight:
|
|
marginRight = parentSize.X * (1 - anchorRight) - margin;
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(preset), preset, null);
|
|
}
|
|
|
|
// Bottom Anchor.
|
|
switch (preset)
|
|
{
|
|
case LayoutPreset.TopWide:
|
|
case LayoutPreset.TopLeft:
|
|
case LayoutPreset.TopRight:
|
|
case LayoutPreset.CenterTop:
|
|
marginBottom = parentSize.Y * (0 - anchorBottom) + newSize.Y + margin;
|
|
break;
|
|
case LayoutPreset.CenterLeft:
|
|
case LayoutPreset.CenterRight:
|
|
case LayoutPreset.Center:
|
|
case LayoutPreset.HorizontalCenterWide:
|
|
marginBottom = parentSize.Y * (0.5f - anchorBottom) + newSize.Y;
|
|
break;
|
|
case LayoutPreset.CenterBottom:
|
|
case LayoutPreset.BottomLeft:
|
|
case LayoutPreset.BottomRight:
|
|
case LayoutPreset.LeftWide:
|
|
case LayoutPreset.Wide:
|
|
case LayoutPreset.RightWide:
|
|
case LayoutPreset.VerticalCenterWide:
|
|
case LayoutPreset.BottomWide:
|
|
marginBottom = parentSize.Y * (1 - anchorBottom) - margin;
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(preset), preset, null);
|
|
}
|
|
|
|
control.SetValue(MarginLeftProperty, marginLeft);
|
|
control.SetValue(MarginTopProperty, marginTop);
|
|
control.SetValue(MarginRightProperty, marginRight);
|
|
control.SetValue(MarginBottomProperty, marginBottom);
|
|
}
|
|
|
|
protected override Vector2 MeasureOverride(Vector2 availableSize)
|
|
{
|
|
var min = Vector2.Zero;
|
|
var uiScale = UIScale;
|
|
|
|
foreach (var child in Children)
|
|
{
|
|
var growH = child.GetValue<GrowDirection>(GrowHorizontalProperty);
|
|
var growV = child.GetValue<GrowDirection>(GrowVerticalProperty);
|
|
|
|
var anchorMargins = CalcAnchorMargins(availableSize, uiScale, child);
|
|
var size = availableSize;
|
|
if (growH == GrowDirection.Constrain)
|
|
size.X = anchorMargins.Width / uiScale;
|
|
|
|
if (growV == GrowDirection.Constrain)
|
|
size.Y = anchorMargins.Height / uiScale;
|
|
|
|
child.Measure(size);
|
|
min = Vector2.ComponentMax(min, child.DesiredSize);
|
|
}
|
|
|
|
return min;
|
|
}
|
|
|
|
protected override Vector2 ArrangeOverride(Vector2 finalSize)
|
|
{
|
|
foreach (var child in Children)
|
|
{
|
|
child.Arrange(CalcChildRect(finalSize, UIScale, child, out _));
|
|
}
|
|
|
|
return finalSize;
|
|
}
|
|
|
|
protected internal override void Draw(DrawingHandleScreen handle)
|
|
{
|
|
base.Draw(handle);
|
|
|
|
if (!Debug)
|
|
return;
|
|
|
|
var (pSizeX, pSizeY) = PixelSize;
|
|
foreach (var child in Children)
|
|
{
|
|
if (!child.GetValue(DebugProperty))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var rect = CalcChildRect(Size, UIScale, child, out var anchorSize);
|
|
|
|
var left = rect.Left * UIScale;
|
|
var right = rect.Right * UIScale;
|
|
var top = rect.Top * UIScale;
|
|
var bottom = rect.Bottom * UIScale;
|
|
|
|
DrawVLine(anchorSize.Left, Color.Pink);
|
|
DrawVLine(anchorSize.Right, Color.Green);
|
|
DrawHLine(anchorSize.Top, Color.Pink);
|
|
DrawHLine(anchorSize.Bottom, Color.Green);
|
|
|
|
/*
|
|
DrawVLine(left, Color.Orange);
|
|
DrawVLine(right, Color.Blue);
|
|
DrawHLine(top, Color.Orange);
|
|
DrawHLine(bottom, Color.Blue);
|
|
*/
|
|
|
|
handle.DrawRect(new UIBox2(left, top, right, bottom), Color.Red, false);
|
|
}
|
|
|
|
void DrawVLine(float x, Color color)
|
|
{
|
|
handle.DrawLine((x, 0), (x, pSizeY), color);
|
|
}
|
|
|
|
void DrawHLine(float y, Color color)
|
|
{
|
|
handle.DrawLine((0, y), (pSizeX, y), color);
|
|
}
|
|
}
|
|
|
|
private static UIBox2 CalcAnchorMargins(Vector2 ourSize, float uiScale, Control child)
|
|
{
|
|
var (pSizeX, pSizeY) = ourSize * uiScale;
|
|
|
|
var anchorLeft = child.GetValue<float>(AnchorLeftProperty);
|
|
var anchorTop = child.GetValue<float>(AnchorTopProperty);
|
|
var anchorRight = child.GetValue<float>(AnchorRightProperty);
|
|
var anchorBottom = child.GetValue<float>(AnchorBottomProperty);
|
|
|
|
var marginLeft = child.GetValue<float>(MarginLeftProperty) * uiScale;
|
|
var marginTop = child.GetValue<float>(MarginTopProperty) * uiScale;
|
|
var marginRight = child.GetValue<float>(MarginRightProperty) * uiScale;
|
|
var marginBottom = child.GetValue<float>(MarginBottomProperty) * uiScale;
|
|
|
|
var left = anchorLeft * pSizeX + marginLeft;
|
|
var top = anchorTop * pSizeY + marginTop;
|
|
var right = anchorRight * pSizeX + marginRight;
|
|
var bottom = anchorBottom * pSizeY + marginBottom;
|
|
|
|
// Yes, this can return boxes with left > right (and top > bottom).
|
|
// This is "intentional", see comment in CalcChildRect.
|
|
|
|
return new UIBox2(left, top, right, bottom);
|
|
}
|
|
|
|
private static UIBox2 CalcChildRect(Vector2 ourSize, float uiScale, Control child, out UIBox2 anchorSize)
|
|
{
|
|
// Calculate where the control "wants" to be by its anchors/margins.
|
|
var growHorizontal = child.GetValue<GrowDirection>(GrowHorizontalProperty);
|
|
var growVertical = child.GetValue<GrowDirection>(GrowVerticalProperty);
|
|
|
|
anchorSize = CalcAnchorMargins(ourSize, uiScale, child);
|
|
|
|
// This intentionally results in negatives if the right bound is < the left bound.
|
|
// Which then causes HandleLayoutOverflow to CORRECTLY work from the right bound instead.
|
|
var (wSizeX, wSizeY) = (anchorSize.Right - anchorSize.Left, anchorSize.Bottom - anchorSize.Top);
|
|
var (minSizeX, minSizeY) = child.DesiredPixelSize;
|
|
|
|
HandleLayoutOverflow(growHorizontal, minSizeX, anchorSize.Left, wSizeX, out var posX, out var sizeX);
|
|
HandleLayoutOverflow(growVertical, minSizeY, anchorSize.Top, wSizeY, out var posY, out var sizeY);
|
|
|
|
return UIBox2.FromDimensions(posX / uiScale, posY / uiScale, sizeX / uiScale, sizeY / uiScale);
|
|
}
|
|
|
|
private static void HandleLayoutOverflow(GrowDirection direction, float minSize, float wPos, float wSize,
|
|
out float pos,
|
|
out float size)
|
|
{
|
|
var overflow = minSize - wSize;
|
|
if (overflow <= 0 || direction == GrowDirection.Constrain)
|
|
{
|
|
pos = wPos;
|
|
size = wSize;
|
|
return;
|
|
}
|
|
|
|
switch (direction)
|
|
{
|
|
case GrowDirection.End:
|
|
pos = wPos;
|
|
break;
|
|
case GrowDirection.Begin:
|
|
pos = wPos - overflow;
|
|
break;
|
|
case GrowDirection.Both:
|
|
pos = wPos - overflow / 2;
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException();
|
|
}
|
|
|
|
size = minSize;
|
|
}
|
|
|
|
private static void LayoutPropertyChangedCallback(Control owner, AttachedPropertyChangedEventArgs eventArgs)
|
|
{
|
|
if (owner.Parent is LayoutContainer container)
|
|
{
|
|
container.InvalidateArrange();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Controls how the control should move when its wanted size (controlled by anchors/margins) is smaller
|
|
/// than its minimum size.
|
|
/// </summary>
|
|
public enum GrowDirection : byte
|
|
{
|
|
/// <summary>
|
|
/// The control will expand to the bottom right to reach its minimum size.
|
|
/// </summary>
|
|
End = 0,
|
|
|
|
/// <summary>
|
|
/// The control will expand to the top left to reach its minimum size.
|
|
/// </summary>
|
|
Begin,
|
|
|
|
/// <summary>
|
|
/// The control will expand on all axes equally to reach its minimum size.
|
|
/// </summary>
|
|
Both,
|
|
|
|
/// <summary>
|
|
/// The control will not be allowed to grow on this axis.
|
|
/// </summary>
|
|
Constrain,
|
|
}
|
|
|
|
/// <seealso cref="Control.SetMarginsPreset" />
|
|
[Flags]
|
|
[PublicAPI]
|
|
public enum LayoutPresetMode : byte
|
|
{
|
|
/// <summary>
|
|
/// Reset control size to minimum size.
|
|
/// </summary>
|
|
MinSize = 0,
|
|
|
|
/// <summary>
|
|
/// Reset height to minimum but keep width the same.
|
|
/// </summary>
|
|
KeepWidth = 1,
|
|
|
|
/// <summary>
|
|
/// Reset width to minimum but keep height the same.
|
|
/// </summary>
|
|
KeepHeight = 2,
|
|
|
|
/// <summary>
|
|
/// Do not modify control size at all.
|
|
/// </summary>
|
|
KeepSize = KeepWidth | KeepHeight,
|
|
}
|
|
|
|
public enum LayoutPreset : byte
|
|
{
|
|
TopLeft = 0,
|
|
TopRight = 1,
|
|
BottomLeft = 2,
|
|
BottomRight = 3,
|
|
CenterLeft = 4,
|
|
CenterTop = 5,
|
|
CenterRight = 6,
|
|
CenterBottom = 7,
|
|
Center = 8,
|
|
LeftWide = 9,
|
|
TopWide = 10,
|
|
RightWide = 11,
|
|
BottomWide = 12,
|
|
VerticalCenterWide = 13,
|
|
HorizontalCenterWide = 14,
|
|
Wide = 15,
|
|
}
|
|
}
|
|
}
|