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
//! **What a window's width buys it**, and the three columns a window is made
//! of (bl-e5d2, bl-dfda).
//!
//! This is the layout's own policy and the one place it is stated. It is a
//! **pure function of one number**, so what the window does as it narrows is a
//! value a test reads back rather than a layout somebody has to look at — and
//! the harness that photographs the window judges every width it renders,
//! because there is no width this answers nothing for.
//!
//! # Two shapes, and the second is the answer the policy used to lack
//!
//! [`widths`] is the yield: the conversation keeps a floor and the two list
//! panes give way to it, together and in proportion to what each is worth,
//! until they reach their own floor. Past that **nothing yields**, and until
//! bl-dfda that was where the policy stopped — at a phone-shaped viewport the
//! three columns were still laid side by side, each about 120 points wide, with
//! every line in every one of them wrapped to two or three words.
//!
//! The answer is not more yielding, because there is none left to do: it is a
//! second **shape**. Below the width at which the yield still leaves the
//! conversation its floor, the window shows [`Column`] — one column at a time,
//! with a bar naming the three. That is the covering-pane idiom this seat
//! already has ([`crate::ui::enroll`], [`crate::ui::tuning`],
//! [`crate::ui::records`]) read across the whole layout rather than only the
//! central panel: a surface you navigate to, act in, and come back from.
//!
//! # There is no floor under the narrow shape, and that is not an omission
//!
//! A width policy needs a floor exactly where two things compete for one
//! window, and in the narrow shape nothing competes: the shown column has the
//! whole width. So there is no width at which this runs out of an answer, which
//! is what lets the picture-taking harness drop its *is this width promised*
//! question entirely rather than keep a gate that now says yes to everything.
//! What a very small window costs is elision inside the content, which is the
//! content's own business and every pane's own rule.
use cratePane;
/// **What the two list panes are worth when the window is wide enough**, in
/// points: the roster holds a handful of short words, the conversation list
/// holds a headline and a preview under it.
pub const ROSTER: f32 = 280.0;
/// The conversation list's own worth, on the same reading.
pub const CONVS: f32 = 320.0;
/// **The floor the conversation and its composer keep** in the broad shape.
/// Below this a chat pane is a strip: a message elides inside its own width,
/// the composer's box shows the first few words of a draft, and `send` sits
/// against the frame. It is what the two list panes yield to, and the width at
/// which it can no longer be kept is where the narrow shape begins.
pub const CHAT_FLOOR: f32 = 420.0;
/// **The width a list pane never goes under** while it is on the glass beside
/// another. A pane below it shows nothing at all, which is worse than a chat
/// pane under its floor — so this is the one thing the floor yields to.
pub const SIDE_FLOOR: f32 = 140.0;
/// **One of the three columns the window is made of.**
///
/// In the broad shape all three are on the glass at once and this says nothing;
/// in the narrow shape it is the one that IS on the glass, and the bar that
/// names all three is how an operator moves between them
/// (`crate::ui::shell`). It is held on the model because it is a navigation an
/// operator performed, which is not a thing any other fact can be asked for.
/// **What a window of this width gets**: every column at once, or one at a
/// time.
/// **The two list panes' widths at a given window width** — the yield, and the
/// policy the window had none of (bl-e5d2).
///
/// The side panels used to keep their widths as the window narrowed and the
/// central panel absorbed the whole loss, so at 900 points the pane the window
/// exists for was a ~140-point strip while the roster kept 280. The rule is the
/// other way round: **the conversation has a floor and the list panes yield to
/// it**, together and in proportion to what each is worth, until they reach
/// their own floor.
/// **The shape a window of this width takes**, and the whole of the decision.
///
/// The line between the two is [`widths`] itself rather than a second constant:
/// the broad shape holds exactly as long as the yield still leaves the
/// conversation [`CHAT_FLOOR`], and the first width where it cannot is the
/// first width one column at a time is the better answer. A number written here
/// would be a copy of a policy that already lives in one function, and the two
/// would part company on the first tuning of either.