Files
gridcraft/docs/egui-037-local-followups.md
T
skylar taylor-barrick 41769f1d16 Migrate GridCraft to egui 0.37 (#2543)
* E37: migrate GridCraft to egui 0.37

Pin the reviewed fork and shared dependencies, adapt input and rendering to the
new frame lifecycle, and preserve bounded capture handling and font notices.
Add representative accessibility checks and meaningful control labels.

Validation: full repository CI passed all ten stages, with 2,249 tests passed,
zero failed and one existing timing test ignored. Native workflows and paired
performance qualification remain documented separately.

* egui037: record final published pins and local qualification

* M0.37: fix RTL clipping and preserve screenshot budgets

* test: share migrated GridCraft UI harness setup

* egui037: document migrated UI and regression contracts

* egui-037: align popup test fonts and avoid redundant tooltip probes
2026-10-25 14:55:00 -04:00

17 KiB

egui 0.37 local port — follow-ups and gates

Status: local implementation on GridCraft b8771d51fb4da60bd48c13cf313d6d6014dc5e66 with canonical immutable dependency pins. Final managed full CI egui037-gridcraft-candidate-20261024-full-ci-queue-07 succeeded at 2026-10-24 05:21 UTC after both accessibility repairs: all ten stages passed, with 2,249 tests passed, none failed and one existing release timing experiment ignored. The representative explicit accessibility/capture checks passed. The required local precommit CI gate is complete for the formatted runtime source; this final refresh changes only the two migration documents. Native/platform/performance acceptance remains separate. No code push or PR publication is authorized; each requires fresh human approval of contents and validation.

Current integration inputs

  • Root Cargo patches select the egui family at 173cc7375d35dc5fbc5b5d0994fdf787f547c5d1 and winit at ae48c756a3b81125eb796c0cc6dccf60990d1181. Selected egui packages are 0.37.0; there is no machine-local path override or second egui 0.36 source.
  • craft-ui and craft-print both pin c488caa9fbc2fb1a86e4674887e9b80b598ebbfa. The shared capture slot is consumed through that revision. Canonical Cargo.lock SHA256 is 98209e84412b8ce08bf290ab706c20909389ca6d1e6668aca929f717ad55ea2f.
  • CI, release and FreeBSD select craft-fonts b076417b5880bd4154bd22f00695b1d28c6bea83. Local validation uses command-scoped private Git mirrors for unpublished immutable inputs; clean remote fetching after approved publication remains a separate gate.
  • The shared capture contract fits GridCraft's requirements without changes: one slot per request, take() at most once, cancel()/drop makes late callbacks inert, a per-image byte bound, and a viewport waker that runs after the slot lock is released. GridCraft adds only its admission budget, request records, geometry epoch and output routing around it.
  • The CaptureSlot callback holds only a weak reference and never encodes, writes a file or sends a control reply; GridCraft drains the slot from logic and writes outside the slot.

Capture lifecycle repair (2026-10-23)

The previous managed run of this checkout (egui037-gridcraft-candidate-20261023-tests-01) finished with 154 passed and 8 failed: five screenshot/lifecycle tests dropped an unapplied TexturesDelta, two more lifecycle tests failed on the same atlas delta, and formula_bar::spill_tests::formula_bar_click_and_theme_presentation reported the Name Box TextInput without an accessible name. Those failures are historical; final full CI07 passed the repaired lifecycle and accessibility tests.

The local source now addresses the fresh review's capture findings:

  • A completion that arrives after the request deadline is refused with the timeout error before any encoding or file write; timeouts, geometry mismatches, oversize images and removed requests all release their reservation. Every screenshot reply carries its request token.
  • Screenshot deadlines and AutoSave pacing use a monotonic clock (std::time::Instant natively, performance.now() on wasm). now_ms() stays the wall clock for user-visible timestamps such as Backstage's "last changed".
  • CaptureGeometry::current reads ViewportInfo (refreshed by run_logic) for the viewport inner_rect and native_pixels_per_point * zoom, falling back to the last screen rect; a hidden resize is therefore seen without a UI pass. Issuance and drain update the geometry and its epoch through one helper, and the slot is created at issuance with a per-image bound equal to the aggregate reservation (which includes the accepted one-pixel rounding).
  • A request admitted while the window has no size (minimized or not laid out) reserves UNKNOWN_CAPTURE_BYTES and waits for a real size; it times out with a size-specific message. Drain runs before admission, and settling counts one non-discarded egui pass per request.
  • The geometry identity intentionally includes zoom, visible and occluded even when the expected physical size is unchanged. A capture is bound to one layout generation; the two-retry budget followed by an explicit error is the accepted bounded failure, not a reason to compare only the expected size.
  • A synthetic step consumed by a font-gated ui early return is offered again on the next pass (the hook tracks the cumulative_pass_nr at injection), and font-set changes wait while an input step is pending. Re-offering the step is safe: the early-returning pass handled no input, so egui's state changes from those events (keys down, pointer down) have not applied a step.
  • The Name Box and the formula-bar/in-cell editors carry explicit accessible names, so the strict kittest accessibility check passes without disabling it or the missing-glyph policy.

Shared eframe web follow-up: RequestPaintWhileHidden

Native egui-winit handles ActionRequested::wants_paint() for RequestPaintWhileHidden and paints the hidden viewport. The pinned eframe web runner does not: a hidden tab runs only run_logic, and handle_viewport_commands handles Screenshot/SetTheme while every other command — including RequestPaintWhileHidden — falls into its unhandled-command log. A queued ui.screenshot or a queued ui.key/ui.text step in a hidden browser tab therefore cannot reach its ui pass until the tab is visible; the screenshot request reports its timeout instead. The fix belongs in the shared eframe web runner (paint a hidden tab on request, with the same queued-work trigger GridCraft sends from logic on native), not in a GridCraft-local channel. GridCraft therefore sends RequestPaintWhileHidden only off wasm (a visible web tab still gets request_repaint). The control protocol documents this web limitation instead of claiming hidden web screenshots work.

E21b — application qualification remains open

The final canonical egui fork includes IMEOutput::surrounding_text, owner/context generation and the Wayland outgoing adapter; the earlier statement that no shared producer/adapter exists is superseded. GridCraft must qualify its actual formula-bar, in-cell and text-box editing paths, including available-empty versus unavailable, stale epoch/serial deletion and commit, deletion-only batches, composition ownership and two-surface isolation. Full CI07 is not a native IME or complete E21b/W27 acceptance result. No application-local API stub is introduced.

Review repair (2026-10-24)

  • RequestPaintWhileHidden is sent only for a queued/settling request that has a capturable size; an issued capture relies on the Screenshot command's wants_paint, and a minimized request no longer forces full hidden frames while it waits.
  • A slot-level per-image refusal (an image larger than the reservation) now spends the same bounded retry as a smaller image, instead of failing only when the window grew.
  • The shared deterministic harness, the Text Box accessible-name translations, the explicit PasteImage/MiddleClickPaste no-op choice, the lifecycle once-per-host-frame tests (autosave, title, print poll, discard after issuance) and wheel unit/source/phase coverage are in the working tree. Final full CI07 exercised these repairs and the explicit accessibility assertions below successfully.

Remaining acceptance gates

  • Required local CI is complete: full CI07 passed i18n-report, fmt, strict workspace all-target clippy, workspace tests, doctest, assets (34 attributed files), layers, WASM, parity and keytips. The existing timing-sensitive recalc_with_range_formulas_scales_linearly test remains ignored by the normal CI command; it is not a performance result. No additional CI execution is claimed for this documentation-only refresh.
  • Strict egui_kittest harness qualification completed for the selected end states: tests/common/mod.rs now installs theme::pinned_font_definitions() (bundled faces plus the pinned craft-fonts input, no host system scan), SheetApp::use_prepared_fonts() keeps that set through the first logic pass, and MissingGlyphPolicy::Panic stays on. Since run_steps does not invoke kittest's accessibility checker, selected ribbon, locale, pane/menu, dialog, active-editor, backstage, text-box and capture/repaint end states now call check_accessibility() explicitly; these assertions passed in CI07, without claiming coverage of every UI state. CRAFT_FONTS_DIR (and CRAFT_FONTS_REQUIRED=1 for acceptance) is the CI contract; without it the deterministic bundled set is used and the all-catalog language test records the Ja/Ko/Zh gap.
  • End-to-end hidden/occluded ui.screenshot with a capturable current size and a minimized request that waits for restore (or times out), through a real wgpu/kittest or native window; the per-image/aggregate budget stress cases, resize-back/DPI/zoom epoch retries, and native clipboard/PRIMARY/IME evidence. No native hidden/minimize/restore run has been performed for this source.
  • Native Linux, Windows clone, browser and macOS workflows; performance, text-tessellation, Windows renderer-construction and print measurements against the recorded baseline.
  • Accessibility roles/names beyond the mechanical Role port (G3) and native screen-reader smoke tests.

Font provisioning (2026-10-23, font-coverage integration)

The font gap is addressed in the font set itself. Test harnesses keep MissingGlyphPolicy::Panic and the enabled accessibility-check policy; the representative explicit checks above are needed to execute that policy on run_steps fixtures. No test calls allow_missing_glyphs() or disables the accessibility check, and production still renders Tofu for a character no face has.

  • gridcraft-ui-egui enables egui/default_fonts and egui/monochrome_emoji_fonts. egui 0.37 moved Noto Emoji and emoji-icon-font behind the monochrome feature, and with default_fonts alone no installed face has ✕ or 📋 (Hack, which the theme adds as a fallback, has the arrows and triangles but neither of those).
  • theme::font_definitions_with appends egui's bundled Hack face (arrows, triangles, ↳) behind the UI/cell and emoji faces of every family, and appends the optional craft-fonts faces at the very end. The order is: app/bundled faces, Hack/emoji, system script fallbacks, permitted craft faces last. A craft face never displaces a system face that already has the glyph.
  • crates/ui-egui/build.rs embeds the faces named by CRAFT_FONTS_DIR through crates/ui-egui/src/craft_fonts_policy.rs, the single policy shared by the build script and the tests. Nothing reads a fixture path from Cargo configuration: the checked .cargo/config.toml holds only the xtask alias, so every local and managed run sets the variable explicitly. With it unset the generated table is empty and the app builds, tests and runs; a script that no installed system face and no bundled face covers stays the platform gap it was before (Tofu in production, and the strict kittest harness still panics on it by design).
  • Required faces, matched by exact family and style and by their exact manifest script tags: BIZ UDPGothic Regular and Bold (Jpan,Latn), NanumGothic Regular (Hang,Kore), Noto Sans Symbols Regular (Zsym) and Noto Sans Symbols 2 Regular (Zsym). NanumGothic has no space, and Noto Sans Symbols is not Noto Sans Symbols 2.
  • CRAFT_FONTS_REQUIRED=1 turns a missing CRAFT_FONTS_DIR, a missing fonts/manifest.txt, a malformed manifest row, a missing font file, an unexpected style of a required family or any missing required face into a build error.
  • Barred families never enter the font set, although the shared repository keeps them for other consumers: Adobe*, Source Han*, Source Sans*, Source Serif*, Noto Sans CJK* and Noto Serif CJK*. There is deliberately no broad Noto Sans prefix, because it would also reject the permitted Noto Sans Symbols faces.
  • Web (wasm32) embeds only the explicit allowlist: BIZ UDPGothic Regular and Bold, NanumGothic Regular, Noto Sans Symbols Regular and Noto Sans Symbols 2 Regular; no CJK collection and no document-only face. Those five files are 12,970,840 bytes raw. Raw font bytes, raw wasm and compressed wasm are not measured yet: the size gate is a parent acceptance item, not a claim made here.
  • Native builds opt into the OFL LXGW WenKai Lite face from craft-fonts' fonts/simplified-chinese-manifest.txt (the same face DeckCraft uses). The barred Noto Sans CJK collection still never enters; system Han faces still lead when installed. CRAFT_FONTS_REQUIRED=1 native builds require the Chinese manifest, ordinary builds tolerate an older checkout without it, and WEB_ALLOWED excludes it. The Chinese samples join the strict script test only when the face is embedded.
  • Native also embeds every other non-barred manifest row (for example BIZ UDMincho, Shippori Mincho, Noto Sans Arabic/Georgian/Hebrew/Thai). They are OFL and asset-policy safe, but the actual set is wider than the five required faces; the size gate must measure it rather than citing only the web allowlist.
  • No-font/default mode: the bundled faces supply ↘ ◀ ↑ 📋 and the rest of the interface's symbols, which tests/symbol_glyphs.rs asserts strictly. ✕, ✓ and ⌃ have no bundled egui face, so chrome draws them instead of painting them: the Symbol gallery and the close/check controls paint the code-drawn Icon::Check/Icon::Close (icons::fallback_for_symbol, widgets::glyph_button), and a tooltip whose symbol is missing falls back to ^ (theme::symbol_fallback through theme::available_text). Only workbook text still needs a real glyph, because its characters are the user's data. The test documents ✓/⌃ as optional-input gaps rather than claiming them.
  • Extended acceptance mode (CRAFT_FONTS_DIR set, and CRAFT_FONTS_REQUIRED=1 for release): tests/symbol_glyphs.rs requires the exact union ↘ ✕ ◀ ↑ 📋 ✓ ⌃ in both Proportional and CELL plus the Korean interface labels, and fails instead of skipping when a loaded or required set lacks a face. ⌃ comes from Noto Sans Symbols, ✕ ◀ ✓ from Noto Sans Symbols 2, ↘/↑ from either Symbols face (NanumGothic also has them), and 📋 from egui's emoji faces.
  • The required faces live in the pinned craft-fonts checkout at b076417b5880bd4154bd22f00695b1d28c6bea83. Point CRAFT_FONTS_DIR at that checkout: the older fixtures/craft-fonts snapshot has no Korean or symbol rows, and a loaded set without the required faces fails the extended checks by design.
  • theme::fallback_covers_its_script is strict whenever a system or craft-fonts face provides the script, now including Arabic and Georgian; its skip prints the script name and sample.
  • The production positive-rectangle fix in formula_bar.rs, the Role::DisclosureTriangle queries, the linear per-line editor-height assertions and the explicit accessible names for the Name Box and both editors are unchanged.

Explicit G1 accessibility repair — 2026-10-24

Seven existing integration tests now check rendered ribbon/light-dark, each supported locale, pivot pane and open menu, fifteen dialogs, active cell/formula edits, backstage pages and active text-box/committed states. The existing capture callback test now uses a full SheetApp Harness, asserts the Name Box tree is present, and checks accessibility before issuance and after callback drain/repaint. Its manually supplied pixels still test callback lifecycle rather than GPU readback; the explicit no-GPU renderer prevents kittest from silently turning it into a platform test. One-callback, request-token, one-reply and released-reservation assertions remain.

All eight affected tests (seven integration tests and the capture unit test) passed in full CI07. The four CI06 failures exposed unnamed Options/ribbon ComboBoxes, custom format input and pivot layout control; visible localized labels or precise semantic names now identify them. The fresh source review found no blocker; CI07's formatting changed only wrapping/trailing commas in those four reviewed production files. Original roles, input sequences and behavior assertions remain. Full CI05 is historical pre-repair evidence and CI06 remains a failed run.

Actual native screenshots/hidden/minimize/restore, screen-reader output, application E21b IME and platform/performance acceptance remain open. Full CI07 uses the canonical lock, required craft-fonts input, command-scoped exact-source mirrors and the private Vulkan loader; it is not host-loader or clean public-fetch qualification. Exact job/log hashes, review union, complete final source inventory and documentation-only delta are preserved in local task evidence under egui037-migration-20261022/final-landing/gridcraft-final-qualification.

Revision: 2026-10-24 — replaced pending accessibility/CI statements with actual full CI07 results, corrected the shared outgoing IME inventory, and retained native/performance/publication gates.