UDN
Search public documentation:

UserInterfaceTechnicalGuide
日本語訳
中国翻译
한국어

Licensees can log in.

Red links require licensee log in.


Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

UE3 Home > User Interfaces & HUDs > UIScene Technical Guide

UIScene Technical Guide


The UI System and its functionality are no longer supported. Please see the Scaleform GFx documentation for information on the currently supported user interface system.

Overview


This document is geared towards programmers, it describes how the major features of the UnrealUI system are designed and implemented.

Widget Scene Layout


The following is an overview of how widget creation, positioning and docking is handled in the UI system. It covers the creation of widget classes as well as the positioning of widgets in a scene and the creation of docking relationships and parent / child relationships between widgets.

Widget Classes

Widgets are the building blocks of UnrealUI scenes. They act as both functional screen objects with their own behavior and containers for text, images and other widgets. Each specific widget type is built to provide specific functionality to the scene such as a Label widget which acts as a container for placeable text or a Button widget that is intended to execute functionality upon user input.

Design Rationale

In the UI system, final virtual is used for functions which are called by both script and C++. In most cases, we don't want them to be overridden in UnrealScript, because the derived script version won't be called if the function is called directly from C++.

Widget Class Hierarchy

WidgetClassesClassDiagram2.jpg

All widget classes derive from the UIObject class which provides the necessary data and methods for any basic widget class. The UIObject class derives itself from the UIScreenObject class which derives off UIRoot and ultimately derives off of Object. For the purpose of discussing widgets the UIScreenObject class and UIObject class will be of most interest. The UIScreenObject class contains much of the data and functionality for dealing with widget positioning, widget states and child widget relationships. The UIObject class contains data pertaining to a widget's style, docking targets, owners or parent and its RenderBounds.

WidgetClassesClassDiagram1.jpg

UIScreenObject

The UIScreenObject class is responsible for much of the core functionality of a widget class. In general terms, this class handles the positioning of the widget's faces, the management of the widget's states and focus propogation, as well as storing child data. It also handles much of the user input functionality and the player tracking necessary for split screen support. For more information on these systems refer to the related specific sections of this document.

UIObject

The UIObject class handles the widget's changing and updating of style data and the creation of docking links as well as the resolution of position due to docking changes. This class also contains references to the object's parent widget and parent scene and the widget's unique identifier. Much of the functionality for the generation of navigation links is also done in the UIObject.

UIString

The UIObject class is the core renderable entity for all data that is presented by the UI.

The data can be rendered in different ways, based on the TextClipMode:

  • Overdraw (None): all text is drawn, even if it is outside the bounding region
  • Clipped (Normal): text outside the region should be clipped
  • Ellipsis: replace the last few visible characters with ellipsis to indicate that more text follows
  • Wrapped (Wrap): wrap the text to the next line; must use the newline character (\n) for new lines

Common Widget Class Components

Although each of the various derived widget classes are designed with specific purposes in mind, there are a number of common objects that exist inside many of them. The most predominant example of common components can be seen through the use of the UITexture class. Most widget classes have the ability to display a background image and as such require the UITexture class to act as a container and a display object for that image. Widgets that contain a UITexture will also typically contain TextureCoordinates object for positioning the UITexture. Another example of a common widget component can be seen with the UIComp_DrawString class which provides the necessary functionality for storing and displaying a string in the Label widget and the Label Button widget. Components like the UITexture and UIComp_DrawString cannot only be used to provide functionality to derived widget classes, but they also act as a foundation for building specialized components for custom widget classes. The UIComp_DrawStringEditbox is an example of a specialized widget component that was conceived for the Editbox widget's needs, but was derived from the UIComp_DrawString class.

Creating Custom Widget Classes

Using the framework provided by the widget class hierarchy and the widget class components it is possible to easily create custom widget classes. Custom widgets should be derived from the UIObject class like all of the existing widget classes. Extending the UIObject class to a new custom widget class will provide it with necessary basic functionality for a widget. However, there are a few additional steps required to create a functional skeleton widget class, other than simply extending the UIObject class.

