mirror of
https://github.com/kiwix/kiwix-apple.git
synced 2025-09-22 19:19:22 -04:00
Move macOS parts to viewModifiers
This commit is contained in:
parent
1cc07a85b3
commit
084d210326
@ -24,7 +24,7 @@ struct SearchResults: View {
|
|||||||
@Environment(\.managedObjectContext) private var managedObjectContext
|
@Environment(\.managedObjectContext) private var managedObjectContext
|
||||||
@EnvironmentObject private var viewModel: SearchViewModel
|
@EnvironmentObject private var viewModel: SearchViewModel
|
||||||
@EnvironmentObject private var navigation: NavigationViewModel
|
@EnvironmentObject private var navigation: NavigationViewModel
|
||||||
@FocusState private var focusedSearchItem: URL?
|
@FocusState private var focusedSearchItem: URL? // macOS only
|
||||||
@FetchRequest(
|
@FetchRequest(
|
||||||
sortDescriptors: [NSSortDescriptor(keyPath: \ZimFile.size, ascending: false)],
|
sortDescriptors: [NSSortDescriptor(keyPath: \ZimFile.size, ascending: false)],
|
||||||
predicate: ZimFile.Predicate.isDownloaded,
|
predicate: ZimFile.Predicate.isDownloaded,
|
||||||
@ -92,23 +92,26 @@ struct SearchResults: View {
|
|||||||
ArticleCell(result: result, zimFile: viewModel.zimFiles[result.zimFileID])
|
ArticleCell(result: result, zimFile: viewModel.zimFiles[result.zimFileID])
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
.id(result.url)
|
.modifier(
|
||||||
.focusable()
|
Focusable( // macOS only
|
||||||
.focused($focusedSearchItem, equals: result.url)
|
$focusedSearchItem,
|
||||||
.modifier(KeyPressHandler(key: .return, action: {
|
equals: result.url,
|
||||||
NotificationCenter.openURL(result.url)
|
onReturn: {
|
||||||
}))
|
NotificationCenter.openURL(result.url)
|
||||||
.modifier(KeyPressHandler(key: .escape, action: {
|
},
|
||||||
$focusedSearchItem.wrappedValue = nil
|
onDismiss: {
|
||||||
dismissSearch()
|
$focusedSearchItem.wrappedValue = nil
|
||||||
}))
|
dismissSearch()
|
||||||
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}.padding()
|
}.padding()
|
||||||
}
|
}
|
||||||
.onReceive(self.focusedSearchItem.publisher) { focusedURL in
|
.onReceive(self.focusedSearchItem.publisher) { focusedURL in
|
||||||
scrollReader.scrollTo(focusedURL, anchor: .center)
|
scrollReader.scrollTo(focusedURL, anchor: .center)
|
||||||
}
|
}
|
||||||
.onMoveCommand { direction in
|
.modifier(MoveCommand(perform: { direction in
|
||||||
|
// macOS only
|
||||||
if let focusedSearchItem,
|
if let focusedSearchItem,
|
||||||
let index = viewModel.results.firstIndex(where: { $0.url == focusedSearchItem }) {
|
let index = viewModel.results.firstIndex(where: { $0.url == focusedSearchItem }) {
|
||||||
let nextIndex: Int
|
let nextIndex: Int
|
||||||
@ -121,7 +124,7 @@ struct SearchResults: View {
|
|||||||
$focusedSearchItem.wrappedValue = viewModel.results[nextIndex].url
|
$focusedSearchItem.wrappedValue = viewModel.results[nextIndex].url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -160,7 +163,7 @@ struct SearchResults: View {
|
|||||||
} header: { searchFilterHeader }
|
} header: { searchFilterHeader }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.focusable(false)
|
.modifier(NotFocusable()) // macOS only
|
||||||
}
|
}
|
||||||
|
|
||||||
private var recentSearchHeader: some View {
|
private var recentSearchHeader: some View {
|
||||||
|
65
Views/ViewModifiers/Focusable.swift
Normal file
65
Views/ViewModifiers/Focusable.swift
Normal 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
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -21,15 +21,19 @@ struct KeyPressHandler: ViewModifier {
|
|||||||
let action: () -> Void
|
let action: () -> Void
|
||||||
|
|
||||||
func body(content: Content) -> some View {
|
func body(content: Content) -> some View {
|
||||||
|
#if os(macOS)
|
||||||
if #available(macOS 14.0, *) {
|
if #available(macOS 14.0, *) {
|
||||||
mac14(content: content)
|
newApi(content: content)
|
||||||
} else {
|
} else {
|
||||||
content
|
content
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
content
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(macOS 14.0, *)
|
@available(macOS 14.0, iOS 17.0, *)
|
||||||
private func mac14(content: Content) -> some View {
|
private func newApi(content: Content) -> some View {
|
||||||
content.onKeyPress(key, action: {
|
content.onKeyPress(key, action: {
|
||||||
Task { await MainActor.run {
|
Task { await MainActor.run {
|
||||||
action()
|
action()
|
||||||
|
57
Views/ViewModifiers/MoveCommand.swift
Normal file
57
Views/ViewModifiers/MoveCommand.swift
Normal 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
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user