-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a8cfe19
commit 9e6d94a
Showing
11 changed files
with
341 additions
and
224 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
57 changes: 57 additions & 0 deletions
57
Sources/CodeEditTextView/TextView/TextView+FirstResponder.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
// | ||
// TextView+FirstResponder.swift | ||
// CodeEditTextView | ||
// | ||
// Created by Khan Winter on 6/15/24. | ||
// | ||
|
||
import AppKit | ||
|
||
extension TextView { | ||
open override func becomeFirstResponder() -> Bool { | ||
isFirstResponder = true | ||
selectionManager.cursorTimer.resetTimer() | ||
needsDisplay = true | ||
return super.becomeFirstResponder() | ||
} | ||
|
||
open override func resignFirstResponder() -> Bool { | ||
isFirstResponder = false | ||
selectionManager.removeCursors() | ||
needsDisplay = true | ||
return super.resignFirstResponder() | ||
} | ||
|
||
open override var canBecomeKeyView: Bool { | ||
super.canBecomeKeyView && acceptsFirstResponder && !isHiddenOrHasHiddenAncestor | ||
} | ||
|
||
/// Sent to the window's first responder when `NSWindow.makeKey()` occurs. | ||
@objc private func becomeKeyWindow() { | ||
_ = becomeFirstResponder() | ||
} | ||
|
||
/// Sent to the window's first responder when `NSWindow.resignKey()` occurs. | ||
@objc private func resignKeyWindow() { | ||
_ = resignFirstResponder() | ||
} | ||
|
||
open override var needsPanelToBecomeKey: Bool { | ||
isSelectable || isEditable | ||
} | ||
|
||
open override var acceptsFirstResponder: Bool { | ||
isSelectable | ||
} | ||
|
||
open override func acceptsFirstMouse(for event: NSEvent?) -> Bool { | ||
return true | ||
} | ||
|
||
open override func resetCursorRects() { | ||
super.resetCursorRects() | ||
if isSelectable { | ||
addCursorRect(visibleRect, cursor: .iBeam) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// | ||
// TextView+KeyDown.swift | ||
// CodeEditTextView | ||
// | ||
// Created by Khan Winter on 6/15/24. | ||
// | ||
|
||
import AppKit | ||
import Carbon.HIToolbox | ||
|
||
extension TextView { | ||
override public func keyDown(with event: NSEvent) { | ||
guard isEditable else { | ||
super.keyDown(with: event) | ||
return | ||
} | ||
|
||
NSCursor.setHiddenUntilMouseMoves(true) | ||
|
||
if !(inputContext?.handleEvent(event) ?? false) { | ||
interpretKeyEvents([event]) | ||
} else { | ||
// Not handled, ignore so we don't double trigger events. | ||
return | ||
} | ||
} | ||
|
||
override public func performKeyEquivalent(with event: NSEvent) -> Bool { | ||
guard isEditable else { | ||
return super.performKeyEquivalent(with: event) | ||
} | ||
|
||
switch Int(event.keyCode) { | ||
case kVK_PageUp: | ||
if !event.modifierFlags.contains(.shift) { | ||
self.pageUp(event) | ||
return true | ||
} | ||
case kVK_PageDown: | ||
if !event.modifierFlags.contains(.shift) { | ||
self.pageDown(event) | ||
return true | ||
} | ||
default: | ||
return false | ||
} | ||
|
||
return false | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
// | ||
// TextView+Layout.swift | ||
// CodeEditTextView | ||
// | ||
// Created by Khan Winter on 6/15/24. | ||
// | ||
|
||
import Foundation | ||
|
||
extension TextView { | ||
open override class var isCompatibleWithResponsiveScrolling: Bool { | ||
true | ||
} | ||
|
||
open override func prepareContent(in rect: NSRect) { | ||
needsLayout = true | ||
super.prepareContent(in: rect) | ||
} | ||
|
||
override public func draw(_ dirtyRect: NSRect) { | ||
super.draw(dirtyRect) | ||
if isSelectable { | ||
selectionManager.drawSelections(in: dirtyRect) | ||
} | ||
} | ||
|
||
override open var isFlipped: Bool { | ||
true | ||
} | ||
|
||
override public var visibleRect: NSRect { | ||
if let scrollView { | ||
var rect = scrollView.documentVisibleRect | ||
rect.origin.y += scrollView.contentInsets.top | ||
return rect.pixelAligned | ||
} else { | ||
return super.visibleRect | ||
} | ||
} | ||
|
||
public var visibleTextRange: NSRange? { | ||
let minY = max(visibleRect.minY, 0) | ||
let maxY = min(visibleRect.maxY, layoutManager.estimatedHeight()) | ||
guard let minYLine = layoutManager.textLineForPosition(minY), | ||
let maxYLine = layoutManager.textLineForPosition(maxY) else { | ||
return nil | ||
} | ||
return NSRange( | ||
location: minYLine.range.location, | ||
length: (maxYLine.range.location - minYLine.range.location) + maxYLine.range.length | ||
) | ||
} | ||
|
||
public func updatedViewport(_ newRect: CGRect) { | ||
if !updateFrameIfNeeded() { | ||
layoutManager.layoutLines() | ||
} | ||
inputContext?.invalidateCharacterCoordinates() | ||
} | ||
|
||
@discardableResult | ||
public func updateFrameIfNeeded() -> Bool { | ||
var availableSize = scrollView?.contentSize ?? .zero | ||
availableSize.height -= (scrollView?.contentInsets.top ?? 0) + (scrollView?.contentInsets.bottom ?? 0) | ||
let newHeight = max(layoutManager.estimatedHeight(), availableSize.height) | ||
let newWidth = layoutManager.estimatedWidth() | ||
|
||
var didUpdate = false | ||
|
||
if newHeight >= availableSize.height && frame.size.height != newHeight { | ||
frame.size.height = newHeight | ||
// No need to update layout after height adjustment | ||
} | ||
|
||
if wrapLines && frame.size.width != availableSize.width { | ||
frame.size.width = availableSize.width | ||
didUpdate = true | ||
} else if !wrapLines && frame.size.width != max(newWidth, availableSize.width) { | ||
frame.size.width = max(newWidth, availableSize.width) | ||
didUpdate = true | ||
} | ||
|
||
if didUpdate { | ||
needsLayout = true | ||
needsDisplay = true | ||
layoutManager.layoutLines() | ||
} | ||
|
||
if isSelectable { | ||
selectionManager?.updateSelectionViews() | ||
} | ||
|
||
return didUpdate | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.