diff --git a/ElementX/Sources/Other/SwiftUI/Search.swift b/ElementX/Sources/Other/SwiftUI/Search.swift index cb951305d9..d90814ca84 100644 --- a/ElementX/Sources/Other/SwiftUI/Search.swift +++ b/ElementX/Sources/Other/SwiftUI/Search.swift @@ -5,6 +5,7 @@ // Please see LICENSE in the repository root for full details. // +import GameController import SwiftUI import SwiftUIIntrospect @@ -165,7 +166,18 @@ private struct SearchController: UIViewControllerRepresentable { // MARK: - Searchable Extensions -struct IsSearchingModifier: ViewModifier { +extension View { + func isSearching(_ isSearching: Binding) -> some View { + modifier(IsSearchingModifier(isSearching: isSearching)) + } + + /// Automatically focusses the view's search field if a hardware keyboard is connected. + func focusSearchIfHardwareKeyboardAvailable() -> some View { + modifier(FocusSearchIfHardwareKeyboardAvailableModifier()) + } +} + +private struct IsSearchingModifier: ViewModifier { @Environment(\.isSearching) private var isSearchingEnv @Binding var isSearching: Bool @@ -175,8 +187,26 @@ struct IsSearchingModifier: ViewModifier { } } -extension View { - func isSearching(_ isSearching: Binding) -> some View { - modifier(IsSearchingModifier(isSearching: isSearching)) +private struct FocusSearchIfHardwareKeyboardAvailableModifier: ViewModifier { + @FocusState private var isFocused + + func body(content: Content) -> some View { + if #available(iOS 18.0, *) { + content + .searchFocused($isFocused) + .onAppear(perform: focusIfHardwareKeyboardAvailable) + } else { + content + } + } + + func focusIfHardwareKeyboardAvailable() { + // The simulator always detects the hardware keyboard as connected + #if !targetEnvironment(simulator) + if GCKeyboard.coalesced != nil { + MXLog.info("Hardware keyboard is connected") + isFocused = true + } + #endif } } diff --git a/ElementX/Sources/Screens/EmojiPickerScreen/View/EmojiPickerScreen.swift b/ElementX/Sources/Screens/EmojiPickerScreen/View/EmojiPickerScreen.swift index 7f139f79a1..0ef5d4653f 100644 --- a/ElementX/Sources/Screens/EmojiPickerScreen/View/EmojiPickerScreen.swift +++ b/ElementX/Sources/Screens/EmojiPickerScreen/View/EmojiPickerScreen.swift @@ -51,6 +51,8 @@ struct EmojiPickerScreen: View { .toolbar { toolbar } .isSearching($isSearching) .searchable(text: $searchString, placement: .navigationBarDrawer(displayMode: .always)) + .focusSearchIfHardwareKeyboardAvailable() + .onSubmit(of: .search, sendFirstEmojiOnMac) .compoundSearchField() } .presentationDetents([.medium, .large]) @@ -76,6 +78,15 @@ struct EmojiPickerScreen: View { } } } + + func sendFirstEmojiOnMac() { + // No sure-fire way to detect that the submit came from a h/w keyboard on iOS/iPadOS. + guard ProcessInfo.processInfo.isiOSAppOnMac else { return } + + if let emoji = context.viewState.categories.first?.emojis.first { + context.send(viewAction: .emojiTapped(emoji: emoji)) + } + } } // MARK: - Previews