Skip to content

Commit

Permalink
General Performance Improvements (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
thecoolwinter authored Feb 21, 2024
1 parent e4d4853 commit 7d2412c
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 13 deletions.
2 changes: 2 additions & 0 deletions Sources/CodeEditTextView/Cursors/CursorView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ open class CursorView: NSView {
true
}

override open func hitTest(_ point: NSPoint) -> NSView? { nil }

/// Create a cursor view.
/// - Parameters:
/// - blinkDuration: The duration to blink, leave as nil to never blink.
Expand Down
24 changes: 19 additions & 5 deletions Sources/CodeEditTextView/TextLayoutManager/TextLayoutManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,17 @@ public class TextLayoutManager: NSObject {
}
}

/// The amount of extra vertical padding used to lay out lines in before they come into view.
///
/// This solves a small problem with layout performance, if you're seeing layout lagging behind while scrolling,
/// adjusting this value higher may help fix that.
/// Defaults to `350`.
public var verticalLayoutPadding: CGFloat = 350 {
didSet {
setNeedsLayout()
}
}

// MARK: - Internal

weak var textStorage: NSTextStorage?
Expand Down Expand Up @@ -215,10 +226,12 @@ public class TextLayoutManager: NSObject {
}

/// Ends a transaction. When called, the layout manager will layout any necessary lines.
public func endTransaction() {
public func endTransaction(forceLayout: Bool = false) {
transactionCounter -= 1
if transactionCounter == 0 {
setNeedsLayout()
if forceLayout {
setNeedsLayout()
}
layoutLines()
} else if transactionCounter < 0 {
// swiftlint:disable:next line_length
Expand All @@ -237,8 +250,8 @@ public class TextLayoutManager: NSObject {
return
}
CATransaction.begin()
let minY = max(visibleRect.minY, 0)
let maxY = max(visibleRect.maxY, 0)
let minY = max(visibleRect.minY - verticalLayoutPadding, 0)
let maxY = max(visibleRect.maxY + verticalLayoutPadding, 0)
let originalHeight = lineStorage.height
var usedFragmentIDs = Set<UUID>()
var forceLayout: Bool = needsLayout
Expand Down Expand Up @@ -283,6 +296,8 @@ public class TextLayoutManager: NSObject {
newVisibleLines.insert(linePosition.data.id)
}

CATransaction.commit()

// Enqueue any lines not used in this layout pass.
viewReuseQueue.enqueueViews(notInSet: usedFragmentIDs)

Expand All @@ -302,7 +317,6 @@ public class TextLayoutManager: NSObject {
}

needsLayout = false
CATransaction.commit()
}

/// Lays out a single text line.
Expand Down
2 changes: 2 additions & 0 deletions Sources/CodeEditTextView/TextLine/LineFragmentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ final class LineFragmentView: NSView {
false
}

override func hitTest(_ point: NSPoint) -> NSView? { nil }

/// Prepare the view for reuse, clears the line fragment reference.
override func prepareForReuse() {
super.prepareForReuse()
Expand Down
10 changes: 3 additions & 7 deletions Sources/CodeEditTextView/TextView/TextView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -381,15 +381,11 @@ public class TextView: NSView, NSTextContent {
/// - Parameter point: The point to find.
/// - Returns: A view at the given point, if any.
override public func hitTest(_ point: NSPoint) -> NSView? {
// For our purposes, cursor and line fragment views should be transparent from the point of view of
// all other views. So, if the normal hitTest returns one of them, we return `self` instead.
let hitView = super.hitTest(point)

if let hitView, hitView != self,
type(of: hitView) == CursorView.self || type(of: hitView) == LineFragmentView.self {
if visibleRect.contains(point) {
return self
} else {
return super.hitTest(point)
}
return hitView
}

// MARK: - Key Down
Expand Down
2 changes: 1 addition & 1 deletion Sources/CodeEditTextView/Utils/ViewReuseQueue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public class ViewReuseQueue<View: NSView, Key: Hashable> {
/// Enqueues all views not in the given set.
/// - Parameter outsideSet: The keys who's views should not be enqueued for reuse.
public func enqueueViews(notInSet keys: Set<Key>) {
// Get all keys that are in "use" but not in the given set.
// Get all keys that are currently in "use" but not in the given set, and enqueue them for reuse.
for key in Set(usedViews.keys).subtracting(keys) {
enqueueView(forKey: key)
}
Expand Down

0 comments on commit 7d2412c

Please sign in to comment.