Alignment & Anchors
Place things relative to the window or to each other — and have that placement survive a resize. Retained anchors are live constraints, not one-time calculations.
Two Ways to Place Something
Malena has two ways to place things relative to something else, and the difference matters more than it first appears.
ml::Align | Retained anchors | |
|---|---|---|
| What it is | Free functions that compute a position | Live constraints stored on the object |
| When it runs | Once, when you call it | Every time the window changes |
| Survives a resize | No | Yes |
| Use for | One-off placement, non-component objects | Layout that must stay correct |
Align::centerOn and found your layout wrong after the user resized the window, retained anchors are the answer. They were added precisely because computing a position once is not the same as meaning it.Anchoring to the Window
Anchors are declared on the object and re-solved whenever the window changes size.
title.centerXInWindow(); // stays centred through any resize
title.anchorTopInWindow(24.f); // 24px below the top edge, always
settingsBtn.anchorRightInWindow(16.f);
settingsBtn.anchorBottomInWindow(16.f);
| Method | Effect |
|---|---|
centerInWindow() | Centre on both axes. |
centerXInWindow(offset) | Centre horizontally, plus an offset in pixels. |
centerYInWindow(offset) | Centre vertically, plus an offset. |
anchorLeftInWindow(margin) | Pin the left edge, margin px inward. |
anchorRightInWindow(margin) | Pin the right edge. |
anchorTopInWindow(margin) | Pin the top edge. |
anchorBottomInWindow(margin) | Pin the bottom edge. |
unanchored() | Drop every anchor, freezing the object where it currently sits. |
Anchors compose. Pinning right and bottom puts an object in the corner and keeps it there.
setPosition Detaches
There is one rule to internalise, and it explains most surprises:
setPosition detaches the object's anchors. Manual control wins. If you place something by hand, Malena stops moving it for you.panel.centerInWindow(); // anchored
panel.setPosition({10.f, 10.f}); // anchor dropped — panel now stays at 10,10
panel.centerInWindow(); // re-anchor it if you want that back
This is what makes dragging work. A user drags a panel, Draggable calls setPosition, the anchor detaches, and the panel stays where they put it — including after the next resize. You get that for free.
To freeze an object deliberately without moving it, call unanchored():
tooltip.centerInWindow();
tooltip.unanchored(); // keeps its current place, but stops tracking the window
ml::Align for One-off Placement
ml::Align is the one-shot form. It computes a position now and does not remember anything, which makes it right for objects that are not Malena components at all — a raw sf::Text, an sf::Sprite, anything with bounds.
ml::Align::centerOn(panel, label); // centre label on panel
ml::Align::centerHorizontally(panel, label); // one axis only
ml::Align::centerVertically(panel, label);
The reference can be a component, an sf::FloatRect, or a bare size:
ml::Align::centerOn(sf::FloatRect({0,0}, {800,600}), sprite);
ml::Align::centerOn(sf::Vector2f{800.f, 600.f}, sprite); // size anchored at the origin
Align::centerText exists for the common case of centring an sf::Text on an object, which needs the text's local bounds rather than its global ones to look right.Placing Objects Against Each Other
Objects can also be placed relative to each other, which is how rows and stacks are usually built.
nameField.setBelow(nameLabel, 6.f); // 6px under the label
submitBtn.setBelow(nameField, 16.f);
cancelBtn.setRightOf(submitBtn, 8.f);
These are retained too, so a chain of them re-solves in order when the window changes. Move the first element and everything downstream follows.
Grid or Panel when you are laying out many children in a regular structure — relative helpers are for the handful of relationships a layout container cannot express naturally.