fix: recreate popup backdrop for each presentation

This commit is contained in:
2026-10-18 01:47:50 +02:00
parent ffad1cf4e6
commit c52c3b227e
2 changed files with 28 additions and 6 deletions
+1 -1
View File
@@ -63,7 +63,7 @@ The launcher, emoji picker, notes, and translation tool open immediately without
Right-click the launcher background for Settings and Quit. Actions include favorites, resetting usage ranking, revealing applications in Finder, quitting them, and uninstalling them. Right-click the launcher background for Settings and Quit. Actions include favorites, resetting usage ranking, revealing applications in Finder, quitting them, and uninstalling them.
Action and provider popups paint a popup-sized background using the launcher window’s gradient coordinates, keeping the glows continuous without showing the results underneath. They do not retain an oversized clipped background between presentations. Layout and background invalidation finish before the opening fade, including when switching popup types. Action and provider popups paint a popup-sized background using the launcher window’s gradient coordinates, keeping the glows continuous without showing the results underneath. Each opening creates a fresh native visual-effect backdrop; closing releases it instead of moving cached material between the footer and header. Layout finishes before the opening fade, including when switching popup types.
### Global shortcuts and launch at login ### Global shortcuts and launch at login
@@ -5,6 +5,7 @@ import ConjureKit
final class ApplicationActionsController: NSViewController, NSSearchFieldDelegate { final class ApplicationActionsController: NSViewController, NSSearchFieldDelegate {
private let search = ActionsSearchField() private let search = ActionsSearchField()
private let background = LauncherBackground(frame: .zero) private let background = LauncherBackground(frame: .zero)
private var material: NSVisualEffectView?
private let entries = [ private let entries = [
ActionEntry(title: "Open", symbol: "arrow.up.forward.app", color: SunsetPalette.text), ActionEntry(title: "Open", symbol: "arrow.up.forward.app", color: SunsetPalette.text),
ActionEntry(title: "Favorite", symbol: "star", color: SunsetPalette.text), ActionEntry(title: "Favorite", symbol: "star", color: SunsetPalette.text),
@@ -58,11 +59,10 @@ final class ApplicationActionsController: NSViewController, NSSearchFieldDelegat
view.layer?.shadowOpacity = 0.35 view.layer?.shadowOpacity = 0.35
view.layer?.shadowRadius = 14 view.layer?.shadowRadius = 14
view.layer?.shadowOffset = NSSize(width: 0, height: -4) view.layer?.shadowOffset = NSSize(width: 0, height: -4)
let content = NSVisualEffectView() let content = NSView()
content.material = .hudWindow content.wantsLayer = true
content.blendingMode = .behindWindow content.layer?.cornerRadius = 18
content.state = .active content.layer?.masksToBounds = true
roundMaterialCorners(content, radius: 18)
content.layer?.borderWidth = 0.5 content.layer?.borderWidth = 0.5
background.setAccessibilityElement(false) background.setAccessibilityElement(false)
content.addSubview(background) content.addSubview(background)
@@ -213,6 +213,24 @@ final class ApplicationActionsController: NSViewController, NSSearchFieldDelegat
coveredPillAlpha = pill.alphaValue coveredPillAlpha = pill.alphaValue
} }
coveredPill?.setAccessibilityHidden(true) coveredPill?.setAccessibilityHidden(true)
// A backdrop belongs to one presentation; do not move a cached native material
// between the footer Actions popup and the header provider popup.
material?.removeFromSuperview()
let backdrop = NSVisualEffectView()
backdrop.material = .hudWindow
backdrop.blendingMode = .behindWindow
backdrop.state = .active
roundMaterialCorners(backdrop, radius: 18)
backdrop.translatesAutoresizingMaskIntoConstraints = false
let content = background.superview!
content.addSubview(backdrop, positioned: .below, relativeTo: background)
NSLayoutConstraint.activate([
backdrop.leadingAnchor.constraint(equalTo: content.leadingAnchor),
backdrop.trailingAnchor.constraint(equalTo: content.trailingAnchor),
backdrop.topAnchor.constraint(equalTo: content.topAnchor),
backdrop.bottomAnchor.constraint(equalTo: content.bottomAnchor),
])
material = backdrop
host.layoutSubtreeIfNeeded() host.layoutSubtreeIfNeeded()
background.needsDisplay = true background.needsDisplay = true
NSAnimationContext.runAnimationGroup { context in NSAnimationContext.runAnimationGroup { context in
@@ -247,6 +265,8 @@ final class ApplicationActionsController: NSViewController, NSSearchFieldDelegat
coveredPill?.setAccessibilityHidden(false) coveredPill?.setAccessibilityHidden(false)
if !animated { if !animated {
view.removeFromSuperview() view.removeFromSuperview()
material?.removeFromSuperview()
material = nil
coveredPill?.layer?.removeAllAnimations() coveredPill?.layer?.removeAllAnimations()
coveredPill?.alphaValue = coveredPillAlpha coveredPill?.alphaValue = coveredPillAlpha
coveredPill = nil coveredPill = nil
@@ -263,6 +283,8 @@ final class ApplicationActionsController: NSViewController, NSSearchFieldDelegat
MainActor.assumeIsolated { MainActor.assumeIsolated {
guard self.transitionGeneration == generation, self.isClosing else { return } guard self.transitionGeneration == generation, self.isClosing else { return }
self.view.removeFromSuperview() self.view.removeFromSuperview()
self.material?.removeFromSuperview()
self.material = nil
self.coveredPill = nil self.coveredPill = nil
self.isClosing = false self.isClosing = false
} }