@@ -13,15 +13,18 @@ import Foundation
13
13
/// This takes into account the reading progression of the navigator to turn
14
14
/// pages in the right direction.
15
15
public final class DirectionalNavigationAdapter {
16
- /// Indicates which viewport edges trigger page turns on tap.
17
- public struct TapEdges : OptionSet {
16
+ @available ( * , deprecated, renamed: " Edges " )
17
+ public typealias TapEdges = Edges
18
+
19
+ /// Indicates which viewport edges trigger page turns on pointer activation.
20
+ public struct Edges : OptionSet {
18
21
/// The user can turn pages when tapping on the edges of both the
19
22
/// horizontal and vertical axes.
20
- public static let all : TapEdges = [ . horizontal, . vertical]
23
+ public static let all : Edges = [ . horizontal, . vertical]
21
24
/// The user can turn pages when tapping on the left and right edges.
22
- public static let horizontal = TapEdges ( rawValue: 1 << 0 )
25
+ public static let horizontal = Edges ( rawValue: 1 << 0 )
23
26
/// The user can turn pages when tapping on the top and bottom edges.
24
- public static let vertical = TapEdges ( rawValue: 1 << 1 )
27
+ public static let vertical = Edges ( rawValue: 1 << 1 )
25
28
26
29
public var rawValue : Int
27
30
@@ -30,62 +33,115 @@ public final class DirectionalNavigationAdapter {
30
33
}
31
34
}
32
35
33
- private let tapEdges : TapEdges
34
- private let handleTapsWhileScrolling : Bool
35
- private let minimumHorizontalEdgeSize : Double
36
- private let horizontalEdgeThresholdPercent : Double ?
37
- private let minimumVerticalEdgeSize : Double
38
- private let verticalEdgeThresholdPercent : Double ?
36
+ public struct PointerPolicy {
37
+ /// The types of pointer that will trigger page turns.
38
+ public var types : [ PointerType ]
39
+
40
+ /// Indicates which viewport edges recognize pointer activation.
41
+ public var edges : Edges
42
+
43
+ /// Indicates whether to ignore pointer events when the publication is
44
+ /// scrollable.
45
+ public var ignoreWhileScrolling : Bool
46
+
47
+ /// The minimum horizontal edge dimension that triggers page turns, in
48
+ /// pixels.
49
+ public var minimumHorizontalEdgeSize : Double
50
+
51
+ /// The percentage of the viewport dimension used to calculate the
52
+ /// horizontal edge size. If it is nil, a fixed edge of
53
+ /// `minimumHorizontalEdgeSize` will be used instead.
54
+ public var horizontalEdgeThresholdPercent : Double ?
55
+
56
+ /// The minimum vertical edge dimension that triggers page turns, in
57
+ /// pixels.
58
+ public var minimumVerticalEdgeSize : Double
59
+
60
+ /// The percentage of the viewport dimension used to calculate the
61
+ /// vertical edge size. If it is nil, a fixed edge of
62
+ /// `minimumVerticalEdgeSize` will be used instead.
63
+ public var verticalEdgeThresholdPercent : Double ?
64
+
65
+ public init (
66
+ types: [ PointerType ] = [ . touch, . mouse] ,
67
+ edges: Edges = . horizontal,
68
+ ignoreWhileScrolling: Bool = true ,
69
+ minimumHorizontalEdgeSize: Double = 80.0 ,
70
+ horizontalEdgeThresholdPercent: Double ? = 0.3 ,
71
+ minimumVerticalEdgeSize: Double = 80.0 ,
72
+ verticalEdgeThresholdPercent: Double ? = 0.3
73
+ ) {
74
+ self . types = types
75
+ self . edges = edges
76
+ self . ignoreWhileScrolling = ignoreWhileScrolling
77
+ self . minimumHorizontalEdgeSize = minimumHorizontalEdgeSize
78
+ self . horizontalEdgeThresholdPercent = horizontalEdgeThresholdPercent
79
+ self . minimumVerticalEdgeSize = minimumVerticalEdgeSize
80
+ self . verticalEdgeThresholdPercent = verticalEdgeThresholdPercent
81
+ }
82
+ }
83
+
84
+ public struct KeyboardPolicy {
85
+ /// Indicates whether arrow keys should turn pages.
86
+ public var handleArrowKeys : Bool
87
+
88
+ /// Indicates whether the space key should turn the page forward.
89
+ public var handleSpaceKey : Bool
90
+
91
+ public init (
92
+ handleArrowKeys: Bool = true ,
93
+ handleSpaceKey: Bool = true
94
+ ) {
95
+ self . handleArrowKeys = handleArrowKeys
96
+ self . handleSpaceKey = handleSpaceKey
97
+ }
98
+ }
99
+
100
+ private let pointerPolicy : PointerPolicy
101
+ private let keyboardPolicy : KeyboardPolicy
39
102
private let animatedTransition : Bool
40
103
41
104
private weak var navigator : VisualNavigator ?
42
105
43
106
/// Initializes a new `DirectionalNavigationAdapter`.
44
107
///
45
108
/// - Parameters:
46
- /// - tapEdges: Indicates which viewport edges handle taps.
47
- /// - handleTapsWhileScrolling: Indicates whether the page turns should be
48
- /// handled when the publication is scrollable.
49
- /// - minimumHorizontalEdgeSize: The minimum horizontal edge dimension
50
- /// triggering page turns, in pixels.
51
- /// - horizontalEdgeThresholdPercent: The percentage of the viewport
52
- /// dimension used to compute the horizontal edge size. When null,
53
- /// `minimumHorizontalEdgeSize` will be used instead.
54
- /// - minimumVerticalEdgeSize: The minimum vertical edge dimension
55
- /// triggering page turns, in pixels.
56
- /// - verticalEdgeThresholdPercent: The percentage of the viewport
57
- /// dimension used to compute the vertical edge size. When null,
58
- /// `minimumVerticalEdgeSize` will be used instead.
109
+ /// - pointerPolicy: Policy on page turns using pointers (touches, mouse).
110
+ /// - keyboardPolicy: Policy on page turns using the keyboard.
59
111
/// - animatedTransition: Indicates whether the page turns should be
60
112
/// animated.
61
113
public init (
62
- tapEdges: TapEdges = . horizontal,
63
- handleTapsWhileScrolling: Bool = false ,
64
- minimumHorizontalEdgeSize: Double = 80.0 ,
65
- horizontalEdgeThresholdPercent: Double ? = 0.3 ,
66
- minimumVerticalEdgeSize: Double = 80.0 ,
67
- verticalEdgeThresholdPercent: Double ? = 0.3 ,
114
+ pointerPolicy: PointerPolicy = PointerPolicy ( ) ,
115
+ keyboardPolicy: KeyboardPolicy = KeyboardPolicy ( ) ,
68
116
animatedTransition: Bool = false
69
117
) {
70
- self . tapEdges = tapEdges
71
- self . handleTapsWhileScrolling = handleTapsWhileScrolling
72
- self . minimumHorizontalEdgeSize = minimumHorizontalEdgeSize
73
- self . horizontalEdgeThresholdPercent = horizontalEdgeThresholdPercent
74
- self . minimumVerticalEdgeSize = minimumVerticalEdgeSize
75
- self . verticalEdgeThresholdPercent = verticalEdgeThresholdPercent
118
+ self . pointerPolicy = pointerPolicy
119
+ self . keyboardPolicy = keyboardPolicy
76
120
self . animatedTransition = animatedTransition
77
121
}
78
122
79
123
/// Binds the adapter to the given visual navigator.
80
124
///
81
125
/// It will automatically observe pointer and key events to turn pages.
82
126
@MainActor public func bind( to navigator: VisualNavigator ) {
83
- navigator. addObserver ( . tap { [ self , weak navigator] event in
84
- guard let navigator = navigator else {
85
- return false
127
+ for pointerType in PointerType . allCases {
128
+ switch pointerType {
129
+ case . touch:
130
+ navigator. addObserver ( . tap { [ self , weak navigator] event in
131
+ guard let navigator = navigator else {
132
+ return false
133
+ }
134
+ return await onTap ( at: event. location, in: navigator)
135
+ } )
136
+ case . mouse:
137
+ navigator. addObserver ( . click { [ self , weak navigator] event in
138
+ guard let navigator = navigator else {
139
+ return false
140
+ }
141
+ return await onTap ( at: event. location, in: navigator)
142
+ } )
86
143
}
87
- return await onTap ( at: event. location, in: navigator)
88
- } )
144
+ }
89
145
90
146
navigator. addObserver ( . key { [ self , weak navigator] event in
91
147
guard let navigator = navigator else {
@@ -97,17 +153,17 @@ public final class DirectionalNavigationAdapter {
97
153
98
154
@MainActor
99
155
private func onTap( at point: CGPoint , in navigator: VisualNavigator ) async -> Bool {
100
- guard handleTapsWhileScrolling || !navigator. presentation. scroll else {
156
+ guard !pointerPolicy . ignoreWhileScrolling || !navigator. presentation. scroll else {
101
157
return false
102
158
}
103
159
104
160
let bounds = navigator. view. bounds
105
161
let options = NavigatorGoOptions ( animated: animatedTransition)
106
162
107
- if tapEdges . contains ( . horizontal) {
108
- let horizontalEdgeSize = horizontalEdgeThresholdPercent
109
- . map { max ( minimumHorizontalEdgeSize, $0 * bounds. width) }
110
- ?? minimumHorizontalEdgeSize
163
+ if pointerPolicy . edges . contains ( . horizontal) {
164
+ let horizontalEdgeSize = pointerPolicy . horizontalEdgeThresholdPercent
165
+ . map { max ( pointerPolicy . minimumHorizontalEdgeSize, $0 * bounds. width) }
166
+ ?? pointerPolicy . minimumHorizontalEdgeSize
111
167
let leftRange = 0.0 ... horizontalEdgeSize
112
168
let rightRange = ( bounds. width - horizontalEdgeSize) ... bounds. width
113
169
@@ -118,10 +174,10 @@ public final class DirectionalNavigationAdapter {
118
174
}
119
175
}
120
176
121
- if tapEdges . contains ( . vertical) {
122
- let verticalEdgeSize = verticalEdgeThresholdPercent
123
- . map { max ( minimumVerticalEdgeSize, $0 * bounds. height) }
124
- ?? minimumVerticalEdgeSize
177
+ if pointerPolicy . edges . contains ( . vertical) {
178
+ let verticalEdgeSize = pointerPolicy . verticalEdgeThresholdPercent
179
+ . map { max ( pointerPolicy . minimumVerticalEdgeSize, $0 * bounds. height) }
180
+ ?? pointerPolicy . minimumVerticalEdgeSize
125
181
let topRange = 0.0 ... verticalEdgeSize
126
182
let bottomRange = ( bounds. height - verticalEdgeSize) ... bounds. height
127
183
@@ -143,23 +199,25 @@ public final class DirectionalNavigationAdapter {
143
199
let options = NavigatorGoOptions ( animated: animatedTransition)
144
200
145
201
switch event. key {
146
- case . arrowUp:
202
+ case . arrowUp where keyboardPolicy . handleArrowKeys :
147
203
return await navigator. goBackward ( options: options)
148
- case . arrowDown, . space :
204
+ case . arrowDown where keyboardPolicy . handleArrowKeys :
149
205
return await navigator. goForward ( options: options)
150
- case . arrowLeft:
206
+ case . arrowLeft where keyboardPolicy . handleArrowKeys :
151
207
return await navigator. goLeft ( options: options)
152
- case . arrowRight:
208
+ case . arrowRight where keyboardPolicy . handleArrowKeys :
153
209
return await navigator. goRight ( options: options)
210
+ case . space where keyboardPolicy. handleSpaceKey:
211
+ return await navigator. goForward ( options: options)
154
212
default :
155
213
return false
156
214
}
157
215
}
158
216
159
- @available ( * , deprecated, message: " Use the initializer without the navigator parameter and call `bind(to:)`. See the migration guide. " )
217
+ @available ( * , deprecated, message: " Use the new initializer without the navigator parameter and call `bind(to:)`. See the migration guide. " )
160
218
public init (
161
219
navigator: VisualNavigator ,
162
- tapEdges: TapEdges = . horizontal,
220
+ tapEdges: Edges = . horizontal,
163
221
handleTapsWhileScrolling: Bool = false ,
164
222
minimumHorizontalEdgeSize: Double = 80.0 ,
165
223
horizontalEdgeThresholdPercent: Double ? = 0.3 ,
@@ -168,12 +226,15 @@ public final class DirectionalNavigationAdapter {
168
226
animatedTransition: Bool = false
169
227
) {
170
228
self . navigator = navigator
171
- self . tapEdges = tapEdges
172
- self . handleTapsWhileScrolling = handleTapsWhileScrolling
173
- self . minimumHorizontalEdgeSize = minimumHorizontalEdgeSize
174
- self . horizontalEdgeThresholdPercent = horizontalEdgeThresholdPercent
175
- self . minimumVerticalEdgeSize = minimumVerticalEdgeSize
176
- self . verticalEdgeThresholdPercent = verticalEdgeThresholdPercent
229
+ pointerPolicy = PointerPolicy (
230
+ types: [ . touch, . mouse] ,
231
+ ignoreWhileScrolling: !handleTapsWhileScrolling,
232
+ minimumHorizontalEdgeSize: minimumHorizontalEdgeSize,
233
+ horizontalEdgeThresholdPercent: horizontalEdgeThresholdPercent,
234
+ minimumVerticalEdgeSize: minimumVerticalEdgeSize,
235
+ verticalEdgeThresholdPercent: verticalEdgeThresholdPercent
236
+ )
237
+ keyboardPolicy = KeyboardPolicy ( )
177
238
self . animatedTransition = animatedTransition
178
239
}
179
240
0 commit comments