Layouts

Simple bind functions

In the basic widget functions I showed how you normally use the setPosition and setSize function. But they can do way more than just take constants.

There are a lot of bind functions in tgui to bind to the position, size or a combination. The best way to exlain is with an example.

button1->setSize(50, 10);
button2->setSize(tgui::bindSize(button1));

button1->setSize(100, 20);
assert(button2->getSize() == sf::Vector2f{100, 20});

Note that when binding the Gui object instead of a widget, its view is used as size, not the size of the window.

The following bind functions exist for position and size: bindLeft, bindTop, bindRight, bindBottom, bindPosition, bindWidth, bindHeight and bindSize.

Getting rid of the namespace

Below I will continue without using "tgui::" in front of all the function calls. When the expressions become more complex you might feel like the whole line is cluttered with the namespace. One solution would be to use "using namespace tgui;" but this is a bad programming practice. This is why I created the TGUI_IMPORT_LAYOUT_BIND_FUNCTIONS define. Adding this somewhere will import all the bind functions while keeping everything else in the tgui namespace.

Just put it somewhere on top of your file or function:

TGUI_IMPORT_LAYOUT_BIND_FUNCTIONS
button2->setPosition(bindPosition(button1));

Binding limits and conditions

Oh look, more bind functions! There are also bind functions to combine those simple bind functions that were mentioned above. They are called bindMin, bindMax and bindIf.

The bindMin and bindMax functions are self explaining, you give them two layouts as parameter and the lowest or highest one will be used. Remember that when any of the bound widgets changes the entire expression will be recalculated so which one is the minimum or maximum will be automatically re-evaluated on changes.

button2->setSize(bindMin(bindWidth(button1), bindHeight(button1)), bindMax(bindWidth(button1).x, bindHeight(button1)));

Finally there is the bindIf function which can use any bound condition to determine which layout to use. The parameters are in the same order as the operands of the ?: operator: first the condition, then the true value and finally the false value. The following code will keep a square image centered in the window while filling the window and keeping the square ratio.

auto width = bindWidth(gui);
auto height = bindHeight(gui);
pic->setSize(bindIf(width > height, {height, height}, {width, width}));
pic->setPosition(bindIf(width > height, {(width - bindWidth(pic)) / 2, 0}, {0, (height - bindHeight(pic)) / 2}));

As you can see in the example above, you can also use operators between layouts. You can use +, -, *, /, %, ==, !=, <, >, <=, >=, && and ||. Basically any operator that c++ allows to overload.

Layouts in a string

Layouts can actually be created without access to the widget that you want to bind. Instead of using the bind functions you can also pass a string.

button->setSize("0.6 * parent.width", "0.2 * parent.height");
button->setSize("{0.6 * parent.width, 0.2 * parent.height}");

Every widget has the following properties available: size, width, height, pos, left, top, right and bottom. You can access the parent widget by adding "parent." in front of it. You can also access sibling widgets directly by their name (which was passed to the add function when adding them to the parent). And you can even nest the parent access.

button2->setSize("button1.size");
button2->setPosition("parent.parent.parent.MyButton.position");

There are also the min and max functions. The min and max are even more powerful than the c++ version as they can take an arbitrary amount of parameters.

widget->setPosition("min(a.x, b.x, c.x, d.x)", "max(a.y, b.y)");

Finally there is the if-then-else statement. Let's use it in the example with the picture:

pic->setSize("if parent.width > parent.height then {parent.height, parent.height} else {parent.width, parent.width}");
pic->setPosition("if parent.width > parent.height then {(parent.width - width) / 2, 0} else {0, (parent.height - height) / 2}");

Those lines are just too long, let's make the final version a bit shorter. Parent can be written as an & and for left, top, width and height we can use x, y, w and h. And then there is also the ?: operator.

pic->setSize("&.w > &.h ? {&.h, &.h} : {&.w, &.w}");
pic->setPosition("&.w > &.h ? {(&.w - w) / 2, 0} : {0, (&.h - h) / 2}");

That's it. It won't get any shorter than that, unless you start dropping whitespace.

Final warning

Layouts are not normal objects, they should only be used as temporary objects. You can store them to not repeat an entire expression on multiple places, but you should best not change an existing layout. If you do have to change them, just make sure that the assigned layout does not appear on the right side of the assignment as well!

Layout x = 5;
x = x + 1; // Mathematically incorrect => may crash
Layout y = x + 1; // No problem