https://github.com/chimehq/textviewplus
Make life better with NSTextView+TextKit 1/2
https://github.com/chimehq/textviewplus
appkit cocoa macos nstextview swift textkit textkit2
Last synced: 6 months ago
JSON representation
Make life better with NSTextView+TextKit 1/2
- Host: GitHub
- URL: https://github.com/chimehq/textviewplus
- Owner: ChimeHQ
- License: bsd-3-clause
- Created: 2020-01-05T12:02:58.000Z (almost 6 years ago)
- Default Branch: main
- Last Pushed: 2024-03-25T11:14:35.000Z (over 1 year ago)
- Last Synced: 2024-04-25T19:42:30.633Z (over 1 year ago)
- Topics: appkit, cocoa, macos, nstextview, swift, textkit, textkit2
- Language: Swift
- Homepage:
- Size: 96.7 KB
- Stars: 42
- Watchers: 5
- Forks: 5
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
[![Build Status][build status badge]][build status]
[![Platforms][platforms badge]][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
```swift
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.
```swift
func textRange(for rect: NSRect) -> NSRange
var visibleTextRange: NSRange
```### Selection
Convenience methods for computing selection ranges/locations.
```swift
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.
```swift
func updateFont(_ newFont: NSFont, color newColor: NSColor)
```### Bounding
Computing bounding rectangles of displayed text.
```swift
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.
```swift
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).
```swift
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.
```swift
extension YourClass: NSTextLayoutManagerDelegate {
func textLayoutManager(_ textLayoutManager: NSTextLayoutManager, textLayoutFragmentFor location: NSTextLocation, in textElement: NSTextElement) -> NSTextLayoutFragment {
let range = textElement.elementRangeswitch textElement {
case let paragraph as NSTextParagraph:
return ParagraphRenderingAttributeTextLayoutFragment(textParagraph: paragraph, range: range)
default:
return NSTextLayoutFragment(textElement: textElement, range: range)
}
}
}
```## TextKit 1 Features
### `NSLayoutManager` extensions
```swift
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](CODE_OF_CONDUCT.md).
[build status]: https://github.com/ChimeHQ/TextViewPlus/actions
[build status badge]: https://github.com/ChimeHQ/TextViewPlus/workflows/CI/badge.svg
[platforms]: https://swiftpackageindex.com/ChimeHQ/TextViewPlus
[platforms badge]: https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FChimeHQ%2FTextViewPlus%2Fbadge%3Ftype%3Dplatforms