fix: complete retail parity stability pass
This commit is contained in:
parent
d3df4cb20a
commit
f7aa8e0eb7
131 changed files with 7765 additions and 1190 deletions
|
|
@ -85,21 +85,20 @@ public sealed class UiMenu : UiElement
|
|||
public float ColumnWidth { get; set; } = 191f; // dat item template W=191
|
||||
|
||||
/// <summary>
|
||||
/// G5 (vendor gate finding): retail's authored vendor category popup
|
||||
/// Retail's authored vendor category popup
|
||||
/// (LayoutDesc <c>0x21000043</c>, root <c>0x1000034F</c>) pairs its
|
||||
/// ListBox (element <c>0x10000350</c>, type <c>0x5</c>) with a SIBLING
|
||||
/// <c>UIElement_Scrollbar</c> (element <c>0x10000351</c>, type <c>0xB</c>,
|
||||
/// 16px wide, docked immediately right of the list at x=100) — verified
|
||||
/// via a live-dat scan (<c>tools/VendorLayoutScan</c>) against
|
||||
/// <c>client_local_English.dat</c>: the ListBox reads a single-column
|
||||
/// shape (attributes resolving to <c>m_nCols=1</c>/<c>m_nRows=6</c>) and
|
||||
/// the row template (<c>0x10000352</c>) is 100×18 — a SCROLLABLE single
|
||||
/// column with 6 visible rows, not our earlier column-major grid
|
||||
/// approximation (which showed all 18 categories at once across 3
|
||||
/// columns, never matching the retail screenshot's ~one-column-with-
|
||||
/// scrollbar look). <see cref="RowsPerColumn"/> becomes the VISIBLE ROW
|
||||
/// COUNT in this mode (still authored-driven — 108px ListBox height / 18px
|
||||
/// row height = 6). Chat's own popup (LayoutDesc <c>0x21000006</c>) has
|
||||
/// via installed-DAT inspection against <c>client_local_English.dat</c>.
|
||||
/// The authored 100x108 ListBox starts with a six-row viewport, but retail
|
||||
/// <c>UIElement_ListBox::UpdateLayout @0x0046e460</c> resizes its scrollable
|
||||
/// content to the number of inserted categories. Because all four edges are
|
||||
/// docked, message <c>0x32</c> reaches
|
||||
/// <c>UIElement_Menu::RecalculatePopupSize @0x0046caf0</c> and grows or
|
||||
/// shrinks the popup to that content. The sibling scrollbar authors property
|
||||
/// <c>0x79=true</c>, so it disappears once the resized viewport fits the
|
||||
/// content. Chat's own popup (LayoutDesc <c>0x21000006</c>) has
|
||||
/// NO sibling scrollbar element and keeps the class default false — the
|
||||
/// legacy column-major grid path below is untouched for it.
|
||||
/// </summary>
|
||||
|
|
@ -140,6 +139,24 @@ public sealed class UiMenu : UiElement
|
|||
public uint ScrollUpSprite { get; set; }
|
||||
public uint ScrollDownSprite { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Retail scrollbar property <c>0x79</c> for the popup's authored sibling
|
||||
/// scrollbar. When the content fits and the scrollbar is therefore disabled,
|
||||
/// hide the sibling completely: no chrome, pointer target, or reserved width.
|
||||
/// Vendor popup element <c>0x10000351</c> authors this true. Retail owns a real
|
||||
/// sibling widget and calls <c>SetVisible(false)</c> from
|
||||
/// <c>UIElement_Scrollbar::UpdateLayout @0x004710d0</c>; this procedural popup
|
||||
/// must also remove the sibling's width from its flattened geometry to produce
|
||||
/// the same visible result.
|
||||
/// </summary>
|
||||
public bool PopupScrollbarHideWhenDisabled { get; set; }
|
||||
|
||||
/// <summary>Presentation projection shared by drawing and pointer dispatch.
|
||||
/// Internal for the same focused-test purpose as
|
||||
/// <see cref="UiScrollbar.IsPresentationVisible"/>.</summary>
|
||||
internal bool IsPopupScrollbarPresentationVisible
|
||||
=> !PopupScrollbarHideWhenDisabled || PopupContentOverflows;
|
||||
|
||||
private bool _draggingPopupThumb;
|
||||
private float _popupThumbDragOffset;
|
||||
|
||||
|
|
@ -252,9 +269,8 @@ public sealed class UiMenu : UiElement
|
|||
/// popup ListBox (0x21000043/0x10000358) reads edges L=T=R=B=1
|
||||
/// (menuprobe3, <c>OptionsPanelLiveMountProbeTests</c>), so
|
||||
/// <see cref="AcDream.App.UI.Layout.ConfigOptionsPageController"/> sets
|
||||
/// this true; chat's grid popup and vendor's shipped 6-row window keep
|
||||
/// the class default false (vendor's authored ListBox is ALSO docked —
|
||||
/// tracked as its own issue, not silently reworked here).
|
||||
/// this true. Vendor's ListBox has the same four docked edges and therefore
|
||||
/// enables it too; chat's grid popup keeps the class default false.
|
||||
/// When set, <see cref="RowsPerColumn"/> stops being the visible-window
|
||||
/// height and the popup shows every item with no scroll overflow.
|
||||
/// </summary>
|
||||
|
|
@ -333,12 +349,14 @@ public sealed class UiMenu : UiElement
|
|||
|
||||
// Interior = the row content; Outer = interior + the 8-piece bevel ring.
|
||||
// Scrollable: always exactly one column (RowsPerColumn is the VISIBLE window,
|
||||
// not a wrap threshold), widened by the docked scrollbar's own authored width.
|
||||
// not a wrap threshold), widened by the docked scrollbar only while that
|
||||
// sibling is presentation-visible. Retail property 0x79 removes a disabled
|
||||
// scrollbar completely; keeping its width caused #386's empty placeholder.
|
||||
private int ColumnCount => Scrollable
|
||||
? 1
|
||||
: (Items.Count + RowsPerColumn - 1) / System.Math.Max(1, RowsPerColumn);
|
||||
private float InteriorW => Scrollable
|
||||
? ColumnWidth + ScrollbarWidth
|
||||
? ColumnWidth + EffectiveScrollbarWidth
|
||||
: ColumnCount * ColumnWidth;
|
||||
|
||||
/// <summary>The popup's visible row count. Size-to-content (retail's
|
||||
|
|
@ -352,6 +370,15 @@ public sealed class UiMenu : UiElement
|
|||
? System.Math.Max(1, Items.Count)
|
||||
: RowsPerColumn;
|
||||
|
||||
/// <summary>UiMenu rows have a fixed authored height, so this is the same
|
||||
/// overflow decision <see cref="ConfigurePopupScroll"/> publishes to
|
||||
/// <see cref="PopupScroll"/>, but is stable before the first draw/event has
|
||||
/// configured that model.</summary>
|
||||
private bool PopupContentOverflows => Items.Count > EffectiveVisibleRows;
|
||||
|
||||
private float EffectiveScrollbarWidth
|
||||
=> IsPopupScrollbarPresentationVisible ? ScrollbarWidth : 0f;
|
||||
|
||||
private float InteriorH => EffectiveVisibleRows * RowHeight;
|
||||
private float OuterW => InteriorW + 2 * Border;
|
||||
private float OuterH => InteriorH + 2 * Border;
|
||||
|
|
@ -362,6 +389,11 @@ public sealed class UiMenu : UiElement
|
|||
/// a full render pass.</summary>
|
||||
public float PopupOuterHeight => OuterH;
|
||||
|
||||
/// <summary>The popup's outer (bevel-inclusive) width. Exposed alongside
|
||||
/// <see cref="PopupOuterHeight"/> so the retail hide-disabled scrollbar rule
|
||||
/// can be pinned without a GPU render harness.</summary>
|
||||
public float PopupOuterWidth => OuterW;
|
||||
|
||||
/// <summary>
|
||||
/// G7 (vendor gate finding, item 2 — popup direction): port of retail
|
||||
/// <c>UIElement_Menu::Open</c> (pc:120210-120252, <c>0x0046cc30</c>)'s Y placement:
|
||||
|
|
@ -558,10 +590,11 @@ public sealed class UiMenu : UiElement
|
|||
/// <summary>
|
||||
/// G5: single-column popup with a docked scrollbar — port of the vendor category
|
||||
/// dropdown's authored shape (LayoutDesc <c>0x21000043</c>, see <see cref="Scrollable"/>'s
|
||||
/// doc comment). Draws exactly <see cref="RowsPerColumn"/> rows (the authored visible
|
||||
/// window), sliced from <see cref="Items"/> starting at <see cref="VisibleTopRow"/>, plus
|
||||
/// the scrollbar chrome using the SAME thumb geometry <see cref="UiScrollbar"/> itself
|
||||
/// uses (<see cref="UiScrollbar.ThumbRect"/>).
|
||||
/// doc comment). Draws <see cref="EffectiveVisibleRows"/> rows, sliced from
|
||||
/// <see cref="Items"/> starting at <see cref="VisibleTopRow"/>, plus the scrollbar
|
||||
/// chrome when its authored disabled-presentation rule allows it. Thumb geometry
|
||||
/// is the same helper <see cref="UiScrollbar"/> itself uses
|
||||
/// (<see cref="UiScrollbar.ThumbRect"/>).
|
||||
/// </summary>
|
||||
private void DrawScrollablePopup(UiRenderContext ctx, Func<uint, (uint tex, int w, int h)> resolve)
|
||||
{
|
||||
|
|
@ -602,9 +635,9 @@ public sealed class UiMenu : UiElement
|
|||
{
|
||||
int lineHeight = System.Math.Max(1, (int)MathF.Round(RowHeight));
|
||||
PopupScroll.LineHeight = lineHeight;
|
||||
// Size-to-content: view == content, so HasOverflow is false and the
|
||||
// scrollbar draws its chrome with no thumb (retail's authored
|
||||
// scrollbar sibling stretches with the docked popup the same way).
|
||||
// Size-to-content: view == content, so HasOverflow is false. Whether
|
||||
// the disabled scrollbar remains visible is its authored 0x79 property,
|
||||
// projected by IsPopupScrollbarPresentationVisible.
|
||||
PopupScroll.SetExtents(Items.Count * lineHeight, EffectiveVisibleRows * lineHeight);
|
||||
}
|
||||
|
||||
|
|
@ -625,6 +658,8 @@ public sealed class UiMenu : UiElement
|
|||
private void DrawPopupScrollbar(
|
||||
UiRenderContext ctx, Func<uint, (uint tex, int w, int h)> resolve, float x, float y)
|
||||
{
|
||||
if (!IsPopupScrollbarPresentationVisible) return;
|
||||
|
||||
DrawSprite(ctx, resolve, ScrollTrackSprite, x, y, ScrollbarWidth, InteriorH);
|
||||
|
||||
float decExtent = System.Math.Clamp(ScrollButtonExtent, 0f, InteriorH);
|
||||
|
|
@ -801,7 +836,9 @@ public sealed class UiMenu : UiElement
|
|||
}
|
||||
|
||||
float scrollbarX = ColumnWidth;
|
||||
if (ix >= scrollbarX && ix < scrollbarX + ScrollbarWidth && iy >= 0 && iy < InteriorH)
|
||||
if (IsPopupScrollbarPresentationVisible
|
||||
&& ix >= scrollbarX && ix < scrollbarX + ScrollbarWidth
|
||||
&& iy >= 0 && iy < InteriorH)
|
||||
{
|
||||
ConfigurePopupScroll();
|
||||
float decExtent = System.Math.Clamp(ScrollButtonExtent, 0f, InteriorH);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue