mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Move logic to Viewport
This commit is contained in:
+5
-73
@@ -21,7 +21,7 @@
|
||||
* http://linux.die.net/man/7/urxvt
|
||||
*/
|
||||
|
||||
import { ICompositionHelper, ITerminal, IBrowser, CustomKeyEventHandler, IViewport, ILinkifier2, CharacterJoinerHandler, IBufferRange, IBufferElementProvider, ISmoothScrollProgressState } from 'browser/Types';
|
||||
import { ICompositionHelper, ITerminal, IBrowser, CustomKeyEventHandler, IViewport, ILinkifier2, CharacterJoinerHandler, IBufferRange, IBufferElementProvider } from 'browser/Types';
|
||||
import { IRenderer } from 'browser/renderer/shared/Types';
|
||||
import { CompositionHelper } from 'browser/input/CompositionHelper';
|
||||
import { Viewport } from 'browser/Viewport';
|
||||
@@ -122,13 +122,6 @@ 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 }>());
|
||||
@@ -505,7 +498,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
this._instantiationService.setService(IMouseService, this._mouseService);
|
||||
|
||||
this.viewport = this._instantiationService.createInstance(Viewport,
|
||||
(amount: number) => this.scrollLines(amount, true, ScrollSource.VIEWPORT),
|
||||
(amount: number, suppressScrollEvent: boolean) => this.scrollLines(amount, suppressScrollEvent, ScrollSource.VIEWPORT),
|
||||
this._viewportElement,
|
||||
this._viewportScrollArea
|
||||
);
|
||||
@@ -876,76 +869,15 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
super.scrollLines(disp, suppressScrollEvent, source);
|
||||
this.refresh(0, this.rows - 1);
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
this.viewport?.scrollLines(disp);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -319,6 +319,9 @@ export class MockViewport implements IViewport {
|
||||
public getBufferElements(startLine: number, endLine?: number | undefined): { bufferElements: HTMLElement[], cursorElement?: HTMLElement | undefined } {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
public scrollLines(disp: number): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
|
||||
export class MockCompositionHelper implements ICompositionHelper {
|
||||
|
||||
Vendored
+1
-10
@@ -148,16 +148,7 @@ export interface IViewport extends IDisposable {
|
||||
handleWheel(ev: WheelEvent): boolean;
|
||||
handleTouchStart(ev: TouchEvent): void;
|
||||
handleTouchMove(ev: TouchEvent): boolean;
|
||||
}
|
||||
|
||||
export interface ISmoothScrollState {
|
||||
startTime: number;
|
||||
origin: number;
|
||||
target: number;
|
||||
}
|
||||
|
||||
export interface ISmoothScrollProgressState extends ISmoothScrollState {
|
||||
progress: number;
|
||||
scrollLines(disp: number): void; // todo api name?
|
||||
}
|
||||
|
||||
export interface ILinkifierEvent {
|
||||
|
||||
+48
-8
@@ -5,7 +5,7 @@
|
||||
|
||||
import { Disposable } from 'common/Lifecycle';
|
||||
import { addDisposableDomListener } from 'browser/Lifecycle';
|
||||
import { IColorSet, ISmoothScrollState, IViewport, ReadonlyColorSet } from 'browser/Types';
|
||||
import { IColorSet, 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';
|
||||
@@ -13,6 +13,18 @@ import { IRenderDimensions } from 'browser/renderer/shared/Types';
|
||||
|
||||
const FALLBACK_SCROLL_BAR_WIDTH = 15;
|
||||
|
||||
interface ISmoothScrollState {
|
||||
startTime: number;
|
||||
origin: number;
|
||||
target: number;
|
||||
}
|
||||
|
||||
interface ISmoothScrollCalculateContext {
|
||||
percent: number;
|
||||
position: number;
|
||||
lastPosition: 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.
|
||||
@@ -43,7 +55,7 @@ export class Viewport extends Disposable implements IViewport {
|
||||
};
|
||||
|
||||
constructor(
|
||||
private readonly _scrollLines: (amount: number) => void,
|
||||
private readonly _scrollLines: (amount: number, suppressScrollEvent: boolean) => void,
|
||||
private readonly _viewportElement: HTMLElement,
|
||||
private readonly _scrollArea: HTMLElement,
|
||||
@IBufferService private readonly _bufferService: IBufferService,
|
||||
@@ -169,16 +181,16 @@ export class Viewport extends Disposable implements IViewport {
|
||||
if (this._ignoreNextScrollEvent) {
|
||||
this._ignoreNextScrollEvent = false;
|
||||
// Still trigger the scroll so lines get refreshed
|
||||
this._scrollLines(0);
|
||||
this._scrollLines(0, true);
|
||||
return;
|
||||
}
|
||||
|
||||
const newRow = Math.round(this._lastScrollTop / this._currentRowHeight);
|
||||
const diff = newRow - this._bufferService.buffer.ydisp;
|
||||
this._scrollLines(diff);
|
||||
this._scrollLines(diff, true);
|
||||
}
|
||||
|
||||
private _smoothScroll(): void {
|
||||
private _smoothScroll(handle: (context: ISmoothScrollCalculateContext) => void, lastPosition: number = 0): void {
|
||||
// Check valid state
|
||||
if (this._isDisposed || this._smoothScrollState.origin === -1 || this._smoothScrollState.target === -1) {
|
||||
return;
|
||||
@@ -186,16 +198,29 @@ export class Viewport extends Disposable implements IViewport {
|
||||
|
||||
// Calculate position complete
|
||||
const percent = this._smoothScrollPercent();
|
||||
this._viewportElement.scrollTop = this._smoothScrollState.origin + Math.round(percent * (this._smoothScrollState.target - this._smoothScrollState.origin));
|
||||
const position = Math.round(percent * (this._smoothScrollState.target - this._smoothScrollState.origin));
|
||||
handle({ percent, position, lastPosition });
|
||||
|
||||
// Continue or finish smooth scroll
|
||||
if (percent < 1) {
|
||||
this._coreBrowserService.window.requestAnimationFrame(() => this._smoothScroll());
|
||||
this._coreBrowserService.window.requestAnimationFrame(() => this._smoothScroll(handle, position));
|
||||
} else {
|
||||
this._clearSmoothScrollState();
|
||||
}
|
||||
}
|
||||
|
||||
private _wheelSmoothScroll(): void {
|
||||
this._smoothScroll(({ position }) => {
|
||||
this._viewportElement.scrollTop = this._smoothScrollState.origin + position;
|
||||
});
|
||||
}
|
||||
|
||||
private _linesSmoothScroll(): void {
|
||||
this._smoothScroll(({ position, lastPosition }) => {
|
||||
this._scrollLines(position - lastPosition, false);
|
||||
});
|
||||
}
|
||||
|
||||
private _smoothScrollPercent(): number {
|
||||
if (!this._optionsService.rawOptions.smoothScrollDuration || !this._smoothScrollState.startTime) {
|
||||
return 1;
|
||||
@@ -249,7 +274,7 @@ export class Viewport extends Disposable implements IViewport {
|
||||
this._smoothScrollState.target += amount;
|
||||
}
|
||||
this._smoothScrollState.target = Math.max(Math.min(this._smoothScrollState.target, this._viewportElement.scrollHeight), 0);
|
||||
this._smoothScroll();
|
||||
this._wheelSmoothScroll();
|
||||
} else {
|
||||
this._clearSmoothScrollState();
|
||||
}
|
||||
@@ -257,6 +282,21 @@ export class Viewport extends Disposable implements IViewport {
|
||||
return this._bubbleScroll(ev, amount);
|
||||
}
|
||||
|
||||
public scrollLines(disp: number): void {
|
||||
if (!this._optionsService.rawOptions.smoothScrollDuration) {
|
||||
this._scrollLines(disp, false);
|
||||
} else {
|
||||
this._smoothScrollState.startTime = Date.now();
|
||||
if (this._smoothScrollPercent() < 1) {
|
||||
this._smoothScrollState.origin = 0;
|
||||
this._smoothScrollState.target = disp;
|
||||
this._linesSmoothScroll();
|
||||
} else {
|
||||
this._clearSmoothScrollState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _getPixelsScrolled(ev: WheelEvent): number {
|
||||
// Do nothing if it's not a vertical scroll event
|
||||
if (ev.deltaY === 0 || ev.shiftKey) {
|
||||
|
||||
Reference in New Issue
Block a user