Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

slab/menuband: full popover repaint on light/dark flip

`handleEffectiveAppearanceChange` only repainted a handful of named
layer-backed views (root background, visualizer bezel, keyboard deck,
title chip). The popover's `draw(_:)`-based children — QwertyLayoutView
and InstrumentListView — paint with dynamically-resolving NSColors
(.labelColor, .controlAccentColor) that re-resolve against
effectiveAppearance at draw time, but AppKit does NOT auto-invalidate
drawing when appearance flips. So toggling system dark mode left the
qwerty map and instrument grid showing the prior palette until a
different action nudged them.

Recurse `needsDisplay = true` across the subtree, and re-resolve the
update banner's cached cgColor while we're here. Result: the whole
popover transitions in one beat.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+23
+23
slab/menuband/Sources/MenuBand/MenuBandPopover.swift
··· 1522 1522 fileprivate func handleEffectiveAppearanceChange() { 1523 1523 rootBackgroundView?.layer?.backgroundColor = 1524 1524 NSColor.windowBackgroundColor.cgColor 1525 + // Update banner uses controlAccentColor.cgColor at build time — 1526 + // accent doesn't normally re-tone with light/dark, but the 1527 + // semi-transparent fill reads visibly different over a flipped 1528 + // window background, so re-resolve it against the current 1529 + // appearance to keep the cached cgColor honest. 1530 + updateBanner?.layer?.backgroundColor = NSColor.controlAccentColor 1531 + .withAlphaComponent(0.14).cgColor 1525 1532 applyAppearanceToVisualizer() 1526 1533 refreshHeldNotes() 1527 1534 updateInstrumentReadout() 1535 + // Most popover children (QwertyLayoutView, InstrumentListView, 1536 + // chord cards' draw passes) paint via `draw(_:)` using 1537 + // dynamically-resolving NSColors like .labelColor and 1538 + // .controlAccentColor. Those resolve against effectiveAppearance 1539 + // *at draw time*, but AppKit doesn't auto-invalidate display 1540 + // when appearance changes — so the views keep showing stale 1541 + // light-mode glyphs over a dark-mode background until something 1542 + // else nudges them. Walk the subtree once and force a redraw so 1543 + // the whole popover lands on the new appearance in one beat. 1544 + forceRedrawSubtree(rootBackgroundView) 1545 + } 1546 + 1547 + private func forceRedrawSubtree(_ root: NSView?) { 1548 + guard let root = root else { return } 1549 + root.needsDisplay = true 1550 + for sub in root.subviews { forceRedrawSubtree(sub) } 1528 1551 } 1529 1552 1530 1553 /// Flip the LED bezel + visualizer between dark-mode (LED-on-black