fix(ui): match retail spell bar controls
Place favorite-bar arrows by their authored sides, import rollover and pressed media through the shared scrollbar, and preserve manual offsets across passive refreshes. Carry the mixed-parent DAT anchor chain to a fixed 18-cell favorite viewport so overflow controls and the Cast button remain inside the retail-sized combat frame. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
02c29e67c8
commit
0134122c28
17 changed files with 424 additions and 59 deletions
49
src/AcDream.App/UI/Layout/RetailCombatLayout.cs
Normal file
49
src/AcDream.App/UI/Layout/RetailCombatLayout.cs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
namespace AcDream.App.UI.Layout;
|
||||
|
||||
/// <summary>Retail geometry policy for the shared melee/missile/magic combat root.</summary>
|
||||
internal static class RetailCombatLayout
|
||||
{
|
||||
internal const int VisibleFavoriteSlots = 18;
|
||||
internal const float FavoriteCellWidth = 32f;
|
||||
|
||||
/// <summary>
|
||||
/// Resize the imported combat tree so its favorite list exposes exactly the
|
||||
/// requested number of cells. The DAT deliberately authors the outer combat
|
||||
/// root at 610 pixels while the magic page's descendants use an 800-pixel
|
||||
/// design parent. Retail <c>UIElement::UpdateForParentSizeChange @
|
||||
/// 0x00462640</c> carries that width delta through every raw-edge policy.
|
||||
/// Applying the same chain and solving for the list viewport keeps the
|
||||
/// endowment, arrows, list, Cast button, and outer frame in one coherent
|
||||
/// retained layout instead of resizing individual controls ad hoc.
|
||||
/// </summary>
|
||||
internal static float FitFavoriteSlots(
|
||||
ImportedLayout layout,
|
||||
int visibleSlots = VisibleFavoriteSlots,
|
||||
float cellWidth = FavoriteCellWidth)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(layout);
|
||||
if (visibleSlots < 1)
|
||||
throw new ArgumentOutOfRangeException(nameof(visibleSlots));
|
||||
if (!(cellWidth > 0f) || !float.IsFinite(cellWidth))
|
||||
throw new ArgumentOutOfRangeException(nameof(cellWidth));
|
||||
|
||||
UiElement root = layout.Root;
|
||||
ApplyDescendantLayout(root);
|
||||
if (layout.FindElement(SpellcastingUiController.FavoriteListId) is not UiItemList list)
|
||||
throw new InvalidOperationException("Retail combat layout has no favorite spell list.");
|
||||
|
||||
float targetViewport = visibleSlots * cellWidth;
|
||||
root.Width = MathF.Max(0f, root.Width + targetViewport - list.Width);
|
||||
ApplyDescendantLayout(root);
|
||||
return root.Width;
|
||||
}
|
||||
|
||||
private static void ApplyDescendantLayout(UiElement parent)
|
||||
{
|
||||
foreach (UiElement child in parent.Children)
|
||||
{
|
||||
child.ApplyAnchor(parent.Width, parent.Height);
|
||||
ApplyDescendantLayout(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue