Progress Bar Architecture

This page documents the internal architecture of the Progress Bar system. Use this guide when you need to:

  • Create custom progress bar visuals (not rectangular or radial)
  • Implement custom animation behaviors
  • Understand how the animation system works
  • Build progress bars for non-UI Toolkit systems

Overview

The Progress Bar system uses a separation of concerns architecture:

Core Classes

ProgressBarData

Pure data container holding all state and configuration. UI-agnostic—can be used with any rendering system.

csharp
public class ProgressBarData { // Animation settings public bool InstantPositive { get; set; } public bool InstantNegative { get; set; } public long UpdateFrequency { get; set; } public float Step { get; set; } public long FirstDelay { get; set; } public long SecondDelay { get; set; } // Progress segments public ProgressBarNode[] Segments { get; set; } public ProgressBarNode Indicator { get; set; } // Indicator appearance public bool IndicatorAutoColor { get; set; } public Color IndicatorColorPositive { get; set; } public Color IndicatorColorNegative { get; set; } }

ProgressBarNode

Represents a single progress segment with current/target values and color.

csharp
public class ProgressBarNode { public float CurrentValue { get; set; } // 0-1, actual displayed value public float TargetValue { get; set; } // 0-1, value animating toward public Color Color { get; set; } // Silent setters (no events) public void SetCurrentValueSilent(float value); public void SetTargetValueSilent(float value); }

ProgressBarController<TElement>

Generic animation orchestrator that works with any UI element type. Handles:

  • Animation timing and scheduling
  • Coordinating progress vs indicator animations
  • Determining which bar moves first based on positive/negative changes
csharp
public class ProgressBarController<TElement> : IDisposable { public ProgressBarController( TElement element, Func<TElement, ProgressBarData> dataGetter, IProgressBarAnimator animator, IProgressBarNodeUpdater nodeUpdater, IProgressBarVisualUpdater<TElement> visualUpdater ); public void PlayProgress(); public void PlayIndicator(); public void Dispose(); }

Interfaces

IProgressBarAnimator

Controls animation scheduling. Default implementation uses IVisualElementScheduledItem.

csharp
public interface IProgressBarAnimator { void Start(long delayMs, long intervalMs, Action<TimerState> callback); void Stop(); }

IProgressBarNodeUpdater

Updates a single node's CurrentValue toward TargetValue.

csharp
public interface IProgressBarNodeUpdater { /// <summary> /// Updates node's CurrentValue toward TargetValue. /// Returns true when animation is complete. /// </summary> bool Update(ProgressBarNode node, float step); }

IProgressBarVisualUpdater<TElement>

Applies data changes to the actual UI element.

csharp
public interface IProgressBarVisualUpdater<TElement> { void UpdateSegmentVisual(TElement element, int index, ProgressBarNode node); void UpdateIndicatorVisual(TElement element, ProgressBarNode indicator, bool isPositive); }

Creating Custom Progress Bars

Step 1: Create Your Visual Element

csharp
public class MyCustomProgressBar : VisualElement { // Your custom rendering logic private ProgressBarData _data; private ProgressBarController<MyCustomProgressBar> _controller; public MyCustomProgressBar() { _data = new ProgressBarData { Segments = new[] { new ProgressBarNode() }, Indicator = new ProgressBarNode() }; } }

Step 2: Implement Visual Updater

csharp
public class MyCustomVisualUpdater : IProgressBarVisualUpdater<MyCustomProgressBar> { public void UpdateSegmentVisual(MyCustomProgressBar element, int index, ProgressBarNode node) { // Your custom rendering logic // e.g., update shader parameters, move sprites, etc. } public void UpdateIndicatorVisual(MyCustomProgressBar element, ProgressBarNode indicator, bool isPositive) { // Update indicator appearance } }

Step 3: Wire Up Controller

csharp
public class MyCustomProgressBar : VisualElement { public MyCustomProgressBar() { _data = new ProgressBarData { /* ... */ }; _controller = new ProgressBarController<MyCustomProgressBar>( this, el => el._data, new ProgressBarScheduledAnimator(this), new DefaultProgressBarNodeUpdater(), new MyCustomVisualUpdater() ); } public void PlayProgress() => _controller.PlayProgress(); public void PlayIndicator() => _controller.PlayIndicator(); }

Animation Flow

When PlayProgress() is called:

Built-in Implementations

ProgressBar (Rectangular)

  • Uses child VisualElements for segments
  • Animates width style property
  • Supports horizontal and vertical orientations
  • Includes overlay image support

RadialProgressBar (Circular)

  • Uses MeshGenerationContext for custom arc rendering
  • Draws arcs using line strips
  • Supports multi-segment mode (Overwatch-style)
  • Each segment rendered as consecutive arc

File Structure

ProgressBar/ ├── Core/ │ ├── ProgressBarData.cs # Pure data container │ ├── ProgressBarNode.cs # Single segment data │ └── ProgressBarController.cs # Animation orchestration ├── Interfaces/ │ ├── IProgressBarAnimator.cs # Animation scheduling │ ├── IProgressBarNodeUpdater.cs # Value interpolation │ └── IProgressBarVisualUpdater.cs # Visual rendering └── UIToolkit/ ├── ProgressBar.cs # Rectangular implementation ├── RadialProgressBar.cs # Circular implementation ├── Animators/ │ └── ProgressBarScheduledAnimator.cs ├── NodeUpdaters/ │ └── DefaultProgressBarNodeUpdater.cs └── VisualUpdaters/ ├── ProgressBarVisualUpdater.cs └── RadialProgressBarVisualUpdater.cs

Best Practices

Do

  • ✅ Use ProgressBarData for all state management
  • ✅ Implement IDisposable pattern in controllers
  • ✅ Set Instant* to true during initialization, then false
  • ✅ Call PlayProgress() and PlayIndicator() after changing values

Don't

  • ❌ Modify CurrentValue directly—use TargetValue
  • ❌ Forget to call RebuildSegments() after changing segment count
  • ❌ Mix animation calls without waiting for completion

Settings

Theme

Light

Contrast

Material

Dark

Dim

Material Dark

System

Sidebar(Light & Contrast only)

Light
Dark

Font Family

DM Sans

Wix

Inclusive Sans

AR One Sans

Direction

LTR
RTL