@@ -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,119 @@ 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
+ guard pointerPolicy . types . contains ( pointerType ) else {
129
+ continue
86
130
}
87
- return await onTap ( at: event. location, in: navigator)
88
- } )
131
+
132
+ switch pointerType {
133
+ case . touch:
134
+ navigator. addObserver ( . tap { [ self , weak navigator] event in
135
+ guard let navigator = navigator else {
136
+ return false
137
+ }
138
+ return await onTap ( at: event. location, in: navigator)
139
+ } )
140
+ case . mouse:
141
+ navigator. addObserver ( . click { [ self , weak navigator] event in
142
+ guard let navigator = navigator else {
143
+ return false
144
+ }
145
+ return await onTap ( at: event. location, in: navigator)
146
+ } )
147
+ }
148
+ }
89
149
90
150
navigator. addObserver ( . key { [ self , weak navigator] event in
91
151
guard let navigator = navigator else {
@@ -97,17 +157,17 @@ public final class DirectionalNavigationAdapter {
97
157
98
158
@MainActor
99
159
private func onTap( at point: CGPoint , in navigator: VisualNavigator ) async -> Bool {
100
- guard handleTapsWhileScrolling || !navigator. presentation. scroll else {
160
+ guard !pointerPolicy . ignoreWhileScrolling || !navigator. presentation. scroll else {
101
161
return false
102
162
}
103
163
104
164
let bounds = navigator. view. bounds
105
165
let options = NavigatorGoOptions ( animated: animatedTransition)
106
166
107
- if tapEdges . contains ( . horizontal) {
108
- let horizontalEdgeSize = horizontalEdgeThresholdPercent
109
- . map { max ( minimumHorizontalEdgeSize, $0 * bounds. width) }
110
- ?? minimumHorizontalEdgeSize
167
+ if pointerPolicy . edges . contains ( . horizontal) {
168
+ let horizontalEdgeSize = pointerPolicy . horizontalEdgeThresholdPercent
169
+ . map { max ( pointerPolicy . minimumHorizontalEdgeSize, $0 * bounds. width) }
170
+ ?? pointerPolicy . minimumHorizontalEdgeSize
111
171
let leftRange = 0.0 ... horizontalEdgeSize
112
172
let rightRange = ( bounds. width - horizontalEdgeSize) ... bounds. width
113
173
@@ -118,10 +178,10 @@ public final class DirectionalNavigationAdapter {
118
178
}
119
179
}
120
180
121
- if tapEdges . contains ( . vertical) {
122
- let verticalEdgeSize = verticalEdgeThresholdPercent
123
- . map { max ( minimumVerticalEdgeSize, $0 * bounds. height) }
124
- ?? minimumVerticalEdgeSize
181
+ if pointerPolicy . edges . contains ( . vertical) {
182
+ let verticalEdgeSize = pointerPolicy . verticalEdgeThresholdPercent
183
+ . map { max ( pointerPolicy . minimumVerticalEdgeSize, $0 * bounds. height) }
184
+ ?? pointerPolicy . minimumVerticalEdgeSize
125
185
let topRange = 0.0 ... verticalEdgeSize
126
186
let bottomRange = ( bounds. height - verticalEdgeSize) ... bounds. height
127
187
@@ -143,23 +203,25 @@ public final class DirectionalNavigationAdapter {
143
203
let options = NavigatorGoOptions ( animated: animatedTransition)
144
204
145
205
switch event. key {
146
- case . arrowUp:
206
+ case . arrowUp where keyboardPolicy . handleArrowKeys :
147
207
return await navigator. goBackward ( options: options)
148
- case . arrowDown, . space :
208
+ case . arrowDown where keyboardPolicy . handleArrowKeys :
149
209
return await navigator. goForward ( options: options)
150
- case . arrowLeft:
210
+ case . arrowLeft where keyboardPolicy . handleArrowKeys :
151
211
return await navigator. goLeft ( options: options)
152
- case . arrowRight:
212
+ case . arrowRight where keyboardPolicy . handleArrowKeys :
153
213
return await navigator. goRight ( options: options)
214
+ case . space where keyboardPolicy. handleSpaceKey:
215
+ return await navigator. goForward ( options: options)
154
216
default :
155
217
return false
156
218
}
157
219
}
158
220
159
- @available ( * , deprecated, message: " Use the initializer without the navigator parameter and call `bind(to:)`. See the migration guide. " )
221
+ @available ( * , deprecated, message: " Use the new initializer without the navigator parameter and call `bind(to:)`. See the migration guide. " )
160
222
public init (
161
223
navigator: VisualNavigator ,
162
- tapEdges: TapEdges = . horizontal,
224
+ tapEdges: Edges = . horizontal,
163
225
handleTapsWhileScrolling: Bool = false ,
164
226
minimumHorizontalEdgeSize: Double = 80.0 ,
165
227
horizontalEdgeThresholdPercent: Double ? = 0.3 ,
@@ -168,12 +230,15 @@ public final class DirectionalNavigationAdapter {
168
230
animatedTransition: Bool = false
169
231
) {
170
232
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
233
+ pointerPolicy = PointerPolicy (
234
+ types: [ . touch, . mouse] ,
235
+ ignoreWhileScrolling: !handleTapsWhileScrolling,
236
+ minimumHorizontalEdgeSize: minimumHorizontalEdgeSize,
237
+ horizontalEdgeThresholdPercent: horizontalEdgeThresholdPercent,
238
+ minimumVerticalEdgeSize: minimumVerticalEdgeSize,
239
+ verticalEdgeThresholdPercent: verticalEdgeThresholdPercent
240
+ )
241
+ keyboardPolicy = KeyboardPolicy ( )
177
242
self . animatedTransition = animatedTransition
178
243
}
179
244
0 commit comments