Skip to content
Version 2.11.0
↓ Get the launcher

WindowSpec record

Immutable declaration of a plugin window’s identity, initial geometry, and chrome options. Passed to Register to create the window.

public record WindowSpec : IRenderGated
name description
Anchor { get; set; } Anchor for the initial placement of DefaultRect on the (possibly scaled) window canvas. Defaults to TopLeft = legacy absolute top-left. Use Center etc. to center/corner-anchor without computing the UI scale yourself; DefaultRect.X/Y then act as a canvas-unit offset from the anchor. A user’s saved drag still overrides this.
AutoSizeWidth { get; set; } Content-size the window WIDTH to its body instead of fixing it to DefaultRect.Width. Only safe for windows without wrapping text (e.g. the launcher’s fixed-width icon tiles) — the in-world clip bug that forced fixed width was a wrapping-text problem the launcher does not have. Defaults false.
BackgroundOpacity { get; set; } Borderless windows only: poll-diffed black background opacity (0 = transparent, 1 = fully black). Applied to the root’s existing click-blocker Image so the background fills the entire window rect and expands when the user resizes height — no separate child GO needed. Null = no background (default).
Category { get; set; } Logical category that determines which group this window appears in within the layout editor.
Closable { get; set; } When true the chrome draws a ✕ close glyph that hides the window. Defaults false (plugin windows manage their own visibility). Independent of Draggable so a window can be draggable without a close button (e.g. the Settings hub).
DefaultRect { get; set; } Initial position and size applied on first run (before user adjustments are persisted).
DismissOnOutsideClick { get; set; } When true the framework auto-hides this window on Escape or a mouse press outside its rect — the click-away dismiss a cursor popup / context menu wants. The dismiss invokes the registration’s OnClose (wire it to SetVisible(false)); with no OnClose the flag is inert. Handled on the per-render-frame interaction ticker, NOT the throttled framework tick, so it never misses a one-frame click/key edge (a press lasting one rendered frame would be missed by a plugin polling input from its throttled OnUpdate).
Draggable { get; set; } When true the window is a movable dialog: drag-by-title-bar (the post-drag rect is committed + persisted) and excluded from the Shift+` Layout editor (it owns its own position). When false the window is positioned via the Layout editor and any title-bar drag is discarded. Defaults false. Settings windows + opt-in plugin panels (e.g. StatInspector settings) set this true.
EditModeDragOnly { get; set; } When true the chrome draws a bottom-right ↘ resize grip; dragging it changes the window size (clamped to MinWidth/MinHeight .. MaxWidth/MaxHeight), and the new size persists alongside the position. The window’s vertical content-fit is disabled (fixed height); a ScrollElement in the body fills the freed space. Defaults false. The CombatMeter list uses this.
Id { get; set; } Stable string id, unique per plugin. Used to persist position and hotkey binding.
MaxHeight { get; set; } Maximum allowed window height in pixels when Resizable is true.
MaxWidth { get; set; } Maximum allowed window width in pixels when Resizable is true.
MinHeight { get; set; } Minimum allowed window height in pixels when Resizable is true.
MinWidth { get; set; } Minimum allowed window width in pixels when Resizable is true.
Resizable { get; set; } When true the chrome draws a resize grip; dragging it changes the window size (clamped to Min/Max bounds).
ShouldRender { get; set; } The single source of visibility truth (hide = !ShouldRender(), evaluated each apply ~10 Hz). Compiler-required: every WindowSpec MUST set it or the build fails. Read whatever you want — Phase, UiState, your own state — via the plugin’s captured services. Use () => true for always-on chrome, () => _services.ClientState.Phase == GamePhase.World for a gameplay window.
ShowTitleBar { get; set; } GlassMenu only: draw the top title bar. Defaults true. Set false for windows that self-compose their own header inside the body (the launcher, whose header is top in Full/vertical but a LEFT strip in horizontal — a single fixed top bar can’t express both). With no title bar the whole frame becomes the drag handle (if Draggable), and the body must supply its own close affordance.
StartVisible { get; set; } Whether the window is visible on first run (before user toggles via hotkey).
Style { get; set; } Visual chrome style applied to the window frame.
Surface { get; set; } Which render surface the window’s Text / Bar (Default style) / Pill leaves use. Defaults to Menu = the window theme chrome (unchanged — every existing window keeps rendering exactly as before). Set HudOverlay to reproduce the borderless HUD look for those leaves: shadowed text over the world, rounded HP-bar chrome, and a transparent pill chip — byte-identical to the native HUD renderer, so a HUD-path plugin can migrate onto the window path with pixel-exact fidelity. Only those three leaf types are affected; all other widgets render identically either way. The positional constructor is unchanged, so this is non-breaking.
Title { get; set; } Display title shown in the title bar and Settings layout editor.
ZOrder { get; set; } Explicit draw-order among Stellar windows: HIGHER draws on top. Default 0. The framework stacks windows by (ZOrder, then Category as a tiebreak — HUD<Tools<Debug — then Id), so a plugin that sets this fully controls where its window sits relative to others regardless of load/mount order; one that leaves it 0 falls back to the category default. Click-away (DismissOnOutsideClick) popups always render above all of these.