Dialog Configuration Tutorial – Arma Reforger
The Configurable Dialog system allows to easily make a custom dialog given a config file.
Dialog Creation
- Make a layout if needed.
- Inherit from SCR_ConfigurableDialogUi for a custom dialog handler, if needed.
- Make a SCR_ConfigurableDialogUiPresets config file or choose an existing one.
- Add entries of SCR_ConfigurableDialogUiPreset in the config: these are your dialogs. Give them exclusive tags within the config file that holds them.
- In scripts, when you want to spawn a dialog, you need to:
- Instantiate the custom handler (if created)
- Call the static method SCR_ConfigurableDialogUi.CreateFromPreset(). The parameters you need to provide are:
- presetsResourceName: ResourceName (GUID) of the config file that holds the dialog preset.
- tag: unique identifier string to target the dialog to be spawned.
- customDialogObj: optional handler class inheriting from SCR_ConfigurableDialogUi - if not provided the base SCR_ConfigurableDialogUi will be used instead.
The CreateFromPreset() method returns the instance of the dialog handler that has been made for the newly spawned dialog, so you can cast to your custom handler type to perform further operations.
Layouts
Arma Reforger generally uses three layouts: ConfigurableDialog, ConfigurableDialog_Medium, ConfigurableDialog_Big.
The structure is:
- a Size widget that defines the dimensions of the whole dialog
- a Header with title and icon
- a Message text
- a Content area
- a Footer for the buttons.
The content of complex dialogs should be its own layout, which the system will dynamically add to the base.
Still, nothing stops anyone from creating their own "complete" layout by inheriting from one of the bases and filling the ContentLayoutContainer widget manually with whatever needed content.
It is recommended to have the content be its own layout; however for ease of maintenance, future changes to the base hierarchy might cause the loss of content that is not protected in its own prefab layout.
Same goes with buttons: they either get added through the .conf files or in the manually created layout.
SCR_ConfigurableDialogUi
The base dialog handler is a ScriptedWidgetComponent that will be instantiated by the dialog creation process and attached to the dialog layout.
It provides handling of the Title, Message, Content and Footer as defined in the presets, and includes methods and invokers for confirming, canceling and closing.
The dialog itself is technically a menu,, since the Configurable layout is inserted into a proxy menu created with the enfusion Menu Manager, and thus has also access to menu events.
By inheriting from SCR_ConfigurableDialogUi you can freely extend its functionality.
SCR_ConfigurableDialogUiPresets
These config files allow us to spread the large amount of dialogs needed by the game into multiple, smaller lists.
This avoids unnecessary clutter in chimeraMenus.conf, and allows to organize dialogs in their own .confs and scripts.
A dialog config file can contain multiple presets, identified by tag. The standard Configurable dialog preset should already have all the attributes necessary for your dialog, but you can always inherit from SCR_ConfigurableDialogUiPreset and extend it.
The Tag is used to know which dialog to create. As such, you need unique tags in a single .conf file.
The system will assemble the dialog, adding the content and buttons to the base.
It is possible to create prefab .conf files for single dialogs and buttons, and then use those in the general dialog .conf files. An example is MessageOkCancel.conf.
Related Classes
Configuration Classes
- SCR_ConfigurableDialogUiPresets - collection of dialog presets. This is a config root.
- SCR_ConfigurableDialogUiPreset - class of one preset configuration. A preset contains some properties of a dialog (dialog tag, style, message, title, buttons configuration)
- SCR_ConfigurableDialogUiButtonPreset - class of one button configuration (button tag, name, label, alignment).
Main Classes
- SCR_ConfigurableDialogUi - the main class which represents a configurable dialog. You can override some methods in inherited classes for custom functionality. This class inherits from ScriptedWidgetComponent, and in the end gets attached to the dialog's root widget as a component. There are several ways to attach it to the widget:
- call CreateByPreset() with customDialogObj left null. In this case the system creates a new SCR_ConfigurableDialogUi object and attaches it to the widget.
- call CreateByPreset() and provide it with a customDialogObj, in this case the provided object will be attached to the widget.
- attach a SCR_ConfigurableDialogUi-inherited object directly to the layout's root widget, in the layout file.
- SCR_ConfigurableDialogUiProxy - this is the parent menu which holds the actual dialog widgets.
Initialisation Sequence
The whole dialog creation and initialisation sequence is done in SCR_ConfigurableDialogUi.CreateFromPreset() and CreateByPreset methods.
- CreateFromPreset(ResourceName presetsResourceName, string tag, SCR_ConfigurableDialogUi customDialogObj = null) is called.
- the config file provided in presetsResourceName is loaded, the preset object is found by tag.
- the proxy dialog in game's MenuManager is created.
- the actual dialog widgets are created inside proxy dialog's root widget. The actual dialog layout is taken from preset's m_sLayout property.
- a new customDialogObj is created and attached to the dialog's root, or the provided customDialogObj is used, or the one which is already attached to the layout.
- Dialog.Init is called - here we apply title, message, add buttons.
- Dialog.OnMenuOpen is called - here we expect derived class to perform the custom initialisation, if needed.
Examples
Basic Usage
We create a simple dialog with message. If we want to do something when a button is pressed, we can subscribe to one of the available events.
Standard Usage
The dialog has some code associated only with itself, and we want that code to run regardless from where the dialog was invoked.
The takeaway here is the call signature of CreateFromPreset:
the last argument is customDialogObj, which lets us tie an object to a dialog if needed.
Advanced Usage
We can create a dialog's more advanced content
Common ConfigurableDialog
- Game exit popup - MainMenuUI:
Binds the OnBack function to the "Back" button click// subscribe to buttons SCR_InputButtonComponent back = SCR_InputButtonComponent.GetInputButtonComponent("Back", footer); if (back) back.m_OnActivated.Insert(OnBack); - OnBack() calls TryExitGame():protected static void TryExitGame() { int numCompleted, numTotal; SCR_DownloadManager dlManager = SCR_DownloadManager.GetInstance(); if (dlManager) dlManager.GetDownloadQueueState(numCompleted, numTotal); if (numTotal > 0) new SCR_ExitGameWhileDownloadingDialog(); else new SCR_ExitGameDialog(); }
- SCR_ExitGameWhileDownloadingDialog and SCR_ExitGameDialog are defined in CommonDialogs.c, they inherit from SCR_ConfigurableDialogUi.class SCR_ExitGameDialog : SCR_ConfigurableDialogUi { //--------------------------------------------------------------------------------------------- void SCR_ExitGameDialog() { SCR_ConfigurableDialogUi.CreateFromPreset(SCR_CommonDialogs.DIALOGS_CONFIG, "exit_game", this); } //--------------------------------------------------------------------------------------------- override void OnConfirm() { GetGame().RequestClose(); SCR_AllFilterSetsStorage.ResetAllToDefault(); } }
- DIALOGS_CONFIG is the config file CommonDialogs.conf.
"exit_game" is a tag in CommonDialogs that CreateFromPreset() uses to find the right preset. CreateFromPreset() then calls CreateByPreset() to initialise the dialog and bind events to the buttons.static SCR_ConfigurableDialogUi CreateByPreset(SCR_ConfigurableDialogUiPreset preset, SCR_ConfigurableDialogUi customDialogObj = null) { // Open the proxy dialog SCR_ConfigurableDialogUiProxy proxyComp = SCR_ConfigurableDialogUiProxy.Cast(GetGame().GetMenuManager().OpenDialog(ChimeraMenuPreset.ConfigurableDialog)); // Create the actual layout inside proxy Widget internalWidget = GetGame().GetWorkspace().CreateWidgets(preset.m_sLayout, proxyComp.GetRootWidget()); if (!internalWidget) { Print(string.Format("[SCR_ConfigurableDialogUi] internalWidget wans't created"), LogLevel.ERROR); return null; } SCR_ConfigurableDialogUi dialog = SCR_ConfigurableDialogUi.Cast(internalWidget.FindHandler(SCR_ConfigurableDialogUi)); // Create a new dialog object, or apply the provided one, if the dialog obj was not found in the layout. if (!dialog) { if (customDialogObj) dialog = customDialogObj; else dialog = new SCR_ConfigurableDialogUi(); dialog.InitAttributedVariables(); internalWidget.AddHandler(dialog); } dialog.Init(internalWidget, preset, proxyComp); proxyComp.Init(dialog); // Set action context if (!preset.m_sActionContext.IsEmpty()) proxyComp.SetActionContext(preset.m_sActionContext); // Call dialog's events manually dialog.OnMenuOpen(preset); return dialog; }
Accessing the content layout
SCR_ConfigurableDialogUi.GetContentLayoutRoot() will return the first widget of the content layout. Useful for those dynamically generated dialogs.