Skip to content

Commit

Permalink
Prevent recycling of cells if it has focus (#339)
Browse files Browse the repository at this point in the history
  • Loading branch information
rajdeep authored Sep 6, 2024
1 parent 0fe6df3 commit 162bf52
Show file tree
Hide file tree
Showing 8 changed files with 84 additions and 12 deletions.
9 changes: 9 additions & 0 deletions Proton/Sources/Swift/Editor/EditorView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,15 @@ open class EditorView: UIView {
richTextView.isFirstResponder
}


/// Describes if one of the nested editor is first responder
/// - Returns: `true` if a nested editor is first responder.
/// - Note:
/// To check if current Editor itself is first responder, use `isFirstResponder()`.
public func containsFirstResponder() -> Bool {
nestedEditors.contains(where: { $0.isFirstResponder() })
}

/// Resets typing attributes back to default text color, font and paragraph style.
///All other attributes are dropped.
open func resetTypingAttributes() {
Expand Down
4 changes: 4 additions & 0 deletions Proton/Sources/Swift/Table/TableCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ public class TableCell {
}
}

public var containsFirstResponder: Bool {
editor?.isFirstResponder() == true || editor?.containsFirstResponder() == true
}

let editorInitializer: EditorInitializer

/// Controls if the cell can be selected or not.
Expand Down
22 changes: 10 additions & 12 deletions Proton/Sources/Swift/Table/TableView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -421,20 +421,19 @@ public class TableView: UIView {
let toGenerate = newCells.subtracting(oldCells)
let toReclaim = oldCells.subtracting(newCells)

// Required to reset the focus to an editor within viewport.
// In absence of this check, if the editor having focus gets reclaimed,
// the focus moves to root editor which may cause the content to be scrolled
// out to end of the root editor.
let needsFocusChange = toReclaim.contains(where: { $0.editor?.isFirstResponder() == true })
if needsFocusChange {
containerAttachment?.containerEditorView?.rootEditor.endEditing(true)
}
toReclaim.forEach { [weak self] cell in
self?.repository.enqueue(cell: cell)
// Ignore reclaiming the cell if it has focus
if cell.containsFirstResponder == false {
self?.repository.enqueue(cell: cell)
}
}

toGenerate.forEach { [weak self] in
self?.repository.dequeue(for: $0)
toGenerate.forEach { [weak self] cell in
// Ignore generating the cell if it has focus. The focussed cell is not reclaimed, hence need not be regenerated.
// In absence of this check, there may be cases where the focussed cell gets duplicated
if cell.containsFirstResponder == false {
self?.repository.dequeue(for: cell)
}
}
}
}
Expand Down Expand Up @@ -810,7 +809,6 @@ public class TableView: UIView {

extension TableView: UIScrollViewDelegate {
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
cellsInViewport.first { $0.editor?.isFirstResponder() == true }?.editor?.resignFocus()
resetShadows()
viewportChanged()
}
Expand Down
61 changes: 61 additions & 0 deletions Proton/Tests/Table/TableViewAttachmentSnapshotTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1065,6 +1065,67 @@ class TableViewAttachmentSnapshotTests: SnapshotTestCase {
assertSnapshot(matching: viewController.view, as: .image, record: recordMode)
}

func FLAKY_testPreventsRecyclingFocussedCell() {
var viewport = CGRect(x: 0, y: 100, width: 350, height: 200)
delegate.viewport = viewport

Utility.drawRect(rect: viewport, color: .red, in: editor)

let attachment = AttachmentGenerator.makeTableViewAttachment(id: 1, numRows: 20, numColumns: 5)
attachment.view.delegate = delegate
editor.replaceCharacters(in: .zero, with: "Some text in editor")
editor.insertAttachment(in: editor.textEndRange, attachment: attachment)
editor.replaceCharacters(in: editor.textEndRange, with: "Text after grid")

XCTAssertEqual(attachment.view.containerAttachment, attachment)

viewController.render(size: CGSize(width: 400, height: 700))
assertSnapshot(matching: viewController.view, as: .image, record: recordMode)

let cell10 = attachment.view.cellAt(rowIndex: 1, columnIndex: 0)
cell10?.editor?.setFocus()

viewport = CGRect(x: 0, y: 300, width: 350, height: 200)
delegate.viewport = viewport
attachment.view.scrollViewDidScroll(editor.scrollView)

Utility.drawRect(rect: viewport, color: .red, in: editor)
viewController.render(size: CGSize(width: 400, height: 700))
assertSnapshot(matching: viewController.view, as: .image, record: recordMode)
}

func FLAKY_testPreventsRecyclingNestedEditorFocussedCell() {
var viewport = CGRect(x: 0, y: 100, width: 350, height: 200)
delegate.viewport = viewport

Utility.drawRect(rect: viewport, color: .red, in: editor)

let attachment = AttachmentGenerator.makeTableViewAttachment(id: 1, numRows: 20, numColumns: 5)
attachment.view.delegate = delegate
editor.replaceCharacters(in: .zero, with: "Some text in editor")
editor.insertAttachment(in: editor.textEndRange, attachment: attachment)
editor.replaceCharacters(in: editor.textEndRange, with: "Text after grid")

XCTAssertEqual(attachment.view.containerAttachment, attachment)

let cell10 = attachment.view.cellAt(rowIndex: 1, columnIndex: 0)
cell10?.attributedText = NSAttributedString(attachment: makePanelAttachment())
let panel = ((cell10?.attributedText?.attachmentRanges[0].attachment as? Attachment)?.contentView as? PanelView)
panel?.editor.setFocus()

viewController.render(size: CGSize(width: 400, height: 700))
assertSnapshot(matching: viewController.view, as: .image, record: recordMode)


viewport = CGRect(x: 0, y: 300, width: 350, height: 200)
delegate.viewport = viewport
attachment.view.scrollViewDidScroll(editor.scrollView)

Utility.drawRect(rect: viewport, color: .red, in: editor)
viewController.render(size: CGSize(width: 400, height: 700))
assertSnapshot(matching: viewController.view, as: .image, record: recordMode)
}

func testRendersTableViewAttachmentInViewportRotation() {
var viewport = CGRect(x: 0, y: 100, width: 350, height: 200)
delegate.viewport = viewport
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 162bf52

Please sign in to comment.