Skip to content

ChimeHQ/TextViewPlus

Folders and files

NameName
Last commit message
Last commit date
Oct 26, 2023
Mar 24, 2024
Mar 24, 2024
Sep 3, 2023
Sep 3, 2023
Nov 13, 2021
Jan 5, 2020
Sep 3, 2023
Jan 5, 2020
Oct 26, 2023
Oct 26, 2023
Oct 26, 2023

Build Status Platforms

TextViewPlus

This project aims to make it easier to use NSTextView. It was originally built to support TextKit 1. But, now a major goal is to support TextKit 2.

Integration

dependencies: [
    .package(url: "https://github.com/ChimeHQ/TextViewPlus")
],
targets: [
    .target(
        name: "UseCoreFunctionality",
        dependencies: ["TextViewPlus"]
    ),
    .target(
        name: "UseBaseTextView",
        dependencies: [.product(name: "BaseTextView", package: "TextViewPlus")]
    ),
]

BaseTextView

This is an TextKit 2-only NSTextView subclass that aims for an absolute minimal amount of changes. Things are allowed only if they are required for correct functionality. It is intended to be a drop-in replacement for NSTextView, and should maintain compatibilty with existing subclasses. Behaviors are appropriate for all types of text.

  • Disables all support for TextKit 1
  • Workaround for scrollRangeToVisible bug (FB13100459)
  • Minimum textContainerInset enforcement to address more scrollRangeToVisible bugs
  • Additional routing to NSTextViewDelegate.textView(_:, doCommandBy:) -> Bool: paste, pasteAsRichText, pasteAsPlainText
  • Hooks for onKeyDown, onFlagsChanged, onMouseDown
  • Configurable selection notifcation delivery via continuousSelectionNotifications

NSTextView Extensions

Ranges

Handy methods for computing ranges of text within the view.

func textRange(for rect: NSRect) -> NSRange
var visibleTextRange: NSRange

Selection

Convenience methods for computing selection ranges/locations.

var selectedTextRanges: [NSRange]
var selectedContinuousRange: NSRange?
var insertionLocation: Int?

Style

Styling changes can be very expensive, this method is much faster in certain common cases.

func updateFont(_ newFont: NSFont, color newColor: NSColor)

Bounding

Computing bounding rectangles of displayed text.

func boundingRect(for range: NSRange) -> NSRect?
func boundingRect(forGlyphRange range: NSRange) -> NSRect?
func boundingSelectionRects(forRange range: NSRange) -> [NSRect]

Attributed Strings

Programmatic modification of the underlying attributed string in the NSTextStorage, with support for delegate callbacks and undo.

func replaceCharacters(in range: NSRange, with attributedString: NSAttributedString)

// with undo supported
func replaceString(in range: NSRange, with attributedString: NSAttributedString)

Behavior

Changing NSTextView behaviors can be tricky, and often involve complex interactions with the whole system (NSLayoutManager, NSTextContainer, NSScrollView, etc).

public var wrapsTextToHorizontalBounds: Bool

TextKit 2 Features

Workarounds

In versions of macOS before 13, TextKit 2 doesn't correctly apply rendering attributes. You can sub in this NSTextLayoutFragment to workaround the issue.

extension YourClass: NSTextLayoutManagerDelegate {
    func textLayoutManager(_ textLayoutManager: NSTextLayoutManager, textLayoutFragmentFor location: NSTextLocation, in textElement: NSTextElement) -> NSTextLayoutFragment {
        let range = textElement.elementRange

        switch textElement {
        case let paragraph as NSTextParagraph:
            return ParagraphRenderingAttributeTextLayoutFragment(textParagraph: paragraph, range: range)
        default:
            return NSTextLayoutFragment(textElement: textElement, range: range)
        }
    }
}

TextKit 1 Features

NSLayoutManager extensions

func enumerateLineFragments(for range: NSRange, block: (NSRect, NSRange) -> Void)
func enumerateLineFragments(for rect: NSRect, block: (NSRect, NSRange) -> Void)

Contributing and Collaboration

I'd love to hear from you! Get in touch via an issue or pull request.

I prefer collaboration, and would love to find ways to work together if you have a similar project.

I prefer indentation with tabs for improved accessibility. But, I'd rather you use the system you want and make a PR than hesitate because of whitespace.

By participating in this project you agree to abide by the Contributor Code of Conduct.