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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
//! The keyboard driver the §11 focus tests steer by: one persistent
//! `egui::Context` across frames, real key events in, and egui's own
//! `wants_keyboard_input()` out. Split from [`super::focus`] for §12's budget —
//! the driver is how a frame is run, the tests are what a frame must show.
use super::super::render;
use super::fixture::World;
use crate::cli_outbound::Cli;
/// **How a beat names a seat on the glass** — the paint-layer locator, split
/// from the driver at §12's budget on the seam the two already had: this file
/// is how a frame is *run*, `aim` is how a coordinate is found in one.
mod aim;
pub(super) use aim::{locate, rect_of, rects_of};
/// The window under test: one persistent `egui::Context`, so focus carries
/// frame to frame exactly as it does for the operator.
pub(super) struct Screen {
ctx: egui::Context,
lernie: Cli,
bl: Cli,
bz: Cli,
/// The window this driver paints into, when a beat needs a **particular**
/// one (bl-86a5). `None` is [`super::input`]'s 1600x2400, which is taller
/// than any list the suite can build — so a §11 rule 5 budget defect in a
/// column that has to divide itself is invisible at that size, and a
/// pointer beat about one has to say which window it means.
size: Option<(f32, f32)>,
}
impl Screen {
/// Binaries that deliberately do not exist: the send test below is the one
/// acceptance case that actually *dispatches*, and what it asserts is where
/// the keyboard ends up, not what `lernie` did. A name nothing resolves
/// fails the spawn at once, so the frame never forks the operator's real
/// substrate to prove a focus rule.
pub(super) fn new() -> Self {
Self {
ctx: egui::Context::default(),
lernie: Cli::new("/yog-absent-lernie"),
bl: Cli::new("/yog-absent-bl"),
bz: Cli::new("/yog-absent-bz"),
size: None,
}
}
/// The same driver on a window of a **named** size ([`Screen::size`]).
pub(super) fn sized(w: f32, h: f32) -> Self {
Self {
size: Some((w, h)),
..Self::new()
}
}
/// The same driver with a `lernie` that really answers — for the one
/// acceptance case whose gesture has to *succeed* end to end (the §3.4
/// raise, whose surface afterwards is what bl-9acf is about). `bl` and `bz`
/// stay absent: a bare rung mutates no ball and paints no login.
pub(super) fn with_lernie(lernie: Cli) -> Self {
Self {
lernie,
..Self::new()
}
}
/// Run one frame on `events` and report whether a text box holds the
/// keyboard afterwards.
pub(super) fn frame(&self, world: &mut World, events: Vec<egui::Event>) -> bool {
let _ = self.run(world, events);
self.ctx.wants_keyboard_input()
}
/// Run one idle frame and return every painted galley's text — the same
/// read [`super::painted`] makes, but on **this** screen's persistent
/// context, so what a box shows can be asserted after the keyboard has
/// typed into it.
pub(super) fn text(&self, world: &mut World) -> String {
let out = self.run(world, Vec::new());
crate::paint_probe::text_of(&out)
}
/// Run one frame and hand back everything it painted. A **pointer** test has
/// to find its coordinate before it can click one: a widget's rect is not
/// addressable from outside the frame that built it, but the galley it
/// painted is, and a key names the same seat far more stably than a number.
pub(super) fn shapes(
&self,
world: &mut World,
events: Vec<egui::Event>,
) -> Vec<egui::epaint::ClippedShape> {
self.output(world, events).shapes
}
/// One frame, whole — for a test that must read two things off the **same**
/// frame (what a run says and what hue it was painted in), which two calls
/// could not honestly give it.
pub(super) fn output(&self, world: &mut World, events: Vec<egui::Event>) -> egui::FullOutput {
self.run(world, events)
}
/// One frame of the real window, **with the wire it spoke on settled** —
/// the acts it fired *and* the reads it declared.
///
/// Since bl-1747 a gesture is posted rather than run (REMOTE §9.8), so its
/// frame-side aftermath — a draft clearing, a workspace adopted, a seed
/// spent, a dialog closing — lands on a *later* frame reading the receipt.
/// And the §8.1 start family is two acts: the second is posted only when
/// the first one's receipt arrives. A window pays that in ask periods; a
/// drive would have to pay it in counted frames, at every call site, which
/// is a fact about the transport leaking into every test that has nothing
/// to do with it. So it is paid here, once.
///
/// **bl-44e9 extended it to the read half, which is the same ruling one
/// door over.** A migrated surface paints an answer that landed a round
/// trip later, so a drive that only settled acts saw every such surface
/// blank — and a beat asserting a row is *there* would have been failing
/// for the transport's reason rather than the window's. The loop settles
/// both to a fixed point: `World::settle` answers what is outstanding and
/// says whether anything **moved**, which for reads is the standing set
/// changing (every call answers the whole set, so "I answered something"
/// would never go false). It terminates for the acts' own reason — only a
/// receipt can post an act, and nothing posts one unprompted — and for the
/// reads' equivalent: a surface's questions are a function of state, and a
/// frame that changed no state declares the set it declared before.
fn run(&self, world: &mut World, events: Vec<egui::Event>) -> egui::FullOutput {
// The engine's spawns are the ones a posted act runs (REMOTE §9.8), so
// this drive's fakes have to be the world's — a seat carries the
// gesture and never a binary.
world.substrate(&self.lernie, &self.bl);
let mut out = self.paint(world, events);
// The fixed point itself is `World::drain` (bl-13f9), one definition
// for both drivers; what is this driver's own is the context the
// repaints run on, so the loop takes the painting as a parameter.
world.drain(&mut |world| out = self.paint(world, Vec::new()));
out
}
/// One frame with the wire **left outstanding** — the gap between an act's
/// post and its receipt, which every other read here settles away.
///
/// That gap is where a whole class of defect lives and the only place it
/// can be driven from (bl-56c6): the composer is not disabled across it, so
/// what the operator types there is a draft like any other, and the fold
/// that runs when the receipt lands has to leave it alone. A drive that
/// settles to a fixed point per frame is never inside the gap and so can
/// assert nothing about it.
pub(super) fn unsettled(&self, world: &mut World, events: Vec<egui::Event>) -> bool {
world.substrate(&self.lernie, &self.bl);
let _ = self.paint(world, events);
self.ctx.wants_keyboard_input()
}
/// One frame, and nothing else.
fn paint(&self, world: &mut World, events: Vec<egui::Event>) -> egui::FullOutput {
let input = egui::RawInput {
// The held modifiers ride `RawInput` beside the events, not only on
// them — `keys::handle` reads `i.modifiers`, so a combo spelled only
// on the event would arrive as its bare twin and the plane under
// test would be the wrong one.
modifiers: modifiers_of(&events),
events,
..self.window()
};
self.ctx.run(input, |ctx| {
render(
ctx,
&mut world.model,
&mut world.state,
&self.lernie,
&self.bl,
&self.bz,
);
})
}
/// The raw input this driver's frames are laid into — its own size, or the
/// suite's default window.
fn window(&self) -> egui::RawInput {
match self.size {
Some((w, h)) => crate::paint_probe::screen_sized(w, h),
None => super::input(),
}
}
/// The right edge of the §11 conversation panel — the boundary between the
/// list column and the centre, read off the panel's own stored rect (the
/// same read `super::geometry` makes of it).
///
/// A drive that asserts a row is **absent** needs it. The altitude-1 descent
/// tree paints the same member names in the centre whenever a conversation
/// is selected, and every gesture under test here selects one, so "nothing
/// paints this name" is a claim about the column and never about the window
/// — asserted window-wide it would fail on a correct tree, and its inverse
/// would pass on one that never unfolded anything.
pub(super) fn column(&self) -> f32 {
egui::containers::panel::PanelState::load(&self.ctx, egui::Id::new("conversations"))
.expect("the conversation panel stores its rect")
.rect
.right()
}
/// Which widget holds the frame's own focus — the §11 floor's cursor, the
/// one Tab moves and Space presses (bl-478d).
pub(super) fn focused(&self) -> Option<egui::Id> {
self.ctx.memory(egui::Memory::focused)
}
/// Force every tooltip to paint, hover or no (`super::hover::live`'s drive
/// and the paint-layer half both need it): the operator's hover is a
/// pointer position no walk of the keyboard floor can be in two places for.
pub(super) fn reveal(&self) {
self.ctx.memory_mut(|m| m.set_everything_is_visible(true));
}
/// What the widget `id` **was** — egui's own record of the response it
/// handed the render site, sense and enablement included. The frame is over
/// by the time a test asks, which is exactly what `read_response` answers.
pub(super) fn response(&self, id: egui::Id) -> Option<egui::Response> {
self.ctx.read_response(id)
}
/// Whether widget `id` had a tooltip open on the frame just run — egui's
/// own association of a tooltip with the widget that owns it (the tooltip
/// area's id is derived from the widget's), so a test needs no list of
/// which controls are supposed to have one.
pub(super) fn tooltipped(&self, id: egui::Id) -> bool {
egui::popup::was_tooltip_open_last_frame(&self.ctx, id)
}
/// A frame with no input.
pub(super) fn idle(&self, world: &mut World) -> bool {
self.frame(world, Vec::new())
}
/// Escape — egui spends it surrendering text focus (§11), which is how a
/// test puts the keyboard back down before asking whether an operation
/// picks it up again.
pub(super) fn release(&self, world: &mut World) {
assert!(
!self.frame(world, vec![press(egui::Key::Escape, egui::Modifiers::NONE)]),
"Escape is the release gesture: the box must let go"
);
}
}
/// The modifier plane a frame's presses arrive on.
fn modifiers_of(events: &[egui::Event]) -> egui::Modifiers {
events
.iter()
.find_map(|e| match e {
egui::Event::Key { modifiers, .. } => Some(*modifiers),
_ => None,
})
.unwrap_or(egui::Modifiers::NONE)
}
/// The §11 Ctrl+Shift plane (⌘⇧ on macOS) — the `new workspace` combo.
pub(super) fn command_shift() -> egui::Modifiers {
egui::Modifiers {
shift: true,
..egui::Modifiers::COMMAND
}
}
/// One full click at `pos`: move, press, release — three frames, because egui
/// hit-tests against the *previous* frame's widget rects, so a press in the
/// frame that first sees the pointer would test against nothing.
pub(super) fn click(screen: &Screen, world: &mut World, pos: egui::Pos2) {
screen.frame(world, vec![egui::Event::PointerMoved(pos)]);
for pressed in [true, false] {
screen.frame(
world,
vec![egui::Event::PointerButton {
pos,
button: egui::PointerButton::Primary,
pressed,
modifiers: egui::Modifiers::NONE,
}],
);
}
}
pub(super) fn press(key: egui::Key, modifiers: egui::Modifiers) -> egui::Event {
egui::Event::Key {
key,
physical_key: None,
pressed: true,
repeat: false,
modifiers,
}
}