GUI Architecture

OpenMW's game UI is built on MyGUI. To learn about MyGUI visit the code documentation, its wiki, the source code and more specifically the examples contained in the source code.

This article will explain MyGUI usage specific to OpenMW.

= Layout system =

MyGUI's XML format for layouts/skins has been used for the majority of the OpenMW GUI. The resulting files can be found here. Currently the formats are lacking a complete documentation, but the amount of examples that are available should more or less make up for this.

MyGUI includes a Layout Editor and Skin Editor application, but unfortunately these tools are difficult to use for us due to specifics of the Morrowind GUI resources, e.g. BSA archives and case insensitivity. In the past we made a MyGUI plugin to work around this, which can still be found in the 'plugins' directory of the OpenMW code. However, this plugin is currently not working as it has not been updated after OpenMW switched to OpenSceneGraph rendering. So for the time being, XML files have to be edited by hand (which, admittedly, is more convenient in many cases anyway).

The coordinates in MyGUI layouts (left, top, width, height - always in that order) are given in pixels and relative to the parent widget. The GUI system internally works in pixels, even if GUI Scaling is enabled. The GUI scaling option works by pretending to the GUI that the screen is smaller than it really is, and then using the graphics card to do the scaling.

Widgets also have an Align property. The Align does not have any effect on the initial placement of the widget, rather, it controls what will happen when the parent widget changes size. For example, if the parent widget's width increases by 1 pixel, and our widget is set to 'HStretch', its width will also increase by 1 pixel. If in that same example our widget instead uses 'Right' then the widget will move to the right by 1 pixel. Here is the list of possible alignments, which can sometimes be combined for example 'Left Bottom'.

Please note that most of OpenMW's skin files were written using a deprecated syntax. The now preferred and more powerful syntax is the ResourceLayout, for which an example can be found here. This syntax more closely resembles the one used for layout files.

Apart from using skin/layout files it is still required to use C++ code in order to connect widget events to certain actions. The code for this can be found in the mwgui directory.

= Box layouting =

Because the above mentioned pixel-based layouting system can be a bit too limited and cumbersome for some cases, we have created a new box-layout system for OpenMW's purposes that functions similar to the way traditional GUI toolkits like Qt or GTK work. It is usually preferred to use this system for new layouts instead of positioning widgets by hand. The new system introduces the following widgets:

HBox, VBox
Automatically positions and resizes their child widgets, horizontally or vertically. Available user strings are 'Spacing' (empty space between each widget), 'Padding' (outer padding), and 'AutoResize'. If 'AutoResize' is true, the box resizes itself to the requested size of the child widgets. Otherwise, the box will stay at its given size.

Child widgets can set the user string 'HStretch' (for HBox) or 'VStretch' (for VBox). If stretching is enabled, the widget will fill up all the space available to it. Otherwise, the widget will remain in its original size i.e. the box will only control that widget's position.

Boxes can be nested within each other e.g. to produce a table layout.

AutoSizedButton
This widget will resize itself to fit its label.

Text padding defaults to (24, 8) but can be overriden by the 'TextPadding' user string.

If the 'ExpandDirection' user string is set to 'Left' then the button will accordingly move to the left upon expanding its size (or to the right on contracting). This is only relevant if the button is not part of a Box.

AutoSizedEditBox
Like AutoSizedButton, but for multi-line text. The widget's width is static, but the height will be adjusted based on the number of lines required.

Spacer
An empty widget for use with HBox/VBox that fills all available space. Spacers can be used to set the alignment of sibling widgets. For example, a box containing a spacer, a button and another spacer (in that order) results in a centered button.

= Escape sequences =

As the MyGUI wiki explains, text widgets support escape sequences starting with '#', for example:


 * #ff0000text results in a red 'text'.
 * #{foobar} will be replaced with the value for 'foobar' in the MyGUI::LanguageManager.

For OpenMW in particular, we have used a callback into the MyGUI::LanguageManager to implement the following meanings:


 * #{setting=} will be replaced with the relevant setting value in the user's OpenMW settings.cfg.
 * #{sCell= } will be replaced with the translated name for a Morrowind cell (this is usually the same as the Cell ID, but the Russian edition of Morrowind uses a separate translation table)
 * #{fontcolour= } will be replaced with the color of FontColor_color_ in openmw.cfg (imported from Morrowind.ini). The output format is R,G,B with RGB ranging from 0 to 1.
 * #{fontcolourhtml= } is the same as above, except that the output format is a '#' followed by an HTML color code.
 * #{ } will be replaced with the value of the Game Setting in the Morrowind Data Files.

Leveraging this replacement mechanism at runtime requires one to use the 'setCaptionWithReplacing' method rather than 'setCaption'. Colour-codes (like #ff0000) are always used regardless of whether 'setCaption' or 'setCaptionWithReplacing' is used. To use escape sequences in a layout one must use the 'Caption' property.

To avoid '#' characters in a caption being treated as an escape sequence, this character needs to be escaped by adding another '#' character: '##' produces '#'. MyGUI provides the function MyGUI::TextIterator::toTagsString to do this. When retrieving text from a user-filled text box, you may want to do the opposite by calling getOnlyText.

Escape sequences are also supported in skins. In order for this to work in legacy skins, the version must be set to '1.1'. Example