VCMI has capabilities to change some UI elements in your mods beyond only replacing one image with another. Not all UI elements are possible to modify currently, but development team is expanding them.
Elements possible to modify are located in `config/widgets`.
Let's take `extendedLobby` mod from `vcmi-extras` as an example for VCMI-1.4. [Example sources](https://github.com/vcmi-mods/vcmi-extras/tree/vcmi-1.4/Mods/extendedLobby).
**You can take all assets from this tutorial from sources.**
This submod offers extended options while player creates new game.
For random map tab it defines UI to select RMG template, select map size bigger than XL, configure teams and roads.
For options tab it introduces UI for chess timers.
In this tutorial we will recreate options tab to support chess timers UI.
Let's copy `config/widgets/optionsTab.json` file from VCMI folder to `content/config/widgets/` folder from our mod.
It defines UI for options tab as it designed in original game, we will keep everything related to player settings and will modify only timer area.
**It's important, that when you are modifying `optionsTab.json`, game restart is not needed! When you updated file, it's enough to go back to main menu and then open lobby and options again. However, when you add new assets (images), you have to restart game to make possible find them.**
It looks like on image below and has 3 elements: label with "Player Turn Duration", label with timer duration ("Unlimited" on picture) and slider to control timer.
<imgwidth="350"alt="Снимок экрана 2023-08-30 в 14 53 49"src="https://github.com/vcmi/vcmi/assets/9308612/a34be309-29fc-412a-9d54-e40a634b56f9">
So we need to modify turn duration label and add combo box with timer types
<imgwidth="345"alt="Снимок экрана 2023-08-30 в 14 53 02"src="https://github.com/vcmi/vcmi/assets/9308612/717b21e6-4ac9-4e27-b90b-b68b0ce65db2">
Open `optionsTab.json` and scroll it until you see comment `timer`. Three elements after this comment are related to timer.
Let's find first element, which is label
```json
{
"items"
[
...
// timer
{
"type": "label",
"font": "small",
"alignment": "center",
"color": "yellow",
"text": "core.genrltxt.521",
"position": {"x": 222, "y": 544}
},
...
],
...
}
```
And modify it a bit
```json
{
"name": "labelTimer", //add name, only for convenience
"type": "label",
"font": "small",
"alignment": "center",
"color": "yellow",
"text": "vcmi.optionsTab.widgets.labelTimer", //replace text
"position": {"x": 104, "y": 542} //move to the left
},
```
But we also need proper background image for this label. Add image widget BEFORE labelTimer widget:
```json
{
"type": "picture",
"image": "RmgTTBk",
"position": {"x": 54, "y": 532}
},
{
"name": "labelTimer", //add name, only for convenience
...
},
```
In order to make it work, add file `RmgTTBk.bmp` to `content/sprites/`
Elements named `labelTurnDurationValue` and `sliderTurnDuration` we will keep without change - they are needed to configure classic timer.
Copy image `DrDoCoBk.bmp` to `content/sprites/`. Button objects use animated images to show different button states.
For normal, pressed, blocked and highlighted. Our combo box inherits this behavior, so let's convert image to animation.
In order to do it, we need to create file `DrDoCoBk.json` in same folder `content/sprites/` with following content:
```json
{
"sequences" :
[
{
"group" : 0,
"frames" :
[
"DrDoCoBk.bmp"
]
}
]
}
```
Thus we created file with animation, containing single frame which can be used for combo box.
Let's add one more element after `//timer` comment:
```json
...
//timer
{
"name": "timerModeSwitch", //this is important to name it timerModeSwitch, because VCMI binds behavior to element called this way
"type": "comboBox",
"image": "DrDoCoBk",
"position": {"x": 158, "y": 532},
"imageOrder": [0, 0, 0, 0],
"dropDown": {}, //here will be defined elements to be shown in drop down list
},
```
`imageOrder` helps VCMI to understand, which frame from animation to use in normal, pressed, blocked and highlighted states. In our case they will be same and we use 0 frame from `DrDoCoBk` animation.
We also want to have label on the top of this combo box showing which element is selected. You need to add `items` array, where additional elements can be specified, label in our case:
```json
...
//timer
{
"name": "timerModeSwitch",
"type": "comboBox",
"image": "DrDoCoBk",
"position": {"x": 158, "y": 532},
"imageOrder": [0, 0, 0, 0],
"dropDown": {}, //here will be defined elements to be shown in drop down list
"items":
[
{
"name": "timer",
"type": "label",
"font": "small",
"alignment": "left",
"color": "yellow",
"text": "vcmi.optionsTab.widgets.timerModeSwitch.classic" //default value for timer label
}
]
},
```
With that we already have desired layout with all elements shown by default, but we also need to add elements with timer modes into drop-down list:
<imgwidth="236"alt="Снимок экрана 2023-08-30 в 15 34 47"src="https://github.com/vcmi/vcmi/assets/9308612/f515f54e-94e1-4650-88e4-721e00c22e0d">
First of all, add images to `content/sprites/` folder: `List2Bk.bmp` for drop-down background and `List10Sl.bmp` for element highlighting.
Now specify items inside `dropDown` field
```json
"dropDown":
{
"items":
[
{
"name": "background",
"type": "picture",
"image": "List2Bk",
"position": {"x": 0, "y": -52} //negative value because our drop-down shall open in the top direction
},
{
"name": "slider", //let's add slider if we have more elements in future
"type": "slider",
"position": {"x": 212, "y": -52},
"size": 52,
"style": "blue",
"itemsVisible": 2, //we show only two elements
"itemsTotal": 0,
"selected": 0,
"orientation": "vertical",
"callback": "sliderMove" //callback predefined for drop-down menu to control which elements to show
},
//now list elements
{ //classic timer
"type": "item", //this is special type for drop-down elements
Now we show and hide elements, but visually you still can some "artifacts":
<imgwidth="341"alt="Снимок экрана 2023-08-30 в 15 51 22"src="https://github.com/vcmi/vcmi/assets/9308612/8a4eecdf-2c44-4f38-a7a0-aff6b9254fe6">
It's because options tab background image we use has those elements drawn. Let's hide them with overlay image `timchebk.bmp`.
It should be drawn before all other timer elements:
```json
...
// timer
{
"name": "timerBackground",
"type": "picture",
"image": "timchebk",
"position": {"x": 0, "y": 530}
},
{
"name": "timerModeSwitch",
...
},
...
```
This background must be visible for chess timer and hidden for classic timer. Just put its name `"timerBackground"` into `"hideWidgets"` and `"showWidgets"` for corresponding elements.
It works and can switch elements, the only missing part is chess timer configuration.
We should add text input fields, to specify different timers. We will use background for them `timerField.bmp`, copy it to `content/sprites/` folder of your mod.
There are 4 different timers: base, turn, battle and creature. Read about them here: https://github.com/vcmi/vcmi/issues/1364
We can add editors for them into items list, their format will be following:
```json
{
"name": "chessFieldBase",
"type": "textInput",
"background": "timerField",
"alignment": "center",
"text": "00:00", //default text
"rect": {"x": 54, "y": 557, "w": 84, "h": 25},
"offset": {"x": 0, "y": 0},
"callback": "parseAndSetTimer_base", //callback to specify base timer value from string
Add three remaining elements for different timers by yourself. You can play with all settings, except callback. There are 4 predefined callbacks to setup timers:
-`parseAndSetTimer_base`
-`parseAndSetTimer_turn`
-`parseAndSetTimer_battle`
-`parseAndSetTimer_creature`
And what we want to do is to hide/show those fields when classic/chess times is selected. Just add names of those elements into corresponding variables `"showWidgets"`, `"hideWidgets".
We are done! You can find more information about configurable UI elements in documentation section.
While designing a new element, you can make it configurable to reuse all functionality described above. It will provide flexibility to further changes as well as modding capabilities.
Class should inherit `InterfaceObjectConfigurable`.
```C++
#include "gui/InterfaceObjectConfigurable.h" //assuming we are in client folder
class MyYesNoDialog: public InterfaceObjectConfigurable
{
}
```
`InterfaceObjectConfigurable` doesn't have default constructor, but has possibility to specify arguments to be passed to `CIntObject`.
To make new object work, it's sufficient to define constructor, which receives const reference to `JsonNode`.
You can build custom widgets, related to your UI element specifically. Like in example above, there is Item widget, which can be also used on JSON config.