feat: unify reader chrome state management

- Add RDEPUBReaderUIState struct for centralized UI state model
- Refactor RDEPUBReaderChromeCoordinator with makeUIState() and applyUIState()
- Remove direct UI updates from RDEPUBReaderAnnotationCoordinator
- Consolidate updateBookmarkChrome() into updateReaderChrome()
- Update RDEPUBReaderContext, Runtime, and LocationCoordinator
- Add reader problem fix development checklist documentation

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-06-12 17:51:08 +08:00
co-authored by Claude Opus 4.7
parent d15187b730
commit ed390da147
12 changed files with 908 additions and 47 deletions
@@ -40,6 +40,8 @@ final class RDEPUBTextContentView: UIView, UIGestureRecognizerDelegate {
let view = RDEPUBTextPageRenderView()
view.backgroundColor = .clear
view.isOpaque = false
view.isAccessibilityElement = true
view.accessibilityTraits = .staticText
return view
}()
@@ -118,6 +120,7 @@ final class RDEPUBTextContentView: UIView, UIGestureRecognizerDelegate {
} else {
self.hideSelectionMenu()
}
self.updateAccessibilityDecorationSummary()
self.delegate?.textContentView(self, didChangeSelection: selection)
}
selectionController.pageProvider = { [weak self] in self?.currentPage }
@@ -250,6 +253,7 @@ final class RDEPUBTextContentView: UIView, UIGestureRecognizerDelegate {
backgroundOverlayView.applyDecorations(bgDecorations)
overlayView.applyDecorations(fgDecorations)
#endif
updateAccessibilityDecorationSummary()
delegate?.textContentView(self, didChangeSelection: nil)
setNeedsLayout()
@@ -262,6 +266,7 @@ final class RDEPUBTextContentView: UIView, UIGestureRecognizerDelegate {
selectionController.clearSelection(renderView: coreTextRenderView)
overlayView.clearSelection()
backgroundOverlayView.clearSelection()
updateAccessibilityDecorationSummary()
UIMenuController.shared.setMenuVisible(false, animated: true)
}
@@ -570,13 +575,21 @@ final class RDEPUBTextContentView: UIView, UIGestureRecognizerDelegate {
#endif
}
private func updateAccessibilityDecorationSummary() {
#if canImport(DTCoreText)
coreTextContentView.accessibilityValue = [
backgroundOverlayView.decorationSummary(),
overlayView.decorationSummary()
].joined(separator: " | ")
#endif
}
private func highlight(at point: CGPoint) -> RDEPUBHighlight? {
guard let page = currentPage else { return nil }
let absoluteRange = backgroundOverlayView.absoluteRange(at: point) ?? overlayView.absoluteRange(at: point)
guard let absoluteRange else { return nil }
let matches = currentHighlights.filter { highlight in
guard highlight.location.href == page.href,
let range = RDEPUBTextOffsetRangeInfo.decode(from: highlight.rangeInfo)?.nsRange else {
guard let range = RDEPUBTextOffsetRangeInfo.decode(from: highlight.rangeInfo)?.nsRange else {
return false
}
return NSIntersectionRange(range, absoluteRange).length > 0