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
|
|
@ -21,6 +21,8 @@ namespace AcDream.App.UI;
|
|||
/// </remarks>
|
||||
public sealed class UiScrollbar : UiElement
|
||||
{
|
||||
public override bool ReceivesHoverMouseMove => true;
|
||||
|
||||
/// <summary>The scroll model this bar reflects + drives (shared with the transcript).</summary>
|
||||
public UiScrollable? Model { get; set; }
|
||||
|
||||
|
|
@ -83,6 +85,14 @@ public sealed class UiScrollbar : UiElement
|
|||
/// <summary>Down-arrow button sprite id (0x06004C6C Normal state, element 0x10000072).</summary>
|
||||
public uint DownSprite { get; set; }
|
||||
|
||||
/// <summary>Rollover and pressed media for the start/decrement button.</summary>
|
||||
public uint UpRolloverSprite { get; set; }
|
||||
public uint UpPressedSprite { get; set; }
|
||||
|
||||
/// <summary>Rollover and pressed media for the end/increment button.</summary>
|
||||
public uint DownRolloverSprite { get; set; }
|
||||
public uint DownPressedSprite { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Authored extent of the decrement button along the scrollbar axis. Retail
|
||||
/// positions and subtracts the referenced button's real size; chat uses
|
||||
|
|
@ -108,6 +118,15 @@ public sealed class UiScrollbar : UiElement
|
|||
private bool _draggingThumb;
|
||||
private float _dragOffsetY;
|
||||
private float _dragOffsetX;
|
||||
private EndButton _hoveredButton;
|
||||
private EndButton _pressedButton;
|
||||
|
||||
private enum EndButton
|
||||
{
|
||||
None,
|
||||
Decrement,
|
||||
Increment,
|
||||
}
|
||||
|
||||
public UiScrollbar() { CapturesPointerDrag = true; }
|
||||
|
||||
|
|
@ -201,8 +220,8 @@ public sealed class UiScrollbar : UiElement
|
|||
float incrementExtent = AxisExtent(IncrementButtonExtent, Height);
|
||||
|
||||
// Decrement/up and increment/down use their authored button heights.
|
||||
DrawSprite(ctx, resolve, UpSprite, 0f, 0f, Width, decrementExtent);
|
||||
DrawSprite(ctx, resolve, DownSprite,
|
||||
DrawSprite(ctx, resolve, ActiveStartSprite, 0f, 0f, Width, decrementExtent);
|
||||
DrawSprite(ctx, resolve, ActiveEndSprite,
|
||||
0f, Height - incrementExtent, Width, incrementExtent);
|
||||
|
||||
// Thumb — only when content overflows the view. Retail 3-slice: top cap +
|
||||
|
|
@ -235,8 +254,8 @@ public sealed class UiScrollbar : UiElement
|
|||
float decrementExtent = AxisExtent(DecrementButtonExtent, Width);
|
||||
float incrementExtent = AxisExtent(IncrementButtonExtent, Width);
|
||||
DrawTiled(ctx, resolve, TrackSprite, 0f, 0f, Width, Height);
|
||||
DrawSprite(ctx, resolve, UpSprite, 0f, 0f, decrementExtent, Height);
|
||||
DrawSprite(ctx, resolve, DownSprite,
|
||||
DrawSprite(ctx, resolve, ActiveStartSprite, 0f, 0f, decrementExtent, Height);
|
||||
DrawSprite(ctx, resolve, ActiveEndSprite,
|
||||
Width - incrementExtent, 0f, incrementExtent, Height);
|
||||
if (!model.HasOverflow) return;
|
||||
|
||||
|
|
@ -322,9 +341,24 @@ public sealed class UiScrollbar : UiElement
|
|||
if (IsModelDisabled)
|
||||
{
|
||||
_draggingThumb = false;
|
||||
_hoveredButton = EndButton.None;
|
||||
_pressedButton = EndButton.None;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (e.Type == UiEventType.HoverEnter)
|
||||
{
|
||||
_hoveredButton = ButtonAt(e.Data1, e.Data2);
|
||||
return true;
|
||||
}
|
||||
if (e.Type == UiEventType.HoverLeave)
|
||||
{
|
||||
_hoveredButton = EndButton.None;
|
||||
return true;
|
||||
}
|
||||
if (e.Type == UiEventType.MouseMove)
|
||||
_hoveredButton = ButtonAt(e.Data1, e.Data2);
|
||||
|
||||
if (Horizontal && ScalarChanged is not null)
|
||||
return OnScalarEvent(e);
|
||||
|
||||
|
|
@ -339,6 +373,7 @@ public sealed class UiScrollbar : UiElement
|
|||
{
|
||||
// e.Data1 = local X, e.Data2 = local Y (int pixel coords, see UiRoot hit dispatch).
|
||||
float ly = e.Data2;
|
||||
_pressedButton = ButtonAt(e.Data1, e.Data2);
|
||||
float decrementExtent = AxisExtent(DecrementButtonExtent, Height);
|
||||
float incrementExtent = AxisExtent(IncrementButtonExtent, Height);
|
||||
|
||||
|
|
@ -386,6 +421,7 @@ public sealed class UiScrollbar : UiElement
|
|||
|
||||
case UiEventType.MouseUp:
|
||||
_draggingThumb = false;
|
||||
_pressedButton = EndButton.None;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -400,6 +436,7 @@ public sealed class UiScrollbar : UiElement
|
|||
case UiEventType.MouseDown:
|
||||
{
|
||||
float x = e.Data1;
|
||||
_pressedButton = ButtonAt(e.Data1, e.Data2);
|
||||
float decrementExtent = AxisExtent(DecrementButtonExtent, Width);
|
||||
float incrementExtent = AxisExtent(IncrementButtonExtent, Width);
|
||||
if (x < decrementExtent) { m.ScrollByLines(-1); return true; }
|
||||
|
|
@ -437,6 +474,7 @@ public sealed class UiScrollbar : UiElement
|
|||
|
||||
case UiEventType.MouseUp:
|
||||
_draggingThumb = false;
|
||||
_pressedButton = EndButton.None;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -475,6 +513,7 @@ public sealed class UiScrollbar : UiElement
|
|||
|
||||
case UiEventType.MouseUp:
|
||||
_draggingThumb = false;
|
||||
_pressedButton = EndButton.None;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -497,6 +536,48 @@ public sealed class UiScrollbar : UiElement
|
|||
ScalarChanged?.Invoke(ScalarPosition);
|
||||
}
|
||||
|
||||
private EndButton ButtonAt(float x, float y)
|
||||
{
|
||||
if (x < 0f || x >= Width || y < 0f || y >= Height)
|
||||
return EndButton.None;
|
||||
|
||||
if (Horizontal)
|
||||
{
|
||||
if (x < AxisExtent(DecrementButtonExtent, Width))
|
||||
return EndButton.Decrement;
|
||||
if (x >= Width - AxisExtent(IncrementButtonExtent, Width))
|
||||
return EndButton.Increment;
|
||||
return EndButton.None;
|
||||
}
|
||||
|
||||
if (y < AxisExtent(DecrementButtonExtent, Height))
|
||||
return EndButton.Decrement;
|
||||
if (y >= Height - AxisExtent(IncrementButtonExtent, Height))
|
||||
return EndButton.Increment;
|
||||
return EndButton.None;
|
||||
}
|
||||
|
||||
private uint ActiveStartSprite
|
||||
=> _pressedButton == EndButton.Decrement
|
||||
&& _hoveredButton == EndButton.Decrement
|
||||
&& UpPressedSprite != 0u
|
||||
? UpPressedSprite
|
||||
: _hoveredButton == EndButton.Decrement && UpRolloverSprite != 0u
|
||||
? UpRolloverSprite
|
||||
: UpSprite;
|
||||
|
||||
private uint ActiveEndSprite
|
||||
=> _pressedButton == EndButton.Increment
|
||||
&& _hoveredButton == EndButton.Increment
|
||||
&& DownPressedSprite != 0u
|
||||
? DownPressedSprite
|
||||
: _hoveredButton == EndButton.Increment && DownRolloverSprite != 0u
|
||||
? DownRolloverSprite
|
||||
: DownSprite;
|
||||
|
||||
internal uint ActiveStartSpriteForTest => ActiveStartSprite;
|
||||
internal uint ActiveEndSpriteForTest => ActiveEndSprite;
|
||||
|
||||
private static float AxisExtent(float authoredExtent, float axisLength)
|
||||
=> Math.Clamp(authoredExtent, 0f, MathF.Max(0f, axisLength));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue