Merge branch 'master' into allowmarkersonaltbuffer
This commit is contained in:
+59
-1
@@ -13,6 +13,12 @@ import { IRenderDimensions } from 'browser/renderer/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.
|
||||
@@ -36,6 +42,11 @@ export class Viewport extends Disposable implements IViewport {
|
||||
|
||||
private _refreshAnimationFrame: number | null = null;
|
||||
private _ignoreNextScrollEvent: boolean = false;
|
||||
private _smoothScrollState: ISmoothScrollState = {
|
||||
startTime: 0,
|
||||
origin: -1,
|
||||
target: -1
|
||||
};
|
||||
|
||||
constructor(
|
||||
private readonly _scrollLines: (amount: number) => void,
|
||||
@@ -168,6 +179,37 @@ export class Viewport extends Disposable implements IViewport {
|
||||
this._scrollLines(diff);
|
||||
}
|
||||
|
||||
private _smoothScroll(): void {
|
||||
// Check valid state
|
||||
if (this._isDisposed || this._smoothScrollState.origin === -1 || this._smoothScrollState.target === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate position complete
|
||||
const percent = this._smoothScrollPercent();
|
||||
this._viewportElement.scrollTop = this._smoothScrollState.origin + Math.round(percent * (this._smoothScrollState.target - this._smoothScrollState.origin));
|
||||
|
||||
// Continue or finish smooth scroll
|
||||
if (percent < 1) {
|
||||
window.requestAnimationFrame(() => this._smoothScroll());
|
||||
} else {
|
||||
this._clearSmoothScrollState();
|
||||
}
|
||||
}
|
||||
|
||||
private _smoothScrollPercent(): number {
|
||||
if (!this._optionsService.rawOptions.smoothScrollDuration || !this._smoothScrollState.startTime) {
|
||||
return 1;
|
||||
}
|
||||
return Math.max(Math.min((Date.now() - this._smoothScrollState.startTime) / this._optionsService.rawOptions.smoothScrollDuration, 1), 0);
|
||||
}
|
||||
|
||||
private _clearSmoothScrollState(): void {
|
||||
this._smoothScrollState.startTime = 0;
|
||||
this._smoothScrollState.origin = -1;
|
||||
this._smoothScrollState.target = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles bubbling of scroll event in case the viewport has reached top or bottom
|
||||
* @param ev The scroll event.
|
||||
@@ -196,7 +238,23 @@ export class Viewport extends Disposable implements IViewport {
|
||||
if (amount === 0) {
|
||||
return false;
|
||||
}
|
||||
this._viewportElement.scrollTop += amount;
|
||||
if (!this._optionsService.rawOptions.smoothScrollDuration) {
|
||||
this._viewportElement.scrollTop += amount;
|
||||
} else {
|
||||
this._smoothScrollState.startTime = Date.now();
|
||||
if (this._smoothScrollPercent() < 1) {
|
||||
this._smoothScrollState.origin = this._viewportElement.scrollTop;
|
||||
if (this._smoothScrollState.target === -1) {
|
||||
this._smoothScrollState.target = this._viewportElement.scrollTop + amount;
|
||||
} else {
|
||||
this._smoothScrollState.target += amount;
|
||||
}
|
||||
this._smoothScrollState.target = Math.max(Math.min(this._smoothScrollState.target, this._viewportElement.scrollHeight), 0);
|
||||
this._smoothScroll();
|
||||
} else {
|
||||
this._clearSmoothScrollState();
|
||||
}
|
||||
}
|
||||
return this._bubbleScroll(ev, amount);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2022 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { toDisposable } from 'common/Lifecycle';
|
||||
import { IDisposable } from 'common/Types';
|
||||
|
||||
export function observeDevicePixelDimensions(element: HTMLElement, callback: (deviceWidth: number, deviceHeight: number) => void): IDisposable {
|
||||
// Observe any resizes to the element and extract the actual pixel size of the element if the
|
||||
// devicePixelContentBoxSize API is supported. This allows correcting rounding errors when
|
||||
// converting between CSS pixels and device pixels which causes blurry rendering when device
|
||||
// pixel ratio is not a round number.
|
||||
let observer: ResizeObserver | undefined = new ResizeObserver((entries) => {
|
||||
const entry = entries.find((entry) => entry.target === element);
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disconnect if devicePixelContentBoxSize isn't supported by the browser
|
||||
if (!('devicePixelContentBoxSize' in entry)) {
|
||||
observer?.disconnect();
|
||||
observer = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
callback(
|
||||
entry.devicePixelContentBoxSize[0].inlineSize,
|
||||
entry.devicePixelContentBoxSize[0].blockSize
|
||||
);
|
||||
});
|
||||
observer.observe(element, { box: ['device-pixel-content-box'] } as any);
|
||||
return toDisposable(() => observer?.disconnect());
|
||||
}
|
||||
Reference in New Issue
Block a user