Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update snapshotting #350

Merged
merged 2 commits into from
Nov 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions Proton.xcworkspace/xcshareddata/swiftpm/Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,21 @@
"object": {
"pins": [
{
"package": "SnapshotTesting",
"package": "swift-snapshot-testing",
"repositoryURL": "https://github.com/pointfreeco/swift-snapshot-testing.git",
"state": {
"branch": null,
"revision": "114882386a815f4a2e6d8f2f7ee4857acd372438",
"version": "1.7.2"
"revision": "42a086182681cf661f5c47c9b7dc3931de18c6d7",
"version": "1.17.6"
}
},
{
"package": "swift-syntax",
"repositoryURL": "https://github.com/swiftlang/swift-syntax",
"state": {
"branch": null,
"revision": "0687f71944021d616d34d922343dcef086855920",
"version": "600.0.1"
}
}
]
Expand Down
2 changes: 1 addition & 1 deletion Proton/Sources/ObjC/PRTextStorage.m
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ - (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str {
NSInteger delta = str.length - range.length;
[_storage replaceCharactersInRange:range withString:str];
[_storage fixAttributesInRange:NSMakeRange(0, _storage.length)];
[self edited:NSTextStorageEditedCharacters & NSTextStorageEditedAttributes range:range changeInLength:delta];
[self edited:NSTextStorageEditedCharacters | NSTextStorageEditedAttributes range:range changeInLength:delta];

[self endEditing];
}
Expand Down
9 changes: 9 additions & 0 deletions Proton/Sources/Swift/Core/RichTextView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,15 @@ class RichTextView: AutogrowingTextView {
draw(CGRect(origin: .zero, size: contentSize))
}

override func becomeFirstResponder() -> Bool {
let didBecomeFirstResponder = super.becomeFirstResponder()
if didBecomeFirstResponder {
context?.selectedTextView = self
context?.activeTextView = self
}
return didBecomeFirstResponder
}

func updateSelectedRangeIgnoringCallback(_ selectedRange: NSRange) {
ignoreSelectedRangeChangeCallback = true
self.selectedRange = selectedRange
Expand Down
37 changes: 3 additions & 34 deletions Proton/Sources/Swift/Editor/EditorView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -150,12 +150,6 @@ open class EditorView: UIView {
}
}


// Holds `attributedText` until Editor move to a window
// Setting attributed text without Editor being fully ready
// causes issues with cached bounds that shows up when rotating the device.
private var pendingAttributedText: NSAttributedString?

var editorContextDelegate: EditorViewDelegate? {
get { editorViewContext.delegate }
}
Expand All @@ -172,12 +166,6 @@ open class EditorView: UIView {
/// Context for the current Editor
public let editorViewContext: EditorViewContext

/// Returns if `attributedText` change is pending. `AttributedText` may not have been applied if the `EditorView` is not already on
/// `window` and `forceApplyAttributedText` is not set to `true`.
public var isAttributedTextPending: Bool {
pendingAttributedText != nil
}

/// Enables asynchronous rendering of attachments.
/// - Note:
/// Since attachments must me rendered on main thread, the rendering only continues when there is no user interaction. By default, rendering starts
Expand Down Expand Up @@ -419,7 +407,6 @@ open class EditorView: UIView {
/// An attachment is only counted as a single character. Content length does not include
/// length of content within the Attachment that is hosting another `EditorView`.
public var contentLength: Int {
guard pendingAttributedText == nil else { return attributedText.length }
return richTextView.contentLength
}

Expand Down Expand Up @@ -539,35 +526,20 @@ open class EditorView: UIView {
}
}

/// Forces setting attributed text in `EditorView` even if it is not
/// yet in view hierarchy.
/// - Note: This may result in misplaced `Attachment`s and is recommended to be set to `true` only in unit tests.
public var forceApplyAttributedText = false

/// Text to be set in the `EditorView`
/// - Important: `attributedText` is not set for rendering in `EditorView` if the `EditorView` is not already in a `Window`. Value of `true`
/// for `isAttributedTextPending` confirms that the text has not yet been rendered even though it is set in the `EditorView`.
/// Notification of text being set can be observed by subscribing to `didSetAttributedText` in `EditorViewDelegate`.
/// Alternatively, `forceApplyAttributedText` may be set to `true` to always apply `attributedText` irrespective of `EditorView` being
/// in a `Window` or not.
public var attributedText: NSAttributedString {
get {
pendingAttributedText ?? richTextView.attributedText
richTextView.attributedText
}
set {
if forceApplyAttributedText == false && window == nil {
pendingAttributedText = newValue
return
}
isSettingAttributedText = true
attachmentRenderingScheduler.cancel()
renderedViewport = nil
// Clear text before setting new value to avoid issues with formatting/layout when
// editor is hosted in a scrollable container and content is set multiple times.
richTextView.attributedText = NSAttributedString()

let isDeferred = pendingAttributedText != nil
pendingAttributedText = nil
let isDeferred = false

AggregateEditorViewDelegate.editor(self, willSetAttributedText: newValue, isDeferred: isDeferred)

Expand Down Expand Up @@ -872,9 +844,6 @@ open class EditorView: UIView {
/// - IMPORTANT: Overriding implementations must call `super.didMoveToWindow()`
open override func didMoveToWindow() {
super.didMoveToWindow()
if let pendingAttributedText {
attributedText = pendingAttributedText
}
let isReady = window != nil
AggregateEditorViewDelegate.editor(self, isReady: isReady)
}
Expand Down Expand Up @@ -1144,7 +1113,7 @@ open class EditorView: UIView {
if let range {
selectedRange = range
}
richTextView.becomeFirstResponder()
_ = richTextView.becomeFirstResponder()
}

/// Makes the `EditorView` lose focus.
Expand Down
8 changes: 8 additions & 0 deletions Proton/Sources/Swift/Grid/View/GridContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,14 @@ class GridContentView: UIScrollView {
}

public override func willMove(toWindow newWindow: UIWindow?) {
defer {
for cell in grid.cells {
if !cell.editorSetupComplete {
cell.setupEditor()
}
}
}

guard isRendered == false,
newWindow != nil else {
return
Expand Down
15 changes: 0 additions & 15 deletions Proton/Sources/Swift/TextProcessors/TextProcessor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@ class TextProcessor: NSObject, NSTextStorageDelegate {
var processed = false
let changedText = textStorage.substring(from: editedRange)

let editedMask = getEditedMask(delta: delta)

let executableProcessors = filteringExecutableOn(editor: editor)

executableProcessors.forEach {
Expand Down Expand Up @@ -97,7 +95,6 @@ class TextProcessor: NSObject, NSTextStorageDelegate {

func textStorage(_ textStorage: NSTextStorage, didProcessEditing editedMask: NSTextStorage.EditActions, range editedRange: NSRange, changeInLength delta: Int) {
guard let editor = editor else { return }
let editedMask = getEditedMask(delta: delta)
let executableProcessors = filteringExecutableOn(editor: editor)

executableProcessors.forEach {
Expand All @@ -113,18 +110,6 @@ class TextProcessor: NSObject, NSTextStorageDelegate {
}
}

// The editedMask is computed here as fixing the actual bug in PRTextStorage.replaceCharacter ([self edited:])
// causing incorrect editedMask coming-in in this delegate causes TableViewAttachmentSnapshotTests.testRendersTableViewAttachmentInViewportRotation
// to hang, possibly due to persistent layout invalidations. This can be fixed if cell has foreApplyAttributedText on
// which ensures TextStorage to always be consistent state. However, given that there is some unknown, the proper fix
// in PRTextStorage will be added at a later time. It may include dropping need for forceApplyAttributedText.
private func getEditedMask(delta: Int) -> NSTextStorage.EditActions {
guard delta != 0 else {
return .editedAttributes
}
return [.editedCharacters, .editedAttributes]
}

private func notifyInterruption(by processor: TextProcessing, editor: EditorView, at range: NSRange) {
let processors = activeProcessors.filter { $0.name != processor.name }
processors.forEach { $0.processInterrupted(editor: editor, at: range) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ class AsyncTextResolverSnapshotTests: SnapshotTestCase {
editor.attributedText = text

viewController.render(size: CGSize(width: 300, height: 170))
assertSnapshot(matching: viewController.view, as: .image, record: recordMode)
assertSnapshot(of: viewController.view, as: .image, record: recordMode)

DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) {
viewController.render(size: CGSize(width: 300, height: 170))
assertSnapshot(matching: viewController.view, as: Snapshotting.image, record: self.recordMode)
assertSnapshot(of: viewController.view, as: Snapshotting.image, record: self.recordMode)
expectation.fulfill()
}

Expand All @@ -64,11 +64,11 @@ class AsyncTextResolverSnapshotTests: SnapshotTestCase {
editor.attributedText = text

viewController.render(size: CGSize(width: 300, height: 170))
assertSnapshot(matching: viewController.view, as: .image, record: recordMode)
assertSnapshot(of: viewController.view, as: .image, record: recordMode)

DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) {
viewController.render(size: CGSize(width: 300, height: 170))
assertSnapshot(matching: viewController.view, as: Snapshotting.image, record: self.recordMode)
assertSnapshot(of: viewController.view, as: Snapshotting.image, record: self.recordMode)
expectation.fulfill()
}

Expand All @@ -91,11 +91,11 @@ class AsyncTextResolverSnapshotTests: SnapshotTestCase {
editor.replaceCharacters(in: NSRange(location: 65, length: 0), with: NSAttributedString(string: " "))

viewController.render(size: CGSize(width: 300, height: 170))
assertSnapshot(matching: viewController.view, as: .image, record: recordMode)
assertSnapshot(of: viewController.view, as: .image, record: recordMode)

DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) {
viewController.render(size: CGSize(width: 300, height: 170))
assertSnapshot(matching: viewController.view, as: Snapshotting.image, record: self.recordMode)
assertSnapshot(of: viewController.view, as: Snapshotting.image, record: self.recordMode)

let attributeRange = viewController.editor.attributedText.rangeOf(attribute: .asyncTextResolver, startingLocation: 0)
XCTAssertNil(attributeRange)
Expand Down
20 changes: 10 additions & 10 deletions Proton/Tests/Attachments/AttachmentUpdateSnapshotTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class AttachmentUpdateSnapshotTests: SnapshotTestCase {
textView.replaceCharacters(in: textView.textEndRange, with: "Text after attachment")

viewController.render()
assertSnapshot(matching: viewController.view, as: .image, record: recordMode)
assertSnapshot(of: viewController.view, as: .image, record: recordMode)
}

func testRendersImageBasedBlockAttachment() {
Expand All @@ -51,7 +51,7 @@ class AttachmentUpdateSnapshotTests: SnapshotTestCase {
textView.replaceCharacters(in: textView.textEndRange, with: "Text after attachment")

viewController.render()
assertSnapshot(matching: viewController.view, as: .image, record: recordMode)
assertSnapshot(of: viewController.view, as: .image, record: recordMode)
}

func testRendersUpdatedImageInAttachment() {
Expand All @@ -65,10 +65,10 @@ class AttachmentUpdateSnapshotTests: SnapshotTestCase {
textView.replaceCharacters(in: textView.textEndRange, with: "Text after attachment")

viewController.render()
assertSnapshot(matching: viewController.view, as: .image, record: recordMode)
assertSnapshot(of: viewController.view, as: .image, record: recordMode)
attachment.update(with: AttachmentImage(name: EditorContent.Name("image"), image: UIImage(systemName: "car")!, size: CGSize(width: 40, height: 80), type: .block))
viewController.render(size: CGSize(width: 300, height: 150))
assertSnapshot(matching: viewController.view, as: .image, record: recordMode)
assertSnapshot(of: viewController.view, as: .image, record: recordMode)
}

func testRendersUpdatedViewInAttachment() {
Expand All @@ -93,10 +93,10 @@ class AttachmentUpdateSnapshotTests: SnapshotTestCase {
textView.replaceCharacters(in: textView.textEndRange, with: "Text after attachment")

viewController.render()
assertSnapshot(matching: viewController.view, as: .image, record: recordMode)
assertSnapshot(of: viewController, as: .image, record: recordMode)
attachment.update(panel, size: .fullWidth)
viewController.render(size: CGSize(width: 300, height: 150))
assertSnapshot(matching: viewController.view, as: .image, record: recordMode)
assertSnapshot(of: viewController, as: .image, record: recordMode)
}

func testRendersUpdatedViewInImageAttachment() {
Expand All @@ -118,11 +118,11 @@ class AttachmentUpdateSnapshotTests: SnapshotTestCase {
textView.replaceCharacters(in: textView.textEndRange, with: "Text after attachment")

viewController.render()
assertSnapshot(matching: viewController.view, as: .image, record: recordMode)
assertSnapshot(of: viewController.view, as: .image, record: recordMode)

attachment.update(panel, size: .fullWidth)
viewController.render(size: CGSize(width: 300, height: 150))
assertSnapshot(matching: viewController.view, as: .image, record: recordMode)
assertSnapshot(of: viewController.view, as: .image, record: recordMode)
}


Expand All @@ -142,10 +142,10 @@ class AttachmentUpdateSnapshotTests: SnapshotTestCase {
textView.replaceCharacters(in: textView.textEndRange, with: "Text after attachment")

viewController.render()
assertSnapshot(matching: viewController.view, as: .image, record: recordMode)
assertSnapshot(of: viewController.view, as: .image, record: recordMode)

attachment.update(with: AttachmentImage(name: EditorContent.Name("image"), image: UIImage(systemName: "car.2.fill")!, size: CGSize(width: 80, height: 40), type: .block))
viewController.render(size: CGSize(width: 300, height: 125))
assertSnapshot(matching: viewController.view, as: .image, record: recordMode)
assertSnapshot(of: viewController.view, as: .image, record: recordMode)
}
}
6 changes: 0 additions & 6 deletions Proton/Tests/Attachments/GridViewAttachment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,3 @@ extension GridView: AttachmentViewIdentifying {

public var type: AttachmentType { .block }
}

extension GridView: BackgroundColorObserving {
public func containerEditor(_ editor: EditorView, backgroundColorUpdated color: UIColor?, oldColor: UIColor?) {
backgroundColor = color
}
}
Loading
Loading