fix(gamut): Close button; skip SCNView without Metal (#147) #161

Merged
gronod merged 2 commits from fix/147-gamut-close-no-gpu into milestone/m10-studio 2026-09-13 18:49:39 +01:00
Owner

Fixes the no-GPU gamut sheet hang: GamutSceneView.makeNSView used to construct an SCNView before the Metal check flipped viewerUnavailable asynchronously — too late on a Metal-less host, stalling the main thread and blocking app quit behind the open sheet.

Changes

  • GamutSceneAvailability.isAvailable (UITestHooks.skipSceneKit or MTLCreateSystemDefaultDevice() != nil) is evaluated in GamutViewModel.init, so viewerUnavailable is set before the body mounts GamutSceneView — no SCNView is ever constructed without Metal. The post-makeNSView async check remains only as a safety net.
  • btnCloseGamut — always-visible footer Close (visible on the fallback banner too), .keyboardShortcut(.cancelAction) so Escape dismisses without SceneKit. Same \.dismiss pattern as btnCloseSpotRead.
  • Layer toggles stay .disabled(layer == nil) only (Phase 11). applicationShouldTerminate / killAll untouched.
  • docs/21-ui-reference.md gains btnCloseGamut.

Tests — runtime GPU branch

  • hasGPU = MTLCreateSystemDefaultDevice() != nil in the XCTest process; ICCERY_TEST_SKIP_SCENEKIT is not in any default launch env — SceneKit cases run once CI has a GPU with no further patch.
  • New: testCloseButtonDismissesSheet, testFallbackBannerMatchesGPUAvailability, testForcedSceneKitSkipShowsBannerAndClose (env forced for that one test only), testResetCameraInteractsWithScene (XCTSkip without Metal), testViewerUnavailableMirrorsMetalAvailability.
  • Every gamut UI test clicks btnCloseGamut before tearDown terminates the app (explicit call + defensive tearDown).

Also in this PR

test(print): scroll gallery until btnPrintPage is hittable (#132)testPerPagePrint no longer drags app.scrollBars (overlay scrollbars are not in the AX tree) and no longer coordinate-clicks a stale frame (that point was the Dock's Calendar icon). It drags the galleryPage-0 cell content upward until btnPrintPage-0 is hittable above the window-bottom zone; XCTFail otherwise.

Local gate (x86_64 host, Metal present)

  • Targeted: Milestone10GamutCompareUITests 10/10, Milestone6GamutUITests 2/2 (SceneKit reset ran, not skipped), GamutViewModelTests 7/7, AboutHelpUITests 2/2 — 0 failures.
  • Milestone3UITests 6/6 after the scroll fix; testPerPagePrint passes with a real hittable click, Calendar never launches.

Refs #147 #146

Fixes the no-GPU gamut sheet hang: `GamutSceneView.makeNSView` used to construct an `SCNView` **before** the Metal check flipped `viewerUnavailable` asynchronously — too late on a Metal-less host, stalling the main thread and blocking app quit behind the open sheet. ## Changes - `GamutSceneAvailability.isAvailable` (`UITestHooks.skipSceneKit` or `MTLCreateSystemDefaultDevice() != nil`) is evaluated in `GamutViewModel.init`, so `viewerUnavailable` is set **before** the body mounts `GamutSceneView` — no `SCNView` is ever constructed without Metal. The post-`makeNSView` async check remains only as a safety net. - `btnCloseGamut` — always-visible footer Close (visible on the fallback banner too), `.keyboardShortcut(.cancelAction)` so Escape dismisses without SceneKit. Same `\.dismiss` pattern as `btnCloseSpotRead`. - Layer toggles stay `.disabled(layer == nil)` only (Phase 11). `applicationShouldTerminate` / `killAll` untouched. - `docs/21-ui-reference.md` gains `btnCloseGamut`. ## Tests — runtime GPU branch - `hasGPU = MTLCreateSystemDefaultDevice() != nil` in the XCTest process; `ICCERY_TEST_SKIP_SCENEKIT` is **not** in any default launch env — SceneKit cases run once CI has a GPU with no further patch. - New: `testCloseButtonDismissesSheet`, `testFallbackBannerMatchesGPUAvailability`, `testForcedSceneKitSkipShowsBannerAndClose` (env forced for that one test only), `testResetCameraInteractsWithScene` (XCTSkip without Metal), `testViewerUnavailableMirrorsMetalAvailability`. - Every gamut UI test clicks `btnCloseGamut` before `tearDown` terminates the app (explicit call + defensive tearDown). ## Also in this PR `test(print): scroll gallery until btnPrintPage is hittable (#132)` — `testPerPagePrint` no longer drags `app.scrollBars` (overlay scrollbars are not in the AX tree) and no longer coordinate-clicks a stale frame (that point was the Dock's Calendar icon). It drags the `galleryPage-0` cell content upward until `btnPrintPage-0` is hittable above the window-bottom zone; `XCTFail` otherwise. ## Local gate (x86_64 host, Metal present) - Targeted: `Milestone10GamutCompareUITests` 10/10, `Milestone6GamutUITests` 2/2 (SceneKit reset ran, not skipped), `GamutViewModelTests` 7/7, `AboutHelpUITests` 2/2 — 0 failures. - `Milestone3UITests` 6/6 after the scroll fix; `testPerPagePrint` passes with a real hittable click, Calendar never launches. Refs #147 #146
gronod added the Kind/Bug
Priority
High
2
Project/ICCery-v2Feature/UI
labels 2026-09-13 18:25:06 +01:00
gronod added 2 commits 2026-09-13 18:25:08 +01:00
gronod merged commit 78baa49e61 into milestone/m10-studio 2026-09-13 18:49:39 +01:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: gronod/iccery-v2-mac#161