feat: EPUB阅读器搜索、注释、CFI模块及大书远距跳转优化
- 实现EPUB阅读器搜索功能及选中注释功能 - 优化CFI模块,修复代码审查发现的11个问题 - 实现大书远距目录跳转与后台补全优化方案 - 优化设置面板与章节运行时联动 - 重构及大量改进优化
This commit is contained in:
+11
-27
@@ -1,33 +1,31 @@
|
||||
import Foundation
|
||||
|
||||
/// 页图接管决策
|
||||
enum RDEPUBPageMapTakeoverDecision {
|
||||
/// 保持当前窗口不变
|
||||
|
||||
case keepCurrentWindow
|
||||
/// 扩窗:将候选段合并到当前窗口
|
||||
|
||||
case expandWindow(RDEPUBBackgroundCoverageSegment)
|
||||
/// 分段替换:用候选段替换当前窗口的部分内容
|
||||
|
||||
case segmentReplace(RDEPUBBackgroundCoverageSegment)
|
||||
/// 全量替换:用完整页图替换当前窗口
|
||||
|
||||
case fullReplace(RDEPUBBookPageMap)
|
||||
}
|
||||
|
||||
/// 页图协调器:负责判断后台解析结果是否可以接管前台窗口
|
||||
final class RDEPUBPageMapReconciliationCoordinator {
|
||||
|
||||
private unowned let context: RDEPUBReaderContext
|
||||
|
||||
init(context: RDEPUBReaderContext) {
|
||||
self.context = context
|
||||
}
|
||||
|
||||
/// 判断是否可以接管前台窗口
|
||||
func evaluateTakeover(
|
||||
candidatePageMap: RDEPUBBookPageMap?,
|
||||
candidateSegment: RDEPUBBackgroundCoverageSegment?,
|
||||
currentWindow: RDEPUBBookPageMap?,
|
||||
jumpSession: RDEPUBJumpSession?
|
||||
) -> RDEPUBPageMapTakeoverDecision {
|
||||
// 如果没有当前窗口,允许接管
|
||||
|
||||
guard let currentWindow else {
|
||||
if let candidatePageMap {
|
||||
return .fullReplace(candidatePageMap)
|
||||
@@ -35,7 +33,6 @@ final class RDEPUBPageMapReconciliationCoordinator {
|
||||
return .keepCurrentWindow
|
||||
}
|
||||
|
||||
// 获取当前阅读位置
|
||||
let currentSpineIndex = context.runtime?.locationCoordinator.currentVisibleLocation()
|
||||
.flatMap { context.normalizedSpineIndex(for: $0) }
|
||||
|
||||
@@ -46,11 +43,10 @@ final class RDEPUBPageMapReconciliationCoordinator {
|
||||
return item.linear && (item.mediaType.contains("html") || item.mediaType.contains("xhtml"))
|
||||
}) ?? 0
|
||||
|
||||
// 检查 JumpSession 保护
|
||||
if let jumpSession {
|
||||
let protectedIndices = jumpSession.protectedSpineIndices
|
||||
if let currentSpineIndex, protectedIndices.contains(currentSpineIndex) {
|
||||
// 在保护区内,检查候选是否覆盖保护区
|
||||
|
||||
if let candidateSegment {
|
||||
let candidateIndices = candidateSegment.resolvedSpineIndices
|
||||
let coverageRatio = Double(protectedIndices.intersection(candidateIndices).count) /
|
||||
@@ -66,7 +62,6 @@ final class RDEPUBPageMapReconciliationCoordinator {
|
||||
}
|
||||
}
|
||||
|
||||
// 检查边界章节覆盖
|
||||
if let currentSpineIndex {
|
||||
let requiresAdjacentCoverage = currentSpineIndex > 0 && currentSpineIndex < lastBuildableSpineIndex
|
||||
|
||||
@@ -85,7 +80,6 @@ final class RDEPUBPageMapReconciliationCoordinator {
|
||||
}
|
||||
}
|
||||
|
||||
// 检查渲染签名一致性
|
||||
if let candidateSegment {
|
||||
let currentRenderSignature = context.currentRenderSignature()
|
||||
if candidateSegment.renderSignature != currentRenderSignature {
|
||||
@@ -97,7 +91,6 @@ final class RDEPUBPageMapReconciliationCoordinator {
|
||||
}
|
||||
}
|
||||
|
||||
// 评估接管类型
|
||||
if let candidateSegment {
|
||||
return evaluateSegmentTakeover(
|
||||
candidateSegment: candidateSegment,
|
||||
@@ -119,7 +112,6 @@ final class RDEPUBPageMapReconciliationCoordinator {
|
||||
return .keepCurrentWindow
|
||||
}
|
||||
|
||||
/// 评估分段接管
|
||||
private func evaluateSegmentTakeover(
|
||||
candidateSegment: RDEPUBBackgroundCoverageSegment,
|
||||
currentWindow: RDEPUBBookPageMap,
|
||||
@@ -129,14 +121,12 @@ final class RDEPUBPageMapReconciliationCoordinator {
|
||||
let currentIndices = Set(currentWindow.entries.map { $0.spineIndex })
|
||||
let candidateIndices = candidateSegment.resolvedSpineIndices
|
||||
|
||||
// 检查是否覆盖当前阅读位置
|
||||
if let currentSpineIndex {
|
||||
if !candidateIndices.contains(currentSpineIndex) {
|
||||
return .keepCurrentWindow
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否覆盖相邻章节
|
||||
if let currentSpineIndex {
|
||||
let hasPrev = candidateIndices.contains(currentSpineIndex - 1) || currentSpineIndex == 0
|
||||
let hasNext = candidateIndices.contains(currentSpineIndex + 1) ||
|
||||
@@ -146,21 +136,20 @@ final class RDEPUBPageMapReconciliationCoordinator {
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否与当前窗口连续
|
||||
let isContinuous = currentIndices.contains(candidateSegment.lowerSpineIndex - 1) ||
|
||||
currentIndices.contains(candidateSegment.upperSpineIndex + 1) ||
|
||||
candidateIndices.contains(currentWindow.entries.first?.spineIndex ?? Int.max) ||
|
||||
candidateIndices.contains(currentWindow.entries.last?.spineIndex ?? Int.min)
|
||||
|
||||
if isContinuous {
|
||||
// 连续,可以扩窗
|
||||
|
||||
return .expandWindow(candidateSegment)
|
||||
} else {
|
||||
// 不连续,检查是否覆盖当前窗口的大部分
|
||||
|
||||
let overlap = currentIndices.intersection(candidateIndices)
|
||||
let overlapRatio = Double(overlap.count) / Double(currentIndices.count)
|
||||
if overlapRatio > 0.5 {
|
||||
// 覆盖大部分,可以替换
|
||||
|
||||
return .segmentReplace(candidateSegment)
|
||||
}
|
||||
}
|
||||
@@ -168,7 +157,6 @@ final class RDEPUBPageMapReconciliationCoordinator {
|
||||
return .keepCurrentWindow
|
||||
}
|
||||
|
||||
/// 评估全量页图接管
|
||||
private func evaluateFullPageMapTakeover(
|
||||
candidatePageMap: RDEPUBBookPageMap,
|
||||
currentWindow: RDEPUBBookPageMap,
|
||||
@@ -177,14 +165,12 @@ final class RDEPUBPageMapReconciliationCoordinator {
|
||||
) -> RDEPUBPageMapTakeoverDecision {
|
||||
let candidateIndices = Set(candidatePageMap.entries.map { $0.spineIndex })
|
||||
|
||||
// 检查是否覆盖当前阅读位置
|
||||
if let currentSpineIndex {
|
||||
if !candidateIndices.contains(currentSpineIndex) {
|
||||
return .keepCurrentWindow
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否覆盖相邻章节
|
||||
if let currentSpineIndex {
|
||||
let hasPrev = candidateIndices.contains(currentSpineIndex - 1) || currentSpineIndex == 0
|
||||
let hasNext = candidateIndices.contains(currentSpineIndex + 1) ||
|
||||
@@ -194,7 +180,6 @@ final class RDEPUBPageMapReconciliationCoordinator {
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否完整覆盖
|
||||
let isComplete = candidateIndices.count >= currentWindow.entries.count
|
||||
if isComplete {
|
||||
return .fullReplace(candidatePageMap)
|
||||
@@ -203,7 +188,6 @@ final class RDEPUBPageMapReconciliationCoordinator {
|
||||
return .keepCurrentWindow
|
||||
}
|
||||
|
||||
/// 生成保护区域的 spineIndex 集合
|
||||
func protectedSpineIndices(
|
||||
currentSpineIndex: Int?,
|
||||
jumpSession: RDEPUBJumpSession?
|
||||
@@ -212,7 +196,7 @@ final class RDEPUBPageMapReconciliationCoordinator {
|
||||
|
||||
if let currentSpineIndex {
|
||||
indices.insert(currentSpineIndex)
|
||||
// 添加相邻章节
|
||||
|
||||
if currentSpineIndex > 0 {
|
||||
indices.insert(currentSpineIndex - 1)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user