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 }