feat(ui): unify retained window mounts

This commit is contained in:
Erik 2026-07-10 22:22:25 +02:00
parent 6e9e10367f
commit a8e9503d2e
20 changed files with 823 additions and 366 deletions

View file

@ -79,7 +79,7 @@ public static class DatWidgetFactory
6 => new UiMenu(), // UIElement_Menu (reg :120163)
7 => BuildMeter(info, resolve, elementFont), // UIElement_Meter
0xD => new UiViewport(), // UIElement_Viewport — 3-D mini-scene blit leaf
11 => new UiScrollbar(), // UIElement_Scrollbar (reg :124137)
11 => BuildScrollbar(info, resolve), // UIElement_Scrollbar (reg :124137)
12 => BuildText(info, resolve, elementFont), // UIElement_Text (reg :115655)
0x10000031u => new UiItemList(resolve), // UIElement_ItemList — toolbar/inventory/paperdoll slots
_ => new UiDatElement(info, resolve), // generic fallback (incl. Type 3 chrome/containers)
@ -129,6 +129,68 @@ public static class DatWidgetFactory
return e;
}
/// <summary>
/// Bind inherited scrollbar media structurally. Property 0x77 names the
/// increment button and 0x78 the decrement button; the remaining Type-1
/// child is the thumb with ordered top/middle/bottom image slices.
/// </summary>
private static UiScrollbar BuildScrollbar(
ElementInfo info,
Func<uint, (uint tex, int w, int h)> resolve)
{
var bar = new UiScrollbar
{
SpriteResolve = resolve,
TrackSprite = DefaultImage(info),
};
uint incrementId = ReferencedElementId(info, 0x77u);
uint decrementId = ReferencedElementId(info, 0x78u);
ElementInfo? increment = info.Children.FirstOrDefault(child => child.Id == incrementId);
ElementInfo? decrement = info.Children.FirstOrDefault(child => child.Id == decrementId);
bar.UpSprite = decrement is null ? 0u : DefaultImage(decrement);
bar.DownSprite = increment is null ? 0u : DefaultImage(increment);
ElementInfo? thumb = info.Children.FirstOrDefault(child =>
child.Type == 1u && child.Id != incrementId && child.Id != decrementId);
if (thumb is not null)
{
ElementInfo[] slices = thumb.Children
.Where(child => DefaultImage(child) != 0u)
.OrderBy(child => child.Y)
.ThenBy(child => child.ReadOrder)
.ToArray();
if (slices.Length > 0) bar.ThumbTopSprite = DefaultImage(slices[0]);
if (slices.Length > 1) bar.ThumbSprite = DefaultImage(slices[1]);
if (slices.Length > 2) bar.ThumbBotSprite = DefaultImage(slices[^1]);
}
return bar;
}
private static uint ReferencedElementId(ElementInfo info, uint propertyId)
{
if (!info.TryGetEffectiveProperty(propertyId, out var property))
return 0u;
return property.Kind switch
{
UiPropertyKind.Enum or UiPropertyKind.DataId => (uint)property.UnsignedValue,
UiPropertyKind.Integer when property.IntegerValue >= 0 => (uint)property.IntegerValue,
_ => 0u,
};
}
private static uint DefaultImage(ElementInfo info)
{
uint stateId = info.EffectiveDefaultStateId();
if (info.States.TryGetValue(stateId, out var state) && state.Image is { } image)
return image.File;
if (info.States.TryGetValue(UiStateInfo.DirectStateId, out var direct)
&& direct.Image is { } directImage)
return directImage.File;
return 0u;
}
// ── Meter ────────────────────────────────────────────────────────────────
/// <summary>