The methods provided by the base classes will provide basic implementations for any widget with the exception of the UIObject::Render_widget method. The UIObject class does not include any renderable objects by default so it does not implement its Render_Widget method. The fact that Render_Method is not defined does not pose an issue for UIObject class being as it is never instanced, however any custom widget class will need to implement it before being instanced. If the widget is utilizing common widget components like the UITexture or the UIComp_String the widget's Render_Method will have to call each of the components render functions and provide them with the FRenderInterface instance that is passed into Render_Method.

A custom widget derived off of the UIObject class will also inherit a number of states and events. The UIObject contains the Enable and Disable states by default as defined in the UIScreenObject class's Default Properties, the class that UIObject extends. There are also events and actions that will be added to a widget's Kismet Global Sequence by the UIObject's Default Properties. The events include UIEvent_Initialized, UIEvent_OnEnterState, and UIEvent_OnLeaveState. The actions include UIAction_ActivateInitialState. Addition Kismet events and actions can be defined in the Default Properties of a custom widget the same way they are in the UIObject and UIScreenObject classes.

  • From UIObject Default Properties in UIObject.uc:
   // Actions
   Begin Object Class=UIAction_ActivateInitialState Name=ActivateInitialState
   End Object

   // Event with an output link bound to an action.
   Begin Object Class=UIEvent_Initialized Name=WidgetInitializedEvent
      OutputLinks(0)=(LinkDesc="Output",Links=((LinkedOp=ActivateInitialState)))
   End Object

   Begin Object Class=UIEvent_OnEnterState Name=EnteredStateEvent
   End Object

   // Add objects
   Begin Object Class=UIComp_Event Name=WidgetEventComponent
      DefaultEvents.Add((EventTemplate=WidgetInitializedEvent))
      DefaultEvents.Add((EventTemplate=EnteredStateEvent))
   End Object
        
   EventProvider=WidgetEventComponent

The final step required to create a basic skeletal widget is to define its Primary Style in the Default Properties. The Primary Style that will be utilized by the custom widget will depend on the data that the widget will be presenting. If the widget contains UIComp_DrawString then the UIStyle_Text style would be most applicable. The following is an example of setting the Primary Style through script for a widget class to the UIStyle_Image style.

  • From UIButton Default Properties in UIButton.uc:
   PrimaryStyle=(DefaultStyleTag="ButtonBackground",RequiredStyleClass=class'Engine.UIStyle_Image')

Custom Widget Class Example via Label Button

TODO

Widget Positioning

The position of a widget in the viewport is ultimately determined by the position of its four faces. The actual pixel position of a widget's face is defined by the RenderBounds float array in the UIObject class from which all widget classes are derived. However, the RenderBounds of a widget are ultimately determined from the UIScreenValue_Bounds Position struct. The Position struct is a member of the UIScreenObject class, the class from which UIObject derives, and the values within it can be stored in any Scale Type? unlike the RenderBounds which are absolute viewport pixels only. All changes to widget face positions should be made to the Position field, being that the Position field is used for calculating the actual pixel position of the widget's RenderBounds

Setting Widget Position

Altering the position of one of a given widget's faces should be done through the UIScreenObject::SetPosition method. It is possible to modify the RenderBounds of a widget face directly, however this is ill-advised being that other widget faces may be docked to that face or children of that face and will need to be updated. The SetPosition method will set the Position field for the given widget's specified face based upon the face and new position value provided. SetPosition can be called with any Scale Type? representation for the new face position. Scale Type? are defined in the User Guide's? widget positioning section. If the Scale Type? provided for the new value differs from the representation of the value stored in the widget's Position field then a conversion will be done. The old face position will be compared against the new position, after the new face position has been converted to the appropriate Scale Type?, to determine if the face's position changed and requiring the scene to update all widget face positions. Should an update be necessary, UIScene::RequestSceneUpdate will be called to notify the scene that it needs to resolve the position of its widget faces during the next UIScene::Update call. On the next frame UIScene::Update will call UIScene::ResolveScenePostion to resolve RenderBounds of all of the widget faces in the scene based on their updated Position fields.

  • Setting Face Position Sequence Diagram

WidgetPositioningSequenceDiagram.jpg

Setting Widget Position via UnrealEd

