Skip to content

Commit d901d74

Browse files
authored
fix: restore epub overlay transitions (#684)
## Problem The EPUB reader regressed after the shared overlay refactor. Hidden overlay entries started clearing their label text before fading out, which made iOS transitions feel abrupt, and macOS EPUB no longer exposed the same overlay toggle behavior as Divina. ## Approach Restore the overlay model so hidden entries keep their last rendered text while only visibility changes animate. On macOS, animate controls-mode switches in the shared AppKit overlay and route `Space` / `C` through the EPUB reader's overlay toggle path so keyboard behavior matches Divina. ## Scope - Preserve overlay label text while hidden in the shared EPUB overlay support - Restore smooth iOS overlay transitions after controls toggles - Add animated overlay switching on macOS EPUB - Align macOS EPUB keyboard shortcuts and help text with Divina for overlay toggling
1 parent a345f89 commit d901d74

4 files changed

Lines changed: 113 additions & 30 deletions

File tree

KMReader/Features/Reader/Views/Epub/EpubReaderView.swift

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,18 @@
120120
private var supportsOverlayControls: Bool {
121121
false
122122
}
123+
124+
private var supportsKeyboardOverlayToggle: Bool {
125+
true
126+
}
123127
#else
124128
private var supportsOverlayControls: Bool {
125129
true
126130
}
131+
132+
private var supportsKeyboardOverlayToggle: Bool {
133+
supportsOverlayControls
134+
}
127135
#endif
128136

129137
private func updateHandoff() {
@@ -726,7 +734,7 @@
726734
hasTOC: !viewModel.tableOfContents.isEmpty,
727735
supportsLiveText: false,
728736
supportsJumpToPage: false,
729-
supportsToggleControls: supportsOverlayControls,
737+
supportsToggleControls: supportsKeyboardOverlayToggle,
730738
hasNextBook: false,
731739
onDismiss: {
732740
hideKeyboardHelp()
@@ -771,24 +779,42 @@
771779
return true
772780
}
773781

774-
if keyCode == 44 || keyCode == 4 {
782+
if keyCode == 44 {
775783
showKeyboardHelp.toggle()
776784
return true
777785
}
778786

779-
if keyCode == 36 || keyCode == 3 {
787+
if keyCode == 36 {
780788
if let window = NSApplication.shared.keyWindow {
781789
window.toggleFullScreen(nil)
782790
}
783791
return true
784792
}
785793

786-
if keyCode == 49 || keyCode == 8 {
794+
if keyCode == 49 {
795+
toggleControls()
787796
return true
788797
}
789798

790799
guard flags.intersection([.command, .option, .control]).isEmpty else { return false }
791800

801+
if keyCode == 3 {
802+
if let window = NSApplication.shared.keyWindow {
803+
window.toggleFullScreen(nil)
804+
}
805+
return true
806+
}
807+
808+
if keyCode == 4 {
809+
showKeyboardHelp.toggle()
810+
return true
811+
}
812+
813+
if keyCode == 8 {
814+
toggleControls()
815+
return true
816+
}
817+
792818
if keyCode == 17 {
793819
if !viewModel.tableOfContents.isEmpty {
794820
showingChapterSheet = true

KMReader/Features/Reader/Views/Epub/WebPubInfoOverlaySupport.swift

Lines changed: 81 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@
1414
case scrolled
1515
}
1616

17-
struct Entry {
18-
let text: String
17+
struct Entry: Equatable {
18+
let text: String?
1919
let isVisible: Bool
2020

21-
static let hidden = Entry(text: "", isVisible: false)
21+
static let hidden = Entry(text: nil, isVisible: false)
2222
}
2323

24-
struct Content {
24+
struct Content: Equatable {
25+
let showingControls: Bool
2526
let topTitle: Entry
2627
let topProgress: Entry
2728
let bottomLeading: Entry
@@ -58,6 +59,7 @@
5859

5960
guard totalPagesInChapter > 0 else {
6061
return Content(
62+
showingControls: showingControls,
6163
topTitle: topTitle,
6264
topProgress: topProgress,
6365
bottomLeading: .hidden,
@@ -68,6 +70,7 @@
6870

6971
if showingControls {
7072
return Content(
73+
showingControls: showingControls,
7174
topTitle: topTitle,
7275
topProgress: topProgress,
7376
bottomLeading: .hidden,
@@ -81,6 +84,7 @@
8184
}
8285

8386
return Content(
87+
showingControls: showingControls,
8488
topTitle: topTitle,
8589
topProgress: topProgress,
8690
bottomLeading: visibleEntry(chapterTitle),
@@ -162,6 +166,14 @@
162166
private let bottomLeadingLabel: UILabel
163167
private let bottomCenterLabel: UILabel
164168
private let bottomTrailingLabel: UILabel
169+
private var currentContent = Content(
170+
showingControls: false,
171+
topTitle: .hidden,
172+
topProgress: .hidden,
173+
bottomLeading: .hidden,
174+
bottomCenter: .hidden,
175+
bottomTrailing: .hidden
176+
)
165177

166178
init(
167179
containerView: UIView,
@@ -211,24 +223,43 @@
211223
}
212224

213225
func update(content: Content, animated: Bool) {
226+
guard content != currentContent else { return }
227+
currentContent = content
214228
let updates = {
215-
self.apply(entry: content.topTitle, to: self.topTitleLabel)
216-
self.apply(entry: content.topProgress, to: self.topProgressLabel)
217-
self.apply(entry: content.bottomLeading, to: self.bottomLeadingLabel)
218-
self.apply(entry: content.bottomCenter, to: self.bottomCenterLabel)
219-
self.apply(entry: content.bottomTrailing, to: self.bottomTrailingLabel)
229+
if let text = content.topTitle.text {
230+
self.topTitleLabel.text = text
231+
}
232+
self.topTitleLabel.alpha = content.topTitle.isVisible ? 1.0 : 0.0
233+
234+
if let text = content.topProgress.text {
235+
self.topProgressLabel.text = text
236+
}
237+
self.topProgressLabel.alpha = content.topProgress.isVisible ? 1.0 : 0.0
238+
239+
if let text = content.bottomLeading.text {
240+
self.bottomLeadingLabel.text = text
241+
}
242+
self.bottomLeadingLabel.alpha = content.bottomLeading.isVisible ? 1.0 : 0.0
243+
244+
if let text = content.bottomCenter.text {
245+
self.bottomCenterLabel.text = text
246+
}
247+
self.bottomCenterLabel.alpha = content.bottomCenter.isVisible ? 1.0 : 0.0
248+
249+
if let text = content.bottomTrailing.text {
250+
self.bottomTrailingLabel.text = text
251+
}
252+
self.bottomTrailingLabel.alpha = content.bottomTrailing.isVisible ? 1.0 : 0.0
220253
}
221254

222-
if animated {
223-
UIView.animate(withDuration: 0.2, animations: updates)
224-
} else {
255+
guard animated else {
225256
updates()
257+
return
226258
}
227-
}
228259

229-
private func apply(entry: Entry, to label: UILabel) {
230-
label.text = entry.text
231-
label.alpha = entry.isVisible ? 1.0 : 0.0
260+
UIView.animate {
261+
updates()
262+
}
232263
}
233264

234265
private static func makeLabel(
@@ -255,6 +286,14 @@
255286
private let bottomLeadingLabel: NSTextField
256287
private let bottomCenterLabel: NSTextField
257288
private let bottomTrailingLabel: NSTextField
289+
private var currentContent = Content(
290+
showingControls: false,
291+
topTitle: .hidden,
292+
topProgress: .hidden,
293+
bottomLeading: .hidden,
294+
bottomCenter: .hidden,
295+
bottomTrailing: .hidden
296+
)
258297

259298
init(
260299
containerView: NSView,
@@ -301,17 +340,35 @@
301340
.forEach { $0.textColor = labelColor }
302341
}
303342

304-
func update(content: Content) {
305-
apply(entry: content.topTitle, to: topTitleLabel)
306-
apply(entry: content.topProgress, to: topProgressLabel)
307-
apply(entry: content.bottomLeading, to: bottomLeadingLabel)
308-
apply(entry: content.bottomCenter, to: bottomCenterLabel)
309-
apply(entry: content.bottomTrailing, to: bottomTrailingLabel)
343+
func update(content: Content, animated: Bool) {
344+
guard content != currentContent else { return }
345+
let isControlsTransition = currentContent.showingControls != content.showingControls
346+
currentContent = content
347+
let updates = {
348+
self.apply(entry: content.topTitle, to: self.topTitleLabel)
349+
self.apply(entry: content.topProgress, to: self.topProgressLabel)
350+
self.apply(entry: content.bottomLeading, to: self.bottomLeadingLabel)
351+
self.apply(entry: content.bottomCenter, to: self.bottomCenterLabel)
352+
self.apply(entry: content.bottomTrailing, to: self.bottomTrailingLabel)
353+
}
354+
355+
guard animated, isControlsTransition else {
356+
updates()
357+
return
358+
}
359+
360+
NSAnimationContext.runAnimationGroup { context in
361+
context.duration = 0.2
362+
context.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
363+
updates()
364+
}
310365
}
311366

312367
private func apply(entry: Entry, to label: NSTextField) {
313-
label.stringValue = entry.text
314-
label.alphaValue = entry.isVisible ? 1.0 : 0.0
368+
if let text = entry.text {
369+
label.stringValue = text
370+
}
371+
label.animator().alphaValue = entry.isVisible ? 1.0 : 0.0
315372
}
316373

317374
private static func makeLabel(

KMReader/Features/Reader/Views/Epub/WebPubPagedScrollView_macOS.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@
205205
totalPagesInChapter: totalPagesInChapter,
206206
showingControls: parent.showingControls
207207
)
208-
infoOverlay?.update(content: content)
208+
infoOverlay?.update(content: content, animated: true)
209209
}
210210

211211
private func applyPagination(on webView: WKWebView, targetPageIndex: Int) {

KMReader/Features/Reader/Views/Epub/WebPubScrolledView_macOS.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@
211211
totalPagesInChapter: totalPagesInChapter,
212212
showingControls: parent.showingControls
213213
)
214-
infoOverlay?.update(content: content)
214+
infoOverlay?.update(content: content, animated: true)
215215
}
216216

217217
private func applyPagination(on webView: WKWebView, targetPageIndex: Int) {

0 commit comments

Comments
 (0)