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
// SPDX-FileCopyrightText: Copyright (c) 2026 Mike Li/Mikewolfli/Wei Li(mikewolfli@163.com)
// SPDX-License-Identifier: MIT
//! Shared dialog chrome: label resolution, the modal scrim, and the reveal geometry.
//!
//! Kept out of `mod.rs` (principle #8). Six dialogs build an action row from `tr!` sites and
//! every modal dims the page behind it, so these bodies live in one file rather than being
//! repeated — which is how six of them came to differ in the first place.
use crate;
use crateRenderContext;
/// The built-in English labels for the dialog catalogue keys.
///
/// # Why a table in addition to the catalogue
///
/// The catalogue is the translation resource table: when it is initialised it wins, and the
/// embedded English file is loaded on first use. A build without the `i18n` capability has no
/// catalogue at all, so this table is the resource table for that build — the labels live in
/// one place instead of being restated at each `localized_label` call site, which is how two
/// dialogs came to spell "OK" differently.
const DIALOG_RESOURCE_FALLBACKS: & = &;
/// The English label this module ships for `key`, if it has one.
/// Resolves a dialog label for **display**: the catalogue's translation when it has one, the
/// built-in resource label for the key, and the English label otherwise.
///
/// # Why the key is not an acceptable fallback
///
/// `translate_key` returns the key itself when no catalogue is initialised (or the key is
/// absent). Drawing that value is how a message box's OK button came to read `common....`: the
/// row reserved 80px for "OK", and the raw key `common.button.ok` was elided to fit. A dialog's
/// buttons are user-facing copy, so the fallback is the English label the enum already carries —
/// never the catalogue key.
///
/// # Why this is one function rather than each dialog's own check
///
/// Six dialogs build their action row from `tr!` sites, and a per-site `== key` comparison is a
/// rule that has to be repeated exactly. One helper means a new dialog inherits the fallback, and
/// a catalogue that is initialised later still resolves correctly because the lookup happens when
/// the label is drawn.
pub
/// The focus targets of a dialog, in tab order.
///
/// # Why a shared iterator and not a per-dialog loop
///
/// Six self-drawn dialogs move a focus ring across their own controls with the same rule:
/// Tab advances, Shift+Tab retreats, and the first/last target wraps. Spelling that rule in
/// each `handle_event` is how the dialogs came to disagree about whether Tab moves forward or
/// whether it wraps at all (issue DLG-10), so the chain and the step live here once.
pub
/// The focus index `forward` steps from `current` within a chain of `count` targets.
///
/// Returns `None` when there is nothing to focus. A `current` that is out of range is treated
/// as "before the first" when moving forward and "after the last" when moving back, so a chain
/// that shrank (a hidden button) cannot leave the ring on an index that no longer exists.
pub
/// Paints the modal scrim over the control's whole rectangle.
///
/// # Why this is one function and not six copies
///
/// Every modal dialog in this module dims the page it sits on, and six of them were silently
/// missing it: `Dialog` and `MessageBox` were fixed first, then the scan showed
/// `color_dialog` / `file_dialog` / `font_dialog` / `input_dialog` / `progress_dialog` carried a
/// `modal` flag with nothing painted for it. Six copies of the same six lines is how six of them
/// came to diverge in the first place, so the body lives here once.
///
/// # Why the `Scrim` role rather than a blend
///
/// BLUE21 B23 recorded that blending toward the ink **lightens** a dark page — a scrim that
/// brightens what it covers is the defect stated in `Colors::scrim`'s own documentation. The
/// role lets a dark theme dim deliberately while a light theme darkens, and the literal survives
/// only as the fallback for a theme that predates the role.
pub
/// Paints the modal scrim at `strength` of its resolved opacity, for a backdrop that fades in.
///
/// Scaling the **alpha** rather than blending toward the page is the one form that is right for
/// both a dark scrim (which must get darker as it arrives) and a light one (which must get lighter):
/// a blend toward the window fill would move a scrim in the wrong direction in one of the two
/// appearances. `strength` is clamped, and the whole plane is skipped at zero so a hidden dialog
/// leaves the page untouched.
pub
/// How small a revealing modal is at the instant it appears: 0.92 of its settled size.
///
/// A dialog that grows from nothing reads as a pop rather than an arrival, and one that grows from
/// far away reads as a zoom unrelated to a dialog's purpose. A few percent is the "settle" every
/// platform uses; the value is deliberately *not* zero, so the first visible frame already shows the
/// dialog's shape. Shared by every modal family so a dialog and a message box arrive alike — two
/// nearby literals is exactly how the eight dialogs acquired four title-bar heights (rule #101).
pub const REVEAL_MIN_SCALE: f32 = 0.92;
/// Scales `rect` about its own centre, rounding outward so a shrinking frame never loses a pixel
/// column to truncation while it is still meant to be visible.
///
/// The frame is grown from its centre rather than from a corner because a dialog that grows from a
/// corner reads as a panel sliding in, which is a different affordance (that is what `bottom_sheet`
/// and `navigation_drawer` do). Kept as a free function so a draw path has one expression for the
/// geometry and a test can compose the same rect without duplicating the arithmetic.
pub