MenuFramework is a rich extendable XML menu engine with LUA integrated script engine (C++ exposition capabilities) It gives an easy way to add interactive interfaces to your program without hassle. Code is cross-platform, tested and compiled on Windows, Linux and MacOS !
- Stabax
- Stalker2106
- CMake
- Nlhomann's json
- Pugixml
- Lua
- Sol2
You need to grab dependencies if they are not installed on your machine (which is probably the case), you can do so with the following command-line:
git submodule update --init --recursive
The project itself is based on CMake, which means you have to generate the build tools for it. You can generate Makefiles, Visual studio solutions, etc with the following commands (when at repository root)
mkdir build && cd build
cmake ..
That procedure should generate the correct build tools based on what your cmake is configured for. You can then just build the library (statically is preferred) and link against it! The library has been tested on C++14 compliant MinGW, GCC, and MSBuild.
The menu framework is component-driven, the core components are the GraphicsRenderer, and the InputManager. They allow to use any rendering system to render Menus, and any user input mechanism to interact with user interface.
Menu framework uses a specific XML markup for menu, which can be supplied either through C++ Strings, or Filesystem. A basic example of a program that shows a menu is the following:
std::string menuDocument = "<Menu>"
" <Text>Menu Example</Text>"
" <Sep/>"
" <Button Type='Intern' Target='alert(\"Stop pressing me!\")'>Press me!</Button>"
" <Button Type='Intern' Target=''>Do nothing</Button>"
"</Menu>";
Menu::init(); //Load library
Menu::goTo("", menuDocument, DataSource::Document);
Menu::run();
By default, the framework uses a NativeRenderer and NativeInput, which uses respectively system standard output and input (raw mode). If you want, you can provide implementation for both of them through the following methods:
static void Menu::setRenderer(std::shared_ptr<GraphicsRenderer> renderer);
static void Menu::setInputManager(std::shared_ptr<InputManager> inputmgr);
To do so, just create your own renderer and inputmanager, inherit from GraphicsRenderer and InputManager classes, and implement pure virtual functions. Examples can be found here
All of the following tag/attributes are writable in MenuFramework markup, and will generate the corresponding widget, based on the position in the hierarchy of the XML Document.
Tag | Attribute | Description |
---|---|---|
All | Every element implements these attributes | |
Id | Sets id of element for interaction in script | |
Menu | Container for menu items | |
OnLoad | scriptId to execute when creating menu | |
Text | Non selectable entry of characters | |
Lang | Substitutes the whole tag with the corresponding lang unit from locale file (Matching Ids) | |
Sep | Non selectable horizontal separator | |
Button | Clickable entry | |
Type | Can be any of: { Intern, Goto, Script } | |
Target | Source, function or scriptId to execute | |
Path | Path where the source is located if extern | |
Input | Field to input characters | |
Select | Multi-value selector | |
Option | Selectable value option for select | |
Value | Sets value corresponding to text | |
Script | Lua script. Can be defined outside of a menu to be called by Id, or inside Menu, will be called when parsed. | |
Alert | Menu embedded alert message |
This is an example of a basic menu that has a button "Print!" which when pressed trigger an alert saying "amazing":
<Menu Id="ExampleMenu">
<Text>Example label</Text>
<Sep/>
<Button Type='Intern' Target='alert("amazing")'>Print!</Button>
</Menu>
an advanced menu example that shows text, two dropdowns with values using localization, buttons, alerts... :
<Menu Id="NewGame">
<Text>Game Creation Menu</Text>
<Select Text="Select difficulty">
<Option Value="0"><Lang Id="difficulty.easy"/></Option>
<Option Value="1"><Lang Id="difficulty.medium"/></Option>
</Select>
<Select Id="Locale" Text="Locale">
<Option Value="en-US">en-US</Option>
<Option Value="fr-FR">fr-FR</Option>
</Select>
<Input Id="Profile"><Lang Id="global.name"/></Input>
<Button Type="Intern" Target='alert("ok")'>Create Profile</Button>
<Button Type="Goto" Target="Home">Back</Button>
</Menu>
You may want to have custom behaviour inside your menus, thats why the framework implements its own script engine. The language of the script is lua, which allow very simple learning for easy customization, and exposes C++ Data easily (C++ binding is explained in next chapter)
If you need to access/alter menu, or manipulate back-end data, the following lua functions are implemented and callable in any script:
Function | Description |
---|---|
alert(string) | add an alert with given string to current menu |
tostring(int) | converts given integer and returns it in string form |
getCursor() | returns integer position of cursor (Only hovereable items count) |
getInputData(id) | returns string contained in input of given string Id (id) |
setInputData(id, value) | sets string data (value) in input of given string Id (id) |
getSelectValue(id) | returns string data (value) of selected option in select of given string Id (id) |
setSelectValue(id, value) | sets option data (value) to select inside select of given string Id (id) |
addMenuItem(pos, xml) | add Item in menu at position (pos) with XML Data (xml) |
This is an example of a menu implementing a script that prints a menu that lists items and allows to select one:
<Menu Id="Test" OnLoad="TestOnLoad">
<Text>Select an item</Text>
<Sep/>
<!-- Items will be inserted here -->
<Sep/>
<Button Type="Intern" Target="exit()">Quit</Button>
</Menu>
<Script Id="TestOnLoad"><![CDATA[
i = 0
while (i < 5) do
addMenuItem(2 + i, "<Button Type='Intern' Target='alert(\"selected: "..i.." !\")'>Item "..i.."</Button>")
i = i + 1
end
]]></Script>
Will output:
Select an item
Item 1
Item 2
Item 3
Item 4
By default, the menu framework does not uses the "locale" system. You can load any locale when starting the program, using the static "load" function in localization where langCode is the locale JSON file name.
Localization::load("en-US"); //Loads english locale
Now, the program will look inside the current working directory for a file named "./en-US.json", which will contains a JSON formatted translated units like so:
{
"localization": "English",
"data": {
"any.key": "Translated value"
}
}
You can change the folder where the localization looks for by altering the following variable
Localization::langLocation = "./my/localization/folder/";
You can insert "Localized" strings in your menus through the "Lang" XML element, which will be substitued by the corresponding text of lang Id in the locale file. If the langId has no translation, it will be shown as is. For example, the following menu will show the previous "any.key" defined unit.
<Menu>
<Lang Id="any.key">
</Menu>
MenuFramework is highly customizable, every item element comes with a default style applied, but the whole system allow stylesheets to override default styles. These stylesheets are written in json, but use more or less CSS syntax. For instance, this is a valid stylesheet:
{
"Text": {
"margin-left": 10
},
"Separator": {
"string": "-=-=-",
"margin-top": 1,
"margin-down": 1
}
}
Styling attributes are grouped by Item, and are extendable, which means you can add your own as long as you handle them inside the renderer or through lua scripts. Any MenuItem can be overriden, just type their name as defined here. The following table shows the existing rules for styling:
Rule | Value | Description |
---|---|---|
color | #FFFFFF | set foreground (text) color of item |
bgcolor | #00FF00 | set background color of item |
string | "-=-" | set the character or string to be rendered for this item |
margin-left margin-top margin-right margin-down | 5 | adds spaces for horizontal spacing, and lines to vertical |
spacing | 2 | set the spacing (in space chars) between the label and the control |
bracing-left bracing-right | "<<" | set the string to print at left of value bracing or element bracing |