ValueComparison is a reusable utility for showing changes between two values in UI (old -> new), with optional increase/decrease coloring.

It is designed to be domain-agnostic: inventory, RPG stats, settings diffs, save slot comparisons, and more.

Types

ValueDeltaType

Represents the direction of change:

  • NEUTRAL
  • INCREASE
  • DECREASE

ValueComparisonLine

Data model for one comparison row.

csharp
public struct ValueComparisonLine { public string Key; public string Name; public Sprite Icon; public string OldValue; public string NewValue; public ValueDeltaType DeltaType; public bool HideOldValue; }

Use GetDeltaType(float value, float comparisonValue) to compute the delta state for numeric comparisons.

ValueComparisonLineController

UI Toolkit controller that renders a single ValueComparisonLine.

Features:

  • optional name and icon
  • old/new value presentation
  • arrow between values
  • delta-based color formatting for new value, themeable (see Theming the change colours)
  • optional tooltip support through TooltipController

Basic Usage

The controller builds its own row elements in C# — all your UXML needs is a host element to parent it under. Assign this UXML to the PanelRenderer's Source Asset:

xml
<ui:UXML xmlns:ui="UnityEngine.UIElements"> <ui:VisualElement> <ui:VisualElement name="ComparisonHost" /> </ui:VisualElement> </ui:UXML>

The generated row uses Luna utility classes internally (flex-row, items-center, size-36, the icon-arrow-right arrow), so the panel's Panel Settings must point at the Luna theme — see Theme Stack.

csharp
using CupkekGames.Luna; using UnityEngine; using UnityEngine.UIElements; public class ValueComparisonExample : MonoBehaviour { [SerializeField] private PanelRenderer _panelRenderer; private ValueComparisonLineController _controller; private void Awake() { if (_panelRenderer == null) { _panelRenderer = GetComponent<PanelRenderer>(); } if (_panelRenderer != null) { _panelRenderer.RegisterUIReloadCallback(OnUIReload); } } // PanelRenderer delivers the visual tree asynchronously — // query elements in the reload callback, not in Awake/Start. private void OnUIReload(PanelRenderer renderer, VisualElement root, int version) { if (_controller != null) return; // sentinel: only init once VisualElement host = root.Q<VisualElement>("ComparisonHost"); float oldValue = 12f; float newValue = 17f; ValueDeltaType delta = ValueComparisonLine.GetDeltaType(newValue, oldValue); var line = new ValueComparisonLine( key: "Damage", name: "Damage", icon: null, oldValue: oldValue.ToString(), newValue: newValue.ToString(), deltaType: delta, hideOldValue: false ); _controller = new ValueComparisonLineController(host, withName: true, withIcon: false); _controller.SetData(line); } private void OnDestroy() { if (_panelRenderer != null) { _panelRenderer.UnregisterUIReloadCallback(OnUIReload); } } }

Theming the change colours

The new value of an increase carries value-comparison--up (ValueComparisonLineController.UpUssClassName), of a decrease value-comparison--down (DownUssClassName), and a neutral value neither. The colour a change is drawn in comes from --value-comparison-color on that label. The main theme (LibColorsText.uss) sets the shipped lime (#a3e635) and red (#ea5252); without it the controller draws the same two colours.

The controller writes the colour into the text as a rich-text <color> tag, as it always has, so it keeps beating any color a sheet gives the label (a row's text colour, a tooltip's label colour). Set the variable, not color, and set it on the label's class: a variable set on an ancestor does not reach it.

css
/* Darker shades for a light card, where the lime would wash out. */ .light-card .value-comparison--up { --value-comparison-color: #2f7a3a; } .light-card .value-comparison--down { --value-comparison-color: #b42f2f; }

The classes take any other property too (a heavier font for a change, say).

Migration Notes

If you previously used item-specific stat line utilities:

  • ItemStatChangeType -> ValueDeltaType
  • ItemStatLine -> ValueComparisonLine
  • ItemStatLineController -> ValueComparisonLineController

ItemStatDataController still lives in Inventory because it depends on ItemStatData, but now uses these generic value-comparison utilities internally.

See also

  • Tooltip — the TooltipController the optional per-line tooltips run through
  • Inventory view — equipment stat diffs, the pattern's flagship consumer
  • Theme Stack — the theme wiring the generated rows depend on

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