namespace AcDream.App.UI;
/// An inclusive integer pixel box, matching retail Box2D.
public readonly record struct UiPixelRect(int X0, int Y0, int X1, int Y1)
{
public int Width => X1 - X0 + 1;
public int Height => Y1 - Y0 + 1;
public static UiPixelRect FromPositionAndSize(int x, int y, int width, int height)
=> new(x, y, x + width - 1, y + height - 1);
}
///
/// Exact retail raw-edge layout policy from
/// UIElement::UpdateForParentSizeChange @ 0x00462640. Imported DAT elements
/// retain all four independent modes; programmatic widgets continue to use
/// .
///
public sealed class UiLayoutPolicy
{
public uint LeftMode { get; }
public uint TopMode { get; }
public uint RightMode { get; }
public uint BottomMode { get; }
public UiPixelRect OriginalChild { get; private set; }
public UiPixelRect OriginalParent { get; private set; }
public bool PreserveCurrentWhenEmpty { get; set; }
public UiLayoutPolicy(
uint leftMode,
uint topMode,
uint rightMode,
uint bottomMode,
UiPixelRect originalChild,
UiPixelRect originalParent)
{
ValidateMode(leftMode, nameof(leftMode));
ValidateMode(topMode, nameof(topMode));
ValidateMode(rightMode, nameof(rightMode));
ValidateMode(bottomMode, nameof(bottomMode));
LeftMode = leftMode;
TopMode = topMode;
RightMode = rightMode;
BottomMode = bottomMode;
OriginalChild = originalChild;
OriginalParent = originalParent;
}
///
/// Makes a controller-supplied geometry change the new design baseline while
/// retaining the raw retail edge modes.
///
public void Rebase(UiPixelRect child, UiPixelRect parent)
{
OriginalChild = child;
OriginalParent = parent;
}
public UiPixelRect Apply(UiPixelRect currentChild, UiPixelRect currentParent)
=> Apply(
LeftMode,
TopMode,
RightMode,
BottomMode,
OriginalChild,
OriginalParent,
currentChild,
currentParent,
PreserveCurrentWhenEmpty);
public static UiPixelRect Apply(
uint leftMode,
uint topMode,
uint rightMode,
uint bottomMode,
UiPixelRect originalChild,
UiPixelRect originalParent,
UiPixelRect currentChild,
UiPixelRect currentParent,
bool preserveCurrentWhenEmpty = false)
{
ValidateMode(leftMode, nameof(leftMode));
ValidateMode(topMode, nameof(topMode));
ValidateMode(rightMode, nameof(rightMode));
ValidateMode(bottomMode, nameof(bottomMode));
int deltaX = currentParent.Width - originalParent.Width;
int deltaY = currentParent.Height - originalParent.Height;
double scaleX = originalParent.Width != 0
? (double)currentParent.Width / originalParent.Width
: 0d;
double scaleY = originalParent.Height != 0
? (double)currentParent.Height / originalParent.Height
: 0d;
int x0 = ApplyNear(leftMode, originalChild.X0, originalChild.Width,
currentParent.Width, deltaX, scaleX);
int y0 = ApplyNear(topMode, originalChild.Y0, originalChild.Height,
currentParent.Height, deltaY, scaleY);
int x1 = ApplyFar(rightMode, originalChild.X1, originalChild.Width,
currentParent.Width, deltaX, scaleX);
int y1 = ApplyFar(bottomMode, originalChild.Y1, originalChild.Height,
currentParent.Height, deltaY, scaleY);
if (currentChild.Width != 0 || currentChild.Height != 0 || preserveCurrentWhenEmpty)
{
if (leftMode == 0) x0 = currentChild.X0;
if (topMode == 0) y0 = currentChild.Y0;
if (rightMode == 0) x1 = currentChild.X1;
if (bottomMode == 0) y1 = currentChild.Y1;
}
return new UiPixelRect(x0, y0, x1, y1);
}
private static int ApplyNear(
uint mode,
int originalEdge,
int originalSize,
int currentParentSize,
int parentDelta,
double scale)
=> mode switch
{
2 => originalEdge + parentDelta,
3 => currentParentSize / 2 - originalSize / 2,
4 => (int)(originalEdge * scale),
_ => originalEdge,
};
private static int ApplyFar(
uint mode,
int originalEdge,
int originalSize,
int currentParentSize,
int parentDelta,
double scale)
=> mode switch
{
1 => originalEdge + parentDelta,
3 => currentParentSize / 2 + originalSize / 2 - 1,
4 => (int)(originalEdge * scale),
_ => originalEdge,
};
private static void ValidateMode(uint mode, string parameterName)
{
if (mode > 4)
throw new ArgumentOutOfRangeException(parameterName, mode, "Retail edge mode must be in 0..4.");
}
}