Skip to content

Commit

Permalink
Add smoothScroll to scrollLines
Browse files Browse the repository at this point in the history
  • Loading branch information
tisilent committed Apr 19, 2023
1 parent 2fdb469 commit b7d73f6
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 9 deletions.
76 changes: 74 additions & 2 deletions src/browser/Terminal.ts
Expand Up @@ -21,7 +21,7 @@
* http://linux.die.net/man/7/urxvt
*/

import { ICompositionHelper, ITerminal, IBrowser, CustomKeyEventHandler, IViewport, ILinkifier2, CharacterJoinerHandler, IBufferRange, IBufferElementProvider } from 'browser/Types';
import { ICompositionHelper, ITerminal, IBrowser, CustomKeyEventHandler, IViewport, ILinkifier2, CharacterJoinerHandler, IBufferRange, IBufferElementProvider, ISmoothScrollProgressState } from 'browser/Types';
import { IRenderer } from 'browser/renderer/shared/Types';
import { CompositionHelper } from 'browser/input/CompositionHelper';
import { Viewport } from 'browser/Viewport';
Expand Down Expand Up @@ -122,6 +122,13 @@ export class Terminal extends CoreTerminal implements ITerminal {
private _compositionHelper: ICompositionHelper | undefined;
private _accessibilityManager: AccessibilityManager | undefined;

private _smoothScrollProgressState: ISmoothScrollProgressState = {
startTime: 0,
origin: 0,
target: 0,
progress: 0
};

private readonly _onCursorMove = this.register(new EventEmitter<void>());
public readonly onCursorMove = this._onCursorMove.event;
private readonly _onKey = this.register(new EventEmitter<{ key: string, domEvent: KeyboardEvent }>());
Expand Down Expand Up @@ -869,11 +876,76 @@ export class Terminal extends CoreTerminal implements ITerminal {
}
}

public scrollLines(disp: number, suppressScrollEvent?: boolean, source = ScrollSource.TERMINAL): void {
private _scrollLines(disp: number, suppressScrollEvent?: boolean, source = ScrollSource.TERMINAL): void {
super.scrollLines(disp, suppressScrollEvent, source);
this.refresh(0, this.rows - 1);
}

public scrollLines(disp: number, suppressScrollEvent?: boolean, source = ScrollSource.TERMINAL): void {
if (source === ScrollSource.VIEWPORT) {
this._scrollLines(disp, suppressScrollEvent, source);
} else {
if (!this.optionsService.rawOptions.smoothScrollDuration) {
this._scrollLines(disp, suppressScrollEvent, source);
} else {
this._smoothScrollProgressState.startTime = Date.now();
if (this._smoothScrollPercent() < 1) {
this._smoothScrollProgressState.origin = 0;
this._smoothScrollProgressState.target = disp;
this._smoothScrollProgressState.progress = 0;
this._smoothScroll(suppressScrollEvent, source);
} else {
this._clearSmoothScrollState();
}
}
}
}

private _smoothScrollPercent(): number {
if (!this.optionsService.rawOptions.smoothScrollDuration || !this._smoothScrollProgressState.startTime) {
return 1;
}
return Math.max(Math.min((Date.now() - this._smoothScrollProgressState.startTime) / this.optionsService.rawOptions.smoothScrollDuration, 1), 0);
}

private _isSmoothScrollEnd(): boolean {
if (this._smoothScrollProgressState.target < 0) {
if (this._smoothScrollProgressState.progress > this._smoothScrollProgressState.target) {
return false;
}
} else if (this._smoothScrollProgressState.target > 0) {
if (this._smoothScrollProgressState.progress < this._smoothScrollProgressState.target) {
return false;
}
}
return true;
}

private _smoothScroll(suppressScrollEvent?: boolean, source = ScrollSource.TERMINAL): void {
if (this._smoothScrollProgressState.startTime === 0 || this._isSmoothScrollEnd()) {
return;
}

const percent = this._smoothScrollPercent();
const step = Math.round(percent * (this._smoothScrollProgressState.target - this._smoothScrollProgressState.origin)) - this._smoothScrollProgressState.progress;
this._smoothScrollProgressState.progress += step;
this._scrollLines(step, suppressScrollEvent, source);

if (this._isSmoothScrollEnd()) {
this._clearSmoothScrollState();
return;
}

this._coreBrowserService?.window.requestAnimationFrame(() => this._smoothScroll(suppressScrollEvent, source));
}

private _clearSmoothScrollState(): void {
this._smoothScrollProgressState.origin = 0;
this._smoothScrollProgressState.target = 0;
this._smoothScrollProgressState.progress = 0;
this._smoothScrollProgressState.startTime = 0;
}

public paste(data: string): void {
paste(data, this.textarea!, this.coreService);
}
Expand Down
10 changes: 10 additions & 0 deletions src/browser/Types.d.ts
Expand Up @@ -150,6 +150,16 @@ export interface IViewport extends IDisposable {
handleTouchMove(ev: TouchEvent): boolean;
}

export interface ISmoothScrollState {
startTime: number;
origin: number;
target: number;
}

export interface ISmoothScrollProgressState extends ISmoothScrollState {
progress: number;
}

export interface ILinkifierEvent {
x1: number;
y1: number;
Expand Down
8 changes: 1 addition & 7 deletions src/browser/Viewport.ts
Expand Up @@ -5,20 +5,14 @@

import { Disposable } from 'common/Lifecycle';
import { addDisposableDomListener } from 'browser/Lifecycle';
import { IColorSet, IViewport, ReadonlyColorSet } from 'browser/Types';
import { IColorSet, ISmoothScrollState, IViewport, ReadonlyColorSet } from 'browser/Types';
import { ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services';
import { IBufferService, IOptionsService } from 'common/services/Services';
import { IBuffer } from 'common/buffer/Types';
import { IRenderDimensions } from 'browser/renderer/shared/Types';

const FALLBACK_SCROLL_BAR_WIDTH = 15;

interface ISmoothScrollState {
startTime: number;
origin: number;
target: number;
}

/**
* Represents the viewport of a terminal, the visible area within the larger buffer of output.
* Logic for the virtual scroll bar is included in this object.
Expand Down

0 comments on commit b7d73f6

Please sign in to comment.