This solution works right now. It does the job, it does it well. But it is not a good solution. Dear ImGui's layouting capabilities are rather lackluster right now and they will definitely change in the future. So use this if you must, but don't let this creep into your mind as the right thing to do!
As a default, Dear ImGui aligns all its widgets towards the top left, adding a newline with most added widgets. Sometimes however, you want one or multiple widgets to be centered, right-aligned, or on the bottom right of a window. This is what we'll cover in this part of the series.
Aligning things in Dear ImGui can be done in a multitude of ways. The ones you should and will use are:
- SameLine - Positions a widget to the right of the previous widget
- SetCursorPos (and its affiliates) - Positions a widget at an arbitrary position
Often we'll also need to know how much space we're working with, or where we currently are. For this the following functions can be useful:
- GetCursorPos - Obtains the position for where the next widget would be drawn.
- GetContentRegionAvail - Obtains the amount of space we have left in the window, accounting for window-padding and the already used space.
- GetContentRegionMax - Obtains the content size of the current window.
- CalcItemSize - Calculates the size of a specific item, useful to use align widgets.
- CalcTextSize - Calculates the size of a specific piece of text, useful to align text
The general idea is that we can tell Dear ImGui where it should draw a widget using SetCursorPos*
.
Finding that position requires a bit of maths, but once you have that it's trivial.
Let's demonstrate how to centre a button:
if(ImGui::Begin("Alignment Tutorial")) {
ImVec2 button_size = ImVec2{150, 0};
// obtain width of window
float width = ImGui::GetWindowSize().x;
// figure out where we need to move the button to. It's good if you understand why this formula works!
float centre_position_for_button = (width - button_size.x) / 2;
// tell Dear ImGui to render the button at the current y pos, but with the new x pos
ImGui::SetCursorPosX(centre_position_for_button);
ImGui::Button("Hello!", button_size);
}
ImGui::End();
This will result in:
Note that we use GetWindowSize
. This step is crucial to make sure that the button will stay centred even if the
window is resized! Using hardcoded numbers will not achieve that effect.
This demonstrates how to centre the button horizontally. You can use the same logic to centre it vertically:
if(ImGui::Begin("Alignment Tutorial 2")) {
// note that 0 tells Dear ImGui "just use the default"
ImVec2 button_size = ImGui::CalcItemSize(ImVec2{150, 0}, 0.0f, 0.0f);
// obtain size of window
ImVec2 avail = ImGui::GetWindowSize();
// calculate centre of window for button. I recommend trying to figure out why this works!
ImVec2 centre_position_for_button{
(avail.x - button_size.x) / 2,
(avail.y - button_size.y) / 2
};
// tell Dear ImGui to render the button at the new pos
ImGui::SetCursorPos(centre_position_for_button);
ImGui::Button("Hello!", button_size);
}
ImGui::End();
Which results in something like this:
What if you wanted to have multiple widgets in one line? It's tedious to always calculate the y
coordinate.
Fret not! SameLine
comes to the rescue. It automatically positions a widget to the right of the previous widget, taking spacing into account:
if(ImGui::Begin("Alignment Tutorial 3")) {
// note that 0 tells Dear ImGui "just use the default"
ImVec2 button_size = ImGui::CalcItemSize(ImVec2{150, 0}, 0.0f, 0.0f);
// obtain size of window
ImVec2 avail = ImGui::GetWindowSize();
// calculate centre of window for button. I recommend trying to figure out why this works!
ImVec2 centre_position_for_button{
// we have two buttons, so twice the size - and we need to account for the spacing in the middle
(avail.x - button_size.x * 2 - ImGui::GetStyle().ItemSpacing.x) / 2,
(avail.y - button_size.y) / 2
};
// tell Dear ImGui to render the button at the new pos
ImGui::SetCursorPos(centre_position_for_button);
ImGui::Button("Hello", button_size);
// tell Dear ImGui to put the new widget directly to the right of the previous one
ImGui::SameLine();
ImGui::Button("World!", button_size);
}
ImGui::End();
Which will result in:
To hammer down this knowledge, try to recreate the following things:
- Have a button where the user can use the arrow keys to change its alignment. All the way from top left, to bottom right
- Recreate the button laoyut of a classic save dialog box
ImGui::CalcItemSize(ImVec2{150, 0}, 0.0f, 0.0f);
This function can't use now!
// [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth(). // Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical. // Note that only CalcItemWidth() is publicly exposed. // The 4.0f here may be changed to match CalcItemWidth() and/or BeginChild() (right now we have a mismatch which is harmless but undesirable)