feat(ui): port retail UIElement_Dragbar so authored drag strips move their windows
Some checks are pending
Headless portability / portable-headless (ubuntu-latest) (push) Waiting to run
Headless portability / portable-headless (windows-latest) (push) Waiting to run
Headless portability / linux-graphical (push) Waiting to run
Headless portability / linux-vulkan (push) Waiting to run
Some checks are pending
Headless portability / portable-headless (ubuntu-latest) (push) Waiting to run
Headless portability / portable-headless (windows-latest) (push) Waiting to run
Headless portability / linux-graphical (push) Waiting to run
Headless portability / linux-vulkan (push) Waiting to run
The combat bar and spell bar could not be moved at all: their window mounts Draggable=false (correct - retail never whole-surface-drags them) and the authored move mechanism was missing. Retail registers element class 2 as UIElement_Dragbar (Register @ 0x0046C840); a press inside it calls UIElement::StartMovement on its parent window (StartMouseMoving @ 0x0046C760) and release calls StopMovement (@ 0x0046C7C0). The combat/spell bar layout (LayoutDesc 0x21000073) authors exactly one such element - a 600 x 5 strip along the top edge, which is where the user expects the move cursor. The powerbar, vitals, indicators, radar, and examination layouts author dragbars too, so they all gain their retail handles from this one port. Our importer knew Type 2 by name but built it as a generic UiDatElement - ClickThrough decoration, so the strip never even claimed the pointer. Now: - UiElement.WindowMoveHandle marks an authored handle; the DAT factory sets it for Type-2 elements and opts them out of ClickThrough. - A left-press inside a handle subtree moves the handle's top-level window (the outer frame directly under the root - the mounted analogue of retail's dragbar parent) even when that window is not whole-surface Draggable. Edge-resize still wins; UiLocked still gates, matching the retail locked/fixed parent-flag check. - HoverWindowMove reports the handle so the window-move cursor shows over the strip - and only there - on non-Draggable windows. Four new tests: handle press moves a non-Draggable window and stops on release, hover shows the move cursor over the strip but not the body, UiLocked suppresses both, and the factory builds Type 2 as a pointer-claiming move handle. App Release suite 3,966 / 3 skips. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
67379d1f9a
commit
e4c99f54c0
5 changed files with 151 additions and 7 deletions
|
|
@ -36,6 +36,20 @@ public class DatWidgetFactoryTests
|
|||
Assert.IsType<UiDatElement>(e);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Type2_Dragbar_IsWindowMoveHandle_AndClaimsThePointer()
|
||||
{
|
||||
// Retail UIElement_Dragbar (element class 2, Register @ 0x0046C840). It must
|
||||
// opt back out of UiDatElement's decoration ClickThrough so the press reaches
|
||||
// it and starts the window move.
|
||||
var e = DatWidgetFactory.Create(
|
||||
new ElementInfo { Type = 2, Width = 600, Height = 5 }, NoTex, null);
|
||||
|
||||
Assert.IsType<UiDatElement>(e);
|
||||
Assert.True(e.WindowMoveHandle);
|
||||
Assert.False(e.ClickThrough);
|
||||
}
|
||||
|
||||
// ── Test 3: Type 12 → UiText (behavioral text widget) ────────────────────
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -235,6 +235,80 @@ public class UiRootInputTests
|
|||
Assert.Equal(80f, panel.Top);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DragHandle_MovesNonDraggableWindow_AndStopsOnRelease()
|
||||
{
|
||||
// Retail UIElement_Dragbar (element class 2, Register @ 0x0046C840): a press on
|
||||
// the authored handle moves a window that is NOT whole-surface Draggable
|
||||
// (StartMouseMoving @ 0x0046C760 → UIElement::StartMovement on the parent).
|
||||
// The combat/spell bar (LayoutDesc 0x21000073) authors a 600x5 strip at Y=0 and
|
||||
// mounts Draggable=false; before the fix the strip was inert and the bars
|
||||
// could not be moved at all.
|
||||
var root = new UiRoot { Width = 800, Height = 600 };
|
||||
var window = new UiPanel { Left = 100, Top = 500, Width = 610, Height = 90 };
|
||||
var handle = new UiPanel
|
||||
{
|
||||
Left = 5, Top = 0, Width = 600, Height = 5,
|
||||
WindowMoveHandle = true,
|
||||
};
|
||||
window.AddChild(handle);
|
||||
root.AddChild(window);
|
||||
|
||||
root.OnMouseDown(UiMouseButton.Left, 110, 502); // press inside the top strip
|
||||
Assert.True(root.IsWindowMoveActive);
|
||||
root.OnMouseMove(160, 452);
|
||||
Assert.Equal(150f, window.Left);
|
||||
Assert.Equal(450f, window.Top);
|
||||
|
||||
root.OnMouseUp(UiMouseButton.Left, 160, 452);
|
||||
root.OnMouseMove(300, 300); // released — must not move
|
||||
Assert.Equal(150f, window.Left);
|
||||
Assert.Equal(450f, window.Top);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DragHandle_Hover_ShowsMoveCursor_WindowBodyDoesNot()
|
||||
{
|
||||
var root = new UiRoot { Width = 800, Height = 600 };
|
||||
var window = new UiPanel { Left = 100, Top = 500, Width = 610, Height = 90 };
|
||||
var handle = new UiPanel
|
||||
{
|
||||
Left = 5, Top = 0, Width = 600, Height = 5,
|
||||
WindowMoveHandle = true,
|
||||
};
|
||||
window.AddChild(handle);
|
||||
root.AddChild(window);
|
||||
|
||||
root.OnMouseMove(110, 502); // over the authored strip
|
||||
Assert.True(root.HoverWindowMove);
|
||||
|
||||
root.OnMouseMove(110, 550); // over the body of the non-Draggable window
|
||||
Assert.False(root.HoverWindowMove);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DragHandle_UiLocked_NeitherMovesNorShowsCursor()
|
||||
{
|
||||
var root = new UiRoot { Width = 800, Height = 600 };
|
||||
var window = new UiPanel { Left = 100, Top = 500, Width = 610, Height = 90 };
|
||||
var handle = new UiPanel
|
||||
{
|
||||
Left = 5, Top = 0, Width = 600, Height = 5,
|
||||
WindowMoveHandle = true,
|
||||
};
|
||||
window.AddChild(handle);
|
||||
root.AddChild(window);
|
||||
root.UiLocked = true;
|
||||
|
||||
root.OnMouseMove(110, 502);
|
||||
Assert.False(root.HoverWindowMove);
|
||||
|
||||
root.OnMouseDown(UiMouseButton.Left, 110, 502);
|
||||
root.OnMouseMove(160, 452);
|
||||
Assert.Equal(100f, window.Left);
|
||||
Assert.Equal(500f, window.Top);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WindowDrag_ConstrainedPanel_StaysFullyInsideParent()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue