feat(epub): complete wxread parity scope
This commit is contained in:
@@ -64,6 +64,15 @@ struct RDEPUBPageLayoutSnapshot {
|
||||
let layoutFrame: DTCoreTextLayoutFrame
|
||||
#endif
|
||||
|
||||
var contentBounds: CGRect {
|
||||
let rects = lines.map(\.frame) + attachments.map(\.frame)
|
||||
guard var bounds = rects.first else { return .zero }
|
||||
for rect in rects.dropFirst() {
|
||||
bounds = bounds.union(rect)
|
||||
}
|
||||
return bounds
|
||||
}
|
||||
|
||||
func line(containing absoluteIndex: Int) -> RDEPUBPageLine? {
|
||||
lines.first { NSLocationInRange(absoluteIndex, $0.stringRange) }
|
||||
}
|
||||
@@ -80,12 +89,44 @@ struct RDEPUBPageLayoutSnapshot {
|
||||
attachments.first { $0.frame.insetBy(dx: -hitSlop, dy: -hitSlop).contains(point) }
|
||||
}
|
||||
|
||||
func line(at point: CGPoint, hitSlop: CGFloat = 4) -> RDEPUBPageLine? {
|
||||
lines.first { line in
|
||||
let lineRect = CGRect(
|
||||
x: line.frame.minX,
|
||||
y: line.baselineOrigin.y - line.ascent,
|
||||
width: max(line.frame.width, 1),
|
||||
height: line.ascent + line.descent + line.leading
|
||||
)
|
||||
return lineRect.insetBy(dx: -hitSlop, dy: -hitSlop).contains(point)
|
||||
}
|
||||
}
|
||||
|
||||
func run(at point: CGPoint, hitSlop: CGFloat = 4) -> RDEPUBPageRun? {
|
||||
runs.first { $0.frame.insetBy(dx: -hitSlop, dy: -hitSlop).contains(point) }
|
||||
}
|
||||
|
||||
func rects(containing point: CGPoint, in decorations: [RDEPUBTextOverlayDecoration]) -> [RDEPUBTextOverlayDecoration] {
|
||||
decorations.filter { decoration in
|
||||
decoration.rects.contains { $0.insetBy(dx: -4, dy: -4).contains(point) }
|
||||
}
|
||||
}
|
||||
|
||||
func absoluteRange(at point: CGPoint, in decorations: [RDEPUBTextOverlayDecoration]) -> NSRange? {
|
||||
if let attachment = attachment(at: point) {
|
||||
return attachment.stringRange
|
||||
}
|
||||
if let run = run(at: point) {
|
||||
return run.stringRange
|
||||
}
|
||||
let hitDecorations = rects(containing: point, in: decorations)
|
||||
if let mostSpecific = hitDecorations.min(by: { lhs, rhs in
|
||||
lhs.absoluteRange.length < rhs.absoluteRange.length
|
||||
}) {
|
||||
return mostSpecific.absoluteRange
|
||||
}
|
||||
return line(at: point)?.stringRange
|
||||
}
|
||||
|
||||
#if canImport(DTCoreText)
|
||||
static func build(
|
||||
from layoutFrame: DTCoreTextLayoutFrame,
|
||||
|
||||
@@ -21,6 +21,10 @@ public struct RDEPUBReaderConfiguration: Equatable {
|
||||
public var fontSize: CGFloat
|
||||
/// 行距倍数,默认 1.6(即行距为字号的 1.6 倍)
|
||||
public var lineHeightMultiple: CGFloat
|
||||
/// 栏数,默认单栏
|
||||
public var numberOfColumns: Int
|
||||
/// 栏间距,默认 20pt
|
||||
public var columnGap: CGFloat
|
||||
/// 翻页显示模式:.pageCurl(仿真翻页)或 .scroll(滚动阅读)
|
||||
public var displayType: RDReaderView.DisplayType
|
||||
/// 横屏时是否启用双页显示,默认开启
|
||||
@@ -73,6 +77,8 @@ public struct RDEPUBReaderConfiguration: Equatable {
|
||||
public init(
|
||||
fontSize: CGFloat = 15,
|
||||
lineHeightMultiple: CGFloat = 1.6,
|
||||
numberOfColumns: Int = 1,
|
||||
columnGap: CGFloat = 20,
|
||||
displayType: RDReaderView.DisplayType = .pageCurl,
|
||||
landscapeDualPageEnabled: Bool = true,
|
||||
showsTableOfContents: Bool = true,
|
||||
@@ -87,6 +93,8 @@ public struct RDEPUBReaderConfiguration: Equatable {
|
||||
) {
|
||||
self.fontSize = fontSize
|
||||
self.lineHeightMultiple = lineHeightMultiple
|
||||
self.numberOfColumns = max(1, numberOfColumns)
|
||||
self.columnGap = max(0, columnGap)
|
||||
self.displayType = displayType
|
||||
self.landscapeDualPageEnabled = landscapeDualPageEnabled
|
||||
self.showsTableOfContents = showsTableOfContents
|
||||
@@ -115,6 +123,8 @@ extension RDEPUBReaderConfiguration {
|
||||
lineHeightMultiple: lineHeightMultiple,
|
||||
reflowableContentInsets: reflowableContentInsets,
|
||||
fixedContentInset: fixedContentInset,
|
||||
numberOfColumns: numberOfColumns,
|
||||
columnGap: columnGap,
|
||||
themeBackgroundColor: theme.themeBackgroundColorCSS,
|
||||
themeTextColor: theme.themeTextColorCSS,
|
||||
fixedBackgroundColor: theme.themeBackgroundColorCSS,
|
||||
@@ -122,4 +132,4 @@ extension RDEPUBReaderConfiguration {
|
||||
fixedLayoutSpreadMode: fixedLayoutSpreadMode
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -112,6 +112,8 @@ public struct RDEPUBReaderSettings: Codable, Equatable {
|
||||
public var fontSize: CGFloat?
|
||||
/// 用户选择的行距倍数,nil 表示使用默认值
|
||||
public var lineHeightMultiple: CGFloat?
|
||||
/// 用户选择的栏数,nil 表示使用默认值
|
||||
public var numberOfColumns: Int?
|
||||
/// 用户选择的翻页模式,nil 表示使用默认值
|
||||
public var displayMode: RDEPUBReaderDisplayMode?
|
||||
/// 用户选择的主题预设,nil 表示使用默认值
|
||||
@@ -122,12 +124,14 @@ public struct RDEPUBReaderSettings: Codable, Equatable {
|
||||
brightness: CGFloat? = nil,
|
||||
fontSize: CGFloat? = nil,
|
||||
lineHeightMultiple: CGFloat? = nil,
|
||||
numberOfColumns: Int? = nil,
|
||||
displayMode: RDEPUBReaderDisplayMode? = nil,
|
||||
themePreset: RDEPUBReaderThemePreset? = nil
|
||||
) {
|
||||
self.brightness = brightness
|
||||
self.fontSize = fontSize
|
||||
self.lineHeightMultiple = lineHeightMultiple
|
||||
self.numberOfColumns = numberOfColumns
|
||||
self.displayMode = displayMode
|
||||
self.themePreset = themePreset
|
||||
}
|
||||
@@ -145,6 +149,9 @@ public struct RDEPUBReaderSettings: Codable, Equatable {
|
||||
if let lineHeightMultiple {
|
||||
resolvedConfiguration.lineHeightMultiple = lineHeightMultiple
|
||||
}
|
||||
if let numberOfColumns {
|
||||
resolvedConfiguration.numberOfColumns = max(1, numberOfColumns)
|
||||
}
|
||||
if let displayMode {
|
||||
resolvedConfiguration.displayType = displayMode.displayType
|
||||
}
|
||||
@@ -169,8 +176,9 @@ public struct RDEPUBReaderSettings: Codable, Equatable {
|
||||
brightness: max(0, min(1, brightness)),
|
||||
fontSize: configuration.fontSize,
|
||||
lineHeightMultiple: configuration.lineHeightMultiple,
|
||||
numberOfColumns: configuration.numberOfColumns,
|
||||
displayMode: RDEPUBReaderDisplayMode(displayType: configuration.displayType),
|
||||
themePreset: RDEPUBReaderThemePreset(theme: configuration.theme)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ final class RDEPUBReaderSettingsViewController: UIViewController {
|
||||
var onFontSizeChange: ((CGFloat) -> Void)?
|
||||
/// 行距倍数变化回调
|
||||
var onLineHeightChange: ((CGFloat) -> Void)?
|
||||
/// 栏数变化回调
|
||||
var onColumnCountChange: ((Int) -> Void)?
|
||||
/// 翻页模式变化回调
|
||||
var onDisplayTypeChange: ((RDReaderView.DisplayType) -> Void)?
|
||||
/// 主题变化回调
|
||||
@@ -65,6 +67,7 @@ final class RDEPUBReaderSettingsViewController: UIViewController {
|
||||
private let decreaseFontButton = UIButton(type: .system)
|
||||
private let increaseFontButton = UIButton(type: .system)
|
||||
private let lineHeightControl = UISegmentedControl(items: ["紧凑", "标准", "宽松"])
|
||||
private let columnCountControl = UISegmentedControl(items: ["单栏", "双栏"])
|
||||
private let displayTypeControl = UISegmentedControl(items: ["仿真", "横滑", "竖滑"])
|
||||
private let themeStackView: UIStackView = {
|
||||
let stackView = UIStackView()
|
||||
@@ -136,6 +139,7 @@ final class RDEPUBReaderSettingsViewController: UIViewController {
|
||||
increaseFontButton.addTarget(self, action: #selector(increaseFontAction), for: .touchUpInside)
|
||||
|
||||
lineHeightControl.addTarget(self, action: #selector(lineHeightChanged(_:)), for: .valueChanged)
|
||||
columnCountControl.addTarget(self, action: #selector(columnCountChanged(_:)), for: .valueChanged)
|
||||
displayTypeControl.addTarget(self, action: #selector(displayTypeChanged(_:)), for: .valueChanged)
|
||||
|
||||
ThemePreset.allCases.forEach { preset in
|
||||
@@ -156,6 +160,7 @@ final class RDEPUBReaderSettingsViewController: UIViewController {
|
||||
contentStack.addArrangedSubview(makeSection(title: "亮度", content: brightnessSlider))
|
||||
contentStack.addArrangedSubview(makeSection(title: "字号", content: makeFontSizeRow()))
|
||||
contentStack.addArrangedSubview(makeSection(title: "行距", content: lineHeightControl))
|
||||
contentStack.addArrangedSubview(makeSection(title: "分栏", content: columnCountControl))
|
||||
contentStack.addArrangedSubview(makeSection(title: "翻页方式", content: displayTypeControl))
|
||||
contentStack.addArrangedSubview(makeSection(title: "主题", content: themeStackView))
|
||||
}
|
||||
@@ -203,6 +208,7 @@ final class RDEPUBReaderSettingsViewController: UIViewController {
|
||||
|
||||
let lineHeightIndex = lineHeightValues.enumerated().min { abs($0.element - currentConfiguration.lineHeightMultiple) < abs($1.element - currentConfiguration.lineHeightMultiple) }?.offset ?? 1
|
||||
lineHeightControl.selectedSegmentIndex = lineHeightIndex
|
||||
columnCountControl.selectedSegmentIndex = currentConfiguration.numberOfColumns > 1 ? 1 : 0
|
||||
|
||||
switch currentConfiguration.displayType {
|
||||
case .pageCurl:
|
||||
@@ -233,7 +239,7 @@ final class RDEPUBReaderSettingsViewController: UIViewController {
|
||||
button.backgroundColor = theme.toolBackgroundColor
|
||||
}
|
||||
|
||||
[lineHeightControl, displayTypeControl].forEach { control in
|
||||
[lineHeightControl, columnCountControl, displayTypeControl].forEach { control in
|
||||
control.backgroundColor = theme.toolBackgroundColor
|
||||
if #available(iOS 13.0, *) {
|
||||
control.selectedSegmentTintColor = theme.toolControlTextColor.withAlphaComponent(0.14)
|
||||
@@ -289,6 +295,12 @@ final class RDEPUBReaderSettingsViewController: UIViewController {
|
||||
onLineHeightChange?(value)
|
||||
}
|
||||
|
||||
@objc private func columnCountChanged(_ control: UISegmentedControl) {
|
||||
let numberOfColumns = control.selectedSegmentIndex == 1 ? 2 : 1
|
||||
currentConfiguration.numberOfColumns = numberOfColumns
|
||||
onColumnCountChange?(numberOfColumns)
|
||||
}
|
||||
|
||||
@objc private func displayTypeChanged(_ control: UISegmentedControl) {
|
||||
let displayType: RDReaderView.DisplayType
|
||||
switch control.selectedSegmentIndex {
|
||||
@@ -309,4 +321,4 @@ final class RDEPUBReaderSettingsViewController: UIViewController {
|
||||
applyTheme(preset.theme)
|
||||
onThemeChange?(preset.theme)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,17 +102,8 @@ final class RDEPUBSelectionOverlayView: UIView {
|
||||
}
|
||||
|
||||
if let snapshot,
|
||||
let attachment = snapshot.attachment(at: point) {
|
||||
return attachment.stringRange
|
||||
}
|
||||
|
||||
if let snapshot {
|
||||
let hitDecorations = snapshot.rects(containing: point, in: resolvedDecorations)
|
||||
if let mostSpecific = hitDecorations.min(by: { lhs, rhs in
|
||||
lhs.absoluteRange.length < rhs.absoluteRange.length
|
||||
}) {
|
||||
return mostSpecific.absoluteRange
|
||||
}
|
||||
let absoluteRange = snapshot.absoluteRange(at: point, in: resolvedDecorations) {
|
||||
return absoluteRange
|
||||
}
|
||||
|
||||
for decoration in resolvedDecorations {
|
||||
|
||||
@@ -383,13 +383,14 @@ final class RDEPUBTextContentView: UIView {
|
||||
return
|
||||
}
|
||||
|
||||
let totalLength = max(page.content.length - 1, 1)
|
||||
let relativeLocation = range.location - page.pageStartOffset
|
||||
let chapterLength = max(page.chapterContent.length - 1, 1)
|
||||
let chapterStart = max(range.location, 0)
|
||||
let chapterEnd = max(chapterStart + range.length - 1, chapterStart)
|
||||
let selection = RDEPUBSelection(
|
||||
location: RDEPUBLocation(
|
||||
href: page.href,
|
||||
progression: Double(max(relativeLocation, 0)) / Double(totalLength),
|
||||
lastProgression: Double(max(relativeLocation + range.length - 1, 0)) / Double(totalLength),
|
||||
progression: Double(chapterStart) / Double(chapterLength),
|
||||
lastProgression: Double(chapterEnd) / Double(chapterLength),
|
||||
fragment: nil
|
||||
),
|
||||
text: selectedText,
|
||||
@@ -698,12 +699,12 @@ extension RDEPUBTextContentView: UITextViewDelegate {
|
||||
|
||||
let globalStart = page.pageStartOffset + selectedRange.location
|
||||
let globalEnd = globalStart + selectedRange.length
|
||||
let totalLength = max(page.content.length - 1, 1)
|
||||
let totalLength = max(page.chapterContent.length - 1, 1)
|
||||
let selection = RDEPUBSelection(
|
||||
location: RDEPUBLocation(
|
||||
href: page.href,
|
||||
progression: Double(selectedRange.location) / Double(totalLength),
|
||||
lastProgression: Double(max(selectedRange.location + selectedRange.length - 1, 0)) / Double(totalLength),
|
||||
progression: Double(globalStart) / Double(totalLength),
|
||||
lastProgression: Double(max(globalEnd - 1, globalStart)) / Double(totalLength),
|
||||
fragment: nil
|
||||
),
|
||||
text: selectedText,
|
||||
|
||||
Reference in New Issue
Block a user