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.
dependencies: [
.package(url: "https://github.com/ChimeHQ/TextViewPlus")
],
targets: [
.target(
name: "UseCoreFunctionality",
dependencies: ["TextViewPlus"]
),
.target(
name: "UseBaseTextView",
dependencies: [.product(name: "BaseTextView", package: "TextViewPlus")]
),
]
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 morescrollRangeToVisible
bugs - Additional routing to
NSTextViewDelegate.textView(_:, doCommandBy:) -> Bool
:paste
,pasteAsRichText
,pasteAsPlainText
- Hooks for
onKeyDown
,onFlagsChanged
,onMouseDown
- Configurable selection notifcation delivery via
continuousSelectionNotifications
Handy methods for computing ranges of text within the view.
func textRange(for rect: NSRect) -> NSRange
var visibleTextRange: NSRange
Convenience methods for computing selection ranges/locations.
var selectedTextRanges: [NSRange]
var selectedContinuousRange: NSRange?
var insertionLocation: Int?
Styling changes can be very expensive, this method is much faster in certain common cases.
func updateFont(_ newFont: NSFont, color newColor: NSColor)
Computing bounding rectangles of displayed text.
func boundingRect(for range: NSRange) -> NSRect?
func boundingRect(forGlyphRange range: NSRange) -> NSRect?
func boundingSelectionRects(forRange range: NSRange) -> [NSRect]
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)
Changing NSTextView
behaviors can be tricky, and often involve complex interactions with the whole system (NSLayoutManager
, NSTextContainer
, NSScrollView
, etc).
public var wrapsTextToHorizontalBounds: Bool
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)
}
}
}
func enumerateLineFragments(for range: NSRange, block: (NSRect, NSRange) -> Void)
func enumerateLineFragments(for rect: NSRect, block: (NSRect, NSRange) -> Void)
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.