-
Notifications
You must be signed in to change notification settings - Fork 0
TurtleTools
Turtle Tools builds on turtle and turtleText to create pre-built UI elements which would be tedious to implement manually. The library is developed on-demand in that whenever I need some kind of interface element for one of my projects, I add it to turtleTools. The current list of elements include:
- Top ribbon (only one)
- Exit popup (only one)
- Buttons
- Switches
- Dials
- Sliders
- Scrollbars
- Contexts
- Dropdowns
- Textboxes
Unlike turtle and turtleText, turtleTools doesn't have to be explicitly initialised. Instead each of the different components must be individually initialised. Each element has different requirements for what it needs.
turtleTools also has its own update function which must be called before turtleUpdate(). turtleToolsUpdate() will update all of the buttons, switches, and textboxes on the screen every tick. You'll also need to get the mouse coordinates and clear the screen at the start of the loop for this module to work correctly. So the basic loop will look like this:
while (turtle.popupClose == 0) {
start = clock();
turtleGetMouseCoords(); // required for mouse functionality of turtleTools
turtleClear(); // clear the screen
...
turtleToolsUpdate(); // update turtleTools
turtleUpdate(); // update the screen
end = clock();
while ((double) (end - start) / CLOCKS_PER_SEC < (1.0 / tps)) {
end = clock();
}
}See the turtle.c included example for more details on how turtleTools is used.
turtleTools has themes! (write later)
The default colors of an element is defined by the turtleTools theme. It will change if the theme is changed.
To change the colors of an element, override the color indices in the element's color field. A table of default colors can be found in turtleTools.c:
int32_t tt_color_default[] = {
/* button switch dial slider scrollbar context dropdown textbox */
TT_COLOR_TEXT_ALTERNATE, TT_COLOR_TEXT_BASE, TT_COLOR_TEXT_BASE, TT_COLOR_TEXT_BASE, 0, TT_COLOR_TEXT_ALTERNATE, TT_COLOR_TEXT_BASE, TT_COLOR_TEXT_ALTERNATE,
TT_COLOR_COMPONENT, TT_COLOR_TEXT_ALTERNATE, TT_COLOR_BACKGROUND_COMPLEMENT, TT_COLOR_COMPONENT_BASE, TT_COLOR_COMPONENT_BASE, TT_COLOR_COMPONENT_BASE, TT_COLOR_TEXT_ALTERNATE, TT_COLOR_COMPONENT_BASE,
TT_COLOR_COMPONENT_HIGHLIGHT, TT_COLOR_COMPONENT_BASE, TT_COLOR_BACKGROUND_BASE, TT_COLOR_BACKGROUND_ALTERNATE, TT_COLOR_COMPONENT_COMPLEMENT, TT_COLOR_COMPONENT_HIGHLIGHT, TT_COLOR_COMPONENT_BASE, TT_COLOR_TEXT_HIGHLIGHT,
TT_COLOR_TEXT_BASE, TT_COLOR_COMPONENT_HIGHLIGHT, 0, 0, TT_COLOR_BACKGROUND_ALTERNATE, 0, TT_COLOR_COMPONENT_HIGHLIGHT, TT_COLOR_TEXT_ALTERNATE,
TT_COLOR_COMPONENT_COMPLEMENT, TT_COLOR_BACKGROUND_ALTERNATE, 0, 0, TT_COLOR_BACKGROUND_HIGHLIGHT, 0, TT_COLOR_COMPONENT_HIGHLIGHT, TT_COLOR_BLUE,
0, TT_COLOR_TERTIARY_BASE, 0, 0, 0, 0, TT_COLOR_TEXT_ALTERNATE, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
};FINISH THIS SECTION
The top ribbon is this set of dropdowns that are common in editors.
To activate this element, use ribbonInit():
ribbonInit("config/ribbonConfig.txt");It requires a configuration file which looks like this:
File, New, Save, Save As..., Open
Edit, Undo, Redo, Cut, Copy, Paste
View, Change Theme, GLFW
Each row corresponds to a column in the ribbon, with name of the column being the first item of the row. There is only support for one dropdown layer (for example, some applications let you do File -> Preferences -> Key Settings. turtleTools ribbon does not support that extra sideways layer). If the ribbon configuration file is not found or NULL is passed in to ribbonInit(), then a default ribbon configuration will be used which is identical to the above example.
In order to detect within the program when the user selects one of these options, there is an example function written in turtle.c called parseRibbonOutput:
void parseRibbonOutput() {
if (tt_ribbon.output[0] == 0) {
return;
}
tt_ribbon.output[0] = 0;
if (tt_ribbon.output[1] == 0) { // File
if (tt_ribbon.output[2] == 1) { // New
printf("New\n");
}
if (tt_ribbon.output[2] == 2) { // Save
if (strcmp(osToolsFileDialog.selectedFilename, "null") == 0) {
if (osToolsFileDialogPrompt(1, "") != -1) {
printf("Saved to: %s\n", osToolsFileDialog.selectedFilename);
}
} else {
printf("Saved to: %s\n", osToolsFileDialog.selectedFilename);
}
}
if (tt_ribbon.output[2] == 3) { // Save As...
if (osToolsFileDialogPrompt(1, "") != -1) {
printf("Saved to: %s\n", osToolsFileDialog.selectedFilename);
}
}
if (tt_ribbon.output[2] == 4) { // Open
if (osToolsFileDialogPrompt(0, "") != -1) {
printf("Loaded data from: %s\n", osToolsFileDialog.selectedFilename);
}
}
}
if (tt_ribbon.output[1] == 1) { // Edit
if (tt_ribbon.output[2] == 1) { // Undo
printf("Undo\n");
}
if (tt_ribbon.output[2] == 2) { // Redo
printf("Redo\n");
}
if (tt_ribbon.output[2] == 3) { // Cut
osToolsClipboardSetText("test123");
printf("Cut \"test123\" to clipboard!\n");
}
if (tt_ribbon.output[2] == 4) { // Copy
osToolsClipboardSetText("test345");
printf("Copied \"test345\" to clipboard!\n");
}
if (tt_ribbon.output[2] == 5) { // Paste
osToolsClipboardGetText();
printf("Pasted \"%s\" from clipboard!\n", osToolsClipboard.text);
}
}
if (tt_ribbon.output[1] == 2) { // View
if (tt_ribbon.output[2] == 1) { // Change theme
printf("Change theme\n");
if (tt_theme == TT_THEME_DARK) {
turtleBgColor(36, 30, 32);
turtleToolsSetTheme(TT_THEME_COLT);
} else if (tt_theme == TT_THEME_COLT) {
turtleBgColor(212, 201, 190);
turtleToolsSetTheme(TT_THEME_NAVY);
} else if (tt_theme == TT_THEME_NAVY) {
turtleBgColor(255, 255, 255);
turtleToolsSetTheme(TT_THEME_LIGHT);
} else if (tt_theme == TT_THEME_LIGHT) {
turtleBgColor(30, 30, 30);
turtleToolsSetTheme(TT_THEME_DARK);
}
}
if (tt_ribbon.output[2] == 2) { // GLFW
printf("GLFW settings\n");
}
}
}Use this code as a template to run arbitrary segments of code when the user clicks on different ribbon options.
If the configuration file is not found, the program will use a default ribbon configuration which is identical to the ribbonConfig.txt example.
For more portable executables that don't rely on extra configuration files, the ribbon can also be initialised with a list from within the program
list_t *ribbonConfig = list_init();
list_append(ribbonConfig, (unitype) "File, New, Save, Save As..., Open", 's');
list_append(ribbonConfig, (unitype) "Edit, Undo, Redo, Cut, Copy, Paste", 's');
list_append(ribbonConfig, (unitype) "View, Change Theme, GLFW", 's');
ribbonInitList(ribbonConfig);The list must contain only strings, each item in the list substitutes a line in a configuration file.
The turtleTools popup element is not a general purpose popup (those can be easily constructed using existing turtleTools elements), but is instead a special popup that automatically activates when the user presses the [X] to close the window. The popup also requires a configuration file to initialise.
popupInit("config/popupConfig.txt");popupConfig.txt:
Are you sure you want to close?
Cancel
Close
You can have an arbitrary number of options - I typically use three with this configuration for editors that have saving features:
Warning: Unsaved changes
Save
Cancel
Close
Like the ribbon, the popup has a parsing function to call arbitrary code when the options are selected. See turtle.c for the example.
void parsePopupOutput(GLFWwindow *window) {
if (tt_popup.output[0] == 0) {
return;
}
tt_popup.output[0] = 0; // untoggle
if (tt_popup.output[1] == 0) { // cancel
turtle.close = 0;
glfwSetWindowShouldClose(window, 0);
}
if (tt_popup.output[1] == 1) { // close
turtle.popupClose = 1;
}
}One more thing to note: when creating your main loop, using while (turtle.close) will break out of the loop when the [X] is clicked. If you want the popup to intercept this break, use while (turtle.popupClose) and then bind one of your popup buttons to toggle turtle.popupClose to 1, like in the example above.
If the configuration file is not found, the program will use a default popup configuration which is identical to the popupConfig.txt example.
For more portable executables that don't rely on extra configuration files, the popup can also be initialised with a list from within the program
list_t *popupConfig = list_init();
list_append(popupConfig, (unitype) "Are you sure you want to close?", 's');
list_append(popupConfig, (unitype) "Cancel", 's');
list_append(popupConfig, (unitype) "Close", 's');
popupInitList(popupConfig);The list must contain only strings, each item in the list substitutes a line in a configuration file.
The turtleTools button comes in four forms:
| Default | Rounded Rectangle | Circle | Text |
|---|---|---|---|
![]() |
The button is always center-aligned, and there is currently no way to change this.
Creating a button requires the following parameters:
- A label (string) (can be NULL for no label)
- A pointer to a variable of type
int8_t, which is used as the output of the button - X coordinate (center)
- Y coordinate (center)
- Size (in coordinates)
int8_t buttonVar = 0;
tt_button_t *button = buttonInit("button", &buttonVar, 150, 20, 10);To use the button, check the button variable every cycle in the main loop. The variable's value will be 1 when the button is being pressed and 0 otherwise. A typical application of a button is to run a function when it's clicked but not continuously if it's held down. The turtleTools button has a simple solution for both use cases of one-time presses and continuous functions. If you want to run a function only one time, simply use code like this:
if (buttonVar) {
printf("button clicked\n");
buttonVar = 0;
}If you want to run a function continuously as the button is pressed, use code like this:
if (buttonVar) {
yposition += 5;
}In general, writing to the buttonVar won't allow you to "phantom" click the button (the button won't change appearance if you set the buttonVar to 1 while the button isn't being clicked). Rather, the mechanism is that the variable is "pulled down" continuously if the button isn't clicked, and when you click the button it is "set high" and "left floating" - so it can be set back low if desired.
There are four distinct button shapes that any given button can morph between. Those shapes are:
typedef enum {
TT_BUTTON_SHAPE_RECTANGLE = 0,
TT_BUTTON_SHAPE_ROUNDED_RECTANGLE = 1,
TT_BUTTON_SHAPE_CIRCLE = 2,
TT_BUTTON_SHAPE_TEXT = 3,
} tt_button_shape_t;When you create a button, it defaults to TT_BUTTON_SHAPE_RECTANGLE. To change the button shape, set the shape field of the button to a new form:
int8_t buttonVar = 0;
tt_button_t *button = buttonInit("button", &buttonVar, 150, 20, 10);
button -> shape = TT_BUTTON_SHAPE_ROUNDED_RECTANGLE;You can also disable or hide the button without deleting it by setting the button's enabled field:
Disable the functionality of the button:
int8_t buttonVar = 0;
tt_button_t *button = buttonInit("button", &buttonVar, 150, 20, 10);
button -> enabled = TT_ELEMENT_NO_MOUSE;Hide the button:
int8_t buttonVar = 0;
tt_button_t *button = buttonInit("button", &buttonVar, 150, 20, 10);
button -> enabled = TT_ELEMENT_HIDE;To delete a button, use buttonFree():
int8_t buttonVar = 0;
tt_button_t *button = buttonInit("button", &buttonVar, 150, 20, 10);
buttonFree(button);The button's output can also be read from the button's status field. The button's status is 0 when it is idle, -1 if the mouse is hovering over the button, 2 for one tick when the button is pressed, and 1 when the button is held down.
int8_t buttonVar = 0;
tt_button_t *button = buttonInit("button", &buttonVar, 150, 20, 10);
if (button -> status == 0) {
// button is idle
}
if (button -> status == -1) {
// mouse is touching button
}
if (button -> status == 2) {
// button just got clicked
}
if (button -> status == 1) {
// button is being held down
}The turtleTools switch comes in four forms:
| Default | Sideswipe | Checkbox | Xbox |
|---|---|---|---|
![]() |
![]() |
Default switch is center-aligned. The other forms are left-aligned. There is not currently a way to change this.
Creating a switch requires the following parameters:
- A label (string) (can be NULL for no label)
- A pointer to a variable of type
int8_t, which is used as the output of the switch - X coordinate
- Y coordinate
- Size (in coordinates)
int8_t switchVar = 0;
tt_switch_t *switch = switchInit("Switch", &switchVar, 0, 30, 10);This will create a switch of the default style. To change the style to sideswipe, checkbox, or xbox, edit the switch's style field.
int8_t sideswipe = 0, checkbox = 1, xbox = 1;
tt_switch_t *switch_sideswipe = switchInit("Side Swipe", &sideswipe, 5, 15, 10);
tt_switch_t *switch_checkbox = switchInit("Checkbox", &checkbox, 0, 0, 10);
tt_switch_t *switch_xbox = switchInit("Xbox", &xbox, 0, -15, 10);
switch_sideswipe -> style = TT_SWITCH_STYLE_SIDESWIPE_LEFT;
switch_checkbox -> style = TT_SWITCH_STYLE_CHECKBOX;
switch_xbox -> style = TT_SWITCH_STYLE_XBOX;The value of the int8_t pointed to by the variable parameter determines whether the switch appears flipped on initialisation. A value of 0 will cause the switch to be initialised with the switch to the left, and a non-zero value will cause the switch to be initialised with the switch to the right.
When the switch is clicked, the variable toggles between 0 and 1, which correspond to left and right states of the switch. The variable can be read to determine the state of the switch. Additionally, the variable can be set to change the state of the switch.
Additionally, setting the value of the variable to 0 in the program by any means will toggle the switch to the left, and setting the value to a non-zero number will toggle the switch to the right.
There is only one style for the dial.
| Default |
|---|
![]() |
Creating a dial requires the following parameters:
- A label (string) (can be NULL for no label)
- A pointer to a variable of type
double, which is used as the output of the dial - A scaling scheme, either
TT_DIAL_LINEAR,TT_DIAL_LOG, orTT_DIAL_EXP - X coordinate (center)
- Y coordinate (center)
- Size (in coordinates)
- Lower bound (minimum value of the variable)
- Upper bound (maximum value of the variable)
- Render Factor (scales the output variable for the display of the value of the dial)
double dialVar = 0.0;
dialInit("Dial", &dialVar, TT_DIAL_LINEAR, 0, 0, 10, 0, 1000, 1);The dial reflects the value of the variable parameter. If you change the value through code, the dial will update to reflect the new value. The value of the variable is also changed by the user interacting with the dial itself.
The dial has a full 360 degree range, where the minimum and maximum positions are at the top of the dial. This cannot be changed currently.
The scaling scheme of the dial determines the change in the variable to dial theta function. Linear scaling has a constant theta to value ratio, exponential scaling has a lower theta to value near zero and a higher one near the max, and logarithmic scaling has a higher theta to value near zero and a lower one near the max.
The dial can also be right clicked to reset it to a default value which is the value of the variable when dialInit() was called.
Despite the dial's minimum and maximum values being of the double type, the dial will always round this variable to the nearest whole number, and will snap the dial's angle accordingly. If you want to avoid snapping, you can use the Render Factor parameter, which multiplies the value of the variable parameter before displaying it. The displayed number will always be a whole number though. You can disable the displayed number by setting the Render Factor parameter to 0, then if you want to you can write the text manually with a turtleTextWriteString() call:
/* this line of code renders the dial's variable (divided by 1000) with two decimal places */
turtleTextWriteStringf(dial -> x + dial -> size + 3, dial -> y, dial -> size / 2.5, 0, "%.2lf", *(dialp -> variable) / 1000);The slider comes in two main forms: horizontal and vertical
| Horizontal | Vertical |
|---|---|
![]() |
![]() |
Creating a slider requires the following parameters:
- A label (string) (can be NULL for no label)
- A pointer to a variable of type
double, which is used as the output of the slider - A slider type of either
TT_SLIDER_HORIZONTALorTT_SLIDER_VERTICAL - An alignment of either
TT_SLIDER_ALIGN_LEFT,TT_SLIDER_ALIGN_CENTER, orTT_SLIDER_ALIGN_RIGHT - X coordinate
- Y coordinate
- Size (in coordinates)
- Length (in coordinates)
- Lower bound (minimum value of the variable)
- Upper bound (maximum value of the variable)
- Render Factor (scales the output variable for the display of the value of the slider)
double sliderVar = 0.0;
sliderInit("Slider", &sliderVar, TT_SLIDER_HORIZONTAL, TT_SLIDER_ALIGN_CENTER, 0, 0, 10, 50, 0, 255, 1);The slider works very similarly to the dial. The value of the variable determines where along the length of the slider the dot resides. The slider also always rounds its variable to the nearest whole number and only displays whole numbers. Displaying of the numbers can be deactivated by setting the Render Factor parameter to 0.
The slider can also be right clicked to reset the value to the value given to it on initialisation.
The scrollbar can be though of as a special type of slider where the slider dot is replaced with a bar. The scrollbar variable also always ranges from 0 to 100.
| Horizontal | Vertical |
|---|---|
![]() |
![]() |
Creating a scrollbar requires the following parameters:
- A pointer to a variable of type
double, which is used as the output of the scrollbar - X coordinate
- Y coordinate
- Size (in coordinates)
- Length (in coordinates)
- Bar Percentage (a value between 0 and 100 which determines the percentage of the total length taken by the scrollbar bar)
double scrollbarVarY = 0.0;
scrollbarInit(&scrollbarVarY, TT_SCROLLBAR_VERTICAL, 310, 0, 10, 320, 33);There's probably some formula to determine the Bar Percentage for typical scrollbars, but I don't know what it is so it's just a user-set parameter.
By default the scrollbar doesn't do anything except change the value of the output variable. You are responsible for turning that value into a scrolling mechanic. The scrollbar also does not by default interact with the scroll wheel. See the turtle.c example for code on how to make a working scrolling mechanic with scrollbar and scroll wheel integration.
The context menu is a unique element. It works like a static standalone dropdown and it's typically used as a menu that pops up by right clicking a space or element.
Creating a context requires the following parameters:
- A list of options (a list of all strings)
- A pointer to a variable of type
int32_t, which is used as the output of the context - X coordinate
- Y coordinate
- size (in coordinates)
list_t *contextOptions = list_init();
list_append(contextOptions, (unitype) "Button", 's');
list_append(contextOptions, (unitype) "Switch", 's');
list_append(contextOptions, (unitype) "Dial", 's');
list_append(contextOptions, (unitype) "Slider", 's');
list_append(contextOptions, (unitype) "Scrollbar", 's');
list_append(contextOptions, (unitype) "Context", 's');
list_append(contextOptions, (unitype) "Dropdown", 's');
list_append(contextOptions, (unitype) "Textbox", 's');
int32_t contextVar = -1;
tt_context_t *context = contextInit(contextOptions, &contextVar, 0, 0, 10);
context -> enabled = TT_ELEMENT_HIDE;
...
// in main loop
if (turtleMouseRight()) {
if (keys[1] == 0) {
keys[1] = 1;
context -> enabled = TT_ELEMENT_ENABLED;
context -> x = turtle.mouseX;
context -> y = turtle.mouseY;
}
} else {
keys[1] = 0;
}
if (contextVar >= 0) {
if (contextVar == 0) {
// "Button" selected
}
if (contextVar == 0) {
// "Switch" selected
}
...
contextVar = -1;
}See turtle.c for an example of how a right click context is implemented.
The dropdown has only one style
| Default |
|---|
![]() |
Creating a dropdown requires the following parameters:
- A label (string) (can be NULL for no label)
- A list of options (a list of all strings)
- A pointer to a variable of type
int32_t, which is used as the output of the dropdown - An alignment of either
TT_DROPDOWN_ALIGN_LEFT,TT_DROPDOWN_ALIGN_CENTER, orTT_DROPDOWN_ALIGN_RIGHT - X coordinate
- Y coordinate
- Size (in coordinates)
list_t *sources = list_init();
int32_t sourceIndex = 0;
list_append(sources, (unitype) "None", 's');
list_append(sources, (unitype) "SP932", 's');
list_append(sources, (unitype) "SP932U", 's');
list_append(sources, (unitype) "SP928", 's');
list_append(sources, (unitype) "SP1203", 's');
list_append(sources, (unitype) "SP-1550M", 's');
dropdownInit("Source", sources, &sourceIndex, TT_DROPDOWN_ALIGN_LEFT, 0, 0, 10);The dropdown allows the user to pick between the strings in the list parameter. The output of the user's selection is an index of the list in the variable parameter. The variable parameter can also be changed by the code to change which item of the dropdown is displayed.
The textbox comes in one style
| Default |
|---|
![]() |
Creating a dropdown requires the following parameters:
- A label (string) (can be NULL for no label)
- A buffer for the text (can be NULL for automatic malloc), must exceed maximum number of characters
- A maximum number of characters allowed in the textbox
- X coordinate
- Y coordinate
- Size (in coordinates)
- Length (in coordinates)
tt_textbox_t *textbox = textboxInit("Textbox", NULL, 128, -50, -110, 10, 100);To access the text from the textbox, read from the character array textbox -> text, which is a standard null-terminated C string.
The textbox does not yet support highlighting, copy pasting, or cursor changing. I plan to add highlighting but the others are not planned as they would require osTools integration with turtleTools and I would like to keep those modules separate.









