| | |
| | | ) -> [QuickSearchIndexedField] { |
| | | let names = uniqueOrdered([appName] + localizedNames) |
| | | let nameFields = names.map { |
| | | let allowsRomanizedFuzzy = containsNonLatinLetter($0) |
| | | return QuickSearchIndexedField( |
| | | kind: .name, |
| | | text: $0, |
| | | normalized: normalizeField($0), |
| | | acronym: acronym(for: $0), |
| | | pinyinCandidates: pinyinCandidates(for: $0), |
| | | allowPinyinSubstring: allowsRomanizedFuzzy, |
| | | allowPinyinFuzzySubsequence: allowsRomanizedFuzzy |
| | | pinyinCandidates: pinyinCandidates(for: $0, includeLatin: true), |
| | | allowPinyinSubstring: true, |
| | | allowPinyinFuzzySubsequence: true |
| | | ) |
| | | } |
| | | let tagFields = tagNames.map { |
| | |
| | | .lowercased() |
| | | } |
| | | |
| | | private static func pinyinCandidates(for value: String) -> [String] { |
| | | guard containsNonLatinLetter(value) else { return [] } |
| | | private static func pinyinCandidates(for value: String, includeLatin: Bool = false) -> [String] { |
| | | guard includeLatin || containsNonLatinLetter(value) else { return [] } |
| | | let mutable = NSMutableString(string: value) |
| | | CFStringTransform(mutable, nil, kCFStringTransformToLatin, false) |
| | | CFStringTransform(mutable, nil, kCFStringTransformStripCombiningMarks, false) |
| | |
| | | .cornerRadius(10) |
| | | |
| | | VStack(alignment: .leading, spacing: 4) { |
| | | Text(result.app.name) |
| | | Text(result.app.displayName) |
| | | .font(.system(size: 20, weight: .semibold)) |
| | | .foregroundStyle(.primary) |
| | | .lineLimit(1) |
| | |
| | | } |
| | | |
| | | private var accessibilityText: String { |
| | | [result.app.name, detailText].compactMap { $0 }.joined(separator: ", ") |
| | | [result.app.displayName, detailText].compactMap { $0 }.joined(separator: ", ") |
| | | } |
| | | } |
| | | |
| | |
| | | field.placeholderString = placeholder |
| | | field.delegate = context.coordinator |
| | | field.onCommand = onCommand |
| | | context.coordinator.onCommand = onCommand |
| | | field.setAccessibilityLabel(tr("quickSearch.inputAccessibility")) |
| | | context.coordinator.field = field |
| | | requestFocus(field) |
| | |
| | | } |
| | | field.placeholderString = placeholder |
| | | field.onCommand = onCommand |
| | | context.coordinator.onCommand = onCommand |
| | | if context.coordinator.lastFocusToken != focusToken { |
| | | context.coordinator.lastFocusToken = focusToken |
| | | requestFocus(field) |
| | |
| | | final class Coordinator: NSObject, NSTextFieldDelegate { |
| | | var text: Binding<String> |
| | | var lastFocusToken = 0 |
| | | var onCommand: ((QuickSearchCommand) -> Void)? |
| | | weak var field: NSTextField? |
| | | |
| | | init(text: Binding<String>) { |
| | |
| | | guard let field = obj.object as? NSTextField else { return } |
| | | text.wrappedValue = field.stringValue |
| | | } |
| | | |
| | | func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool { |
| | | switch commandSelector { |
| | | case #selector(NSResponder.moveUp(_:)): |
| | | onCommand?(.moveUp) |
| | | return true |
| | | case #selector(NSResponder.moveDown(_:)): |
| | | onCommand?(.moveDown) |
| | | return true |
| | | case #selector(NSResponder.insertNewline(_:)): |
| | | onCommand?(.submit) |
| | | return true |
| | | case #selector(NSResponder.insertNewlineIgnoringFieldEditor(_:)): |
| | | onCommand?(.submit) |
| | | return true |
| | | case #selector(NSResponder.cancelOperation(_:)): |
| | | onCommand?(.dismiss) |
| | | return true |
| | | default: |
| | | return false |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |