diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 000000000..81a397c11 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,95 @@ +{ + "pins" : [ + { + "identity" : "hdrhistogram-swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/HdrHistogram/hdrhistogram-swift.git", + "state" : { + "revision" : "de0b9b8a27956b9bfc9b4dce7d1c38ad7c579f19", + "version" : "0.1.4" + } + }, + { + "identity" : "package-benchmark", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ordo-one/package-benchmark", + "state" : { + "revision" : "a1075611342d4b164bf6e977edb02a3db4434afd", + "version" : "1.29.11" + } + }, + { + "identity" : "package-jemalloc", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ordo-one/package-jemalloc.git", + "state" : { + "revision" : "e8a5db026963f5bfeac842d9d3f2cc8cde323b49", + "version" : "1.0.0" + } + }, + { + "identity" : "swift-argument-parser", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-argument-parser", + "state" : { + "revision" : "c5d11a805e765f52ba34ec7284bd4fcd6ba68615", + "version" : "1.7.0" + } + }, + { + "identity" : "swift-atomics", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-atomics.git", + "state" : { + "revision" : "b601256eab081c0f92f059e12818ac1d4f178ff7", + "version" : "1.3.0" + } + }, + { + "identity" : "swift-docc-plugin", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-docc-plugin", + "state" : { + "revision" : "e977f65879f82b375a044c8837597f690c067da6", + "version" : "1.4.6" + } + }, + { + "identity" : "swift-docc-symbolkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-docc-symbolkit", + "state" : { + "revision" : "b45d1f2ed151d057b54504d653e0da5552844e34", + "version" : "1.0.0" + } + }, + { + "identity" : "swift-numerics", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-numerics", + "state" : { + "revision" : "0c0290ff6b24942dadb83a929ffaaa1481df04a2", + "version" : "1.1.1" + } + }, + { + "identity" : "swift-system", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-system.git", + "state" : { + "revision" : "7c6ad0fc39d0763e0b699210e4124afd5041c5df", + "version" : "1.6.4" + } + }, + { + "identity" : "texttable", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ordo-one/TextTable.git", + "state" : { + "revision" : "a27a07300cf4ae322e0079ca0a475c5583dd575f", + "version" : "0.0.2" + } + } + ], + "version" : 2 +} diff --git a/Sources/SwiftTerm/iOS/iOSTerminalView.swift b/Sources/SwiftTerm/iOS/iOSTerminalView.swift index d1856b587..25ad2531c 100644 --- a/Sources/SwiftTerm/iOS/iOSTerminalView.swift +++ b/Sources/SwiftTerm/iOS/iOSTerminalView.swift @@ -260,6 +260,8 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi setupProgressBar() setupGestures () setupAccessoryView () + + panGestureRecognizer.addTarget(self, action: #selector(handleScrollPanGesture(_:))) } func setupDisplayUpdates () @@ -335,6 +337,9 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi @objc func step(displaylink: CADisplayLink) { updateDisplay() + if userScrolling { + checkScrollDeceleration() + } } func startDisplayUpdates() @@ -1041,6 +1046,8 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi var lineLeading: CGFloat = 0 open func bufferActivated(source: Terminal) { + userScrolling = false + terminal.userScrolling = false updateScroller () } @@ -1133,14 +1140,56 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi let displayBuffer = terminal.displayBuffer contentSize = CGSize (width: CGFloat (displayBuffer.cols) * cellDimension.width, height: CGFloat (displayBuffer.lines.count) * cellDimension.height) - //contentOffset = CGPoint (x: 0, y: CGFloat (displayBuffer.lines.count-displayBuffer.rows)*cellDimension.height) - contentOffset = CGPoint (x: 0, y: CGFloat (displayBuffer.lines.count-displayBuffer.rows)*cellDimension.height) - //Xscroller.doubleValue = scrollPosition - //Xscroller.knobProportion = scrollThumbsize + if !userScrolling { + contentOffset = CGPoint (x: 0, y: CGFloat (displayBuffer.yDisp) * cellDimension.height) + } } - + var userScrolling = false + @objc private func handleScrollPanGesture(_ gesture: UIPanGestureRecognizer) { + switch gesture.state { + case .began: + userScrolling = true + terminal.userScrolling = true + case .ended, .cancelled, .failed: + if !isDecelerating { + syncYDispFromContentOffset() + } + default: + break + } + } + + func checkScrollDeceleration() { + if userScrolling && !isTracking && !isDecelerating { + syncYDispFromContentOffset() + } + } + + private func syncYDispFromContentOffset() { + let displayBuffer = terminal.displayBuffer + let maxRow = displayBuffer.lines.count - displayBuffer.rows + guard maxRow > 0 else { + userScrolling = false + terminal.userScrolling = false + return + } + + let row = Int(round(contentOffset.y / cellDimension.height)) + let clampedRow = max(0, min(row, maxRow)) + + if clampedRow != displayBuffer.yDisp { + terminal.setViewYDisp(clampedRow) + } + + // If at or near the bottom, resume auto-scrolling + if clampedRow >= maxRow { + userScrolling = false + terminal.userScrolling = false + } + } + func getCurrentGraphicsContext () -> CGContext? { UIGraphicsGetCurrentContext () @@ -1259,6 +1308,14 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi } private func commitTextInput(_ text: String, applyModifiers: Bool) { + if userScrolling { + userScrolling = false + terminal.userScrolling = false + let displayBuffer = terminal.displayBuffer + terminal.setViewYDisp(displayBuffer.yBase) + updateScroller() + } + let hadPendingAutoPeriodDelete = pendingAutoPeriodDeleteWasSpace if text != ". " { pendingAutoPeriodDeleteWasSpace = false @@ -1382,7 +1439,15 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi func ensureCaretIsVisible () { let displayBuffer = terminal.displayBuffer - contentOffset = CGPoint (x: 0, y: CGFloat (displayBuffer.lines.count-displayBuffer.rows)*cellDimension.height) + let realCaret = displayBuffer.y + displayBuffer.yBase + let viewportEnd = displayBuffer.yDisp + displayBuffer.rows + + // Only scroll to bottom if the caret is actually off-screen + if realCaret >= viewportEnd || realCaret < displayBuffer.yDisp { + userScrolling = false + terminal.userScrolling = false + contentOffset = CGPoint (x: 0, y: CGFloat (displayBuffer.yBase) * cellDimension.height) + } } public func deleteBackward() {