fix(ui): restore retail magic shortcut bar
Port the retail horizontal ItemList empty-slot padding and share UIItem shortcut-number graphics with the status toolbar. Preserve all authored face children on compound DAT buttons so the three-piece Cast control reflows and renders as one complete button. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
0527325b25
commit
e3605672bb
12 changed files with 466 additions and 62 deletions
|
|
@ -552,8 +552,12 @@ public static class DatWidgetFactory
|
|||
// Spellbook school/level filters are Type-1 buttons with no parent media:
|
||||
// their 13x13 child carries Normal/Highlight art. Keep that child as the
|
||||
// face of this retained leaf instead of consuming and losing it.
|
||||
ElementInfo? face = info.StateMedia.Count == 0
|
||||
? FindStatefulFaceChild(info)
|
||||
ElementInfo[] authoredFaces = info.StateMedia.Count == 0
|
||||
? FindStatefulFaceChildren(info)
|
||||
: [];
|
||||
ElementInfo? face = authoredFaces.Length == 1 ? authoredFaces[0] : null;
|
||||
IReadOnlyList<ElementInfo>? faceSegments = authoredFaces.Length > 1
|
||||
? authoredFaces
|
||||
: null;
|
||||
|
||||
string? label = ResolveAuthoredString(info, stringResolve);
|
||||
|
|
@ -576,7 +580,7 @@ public static class DatWidgetFactory
|
|||
if (labelInfo.FontDid != 0u && fontResolve is not null)
|
||||
labelFont = fontResolve(labelInfo.FontDid) ?? elementFont;
|
||||
|
||||
var button = new UiButton(info, resolve, face)
|
||||
var button = new UiButton(info, resolve, face, faceSegments)
|
||||
{
|
||||
Label = label,
|
||||
LabelFont = labelFont,
|
||||
|
|
@ -638,11 +642,16 @@ public static class DatWidgetFactory
|
|||
}
|
||||
|
||||
private static ElementInfo? FindStatefulFaceChild(ElementInfo info)
|
||||
=> info.Children.FirstOrDefault(child =>
|
||||
=> FindStatefulFaceChildren(info).FirstOrDefault();
|
||||
|
||||
private static ElementInfo[] FindStatefulFaceChildren(ElementInfo info)
|
||||
=> info.Children.Where(child =>
|
||||
child.StateMedia.Count != 0
|
||||
&& child.StateMedia.Keys.Any(childState =>
|
||||
info.States.Values.Any(parentState =>
|
||||
string.Equals(parentState.Name, childState, StringComparison.Ordinal))));
|
||||
string.Equals(parentState.Name, childState, StringComparison.Ordinal))))
|
||||
.OrderBy(child => child.ReadOrder)
|
||||
.ToArray();
|
||||
|
||||
private static string? ResolveAuthoredString(
|
||||
ElementInfo info,
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ public sealed class SpellcastingUiController : IRetainedPanelController
|
|||
private readonly Action<uint> _useItem;
|
||||
private readonly Action<int, int, uint>? _addFavorite;
|
||||
private readonly Action<int, uint>? _removeFavorite;
|
||||
private readonly UiShortcutSlotGraphics? _shortcutGraphics;
|
||||
private readonly UiElement[] _tabs;
|
||||
private readonly UiElement[] _groups;
|
||||
private readonly UiItemList?[] _lists;
|
||||
|
|
@ -77,7 +78,8 @@ public sealed class SpellcastingUiController : IRetainedPanelController
|
|||
UiElement[] groups,
|
||||
UiItemList?[] lists,
|
||||
UiButton cast,
|
||||
UiElement endowmentHost)
|
||||
UiElement endowmentHost,
|
||||
UiShortcutSlotGraphics? shortcutGraphics)
|
||||
{
|
||||
_spellbook = spellbook;
|
||||
_casting = casting;
|
||||
|
|
@ -89,6 +91,7 @@ public sealed class SpellcastingUiController : IRetainedPanelController
|
|||
_useItem = useItem;
|
||||
_addFavorite = addFavorite;
|
||||
_removeFavorite = removeFavorite;
|
||||
_shortcutGraphics = shortcutGraphics;
|
||||
_tabs = tabs;
|
||||
_groups = groups;
|
||||
_lists = lists;
|
||||
|
|
@ -139,7 +142,8 @@ public sealed class SpellcastingUiController : IRetainedPanelController
|
|||
Action<uint> useItem,
|
||||
SelectionState selection,
|
||||
Action<int, int, uint>? addFavorite,
|
||||
Action<int, uint>? removeFavorite)
|
||||
Action<int, uint>? removeFavorite,
|
||||
UiShortcutSlotGraphics? shortcutGraphics = null)
|
||||
{
|
||||
if (layout.FindElement(CastButtonId) is not UiButton cast
|
||||
|| layout.FindElement(EndowmentId) is not { } endowmentHost)
|
||||
|
|
@ -162,7 +166,7 @@ public sealed class SpellcastingUiController : IRetainedPanelController
|
|||
layout, spellbook, casting, objects, playerGuid, resolveSpellIcon,
|
||||
resolveItemDragIcon, useItem, selection,
|
||||
addFavorite, removeFavorite,
|
||||
tabs, groups, lists, cast, endowmentHost);
|
||||
tabs, groups, lists, cast, endowmentHost, shortcutGraphics);
|
||||
}
|
||||
|
||||
public void AddFavorite(uint spellId)
|
||||
|
|
@ -278,14 +282,17 @@ public sealed class SpellcastingUiController : IRetainedPanelController
|
|||
list.CatalogDropped = (payload, x, _) =>
|
||||
{
|
||||
if (payload is SpellbookShortcutDragPayload shortcut)
|
||||
DropSpellbookShortcut(shortcut, targetTab, DropPosition(list, x));
|
||||
DropSpellbookShortcut(
|
||||
shortcut, targetTab, DropPosition(list, x, favorites.Count));
|
||||
};
|
||||
using (list.DeferLayout())
|
||||
{
|
||||
list.Flush();
|
||||
list.Columns = 9;
|
||||
list.SingleRow = true;
|
||||
list.CellWidth = 32f;
|
||||
list.CellHeight = 32f;
|
||||
list.EmptySlotFactory = () => CreateEmptyFavoriteSlot(list, targetTab);
|
||||
list.FillVisibleEmptySlots = true;
|
||||
foreach (uint spellId in favorites)
|
||||
{
|
||||
uint id = spellId;
|
||||
|
|
@ -313,10 +320,59 @@ public sealed class SpellcastingUiController : IRetainedPanelController
|
|||
list.AddItem(slot);
|
||||
}
|
||||
}
|
||||
StampShortcutOverlays(list);
|
||||
}
|
||||
SelectTab(_activeTab);
|
||||
}
|
||||
|
||||
private UiCatalogSlot CreateEmptyFavoriteSlot(UiItemList list, int targetTab)
|
||||
{
|
||||
int shortcutIndex = list.GetNumUIItems();
|
||||
UiCatalogSlot? slot = null;
|
||||
slot = new UiCatalogSlot
|
||||
{
|
||||
SpriteResolve = list.SpriteResolve,
|
||||
EmptySprite = _shortcutGraphics is { EmptySprite: > 0u } graphics
|
||||
? graphics.EmptySprite
|
||||
: 0x060074CFu,
|
||||
Dropped = payload =>
|
||||
{
|
||||
int position = Math.Max(0, list.IndexOf(slot!));
|
||||
int favoriteCount = _spellbook.GetFavorites(targetTab).Count;
|
||||
position = Math.Min(position, favoriteCount);
|
||||
if (payload is SpellFavoriteDragPayload favorite)
|
||||
DropFavorite(favorite, targetTab, position);
|
||||
else if (payload is SpellbookShortcutDragPayload shortcut)
|
||||
DropSpellbookShortcut(shortcut, targetTab, position);
|
||||
},
|
||||
};
|
||||
ConfigureShortcutOverlay(slot, shortcutIndex);
|
||||
return slot;
|
||||
}
|
||||
|
||||
private void StampShortcutOverlays(UiItemList list)
|
||||
{
|
||||
for (int i = 0; i < list.GetNumUIItems(); i++)
|
||||
{
|
||||
UiItemSlot? slot = list.GetItem(i);
|
||||
if (slot is null) continue;
|
||||
ConfigureShortcutOverlay(slot, i);
|
||||
}
|
||||
}
|
||||
|
||||
private void ConfigureShortcutOverlay(UiItemSlot slot, int index)
|
||||
{
|
||||
slot.RegularDigits = _shortcutGraphics?.RegularDigits;
|
||||
slot.GhostedDigits = _shortcutGraphics?.GhostedDigits;
|
||||
slot.EmptyDigits = _shortcutGraphics?.EmptyDigits;
|
||||
if (_shortcutGraphics is { EmptySprite: > 0u } graphics)
|
||||
slot.EmptySprite = graphics.EmptySprite;
|
||||
if (index < 9)
|
||||
slot.SetShortcutNum(index, ghosted: false);
|
||||
else
|
||||
slot.ClearShortcutNum();
|
||||
}
|
||||
|
||||
private void BeginFavoriteDrag(SpellFavoriteDragPayload payload)
|
||||
=> _removeFavorite?.Invoke(payload.SourceTab, payload.SpellId);
|
||||
|
||||
|
|
@ -343,12 +399,12 @@ public sealed class SpellcastingUiController : IRetainedPanelController
|
|||
_selected[targetTab] = payload.SpellId;
|
||||
}
|
||||
|
||||
private static int DropPosition(UiItemList list, int localX)
|
||||
private static int DropPosition(UiItemList list, int localX, int favoriteCount)
|
||||
{
|
||||
int position = list.CellWidth <= 0f
|
||||
? list.GetNumUIItems()
|
||||
? favoriteCount
|
||||
: (int)MathF.Floor(MathF.Max(0f, localX) / list.CellWidth);
|
||||
return Math.Clamp(position, 0, list.GetNumUIItems());
|
||||
return Math.Clamp(position, 0, favoriteCount);
|
||||
}
|
||||
|
||||
private void OnSpellbookChanged() => _favoritesDirty = true;
|
||||
|
|
@ -381,9 +437,9 @@ public sealed class SpellcastingUiController : IRetainedPanelController
|
|||
for (int i = 0; i < list.GetNumUIItems(); i++)
|
||||
if (list.GetItem(i) is UiCatalogSlot slot)
|
||||
slot.Selected = slot.EntryId == _selected[tab];
|
||||
}
|
||||
_endowmentSlot.Selected = _endowmentItemId != 0u
|
||||
&& _endowmentSelected[_activeTab];
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSelectionChanged(SelectionTransition _) => UpdateCastAvailability();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue