Skip to content

ProgressRing

Header: winui/ui/status/ProgressRing.hpp. Spec: winui/ui/status/ProgressRing.hpp.

Circular progress, by default an indeterminate spinner.

Indeterminate (the default), a three-quarter arc rotates continuously. Determinate, an accent arc sweeps clockwise from the top over the fraction of [min(), max()] that value() has reached. The ring is 32 DIPs across by default (at least 16) with a 4 DIP stroke, and draws no track unless a background() is set. set_active(false) hides it entirely. It takes no input and has no disabled state. ui::ProgressRing is the declarative form.

Example:

ui::Property<bool> working{true};

window.set_content(ui::HorizontalStack{
    .spacing  = 12,
    .content  = {
        ui::ProgressRing{.active = working, .width = 32.0f},
        ui::Button{.text = "Stop", .on_click = [&] { working = false; }},
    },
});

// A determinate gauge with a visible track:
auto ring = std::make_unique<ui::ProgressRingControl>(/*value=*/40.0);
ring->set_indeterminate(false);
ring->set_background(ui::Color::from_argb(0x33808080));
ring->set_value(75.0);

ui::ProgressRing

The circular counterpart of ui::ProgressBar. Builds a ui::ProgressRingControl.

Example:

ui::Property<bool> working{false};

window.set_content(ui::HorizontalStack{
    .spacing  = 8,
    .content  = {
        ui::ProgressRing{.active = working, .width = 32.0f, .height = 32.0f},
        ui::ProgressRing{.value = 70.0, .width = 32.0f, .height = 32.0f},
        ui::ToggleSwitch{.header = "Working"},
    },
});

Every field, in the order a designated initializer names them:

ui::ProgressRing{
    .value, .min, .max, .active, .background, .style, .margin, .width, .height, .min_width,
    .min_height, .max_width, .max_height, .horizontal_alignment, .vertical_alignment, .visible,
    .tool_tip, .accessibility, .on_value_changed, .on_pointer_entered, .on_pointer_exited,
    .on_got_focus, .on_lost_focus, .on_key_down, .on_wheel, .control
}

Fields, in declaration order (designators must follow it):

Field Type Default
value ui::Binding<std::optional<double>> Value, clamped to [min, max]: the arc. Unset (the default) is the indeterminate spinner, for work whose length is unknown.
min double 0.0 The value with no arc. Fixed at build time.
max double 100.0 The value drawing the full circle. Fixed at build time.
active ui::Binding<bool> {true} False draws nothing but keeps the ring's layout size.
background ui::Binding<ui::Paint> A full track ring under the arc: a fixed color or a theme resource, which follows theme switches. Unset draws no track.
on_value_changed ui::Callback<void(double)> The new (clamped) value, whenever a bound value moves it.
on_pointer_entered ui::Action The pointer moved onto the control.
on_pointer_exited ui::Action The pointer left the control.
on_got_focus ui::Action Keyboard focus arrived on the control, or on anything inside it, from outside it. A control with nothing focusable in it never gets focus, and never raises this.
on_lost_focus ui::Action Keyboard focus left the control and everything inside it.
on_key_down ui::Callback<ui::Handling(const ui::KeyEvent&)> A key pressed while focus is on the control or inside it, before the control's own handling; ui::Handled stops it there (Element::on_key_down()).
on_wheel ui::Callback<ui::Handling(const ui::PointerEvent&)> A wheel turn over the control, before the control scrolls; ui::Handled stops it there (Element::on_wheel()).
control ui::ControlOut<ui::ProgressRing> Out: the handle that receives the built control.

Plus the common fields style, margin, width, height, min_width, min_height, max_width, max_height, horizontal_alignment, vertical_alignment, visible, tool_tip, accessibility every spec carries (conventions).

build

std::unique_ptr<Element> build()&&

Consumes the spec and returns the built ui::ProgressRingControl; the caller owns it.

ui::ProgressRingControl

Derives from ui::Element, ui::RangeValueProvider.

Circular progress, by default an indeterminate spinner.

Indeterminate (the default), a three-quarter arc rotates continuously. Determinate, an accent arc sweeps clockwise from the top over the fraction of [min(), max()] that value() has reached. The ring is 32 DIPs across by default (at least 16) with a 4 DIP stroke, and draws no track unless a background() is set. set_active(false) hides it entirely. It takes no input and has no disabled state. ui::ProgressRing is the declarative form.

Example:

ui::Property<bool> working{true};

window.set_content(ui::HorizontalStack{
    .spacing  = 12,
    .content  = {
        ui::ProgressRing{.active = working, .width = 32.0f},
        ui::Button{.text = "Stop", .on_click = [&] { working = false; }},
    },
});

// A determinate gauge with a visible track:
auto ring = std::make_unique<ui::ProgressRingControl>(/*value=*/40.0);
ring->set_indeterminate(false);
ring->set_background(ui::Color::from_argb(0x33808080));
ring->set_value(75.0);

ProgressRingControl

ProgressRingControl(double value = 0.0, double min = 0.0, double max = 100.0)

An active, indeterminate ring; max below min is raised to min and value is clamped into the range. Sets a 16x16 DIP minimum size.

min

double min() const

The value with no arc; 0 by default.

max

double max() const

The value drawing the full circle; 100 by default.

set_range

void set_range(double min, double max)

Sets both ends (max below min becomes min) and re-clamps value(), firing on_value_changed() if that moves it. Always marks the ring for redraw.

value

double value() const

The value, always within [min(), max()]; 0 by default. Drawn only while determinate.

set_value

void set_value(double value)

Clamps into the range. Does nothing, and fires no callback, when the clamped value is unchanged. Otherwise redraws and fires on_value_changed().

is_indeterminate

bool is_indeterminate() const

Whether it spins endlessly instead of showing a share; true by default.

set_indeterminate

void set_indeterminate(bool indeterminate)

Turning it on restarts the rotation from the top. Marks the ring for redraw.

is_active

bool is_active() const

Whether the ring shows; true by default. An inactive ring draws nothing but keeps its layout size.

set_active

void set_active(bool active)

Shows or hides the ring. Redraws only.

background

const std::optional<Color>& background() const

The fixed fill of the track circle. nullopt (the default) draws no track, unless a theme resource is set.

background_resource

ThemeColor background_resource() const

The track fill as a theme resource, or nullptr.

set_background

void set_background(std::optional<Color> background)

Sets the fixed track fill and clears a theme-resource one. Marks the ring for redraw when the color changes.

set_background

void set_background(ThemeColor resource)

Sets the track fill to a theme resource, re-read under every theme, and clears a fixed one.

on_value_changed

void on_value_changed(Callback<void(double)> handler)

Fired with the new, clamped value whenever value() changes. Replaces any previous handler.

parallel_measure_safe

bool parallel_measure_safe() const override

True: measuring reads nothing shared.

automation_role

AutomationRole automation_role() const override

ProgressBar, with a range value that UI Automation can read but not set.

measure_override

Size measure_override(Size available, const MeasureContext& context) const override

32 x 32 DIPs, capped at a bounded available size.

render_override

void render_override(Renderer& renderer, const RenderContext& context) const override

Draws the optional background circle, then the arc as one stroked path with round ends.

tick_override

void tick_override(Duration delta) override

Advances the indeterminate arc's rotation (a 1.5 s turn) and redraws while active and indeterminate. Ignored otherwise, and while the arc is animated without redrawing.

Tests

  • tests/test_progress_ring.cpp