Skip to content

Commit

Permalink
Use FormattedTextView for annotation text editing
Browse files Browse the repository at this point in the history
  • Loading branch information
mvasilak committed Aug 10, 2024
1 parent 6ac4b69 commit cf97c8f
Show file tree
Hide file tree
Showing 17 changed files with 112 additions and 108 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ extension AnnotationPopoverCoordinator: AnnotationPopoverAnnotationCoordinatorDe
lineWidth: state.lineWidth,
pageLabel: state.pageLabel,
highlightText: state.highlightText,
highlightFont: state.highlightFont,
fontSize: nil
)
let state = AnnotationEditState(data: data)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ enum AnnotationEditAction {
case setColor(String)
case setLineWidth(CGFloat)
case setPageLabel(String, Bool)
case setHighlight(String)
case setHighlight(NSAttributedString)
case setFontSize(UInt)
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ struct AnnotationEditState: ViewModelState {
let color: String
let lineWidth: CGFloat
let pageLabel: String
let highlightText: String
let highlightText: NSAttributedString
let highlightFont: UIFont
let fontSize: UInt?
}

Expand All @@ -35,7 +36,8 @@ struct AnnotationEditState: ViewModelState {
var lineWidth: CGFloat
var pageLabel: String
var fontSize: UInt
var highlightText: String
var highlightText: NSAttributedString
var highlightFont: UIFont
var updateSubsequentLabels: Bool
var changes: Changes

Expand All @@ -46,6 +48,7 @@ struct AnnotationEditState: ViewModelState {
lineWidth = data.lineWidth
pageLabel = data.pageLabel
highlightText = data.highlightText
highlightFont = data.highlightFont
fontSize = data.fontSize ?? 0
updateSubsequentLabels = false
changes = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ enum AnnotationPopoverAction {
case setColor(String)
case setLineWidth(CGFloat)
case setPageLabel(String, Bool)
case setHighlight(String)
case setTags([Tag])
case setComment(NSAttributedString)
case delete
case setProperties(pageLabel: String, updateSubsequentLabels: Bool, highlightText: String)
case setProperties(pageLabel: String, updateSubsequentLabels: Bool, highlightText: NSAttributedString)
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ struct AnnotationPopoverState: ViewModelState {
let color: String
let lineWidth: CGFloat
let pageLabel: String
let highlightText: String
let highlightText: NSAttributedString
let highlightFont: UIFont
let tags: [Tag]
let showsDeleteButton: Bool
}
Expand Down Expand Up @@ -47,7 +48,8 @@ struct AnnotationPopoverState: ViewModelState {
var color: String
var lineWidth: CGFloat
var pageLabel: String
var highlightText: String
var highlightText: NSAttributedString
var highlightFont: UIFont
var updateSubsequentLabels: Bool
var tags: [Tag]
var changes: Changes
Expand All @@ -62,6 +64,7 @@ struct AnnotationPopoverState: ViewModelState {
self.lineWidth = data.lineWidth
self.pageLabel = data.pageLabel
self.highlightText = data.highlightText
self.highlightFont = data.highlightFont
self.tags = data.tags
self.showsDeleteButton = data.showsDeleteButton
self.updateSubsequentLabels = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,6 @@ struct AnnotationPopoverActionHandler: ViewModelActionHandler {
state.changes = .pageLabel
}

case .setHighlight(let text):
update(viewModel: viewModel) { state in
state.highlightText = text
state.changes = .highlight
}

case .setComment(let comment):
update(viewModel: viewModel) { state in
state.comment = comment
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import UIKit

import RxSwift

// key, color, lineWidth, fontSize, pageLabel, updateSubsequentLabels, highlightText
typealias AnnotationEditSaveAction = (String, CGFloat, UInt, String, Bool, String) -> Void
typealias AnnotationEditSaveAction = (_ key: String, _ lineWidth: CGFloat, _ fontSize: UInt, _ pageLabel: String, _ updateSubsequentLabels: Bool, _ highlightText: NSAttributedString) -> Void
typealias AnnotationEditDeleteAction = () -> Void

final class AnnotationEditViewController: UIViewController {
Expand Down Expand Up @@ -133,7 +132,8 @@ final class AnnotationEditViewController: UIViewController {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.minimumLineHeight = AnnotationPopoverLayout.annotationLayout.lineHeight
paragraphStyle.maximumLineHeight = AnnotationPopoverLayout.annotationLayout.lineHeight
let attributedText = NSAttributedString(string: self.viewModel.state.highlightText, attributes: [.font: AnnotationPopoverLayout.annotationLayout.font, .paragraphStyle: paragraphStyle])
let attributedText = NSMutableAttributedString(attributedString: viewModel.state.highlightText)
attributedText.addAttribute(.paragraphStyle, value: paragraphStyle, range: .init(location: 0, length: attributedText.length))
let boundingRect = attributedText.boundingRect(with: CGSize(width: width, height: .greatestFiniteMagnitude), options: .usesLineFragmentOrigin, context: nil)
height += ceil(boundingRect.height) + 58 // 58 for 22 insets and 36 spacer
}
Expand Down Expand Up @@ -231,9 +231,9 @@ extension AnnotationEditViewController: UITableViewDataSource {
case .textContent:
if let cell = cell as? TextContentEditCell {
cell.setup(with: self.viewModel.state.highlightText, color: self.viewModel.state.color)
cell.textAndHeightReloadNeededObservable.subscribe { [weak self] text, needsHeightReload in
cell.attributedTextAndHeightReloadNeededObservable.subscribe { [weak self] attributedText, needsHeightReload in
guard let self else { return }
viewModel.process(action: .setHighlight(text))
viewModel.process(action: .setHighlight(attributedText))
if needsHeightReload {
updatePreferredContentSize()
reloadHeight()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import UIKit
import RxSwift

final class TextContentEditCell: RxTableViewCell {
let textAndHeightReloadNeededObservable: PublishSubject<(String, Bool)>
let attributedTextAndHeightReloadNeededObservable: PublishSubject<(NSAttributedString, Bool)>

private var lineView: UIView?
private var textView: UITextView?
private var textView: FormattedTextView?

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
textAndHeightReloadNeededObservable = PublishSubject()
attributedTextAndHeightReloadNeededObservable = PublishSubject()
super.init(style: style, reuseIdentifier: reuseIdentifier)
setup()
selectionStyle = .none
Expand All @@ -28,7 +28,7 @@ final class TextContentEditCell: RxTableViewCell {
contentView.addSubview(lineView)
self.lineView = lineView

let textView = TextKit1TextView()
let textView = FormattedTextView(defaultFont: AnnotationPopoverLayout.annotationLayout.font)
textView.textContainerInset = UIEdgeInsets()
textView.textContainer.lineFragmentPadding = 0
textView.delegate = self
Expand Down Expand Up @@ -59,14 +59,12 @@ final class TextContentEditCell: RxTableViewCell {
return textView.caretRect(for: selectedPosition)
}

func setup(with text: String, color: String) {
func setup(with text: NSAttributedString, color: String) {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.minimumLineHeight = AnnotationPopoverLayout.annotationLayout.lineHeight
paragraphStyle.maximumLineHeight = AnnotationPopoverLayout.annotationLayout.lineHeight
let attributedText = NSAttributedString(
string: text,
attributes: [.font: AnnotationPopoverLayout.annotationLayout.font, .foregroundColor: Asset.Colors.annotationText.color, .paragraphStyle: paragraphStyle]
)
let attributedText = NSMutableAttributedString(attributedString: text)
attributedText.addAttributes([.foregroundColor: Asset.Colors.annotationText.color, .paragraphStyle: paragraphStyle], range: .init(location: 0, length: attributedText.length))

lineView?.backgroundColor = UIColor(hex: color)
textView?.attributedText = attributedText
Expand All @@ -77,6 +75,6 @@ extension TextContentEditCell: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
let height = textView.contentSize.height
textView.sizeToFit()
textAndHeightReloadNeededObservable.onNext((textView.text, height != textView.contentSize.height))
attributedTextAndHeightReloadNeededObservable.onNext((textView.attributedText, height != textView.contentSize.height))
}
}
14 changes: 11 additions & 3 deletions Zotero/Scenes/Detail/PDF/Models/PDFReaderAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,23 @@ enum PDFReaderAction {
case setTags(key: String, tags: [Tag])
case setColor(key: String, color: String)
case setLineWidth(key: String, width: CGFloat)
case setHighlight(key: String, text: String)
case updateAnnotationProperties(key: String, color: String, lineWidth: CGFloat, fontSize: UInt, pageLabel: String, updateSubsequentLabels: Bool, highlightText: String)
case updateAnnotationProperties(
key: String,
color: String,
lineWidth: CGFloat,
fontSize: UInt,
pageLabel: String,
updateSubsequentLabels: Bool,
highlightText: NSAttributedString,
higlightFont: UIFont
)
case userInterfaceStyleChanged(UIUserInterfaceStyle)
case updateAnnotationPreviews
case setToolOptions(color: String?, size: CGFloat?, tool: PSPDFKit.Annotation.Tool)
case createNote(pageIndex: PageIndex, origin: CGPoint)
case createImage(pageIndex: PageIndex, origin: CGPoint)
case createHighlight(pageIndex: PageIndex, rects: [CGRect])
case parseAndCacheText(key: String, text: String)
case parseAndCacheText(key: String, text: String, font: UIFont)
case parseAndCacheComment(key: String, comment: String)
case setComment(key: String, comment: NSAttributedString)
case setCommentActive(Bool)
Expand Down
4 changes: 3 additions & 1 deletion Zotero/Scenes/Detail/PDF/Models/PDFReaderState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ struct PDFReaderState: ViewModelState {
let displayTitle: String?
let previewCache: NSCache<NSString, UIImage>
let textFont: UIFont
let textEditorFont: UIFont
let commentFont: UIFont
let userId: Int
let username: String
Expand All @@ -83,7 +84,7 @@ struct PDFReaderState: ViewModelState {
var itemToken: NotificationToken?
var databaseAnnotations: Results<RItem>!
var documentAnnotations: [String: PDFDocumentAnnotation]
var texts: [String: NSAttributedString]
var texts: [String: (String, [UIFont: NSAttributedString])]
var comments: [String: NSAttributedString]
var searchTerm: String?
var filter: AnnotationsFilter?
Expand Down Expand Up @@ -147,6 +148,7 @@ struct PDFReaderState: ViewModelState {
self.displayTitle = displayTitle
self.previewCache = NSCache()
self.textFont = PDFReaderLayout.annotationLayout.font
self.textEditorFont = AnnotationPopoverLayout.annotationLayout.font
self.commentFont = PDFReaderLayout.annotationLayout.font
self.userId = userId
self.username = username
Expand Down
15 changes: 12 additions & 3 deletions Zotero/Scenes/Detail/PDF/PDFCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ protocol PdfAnnotationsCoordinatorDelegate: AnyObject {
func showTagPicker(libraryId: LibraryIdentifier, selected: Set<String>, userInterfaceStyle: UIUserInterfaceStyle?, picked: @escaping ([Tag]) -> Void)
func showCellOptions(
for annotation: PDFAnnotation,
highlightFont: UIFont,
userId: Int,
library: Library,
sender: UIButton,
Expand Down Expand Up @@ -198,7 +199,10 @@ extension PDFCoordinator: PdfReaderCoordinatorDelegate {
navigationController.overrideUserInterfaceStyle = userInterfaceStyle

let author = viewModel.state.library.identifier == .custom(.myLibrary) ? "" : annotation.author(displayName: viewModel.state.displayName, username: viewModel.state.username)
let comment: NSAttributedString = (self.navigationController?.viewControllers.first as? AnnotationsDelegate)?.parseAndCacheIfNeededAttributedComment(for: annotation) ?? NSAttributedString()
let comment: NSAttributedString = (self.navigationController?.viewControllers.first as? AnnotationsDelegate)?.parseAndCacheIfNeededAttributedComment(for: annotation) ?? .init(string: "")
let highlightFont = viewModel.state.textEditorFont
let highlightText: NSAttributedString = (self.navigationController?.viewControllers.first as? AnnotationsDelegate)?
.parseAndCacheIfNeededAttributedText(for: annotation, with: highlightFont) ?? .init(string: "")
let editability = annotation.editability(currentUserId: viewModel.state.userId, library: viewModel.state.library)

let data = AnnotationPopoverState.Data(
Expand All @@ -210,7 +214,8 @@ extension PDFCoordinator: PdfReaderCoordinatorDelegate {
color: annotation.color,
lineWidth: annotation.lineWidth ?? 0,
pageLabel: annotation.pageLabel,
highlightText: annotation.text ?? "",
highlightText: highlightText,
highlightFont: highlightFont,
tags: annotation.tags,
showsDeleteButton: editability != .notEditable
)
Expand Down Expand Up @@ -603,6 +608,7 @@ extension PDFCoordinator: PdfAnnotationsCoordinatorDelegate {

func showCellOptions(
for annotation: PDFAnnotation,
highlightFont: UIFont,
userId: Int,
library: Library,
sender: UIButton,
Expand All @@ -613,14 +619,17 @@ extension PDFCoordinator: PdfAnnotationsCoordinatorDelegate {
let navigationController = NavigationViewController()
navigationController.overrideUserInterfaceStyle = userInterfaceStyle

let highlightText: NSAttributedString = (self.navigationController?.viewControllers.first as? AnnotationsDelegate)?
.parseAndCacheIfNeededAttributedText(for: annotation, with: highlightFont) ?? .init(string: "")
let coordinator = AnnotationEditCoordinator(
data: AnnotationEditState.Data(
type: annotation.type,
isEditable: annotation.editability(currentUserId: userId, library: library) == .editable,
color: annotation.color,
lineWidth: annotation.lineWidth ?? 0,
pageLabel: annotation.pageLabel,
highlightText: annotation.text ?? "",
highlightText: highlightText,
highlightFont: highlightFont,
fontSize: annotation.fontSize
),
saveAction: saveAction,
Expand Down
Loading

0 comments on commit cf97c8f

Please sign in to comment.