Standard/Data Driven UI
Support for M2 Standard button will be ending soon. This system will remain usable for a while but MSquared advises that you implement your own widgets without the M2 Standard Button to avoid disruption.
Overview
We have a number of places in our codebase where we want simple data-driven widgets, displaying e.g. lists of buttons, options or sub-menus. To handle these, weโve made a set of โstandard button dataโ objects and widgets. By using these, we can automatically build simple widgets without needing to create bespoke widgets, for e.g. interaction or devices.
A "Standard UI widget" is comprised of two main parts: the button widget (extending M2_StandardButtonWidgetBase
), which is the actual UI widget added to the screen, and the button data (extending M2_StandardButtonBaseData
), which contains the data which can be used to build the button widget. That way, we can build and modify more complex sets of buttons at runtime by updating the data, without needing widgets to be added.
How to use standard button data
Adding a standard UI widget
You can add standard UI widgets directly in the designer, and fill out the details there if desired. E.g. adding a WBP_M2_StandardButtonList
, or WBP_M2_StandardButton
In the Event Graph, you can then listen to the widgetโs events, e.g. OnButtonClicked
for the WBP_M2_StandardButton
Making a data object
Each standard UI widget has an expected button data type. Within that are a number of fields that define the appearance.
Every data object has the โcommonโ fields - an
Id
(to search for/identify particular data objects), someText
, optionalSubText
, and icons. There are 4 possible icon locations:IconLeft1
,IconLeft2
,IconRight1
andIconRight2
, which can be used to add a number of optional icons to different locations on the button.
Some data types have sub-lists of other data objects, such as buttons (
M2_StandardButtonData
) spawning sub-menus, or lists (M2_StandardButtonList
) containing other widgets. In these cases, you can select the data type from a dropdown.
Data objects can also be created outside of the designer view, e.g.
Creating them dynamically at runtime
Making โinstancedโ data variables, of
M2_StandardButtonDataBase
or one of the more specific child classes. NOTE: You will need to make sure it has theDetails->Advanced->Instanced
box checked, so that you can create the subobject within your executo
Listening to and updating the standard data
Each data type has specific events relevant to its data, e.g. the
BP_StandardInputTextButtonData
having theOnTextCommitted
event.The data also has setters, which trigger the UI to refresh, for instance
UpdateDetails
, which updates the text and icon shared across all button data types, orUpdateList
, which is exclusive toM2_StandardButtonListData
, and can be used to modify the list of button data contained within the listIn some cases, a widget's available button data will be of the base button data class, so may not have the relevant fields. you will need to cast it to modify fields specific to particular types of button data.
Note that it is bad practice to directly modify the sub-widgets inside a standard button widget. If the field is represented by the widget's data, e.g. its text or icon, we should only modify it from its button data. Modifying the data via the relevant methods will trigger the widget to refresh. That way, the data and widget always match.
As of v21, we have an extra helper function GetWidgetButtonData
, which should be used in favor of getting the widget's button data directly - this automatically converts the button data from M2_StandardButtonBaseData
to the appropriate type for the widget, e.g. M2_StandardButttonData
for WBP_M2_StandardButton
, or M2_StandardButtonListData
for WBP_M2_StandardButtonList
.
Finding nested button data
To navigate a nested button data variable, some helper functions have been added:
FindStandardButtonByClass
: Searches for the first button of the provided type within the button data.FindStandardButtonById
: Similar to the above, but allows us to search for a specific button data by IdFindStandardButtonsByClass
: Searches for all standard buttons of the provided class within the data. Optionally includes a โrecursion depthโ, in case we know how deep we should be looking inside the data (i.e. only looking for top-level buttons)
Modifying standard buttons' visuals
Via skins (Support ending)
Support for skinning is ending soon. Please see https://docs.msquared.io/tutorials-and-features/ui/how-to-use-skins for details.
If you want to change the appearance of buttons, such as the styling or color scheme, this can be handled through the skin. The BP_StandardButtonSkinSettings
has various fields that control how the visuals look, such as the background color, the images used for check-boxes and buttons, and the location of the button's "shortcut icon". For more details on skins, see How to use skins
Via modifying the widget
If you want to modify a specific button type in more bespoke ways, you will need to customize the widgets themselves. If you have access to the widget, you can do this directly. If you don't (i.e. it is a base origin widget and you are a downstream project), you will need to replace the widget. For this, see Widget themes.
Some important points to note when working with standard UI widgets (extending M2_StandardButtonWidgetBase
):
ButtonData
is the defaultM2_StandardButtonBaseData
type, so may not have all the fields you are expecting. You will need to cast to the correct data type.Event Initialize From Data
is the best place to respond to any changes in button data. is called when the widget is created (in-game and in design-time), and when the data is updated, so can be used to respond to changes, such as hiding parts of the widget that are no longer relevant, updating text, etc.When making cosmetic changes, you should consider What needs to be button data? - some things could be achieved simply by adding extra variables to the base widget, others should be in data to be propagated effectively.
What needs to be button data?
If you are wanting to make visual changes to a standard UI widget, it's worth considering where the change should be made in the button data, or exclusively in the widget itself. Things to think about:
If the change is related to the function of the widget, e.g. the state of a check-box, that is needed to accurately represent the widget, it will need to be in button data.
If the change is purely cosmetic, you may be able to get away with making it a field in the widget, without having it in button data:
If you want the styling change to be global for all widgets of that type, you can put it in a variable (and add it to the skin, if you want it to be modifiable in different styles)
If you want it to be modifiable per-instance, the change may need to be data driven:
If you're only having the property configurable for "root" widgets, e.g. adding the
WBP_M2_StandardButton
directly to the UI, you could get away with making it an Instance Editable property in the widget BP.If you want the property to be configurable for "non-root" widgets, e.g. in a widget entry inside a
WBP_M2_StandardButtonList
, it will need to be button data.
Standard button data types
M2_StandardButtonData (Widget type: WBP_M2_StandardButton)
The common button. Supports being either regular clickable buttons, toggle buttons, or โmenu buttonsโ
The
ButtonType
can be eitherActionButton
,ToggleButton
,MenuOnly
orDisabledButtonWithSubmenu
.The
SubMenuData
can either be left blank, or you can add aM2_StandardButtonListData
to it.If a sub-menu is present, and the
ButtonType
isMenuOnly
, clicking the button will open the sub-menu.If a sub-menu is present, and the
ButtonType
is notMenuOnly
, an extra button will be spawned to the side, to open the sub-menu.If the
ButtonType
isMenuOnly
and there is no sub-menu, the button will be treated as โdisabledโ.DisabledButtonWithSubmenu
is similar toMenuOnly
, but forces the button to be treated as "disabled", but having an extra "menu button" to the side. This can be used for buttons where the text is more of an indication, rather than an actual button, and the sub-menu is optional extra details with less focus.
The standard button also contains shortcut data. For more details on this, see Keyboard Shortcuts in Standard Buttons
M2_StandardButtonListData (Widget type: WBP_M2_StandardButtonList)
Contains a ButtonList
field, which can be filled with any form of standard button data.
M2_StandardButtonGroupData (Widget type: WBP_M2_StandardButtonGroup)
A group, specifically of M2_StandardButtonGroupEntryData
(Widget type: WBP_M2_StandardGroupEntryButton
). These are checkboxes, where clicking one can affect others in the group:
You can listen to entries in the group being selected or deselected, and can get any selected options.
If
SingleSelection
is false, you can select multiple options from the group. Otherwise, you can only click one (and clicking something else will deselect the current one)
BP_StandardTextBlockData (Widget type: WBP_M2_StandardTextBlock)
A text block, which has wrapping text.
BP_StandardSliderButtonData (Widget type: WBP_M2_StandardButtonSlider)
A basic slider. Has an OnValueUpdated
event that is triggered when the value changes, and a UpdateValue
method used to update the slider's current value
BP_StandardInputTextButtonData (Widget type: WBP_M2_StandardButtonInputText)
An "input field" widget, where you can enter text. Has UpdateCurrentText
and CommitCurrentText
methods, and OnTextUpdated
and OnTextCommitted
.
M2_StandardButtonListWithScrollAndShortcutData (Widget type WBP_M2_StandardButtonListWithScrollAndShortcut)
A modification of the standard button list, where there is a bespoke scrollbar widget, and a shortcut that tracks the selected button and can be used to click on it. Used by the interaction UI. Has added shortcut data fields to listen to scrolling up/down, and triggering the buttons
Entries in the list must be M2_StandardButtonWithScrollShortcutData
(Widget type WBP_M2_StandardButtonWithScrollShortcut
)
Device Settings
As well as the BP/M2 standard button data types, we also have some "device settings" as entries in the list of standard button data types. These are specializations of the M2_StandardButtonData, used by Inventory. For more details, see Device Settings.
Adding new data types
If you create a new class extending
M2_StandardButtonBaseData
, you can add fields, methods and events to it.If you want to make changes that should update the UI, the recommended approach is to call
BroadcastDataUpdated
once the changes have been applied. This will retrigger the UI to refresh (callingInitializeFromData
), so that it will reflect your updates.NOTE: When making a button data class, consider What needs to be button data?
Then make a corresponding widget to represent this button data, using
M2_StandardButtonWidgetBase
as a base.Make sure to specify your intended data as the
RequiredButtonDataClass
.You can override its
EventInitializeFromData
to apply any additional data specific to your new button data type, casting the existingButtonData
to your correct data typeEventInitializeFromData
is called when the widget is created (in-game and in design-time), and when the data is updated, so can be used to respond to changes, such as hiding parts of the widget that are no longer relevant, updating text, etc.
If you want your data type to be usable dynamically from e.g. a button list, you'll also need to add it to the visuals theme map. For more details, see Widget themes
Widget themes
You may have seen _Info
widgets in Origin, e.g. WBP_M2_StandardButtonList_Info
. These are an example of an alternative "theme" for the standard button data.
A standard UI "theme" is the mapping from button data to widgets. This is how we build the widgets from the data in e.g. a standard button list.
If there is not a specific class in the list, then the most relevant parent class will be used instead, e.g. BP_DeviceModule
will be treated as a M2_StandardButtonData
.
In a given button data widget, you can provide an override theme using the WidgetVisualsTheme
variable. For lists, this theme will then be applied to all entries within the list.
Last updated