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;
}
}