Skip to content

Commit

Permalink
Break Up TextLayoutManager
Browse files Browse the repository at this point in the history
  • Loading branch information
thecoolwinter committed Feb 24, 2024
1 parent dd7590b commit 58a095c
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 56 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// TextLayoutManager+Invalidation.swift
// CodeEditTextView
//
// Created by Khan Winter on 2/24/24.
//

import Foundation

extension TextLayoutManager {
/// Invalidates layout for the given rect.
/// - Parameter rect: The rect to invalidate.
public func invalidateLayoutForRect(_ rect: NSRect) {
for linePosition in lineStorage.linesStartingAt(rect.minY, until: rect.maxY) {
linePosition.data.setNeedsLayout()
}
layoutLines()
}

/// Invalidates layout for the given range of text.
/// - Parameter range: The range of text to invalidate.
public func invalidateLayoutForRange(_ range: NSRange) {
for linePosition in lineStorage.linesInRange(range) {
linePosition.data.setNeedsLayout()
}

layoutLines()
}

public func setNeedsLayout() {
needsLayout = true
visibleLineIds.removeAll(keepingCapacity: true)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// TextLayoutManager+Transaction.swift
// CodeEditTextView
//
// Created by Khan Winter on 2/24/24.
//

import Foundation

extension TextLayoutManager {
/// Begins a transaction, preventing the layout manager from performing layout until the `endTransaction` is called.
/// Useful for grouping attribute modifications into one layout pass rather than laying out every update.
///
/// You can nest transaction start/end calls, the layout manager will not cause layout until the last transaction
/// group is ended.
///
/// Ensure there is a balanced number of begin/end calls. If there is a missing endTranscaction call, the layout
/// manager will never lay out text. If there is a end call without matching a start call an assertionFailure
/// will occur.
public func beginTransaction() {
transactionCounter += 1
}

/// Ends a transaction. When called, the layout manager will layout any necessary lines.
public func endTransaction(forceLayout: Bool = false) {
transactionCounter -= 1
if transactionCounter == 0 {
if forceLayout {
setNeedsLayout()
}
layoutLines()
} else if transactionCounter < 0 {
// swiftlint:disable:next line_length
assertionFailure("TextLayoutManager.endTransaction called without a matching TextLayoutManager.beginTransaction call")
}
}
}
59 changes: 3 additions & 56 deletions Sources/CodeEditTextView/TextLayoutManager/TextLayoutManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ public class TextLayoutManager: NSObject {
var lineStorage: TextLineStorage<TextLine> = TextLineStorage()
var markedTextManager: MarkedTextManager = MarkedTextManager()
private let viewReuseQueue: ViewReuseQueue<LineFragmentView, UUID> = ViewReuseQueue()
private var visibleLineIds: Set<TextLine.ID> = []
package var visibleLineIds: Set<TextLine.ID> = []
/// Used to force a complete re-layout using `setNeedsLayout`
private var needsLayout: Bool = false
package var needsLayout: Bool = false

private var transactionCounter: Int = 0
package var transactionCounter: Int = 0
public var isInTransaction: Bool {
transactionCounter > 0
}
Expand Down Expand Up @@ -186,59 +186,6 @@ public class TextLayoutManager: NSObject {
/// ``TextLayoutManager/estimateLineHeight()`` is called.
private var _estimateLineHeight: CGFloat?

// MARK: - Invalidation

/// Invalidates layout for the given rect.
/// - Parameter rect: The rect to invalidate.
public func invalidateLayoutForRect(_ rect: NSRect) {
for linePosition in lineStorage.linesStartingAt(rect.minY, until: rect.maxY) {
linePosition.data.setNeedsLayout()
}
layoutLines()
}

/// Invalidates layout for the given range of text.
/// - Parameter range: The range of text to invalidate.
public func invalidateLayoutForRange(_ range: NSRange) {
for linePosition in lineStorage.linesInRange(range) {
linePosition.data.setNeedsLayout()
}

layoutLines()
}

public func setNeedsLayout() {
needsLayout = true
visibleLineIds.removeAll(keepingCapacity: true)
}

/// Begins a transaction, preventing the layout manager from performing layout until the `endTransaction` is called.
/// Useful for grouping attribute modifications into one layout pass rather than laying out every update.
///
/// You can nest transaction start/end calls, the layout manager will not cause layout until the last transaction
/// group is ended.
///
/// Ensure there is a balanced number of begin/end calls. If there is a missing endTranscaction call, the layout
/// manager will never lay out text. If there is a end call without matching a start call an assertionFailure
/// will occur.
public func beginTransaction() {
transactionCounter += 1
}

/// Ends a transaction. When called, the layout manager will layout any necessary lines.
public func endTransaction(forceLayout: Bool = false) {
transactionCounter -= 1
if transactionCounter == 0 {
if forceLayout {
setNeedsLayout()
}
layoutLines()
} else if transactionCounter < 0 {
// swiftlint:disable:next line_length
assertionFailure("TextLayoutManager.endTransaction called without a matching TextLayoutManager.beginTransaction call")
}
}

// MARK: - Layout

/// Lays out all visible lines
Expand Down

0 comments on commit 58a095c

Please sign in to comment.