Numeric Values

PixelUI keeps numeric state, editing transactions, presentation, and drawing separate. The shared layer uses int32_t values, fixed-capacity storage, and integer arithmetic.

Range and normalization

Create a NumericRange through its validating factory:

1
2
3
4
NumericRange range;
if (!NumericRange::tryCreate(-40, 120, 5, range)) {
// minimum > maximum or step <= 0
}

clamp(), canIncrement(), canDecrement(), incremented(), and decremented() are saturating. A final step that does not divide the range exactly lands on the boundary. normalizeToExtent(range, value, pixels) maps a clamped value to an integer pixel extent with nearest-integer rounding. These operations are safe across the full INT32_MIN to INT32_MAX range.

tryCreate() returns false for minimum > maximum or step <= 0 and leaves the output object unchanged. Check that result before retaining or passing the range.

Formatting

NumericFormatter formats the value passed to it; it does not own or read application state. Built-in policies cover integers, zero padding, scaled integers, suffixes, and percentages:

1
2
3
4
5
6
7
8
IntegerFormat padded{3, nullptr};
NumericFormatter digits = NumericFormatter::integer(padded); // -7 -> "-007"

ScaledIntegerFormat volts{10, 1, 1, " V"};
NumericFormatter voltage = NumericFormatter::scaled(volts); // -7 -> "-0.7 V"

PercentageFormat percentage{&range, "%"};
NumericFormatter percent = NumericFormatter::percentage(percentage);

Formatting is allocation-free. format() returns false when the formatter is invalid or the destination is too small; a non-empty destination is reset to an empty string on failure. Use FixedBufferWriter::appendInteger() when composing text such as current/total instead of introducing a separate conversion routine.

The formatter stores only a function pointer and a non-owning context pointer. Every referenced format object, suffix string, percentage range, or custom context must outlive all calls through that formatter. A local formatter is safe for immediate formatting; a formatter retained by a Widget or queued Popup must refer to static storage or persistent App members.

Declare retained dependencies before their formatter so reverse member destruction keeps them alive:

1
2
3
4
NumericRange temperatureRange_{};
ScaledIntegerFormat temperatureFormat_{10, 1, 1, " C"};
NumericFormatter temperatureFormatter_ =
NumericFormatter::scaled(temperatureFormat_);

Binding and edit session

ValueEditorBinding is only a non-owning read/write/notification boundary. ValueEditSession owns the transaction state: its original value, draft value, and policy.

1
2
3
4
5
6
7
static void changed(void* context, int32_t value) {
static_cast<Settings*>(context)->brightness = value;
}

ValueEditorBinding binding =
ValueEditorBinding::reference(brightness_, &changed, this);
ValueEditSession edit(binding, ValueEditPolicy::CommitOnConfirm);

With CommitOnConfirm, draft changes remain inside the session until commit() writes and notifies; cancel() discards the draft. With Live, each successful setDraftValue() writes and notifies immediately; cancel() restores the original value and notifies that restoration. A session constructed from an initial integer has no external binding but follows the same draft/commit/cancel path.

Operation CommitOnConfirm Live
setDraftValue(value) Update only the draft Write, notify, then update the draft
commit() Write and notify if changed; make draft the new original Make the current draft the new original
cancel() Discard the draft Restore and notify the original value if changed

All three operations return false when the session is invalid or a required write fails. A failed write does not advance the transaction state; the caller should keep the editor open or restore its visible controls from draftValue().

Binding value and changed contexts are non-owning and must outlive the session and every operation performed through it. A Popup request that copies the binding extends this requirement through both its pending and active lifetime.

Component responsibilities

  • NumScroll uses NumericRange for stepping and NumericFormatter for labels. Use setRange(min, max, step) or setRange(range), and setFormatter(formatter). Zero padding is a formatter policy; setFixedIntDigits() no longer exists.
  • PopupProgress maps LEFT/RIGHT through range.step(), normalizes only the bar geometry, and delegates displayed text to the formatter.
  • PopupValueDigits uses a ValueEditSession; construction does not modify the external value. SELECT on a digit only finishes that digit’s local edit, allowing other digits to be changed. The OK button commits the whole session; CANCEL or BACK cancels it.
avatar
Link0327
喵🐱me0w, but furry wolf. 尝试变得毛茸茸
Link's Github
最新文章
网站信息
文章数目 :
2
本站访客数 :
本站总浏览量 :
最后更新时间 :