fix: input with ime better interacts with search (closes #5377)

This commit is contained in:
lwouis committed 2026-03-08 18:49:57 +01:00
1 parent d905617248
commit 15caa33a60
6 files changed
+46 -96

No files matched your search

-1
View File
@@ -34,7 +34,6 @@ class Windows {
shouldSelectBestMatchOnSearchChange = true
shouldRestoreDefaultSelectionOnSearchClear = false
hoveredWindowIndex = nil
selectedWindowTarget = nil
}
}
sort()
+2
View File
@@ -58,6 +58,7 @@ class CursorEvents {
}
private static func handleLeftMouseDown(_ cgEvent: CGEvent) -> Unmanaged<CGEvent>? {
if TilesView.hasMarkedText() { return Unmanaged.passUnretained(cgEvent) }
if isPointerInsideSearchField() {
mouseDownInsideSearchField = true
return Unmanaged.passUnretained(cgEvent)
@@ -69,6 +70,7 @@ class CursorEvents {
}
private static func handleLeftMouseUp(_ cgEvent: CGEvent) -> Unmanaged<CGEvent>? {
if TilesView.hasMarkedText() { return Unmanaged.passUnretained(cgEvent) }
if mouseDownInsideSearchField || isPointerInsideSearchField() {
mouseDownInsideSearchField = false
return Unmanaged.passUnretained(cgEvent)
@@ -11,13 +11,16 @@ class KeyboardEventsTestable {
@discardableResult
func handleKeyboardEvent(_ globalId: Int?, _ shortcutState: ShortcutState?, _ keyCode: UInt32?, _ modifiers: NSEvent.ModifierFlags?, _ isARepeat: Bool, _ event: NSEvent? = nil) -> Bool {
let shouldAbsorbEvent = shouldAbsorbSearchEditingKeyDown(event)
if let event, shouldAbsorbEvent, TilesView.handleSearchEditingKeyDown(event) {
return true
if let event, shouldAbsorbSearchEditingKeyDown(event) {
switch TilesView.handleSearchEditingKeyDown(event) {
case .handled: return true
case .passToField: return false
case .passToShortcuts: break
}
}
logKeyboardEvent(globalId, shortcutState, keyCode, modifiers, isARepeat)
let someShortcutTriggered = triggerMatchingShortcuts(globalId, shortcutState, keyCode, modifiers, isARepeat)
return shouldAbsorbEvent || someShortcutTriggered
return someShortcutTriggered
}
private func logKeyboardEvent(_ globalId: Int?, _ shortcutState: ShortcutState?, _ keyCode: UInt32?, _ modifiers: NSEvent.ModifierFlags?, _ isARepeat: Bool) {
+1 -1
View File
@@ -36,7 +36,7 @@ class TileOverView: FlippedView {
// MARK: - Mouse hover management
func updateHover() {
guard let scrollView, !scrollView.isCurrentlyScrolling else { return }
guard let scrollView, !scrollView.isCurrentlyScrolling, !TilesView.hasMarkedText() else { return }
let location = convert(TilesPanel.shared.mouseLocationOutsideOfEventStream, from: nil)
updateButtonHover(location)
let newTarget = findTarget(location)
+28 -88
View File
@@ -8,6 +8,12 @@ enum SearchMode {
case locked
}
enum SearchKeyResult {
case handled
case passToField
case passToShortcuts
}
class TilesView {
static var scrollView: ScrollView!
static var contentView: EffectView!
@@ -100,30 +106,18 @@ class TilesView {
placeSearchCaretAtEnd()
}
static func handleSearchEditingKeyDown(_ event: NSEvent) -> Bool {
static func handleSearchEditingKeyDown(_ event: NSEvent) -> SearchKeyResult {
if hasMarkedText() { return .passToField }
let keyCode = event.keyCode
if keyCode == UInt16(kVK_LeftArrow) { App.cycleSelection(.left); return true }
if keyCode == UInt16(kVK_RightArrow) { App.cycleSelection(.right); return true }
if keyCode == UInt16(kVK_UpArrow) { App.cycleSelection(.up); return true }
if keyCode == UInt16(kVK_DownArrow) { App.cycleSelection(.down); return true }
if keyCode == UInt16(kVK_Space) { return false }
if keyCode == UInt16(kVK_Delete) || keyCode == UInt16(kVK_ForwardDelete) {
deleteSearchCharacter(keyCode)
return true
}
if let insertedText = insertedSearchText(from: event) {
appendSearchText(insertedText)
return true
}
if matchesShortcut(event, "cancelShortcut") {
App.cancelSearchModeOrHideUi()
return true
}
if matchesSearchShortcut(event) {
toggleSearchModeFromShortcut()
return true
}
return false
if keyCode == UInt16(kVK_LeftArrow) { App.cycleSelection(.left); return .handled }
if keyCode == UInt16(kVK_RightArrow) { App.cycleSelection(.right); return .handled }
if keyCode == UInt16(kVK_UpArrow) { App.cycleSelection(.up); return .handled }
if keyCode == UInt16(kVK_DownArrow) { App.cycleSelection(.down); return .handled }
if keyCode == UInt16(kVK_Tab) { return .handled }
if matchesShortcut(event, "cancelShortcut") ||
matchesShortcut(event, "lockSearchShortcut") ||
matchesShortcut(event, "focusWindowShortcut") { return .passToShortcuts }
return .passToField
}
private static func configureSearchField() {
@@ -141,6 +135,9 @@ class TilesView {
searchField.usesSingleLineMode = true
searchField.target = Self.self
searchField.action = #selector(Self.searchFieldChanged(_:))
NotificationCenter.default.addObserver(forName: NSControl.textDidChangeNotification, object: searchField, queue: .main) { _ in
updateSearchQuery(searchField.stringValue)
}
updateSearchFieldEditability()
}
@@ -156,47 +153,6 @@ class TilesView {
App.refreshUi(true)
}
private static func appendSearchText(_ text: String) {
guard !text.isEmpty else { return }
if let editor = searchField.currentEditor() {
let current = searchField.stringValue as NSString
let range = clampedSelectionRange(current.length, editor.selectedRange)
searchField.stringValue = current.replacingCharacters(in: range, with: text)
editor.selectedRange = NSRange(location: range.location + (text as NSString).length, length: 0)
} else {
searchField.stringValue += text
}
updateSearchQuery(searchField.stringValue)
}
private static func deleteSearchCharacter(_ keyCode: UInt16) {
guard !searchField.stringValue.isEmpty else { return }
if let editor = searchField.currentEditor() {
let current = searchField.stringValue as NSString
var range = clampedSelectionRange(current.length, editor.selectedRange)
if range.length == 0 {
if keyCode == UInt16(kVK_Delete) {
guard range.location > 0 else { return }
range = NSRange(location: range.location - 1, length: 1)
} else {
guard range.location < current.length else { return }
range = NSRange(location: range.location, length: 1)
}
}
searchField.stringValue = current.replacingCharacters(in: range, with: "")
editor.selectedRange = NSRange(location: range.location, length: 0)
} else {
searchField.stringValue.removeLast()
}
updateSearchQuery(searchField.stringValue)
}
private static func clampedSelectionRange(_ stringLength: Int, _ selectedRange: NSRange) -> NSRange {
let location = max(0, min(selectedRange.location, stringLength))
let length = max(0, min(selectedRange.length, stringLength - location))
return NSRange(location: location, length: length)
}
private static func clearHover() {
if let oldHoveredWindowIndex = Windows.hoveredWindowIndex {
Windows.hoveredWindowIndex = nil
@@ -225,30 +181,8 @@ class TilesView {
editor.selectedRange = NSRange(location: end, length: 0)
}
private static func insertedSearchText(from event: NSEvent) -> String? {
if event.modifierFlags.contains(.command) || event.modifierFlags.contains(.control) || event.modifierFlags.contains(.function) {
return nil
}
let keyCode = event.keyCode
if keyCode == UInt16(kVK_Tab) || keyCode == UInt16(kVK_Escape) || keyCode == UInt16(kVK_Return) || keyCode == UInt16(kVK_ANSI_KeypadEnter) {
return nil
}
if keyCode == UInt16(kVK_LeftArrow) || keyCode == UInt16(kVK_RightArrow) || keyCode == UInt16(kVK_UpArrow) || keyCode == UInt16(kVK_DownArrow) || keyCode == UInt16(kVK_Home) || keyCode == UInt16(kVK_End) || keyCode == UInt16(kVK_PageUp) || keyCode == UInt16(kVK_PageDown) {
return nil
}
guard let value = event.charactersIgnoringModifiers, value.count == 1 else { return nil }
guard let scalar = value.unicodeScalars.first, !CharacterSet.controlCharacters.contains(scalar) else { return nil }
return String(value)
}
private static func matchesSearchShortcut(_ event: NSEvent) -> Bool {
matchesShortcut(event, "searchShortcut")
}
private static func updateSearchFieldEditability() {
let editable = searchMode == .editing
searchField.isEditable = editable
searchField.isSelectable = editable
static func hasMarkedText() -> Bool {
(searchField.currentEditor() as? NSTextView)?.hasMarkedText() == true
}
private static func matchesShortcut(_ event: NSEvent, _ shortcutId: String) -> Bool {
@@ -260,6 +194,12 @@ class TilesView {
return eventModifiers == shortcutModifiers || eventModifiers == (shortcutModifiers | holdModifiers)
}
private static func updateSearchFieldEditability() {
let editable = searchMode == .editing
searchField.isEditable = editable
searchField.isSelectable = editable
}
static func updateCachedSizes() {
guard let firstView = TilesView.recycledViews.first else { return }
layoutCache.labelHeight = firstView.label.cell!.cellSize.height
+8 -2
View File
@@ -1,8 +1,14 @@
import ShortcutRecorder
enum SearchKeyResult {
case handled
case passToField
case passToShortcuts
}
class TilesViewMock {
var isSearchEditing = false
func handleSearchEditingKeyDown(_ event: NSEvent) -> Bool { return false }
func handleSearchEditingKeyDown(_ event: NSEvent) -> SearchKeyResult { return .passToField }
}
class TilesPanelMock {
@@ -46,7 +52,7 @@ class TilesView {
set { App.app.tilesPanel.tilesView.isSearchEditing = newValue }
}
static func handleSearchEditingKeyDown(_ event: NSEvent) -> Bool {
static func handleSearchEditingKeyDown(_ event: NSEvent) -> SearchKeyResult {
return App.app.tilesPanel.tilesView.handleSearchEditingKeyDown(event)
}
}