UI Screen Authoring
For campaign loading and asset diagnostics, see WC3 loading and assets.
FDF-Driven Layout
- In client/UI code, never define or hardcode UI elements, layout coordinates, textures, frame names, or control structures that can be read from FDF. Parse and reuse the actual FDF frames/templates, then bind dynamic data into those frames.
- WC3 game code has access to the FDF parser and uses it for frames that exist in War3.mpq (e.g. building detail, resource bar, quest dialog). Native runtime controls with no MPQ frame geometry, such as the portrait, command-card buttons, minimap content, and tooltips, use compact proxy frames serialized via
UI_WriteProxyFrame. - Keep proxy-frame buffers as compact wire schemas, not copies of runtime structs. See Server-Authored UI Payloads.
No project-owned FDF
There are no project-owned FDF files under share/UI/FrameDef/. Every FDF frame used by OpenWarcraft comes from the War3.mpq
archives. The authoritative source for any frame's geometry is always the MPQ FDF, never C code and never a project overlay.
When you need a frame that exists in the MPQ, load it with UI_EnsureFDF and generate a binding header with fdfbindgen
(see AGENTS.md §WC3 UI Tooling). Examples: InfoPanelBuildingDetail.fdf for the building-detail HUD sub-panel; QuestDialog.fdf
for the quest window; MapListBox.fdf for a list-box control reused by campaign and multiplayer screens.
fdfbindgen reserves space for the _H suffix even at its maximum prefix length. Duplicate-name
suffixes and frame-type copies fail with a diagnostic if they exceed their destination buffer;
they must not silently truncate generated identifiers. Run python3 tests/test_fdfbindgen.py
after building the tool to check the name boundaries.
For runtime controls that have no FDF geometry in War3.mpq — portraits, command buttons, minimap content, tooltip payloads — construct a
small proxy frame in C. Warsmash ships a project-owned UI\FrameDef\SmashUI\UnitPortrait.fdf to describe its portrait geometry;
that file is not a retail MPQ source and must not be introduced as an OpenRealm data dependency. OpenRealm's portrait remains a runtime
proxy, with its coordinates kept in parity with the reference runtime layout.
Passive FDF tooltips
uiFrame_t.tooltip is a generic hover contract, not a command-button-only field. Persistent HUD hit testing treats a frame with non-empty tooltip text as hoverable even when onclick is empty; this is required for passive FDF labels such as the WC3 resource bar. Do not add fake click commands merely to make a frame hoverable. FT_TOOLTIPTEXT remains the shared presentation proxy. When multiple visible HUD layers serialize that presentation frame, the client draws it only in the layer/window that owns the current hovered source.
For the WC3 resource bar, ResourceBarGoldText, ResourceBarLumberText, ResourceBarSupplyText, and ResourceBarUpkeepText are the stable named anchors. The retail resource icon textures are unnamed, so bind passive resource help to the named FDF value frames rather than creating project-owned icon geometry. Gold/Lumber/Food resource Tip headings use the generic {value} marker; SCR_GetTooltipText() expands it from the hovered frame's current Stat display value, preventing a server-authored layout packet from freezing an older amount while replicated playerstate continues to change. The descriptive Ubertip remains driven by Warcraft GlobalStrings.fdf. Upkeep prefers split RESOURCE_UBERTIP_UPKEEP_INFO[_WOOD] rows, preserves a base body that already contains tier ranges, and generates a gameplay-data-backed fallback legend if neither representation supplies one. See WC3 food and upkeep.
The same passive-frame contract applies to the WC3 selected-unit status strip. Runtime FT_SIMPLEFRAME status icons carry
Bufftip / Buffubertip text but no click command, and LAYER_INFOPANEL must serialize an FT_TOOLTIPTEXT presenter so the
client can draw that tooltip in the layer that owns the hovered icon. Do not make buff icons fake command buttons just to obtain
hover behavior. See WC3 economy and unit presentation.
TextLength semantics
WC3 FDF TextLength is layout geometry, not merely a character limit. When a text frame has no explicit Width, its implicit width
is TextLength multiplied by the active font's space-glyph advance. uiFrame_t.textLength therefore belongs to the retained UI wire
contract and must survive MSG_WriteDeltaUIFrame / MSG_ReadDeltaUIFrame; the client resolves the final width with its registered
font metrics. Falling back to the current string's natural width breaks right-aligned resource fields because their right anchor stays
correct while the left edge moves with the number of digits.
Single-line FDF text with no authored Width/Height uses the declared FDF font size as its layout line-box height. The renderer's
glyph bounds are drawing metrics and may be shorter; using them as layout height causes chained labels to drift vertically. Conversely,
when an FDF frame has an authored Width or Height, that dimension remains authoritative even if both opposing anchors are present.
On the Y axis WC3 preserves the bottom anchor and lets an authored-height frame extend upward; only an auto-sized axis stretches between
its opposing anchors. This is required by SimpleInfoPanel.fdf, whose 0.03125 damage/armor frames are deliberately taller than the
0.030125 runtime wrappers they are attached to.
Runtime WC3 command-card geometry follows the 4x3 retail/Warsmash grid: x starts at 0.6175 with a 0.0434 column stride, y starts
at 0.4660 with a 0.0440 row stride, and buttons are 0.039 x 0.039. These are runtime control coordinates rather than FDF-owned
layout.
Screen Controller Conventions
- In
games/warcraft-3/menu/screens/*.c, preferUI_FRAME(...)andUI_CHILD_FRAME(...)for readability and FDF-name coupling. - Use
UI_FindChildFrame(...)when it is clearly shorter or cleaner than introducing temporary macro-bound locals. - Avoid excessive pointer null-check noise in screen controllers. Prefer one scene-level readiness gate (early return) over repeated per-widget checks.
- If a required root frame is missing, fail fast for that screen and skip further scene setup/update work.
- Keep frame names data-driven by FDF; avoid hardcoded lookup strings when macro-based lookup can use the frame identifier directly.
Game-Authored HUD
ConsoleUI and its resource, command, inventory, and information panels are authored by
games/warcraft-3/game/hud/. The game parses FDF and emits svc_layout; the generic client draws and interacts
with those frames. Dynamic fields use replicated state bindings or updated server-authored frames.
In-game dialogs use svc_window. Do not create ConsoleUI controllers or player-state callbacks in the menu library.
See the exclusive menu/HUD boundary.
stb_fdf.h Pattern
stb_fdf.his the shared declarations-only header for FDF types (FRAMEDEF, enums, bind macros) and API declarations (UI_ParseFDF,UI_DrawFrames, etc.).- Parser implementation stays in
menu_fdf.c(hasmidependency for MPQ asset loading).stb_fdf.hprovides shared types + declarations so both modules see identical structs without circular includes. - Generated binding headers in
generated/map FDF field names to struct member offsets via macros likebind_<fieldname>. Usefdfbindgentool to regenerate from MPQ source FDF files.
DDX-Style Schema Tables Across Layout Engines
All three game layout engines follow the single-header DDX-style schema table architecture:
| Game | Header | Schema Tables | Role |
|---|---|---|---|
| Warcraft III | games/warcraft-3/common/stb_fdf.h |
items[], classes[] (FDF_F macros) |
Parses .fdf frame templates into FRAMEDEF frames[] |
| StarCraft II | games/starcraft-2/common/stb_sc2layout.h |
sc2_frame_attrs[], sc2_frame_fields[], sc2_child_tags[] |
Parses .SC2Layout XML into sc2Frame_t and flattened sc2BaseFrame_t |
| World of Warcraft | games/world-of-warcraft/menu/stb_wowxml.h |
uiwow_node_types[], uiwow_script_tags[], uiwow_button_part_tags[], uiwow_shared_attrs[], uiwow_point_factors[] |
Parses FrameXML (.xml) into uiWowXmlElem_t wow_xml.elems[] |
Each parser defines the format grammar as data (table of names, offsets, types, flags/callbacks) and dispatches in one generic loop without manual if/else ladders.
Native Map-Info Preview Placement
Retail UI/FrameDef/Glue/MapInfoPane.fdf leaves the MinimapImage and metadata-row anchors to
native runtime placement. Its sprite has an intrinsic size of 0.13125, while the decorative
MinimapImageBackdrop is 0.183125 and supplies its own center anchor. Preserve these authored
proportions and anchor metadata when laying out the native pane.
LocalMultiplayerJoin.fdf allocates a compact pane height of 0.223125. The former compact path
moved Suggested Players to 0.163 below the pane top but kept the full-size preview; its border
extended to about 0.190, overlapping that row. UI_LayoutMapInfoPane now fits the image and border
proportionally into the compact preview slot above the rows. The full pane retains the FDF sizes,
and repeated layout of a compact pane does not keep shrinking it. No replacement FDF is loaded.
menu_fdf.map_preview_fits_compact_and_full_panes covers both sizes and repeated layout.
Capture the settled browser with +menu_multiplayer +screenshot 120 +com_frame_limit 135, using
-vid_hidden 1 for a hidden window, in both ROC and TFT. Very early screenshots capture the glue
opening animation with the panels still offscreen and cannot verify the final layout.