Using the UI Editor a widget's faces can be repositioned by either dragging a resized handle of a widget in the scene viewport or by using the text fields in the Positioning editor.

The first scenario, where a designer drags the resize handle of one of a widget's faces, will result in the following sequence of events. UIScreenObject::SetPosition will be called for each face upon receiving input when the mouse button has been released after dragging a face of a widget. SetPosition will be passed a new value for the face's position based on where the designer dragged the face to. This new value that is passed to SetPosition in the form of the PixelOwner Scale Type?, though SetPosition can be passed values in any Scale Type?. Scale Types? are defined in the User Guide's widget positioning section.

In this scenario, UIScene::RequestSceneUpdate is called from UUIScreenObject::SetPosition. After all of the positions are set for each face, UUIScreenObject::RefreshPosition() is called, which handles notifying that positions have changed via the delegate function NotifyPositionChanged.

The second scenario, where the position of a widget's face is changed through the Positioning editor, does not utilize the UIScreenObject::SetPosition. Instead the widget's Position field is modified directly. After the Position field has been set, UIScene::RequestSceneUpdate is called to notify the scene that the RenderBounds of a widget need to be updated. Immediately after the call to RequestSceneUpdate, a call to UIScene::UpdateScene is made. UIScene::UpdateScene will then call UIScene::ResolveScenePositions to resolve RenderBounds of all of the widget faces in the scene.

In either scenario, the end result is a call to UUIScreenObject::RefreshPosition().

Widget Docking

Docking is the process in which a face on one widget is docked to a face on another widget to create a relationship between the position of the two faces. This relationship equates the docked face's position being equal to the face that it is docked to, plus or minus an offset called padding. Any changes made to the position of the face that another face is docked to will be propagated to the docked face. A full explanation of the docking behavior can be found in the User Guide?. There are two primary components to the docking system that should be mentioned for the purpose of the technical guide. The first being the UIDockingSet structure that is contained in every widget that is derived from the UIObject class. This structure contains the information about which of a widget's faces are docked to which faces on which widgets. Setting the values in this structure is discussed in the Creating a Docking Link section. The second component is the DockingStack that exists in the UIScene class. The DockingStack provides a list of all of the widget faces in the scene ordered by dependencies created by docking and child relationships. The DockingStack is discussed in the Docking Stack section.

Creating a Docking Link

Creating a Docking Link between faces of two widgets is as simple as calling UIObject::SetDockingTarget on the widget whose face will be docked. SetDockingTarget needs to be provided with information about which face on the docked widget will be docked to which face on which target widget. This function will then set the fields for the docked face in the UIDockingSet member DockTargets to correspond to the specified face on the specified target widget. Once the information about the docking link has been set up UIScene::RequestSceneUpdate will be called on the scene to notify it that the DockingStack needs to be updated to account for the new docking link. Immediately after UIScene::RebuildDockingStack will be called from UIScene::Update to rebuild the DockingStack.

Docking Stack

The UIObject class contains a UIDockingSet member called DockTargets. The UIDockingSet contains information about which face on which widget a given face of its container widget is bound to. This is the information that is used to calculate the widget's RenderBounds when UIScene::Update calls UIScene::ResolveScenePostion. However, the widgets in a scene cannot just be updated arbitrarily when ResolveScenePostion is called. Updates to the position of docked faces of a widget must happen after updates to the faces they are docked to otherwise the propagation of position changes to docked widget faces will be lost. Face position propagation also an issue between parent widgets and child widgets. The DockingStack member of the UIScene class is the solution to this issue. The DockingStack is a stack of all widget faces in the scene that is ordered based upon the dependencies created by docked faces and parent and child widget relationships. The DockingStack provides a data structure that ResolveScenePostion can iterate over in one pass rather than having to iterate over widgets multiple times to resolve position propagation dependencies. For each face in the scene ResolveScenePostion calls UIObject::ResolveFacePosition to recalculate the RenderBounds of the given face based on its position and the position of the face it is docked to if it is docked.

  • Face Position Resolving Diagram

https://udn.epicgames.com/pub/Three/UserInterfaceTechnicalGuide/WidgetDockingSequenceDiagram.jpg"

Input Handling


