Traits, known unofficially as properties, are specified as child elements of a tile, and their contents provide their values. If a tile has a trait specified more than once, the last definition is the one that will be used.
Due to a bug in Oblivion’s XML parser, all of a tile’s traits must precede its child tiles, or properties will malfunction unpredictably. (It appears to be possible to redefine a trait after defining child tiles if the trait was first defined before all child tiles, however.) The MenuQue mod purports to correct this bug.
Trait elements can only contain raw number values, raw string values, or operator child elements. Operators can be used to compute a trait's value dynamically. Unrecognized traits are discarded by the XML parser unless they are prefixed with an underscore.
These traits can be used on any tile.
- The executable sets every tile’s childcount trait to the number of direct child tiles.
- The executable is hardcoded to provide input and values to tiles with specific ids within each menu, and occasionally to read values from them as well. Generally speaking, only one tile within a menu can use a given id, and all needed ids must be used, but there are exceptions.
- To give an example of how this works: in the HUD (hud_main_menu.xml), the tile with id 1 will have its user0 trait set to the player’s current health as a percentage value, between 0 and 1. In the inventory menu (inventory_menu.xml), the tile with id 1 doesn’t receive any values, but clicking on it (if its target trait is true) will cause the menu to switch to the “All Items” tab.
- These traits have context-dependent meanings, and are generally used to send/receive values to/from the executable on a per-menu basis.
- Custom traits that can be used to hold whatever values you like. These can also be used to pass arguments to prefabs (since prefabs are spliced at run-time).
This category includes any behavior-related traits, but it also includes keyboard navigation traits. A keyboard navigation trait tells Oblivion how to respond to a specific keypress when a tile or its menu has focus.
- Found in the executable. Purpose unknown.
- If the tile’s target trait is true and the tile has an id, then clicking on the tile will set this property to 1 and then back to zero, on the same frame.
- If the tile’s target trait is true and the tile has an id, then clicking on the tile will play the specified sound. Values are numbers which correspond to the hardcoded editor IDs of sounds. (The sounds themselves are defined in Oblivion.esm.) The default value is zero.
Sound index Sound editor ID Description 1 UIMenuOK A clicking sound overtop a low drumbeat. 2 UIMenuCancel A clicking sound. 3 UIMenuPrevNext A slightly higher-pitched clicking sound. 4 UIMenuFocus Silent. 5 UIMenuTabs Silent. 6 ITMBookPageTurn 7 UISpeechRollover A quick, high-pitched clink. 8 UISpeechRotate Something rotating into place, with several quick metallic clicks as it goes. A longer sound. Used for the Persuade minigame. 9 UIQuestNew Quick cymbals and a drumbeat. Used when a quest starts. 10 UIQuestUpdate The same as UIQuestNew, but with some chiming at the end. Used when a quest updates. 11 UIMessage A low drumbeat. 12 MenuEnd 13 MenuStart 14 UIMenuBracket A metallic object sliding into place, with a high-pitched clink at the end. 15 UIMessageFade Two high-pitched chimes. 16 UIInventoryOpen Silent. 17 UIInventoryClose Silent. 18 UIPotionCreate A glass clink, followed by a low beat and the sound of bubbling. Used when you brew a poison or potion. 19 DRSLocked 20 UIMessage 21 UIMenuCancel 22 UIStatsSkillUp Drums. Used when a skill increases. 23 SPLEquip A quick shuffling of papers over a low drumbeat. 24 ITMWelkyndStoneUse 25 ITMScrollOpen 26 ITMScrollClose 27 ITMBookOpen 28 ITMBookClose 29 ITMTakeAll 30 ITMIngredientNothing 31 ITMIngredientDown 32 ITMSoulTrap 33 UIArmorWeaponRepairBreak The sound of a Repair Hammer breaking after being used up. 34 ITMBoundDisappear 35 ITMGoldUp The sound of coins clinking together. Good for spending or receiving gold. 36 UIItemEnchant A long, louder magical sound effect.
- The game uses this to offset the position of a focus box, shrinking it inward by the value; the trait must be manually read and used by each menu with a focus box. See the description for target for further information on focus boxes.
- When a tile is generated as part of a list, the executable typically sets its listindex trait to its index within the list. Note that you’re perfectly free to use listindex in “preplaced” content; in fact, this can be incredibly convenient for setting up keyboard navigation!
- If a keyboard navigation trait (e.g. xup) is set to &prev; or &next;, then pressing that button while a tile has focus will transfer mouseover focus to the previous or next list item – that is, the tile whose listindex is the "closest" to the current tile in either direction. When searching for the previous or next list item, the game starts by scanning all of the current tile’s siblings (in order from first to last), before recursing outward (i.e. scanning the parent tile’s siblings and so on) if nothing is found. For the purposes of this entire feature, the listindex values are truncated to integers. Gaps between listindex values won’t interfere with &prev; or &next;; for example, if the only two children in a tile have listindex values 1 and 3, you can still use listindex for keyboard navigation.
- Refer to the keyboard navigation explanation at the top of this section for complete information.
- If the tile’s target property is true, then this trait will be set to 1 when the mouse is over the tile, and 0 at all other times. Note that this trait will not update if the tile is moved under the player’s cursor, or if the tile is moved out from under the player’s cursor (both are common when working with tiles in scrollable panes: the user can use the scroll wheel to move tiles within these panes under and out from the cursor without actually moving the cursor over them first).
- Keep in mind that Oblivion XML doesn’t use standard Boolean values: 1 converts to false in Oblivion. It’s generally better to use mult instead of onlyif to handle this trait. For example, to make a trait use the value 140 normally and 255 when the tile is under the mouse, try
<copy>140</copy><max><copy>255</copy><mult src="me()" trait="mouseover" /></max>.
- Testing suggests that elements cannot receive mouseover focus from the mouse if they do not have an id trait, even though they can still receive mouseover focus from keyboard navigation. Further investigation is required. Remember that you can usually set a tile’s id to &generic; to ensure that it doesn’t trigger any special menu-related behaviors.
- If the tile’s target trait is true and the tile has an id, then clicking on the tile while holding the Shift key will set this trait to 1 for a single frame.
- If this trait is set to true, the tile will be able to receive mouse and keyboard focus. Moreover, if the tile has an id, the game engine will also apply the clicked and clicksound properties and forward click events to the menu’s engine-level code to be handled (or not) by that menu.
- In (some?) vanilla menus that have scrollbars, using the mouse wheel while the cursor is over any targetable tile (or one of its descendants) will cause the scrollbar to scroll. It is not known whether this behavior is linked to the xlist trait.
- Some menus will show “focus boxes” under targetable elements with specific IDs, when those elements have keyboard and mouse focus. The focus boxes are themselves elements with special IDs, whose sizes and positions are altered by the executable.
- These keyboard navigation traits indicate how the tile should respond to various Xbox buttons when the tile has mouseover state. For example, if you want a button to act as Shift+Click, then you might use the value
<ref src="me()" trait="shiftclicked" />.
- It’s not clear whether ref operators work inside of prefabs, but a tile defined in a prefab can be targeted by a ref operator outside of the prefab.
- The xbuttonlt and xbuttonrt traits also trigger when the player presses the left or right arrow keys while holding the Shift key.
- This trait has something to do with specifying which tile will take keyboard navigation focus, if the user presses a keyboard navigation key when no tile has focus. However, the exact meaning of its value is not known at this time. Bethesda specifies it as both a Boolean and an integer – sometimes mixing formats even within the same menu. Analysis of the executable has not yet revealed the value’s meaning, but certain details (described below) suggest that it may be a way of prioritizing input focus: it may be that the tile with the highest xdefault value is the one that gets initial focus.
- If xdefault is set to -1, then the tile can be used in keyboard navigation, but its xdefault trait will not be increased. For tiles that are visible and targetable, xdefault values below -99 skip some sort of processing apparently related to the xlist trait, and in testing seem to prevent keyboard navigation to the tiles in question as well.
- The value can be modified by the executable. Testing indicates that the executable maintains a running counter of some kind, which persists past the closure of a menu and may persist across all menus. Every time a tile gains or loses mouseover focus from keyboard navigation (but not mouse movement), the counter is incremented by a value between 1 and 3 (inclusive), and the tile’s xdefault trait is set to that value. Modifications to a tile’s xdefault trait do not persist across the closure and later reopening of the tile’s containing menu, but (as stated before) the counter’s value does persist. This may be how the game remembers focus when navigating between two different list panes (see description for xlist).
- This trait has something to do with indicating whether a tile is a list container, a list item, or (by default) neither. Vanilla code comments wrongly describe this as a Boolean even when the values supplied are not Booleans (or numerically equivalent to Booleans under the hood). The trait should be set to &xlist; for a list’s immediate container and &xitem; for generated list items.
- This trait influences how keyboard navigation works: if a keyboard navigation trait is processed and “leads” to a tile whose xlist trait is set to &xlist;, then Oblivion will instead attempt to focus a list item inside of the tile. The precise operational definition of “list item” with respect to the previous sentence is not known at this time, but testing and disassembly indicate that “list items” work for this purpose if they have an xdefault trait (set to any value), are visible and targetable (a requirement for receiving keyboard focus), and if their xlist trait is set to &xitem;. The result of this functionality is that you can place keyboard navigation traits (e.g. xleft and xright) on two list containers, with each container’s trait pointing at the other, to allow keyboard navigation between the two lists; Oblivion will remember which list item previously had focus in each list, and will return focus to that list item if the user navigates back to its containing list.
- All of this is necessary because for a number of reasons, it is impossible to target generated list items by name for keyboard navigation. The biggest obstacle is that if the list items are not renamed after being generated from a template, then they will all have the same name; if they are renamed, they will be renamed to an executable-defined string that will likely vary based on the generated content (e.g. for an inventory item in the RepairMenu, a sanitized version of the item name with certain characters replaced). Aside from this, src attributes and selectors seem to only be processed at final parse time (templates are kept “half-parsed” and parsing finishes each time tiles are generated from them), so attempting to reference any generated list item from outside of its list will fail (because the list item does not exist at parse time).
- These keyboard navigation traits indicate how the tile should respond to the arrow keys (and presumably the Xbox D-Pad and joysticks) when the tile has mouseover state. For example, if you want one tile to transfer mouseover status to another tile named SomeElement, you might use the value
<ref src="SomeElement" trait="mouseover" />.
- Disassembly of the executable suggests that the trait you specify here is ignored; these always influence a target tile’s mouseover trait. This hasn’t been tested yet.
- This trait is used when scrolling a tile into view, when that tile receives mouseover focus from keyboard navigation rather than from a mouse movement.
- For list items, this trait should be set to the value that the scrollbar should be scrolled to. For list containers (always the direct parents of list items), this value should be a ref operator indicating the scrollbar whose state needs to be modified. The trait specified in the ref operator will be ignored: the executable will perform a special-case modification to the scrollbar’s user5 trait in order to forcibly set the scroll position.
- It is not yet clear how the game determines that a tile is a list item that should receive the above behavior.
The process for handling a keyboard navigation trait is as follows:
- The tile that had mouseover focus at the time of the navigation keypress shall be referred to as the trigger tile. The trait for the navigation keypress (e.g. xup for the up arrow key) shall be referred to as the navigation trait.
- Identify a basis tile.
- If the trigger tile has the navigation trait, then it is the basis tile.
- Otherwise, search upward through the trigger tile’s ancestors, stopping at the first found tile that has the navigation trait. That found tile will be the basis tile.
- If there is no basis tile, abort.
- If the navigation trait uses any ref operator, then modify the specified trait on the specified other tile only if that tile is suitable. Regardless of whether the tile is targetable, stop here.
- For the purposes of this task, we use the first ref operator in the trait, ignoring hierarchy and treating all operators as a flat list.
- The exact definition of suitable is yet to be determined. However, a tile can be used if it itself is targetable and has an xdefault greater than -99, or if its xlist trait is set to &xlist; and it contains any targetable tiles with xdefault traits.
- If the trait being modified is clicked, but the target tile has an xdefault trait, then give it mouseover focus as well.
- If the navigation trait uses the &first;, &last;, &prev;, or &next; entities, then search the basis tile’s siblings (in order, from first to last) for a visible and targetable tile with an xdefault trait (set to any value) and a suitable listindex:
- For &first;, choose the sibling tile with the lowest listindex.
- For &last;, choose the sibling tile with the highest listindex.
- For &prev;, pick from among the sibling tiles whose listindex values are less than that of the basis tile: choose the highest listindex among those.
- For &next;, pick from among the sibling tiles whose listindex values are greater than that of the basis tile: choose the smallest listindex among those.
- If no matching tile is found, then return to Step 2, treating the current basis tile as the trigger tile for that step.
- If a matching tile was found, scroll it into view using the xscroll trait: assume that it is a list item tile and its parent is a list container tile.
- Give the matching tile mouseover focus as appropriate.
Code analysis suggests that keyboard navigation cannot be “chained:” if one tile points xup at another tile, and that other tile points xup at a third, moving up from the first tile will not bring you directly to the third.
In at least some cases, using keyboard navigation to forward clicks will not also forward mouseover focus, unless xdefault is &true; or greater. This may be limited to the listindex-related XML entities.
Bethesda’s code comments indicate that keyboard navigation in a menu may break if no tile in the menu has initial focus. Be sure that you set an xdefault trait on any targetable tile.
Those interested in reviewing the engine-level functionality involved can refer to subroutine
Tile* Tile::ResolveTraitReference(UInt32 keynavTraitID, UInt32* outTargetTraitID), located at
0x0058E3B0 in the latest version of Oblivion. That function handles Steps 1 through 7 in the above outline; Steps 4 (modifying the trait) and 8 are handled by some (all?) callers. The return value is the tile pointed to by the ref operator in Step 4, or the tile identified in Step 5;
*outTargetTraitID is set to the trait identified by the ref operator in Step 4.
To investigate the meaning of “suitable” in Step 4, examine
InterfaceManager::HandleNavigationKeypress (located at
0x00580BA0 in the latest version of Oblivion), which calls
Tile::ResolveTraitReference multiple times.
These traits relate to how tiles are sized and positioned.
- If this trait is set to true and the tile has an ancestor whose clipwindow trait is true, then any part of this tile that lies outside of that ancestor’s bounds will be hidden.
- Note that like visible, clips defaults to 0 (which is treated as false) rather than
&false;; and unlike visible, clips does not propagate to descendant tiles. This means that if you want tiles to blindly copy the clips state of some ancestor (e.g. tiles in a prefab), you must use
<clips><copy src=”parent()” trait=”clips” /><eq>&true;</eq></clips>.
- Disassembly of the executable confirms that this trait is only valid for Image, Rect, and Text tiles.
- If this trait is set to a non-zero value (i.e.
&false;), then descendant tiles will be clipped if their clips trait is also true and if they lie outside of this tile’s bounds: that is, the descendants in question will be partially or fully hidden.
- Disassembly of the executable confirms that this trait is only processed on Image and Rect tiles.
- The depth of the element: tiles with a higher depth will be drawn on top of tiles with a lower depth, and they can also block mouse interactions with target-able tiles beneath them. Note that this value is relative to the parent tile’s depth.
- Menus are separated by two depth units. Specifically, when a menu is opened, its depth is set to 2.0 plus the highest depth among all other menus. (Whether “highest depth” refers to each menu’s own depth or the highest depth of any contained tile is unclear, and needs further investigation.)
- Oblivion’s menus are rendered in 3D, and each unit of menu depth translates to -0.008 3D units. This means that depth values will offset tiles, even if only by unnoticeably small sub-pixel amounts in typical situations. Very large depth values (in excess of 1000) will start to cause noticeable deviations in a tile’s apparent position. Depth values in excess of 40000 will noticeably affect the menu cursor (presumably because Oblivion tries to keep the cursor tile’s own depth in front of everything else), enlarging and displacing it; the cursor will remain affected until the offending menu is closed and any menu is (re)opened.
- Oblivion renders its UI in 3D; most tiles are just flat shapes in a 3D scene. A NIF tile renders another 3D scene (defined in a NIF file) directly into the UI, where the tile would go. By default, the scene’s origin (0, 0, 0) is aligned with the tile’s own coordinates in the scene. The depth3d trait offsets the scene’s depth axis from the tile’s position. The value of the depth3d trait is a measurement in 3D units, not depth units; one depth unit represents -0.008 3D units. A depth3d of 500 produces exactly the same position and size change (due to perspective) as a depth of 62500.
- This trait overrides visible. If set to
&true;, the tile will be hidden even if its visible trait is
&true;. Presumably it’s set by the executable in some cases in order to forcibly hide generated list items if they lie outside of their list’s visible region.
- Note that input-handling and keyboard navigation code pays attention to visible, but not to listclip. Tiles hidden with listclip can still be targets for keyboard navigation.
- Boolean. Tiles are positioned relative to the nearest ancestor tile whose locus trait is true. Such an ancestor shall be referred to in this document as the locus ancestor.
- This trait is forced to
&true;for all NIF tiles that specify valid 3D models.
- The dimensions of the tile in pixels.
- Note that these traits are not automatically set on Image tiles. If you want an Image tile to display its full image, unscaled, then you must manually set these traits to copy the filewidth and fileheight traits' values.
- Note that these traits cannot be set on Text tiles; instead, the executable sets them to the size of the displayed text if the text is visible.
- The position of the tile’s left edge, relative to the left edge of the tile’s locus ancestor.
- The position of the tile’s top edge, relative to the top edge of the tile’s locus ancestor.
- A Boolean trait indicating whether the tile will be displayed. Note that hidden tiles cannot be clicked or receive mouseover status even if their target trait is true. If a tile is set to not be visible, then all of its descendant tiles will also be hidden.
- Note that this trait defaults to 0, and 0 is treated as unspecified (i.e. true). In practice, this means that onlyif and similar operators treat an unspecified visible trait as false even though it behaves as true.
- When working with tiles that have ids, it’s somewhat common for the executable to force visible to a particular value even when there’s no real need or reason to do so.
All tiles with graphical content are rendered as 3D polygons. The color traits modify the vertex colors for these polygons: they can be used to dim a color component on a rendered texture (including text), but do not affect child or descendant tiles.
- A numeric value between 0 and 255, inclusive, indicating how opaque the tile is. Note that even fully transparent tiles can still respond to mouse clicks and other interactions if their target trait is true.
- Note that if you try to query the alpha of a trait that doesn’t define alpha, you will retrieve 0, not 255. This means that child tiles that try to "inherit" their parent’s unset alpha via
<alpha><copy src="parent()" trait="alpha" /></alpha>will become fully transparent even though the parent is opaque.
- Note that in some cases, setting an alpha on a Rect can cause the Rect to actually become a visible, solid box of color – typically white, if no other color traits have been set. The exact circumstances needed to trigger this behavior are not known; it only occurs if something causes the Rect to generate a rendered 3D node, but although that’s clearly intended to happen, Oblivion’s code doesn’t appear to reliably ensure that it happens.
- A numeric value between 0 and 255, inclusive.
- For text tiles, these values indicate the color of the text: font files define typefaces in white, and the RGB components of each pixel are reduced if they are greater than these values.
- For image tiles, these values work similarly, capping each color channel in the rendered texture. The exact mechanics here are unclear. In theory, an all-white texture with these values set to 140 should present with a color (140, 140, 140). In practice, it seems that one must use 0.55 (140 / 255).
These traits are unique to Image tiles, and generally have no effect elsewhere.
- The texture file will be cropped from the left and top edges by these amounts of pixels, such that the pixel at (cropX, cropY) in the texture is aligned at the top-left corner of the tile. Note that this value is applied after the zoom trait, so you should pre-multiply it by the zoom trait (if it’s not -1) for consistent results.
- The “crop offset” traits are just aliases of the “crop” traits.
- The path to the texture file that the image tile should display. This path is case-sensitive (unless MenuQue is installed) and is relative to
Data\Textures; however, textures outside of
Data\Textures\Menuswill not work.
- These traits are computed by the game engine and set on your tiles automatically. They indicate the width and height of the loaded texture file, in pixels, after zoom has been applied but before any cropping has been applied.
- Note that if the filename trait has been copied from “too far away,” the filewidth and fileheight traits will not be updated properly when the image is loaded; this bug has been observed to lead to too small a fileheight (8 in testing, when dealing with a 16px-tall image). The precise operational definition of “too far away” has yet to be determined.
- A Boolean trait indicating whether the texture should be tiled across the tile (oh dear, that’s confusing), if the tile’s width and height exceed (zoom × filewidth - cropX) or (zoom × fileheight - cropY), respectively. Tiling is based on the dimensions of the texture; the game engine will not check and “compensate” for fully-transparent areas at the texture’s edges.
- The engine behaves differently if this trait’s value is lower than -1.0 versus if it’s higher or equal, but that behavior hasn't been decoded yet.
- The texture file will be scaled by (zoom ÷ 100) before being displayed within the tile. If the zoom value is -1, the texture file will be stretched or shrunk to fill the tile’s width and height (non-uniform scaling is supported in this case).
&scale;XML entity is provided as a "semantic" equivalent to -1.
- This trait is valid on one specific Text tile in the vanilla MainMenu, but is not valid on other Text tiles. Presumably the MainMenu class tampers with that one tile’s rendered
These traits are unique to Menu tiles, and generally have no effect elsewhere.
- Associates the menu’s root tile with a specific internal class, identified numerically. All vanilla menus have XML entities defined and available for use as shortcuts.
- Boolean. If set to true, it appears to prevent the menu from being faded in some or all cases; it will simply appear or disappear instead.
- This value is sometimes(?) used in place of menufade, if menufade is set to a negative or zero value. The precise difference is not clear; explorefade is used even outside of gameplay settings (e.g. on the options menus, when they’re opened from the game’s main menu).
- Time in seconds that it takes for the menu to fade in or out.
- To be documented.
These traits are unique to NIF tiles, and generally have no effect elsewhere.
- The name of an animation (specifically a
NiControllerSequence) within the NIF file.
- To be determined: if this computes to a non-existent animation, does the NIF tile simply display without animating?
- The filename of the NIF file to display. If the path does not start with
Data\(the use of a backslash matters), then it is relative to
These traits are unique to Text tiles, and generally have no effect elsewhere.
- An integer between 1 and 5 indicating which font to use. The Oblivion.ini file associates each number with a font file. Numbers outside of this range will cause crashes.
- If this value is non-zero, the string is parsed as HTML before being displayed. However, this only works when the tile’s string is supplied by the executable. HTML tags placed inside of the string trait are misinterpreted (as if they were invalid operators) and cause the value to compute to zero or blank; escaping these tags with the
&rt;entities doesn’t work either, because the parser doesn't understand those entities.
- Species how the text is positioned relative to the element’s X-coordinate. The values
&right;indicate which edge of the text will align with the X-coordinate.
- HTML-formatted text can be broken into “pages” using the HR element. In these cases, the executable automatically sets the pagecount trait to the number of pages available.
- HTML-formatted text can be broken into “pages” using the HR element. In these cases, this trait indicates which zero-indexed “page” to display.
- The text that the tile will display. Raw numeric values are supported, as are numeric values copied from other traits; however, displayed floats will be rounded down to the nearest whole number (they're stringified using the format string
- If word-wrapping is enabled on the tile, its text will be limited to this many lines, and extra lines of text will not be displayed.
- To be determined. Based on its usage in vanilla, it appears to be an alias for wraplimit.
- If this trait is set, the text in the tile will word-wrap to avoid exceeding this width in pixels.
- When it comes time to render text, this value is rounded down to the nearest integer. If it is lower than 1, then the (normalized) width of the screen is used instead.
These tag IDs do not have defined names, and therefore cannot be used in Oblivion XML. The tag IDs are used directly by Oblivion’s internals, so OBSE plug-in authors wishing to add new traits should avoid using them.
- Used on the menu root (screen()). It is only ever set to the ID of one of the “big four” menus; it is initialized to 0x3EB (the ID of StatsMenu) when the menu root is created. It may refer to the current "Big Four" menu being displayed, but this is unverified.
- Boolean. Set on menus’ root tiles in some cases. Purpose unknown. It gets set to true when a menu is closing (and some menus will even do this multiple times redundantly).
These traits are set on tiles that only the executable is allowed to directly read or modify:
- Set on the menu root (
screen()). Boolean. Related to debugging and the console. Purpose unknown.
Disassembly of the executable confirms that these traits are considered valid, but are never actually used by the executable.
- The executable registers these traits, but nothing in the executable ever actually uses them or acts on them.