fix: dismiss active launcher window on Escape

This commit is contained in:
2026-10-17 00:05:20 +02:00
parent bc7e4fd013
commit a891d49bb1
10 changed files with 98 additions and 50 deletions
@@ -267,7 +267,6 @@ final class ApplicationActionsController: NSViewController, NSSearchFieldDelegat
switch selector {
case #selector(NSResponder.insertNewline(_:)):
if let selectedEntry { chooseAction(at: selectedEntry) }
case #selector(NSResponder.cancelOperation(_:)): close()
case #selector(NSResponder.moveUp(_:)): moveSelection(by: -1)
case #selector(NSResponder.moveDown(_:)): moveSelection(by: 1)
default: return false
@@ -116,7 +116,6 @@ final class EmojiPickerController: NSViewController, NSCollectionViewDataSource,
actions.close(animated: false)
closed?()
}
override func cancelOperation(_ sender: Any?) { goBack() }
func inputChanged(preservingSelection: Bool = false) {
let previous = preservingSelection && results.indices.contains(selected) ? results[selected].value : nil
@@ -223,7 +222,6 @@ final class EmojiPickerController: NSViewController, NSCollectionViewDataSource,
case #selector(NSResponder.moveUp(_:)): moveVertically(-1)
case #selector(NSResponder.moveDown(_:)): moveVertically(1)
case #selector(NSResponder.insertNewline(_:)): copyAndPasteSelection()
case #selector(NSResponder.cancelOperation(_:)): goBack()
default: return false
}
return true
+20 -6
View File
@@ -158,9 +158,13 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
func dismiss(restoringFocus: Bool = true) {
guard !restoringFocus || (!isLaunching && window?.attachedSheet == nil) else { return }
if let review = uninstallReview { review.goBack(); return }
if launcherFrameBeforeReview != nil {
guard uninstallReview?.canClose != false else { return }
pendingPresentation = { [weak self] in self?.dismiss(restoringFocus: restoringFocus) }
uninstallReview?.goBack()
return
}
closeCommand()
guard launcherFrameBeforeReview == nil else { return }
actions.close(animated: false)
alignmentGuides?.end()
let previous = previousApplication
@@ -257,7 +261,7 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
commandBack.isBordered = false
commandBack.contentTintColor = SunsetPalette.secondaryText
commandBack.setAccessibilityLabel("Back to applications")
commandBack.toolTip = "Back to applications (Escape)"
commandBack.toolTip = "Back to applications"
commandBack.isHidden = true
NSLayoutConstraint.activate([
commandBack.widthAnchor.constraint(equalToConstant: IconStyle.applicationSize(for: searchFont)),
@@ -478,10 +482,13 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
}
func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
if commandSelector == #selector(NSResponder.cancelOperation(_:)) {
dismiss()
return true
}
if let emojiPicker { return emojiPicker.handle(commandSelector) }
if let translation {
if commandSelector == #selector(NSResponder.insertNewline(_:)) { translation.translate(); return true }
if commandSelector == #selector(NSResponder.cancelOperation(_:)) { translation.goBack(); return true }
return false
}
switch commandSelector {
@@ -491,8 +498,6 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
moveSelection(by: -1)
case #selector(NSResponder.insertNewline(_:)):
openSelection()
case #selector(NSResponder.cancelOperation(_:)):
dismiss()
default:
return false
}
@@ -1201,6 +1206,11 @@ final class LauncherPanel: NSPanel {
}
override func sendEvent(_ event: NSEvent) {
if event.type == .keyDown, event.keyCode == 53, attachedSheet == nil,
event.modifierFlags.intersection([.command, .control, .option, .shift]).isEmpty {
cancelOperation(nil)
return
}
let eligible = numberShortcutsEligible
if event.type == .flagsChanged || (event.type == .keyUp && event.keyCode == 49) {
reevaluateNumberHints(modifierFlags: event.modifierFlags, eligible: eligible, at: event.timestamp)
@@ -1233,6 +1243,10 @@ final class LauncherPanel: NSPanel {
}
}
override func cancelOperation(_ sender: Any?) {
dismiss?()
}
override func performClose(_ sender: Any?) {
dismiss?()
}
+2 -17
View File
@@ -3,7 +3,7 @@ import ConjureKit
import Security
@MainActor
final class TranslationController: NSViewController, NSTextFieldDelegate, NSTextViewDelegate {
final class TranslationController: NSViewController, NSTextFieldDelegate {
private let keyField = NSSecureTextField()
private let provider = NSPopUpButton()
private let credentials = NSStackView()
@@ -59,7 +59,7 @@ final class TranslationController: NSViewController, NSTextFieldDelegate, NSText
translateButton.isBordered = false
translateButton.font = .systemFont(ofSize: 12, weight: .medium)
translateButton.contentTintColor = SunsetPalette.text
translateButton.toolTip = "Translate the English input (Return). Escape returns to the launcher."
translateButton.toolTip = "Translate the English input (Return). Escape dismisses Conjure."
let returnKey = ShortcutIndicator("↵")
let actionPill = NSView()
actionPill.wantsLayer = true
@@ -91,7 +91,6 @@ final class TranslationController: NSViewController, NSTextFieldDelegate, NSText
output.textColor = SunsetPalette.text
output.font = .systemFont(ofSize: 20)
output.setAccessibilityLabel("Japanese translation")
output.delegate = self
let scroll = NSScrollView()
scroll.documentView = output
scroll.hasVerticalScroller = true
@@ -129,20 +128,6 @@ final class TranslationController: NSViewController, NSTextFieldDelegate, NSText
updateProvider()
}
override func cancelOperation(_ sender: Any?) { goBack() }
func control(_ control: NSControl, textView: NSTextView, doCommandBy selector: Selector) -> Bool {
guard selector == #selector(NSResponder.cancelOperation(_:)) else { return false }
goBack()
return true
}
func textView(_ textView: NSTextView, doCommandBy selector: Selector) -> Bool {
guard selector == #selector(NSResponder.cancelOperation(_:)) else { return false }
goBack()
return true
}
func controlTextDidChange(_ notification: Notification) { inputChanged() }
@objc func goBack() {
@@ -25,6 +25,7 @@ final class UninstallReviewController: NSViewController, NSSearchFieldDelegate,
private var window: NSWindow? { view.window }
var closed: (() -> Void)?
var changed: (() -> Void)?
var canClose: Bool { !busy }
init(application: Application, uninstaller: ApplicationUninstaller) {
self.application = application
@@ -117,7 +118,6 @@ final class UninstallReviewController: NSViewController, NSSearchFieldDelegate,
table.allowsEmptySelection = false
table.setAccessibilityLabel("Files to uninstall for \(application.name)")
table.toggle = { [weak self] in self?.toggleHighlighted() }
table.dismiss = { [weak self] in self?.goBack() }
let scroll = NSScrollView()
scroll.documentView = table
scroll.hasVerticalScroller = true
@@ -229,7 +229,6 @@ final class UninstallReviewController: NSViewController, NSSearchFieldDelegate,
case #selector(NSResponder.moveDown(_:)): moveSelection(1)
case #selector(NSResponder.moveUp(_:)): moveSelection(-1)
case #selector(NSResponder.insertNewline(_:)): toggleHighlighted()
case #selector(NSResponder.cancelOperation(_:)): goBack()
default: return false
}
return true
@@ -391,10 +390,8 @@ final class UninstallReviewController: NSViewController, NSSearchFieldDelegate,
}
}
override func cancelOperation(_ sender: Any?) { goBack() }
@objc func goBack() {
guard !busy else { return }
guard canClose else { return }
scanTask?.cancel()
(view as! UninstallReviewContent).acceptsInteraction = false
view.setAccessibilityHidden(true)
@@ -467,10 +464,8 @@ private final class UninstallFileCell: NSTableCellView {
@MainActor
private final class UninstallFilesTable: NSTableView {
var toggle: (() -> Void)?
var dismiss: (() -> Void)?
override func keyDown(with event: NSEvent) {
if [36, 76, 49].contains(event.keyCode), event.modifierFlags.intersection([.command, .option, .control]).isEmpty { toggle?() }
else if event.keyCode == 53 { dismiss?() }
else { super.keyDown(with: event) }
}
}