The following is an more comprehensive overview of how input processing is handled in the UI system. It covers how the input keys that a widget responds to are defined, how the widget registers itself to receive notifications when that input key event is received, and what happens when the widget receives the input key event itself.

Basic Control Flow

When a scene receives an input key event, it looks up the key name associated with this event in its InputSubscriptions map. This returns a list of widgets which are eligible to respond to that input key event at that exact moment. The scene then calls ProcessInputKey on those widgets to allow the widget the opportunity to respond to that input key event. So only widgets that are in the scene's InputSubscriptions map for a particular input key will receive calls to ProcessInputKey. Widgets are added to this array by calling UUIScene::SubscribeInputEvent, and are removed by calling UUIScene::UnsubscribeInputEvent.

Input Keys and States

Information pertaining to input keys is stored in a InputKeyAction structure, it defines an input key name, input key state (pressed, released, etc. ) and an optional UIAction that will be activated when the widget receives that input key event. The input keys that a widget can respond to are specific to the UIStates that are currently active. A UIState contains an array of InputKeyAction structs, which defines the input keys that the widget will respond to when that state is active. When the state is activated, any input keys in the state's InputKeyAction array are added to the widget's list of "input keys that I'm currently listening for", and when the state is deactivated, the input keys contained by the state's InputKeyAction array are removed from the widget's running list of input key names it will respond to (see UUIState::OnActivate() and UUIComp_Event::RegisterInputEvents).

Widget's State Input Key Overrides

UIEvent_ProcessInput object (also called "input processor" ) is an event node present in each widget's state sequence. Its responsibility is to route input key events to UIActions. Input processors are always contained by a state sequence, and never by the widget's main sequence. This way input keys are always scoped to particular menu states, so that when a states is deactivated, those input keys which should only be active while the widget is in that state are automatically removed from the list of input keys that the widget will respond to. Adding input keys to widget's input processors allows user to modify input handling behavior for individual widgets. Input processors contain a map of input key names to UIAction objects. Input key names contained by that map can include those already bound to the widget class through an input alias, or new keys not yet associated with the widget class. Doing so will completely override any default behavior that would be associated with this input key. User can then manually implement actions associated with this input key inside the widget's state sequence.

Input Events Aliases

Input Event Aliases provide for a way of associating a set of input keys with an action that a widget can perform in response to user's input of these keys. Each widget type has its unique set of aliases that should express widget's functionality, for example an EditBox has a SubmitText alias and a Button has a Clicked alias. Designers can pick which input keys in which widget state will launch the associated action through the Default Alias Key Bindings Editor, these settings are then stored into an GameInput.ini file. List of alias names is set in the GetSupportedUIActionKeyNames() function inside each widget class. Programmers define how will the widget respond to the event alias inside the ProcessInputKey() function.

Handling Of Input Key Events By Widgets

Widgets automatically handle the input key events send to them in two ways. First the widget tries to pass the key event to its Input Processor to launch any Kismet UIActions associated with this input key. If that does not succeed because no UIActions have been assigned in Kismet, then widget calls its class default input handling function ProcessInputKey. An input key is not required to have an associated UIAction that should be activated when the input key event is received. In fact, most won't - the UIAction is intended to provide designers with an easy way to do some action in response to an input key event. In most cases, the logic that will be performed in response to an input key event will be written in C++ by the person who writes the widget class. In the ProcessInputKey method for a widget, you could do whatever you want to do when an input key event is received. The default behavior would be to figure out which input alias is the input key associated with and then properly respond to the action alias. This translation process can be done by invoking the UUIObject::TranslateKey() function which will respond with the input alias name as previously specified by the designer.

Defining Which Input Key Events Widgets Will Respond To

There are two ways to define the input keys that a widget will respond to; you can define the input keys and their optional associated action in the Kismet state input events processor, or you can do this by defining event aliases for the widget class and then binding input keys to these aliases through the Default Alias Key Bindings Editor. Adding input keys to a widget from within the Kismet means that only that instance of the widget will respond to those input keys. Defining input keys in default Alias Key Bindings Editor means that all instances of that widget class will contain those input events.

