feat(epub): complete wxread parity scope
This commit is contained in:
@@ -106,6 +106,12 @@ public final class RDEPUBReaderController: UIViewController {
|
||||
activeBookmarks
|
||||
}
|
||||
|
||||
/// 统一标注语义视图,对标 WXRead 的 WRBookmark 主模型。
|
||||
public var annotations: [RDEPUBAnnotation] {
|
||||
let merged = activeHighlights.map(\.annotation) + activeBookmarks.map(\.annotation)
|
||||
return merged.sorted { $0.createdAt < $1.createdAt }
|
||||
}
|
||||
|
||||
/// 原始目录树结构
|
||||
public var tableOfContents: [EPUBTableOfContentsItem] {
|
||||
publication?.tableOfContents ?? []
|
||||
@@ -164,6 +170,7 @@ public final class RDEPUBReaderController: UIViewController {
|
||||
private var searchState: RDEPUBSearchState?
|
||||
private var lastAppliedViewportSignature: RDEPUBViewportSignature?
|
||||
private var pendingViewportChangeReason: RDEPUBViewportChangeReason?
|
||||
private var pendingPresentationRestoreLocation: RDEPUBLocation?
|
||||
private var isWaitingForViewportTransitionCompletion = false
|
||||
/// 标记是否通过外部 TextBook 初始化(跳过 EPUB 解析流程)
|
||||
private var isExternalTextBook = false
|
||||
@@ -289,6 +296,7 @@ public final class RDEPUBReaderController: UIViewController {
|
||||
super.viewWillTransition(to: size, with: coordinator)
|
||||
|
||||
guard didStartInitialLoad else { return }
|
||||
pendingPresentationRestoreLocation = currentVisibleLocation() ?? persistenceLocation()
|
||||
isWaitingForViewportTransitionCompletion = true
|
||||
|
||||
coordinator.animate(alongsideTransition: nil) { [weak self] _ in
|
||||
@@ -315,6 +323,7 @@ public final class RDEPUBReaderController: UIViewController {
|
||||
searchState = nil
|
||||
lastAppliedViewportSignature = currentViewportSignature()
|
||||
pendingViewportChangeReason = nil
|
||||
pendingPresentationRestoreLocation = nil
|
||||
isWaitingForViewportTransitionCompletion = false
|
||||
updateCurrentSelection(nil)
|
||||
readerView.reloadData()
|
||||
@@ -587,17 +596,23 @@ public final class RDEPUBReaderController: UIViewController {
|
||||
}
|
||||
|
||||
private func applyReaderViewConfiguration() {
|
||||
let displayTypeDidChange = readerView.currentDisplayType != configuration.displayType
|
||||
let preservedLocation = displayTypeDidChange ? currentVisibleLocation() : nil
|
||||
let resolvedDirection = resolvedPageDirection()
|
||||
let presentationDidChange = readerView.currentDisplayType != configuration.displayType
|
||||
|| readerView.landscapeDualPageEnabled != configuration.landscapeDualPageEnabled
|
||||
|| readerView.pageDirection != resolvedDirection
|
||||
let preservedLocation = presentationDidChange
|
||||
? (pendingPresentationRestoreLocation ?? currentVisibleLocation() ?? persistenceLocation())
|
||||
: nil
|
||||
view.backgroundColor = configuration.theme.contentBackgroundColor
|
||||
readerView.landscapeDualPageEnabled = configuration.landscapeDualPageEnabled
|
||||
readerView.pageDirection = resolvedPageDirection()
|
||||
readerView.pageDirection = resolvedDirection
|
||||
updateReaderChrome()
|
||||
if displayTypeDidChange {
|
||||
if presentationDidChange {
|
||||
readerView.switchReaderDisplayType(configuration.displayType)
|
||||
if let preservedLocation {
|
||||
_ = restoreReadingLocation(preservedLocation)
|
||||
}
|
||||
pendingPresentationRestoreLocation = nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -789,7 +804,8 @@ public final class RDEPUBReaderController: UIViewController {
|
||||
|
||||
private func repaginatePreservingCurrentLocation() {
|
||||
guard publication != nil else { return }
|
||||
let restoreLocation = currentVisibleLocation() ?? persistenceLocation()
|
||||
let restoreLocation = pendingPresentationRestoreLocation ?? currentVisibleLocation() ?? persistenceLocation()
|
||||
pendingPresentationRestoreLocation = nil
|
||||
paginatePublication(restoreLocation: restoreLocation)
|
||||
}
|
||||
|
||||
@@ -1042,6 +1058,9 @@ public final class RDEPUBReaderController: UIViewController {
|
||||
settingsController.onLineHeightChange = { [weak self] lineHeightMultiple in
|
||||
self?.updateConfiguration { $0.lineHeightMultiple = lineHeightMultiple }
|
||||
}
|
||||
settingsController.onColumnCountChange = { [weak self] numberOfColumns in
|
||||
self?.updateConfiguration { $0.numberOfColumns = numberOfColumns }
|
||||
}
|
||||
settingsController.onDisplayTypeChange = { [weak self] displayType in
|
||||
self?.updateConfiguration { $0.displayType = displayType }
|
||||
}
|
||||
@@ -1097,6 +1116,8 @@ public final class RDEPUBReaderController: UIViewController {
|
||||
) -> Bool {
|
||||
oldConfiguration.fontSize != newConfiguration.fontSize ||
|
||||
oldConfiguration.lineHeightMultiple != newConfiguration.lineHeightMultiple ||
|
||||
oldConfiguration.numberOfColumns != newConfiguration.numberOfColumns ||
|
||||
oldConfiguration.columnGap != newConfiguration.columnGap ||
|
||||
oldConfiguration.reflowableContentInsets != newConfiguration.reflowableContentInsets ||
|
||||
oldConfiguration.fixedContentInset != newConfiguration.fixedContentInset ||
|
||||
oldConfiguration.fixedLayoutFit != newConfiguration.fixedLayoutFit ||
|
||||
@@ -1160,6 +1181,24 @@ public final class RDEPUBReaderController: UIViewController {
|
||||
return nil
|
||||
}
|
||||
|
||||
if let textBook,
|
||||
let chapterData = textBook.chapterData(
|
||||
for: currentLocation,
|
||||
resolver: publication.resourceResolver,
|
||||
bookIdentifier: currentBookIdentifier
|
||||
),
|
||||
let tocItem = chapterData.primaryTableOfContentsItem(
|
||||
from: publication.tableOfContents,
|
||||
normalizer: { publication.resourceResolver.normalizedHref($0) }
|
||||
) {
|
||||
return items.last { $0.href == tocItem.href } ?? items.last {
|
||||
guard let normalizedItemHref = publication.resourceResolver.normalizedHref($0.href.components(separatedBy: "#").first ?? $0.href) else {
|
||||
return false
|
||||
}
|
||||
return normalizedItemHref == normalizedCurrentHref
|
||||
}
|
||||
}
|
||||
|
||||
return items.last { item in
|
||||
guard let normalizedItemHref = publication.resourceResolver.normalizedHref(item.href.components(separatedBy: "#").first ?? item.href) else {
|
||||
return false
|
||||
@@ -1175,12 +1214,18 @@ public final class RDEPUBReaderController: UIViewController {
|
||||
items.flatMap { item in
|
||||
let location = RDEPUBLocation(bookIdentifier: currentBookIdentifier, href: item.href, progression: 0)
|
||||
let pageNumber: Int?
|
||||
if let textBook, let publication {
|
||||
pageNumber = textBook.pageNumber(
|
||||
for: location,
|
||||
resolver: publication.resourceResolver,
|
||||
if let textBook, let publication,
|
||||
let chapterData = textBook.chapterData(for: location, resolver: publication.resourceResolver, bookIdentifier: currentBookIdentifier) {
|
||||
let normalizedLocation = publication.resourceResolver.normalizedLocation(
|
||||
location,
|
||||
bookIdentifier: currentBookIdentifier
|
||||
)
|
||||
) ?? location
|
||||
pageNumber = chapterData.pageNumber(for: normalizedLocation)
|
||||
?? textBook.pageNumber(
|
||||
for: location,
|
||||
resolver: publication.resourceResolver,
|
||||
bookIdentifier: currentBookIdentifier
|
||||
)
|
||||
} else if let readingSession {
|
||||
pageNumber = readingSession.pageIndex(for: location, bookIdentifier: currentBookIdentifier).map { $0 + 1 }
|
||||
} else {
|
||||
@@ -1242,8 +1287,8 @@ public final class RDEPUBReaderController: UIViewController {
|
||||
frameWidth: derivedFrameSize.width,
|
||||
frameHeight: derivedFrameSize.height,
|
||||
edgeInsets: .zero,
|
||||
numberOfColumns: 1,
|
||||
columnGap: 20,
|
||||
numberOfColumns: configuration.numberOfColumns,
|
||||
columnGap: configuration.columnGap,
|
||||
avoidOrphans: true,
|
||||
avoidWidows: true,
|
||||
avoidPageBreakInsideEnabled: true,
|
||||
@@ -1646,22 +1691,12 @@ extension RDEPUBReaderController {
|
||||
|
||||
private func pageNumber(for searchMatch: RDEPUBSearchMatch) -> Int? {
|
||||
if let chapterData = textChapterData(forNormalizedHref: searchMatch.href) {
|
||||
let location = RDEPUBLocation(
|
||||
bookIdentifier: currentBookIdentifier,
|
||||
href: chapterData.href,
|
||||
progression: searchMatch.progression,
|
||||
lastProgression: searchMatch.progression,
|
||||
fragment: nil,
|
||||
rangeAnchor: searchMatch.rangeAnchor
|
||||
)
|
||||
if let pageNumber = chapterData.pageNumber(for: location) {
|
||||
if let pageNumber = chapterData.pageNumber(for: searchMatch) {
|
||||
return pageNumber
|
||||
}
|
||||
|
||||
if let rangeLocation = searchMatch.rangeLocation,
|
||||
let page = chapterData.pages.first(where: {
|
||||
rangeLocation >= $0.pageStartOffset && rangeLocation <= $0.pageEndOffset
|
||||
}) {
|
||||
let page = chapterData.page(containing: rangeLocation) {
|
||||
return page.absolutePageIndex + 1
|
||||
}
|
||||
}
|
||||
@@ -1822,6 +1857,7 @@ extension RDEPUBReaderController: RDReaderDataSource, RDReaderDelegate {
|
||||
|
||||
public func readerViewOrientationWillChange(readerView: RDReaderView, isLandscape: Bool) {
|
||||
_ = isLandscape
|
||||
pendingPresentationRestoreLocation = currentVisibleLocation() ?? persistenceLocation()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1958,7 +1994,7 @@ extension RDEPUBReaderController: RDEPUBTextContentViewDelegate {
|
||||
return nil
|
||||
}
|
||||
|
||||
let location = textBook.chapterData(for: page.href)?.location(forPage: page, bookIdentifier: currentBookIdentifier)
|
||||
let location = textBook.chapterData(forPageNumber: pageNumber)?.location(forPage: page, bookIdentifier: currentBookIdentifier)
|
||||
?? textBook.location(forPageNumber: pageNumber, bookIdentifier: currentBookIdentifier)
|
||||
guard let location else { return nil }
|
||||
|
||||
@@ -1986,13 +2022,7 @@ extension RDEPUBReaderController: RDEPUBTextContentViewDelegate {
|
||||
}
|
||||
|
||||
private func nativeTextSnapshot(from textBook: RDEPUBTextBook) -> NativeTextSnapshot {
|
||||
let chapters = textBook.chapters.map {
|
||||
EPUBChapterInfo(
|
||||
spineIndex: $0.spineIndex,
|
||||
title: $0.title,
|
||||
pageCount: $0.pages.count
|
||||
)
|
||||
}
|
||||
let chapters = textBook.chapterInfos
|
||||
let pages = textBook.pages.map {
|
||||
EPUBPage(
|
||||
spineIndex: $0.spineIndex,
|
||||
|
||||
Reference in New Issue
Block a user