using System; namespace AcDream.App.UI.Layout; /// /// Behavioral root widget for the vitals windows — retail's gmVitalsUI /// family (gmVitalsUI 0x10000009, gmFloatyVitalsUI 0x1000004D = /// the stacked window LayoutDesc 0x2100006C, gmFloatySideVitalsUI /// 0x10000056 = the side-by-side window LayoutDesc 0x21000075; registrations /// @0x004BFE10 / @0x004CED90 / @0x004D0490). /// /// /// The click toggle (gmVitalsUI::ListenToElementMessage @0x004BFC00, /// inherited verbatim by both floaty subclasses): on Element_mouse_press /// (0x1C) with dwParam1 7 (left) or 0xA (right) the root flips /// SetState(m_state == HideDetail ? ShowDetail : HideDetail). /// State semantics from the authored data + UIElement::OnSetAttribute /// case 8 (0x3B = invisible, SetVisible(value == 0) @0x00462DAE): /// /// /// Undef (login default; DefaultState is authored /// Undef and nothing calls SetState at init): numbers visible, no icons — /// visually identical to HideDetail. /// HideDetail (0x10000006): the numeric mode — the /// cur/max labels author {0x3B:false} (visible); the icon overlays author /// File=0 (nothing). /// ShowDetail (0x10000007): the graphical mode — /// labels author {0x3B:true} (hidden); each bar shows its authored icon pair /// (dim back + bright fill-clipped front: heart / sword / scepter). /// /// /// /// The first press from Undef lands on HideDetail (retail's exact expression: /// ecx = (m_state == 0x10000006); SetState(ecx + 0x10000006) — any /// state that is not HideDetail, including the initial Undef, goes to /// HideDetail first), so the first click appears to do nothing and the second /// enters icon mode. NOT persisted anywhere: gmGamePlayUI::SaveScreenLayout /// @0x004EAD50 writes only window rects, and no PlayerModule option is /// touched — the mode resets to Undef every session, per window. /// /// /// /// Press routing: retail broadcasts the press to the pressed element /// and forwards up the parent chain (UIElement::ListenToElementMessage /// @0x00462340 → ForwardElementMessage), but UIElement_Dragbar /// (@0x0046C850) and UIElement_Resizebar (@0x0046B930) both return 2 /// (consumed) unconditionally — a press that starts a window move or resize /// never reaches the vitals root. Mirrored here: presses whose hit target is a /// move handle or resize grip are ignored. The handler returns false so the /// event keeps bubbling, matching retail's fall-through to the base handler. /// /// public sealed class UiVitalsRoot : UiDatElement { /// gmVitalsUI registered element class (@0x004BFE1A). public const uint GmVitalsClassId = 0x10000009u; /// gmFloatyVitalsUI registered element class (@0x004CED9A) — the stacked window root. public const uint GmFloatyVitalsClassId = 0x1000004Du; /// gmFloatySideVitalsUI registered element class (@0x004D049A) — the side-by-side window root. public const uint GmFloatySideVitalsClassId = 0x10000056u; public UiVitalsRoot(ElementInfo info, Func resolve) : base(info, resolve) { } public override bool OnEvent(in UiEvent e) { if (e.Type is UiEventType.MouseDown or UiEventType.RightDown && !PressConsumedByChrome(e.Target)) { // gmVitalsUI::ListenToElementMessage @0x004BFC04: // this->SetState(m_state == HideDetail ? ShowDetail : HideDetail) // then falls through to the base handler (keep bubbling). TrySetRetailState( ActiveRetailStateId == RetailUiStateIds.HideDetail ? RetailUiStateIds.ShowDetail : RetailUiStateIds.HideDetail); } return base.OnEvent(in e); } /// /// True when the pressed element is (or sits inside) a window-move handle /// or resize grip — the two retail element classes that consume the press /// before it can bubble to the vitals root. /// private bool PressConsumedByChrome(UiElement? target) { for (UiElement? w = target; w is not null && w != this; w = w.Parent) if (w.WindowMoveHandle || w is UiResizeGrip) return true; return false; } }