Defining input event aliases to which widget will respond is done inside the function GetSupportedUIActionKeyNames(). Simply add the desired input alias name to the out_KeyNames array and you will be able to bind input keys to this alias through Default Alias Key Bindings Editor. All the widget class specific alias key bindings are stored in the .ini file for example DefaultInputs.ini

See the UIButton class for a fairly simple example of how to hook up and process input key events.

Multiple Player's Input Handling

Here is a brief description on how the scene differentiates and handles input from multiple players. When scene is notified about the input key event it is also told the index of the controller which caused this event. Internally however scene uses a more general PlayerIndex to distinguish between players, so it converts the controller index into a player index inside the UUIScene::PreprocessInput function. Internally scene stores the mapping between input keys and widgets subscribed to handle them in a InputSubscriptions map. Each entry in this map contains a input key name and an array of FInputEventSubscription objects. Each index in that array corresponds to a player index, so each player has a unique subscription of handlers to each input key. Scene then proceeds to obtain the widgets subscribed to respond to this input event only for the player which caused the input key event to happen. Scene then invokes these individual widget input handlers. This process can be observed in the UUIScene::InputKey function.

Player Input Mask

Each UUIScreenObject contains a PlayerInputMask data field. This value specifies which indexes in the Engine.GamePlayers array is this control going to process input from. When widgets subscribe themselves to the scene to handle input keys, they do it only for the players specified in their PlayerInputMask. This data field can be set throught the script event functions UIScreenObject::EnablePlayerInput and UIScreenObject::DisablePlayerInput. PlayerInputMask is implemented like a bitfield; if the control is supposed to accept input from player index 2, the mask value will have a 1 in the second bit position. So PlayerInputMask value 255 means that this control will accept input from all the gamepads.

Split Screen

There is support in the UI system to create a split screen UI. UI system allows for multiple scenes to be opened simultaneously, one for each player, and allows to specify how each scene will process input from different players. GameUISceneClient is the class responsible for keeping track of the currently active scenes. It provides scenes with all data necessary for their operation, routes the rendering to the scenes, and passes input key events to them.

You can control which scene will receive input from which player based on the the EScreenInputMode field. Each UUIScene object contains a EScreenInputMode field which allows to specify player input constraints. You can assign scene's input mode via UIScene::SetSceneinputMode script function. During scene initialization the CalculateInputMask script function is called which sets up scene's PlayerInputMask properly based on the value of EScreenInputMode and player index value which activated the scene. Here is an overview of the available screen input modes.

INPUTMODE_None
This scene doesn't process input at all. Useful for optimizing input processing for scenes which don't process any input such as HUD scenes.,

INPUTMODE_Locked
Simultaneous inputs are not supported in this scene. Only input from the gamepad that is associated with this scene will be processed. Input from other gamepads will be ignored and swallowed. This is the most common input mode.

INPUTMODE_MatchingOnly
Similar to INPUTMODE_Locked, except that input from gamepads not associated with this scene is passed to the next scene in the stack. Used for e.g. profile selection scenes where each player can open their own profile selection menu.

INPUTMODE_ActiveOnly
Similar to INPUTMODE_Free, except that input is only accepted from active gamepads which are associated with a player. All input and focus is treated as though it came from the same gamepad, regardless of where it came from. Allows any active player to interact with this screen.

INPUTMODE_Free
Any active gamepad can interact with this menu, even if it isn't associated with a player. Used for menus which allow additional players to become active, such as the character selection menu.

INPUTMODE_Simultaneous
Input from any active gamepad will be processed by this scene. The scene contains a unique set of controls for each active gamepad, and those controls only respond to input from the gamepad they're associated with. Used for scenes where all players should be able to interact with the same controls in the scene (such as a character selection menu in most fighting games)

Input Handling Scenario Example

InputHandlingSequenceDiagram.jpg

This diagram describes how input key events are passed from the user to the widgets and how do widgets handle input key events. When player causes an input key event the current scene looks into its InputSubscriptions map to find the widgets which are currently subscribed to handle this input key. It then aggregates the input handling to the specific widgets. Widget HandleInputKeyEvent function first tries to fire off any Kismet UIActions associated with this input key which would override default input handling. If no widget specific overrides are found in Kismet then the ProcessInputKey method is called which contains the default input handling implementation.

  • Input Handling Sequence Diagram

