Skip to content

Layouts in User Interfaces

This article will help you understand how to customize the layouts of your interfaces in SplashKit. Written by Sean Boettger and others on October 2024

In this article we’ll see how we can create layouts for our SplashKit interfaces! This will allow us to automatically position our UI elements in rows and columns, hide them under drop downs, and place them on draggable panels, which will help us create really detailed and dynamic interfaces.

The last few sections of this article will be closer to an API reference than the previous one - rather than complete examples, there will be mostly snippets of code you can try out to explore each concept!

Part 1: Creating panels

All the automatic layouting functionality in SplashKit starts by creating a container, and the most useful container is a ‘panel’. A panel is like a mini window inside your program, that can be dragged around and resized. We’ll be placing our elements inside these panels.

Here’s just a basic example from the previous tutorial that we can get started from:

#include "splashkit.h"
int main()
{
// open a window
open_window("My Interface!", 800, 600);
// main loop
while (!quit_requested())
{
// get user events
process_events();
// clear screen
clear_screen(COLOR_WHITE);
// ...we'll put our interface code here...!
// finally draw interface, then refresh screen
draw_interface();
refresh_screen();
}
// close all open windows
close_all_windows();
return 0;
}

Let’s add a panel now! Adding a panel takes two lines of code:

  1. First we begin the panel with Start Panel, which takes a name for the panel, and a rectangle for its initial position. For example start_panel("My panel", rectangle_from(50,50,200,100)) creates a panel with the title “My panel”, that appears close to the top-left corner (50,50), and a width and height of 200 by 100.

    After this we can place all the UI elements that should go on the panel, just like we were!

  2. Then once we’re done, we need to end the panel with End Panel, where we just give it the same name (so for example end_panel("My panel")).

One really important thing to note here is that Start Panel returns whether the panel is visible or not, so we need to make sure to check this as well, and only create elements and end the panel if the panel is open, like so:

// start the panel and check if it's open/visible
if (start_panel("My panel", rectangle_from(50,50,200,100)))
{
// ...here we'll create all the UI elements, inside the braces { ... }
// end the panel
end_panel("My panel");
}

Just testing the code above will give us this! We can already drag around and resize the panel, so let’s start adding elements!

A draggable panel inside the main window.

Part 2: Adding UI elements

Adding elements is very similar to what we were doing last tutorial - we just don’t need to specify the position anymore! For example previously we added a button with button("My Button!", rectangle_from(300, 260, 200, 24)) - now we can just use button("My Button!")!

This’ll give us a button like this:

A panel with a button in it.

Similarly, we can add a Text Box by declaring a string variable outside the main loop, and then calling my_string = text_box(my_string):

A panel with a button and a text box in it.

Here’s the full code recreating what we had last tutorial, while creating a panel and adding the elements inside it.

#include "splashkit.h"
int main()
{
// open a window
open_window("My Interface!", 800, 600);
// setup a variable to store the user's string - same as last tutorial
string user_message = "Default message";
// main loop
while (!quit_requested())
{
// get user events
process_events();
// clear screen
clear_screen(COLOR_WHITE);
// here we start the panel
if (start_panel("My panel", rectangle_from(50,50,200,100)))
{
// create the elements, just like last tutorial!
if (button("Write To Terminal!"))
{
write_line(user_message);
}
user_message = text_box(user_message);
// end the panel
end_panel("My panel");
}
// finally draw interface, then refresh screen
draw_interface();
refresh_screen();
}
// close all open windows
close_all_windows();
return 0;
}

Another useful thing you can do now is add labels to your elements. You can do this by passing an additional string to each element function at the start - as an example, text_box(my_string) can become text_box("Message", my_string), which will give us this:

A panel with a textbox that has a label to the left of it.

Part 3: Layout elements (Reference)

Now that we can create panels and place UI elements on those panels, let’s see how we can use extra elements to help lay them out!

This section will focus on mainly demonstrating each concept.

Make sure to try each of these snippets out - you can just copy paste them into anywhere between your start/end panel calls.

Some of the easiest ways to group our elements together, is to place them under headers and inside ‘insets’.

Headers

Headers are really easy to add - we can use the Header function, which takes a name for the heading, and returns if the header is open (like the panels!):

if (header("Edit Message!"))
{
user_message = text_box(user_message);
}

A panel with a hidden section - clicking a dropdown that says 'Edit Message!' shows the textbox from before.

Note: headers do not need to be began/ended, so they’re nice and easy to use!

