feat(ui): port retained widget foundations
This commit is contained in:
parent
44f9ec13d9
commit
d825572e31
44 changed files with 84813 additions and 292 deletions
151
src/AcDream.App/UI/UiLayoutPolicy.cs
Normal file
151
src/AcDream.App/UI/UiLayoutPolicy.cs
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
namespace AcDream.App.UI;
|
||||
|
||||
/// <summary>An inclusive integer pixel box, matching retail <c>Box2D</c>.</summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exact retail raw-edge layout policy from
|
||||
/// <c>UIElement::UpdateForParentSizeChange @ 0x00462640</c>. Imported DAT elements
|
||||
/// retain all four independent modes; programmatic widgets continue to use
|
||||
/// <see cref="AnchorEdges"/>.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes a controller-supplied geometry change the new design baseline while
|
||||
/// retaining the raw retail edge modes.
|
||||
/// </summary>
|
||||
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.");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue