-
Notifications
You must be signed in to change notification settings - Fork 127
/
Copy pathVisualNavigator.swift
153 lines (126 loc) · 5.31 KB
/
VisualNavigator.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//
// Copyright 2025 Readium Foundation. All rights reserved.
// Use of this source code is governed by the BSD-style license
// available in the top-level LICENSE file of the project.
//
import Foundation
import ReadiumShared
import UIKit
/// A navigator rendering the publication visually on-screen.
public protocol VisualNavigator: Navigator {
/// Viewport view.
var view: UIView! { get }
/// Current presentation rendered by the navigator.
var presentation: VisualNavigatorPresentation { get }
/// Moves to the left content portion (eg. page) relative to the reading
/// progression direction.
///
/// - Parameter completion: Called when the transition is completed.
/// - Returns: Whether the navigator is able to move to the previous
/// content portion. The completion block is only called if true was
/// returned.
@discardableResult
func goLeft(options: NavigatorGoOptions) async -> Bool
/// Moves to the right content portion (eg. page) relative to the reading
/// progression direction.
///
/// - Parameter completion: Called when the transition is completed.
/// - Returns: Whether the navigator is able to move to the previous
/// content portion. The completion block is only called if true was
/// returned.
@discardableResult
func goRight(options: NavigatorGoOptions) async -> Bool
/// Returns the `Locator` to the first content element that begins on the
/// current screen.
func firstVisibleElementLocator() async -> Locator?
}
public extension VisualNavigator {
/// Current reading progression direction.
@available(*, unavailable, message: "Use `presentation.readingProgression` instead", renamed: "presentation.readingProgression")
var readingProgression: ReadiumShared.ReadingProgression { fatalError() }
}
public extension VisualNavigator {
func firstVisibleElementLocator() async -> Locator? {
currentLocation
}
@discardableResult
func goLeft(options: NavigatorGoOptions) async -> Bool {
switch presentation.readingProgression {
case .ltr:
return await goBackward(options: options)
case .rtl:
return await goForward(options: options)
}
}
@discardableResult
func goRight(options: NavigatorGoOptions) async -> Bool {
switch presentation.readingProgression {
case .ltr:
return await goForward(options: options)
case .rtl:
return await goBackward(options: options)
}
}
@available(*, unavailable, message: "Use the async variant")
func firstVisibleElementLocator(completion: @escaping (Locator?) -> Void) {
fatalError()
}
@available(*, unavailable, message: "Use the async variant")
func goLeft(animated: Bool = false, completion: @escaping () -> Void = {}) -> Bool {
fatalError()
}
@available(*, unavailable, message: "Use the async variant")
func goRight(animated: Bool = false, completion: @escaping () -> Void = {}) -> Bool {
fatalError()
}
}
public struct VisualNavigatorPresentation {
/// Horizontal direction of progression across resources.
public let readingProgression: ReadingProgression
/// If the overflow of the content is managed through scroll instead of
/// pagination.
public let scroll: Bool
/// Main axis along which the resources are laid out.
public let axis: Axis
public init(readingProgression: ReadingProgression, scroll: Bool, axis: Axis) {
self.readingProgression = readingProgression
self.scroll = scroll
self.axis = axis
}
}
@MainActor public protocol VisualNavigatorDelegate: NavigatorDelegate {
/// Called when the navigator presentation changed, for example after
/// applying a new set of preferences.
func navigator(_ navigator: Navigator, presentationDidChange presentation: VisualNavigatorPresentation)
/// Called when the user tapped the publication, and it didn't trigger any
/// internal action. The point is relative to the navigator's view.
func navigator(_ navigator: VisualNavigator, didTapAt point: CGPoint)
/// Called when the user pressed a key down and it was not handled by the
/// resource.
func navigator(_ navigator: VisualNavigator, didPressKey event: KeyEvent)
/// Called when the user released a key and it was not handled by the
/// resource.
func navigator(_ navigator: VisualNavigator, didReleaseKey event: KeyEvent)
/// Called when the user taps on an internal link.
///
/// Return `true` to navigate to the link, or `false` if you intend to
/// present the link yourself
func navigator(_ navigator: VisualNavigator, shouldNavigateToLink link: Link) -> Bool
}
public extension VisualNavigatorDelegate {
func navigator(_ navigator: Navigator, presentationDidChange presentation: VisualNavigatorPresentation) {
// Optional
}
func navigator(_ navigator: VisualNavigator, didTapAt point: CGPoint) {
// Optional
}
func navigator(_ navigator: VisualNavigator, didPressKey event: KeyEvent) {
// Optional
}
func navigator(_ navigator: VisualNavigator, didReleaseKey event: KeyEvent) {
// Optional
}
func navigator(_ navigator: VisualNavigator, shouldNavigateToLink link: Link) -> Bool {
true
}
}