Examples¶
Animating values¶
First, grab a component by ID:
ProgressBar* progressBar = dynamic_cast<ProgressBar*>(displayManager->GetActiveScreen()->GetElement("progress_bar"));
Then execute a setter in the main loop:
progressBar->SetProgressValue((SDL_GetTicks() / 100) % 100);
Callbacks¶
There are two ways to add callbacks to GUI components. You can write them in either C++ or JavaScript.
C++¶
Create the callbacks:
void ButtonCallback(Button *Sender, const Event::ArgVector& Args) {
printf("Button clicked! (%s)\n", Sender->GetID());
}
void SwitchCallback(SwitchButton *Sender, const Event::ArgVector& Args) {
switch (Sender->GetSwitchState()) {
case true:
printf("Switch is ON! (%s)\n", Sender->GetID());
break;
default:
printf("Switch is OFF! (%s)\n", Sender->GetID());
}
}
Add them to the callbacks container:
displayManager->AddCallbackToContainer("ButtonCallback", (grvl::Event::CallbackPointer)ButtonCallback);
displayManager->AddCallbackToContainer("SwitchCallback", (grvl::Event::CallbackPointer)SwitchCallback);
/*^^^^ choose a callback name */
JavaScript¶
Create a file with JavaScript callbacks, e.g.:
// callbacks.js
function ButtonCallback(caller) {
const buttonId = caller.name
Print("Button clicked! (" + buttonId + ")")
}
function SwitchCallback(caller) {
const callerName = caller.name
if (caller.switchState) {
Print("Switch is ON! (" + callerName + ")")
} else {
Print("Switch is OFF! (" + callerName + ")")
}
}
Then include it in your application’s XML layout:
<script src="callbacks.js"></script>
You can specify a non-default working directory for JavaScript files with:
JSEngine::SetSourceCodeWorkingDirectory("Scripts/JavaScript/");
See JavaScript documentation for further reference.
Binding callbacks¶
Bind callbacks to GUI components in XML by referencing the callback name:
<Button id="test_button" x="0" y="0" width="100" height="100" onClick="ButtonCallback" text="TEST" />
<SwitchButton id="test_switch" x="500" y="500" width="150" height="100" onSwitchON="SwitchCallback" onSwitchOFF="SwitchCallback" />
Popups¶
Adding an element in XML with a callback to show a popup:
<Button id="btn" x="0" y="0" width="150" height="100" onClick="ShowPopup('example_popup')" text="Show Popup" />
Adding a popup in XML (with an element closing it on callback):
<Popup id="example_popup" x="0" y="0" width="300" height="200">
<button id="close_popup_btn" x="100" y="10" width="100" height="50" onClick="ClosePopup" text="Close" />
<label id="popup_label" font="roboto-medium" x="0" y="100" width="300" height="50" text="This is a Popup." />
</Popup>
Adding images and fonts¶
Grvl support two font types Grvl Baked Fonts (.gbf) and True Type Fonts (.ttf). Both formats have their valid uses.
You can create a grvl baked font using the provided gbf CLI utility, that converts TTFs to GBFs.
The baking process allows you to select which codepoints should be included, refer to gbf --help for more information.
# Build GBF utility application
cmake -B build
cmake --build build --target gbf
# Bake characters into GBF
./build/gbf --ttf ./romfs/fonts/MyFont.ttf --size 18 --gbf ./romfs/fonts/MyFont.gbf --range ascii,0x100-0x200
If you want to use TTF fonts in a memory contained enviroment it may be beneficial to create a smaller TTF fonts with some, unused, glyphs removed.
You can use the open source pyftsubset utility for that. Unused glyphs can also be deleted from a font in Font Forge graphically.
# Create a ASCII-only version of the font
pyftsubset ./romfs/fonts/MyFont.ttf --unicodes=U+0020-007E --output-file=./romfs/fonts/MyFont-ascii.ttf
Loading both font types is shown below.
displayManager->AddFontToFontContainer("my_font_gbf", new GrvlBakedFont(path_to_font));
displayManager->AddImageContentToContainer("my_image", new ImageContent(path_to_image));
// using True Type Fonts
auto data = std::make_shared<grvl::TrueTypeData>(path_to_font);
displayManager->AddFontToFontContainer("my_font_ttf", new TrueTypeFont(data, 18));
Default fonts¶
When a font is not specified for an element (or the font is not found) grvl will try using the normal font - and if that is not present - the default font. You may see this in the logs:
[WARNING] Font "normal" doesn't exist. Using default font!
[ERROR] Default font doesn't exist.
[WARNING] Font "normal" doesn't exist. Using default font!
[ERROR] Default font doesn't exist.
[WARNING] Font "normal" doesn't exist. Using default font!
[ERROR] Default font doesn't exist.
To fix those errors you just need to provide a normal (and/or default) font, like so:
manager.AddFontToFontContainer("normal", new grvl::TrueTypeFont( /* ... */ ));
manager.AddFontToFontContainer("default", new grvl::TrueTypeFont( /* ... */ ));