fix: input with ime better interacts with search (closes #5377)
This commit is contained in:
1 parent
d905617248
commit
15caa33a60
6 files changed
+46
-96
No files matched your search
@@ -34,7 +34,6 @@ class Windows {
|
||||
shouldSelectBestMatchOnSearchChange = true
|
||||
shouldRestoreDefaultSelectionOnSearchClear = false
|
||||
hoveredWindowIndex = nil
|
||||
selectedWindowTarget = nil
|
||||
}
|
||||
}
|
||||
sort()
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user