@@ -9,56 +9,42 @@ import Foundation
9
9
10
10
extension TextSelectionManager {
11
11
/// 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.
16
14
///
17
15
/// - Parameters:
18
16
/// - rect: The bounding rect of available draw space.
19
17
/// - textSelection: The selection to use.
20
18
/// - Returns: An array of rects that the selection overlaps.
21
19
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 {
30
22
return [ ]
31
23
}
32
24
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 ] = [ ]
41
26
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
45
29
} else {
46
- [ ]
30
+ layoutManager . maxLineLayoutWidth
47
31
}
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)
48
39
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
+ )
58
44
}
59
45
60
46
// 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 }
62
48
}
63
49
64
50
/// Find fill rects for a specific line position.
0 commit comments