@@ -9,56 +9,42 @@ import Foundation
99
1010extension TextSelectionManager {
1111 /// Calculate a set of rects for a text selection suitable for filling with the selection color to indicate a
12- /// multi-line selection.
13- ///
14- /// The returned rects are inset by edge insets passed to the text view, the given `rect` parameter can be the 'raw'
15- /// rect to draw in, no need to inset it before this method call.
12+ /// multi-line selection. The returned rects surround all selected line fragments for the given selection,
13+ /// following the available text layout space, rather than the available selection layout space.
1614 ///
1715 /// - Parameters:
1816 /// - rect: The bounding rect of available draw space.
1917 /// - textSelection: The selection to use.
2018 /// - Returns: An array of rects that the selection overlaps.
2119 func getFillRects(in rect: NSRect, for textSelection: TextSelection) -> [CGRect] {
22- guard let layoutManager else { return [] }
23- let range = textSelection.range
24-
25- var fillRects: [CGRect] = []
26- guard let firstLinePosition = layoutManager.lineStorage.getLine(atOffset: range.location),
27- let lastLinePosition = range.max == layoutManager.lineStorage.length
28- ? layoutManager.lineStorage.last
29- : layoutManager.lineStorage.getLine(atOffset: range.max) else {
20+ guard let layoutManager,
21+ let range = textSelection.range.intersection(textView?.visibleTextRange ?? .zero) else {
3022 return []
3123 }
3224
33- let insetXPos = max(edgeInsets.left, rect.minX)
34- let insetWidth = max(0, rect.maxX - insetXPos - edgeInsets.right)
35- let insetRect = NSRect(x: insetXPos, y: rect.origin.y, width: insetWidth, height: rect.height)
36-
37- // Calculate the first line and any rects selected
38- // If the last line position is not the same as the first, calculate any rects from that line.
39- // If there's > 0 space between the first and last positions, add a rect between them to cover any
40- // intermediate lines.
25+ var fillRects: [CGRect] = []
4126
42- let firstLineRects = getFillRects(in: rect, selectionRange: range, forPosition: firstLinePosition)
43- let lastLineRects: [CGRect] = if lastLinePosition.range != firstLinePosition.range {
44- getFillRects(in: rect, selectionRange: range, forPosition: lastLinePosition)
27+ let textWidth = if layoutManager.maxLineLayoutWidth == .greatestFiniteMagnitude {
28+ layoutManager.maxLineWidth
4529 } else {
46- []
30+ layoutManager.maxLineLayoutWidth
4731 }
32+ let maxWidth = max(textWidth, layoutManager.wrapLinesWidth)
33+ let validTextDrawingRect = CGRect(
34+ x: layoutManager.edgeInsets.left,
35+ y: rect.minY,
36+ width: maxWidth,
37+ height: rect.height
38+ ).intersection(rect)
4839
49- fillRects.append(contentsOf: firstLineRects + lastLineRects)
50-
51- if firstLinePosition.yPos + firstLinePosition.height < lastLinePosition.yPos {
52- fillRects.append(CGRect(
53- x: insetXPos,
54- y: firstLinePosition.yPos + firstLinePosition.height,
55- width: insetWidth,
56- height: lastLinePosition.yPos - (firstLinePosition.yPos + firstLinePosition.height)
57- ))
40+ for linePosition in layoutManager.lineStorage.linesInRange(range) {
41+ fillRects.append(
42+ contentsOf: getFillRects(in: validTextDrawingRect, selectionRange: range, forPosition: linePosition)
43+ )
5844 }
5945
6046 // Pixel align these to avoid aliasing on the edges of each rect that should be a solid box.
61- return fillRects.map { $0.intersection(insetRect ).pixelAligned }
47+ return fillRects.map { $0.intersection(validTextDrawingRect ).pixelAligned }
6248 }
6349
6450 /// Find fill rects for a specific line position.
0 commit comments