Focus Chaining and Bound Navigation


FocusChainDrawings.vsd:

Overview of Navigation

Navigation between widgets in a scene causes them to change their states. The current user selected widget is traditionally in the Focused state. When the user navigates between widgets they leave their present state and enter the Focused state. There are two ways to navigate between widgets, Unbound and Bound. Unbound navigation is set up using the focus chain tool and bound navigation is defined by the widget Tab order.

Unbound navigation relates to widget faces. Each widget contains four faces: left, right, top, and bottom. Each face can be linked to another widget. The path of widget focus within a scene will follow this link upon receiving an unbound navigation input event. Bound navigation is initialy setup by the system and can be modified by the desiger through changing the widget TabIndex ordering. Both navigation orders can dynamically change in the game based on which widgets are visible or enabled at the present time.

Which Widgets Are Eligible To Receive Focus

Not all widgets will be able to receive focus at all times within a scene. Certain actions can enable or disable widgets which might interfere with the focus path. The following conditions must be satisfied for widgets to be eligible to receive focus:

  1. a widget must be visible, and
  2. not in a disabled state.

Those values are indicated by the bHidden flag and states present in the widget's StateStack.

Unbound Navigation

Each widget contains a FUINavigationData field for each player within the game. This Navigation data internally stores four widget targets, one for each face that should receive focus when the corresponding navigation input event is received. Navigation links can be set up either by the designer or automatically by the UI system. Initially, all navigation links are undefined. If the designer does not define them they will be automatically setup by the UI system. A link is defined upon either being connected to another widget or explicitly set to NULL. NULL widget links are omitted throughout the UI system automatic link generation process. The automatic link generation within the UI system will assign its own "best choice" targets. The algorithm for automatic focus link generation is as follows. Automatic links will only be set up between sibling widgets in the scene. An automatic focus link will never be generated between a parent widget and any of its child widgets. Whenever a child has focus its parent must also have focus. The system will link up each face of a source widget to a different sibling widget by comparing which widget is the closest to the respective face, and also checking if it is "correctly" positioned with respect to the source face. For example, the left face of a target widget needs to exceed the right face of the source widget. This algorythm can be observed within the UUIObject::GenerateAutoNavigationLinks method.

After the links are defined, the system is ready to handle unbound navigation events. Navigation events are treated the same way as any other input key event, meaning the widget which is currently in focus subscribes itself to the scene to handle navigation input events when it enters the focused state. When the user presses the Left arrow, that input event is passed to that widget which is currently in focus, and then this widget decides where the focus should go next, based on what target is currently assigned in its Navigation data field. In practice a widget's parent gives focus to the appropriate sibling of the widget which received the navigation event. The process which is responsible handling navigation events is common across all widgets and it can be found in the UUIScreenObject::ProcessInputKey and in UUIScreenObject::NavigateFocus methods (see Unbound Scene Navigation Scenario Example below).

Focus Propagation Between Parent and Child Widgets

There is a special rule in place for handling of focus gain by parent widgets. When a parent widget contains child widgets and it receives focus, the focus will be also automatically propagated down to the most eligible child widget. The most eligible widget is determined by the TabIndex value set by the designer in the UIEditor. If the designer does not specify the TabIndex values for the children, then the most eligible child will be the one which is currently in the first position of the UUIScreenObject::Children array. After parent propagates the focus to its children, both the parent widget and child widget will be in the focused state; however, only the child widget will be responding to any input events. From this point on, focus navigation will remain within the scope of the children widgets, however with one important feature. If a child widget receives the navigation input event and it doesn't have a defined focus link in the specified direction, then it will forward the navigation request to its parent. Then the parent widget's focus chain determines next focus target; therefore, focus will jump from the parent's child to parent's sibling.

Bound Navigation

