From 41ea36f8acf1d09ab91b0ef8d93650b281ad970b Mon Sep 17 00:00:00 2001 From: Khan Winter <35942988+thecoolwinter@users.noreply.github.com> Date: Thu, 9 Jan 2025 21:52:25 -0600 Subject: [PATCH] Inset Selection Rect By Edge Insets (#60) ### Description Insets the drawn selection box by the text view's edge insets. ### Related Issues * #59 ### Checklist - [x] I read and understood the [contributing guide](https://github.com/CodeEditApp/CodeEdit/blob/main/CONTRIBUTING.md) as well as the [code of conduct](https://github.com/CodeEditApp/CodeEdit/blob/main/CODE_OF_CONDUCT.md) - [x] The issues this PR addresses are related to each other - [x] My changes generate no new warnings - [x] My code builds and runs on my machine - [x] My changes are all related to the related issue above - [x] I documented my code ### Screenshots https://github.com/user-attachments/assets/74f8a8bd-4467-4b65-b068-96544c230dad --- .../TextSelectionManager+FillRects.swift | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/Sources/CodeEditTextView/TextSelectionManager/TextSelectionManager+FillRects.swift b/Sources/CodeEditTextView/TextSelectionManager/TextSelectionManager+FillRects.swift index 5b3b9a2b..46f56758 100644 --- a/Sources/CodeEditTextView/TextSelectionManager/TextSelectionManager+FillRects.swift +++ b/Sources/CodeEditTextView/TextSelectionManager/TextSelectionManager+FillRects.swift @@ -8,7 +8,12 @@ import Foundation extension TextSelectionManager { - /// Calculate a set of rects for a text selection suitable for highlighting the selection. + /// Calculate a set of rects for a text selection suitable for filling with the selection color to indicate a + /// multi-line selection. + /// + /// The returned rects are inset by edge insets passed to the text view, the given `rect` parameter can be the 'raw' + /// rect to draw in, no need to inset it before this method call. + /// /// - Parameters: /// - rect: The bounding rect of available draw space. /// - textSelection: The selection to use. @@ -25,27 +30,35 @@ extension TextSelectionManager { return [] } + let insetXPos = max(layoutManager.edgeInsets.left, rect.minX) + let insetWidth = max(0, rect.maxX - insetXPos - layoutManager.edgeInsets.right) + let insetRect = NSRect(x: insetXPos, y: rect.origin.y, width: insetWidth, height: rect.height) + // Calculate the first line and any rects selected // If the last line position is not the same as the first, calculate any rects from that line. // If there's > 0 space between the first and last positions, add a rect between them to cover any // intermediate lines. - fillRects.append(contentsOf: getFillRects(in: rect, selectionRange: range, forPosition: firstLinePosition)) - - if lastLinePosition.range != firstLinePosition.range { - fillRects.append(contentsOf: getFillRects(in: rect, selectionRange: range, forPosition: lastLinePosition)) + let firstLineRects = getFillRects(in: rect, selectionRange: range, forPosition: firstLinePosition) + let lastLineRects: [CGRect] = if lastLinePosition.range != firstLinePosition.range { + getFillRects(in: rect, selectionRange: range, forPosition: lastLinePosition) + } else { + [] } + fillRects.append(contentsOf: firstLineRects + lastLineRects) + if firstLinePosition.yPos + firstLinePosition.height < lastLinePosition.yPos { fillRects.append(CGRect( - x: rect.minX, + x: insetXPos, y: firstLinePosition.yPos + firstLinePosition.height, - width: rect.width, + width: insetWidth, height: lastLinePosition.yPos - (firstLinePosition.yPos + firstLinePosition.height) )) } - return fillRects + // Pixel align these to avoid aliasing on the edges of each rect that should be a solid box. + return fillRects.map { $0.intersection(insetRect).pixelAligned } } /// Find fill rects for a specific line position.