1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
// SPDX-FileCopyrightText: Copyright (c) 2026 Mike Li/Mikewolfli/Wei Li(mikewolfli@163.com)
// SPDX-License-Identifier: MIT
//! Numeric helpers for widget value clamping.
//!
//! # Why this module exists
//!
//! Widgets that carry a value next to a `min`/`max` pair — sliders, sliders' arcs,
//! meters, scroll bars, steppers, spin boxes, dials, LCD numbers, progress bars,
//! range sliders — wrote `value.clamp(self.min, self.max)` at the point of use.
//! That is safe exactly as long as `min <= max` and neither is `NaN`, and
//! `f32::clamp` / `f64::clamp` **panic** rather than clamp when that does not hold:
//!
//! ```text
//! thread 'main' panicked at core/src/num/f32.rs:1566:9:
//! min > max, or either was NaN. min = 3.5, max = 1.0
//! ```
//!
//! The bounds are not constants — they are public setters (`set_min`,
//! `set_maximum`, …) and writable capability properties, so an ordinary sequence of
//! two documented calls reaches the panic:
//!
//! ```text
//! cupertino_slider: create (min=0.0, max=1.0)
//! set_property("min", 3.5) -> abort
//! ```
//!
//! That sequence is reachable from Rust, from declarative JSON, and from every
//! language binding through the C ABI. A GUI library must not abort its process
//! because a caller set two numbers in the wrong order.
//!
//! # The rule this encodes
//!
//! Clamping is defined against the **ordered** pair, not against the pair as
//! declared: `min` and `max` are treated as two bounds, and the smaller of them is
//! the floor. A caller that swaps them gets a value inside the range they named,
//! instead of a crash. `NaN` bounds are ignored rather than propagated, because a
//! `NaN` bound is not a range at all; when both bounds are `NaN` the value is
//! returned unchanged, which is the only non-arbitrary answer.
//!
//! This is deliberately *not* a silent repair of the widget's own state — the
//! setters remain free to normalise `min`/`max` themselves (several already do,
//! see `slider.rs`'s "adjusts maximum when crossed" tests). This module only
//! guarantees that the act of clamping cannot panic.
/// Clamps `value` into the range spanned by `a` and `b`, in either order.
///
/// Either bound may be `NaN`; a `NaN` bound is treated as absent. If both are
/// `NaN` there is no range to clamp to, so `value` is returned unchanged.
///
/// Never panics, which is the entire point: it replaces `value.clamp(min, max)`
/// at call sites where `min` and `max` are mutable fields rather than constants.
///
/// ```
/// # use rust_widgets::widget::numeric::ordered_clamp;
/// // Declared order.
/// assert_eq!(ordered_clamp(5.0, 0.0, 10.0), 5.0);
/// // Crossed bounds clamp instead of panicking.
/// assert_eq!(ordered_clamp(5.0, 10.0, 0.0), 5.0);
/// assert_eq!(ordered_clamp(-3.0, 10.0, 0.0), 0.0);
/// // A NaN bound is ignored.
/// assert_eq!(ordered_clamp(5.0, f32::NAN, 10.0), 5.0);
/// // No usable bounds at all: the value is returned as-is.
/// assert_eq!(ordered_clamp(5.0, f32::NAN, f32::NAN), 5.0);
/// ```
/// `f64` counterpart of [`ordered_clamp`], with identical semantics.
///
/// ```
/// # use rust_widgets::widget::numeric::ordered_clamp_f64;
/// assert_eq!(ordered_clamp_f64(5.0, 0.0, 10.0), 5.0);
/// assert_eq!(ordered_clamp_f64(5.0, 10.0, 0.0), 5.0);
/// assert_eq!(ordered_clamp_f64(5.0, f64::NAN, 10.0), 5.0);
/// assert_eq!(ordered_clamp_f64(5.0, f64::NAN, f64::NAN), 5.0);
/// ```
/// Clamps an integer into the range spanned by `a` and `b`, in either order.
///
/// Integer `clamp` panics on `min > max` for the same reason the float one does, and
/// integer widgets (`scroll_bar`, `progress_bar`, `stepper`, `spin_box`, `dial`)
/// carry the same two mutable bounds. Reordering is the whole fix; there is no `NaN`
/// case to consider.
///
/// ```
/// # use rust_widgets::widget::numeric::ordered_clamp_i32;
/// assert_eq!(ordered_clamp_i32(5, 0, 10), 5);
/// assert_eq!(ordered_clamp_i32(5, 10, 0), 5);
/// assert_eq!(ordered_clamp_i32(-1, 10, 0), 0);
/// ```
/// `i64` counterpart of [`ordered_clamp_i32`].
///
/// Needed because `InputDialog` carries its bounds as `i64` (so that
/// `i64::MIN`/`i64::MAX` remain usable as open bounds) and its `get_int`
/// constructor takes them as parameters — a caller-supplied pair that
/// `i64::clamp` panics on when crossed.
///
/// ```
/// # use rust_widgets::widget::numeric::ordered_clamp_i64;
/// assert_eq!(ordered_clamp_i64(5, 0, 10), 5);
/// assert_eq!(ordered_clamp_i64(5, 10, 0), 5);
/// assert_eq!(ordered_clamp_i64(-1, 10, 0), 0);
/// ```
/// `u32` counterpart of [`ordered_clamp_i32`].
///
/// ```
/// # use rust_widgets::widget::numeric::ordered_clamp_u32;
/// assert_eq!(ordered_clamp_u32(5, 0, 10), 5);
/// assert_eq!(ordered_clamp_u32(5, 10, 0), 5);
/// assert_eq!(ordered_clamp_u32(50, 10, 0), 10);
/// ```
/// `usize` counterpart of [`ordered_clamp_i32`], for index-valued bounds.
///
/// ```
/// # use rust_widgets::widget::numeric::ordered_clamp_usize;
/// assert_eq!(ordered_clamp_usize(5, 0, 10), 5);
/// assert_eq!(ordered_clamp_usize(5, 10, 0), 5);
/// ```