Bound navigation iterates over all widgets in the scene in a "linear" fashion. From the current widget you can navigate to only one next or one previous widget. Each widget has a FUIFocusPropagationData array which contains sibling widgets which will receive focus when a bound navigation event occurs. FUIFocusPropagationData::NextFocusTarget is the widget which will receive focus in case of the NextControl event, and FUIFocusPropagationData::PrevFocusTarget is the widget which will receive focus in case of the PrevControl event. These targets are assigned during requested scene updates when UUIScreenObject::RebuildKeyboardNavigationLinks is called. The order in which the rebuild links process will arrange the sibling widgets depends on their TabIndex values. The widget with the smallest tab value will recieve the focus first, and the next target will be the sibling widget with the next smallest TabIndex value. In an event that two or more widgets have the same TabIndex value, their respective order will be determined by current position in their parent's Children array. The methods which are responsible for handling the focus switch to the next and previous control are UUIScreenObject::NextControl and UUIScreenObject::PrevControl. It is worth noting that if the current widget does not have a next control, because it is the sibling with the last TabIndex, then bound navigation handling will forward the NextControl or PrevControl request to the widget's parent and the parent's link will determine the next focus target; thereby, achieving iteration over all widgets in the scene, and not just the siblings.

Unbound Scene Navigation Scenario Example

Presented below is a scenario where user navigates from one widget to another in the scene. There are four buttons in the scene, Button0 is a parent for Button1 and Button2, and Button2 is a parent for Button3. The scenario begins with Button1 being focused and user navigates focus to Button2.

  • Button Hierarchy

FocusChainDiagramButtonHierarchy.jpg

  • Navigation Event Sequence Diagram

FocusChainNavFocusSequenceDiagram.jpg

This diagram describes what happens when the system receives a NavFocus input event, and how it passes the focus from the current widget to the navigation target. User sends NavFocus event to the currently focused Button1. Button1 passes the NavigateFocus call to its parent who retrieves the sibling widget which is the navigation target in Button1's FUINavigationData field (as a side note, if the designer did not specify a focus chain target for Button1 then Button0 would forward the call to its parent and Button0's focus chain target would be chosen). Focus chain link indicates that Button2 is the sibling widget which is to receive focus so its passed the SetFocus call. The execution of this method causes the target to transition into the focused state and also propagate the focus to its children ( if it contains any ). Therefore in our example both Button2 and Button3 become focused as a result.

  • Focus Gain Sequence Diagram

FocusChainGainFocusSequenceDiagram.jpg

This diagram describes what occurs when the widget is called upon to gain focus, and how the previously focused control loses its focus. When widget's UUIScreenObject::GainFocus method is called the widget state transition occurs when calling ActivateState( focused ),which adds the focused state into widget's current state stack. Widget then needs to notify its parent that it became focused. The parent is responsible for storing information about which of its children is in focus currently and is also responsible for finding the previously focused control and killing the focus on it. Button0 looks into its FPlayerInteractionData array to find the previously focused control which in this case was Button1. After Button1 loses focus it returns to whatever state it was in before it gained focus. Next step for Button0 is to transition into the focused state if it hasn't been focused yet. After all appropriate widgets changed their states the original control ( Button2 ) registers itself as a new listener for input key events.

Menu States


Overview

A Widget State is a condition that a widget can be put into that can contain specific user input settings, Kismet logic, and style data. Below is a more comprehensive description of how menu states work in the Unreal system.

State Class

The generic abstract class for all the widget states is the UUIState. It holds state specific information pertaining to widgets such as kismet sequence logic and user defined input key events overrides. States are also responsible for handling activation and deactivation requests from widgets. Programmer can create new states that derive off of the generic UUIState class. In the derived states you can modify the default actions taken when this state becomes activated or deactivated. You can modify the default state activation behavior by implementing Unreal Script event function OnActivate or in C++ by overriding the OnActivate method, likewise state's deactivation behavior can be changed by redefining OnDeactivate functions either in your state's script or c++ implementation. Both of these methods would affect all the instances of this state across all the widgets. If however you wanted to modify just one particular state instance behavior upon activation or deactivation you could also do it through Kismet by adding logic to the events "Enter State" and "Leave State". Look at the existing state's implementation, i.e. UUIState_Active, for some more examples.

How widgets interact with states