Insets

Insets let you group a bunch of elements inside a fixed-size region! They can be created using Start Inset, which takes a name, and a height. After creating your elements, you can then call End Inset with the same name to finish it.

Here’s a code example - an inset is started, and six buttons are created inside it. Finally, the inset is ended.

paragraph("This text is outside the inset area!");
start_inset("Inset area", 60);
if (button("Button 1!"))
write_line("Button 1 clicked!");
if (button("Button 2!"))
write_line("Button 2 clicked!");
if (button("Button 3!"))
write_line("Button 3 clicked!");
if (button("Button 4!"))
write_line("Button 4 clicked!");
if (button("Button 5!"))
write_line("Button 5 clicked!");
if (button("Button 6!"))
write_line("Button 6 clicked!");
end_inset("Inset area");

A panel with an inset region that is at a fixed position - it has its own scrollbar.

Another powerful feature of insets, is that you can use them as a container directly on the main window too!

Tree nodes

Tree nodes allow you to organize your elements as a tree - like a file system! You can collapse and expand the nodes individually, so this is a great way to organize data.

Here’s a code example - various nested tree nodes are started and ended with, buttons placed in between - this creates a hierarchy.

if (start_treenode("Some buttons!"))
{
if (button("Button 1!"))
write_line("Button 1 clicked!");
if (button("Button 2!"))
write_line("Button 2 clicked!");
if (button("Button 3!"))
write_line("Button 3 clicked!");
end_treenode("Some buttons!");
}
if (start_treenode("Some more buttons!"))
{
if (button("Button 4!"))
write_line("Button 4 clicked!");
if (button("Button 5!"))
write_line("Button 5 clicked!");
if (start_treenode("Buttons in buttons!!")){
if (button("Button 6!")) write_line("Button 6 clicked!");
end_treenode("Buttons in buttons!!");
}
end_treenode("Some more buttons!");
}

A panel with tree nodes, that can be expanded/contracted.

Part 4: Advanced layouts (Reference)

Rows/Columns

The elements placed in panels are automatically placed in rows - by default one element per row. We can create columns ourselves, allowing us to group our elements together on single rows!

To start, we need to call Start Custom Layout - this removes the default layouting. Now we can use the following functions to adjust how many elements we can place on the row:

Finally, to return to the default layout, just call Reset Layout.

You can also adjust the height of a row, using Set Layout Height.

Here’s an example, where we create six buttons - three on each row, with the top row being taller.

start_custom_layout();
split_into_columns(3);
set_layout_height(64);
if (button("Button 1!"))
write_line("Button 1 clicked!");
if (button("Button 2!"))
write_line("Button 2 clicked!");
if (button("Button 3!"))
write_line("Button 3 clicked!");
set_layout_height(0); // 0 resets to default
if (button("Button 4!"))
write_line("Button 4 clicked!");
if (button("Button 5!"))
write_line("Button 5 clicked!");
if (button("Button 6!"))
write_line("Button 6 clicked!");
reset_layout();

A panel with 6 buttons. Each set of three are on a single row, and the top row is taller than the bottom.

Entering Columns

A really powerful feature of columns, is that we can enter a column, and add elements to it directly without automatically moving to the next one. This allows us to create much more complex layouts.

To enter a column, simple use Enter Column - now elements will be placed inside the current column.

Once you’re done, you can leave it with Leave Column.

Here’s an example:

start_custom_layout();
split_into_columns(2);
enter_column();
set_layout_height(64);
if (button("Button 1!"))
write_line("Button 1 clicked!");
if (button("Button 2!"))
write_line("Button 2 clicked!");
leave_column();
enter_column();
set_layout_height(0); // 0 resets to default
if (button("Button 4!"))
write_line("Button 4 clicked!");
if (button("Button 5!"))
write_line("Button 5 clicked!");
if (button("Button 6!"))
write_line("Button 6 clicked!");
leave_column();
reset_layout();

A panel with 5 buttons, split into two columns. The first two are stacked on the left, and are tall. The last three are stacked on the right.

Take note how Button 1 and Button 2 are placed in the same column, rather than row now. This is really powerful - you can now split the column again, adjust heights, and so on, to lay things out exactly how you want.

Wrap up

Hopefully you now understand how to use SplashKit’s layouting functionality! This can be a little tricky to understand, so as always make sure to play around with the code and try things out!

Good luck using it in your projects! In the next tutorial, we’ll see how we can style interfaces, to make them match our projects.