Move macOS parts to viewModifiers

This commit is contained in:
Balazs Perlaki-Horvath 2025-01-19 15:16:33 +01:00 committed by BPH
parent 1cc07a85b3
commit 084d210326
4 changed files with 146 additions and 17 deletions

View File

@ -24,7 +24,7 @@ struct SearchResults: View {
@Environment(\.managedObjectContext) private var managedObjectContext
@EnvironmentObject private var viewModel: SearchViewModel
@EnvironmentObject private var navigation: NavigationViewModel
@FocusState private var focusedSearchItem: URL?
@FocusState private var focusedSearchItem: URL? // macOS only
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \ZimFile.size, ascending: false)],
predicate: ZimFile.Predicate.isDownloaded,
@ -92,23 +92,26 @@ struct SearchResults: View {
ArticleCell(result: result, zimFile: viewModel.zimFiles[result.zimFileID])
}
.buttonStyle(.plain)
.id(result.url)
.focusable()
.focused($focusedSearchItem, equals: result.url)
.modifier(KeyPressHandler(key: .return, action: {
NotificationCenter.openURL(result.url)
}))
.modifier(KeyPressHandler(key: .escape, action: {
$focusedSearchItem.wrappedValue = nil
dismissSearch()
}))
.modifier(
Focusable( // macOS only
$focusedSearchItem,
equals: result.url,
onReturn: {
NotificationCenter.openURL(result.url)
},
onDismiss: {
$focusedSearchItem.wrappedValue = nil
dismissSearch()
})
)
}
}.padding()
}
.onReceive(self.focusedSearchItem.publisher) { focusedURL in
scrollReader.scrollTo(focusedURL, anchor: .center)
}
.onMoveCommand { direction in
.modifier(MoveCommand(perform: { direction in
// macOS only
if let focusedSearchItem,
let index = viewModel.results.firstIndex(where: { $0.url == focusedSearchItem }) {
let nextIndex: Int
@ -121,7 +124,7 @@ struct SearchResults: View {
$focusedSearchItem.wrappedValue = viewModel.results[nextIndex].url
}
}
}
}))
}
}
}
@ -160,7 +163,7 @@ struct SearchResults: View {
} header: { searchFilterHeader }
}
}
.focusable(false)
.modifier(NotFocusable()) // macOS only
}
private var recentSearchHeader: some View {

View File

@ -0,0 +1,65 @@
// This file is part of Kiwix for iOS & macOS.
//
// Kiwix is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// any later version.
//
// Kiwix is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Kiwix; If not, see https://www.gnu.org/licenses/.
import SwiftUI
struct NotFocusable: ViewModifier {
func body(content: Content) -> some View {
#if os(macOS)
content.focusable(false)
#else
content
#endif
}
}
struct Focusable<Value: Hashable>: ViewModifier {
private let value: Value
private let focusState: FocusState<Value>.Binding
private let onReturn: () -> Void
private let onDissmiss: () -> Void
init(
_ binding: FocusState<Value>.Binding,
equals value: Value,
onReturn: @escaping () -> Void,
onDismiss: @escaping () -> Void
) {
self.focusState = binding
self.value = value
self.onReturn = onReturn
self.onDissmiss = onDismiss
}
func body(content: Content) -> some View {
#if os(macOS)
content
.id(value)
.focusable()
.focused(focusState, equals: value)
.modifier(KeyPressHandler(key: .return, action: {
onReturn()
}))
.modifier(KeyPressHandler(key: .escape, action: {
onDissmiss()
}))
#else
content
#endif
}
}

View File

@ -21,15 +21,19 @@ struct KeyPressHandler: ViewModifier {
let action: () -> Void
func body(content: Content) -> some View {
#if os(macOS)
if #available(macOS 14.0, *) {
mac14(content: content)
newApi(content: content)
} else {
content
}
#else
content
#endif
}
@available(macOS 14.0, *)
private func mac14(content: Content) -> some View {
@available(macOS 14.0, iOS 17.0, *)
private func newApi(content: Content) -> some View {
content.onKeyPress(key, action: {
Task { await MainActor.run {
action()

View File

@ -0,0 +1,57 @@
// This file is part of Kiwix for iOS & macOS.
//
// Kiwix is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// any later version.
//
// Kiwix is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Kiwix; If not, see https://www.gnu.org/licenses/.
import SwiftUI
enum MoveDirection: Sendable {
case up
case down
case left
case right
#if os(macOS)
init?(from direction: MoveCommandDirection) {
switch direction {
case .up: self = .up
case .down: self = .down
case .left: self = .left
case .right: self = .right
@unknown default: return nil
}
}
#endif
}
struct MoveCommand: ViewModifier {
private let action: ((MoveDirection) -> Void)?
init(perform action: ((MoveDirection) -> Void)?) {
self.action = action
}
func body(content: Content) -> some View {
#if os(macOS)
content.onMoveCommand { (direction: MoveCommandDirection) in
if let mappedDirection = MoveDirection(from: direction) {
action?(mappedDirection)
}
}
#else
content
#endif
}
}