Each UUIScreenObject (widget) comes with a programmer defined list of states that it can enter but by definition every widget must contain at least the enabled and disabled states. That list of supported states is defined in the widget class defult properties block by adding the state names to the DefaultStates array.

  • From UIButton DefaultProperties in UIButton.uc
      // States
      DefaultStates.Add(class'Engine.UIState_Focused')
      DefaultStates.Add(class'Engine.UIState_Active')
      DefaultStates.Add(class'Engine.UIState_Pressed')

When the scene gets initialized all the widgets instantiate items in their DefaultStates array and populate the UUIScreenObject::InactiveStates array with UUIState objects. Each object within a widget's InactiveStates list serves as a container for unique user definable behavior. When a particular state from the InactiveStates list is activated it is moved onto the widget's StateStack and the state which is currently on top of the stack indicates widget's current state. Putting states on the state stack allows us to enforce certain state transition rules, for example, a widget cannot enter the focused state unless the enabled state is already on the stack. States present in the StateStack can be deactivated at any time either through navigation, input key events, or manually through code. When a state is deactivated it is removed from the stack and if the deactivated state is on top of the stack, the widget will then change its current state to the previous state on the stack.

When a widget enters a new state a change in its underlying logic and a change in its visual appearance occurs. Widget's logic is represented through the events and actions present in the current state's Kismet sequence and also any state defined input key overrides. When the new widget state becomes activated the default behavior is to:

  1. push the state onto widget's StateStack,
  2. fire off any OnEnterState events currently present in Kismet,
  3. add the StateSequence to the widget's EventProvider component, which brings the logic contained by this state into scope,
  4. Registers the state's specific input key handlers with the scene for proper user input handling, and
  5. change the widget's underlying style data for the current state.

This default behavior can be found in UUIState::OnActivate method which can be overriten in the derived classes by the programmer. Updating the visual appearanace of a new state is the responsibility of individual widgets and can be found in their respective OnStyleResolved method.

System Defined States

Currently there are 5 menu states implemented in the UI system, each has its own special rules and behavior, below is a more in depth description of how the system responds when widgets enter these states.

Active
Represented by the UUIState_Active class. The enabled state must be present within a widget's StateStack prior to entering active state. A widget will enter the active state when the mouse cursor is over the given widget. The UUIState_Active can not be deactivated, i.e. removed from the StateStack, if the widget is in the pressed state.

Disabled
Represented by the UUIState_Disabled class. When a widget enters this state all states on its StateStack are cleared off and deactivated. Once in the disabled state the widget can only enter the enabled state. The children of a disabled parent widget will also be put into the disabled state.

Enabled
Represented by the UUIState_Enabled class. A widget must be in the enabled state before it may transition into states other than the disabled state. Deactivation of the enabled state also deactivates any states currently on top of the enabled state on the stack.

Focused
Represented by the UUIState_Focused class. A widget is required to be in the enabled state prior to enetering the focused state. Bound and Unbound navigation events cause widgets to enter this state. Pre-defined behavior exists within the UI navigation system which will additionally set focus to the children of the widget which enters the focused state. Due to this functionality the input event registration is not done in the UUIState_Focused::OnActivate method but rather in the UI system navigation handling code. Only the child most widget with focus will register itself with the parent scene to receive user input events. Deactivation of the focused state will also deactivate the pressed state if it exists in the stack.

Pressed
Represented by the UUIState_Pressed class. Widgets must be in the enabled state prior to entering pressed state. Activation of the pressed state also causes the widget to enter the focused state.

  • Menu States Class Diagram

MenuStatesClassDiagram.jpg

Data Representation


See the Data Store Technical Guide for how data is manipulated and represented in the UI.

Animation


See the UI Animation Technical Guide for how to animate widgets in the UI

Limitations


Keep in mind that in order to navigate between scenes, the scene that is currently focused must be closed.

If your UI solution is using multiple scenes for animation or transition purposes, you could put them into their own panel within the scene and use it as a master scene. The scoreboards in Gears 2 all existed in one UI scene, and were toggled on/off based on gametype, and likewise for the Party Menus - everything existed in the one UI scene.

Debugging UI Scenes and Widgets


You can press Ctrl+Alt+D while a menu is open to display UI debug text, which will show you which widgets are currently active vs. focused, and which state they are in.

Downloads


* MenuStatesDrawings.vsd