76 lines
2.4 KiB
Swift
76 lines
2.4 KiB
Swift
import SwiftUI
|
|
import AppKit
|
|
import VorleserKit
|
|
|
|
struct BookTextView: NSViewRepresentable {
|
|
let attributedText: NSAttributedString
|
|
let highlightRange: Range<Int>?
|
|
let onClickCharacter: (CharacterOffset) -> Void
|
|
var scrollToOffset: CharacterOffset?
|
|
|
|
func makeNSView(context: Context) -> NSScrollView {
|
|
let scrollView = NSTextView.scrollableTextView()
|
|
let textView = scrollView.documentView as! NSTextView
|
|
textView.isEditable = false
|
|
textView.isSelectable = false
|
|
textView.textContainerInset = NSSize(width: 16, height: 16)
|
|
|
|
let click = NSClickGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handleClick(_:)))
|
|
textView.addGestureRecognizer(click)
|
|
context.coordinator.textView = textView
|
|
|
|
return scrollView
|
|
}
|
|
|
|
func updateNSView(_ scrollView: NSScrollView, context: Context) {
|
|
guard let textView = scrollView.documentView as? NSTextView else { return }
|
|
|
|
let highlighted = NSMutableAttributedString(attributedString: attributedText)
|
|
|
|
if let range = highlightRange,
|
|
range.lowerBound >= 0,
|
|
range.upperBound <= attributedText.length {
|
|
let nsRange = NSRange(location: range.lowerBound, length: range.upperBound - range.lowerBound)
|
|
highlighted.addAttribute(.backgroundColor, value: NSColor.systemYellow.withAlphaComponent(0.3), range: nsRange)
|
|
}
|
|
|
|
textView.textStorage?.setAttributedString(highlighted)
|
|
|
|
if let range = highlightRange,
|
|
range.lowerBound >= 0,
|
|
range.upperBound <= attributedText.length {
|
|
let nsRange = NSRange(location: range.lowerBound, length: range.upperBound - range.lowerBound)
|
|
textView.scrollRangeToVisible(nsRange)
|
|
}
|
|
|
|
if let offset = scrollToOffset,
|
|
offset >= 0,
|
|
offset < attributedText.length {
|
|
let nsRange = NSRange(location: offset, length: 1)
|
|
textView.scrollRangeToVisible(nsRange)
|
|
}
|
|
}
|
|
|
|
func makeCoordinator() -> Coordinator {
|
|
Coordinator(onClickCharacter: onClickCharacter)
|
|
}
|
|
|
|
class Coordinator: NSObject {
|
|
weak var textView: NSTextView?
|
|
let onClickCharacter: (CharacterOffset) -> Void
|
|
|
|
init(onClickCharacter: @escaping (CharacterOffset) -> Void) {
|
|
self.onClickCharacter = onClickCharacter
|
|
}
|
|
|
|
@objc func handleClick(_ gesture: NSClickGestureRecognizer) {
|
|
guard let textView else { return }
|
|
let point = gesture.location(in: textView)
|
|
let characterIndex = textView.characterIndexForInsertion(at: point)
|
|
if characterIndex < textView.string.count {
|
|
onClickCharacter(characterIndex)
|
|
}
|
|
}
|
|
}
|
|
}
|