|
21 | 21 | * http://linux.die.net/man/7/urxvt |
22 | 22 | */ |
23 | 23 |
|
24 | | -import { ICompositionHelper, ITerminal, IBrowser, CustomKeyEventHandler, IViewport, ILinkifier2, CharacterJoinerHandler, IBufferRange, IBufferElementProvider } from 'browser/Types'; |
| 24 | +import { ICompositionHelper, ITerminal, IBrowser, CustomKeyEventHandler, IViewport, ILinkifier2, CharacterJoinerHandler, IBufferRange, IBufferElementProvider, ISmoothScrollProgressState } from 'browser/Types'; |
25 | 25 | import { IRenderer } from 'browser/renderer/shared/Types'; |
26 | 26 | import { CompositionHelper } from 'browser/input/CompositionHelper'; |
27 | 27 | import { Viewport } from 'browser/Viewport'; |
@@ -122,6 +122,13 @@ export class Terminal extends CoreTerminal implements ITerminal { |
122 | 122 | private _compositionHelper: ICompositionHelper | undefined; |
123 | 123 | private _accessibilityManager: AccessibilityManager | undefined; |
124 | 124 |
|
| 125 | + private _smoothScrollProgressState: ISmoothScrollProgressState = { |
| 126 | + startTime: 0, |
| 127 | + origin: 0, |
| 128 | + target: 0, |
| 129 | + progress: 0 |
| 130 | + }; |
| 131 | + |
125 | 132 | private readonly _onCursorMove = this.register(new EventEmitter<void>()); |
126 | 133 | public readonly onCursorMove = this._onCursorMove.event; |
127 | 134 | private readonly _onKey = this.register(new EventEmitter<{ key: string, domEvent: KeyboardEvent }>()); |
@@ -869,11 +876,76 @@ export class Terminal extends CoreTerminal implements ITerminal { |
869 | 876 | } |
870 | 877 | } |
871 | 878 |
|
872 | | - public scrollLines(disp: number, suppressScrollEvent?: boolean, source = ScrollSource.TERMINAL): void { |
| 879 | + private _scrollLines(disp: number, suppressScrollEvent?: boolean, source = ScrollSource.TERMINAL): void { |
873 | 880 | super.scrollLines(disp, suppressScrollEvent, source); |
874 | 881 | this.refresh(0, this.rows - 1); |
875 | 882 | } |
876 | 883 |
|
| 884 | + public scrollLines(disp: number, suppressScrollEvent?: boolean, source = ScrollSource.TERMINAL): void { |
| 885 | + if (source === ScrollSource.VIEWPORT) { |
| 886 | + this._scrollLines(disp, suppressScrollEvent, source); |
| 887 | + } else { |
| 888 | + if (!this.optionsService.rawOptions.smoothScrollDuration) { |
| 889 | + this._scrollLines(disp, suppressScrollEvent, source); |
| 890 | + } else { |
| 891 | + this._smoothScrollProgressState.startTime = Date.now(); |
| 892 | + if (this._smoothScrollPercent() < 1) { |
| 893 | + this._smoothScrollProgressState.origin = 0; |
| 894 | + this._smoothScrollProgressState.target = disp; |
| 895 | + this._smoothScrollProgressState.progress = 0; |
| 896 | + this._smoothScroll(suppressScrollEvent, source); |
| 897 | + } else { |
| 898 | + this._clearSmoothScrollState(); |
| 899 | + } |
| 900 | + } |
| 901 | + } |
| 902 | + } |
| 903 | + |
| 904 | + private _smoothScrollPercent(): number { |
| 905 | + if (!this.optionsService.rawOptions.smoothScrollDuration || !this._smoothScrollProgressState.startTime) { |
| 906 | + return 1; |
| 907 | + } |
| 908 | + return Math.max(Math.min((Date.now() - this._smoothScrollProgressState.startTime) / this.optionsService.rawOptions.smoothScrollDuration, 1), 0); |
| 909 | + } |
| 910 | + |
| 911 | + private _isSmoothScrollEnd(): boolean { |
| 912 | + if (this._smoothScrollProgressState.target < 0) { |
| 913 | + if (this._smoothScrollProgressState.progress > this._smoothScrollProgressState.target) { |
| 914 | + return false; |
| 915 | + } |
| 916 | + } else if (this._smoothScrollProgressState.target > 0) { |
| 917 | + if (this._smoothScrollProgressState.progress < this._smoothScrollProgressState.target) { |
| 918 | + return false; |
| 919 | + } |
| 920 | + } |
| 921 | + return true; |
| 922 | + } |
| 923 | + |
| 924 | + private _smoothScroll(suppressScrollEvent?: boolean, source = ScrollSource.TERMINAL): void { |
| 925 | + if (this._smoothScrollProgressState.startTime === 0 || this._isSmoothScrollEnd()) { |
| 926 | + return; |
| 927 | + } |
| 928 | + |
| 929 | + const percent = this._smoothScrollPercent(); |
| 930 | + const step = Math.round(percent * (this._smoothScrollProgressState.target - this._smoothScrollProgressState.origin)) - this._smoothScrollProgressState.progress; |
| 931 | + this._smoothScrollProgressState.progress += step; |
| 932 | + this._scrollLines(step, suppressScrollEvent, source); |
| 933 | + |
| 934 | + if (this._isSmoothScrollEnd()) { |
| 935 | + this._clearSmoothScrollState(); |
| 936 | + return; |
| 937 | + } |
| 938 | + |
| 939 | + this._coreBrowserService?.window.requestAnimationFrame(() => this._smoothScroll(suppressScrollEvent, source)); |
| 940 | + } |
| 941 | + |
| 942 | + private _clearSmoothScrollState(): void { |
| 943 | + this._smoothScrollProgressState.origin = 0; |
| 944 | + this._smoothScrollProgressState.target = 0; |
| 945 | + this._smoothScrollProgressState.progress = 0; |
| 946 | + this._smoothScrollProgressState.startTime = 0; |
| 947 | + } |
| 948 | + |
877 | 949 | public paste(data: string): void { |
878 | 950 | paste(data, this.textarea!, this.coreService); |
879 | 951 | } |
|
0 commit comments