diff --git a/README.md b/README.md
index c6fc6af..2caa83c 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,6 @@ A high-performance markdown rendering library for iOS, macOS, and visionOS.
- Inline image rendering with async loading and caching
- Comprehensive theming with fonts, colors, and spacing
- Text selection with long-press, double-tap, and triple-tap gestures
-- Line selection in code and diff views with tap (single line) or long-press-drag (multi-line) and callback
- VoiceOver accessibility for text, code blocks, tables, and math content
- UIKit and AppKit support via a single API
@@ -214,8 +213,6 @@ theme.colors.selectionTint = .systemBlue
// Optional override if you want a custom translucent fill instead of
// selectionTint.withAlphaComponent(0.2)
theme.colors.selectionBackground = .systemBlue.withAlphaComponent(0.16)
-// Optional override for line selection highlight in code/diff views
-theme.colors.lineSelectionBackground = .systemBlue.withAlphaComponent(0.15)
// Diff-specific styling
theme.diff.backgroundColor = .black
@@ -240,36 +237,6 @@ markdownView.theme = theme
Selection tint is theme-driven. By default, `selectionBackground` is derived from `selectionTint` with a 20% alpha. Set `selectionBackground` explicitly when you want a different selection fill without changing the tint.
-### Line Selection
-
-Code blocks and diff views support tapping to select a line, or long-press-and-drag to select a range of lines. The selected lines are highlighted and a callback provides the 1-based line range, the text contents, and the language.
-
-
-Show line selection example
-
-```swift
-markdownView.lineSelectionHandler = { info in
- guard let info else {
- print("Selection cleared")
- return
- }
- print("Selected lines \(info.lineRange) in \(info.language ?? "unknown"):")
- for line in info.contents {
- print(" \(line)")
- }
-}
-
-// Customize the selection highlight color
-var theme = MarkdownTheme()
-theme.colors.lineSelectionBackground = .systemBlue.withAlphaComponent(0.2)
-markdownView.theme = theme
-```
-
-
-
-Selection is exclusive: selecting lines in one code or diff block automatically clears any selection in other blocks.
-
-
### Unified Diffs
@@ -344,3 +311,6 @@ The library is split into two modules:
MIT
+## Inspiration
+
+Inspired by [Lakr233/MarkdownView](https://github.com/Lakr233/MarkdownView)
diff --git a/Sources/MarkdownView/Components/CodeView/CodeView.swift b/Sources/MarkdownView/Components/CodeView/CodeView.swift
index 457741f..bd6255b 100644
--- a/Sources/MarkdownView/Components/CodeView/CodeView.swift
+++ b/Sources/MarkdownView/Components/CodeView/CodeView.swift
@@ -42,7 +42,6 @@ import Litext
textView.attributedText = highlightMap.apply(to: content, with: theme)
lineNumberView.updateForContent(content)
updateLineNumberView()
- clearLineSelection()
}
}
@@ -56,96 +55,6 @@ import Litext
}
}
- // MARK: - LINE SELECTION
-
- var lineSelectionHandler: LineSelectionHandler?
- private(set) var selectedLineRange: ClosedRange?
- private lazy var selectionOverlay: LineSelectionOverlayView = .init()
- private var dragAnchorLine: Int?
-
- func clearLineSelection() {
- guard selectedLineRange != nil else { return }
- selectedLineRange = nil
- selectionOverlay.clearSelection()
- }
-
- private func lineIndex(at point: CGPoint) -> Int? {
- let localPoint = scrollView.convert(point, from: self)
- let contentPoint = CGPoint(
- x: localPoint.x + scrollView.contentOffset.x,
- y: localPoint.y + scrollView.contentOffset.y
- )
- let font = theme.fonts.code
- let lineHeight = font.lineHeight
- let rowAdvance = lineHeight + CodeViewConfiguration.codeLineSpacing
- let barHeight = CodeViewConfiguration.barHeight(theme: theme)
- let adjustedY = contentPoint.y
- guard adjustedY >= CodeViewConfiguration.codePadding else { return nil }
- let line = Int((adjustedY - CodeViewConfiguration.codePadding) / rowAdvance) + 1
- guard line >= 1, line <= cachedLineCount else { return nil }
- return line
- }
-
- private func updateLineSelection(_ range: ClosedRange?) {
- selectedLineRange = range
- selectionOverlay.selectedRange = range
- if let range = range {
- let lines = content.components(separatedBy: .newlines)
- let contents = (range.lowerBound...range.upperBound).compactMap { idx -> String? in
- let arrayIdx = idx - 1
- guard arrayIdx >= 0, arrayIdx < lines.count else { return nil }
- return lines[arrayIdx]
- }
- let info = LineSelectionInfo(
- lineRange: range,
- contents: contents,
- language: language.isEmpty ? nil : language
- )
- lineSelectionHandler?(info)
- } else {
- lineSelectionHandler?(nil)
- }
- }
-
- @objc private func handleLineTap(_ gesture: UITapGestureRecognizer) {
- let point = gesture.location(in: self)
- guard let line = lineIndex(at: point) else { return }
- if selectedLineRange == line...line {
- updateLineSelection(nil)
- } else {
- updateLineSelection(line...line)
- }
- #if !os(visionOS)
- UIImpactFeedbackGenerator(style: .light).impactOccurred()
- #endif
- }
-
- @objc private func handleLineLongPress(_ gesture: UILongPressGestureRecognizer) {
- let point = gesture.location(in: self)
- switch gesture.state {
- case .began:
- guard let line = lineIndex(at: point) else { return }
- dragAnchorLine = line
- updateLineSelection(line...line)
- #if !os(visionOS)
- UIImpactFeedbackGenerator(style: .light).impactOccurred()
- #endif
- case .changed:
- guard let anchor = dragAnchorLine,
- let line = lineIndex(at: point) else { return }
- let newRange = min(anchor, line)...max(anchor, line)
- if newRange != selectedLineRange {
- updateLineSelection(newRange)
- }
- case .ended, .cancelled, .failed:
- dragAnchorLine = nil
- default:
- break
- }
- }
-
- // MARK: LINE SELECTION -
-
private let callerIdentifier = UUID()
private var currentTaskIdentifier: UUID?
@@ -304,7 +213,6 @@ import Litext
textView.attributedText = highlightMap.apply(to: content, with: theme)
lineNumberView.updateForContent(content)
updateLineNumberView()
- clearLineSelection()
}
}
@@ -316,84 +224,6 @@ import Litext
}
}
- // MARK: - LINE SELECTION
-
- var lineSelectionHandler: LineSelectionHandler?
- private(set) var selectedLineRange: ClosedRange?
- private lazy var selectionOverlay: LineSelectionOverlayView = .init()
- private var dragAnchorLine: Int?
-
- func clearLineSelection() {
- guard selectedLineRange != nil else { return }
- selectedLineRange = nil
- selectionOverlay.clearSelection()
- }
-
- private func lineIndex(at point: CGPoint) -> Int? {
- let localPoint = convert(point, from: nil)
- let font = theme.fonts.code
- let lineHeight = font.ascender + abs(font.descender) + font.leading
- let rowAdvance = lineHeight + CodeViewConfiguration.codeLineSpacing
- let barHeight = CodeViewConfiguration.barHeight(theme: theme)
- let adjustedY = localPoint.y - barHeight
- guard adjustedY >= CodeViewConfiguration.codePadding else { return nil }
- let line = Int((adjustedY - CodeViewConfiguration.codePadding) / rowAdvance) + 1
- guard line >= 1, line <= cachedLineCount else { return nil }
- return line
- }
-
- private func updateLineSelection(_ range: ClosedRange?) {
- selectedLineRange = range
- selectionOverlay.selectedRange = range
- if let range = range {
- let lines = content.components(separatedBy: .newlines)
- let contents = (range.lowerBound...range.upperBound).compactMap { idx -> String? in
- let arrayIdx = idx - 1
- guard arrayIdx >= 0, arrayIdx < lines.count else { return nil }
- return lines[arrayIdx]
- }
- let info = LineSelectionInfo(
- lineRange: range,
- contents: contents,
- language: language.isEmpty ? nil : language
- )
- lineSelectionHandler?(info)
- } else {
- lineSelectionHandler?(nil)
- }
- }
-
- override func mouseDown(with event: NSEvent) {
- let point = convert(event.locationInWindow, from: nil)
- guard let line = lineIndex(at: point) else {
- super.mouseDown(with: event)
- return
- }
- dragAnchorLine = line
- if selectedLineRange == line...line {
- updateLineSelection(nil)
- } else {
- updateLineSelection(line...line)
- }
- }
-
- override func mouseDragged(with event: NSEvent) {
- let point = convert(event.locationInWindow, from: nil)
- guard let anchor = dragAnchorLine,
- let line = lineIndex(at: point) else { return }
- let newRange = min(anchor, line)...max(anchor, line)
- if newRange != selectedLineRange {
- updateLineSelection(newRange)
- }
- }
-
- override func mouseUp(with event: NSEvent) {
- dragAnchorLine = nil
- super.mouseUp(with: event)
- }
-
- // MARK: LINE SELECTION -
-
private let callerIdentifier = UUID()
private var currentTaskIdentifier: UUID?
diff --git a/Sources/MarkdownView/Components/CodeView/CodeViewConfiguration.swift b/Sources/MarkdownView/Components/CodeView/CodeViewConfiguration.swift
index 2615872..4dd9461 100644
--- a/Sources/MarkdownView/Components/CodeView/CodeViewConfiguration.swift
+++ b/Sources/MarkdownView/Components/CodeView/CodeViewConfiguration.swift
@@ -76,7 +76,6 @@ enum CodeViewConfiguration {
setupScrollView()
setupTextView()
setupLineNumberView()
- setupLineSelectionGestures()
updateHeaderVisibility()
}
@@ -143,21 +142,6 @@ enum CodeViewConfiguration {
updateLineNumberView()
}
- private func setupLineSelectionGestures() {
- selectionOverlay.isUserInteractionEnabled = false
- let selectionColor = theme.colors.lineSelectionBackground
- ?? theme.colors.selectionTint.withAlphaComponent(0.15)
- selectionOverlay.selectionColor = selectionColor
- scrollView.addSubview(selectionOverlay)
-
- let tap = UITapGestureRecognizer(target: self, action: #selector(handleLineTap(_:)))
- addGestureRecognizer(tap)
-
- let longPress = UILongPressGestureRecognizer(target: self, action: #selector(handleLineLongPress(_:)))
- longPress.minimumPressDuration = 0.15
- addGestureRecognizer(longPress)
- }
-
func performLayout() {
let labelSize = languageLabel.intrinsicContentSize
let barHeight = CodeViewConfiguration.barHeight(theme: theme)
@@ -252,24 +236,12 @@ enum CodeViewConfiguration {
textView.setNeedsLayout()
textView.layoutIfNeeded()
- let resolvedLineRects = offsetCodeLineRects(
- textView.lineRects(),
- by: textView.frame.origin
- )
- lineNumberView.updateLineRects(resolvedLineRects)
-
- selectionOverlay.frame = CGRect(
- origin: .zero,
- size: CGSize(
- width: scrollView.contentSize.width,
- height: max(textView.frame.maxY + CodeViewConfiguration.codePadding, scrollView.bounds.height)
+ lineNumberView.updateLineRects(
+ offsetCodeLineRects(
+ textView.lineRects(),
+ by: textView.frame.origin
)
)
- selectionOverlay.updateLineRects(resolvedLineRects)
-
- let selectionColor = theme.colors.lineSelectionBackground
- ?? theme.colors.selectionTint.withAlphaComponent(0.15)
- selectionOverlay.selectionColor = selectionColor
}
}
@@ -282,7 +254,6 @@ enum CodeViewConfiguration {
setupScrollView()
setupTextView()
setupLineNumberView()
- setupLineSelectionOverlay()
updateHeaderVisibility()
}
@@ -359,13 +330,6 @@ enum CodeViewConfiguration {
updateLineNumberView()
}
- private func setupLineSelectionOverlay() {
- let selectionColor = theme.colors.lineSelectionBackground
- ?? theme.colors.selectionTint.withAlphaComponent(0.15)
- selectionOverlay.selectionColor = selectionColor
- scrollView.documentView?.addSubview(selectionOverlay, positioned: .below, relativeTo: textView)
- }
-
func performLayout() {
let labelSize = languageLabel.intrinsicContentSize
let barHeight = CodeViewConfiguration.barHeight(theme: theme)
@@ -455,24 +419,12 @@ enum CodeViewConfiguration {
textView.needsLayout = true
textView.layoutSubtreeIfNeeded()
- let resolvedLineRects = offsetCodeLineRects(
- textView.lineRects(),
- by: textView.frame.origin
- )
- lineNumberView.updateLineRects(resolvedLineRects)
-
- selectionOverlay.frame = CGRect(
- origin: .zero,
- size: CGSize(
- width: max(scrollView.bounds.width - CodeViewConfiguration.codePadding * 2, textView.frame.width),
- height: max(textView.frame.maxY + CodeViewConfiguration.codePadding, scrollView.bounds.height)
+ lineNumberView.updateLineRects(
+ offsetCodeLineRects(
+ textView.lineRects(),
+ by: textView.frame.origin
)
)
- selectionOverlay.updateLineRects(resolvedLineRects)
-
- let selectionColor = theme.colors.lineSelectionBackground
- ?? theme.colors.selectionTint.withAlphaComponent(0.15)
- selectionOverlay.selectionColor = selectionColor
}
}
#endif
diff --git a/Sources/MarkdownView/Components/DiffView/DiffView.swift b/Sources/MarkdownView/Components/DiffView/DiffView.swift
index a5506ca..bdba05b 100644
--- a/Sources/MarkdownView/Components/DiffView/DiffView.swift
+++ b/Sources/MarkdownView/Components/DiffView/DiffView.swift
@@ -796,7 +796,6 @@ private func makeSideBySideAttributedText(
var renderBlock: DiffRenderBlock = .init(language: nil, rows: []) {
didSet {
- clearLineSelection()
applyRenderBlock()
}
}
@@ -805,101 +804,6 @@ private func makeSideBySideAttributedText(
private var displayRows: DiffDisplayRows = .unified([])
private var sideBySideTextMetrics: DiffSideBySideTextMetrics?
- // MARK: - LINE SELECTION
-
- var lineSelectionHandler: LineSelectionHandler?
- private(set) var selectedLineRange: ClosedRange?
- private lazy var selectionOverlay: LineSelectionOverlayView = .init()
- private var dragAnchorLine: Int?
-
- func clearLineSelection() {
- guard selectedLineRange != nil else { return }
- selectedLineRange = nil
- selectionOverlay.clearSelection()
- }
-
- private func diffRowCount() -> Int {
- displayRows.effectiveCount
- }
-
- private func rowIndex(at point: CGPoint) -> Int? {
- let localPoint = scrollView.convert(point, from: self)
- let contentPoint = CGPoint(
- x: localPoint.x + scrollView.contentOffset.x,
- y: localPoint.y + scrollView.contentOffset.y
- )
- let font = theme.fonts.code
- let lineHeight = font.lineHeight
- let rowAdvance = lineHeight + CodeViewConfiguration.codeLineSpacing
- let adjustedY = contentPoint.y - DiffViewConfiguration.verticalPadding
- guard adjustedY >= 0 else { return nil }
- let row = Int(adjustedY / rowAdvance) + 1
- guard row >= 1, row <= diffRowCount() else { return nil }
- return row
- }
-
- private func rowContents(for range: ClosedRange) -> [String] {
- range.compactMap { idx -> String? in
- let arrayIdx = idx - 1
- guard arrayIdx >= 0, arrayIdx < renderBlock.rows.count else { return nil }
- return renderBlock.rows[arrayIdx].text
- }
- }
-
- private func updateDiffLineSelection(_ range: ClosedRange?) {
- selectedLineRange = range
- selectionOverlay.selectedRange = range
- if let range = range {
- let info = LineSelectionInfo(
- lineRange: range,
- contents: rowContents(for: range),
- language: renderBlock.language
- )
- lineSelectionHandler?(info)
- } else {
- lineSelectionHandler?(nil)
- }
- }
-
- @objc private func handleDiffLineTap(_ gesture: UITapGestureRecognizer) {
- let point = gesture.location(in: self)
- guard let row = rowIndex(at: point) else { return }
- if selectedLineRange == row...row {
- updateDiffLineSelection(nil)
- } else {
- updateDiffLineSelection(row...row)
- }
- #if !os(visionOS)
- UIImpactFeedbackGenerator(style: .light).impactOccurred()
- #endif
- }
-
- @objc private func handleDiffLineLongPress(_ gesture: UILongPressGestureRecognizer) {
- let point = gesture.location(in: self)
- switch gesture.state {
- case .began:
- guard let row = rowIndex(at: point) else { return }
- dragAnchorLine = row
- updateDiffLineSelection(row...row)
- #if !os(visionOS)
- UIImpactFeedbackGenerator(style: .light).impactOccurred()
- #endif
- case .changed:
- guard let anchor = dragAnchorLine,
- let row = rowIndex(at: point) else { return }
- let newRange = min(anchor, row)...max(anchor, row)
- if newRange != selectedLineRange {
- updateDiffLineSelection(newRange)
- }
- case .ended, .cancelled, .failed:
- dragAnchorLine = nil
- default:
- break
- }
- }
-
- // MARK: LINE SELECTION -
-
lazy var scrollView: UIScrollView = .init()
lazy var barView: UIView = .init()
lazy var titleLabel: UILabel = .init()
@@ -1084,12 +988,6 @@ private func makeSideBySideAttributedText(
)
gutterView.updateLineRects(lineRects)
backgroundView.updateLineRects(lineRects)
- selectionOverlay.frame = contentContainerView.bounds
- selectionOverlay.updateLineRects(lineRects)
-
- let selectionColor = theme.colors.lineSelectionBackground
- ?? theme.colors.selectionTint.withAlphaComponent(0.15)
- selectionOverlay.selectionColor = selectionColor
}
private func performLayout() {
@@ -1609,7 +1507,6 @@ private func makeSideBySideAttributedText(
var renderBlock: DiffRenderBlock = .init(language: nil, rows: []) {
didSet {
- clearLineSelection()
applyRenderBlock()
}
}
@@ -1618,90 +1515,6 @@ private func makeSideBySideAttributedText(
private var displayRows: DiffDisplayRows = .unified([])
private var sideBySideTextMetrics: DiffSideBySideTextMetrics?
- // MARK: - LINE SELECTION
-
- var lineSelectionHandler: LineSelectionHandler?
- private(set) var selectedLineRange: ClosedRange?
- private lazy var selectionOverlay: LineSelectionOverlayView = .init()
- private var dragAnchorLine: Int?
-
- func clearLineSelection() {
- guard selectedLineRange != nil else { return }
- selectedLineRange = nil
- selectionOverlay.clearSelection()
- }
-
- private func diffRowCount() -> Int {
- displayRows.effectiveCount
- }
-
- private func rowIndex(at point: CGPoint) -> Int? {
- let localPoint = convert(point, from: nil)
- let barHeight = DiffViewConfiguration.barHeight(theme: theme)
- let font = theme.fonts.code
- let lineHeight = font.ascender + abs(font.descender) + font.leading
- let rowAdvance = lineHeight + CodeViewConfiguration.codeLineSpacing
- let adjustedY = localPoint.y - barHeight - DiffViewConfiguration.verticalPadding
- guard adjustedY >= 0 else { return nil }
- let row = Int(adjustedY / rowAdvance) + 1
- guard row >= 1, row <= diffRowCount() else { return nil }
- return row
- }
-
- private func rowContents(for range: ClosedRange) -> [String] {
- range.compactMap { idx -> String? in
- let arrayIdx = idx - 1
- guard arrayIdx >= 0, arrayIdx < renderBlock.rows.count else { return nil }
- return renderBlock.rows[arrayIdx].text
- }
- }
-
- private func updateDiffLineSelection(_ range: ClosedRange?) {
- selectedLineRange = range
- selectionOverlay.selectedRange = range
- if let range = range {
- let info = LineSelectionInfo(
- lineRange: range,
- contents: rowContents(for: range),
- language: renderBlock.language
- )
- lineSelectionHandler?(info)
- } else {
- lineSelectionHandler?(nil)
- }
- }
-
- override func mouseDown(with event: NSEvent) {
- let point = convert(event.locationInWindow, from: nil)
- guard let row = rowIndex(at: point) else {
- super.mouseDown(with: event)
- return
- }
- dragAnchorLine = row
- if selectedLineRange == row...row {
- updateDiffLineSelection(nil)
- } else {
- updateDiffLineSelection(row...row)
- }
- }
-
- override func mouseDragged(with event: NSEvent) {
- let point = convert(event.locationInWindow, from: nil)
- guard let anchor = dragAnchorLine,
- let row = rowIndex(at: point) else { return }
- let newRange = min(anchor, row)...max(anchor, row)
- if newRange != selectedLineRange {
- updateDiffLineSelection(newRange)
- }
- }
-
- override func mouseUp(with event: NSEvent) {
- dragAnchorLine = nil
- super.mouseUp(with: event)
- }
-
- // MARK: LINE SELECTION -
-
lazy var scrollView: NSScrollView = {
let scrollView = NSScrollView()
scrollView.hasVerticalScroller = false
@@ -1808,12 +1621,6 @@ private func makeSideBySideAttributedText(
textView.isSelectable = true
textView.selectionBackgroundColor = theme.colors.selectionBackground
contentContainerView.addSubview(textView)
-
- let selectionColor = theme.colors.lineSelectionBackground
- ?? theme.colors.selectionTint.withAlphaComponent(0.15)
- selectionOverlay.selectionColor = selectionColor
- contentContainerView.addSubview(selectionOverlay, positioned: .above, relativeTo: backgroundView)
-
updateHeaderVisibility()
}
@@ -1904,12 +1711,6 @@ private func makeSideBySideAttributedText(
)
gutterView.updateLineRects(lineRects)
backgroundView.updateLineRects(lineRects)
- selectionOverlay.frame = contentContainerView.bounds
- selectionOverlay.updateLineRects(lineRects)
-
- let selectionColor = theme.colors.lineSelectionBackground
- ?? theme.colors.selectionTint.withAlphaComponent(0.15)
- selectionOverlay.selectionColor = selectionColor
}
private func performLayout() {
diff --git a/Sources/MarkdownView/Components/LineSelection/LineSelectionOverlayView.swift b/Sources/MarkdownView/Components/LineSelection/LineSelectionOverlayView.swift
deleted file mode 100644
index d3c02cc..0000000
--- a/Sources/MarkdownView/Components/LineSelection/LineSelectionOverlayView.swift
+++ /dev/null
@@ -1,119 +0,0 @@
-#if canImport(UIKit)
- import UIKit
-
- final class LineSelectionOverlayView: UIView {
- var selectionColor: UIColor = .systemBlue.withAlphaComponent(0.15) {
- didSet { setNeedsDisplay() }
- }
-
- var selectedRange: ClosedRange? {
- didSet {
- guard oldValue != selectedRange else { return }
- setNeedsDisplay()
- }
- }
-
- private var lineRects: [CGRect] = []
-
- override init(frame: CGRect) {
- super.init(frame: frame)
- isOpaque = false
- backgroundColor = .clear
- }
-
- @available(*, unavailable)
- required init?(coder _: NSCoder) { fatalError() }
-
- func updateLineRects(_ rects: [CGRect]) {
- lineRects = rects
- setNeedsDisplay()
- }
-
- func clearSelection() {
- selectedRange = nil
- }
-
- override func draw(_ rect: CGRect) {
- guard let range = selectedRange,
- let ctx = UIGraphicsGetCurrentContext()
- else { return }
-
- ctx.setFillColor(selectionColor.cgColor)
-
- for lineIndex in range {
- let arrayIndex = lineIndex - 1
- guard arrayIndex >= 0, arrayIndex < lineRects.count else { continue }
- let lineRect = lineRects[arrayIndex]
- // Extend selection across full width
- let highlightRect = CGRect(
- x: 0,
- y: lineRect.origin.y,
- width: bounds.width,
- height: lineRect.height
- )
- guard highlightRect.intersects(rect) else { continue }
- ctx.fill(highlightRect)
- }
- }
- }
-
-#elseif canImport(AppKit)
- import AppKit
-
- final class LineSelectionOverlayView: NSView {
- var selectionColor: NSColor = .systemBlue.withAlphaComponent(0.15) {
- didSet { needsDisplay = true }
- }
-
- var selectedRange: ClosedRange? {
- didSet {
- guard oldValue != selectedRange else { return }
- needsDisplay = true
- }
- }
-
- private var lineRects: [CGRect] = []
-
- override init(frame: CGRect) {
- super.init(frame: frame)
- wantsLayer = true
- layer?.backgroundColor = NSColor.clear.cgColor
- }
-
- @available(*, unavailable)
- required init?(coder _: NSCoder) { fatalError() }
-
- override var isFlipped: Bool { true }
-
- func updateLineRects(_ rects: [CGRect]) {
- lineRects = rects
- needsDisplay = true
- }
-
- func clearSelection() {
- selectedRange = nil
- }
-
- override func draw(_ dirtyRect: NSRect) {
- guard let range = selectedRange,
- let ctx = NSGraphicsContext.current?.cgContext
- else { return }
-
- ctx.setFillColor(selectionColor.cgColor)
-
- for lineIndex in range {
- let arrayIndex = lineIndex - 1
- guard arrayIndex >= 0, arrayIndex < lineRects.count else { continue }
- let lineRect = lineRects[arrayIndex]
- let highlightRect = CGRect(
- x: 0,
- y: lineRect.origin.y,
- width: bounds.width,
- height: lineRect.height
- )
- guard highlightRect.intersects(dirtyRect) else { continue }
- ctx.fill(highlightRect)
- }
- }
- }
-#endif
diff --git a/Sources/MarkdownView/MarkdownTextBuilder/TextBuilder+Do.swift b/Sources/MarkdownView/MarkdownTextBuilder/TextBuilder+Do.swift
index 5f849be..813f19c 100644
--- a/Sources/MarkdownView/MarkdownTextBuilder/TextBuilder+Do.swift
+++ b/Sources/MarkdownView/MarkdownTextBuilder/TextBuilder+Do.swift
@@ -179,10 +179,6 @@ extension TextBuilder {
size: .init(width: view.bounds.width - leftIndent, height: intrinsicContentSize.height)
)
codeView.previewAction = view.codePreviewHandler
- codeView.lineSelectionHandler = { [weak view, weak codeView] info in
- guard let view, let codeView else { return }
- view.handleLineSelection(from: codeView, info: info)
- }
}
.withDiffDrawing { [weak view] _, line, lineOrigin in
guard let view else { return }
@@ -205,10 +201,6 @@ extension TextBuilder {
origin: .init(x: lineOrigin.x + leftIndent, y: view.bounds.height - lineBoundingBox.maxY),
size: .init(width: view.bounds.width - leftIndent, height: intrinsicContentSize.height)
)
- diffView.lineSelectionHandler = { [weak view, weak diffView] info in
- guard let view, let diffView else { return }
- view.handleLineSelection(from: diffView, info: info)
- }
}
.withTableDrawing { [weak view] _, line, lineOrigin in
guard let view else { return }
diff --git a/Sources/MarkdownView/MarkdownTextView/MarkdownTextView.swift b/Sources/MarkdownView/MarkdownTextView/MarkdownTextView.swift
index 5e0f00d..85d7d35 100644
--- a/Sources/MarkdownView/MarkdownTextView/MarkdownTextView.swift
+++ b/Sources/MarkdownView/MarkdownTextView/MarkdownTextView.swift
@@ -21,7 +21,6 @@ enum ContentPipelineMode {
public var linkHandler: ((LinkPayload, NSRange, CGPoint) -> Void)?
public var imageTapHandler: ((String, CGPoint) -> Void)?
public var codePreviewHandler: ((String?, NSAttributedString) -> Void)?
- public var lineSelectionHandler: LineSelectionHandler?
public internal(set) var document: PreprocessedContent = .init()
public let textView: LTXLabel = .init()
@@ -70,21 +69,6 @@ enum ContentPipelineMode {
observeImageLoading()
}
- /// Handles line selection from a code or diff view, clearing other blocks (exclusive mode)
- /// and forwarding the selection info to the public handler.
- func handleLineSelection(from sourceView: UIView, info: LineSelectionInfo?) {
- // Exclusive mode: clear selection in all other code/diff views
- for view in contextViews {
- guard view !== sourceView else { continue }
- if let codeView = view as? CodeView {
- codeView.clearLineSelection()
- } else if let diffView = view as? DiffView {
- diffView.clearLineSelection()
- }
- }
- lineSelectionHandler?(info)
- }
-
@available(*, unavailable)
public required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
@@ -159,7 +143,6 @@ enum ContentPipelineMode {
public var linkHandler: ((LinkPayload, NSRange, CGPoint) -> Void)?
public var imageTapHandler: ((String, CGPoint) -> Void)?
public var codePreviewHandler: ((String?, NSAttributedString) -> Void)?
- public var lineSelectionHandler: LineSelectionHandler?
public internal(set) var document: PreprocessedContent = .init()
public let textView: LTXLabel = .init()
@@ -208,20 +191,6 @@ enum ContentPipelineMode {
observeImageLoading()
}
- /// Handles line selection from a code or diff view, clearing other blocks (exclusive mode)
- /// and forwarding the selection info to the public handler.
- func handleLineSelection(from sourceView: NSView, info: LineSelectionInfo?) {
- for view in contextViews {
- guard view !== sourceView else { continue }
- if let codeView = view as? CodeView {
- codeView.clearLineSelection()
- } else if let diffView = view as? DiffView {
- diffView.clearLineSelection()
- }
- }
- lineSelectionHandler?(info)
- }
-
@available(*, unavailable)
public required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
diff --git a/Sources/MarkdownView/MarkdownTheme/MarkdownTheme.swift b/Sources/MarkdownView/MarkdownTheme/MarkdownTheme.swift
index 1e222eb..c830f41 100644
--- a/Sources/MarkdownView/MarkdownTheme/MarkdownTheme.swift
+++ b/Sources/MarkdownView/MarkdownTheme/MarkdownTheme.swift
@@ -76,10 +76,6 @@ public struct MarkdownTheme: Equatable {
get { selectionBackgroundOverride ?? selectionTint.withAlphaComponent(0.2) }
set { selectionBackgroundOverride = newValue }
}
-
- /// Background color for line selection highlights in code and diff views.
- /// Defaults to `selectionTint` at 15% opacity when `nil`.
- public var lineSelectionBackground: UIColor?
#elseif canImport(AppKit)
private static var defaultAccentColor: NSColor {
NSColor(named: "AccentColor")
@@ -99,10 +95,6 @@ public struct MarkdownTheme: Equatable {
get { selectionBackgroundOverride ?? selectionTint.withAlphaComponent(0.2) }
set { selectionBackgroundOverride = newValue }
}
-
- /// Background color for line selection highlights in code and diff views.
- /// Defaults to `selectionTint` at 15% opacity when `nil`.
- public var lineSelectionBackground: NSColor?
#endif
}