We’ll cover some new topics this installment, including:
- Dialogs and dialog resources
- Event handlers
- Exploding a string
- Timers
Our Sample Script
string frames[];
int pos, size;
int main () {
string s1;
int rc;
// Get our ASCII art
s1 = GetScriptFolder() + "ascii art.txt";
// Convert file to string
s1 = FileToString(s1);
if (s1 == "") {
rc = GetLastError();
MessageBox('x', "Error opening frames (%08X)",rc);
return rc;
}
// Break string into array by line endings
frames = ExplodeString(s1);
// Get the size of the array
size = ArrayGetAxisDepth(frames);
// Open our dialog box
DialogBox("Holiday", "holiday_");
return ERROR_NONE;
}
// Event handler: dialog box load
int holiday_load() {
ControlChangeFont(100, "Mono");
EditSetText(100, "\r\r\r Press Start ");
}
// Event handler: dialog action
int holiday_action(int id, int action) {
// If Start was pressed, start timer
if (id == 101) {
pos = 0;
DialogSetTimer(75, 1);
}
// If Stop was pressed, end timer
if (id == 102) {
DialogKillTimer(1);
EditSetText(100, "\r\r\r Press Start ");
}
return ERROR_NONE;
}
// Event handler: dialog box timer
int holiday_timer(int id) {
string s1;
// Reset to the beginning if at end
if (pos >= size) { pos = 0; }
// As long as less than end and not an empty string, build our frame from ASCII strings
while ((pos < size) && (frames[pos] != "")) {
s1 += frames[pos] + "\r\n";
pos++;
}
pos++;
// Set the text of static control on dialog box
EditSetText(100, s1);
return ERROR_NONE;
}
#beginresource
Holiday DIALOG 0, 0, 275, 190
EXSTYLE WS_EX_DLGMODALFRAME
STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "BOOOOO!"
FONT 8, "MS Sans Serif"
{
CONTROL "Start", 101, "BUTTON", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 70, 172, 30, 12
CONTROL "Stop", 102, "BUTTON", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 170, 172, 30, 12
CONTROL "", 100, "static", SS_LEFT | WS_CHILD | WS_VISIBLE, 6, 0, 274, 170, 0
}
#endresource
Script Walkthrough
Our concept this week is pretty simple. We want to open a dialog box, allow the user to interact with the dialog box via a couple controls (buttons), and animate some text within the box. The Legato SDK provides an entire library of functions to create and control dialogs, from standard message boxes to more complicated custom dialogs. See the Legato SDK for more information on dialogs and how to use them. In this case, we’re going to create a
simple custom dialog box using a dialog resource:
#beginresource
Holiday DIALOG 0, 0, 275, 190
EXSTYLE WS_EX_DLGMODALFRAME
STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "BOOOOO!"
FONT 8, "MS Sans Serif"
{
CONTROL "Start", 101, "BUTTON", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 70, 172, 30, 12
CONTROL "Stop", 102, "BUTTON", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 170, 172, 30, 12
CONTROL "", 100, "static", SS_LEFT | WS_CHILD | WS_VISIBLE, 6, 0, 274, 170, 0
}
#endresource
In Legato, resource templates like this one can occur in a script file itself or in a separate file with an “.rc” extension. Resource templates are required for the script to be able to create a dialog. Here we are using a simple dialog with the attributes as listed above. Again, for complete instructions concerning dialog resource templates, see the Legato Documentation. As defined in the dialog template’s header, our custom dialog box
has “BOOOOO!” as its caption and other parameters, such as being modal and visible. It also has three controls: two buttons labeled “Start” and “Stop” and a static box control where we will be rendering our ASCII art. We place our resource template at the end of the script file nestled in between #beginresource and #endresource statements.
Now that we have a dialog template, let’s do something fun with it. We first declare a global string array to hold our ASCII text frames and a couple of global integers for position and size. Let’s look at our main function.
int main () {
string s1;
int rc;
s1 = GetScriptFolder() + "ascii art.txt";
s1 = FileToString(s1);
if (s1 == "") {
rc = GetLastError();
MessageBox('x', "Error opening frames (%08X)",rc);
return rc;
}
frames = ExplodeString(s1);
size = ArrayGetAxisDepth(frames);
DialogBox("Holiday", "holiday_");
return ERROR_NONE;
}
Our main function loads our text file, which contains our “frames” of ASCII text delimited by return characters (“\r” or “\r\n”), into a string. We do this with the FiletoString SDK function, which we have mentioned in previous weeks. Assuming we are able to read our file, we then use a second SDK function, ExplodeString, to separate our string into lines. These are simply separate strings that together comprise an entire “frame” of ASCII text animation, and each frame is separated by an empty string as per our source file and the blank lines that separate the frames. We store the text lines in our global string array. Note that the ExplodeString function can use any delimiter to separate a string into an array of strings, but if no delimiter is specified, it uses return characters, which is exactly what we want here. We can store the size of our array using the ArrayGetAxisDepth SDK function.
Now we load our dialog box. We do this with the DialogBox SDK function. We need to pass two parameters to this function: the name of the resource template (“Holiday”), and the prefix of the functions that will handle this dialog’s events (“holiday_”). Legato controls the message loop for dialog boxes and provides default functions to handle events pertaining to the dialog (such as exiting the dialog). As with other scripting languages like Javascript, you as the programmer write event handlers that override the default behavior the dialog box has. This isn’t necessary, but we want to control how this dialog behaves so we’ll point the script toward our own set of event handlers.
That’s all our main function does. At this point, our “Holiday” dialog loads. Let’s take a look at our event handlers. Our _load function will execute first:
int holiday_load() {
ControlChangeFont(100, "Mono");
EditSetText(100, "\r\r\r Press Start ");
}
It’s pretty simple. We change our dialog font using the ControlChangeFont SDK function (note that the ControlChangeFont function is only available in version 4.15b of GoFiler Complete and later). We also set some text using the EditSetText SDK function, targeting our static control via its ID. We instruct our users to press the Start button to get our animation going.
int holiday_action(int id, int action) {
if (id == 101) {
pos = 0;
DialogSetTimer(75, 1);
}
if (id == 102) {
DialogKillTimer(1);
EditSetText(100, "\r\r\r Press Start ");
}
return ERROR_NONE;
}
Our main event handler is the _action function, which is executed when the user interacts with the dialog box through either of its two button controls. This function receives two parameters: the id, which is the ID of the control that fired the event, and the action, which is the submessage. In this case, we want to know which button the user pressed and execute accordingly. The id will match the ID of the control as specified in our dialog template, so we can check to see if the Start or Stop button was pressed. If it’s the Start button, we set our position to zero with the pos variable and begin our timer using the DialogSetTimer SDK function. This function sets a timer within the dialog, which we create here with a 75 ms interval. Every tick of the timer fires an event that can be handled by our _timer function.
int holiday_timer(int id) {
string s1;
if (pos >= size) { pos = 0; }
while ((pos < size) && (frames[pos] != "")) {
s1 += frames[pos] + "\r\n";
pos++;
}
pos++;
EditSetText(100, s1);
return ERROR_NONE;
}
The _timer function is what actually performs our “animation”. First, we check to see if our current position within the array (pos) is greater than the array size. Again, these are global variables, so they maintain their value outside our event handler functions. If we’ve finished our animation by moving in the array to the last line of text, we reset to the beginning. Then, using a while loop, we concatenate our lines together to make a frame. We go through the array, line by line, until we encounter the end of a frame, which is denoted by an empty string. Then we again use the SDK function EditSetText to set our static frame, with the control ID of 100, to contain the value of our string.
Our timer runs indefinitely until the user presses the Stop button. Back in our _action event handler, we execute the code in the if block matching our Stop button’s ID.
if (id == 102) {
DialogKillTimer(1);
EditSetText(100, "\r\r\r Press Start ");
}
Once the user presses Stop, we end our timer with the DialogKillTimer SDK function and reset our static frame to the instructions with which we began.
So there you have it, a way to animate ASCII text in a custom dialog within Legato using timers, strings, and event handlers. I hope this introduction to dialogs and events is helpful, and have a frightful Halloween!
Download the ascii art.txt!
Maggie Gardner joined Novaworks in the summer of 2016 but has been working with Legato since its release in 2015. She has over ten years experience programming in SAS, PHP and C++. |
Additional Resources
Novaworks’ Legato Resources
Legato Script Developers LinkedIn Group
Primer: An Introduction to Legato