mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Implement smooth scroll for pages/top/bottom, add viewport request event
This commit is contained in:
@@ -29,6 +29,7 @@ describe('Terminal', () => {
|
||||
term.refresh = () => { };
|
||||
(term as any).renderer = new MockRenderer();
|
||||
term.viewport = new MockViewport();
|
||||
term.viewport.onRequestScrollLines(e => term.scrollLines(e.amount, e.suppressScrollEvent, ScrollSource.VIEWPORT));
|
||||
(term as any)._compositionHelper = new MockCompositionHelper();
|
||||
(term as any).element = {
|
||||
classList: {
|
||||
@@ -258,27 +259,27 @@ describe('Terminal', () => {
|
||||
});
|
||||
it('should scroll a single line', () => {
|
||||
assert.equal(term.buffer.ydisp, startYDisp);
|
||||
term.scrollLines(-1, undefined, ScrollSource.VIEWPORT);
|
||||
term.scrollLines(-1);
|
||||
assert.equal(term.buffer.ydisp, startYDisp - 1);
|
||||
term.scrollLines(1, undefined, ScrollSource.VIEWPORT);
|
||||
term.scrollLines(1);
|
||||
assert.equal(term.buffer.ydisp, startYDisp);
|
||||
});
|
||||
it('should scroll multiple lines', () => {
|
||||
assert.equal(term.buffer.ydisp, startYDisp);
|
||||
term.scrollLines(-5, undefined, ScrollSource.VIEWPORT);
|
||||
term.scrollLines(-5);
|
||||
assert.equal(term.buffer.ydisp, startYDisp - 5);
|
||||
term.scrollLines(5, undefined, ScrollSource.VIEWPORT);
|
||||
term.scrollLines(5);
|
||||
assert.equal(term.buffer.ydisp, startYDisp);
|
||||
});
|
||||
it('should not scroll beyond the bounds of the buffer', () => {
|
||||
assert.equal(term.buffer.ydisp, startYDisp);
|
||||
term.scrollLines(1, undefined, ScrollSource.VIEWPORT);
|
||||
term.scrollLines(1);
|
||||
assert.equal(term.buffer.ydisp, startYDisp);
|
||||
for (let i = 0; i < startYDisp; i++) {
|
||||
term.scrollLines(-1, undefined, ScrollSource.VIEWPORT);
|
||||
term.scrollLines(-1);
|
||||
}
|
||||
assert.equal(term.buffer.ydisp, 0);
|
||||
term.scrollLines(-1, undefined, ScrollSource.VIEWPORT);
|
||||
term.scrollLines(-1);
|
||||
assert.equal(term.buffer.ydisp, 0);
|
||||
});
|
||||
});
|
||||
@@ -329,7 +330,7 @@ describe('Terminal', () => {
|
||||
startYDisp = (term.rows * 2) + 1;
|
||||
});
|
||||
it('should scroll to the bottom', () => {
|
||||
term.scrollLines(-1, undefined, ScrollSource.VIEWPORT);
|
||||
term.scrollLines(-1);
|
||||
term.scrollToBottom();
|
||||
assert.equal(term.buffer.ydisp, startYDisp);
|
||||
term.scrollPages(-1);
|
||||
@@ -398,7 +399,7 @@ describe('Terminal', () => {
|
||||
});
|
||||
|
||||
assert.equal(term.buffer.ydisp, startYDisp);
|
||||
term.scrollLines(-1, undefined, ScrollSource.VIEWPORT);
|
||||
term.scrollLines(-1);
|
||||
assert.equal(term.buffer.ydisp, startYDisp - 1);
|
||||
term.keyPress({ keyCode: 0 });
|
||||
assert.equal(term.buffer.ydisp, startYDisp - 1);
|
||||
|
||||
@@ -497,11 +497,8 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
this._mouseService = this._instantiationService.createInstance(MouseService);
|
||||
this._instantiationService.setService(IMouseService, this._mouseService);
|
||||
|
||||
this.viewport = this._instantiationService.createInstance(Viewport,
|
||||
(amount: number, suppressScrollEvent: boolean) => this.scrollLines(amount, suppressScrollEvent, ScrollSource.VIEWPORT),
|
||||
this._viewportElement,
|
||||
this._viewportScrollArea
|
||||
);
|
||||
this.viewport = this._instantiationService.createInstance(Viewport, this._viewportElement, this._viewportScrollArea);
|
||||
this.viewport.onRequestScrollLines(e => this.scrollLines(e.amount, e.suppressScrollEvent, ScrollSource.VIEWPORT)),
|
||||
this.register(this._inputHandler.onRequestSyncScrollBar(() => this.viewport!.syncScrollArea()));
|
||||
this.register(this.viewport);
|
||||
|
||||
@@ -1007,7 +1004,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
|
||||
if (!shouldIgnoreComposition && !this._compositionHelper!.keydown(event)) {
|
||||
if (this.options.scrollOnUserInput && this.buffer.ybase !== this.buffer.ydisp) {
|
||||
this._bufferService.scrollToBottom();
|
||||
this.scrollToBottom();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -296,6 +296,8 @@ export class MockRenderer implements IRenderer {
|
||||
}
|
||||
|
||||
export class MockViewport implements IViewport {
|
||||
private readonly _onRequestScrollLines = new EventEmitter<{ amount: number, suppressScrollEvent: boolean }>();
|
||||
public readonly onRequestScrollLines = this._onRequestScrollLines.event;
|
||||
public dispose(): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
@@ -319,7 +321,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 { }
|
||||
public scrollLines(disp: number): void {
|
||||
this._onRequestScrollLines.fire({ amount: disp, suppressScrollEvent: false });
|
||||
}
|
||||
}
|
||||
|
||||
export class MockCompositionHelper implements ICompositionHelper {
|
||||
|
||||
Vendored
+19
@@ -69,10 +69,28 @@ export interface IPublicTerminal extends IDisposable {
|
||||
selectAll(): void;
|
||||
selectLines(start: number, end: number): void;
|
||||
dispose(): void;
|
||||
/**
|
||||
* Scroll the display of the terminal
|
||||
* @param amount The number of lines to scroll down (negative scroll up).
|
||||
*/
|
||||
scrollLines(amount: number): void;
|
||||
/**
|
||||
* Scroll the display of the terminal by a number of pages.
|
||||
* @param pageCount The number of pages to scroll (negative scrolls up).
|
||||
*/
|
||||
scrollPages(pageCount: number): void;
|
||||
/**
|
||||
* Scrolls the display of the terminal to the top.
|
||||
*/
|
||||
scrollToTop(): void;
|
||||
/**
|
||||
* Scrolls the display of the terminal to the bottom.
|
||||
*/
|
||||
scrollToBottom(): void;
|
||||
/**
|
||||
* Scrolls to a line within the buffer.
|
||||
* @param line The 0-based line index to scroll to.
|
||||
*/
|
||||
scrollToLine(line: number): void;
|
||||
clear(): void;
|
||||
write(data: string | Uint8Array, callback?: () => void): void;
|
||||
@@ -142,6 +160,7 @@ export interface IPartialColorSet {
|
||||
|
||||
export interface IViewport extends IDisposable {
|
||||
scrollBarWidth: number;
|
||||
readonly onRequestScrollLines: IEvent<{ amount: number, suppressScrollEvent: boolean }>;
|
||||
syncScrollArea(immediate?: boolean): void;
|
||||
getLinesScrolled(ev: WheelEvent): number;
|
||||
getBufferElements(startLine: number, endLine?: number): { bufferElements: HTMLElement[], cursorElement?: HTMLElement };
|
||||
|
||||
@@ -10,6 +10,7 @@ import { ICharSizeService, ICoreBrowserService, IRenderService, IThemeService }
|
||||
import { IBufferService, IOptionsService } from 'common/services/Services';
|
||||
import { IBuffer } from 'common/buffer/Types';
|
||||
import { IRenderDimensions } from 'browser/renderer/shared/Types';
|
||||
import { EventEmitter } from 'common/EventEmitter';
|
||||
|
||||
const FALLBACK_SCROLL_BAR_WIDTH = 15;
|
||||
|
||||
@@ -48,8 +49,10 @@ export class Viewport extends Disposable implements IViewport {
|
||||
target: -1
|
||||
};
|
||||
|
||||
private readonly _onRequestScrollLines = this.register(new EventEmitter<{ amount: number, suppressScrollEvent: boolean }>());
|
||||
public readonly onRequestScrollLines = this._onRequestScrollLines.event;
|
||||
|
||||
constructor(
|
||||
private readonly _scrollLines: (amount: number, suppressScrollEvent: boolean) => void,
|
||||
private readonly _viewportElement: HTMLElement,
|
||||
private readonly _scrollArea: HTMLElement,
|
||||
@IBufferService private readonly _bufferService: IBufferService,
|
||||
@@ -175,13 +178,13 @@ export class Viewport extends Disposable implements IViewport {
|
||||
if (this._ignoreNextScrollEvent) {
|
||||
this._ignoreNextScrollEvent = false;
|
||||
// Still trigger the scroll so lines get refreshed
|
||||
this._scrollLines(0, true);
|
||||
this._onRequestScrollLines.fire({ amount: 0, suppressScrollEvent: true });
|
||||
return;
|
||||
}
|
||||
|
||||
const newRow = Math.round(this._lastScrollTop / this._currentRowHeight);
|
||||
const diff = newRow - this._bufferService.buffer.ydisp;
|
||||
this._scrollLines(diff, true);
|
||||
this._onRequestScrollLines.fire({ amount: diff, suppressScrollEvent: true });
|
||||
}
|
||||
|
||||
private _smoothScroll(): void {
|
||||
@@ -265,7 +268,7 @@ export class Viewport extends Disposable implements IViewport {
|
||||
|
||||
public scrollLines(disp: number): void {
|
||||
if (!this._optionsService.rawOptions.smoothScrollDuration) {
|
||||
this._scrollLines(disp, false);
|
||||
this._onRequestScrollLines.fire({ amount: disp, suppressScrollEvent: false });
|
||||
} else {
|
||||
const amount = disp * this._currentRowHeight;
|
||||
this._smoothScrollState.startTime = Date.now();
|
||||
|
||||
+11
-17
@@ -195,38 +195,32 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
|
||||
/**
|
||||
* Scroll the display of the terminal
|
||||
* @param disp The number of lines to scroll down (negative scroll up).
|
||||
* @param suppressScrollEvent Don't emit the scroll event as scrollLines. This is used
|
||||
* to avoid unwanted events being handled by the viewport when the event was triggered from the
|
||||
* viewport originally.
|
||||
* @param suppressScrollEvent Don't emit the scroll event as scrollLines. This is used to avoid
|
||||
* unwanted events being handled by the viewport when the event was triggered from the viewport
|
||||
* originally.
|
||||
* @param source Which component the event came from.
|
||||
*/
|
||||
public scrollLines(disp: number, suppressScrollEvent?: boolean, source?: ScrollSource): void {
|
||||
this._bufferService.scrollLines(disp, suppressScrollEvent, source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll the display of the terminal by a number of pages.
|
||||
* @param pageCount The number of pages to scroll (negative scrolls up).
|
||||
*/
|
||||
public scrollPages(pageCount: number): void {
|
||||
this._bufferService.scrollPages(pageCount);
|
||||
this.scrollLines(pageCount * (this.rows - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrolls the display of the terminal to the top.
|
||||
*/
|
||||
public scrollToTop(): void {
|
||||
this._bufferService.scrollToTop();
|
||||
this.scrollLines(-this._bufferService.buffer.ydisp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrolls the display of the terminal to the bottom.
|
||||
*/
|
||||
public scrollToBottom(): void {
|
||||
this._bufferService.scrollToBottom();
|
||||
this.scrollLines(this._bufferService.buffer.ybase - this._bufferService.buffer.ydisp);
|
||||
}
|
||||
|
||||
public scrollToLine(line: number): void {
|
||||
this._bufferService.scrollToLine(line);
|
||||
const scrollAmount = line - this._bufferService.buffer.ydisp;
|
||||
if (scrollAmount !== 0) {
|
||||
this.scrollLines(scrollAmount);
|
||||
}
|
||||
}
|
||||
|
||||
/** Add handler for ESC escape sequence. See xterm.d.ts for details. */
|
||||
|
||||
@@ -147,33 +147,4 @@ export class BufferService extends Disposable implements IBufferService {
|
||||
this._onScroll.fire(buffer.ydisp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll the display of the terminal by a number of pages.
|
||||
* @param pageCount The number of pages to scroll (negative scrolls up).
|
||||
*/
|
||||
public scrollPages(pageCount: number): void {
|
||||
this.scrollLines(pageCount * (this.rows - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrolls the display of the terminal to the top.
|
||||
*/
|
||||
public scrollToTop(): void {
|
||||
this.scrollLines(-this.buffer.ydisp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrolls the display of the terminal to the bottom.
|
||||
*/
|
||||
public scrollToBottom(): void {
|
||||
this.scrollLines(this.buffer.ybase - this.buffer.ydisp);
|
||||
}
|
||||
|
||||
public scrollToLine(line: number): void {
|
||||
const scrollAmount = line - this.buffer.ydisp;
|
||||
if (scrollAmount !== 0) {
|
||||
this.scrollLines(scrollAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,11 +21,7 @@ export interface IBufferService {
|
||||
onResize: IEvent<{ cols: number, rows: number }>;
|
||||
onScroll: IEvent<number>;
|
||||
scroll(eraseAttr: IAttributeData, isWrapped?: boolean): void;
|
||||
scrollToBottom(): void;
|
||||
scrollToTop(): void;
|
||||
scrollToLine(line: number): void;
|
||||
scrollLines(disp: number, suppressScrollEvent?: boolean, source?: ScrollSource): void;
|
||||
scrollPages(pageCount: number): void;
|
||||
resize(cols: number, rows: number): void;
|
||||
reset(): void;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user