Reflex gives you two ways to show a loading spinner:
AddFloatAcquireOverlayPick by whether the load actually stops the user from interacting.
AddFloat or AddInline the spinner into a container.AcquireOverlay for blocking and full-bleed coverage, with the spinner drawn by the overlay's style or floated in as a child.GLX::AddFloat is the simple way to place an object on top of others:
GLX::AddFloat(m_panel, m_spinner, GLX::kAlignmentCenter);
kAlignment values from kAlignmentTopLeft to kAlignmentBottomRight.Whatever the float covers sits behind it and cannot be accessed, but input everywhere else stays live. That makes it right for a passive indicator. The spinner shows work is continuing while the user keeps using the rest of the UI. Remove it with m_spinner.Detach() when the work completes.
If you want the spinner to take its own place in the layout instead of overlapping anything, use GLX::AddInline(parent, m_spinner); the child then participates in the flow like any sibling.
If the user must not interact at all during the load, a float is not enough. GLX::AcquireOverlay mounts an overlay object over a parent, covering it completely, children included.
It takes six arguments, and the style name is the fifth:
GLX::AcquireOverlay(m_content, "Loading", true, true, "BusyOverlay",
[this](GLX::Object & overlay)
{
// build the spinner into the overlay, covered below
});
The six arguments, in order.
m_content is the parent the overlay stretches over."Loading" is the key that identifies the overlay so you can dismiss it later."BusyOverlay" names the style the overlay is drawn with.Calling AcquireOverlay again with the same key while the overlay is already up is safe and does nothing new, so you can call it from a per-frame update without guarding.
Pass all six arguments. The overload with five compiles fine but drops the style name, and the overlay renders unstyled. If your spinner shows up invisible, count your arguments first.
When the load finishes, dismiss it with the same parent and key pair:
GLX::DiscardOverlay(m_content, "Loading");
DiscardOverlay returns whether an overlay was actually removed, and calling it when nothing is mounted is harmless. Always pair every acquire with a discard on every exit path, or the overlay stays up forever.
Reflex has no built-in spinner widget. The standard pattern is a style whose arc length is bound to a property, driven from C++ by an animation clock. In your stylesheet:
BusyOverlay:
{
progress:
Circle(width: 8; color: 50,58,62,32),
Circle(width: 8; sweep: &value; color: 50,58,62; round_cap: true),
Rotate(angle: 0.5; content: Circle(width: 8; sweep: &value; color: 50,58,62; round_cap: true));
bg_colour: 235,238,240,160;
bg:
Align
(
position: center;
size: 128;
content: Render(pad: 16; density: 8; content: Align(content: progress))
);
};
progress is a named slot holding three layers. A faint full circle is the track (the same grey at alpha 32), a solid arc has its sweep bound to the object's value property, and a copy of that arc rotated half a turn makes the spinner read as two arcs chasing each other. The translucent light grey bg_colour frosts the covered content, and Align centres a 128 pixel render of the slot inside the overlay.
On the C++ side, hold a heap-allocated property as a member and initialise it in the constructor's init list:
Reference<Data::Float32Property> m_progress;
// in the constructor init list:
m_progress(New<Data::Float32Property>())
Then wire it up inside the AcquireOverlay init lambda:
GLX::AcquireOverlay(m_content, "Loading", true, true, "BusyOverlay",
[this](GLX::Object & overlay)
{
m_progress->value = 0.0f;
GLX::AttachAnimationClock(overlay, "clock", [this, &overlay](Float delta)
{
m_progress->value = Modulo(m_progress->value + (delta * 0.5f), 1.0f);
overlay.Realign();
});
overlay.SetProperty(GLX::kvalue, m_progress);
});
Three details matter here. delta is seconds since the last frame, and GLX angles are turns rather than radians, so delta * 0.5f with a Modulo wrap spins the arc half a turn per second. overlay.Realign() is what triggers the repaint; without it the property changes but the spinner never redraws. And SetProperty(GLX::kvalue, m_progress) is the binding that lets &value in the stylesheet read your property.
There is no cleanup step. The clock is attached to the overlay object, so DiscardOverlay tears both down together.
The style route keeps the whole visual in the stylesheet, but the init lambda receives the overlay object, so you can also AddFloat a ready-made spinner child into it there instead.
AddFloat and the rest of the layout family.