Merge branch 'master' into protected
This commit is contained in:
@@ -98,7 +98,7 @@ export class AccessibilityManager extends Disposable {
|
||||
this.register(this._terminal.onBlur(() => this._clearLiveRegion()));
|
||||
this.register(this._renderService.onDimensionsChange(() => this._refreshRowsDimensions()));
|
||||
|
||||
this._screenDprMonitor = new ScreenDprMonitor();
|
||||
this._screenDprMonitor = new ScreenDprMonitor(window);
|
||||
this.register(this._screenDprMonitor);
|
||||
this._screenDprMonitor.setListener(() => this._refreshRowsDimensions());
|
||||
// This shouldn't be needed on modern browsers but is present in case the
|
||||
|
||||
@@ -16,13 +16,14 @@ export class RenderDebouncer implements IRenderDebouncerWithCallback {
|
||||
private _refreshCallbacks: FrameRequestCallback[] = [];
|
||||
|
||||
constructor(
|
||||
private _parentWindow: Window,
|
||||
private _renderCallback: (start: number, end: number) => void
|
||||
) {
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
if (this._animationFrame) {
|
||||
window.cancelAnimationFrame(this._animationFrame);
|
||||
this._parentWindow.cancelAnimationFrame(this._animationFrame);
|
||||
this._animationFrame = undefined;
|
||||
}
|
||||
}
|
||||
@@ -30,7 +31,7 @@ export class RenderDebouncer implements IRenderDebouncerWithCallback {
|
||||
public addRefreshCallback(callback: FrameRequestCallback): number {
|
||||
this._refreshCallbacks.push(callback);
|
||||
if (!this._animationFrame) {
|
||||
this._animationFrame = window.requestAnimationFrame(() => this._innerRefresh());
|
||||
this._animationFrame = this._parentWindow.requestAnimationFrame(() => this._innerRefresh());
|
||||
}
|
||||
return this._animationFrame;
|
||||
}
|
||||
@@ -48,7 +49,7 @@ export class RenderDebouncer implements IRenderDebouncerWithCallback {
|
||||
return;
|
||||
}
|
||||
|
||||
this._animationFrame = window.requestAnimationFrame(() => this._innerRefresh());
|
||||
this._animationFrame = this._parentWindow.requestAnimationFrame(() => this._innerRefresh());
|
||||
}
|
||||
|
||||
private _innerRefresh(): void {
|
||||
|
||||
@@ -18,11 +18,16 @@ export type ScreenDprListener = (newDevicePixelRatio?: number, oldDevicePixelRat
|
||||
* monitor with a different DPI.
|
||||
*/
|
||||
export class ScreenDprMonitor extends Disposable {
|
||||
private _currentDevicePixelRatio: number = window.devicePixelRatio;
|
||||
private _currentDevicePixelRatio: number;
|
||||
private _outerListener: ((this: MediaQueryList, ev: MediaQueryListEvent) => any) | undefined;
|
||||
private _listener: ScreenDprListener | undefined;
|
||||
private _resolutionMediaMatchList: MediaQueryList | undefined;
|
||||
|
||||
constructor(private _parentWindow: Window) {
|
||||
super();
|
||||
this._currentDevicePixelRatio = this._parentWindow.devicePixelRatio;
|
||||
}
|
||||
|
||||
public setListener(listener: ScreenDprListener): void {
|
||||
if (this._listener) {
|
||||
this.clearListener();
|
||||
@@ -32,7 +37,7 @@ export class ScreenDprMonitor extends Disposable {
|
||||
if (!this._listener) {
|
||||
return;
|
||||
}
|
||||
this._listener(window.devicePixelRatio, this._currentDevicePixelRatio);
|
||||
this._listener(this._parentWindow.devicePixelRatio, this._currentDevicePixelRatio);
|
||||
this._updateDpr();
|
||||
};
|
||||
this._updateDpr();
|
||||
@@ -52,8 +57,8 @@ export class ScreenDprMonitor extends Disposable {
|
||||
this._resolutionMediaMatchList?.removeListener(this._outerListener);
|
||||
|
||||
// Add listeners for new DPR
|
||||
this._currentDevicePixelRatio = window.devicePixelRatio;
|
||||
this._resolutionMediaMatchList = window.matchMedia(`screen and (resolution: ${window.devicePixelRatio}dppx)`);
|
||||
this._currentDevicePixelRatio = this._parentWindow.devicePixelRatio;
|
||||
this._resolutionMediaMatchList = this._parentWindow.matchMedia(`screen and (resolution: ${this._parentWindow.devicePixelRatio}dppx)`);
|
||||
this._resolutionMediaMatchList.addListener(this._outerListener);
|
||||
}
|
||||
|
||||
|
||||
@@ -495,7 +495,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
this.register(addDisposableDomListener(this.textarea, 'blur', () => this._onTextAreaBlur()));
|
||||
this._helperContainer.appendChild(this.textarea);
|
||||
|
||||
this._coreBrowserService = this._instantiationService.createInstance(CoreBrowserService, this.textarea);
|
||||
this._coreBrowserService = this._instantiationService.createInstance(CoreBrowserService, this.textarea, this._document.defaultView ?? window);
|
||||
this._instantiationService.setService(ICoreBrowserService, this._coreBrowserService);
|
||||
|
||||
this._charSizeService = this._instantiationService.createInstance(CharSizeService, this._document, this._helperContainer);
|
||||
|
||||
@@ -343,6 +343,10 @@ export class MockCompositionHelper implements ICompositionHelper {
|
||||
export class MockCoreBrowserService implements ICoreBrowserService {
|
||||
public serviceBrand: undefined;
|
||||
public isFocused: boolean = true;
|
||||
public get window(): Window & typeof globalThis {
|
||||
throw Error('Window object not available in tests');
|
||||
}
|
||||
public dpr: number = 1;
|
||||
}
|
||||
|
||||
export class MockCharSizeService implements ICharSizeService {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { Disposable } from 'common/Lifecycle';
|
||||
import { addDisposableDomListener } from 'browser/Lifecycle';
|
||||
import { IColorSet, IViewport } from 'browser/Types';
|
||||
import { ICharSizeService, IRenderService } from 'browser/services/Services';
|
||||
import { ICharSizeService, ICoreBrowserService, IRenderService } from 'browser/services/Services';
|
||||
import { IBufferService, IOptionsService } from 'common/services/Services';
|
||||
import { IBuffer } from 'common/buffer/Types';
|
||||
import { IRenderDimensions } from 'browser/renderer/Types';
|
||||
@@ -56,7 +56,8 @@ export class Viewport extends Disposable implements IViewport {
|
||||
@IBufferService private readonly _bufferService: IBufferService,
|
||||
@IOptionsService private readonly _optionsService: IOptionsService,
|
||||
@ICharSizeService private readonly _charSizeService: ICharSizeService,
|
||||
@IRenderService private readonly _renderService: IRenderService
|
||||
@IRenderService private readonly _renderService: IRenderService,
|
||||
@ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -88,18 +89,18 @@ export class Viewport extends Disposable implements IViewport {
|
||||
if (immediate) {
|
||||
this._innerRefresh();
|
||||
if (this._refreshAnimationFrame !== null) {
|
||||
cancelAnimationFrame(this._refreshAnimationFrame);
|
||||
this._coreBrowserService.window.cancelAnimationFrame(this._refreshAnimationFrame);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (this._refreshAnimationFrame === null) {
|
||||
this._refreshAnimationFrame = requestAnimationFrame(() => this._innerRefresh());
|
||||
this._refreshAnimationFrame = this._coreBrowserService.window.requestAnimationFrame(() => this._innerRefresh());
|
||||
}
|
||||
}
|
||||
|
||||
private _innerRefresh(): void {
|
||||
if (this._charSizeService.height > 0) {
|
||||
this._currentRowHeight = this._renderService.dimensions.scaledCellHeight / window.devicePixelRatio;
|
||||
this._currentRowHeight = this._renderService.dimensions.scaledCellHeight / this._coreBrowserService.dpr;
|
||||
this._currentScaledCellHeight = this._renderService.dimensions.scaledCellHeight;
|
||||
this._lastRecordedViewportHeight = this._viewportElement.offsetHeight;
|
||||
const newBufferHeight = Math.round(this._currentRowHeight * this._lastRecordedBufferLength) + (this._lastRecordedViewportHeight - this._renderService.dimensions.canvasHeight);
|
||||
@@ -191,7 +192,7 @@ export class Viewport extends Disposable implements IViewport {
|
||||
|
||||
// Continue or finish smooth scroll
|
||||
if (percent < 1) {
|
||||
window.requestAnimationFrame(() => this._smoothScroll());
|
||||
this._coreBrowserService.window.requestAnimationFrame(() => this._smoothScroll());
|
||||
} else {
|
||||
this._clearSmoothScrollState();
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { ColorZoneStore, IColorZone, IColorZoneStore } from 'browser/decorations/ColorZoneStore';
|
||||
import { addDisposableDomListener } from 'browser/Lifecycle';
|
||||
import { IRenderService } from 'browser/services/Services';
|
||||
import { ICoreBrowserService, IRenderService } from 'browser/services/Services';
|
||||
import { Disposable } from 'common/Lifecycle';
|
||||
import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services';
|
||||
|
||||
@@ -51,7 +51,8 @@ export class OverviewRulerRenderer extends Disposable {
|
||||
@IBufferService private readonly _bufferService: IBufferService,
|
||||
@IDecorationService private readonly _decorationService: IDecorationService,
|
||||
@IRenderService private readonly _renderService: IRenderService,
|
||||
@IOptionsService private readonly _optionsService: IOptionsService
|
||||
@IOptionsService private readonly _optionsService: IOptionsService,
|
||||
@ICoreBrowserService private readonly _coreBrowseService: ICoreBrowserService
|
||||
) {
|
||||
super();
|
||||
this._canvas = document.createElement('canvas');
|
||||
@@ -112,7 +113,7 @@ export class OverviewRulerRenderer extends Disposable {
|
||||
}
|
||||
}));
|
||||
// device pixel ratio changed
|
||||
this.register(addDisposableDomListener(window, 'resize', () => {
|
||||
this.register(addDisposableDomListener(this._coreBrowseService.window, 'resize', () => {
|
||||
this._queueRefresh(true);
|
||||
}));
|
||||
// set the canvas dimensions
|
||||
@@ -142,11 +143,11 @@ export class OverviewRulerRenderer extends Disposable {
|
||||
}
|
||||
|
||||
private _refreshDrawHeightConstants(): void {
|
||||
drawHeight.full = Math.round(2 * window.devicePixelRatio);
|
||||
drawHeight.full = Math.round(2 * this._coreBrowseService.dpr);
|
||||
// Calculate actual pixels per line
|
||||
const pixelsPerLine = this._canvas.height / this._bufferService.buffer.lines.length;
|
||||
// Clamp actual pixels within a range
|
||||
const nonFullHeight = Math.round(Math.max(Math.min(pixelsPerLine, 12), 6) * window.devicePixelRatio);
|
||||
const nonFullHeight = Math.round(Math.max(Math.min(pixelsPerLine, 12), 6) * this._coreBrowseService.dpr);
|
||||
drawHeight.left = nonFullHeight;
|
||||
drawHeight.center = nonFullHeight;
|
||||
drawHeight.right = nonFullHeight;
|
||||
@@ -164,9 +165,9 @@ export class OverviewRulerRenderer extends Disposable {
|
||||
|
||||
private _refreshCanvasDimensions(): void {
|
||||
this._canvas.style.width = `${this._width}px`;
|
||||
this._canvas.width = Math.round(this._width * window.devicePixelRatio);
|
||||
this._canvas.width = Math.round(this._width * this._coreBrowseService.dpr);
|
||||
this._canvas.style.height = `${this._screenElement.clientHeight}px`;
|
||||
this._canvas.height = Math.round(this._screenElement.clientHeight * window.devicePixelRatio);
|
||||
this._canvas.height = Math.round(this._screenElement.clientHeight * this._coreBrowseService.dpr);
|
||||
this._refreshDrawConstants();
|
||||
this._refreshColorZonePadding();
|
||||
}
|
||||
@@ -220,7 +221,7 @@ export class OverviewRulerRenderer extends Disposable {
|
||||
if (this._animationFrame !== undefined) {
|
||||
return;
|
||||
}
|
||||
this._animationFrame = window.requestAnimationFrame(() => {
|
||||
this._animationFrame = this._coreBrowseService.window.requestAnimationFrame(() => {
|
||||
this._refreshDecorations();
|
||||
this._animationFrame = undefined;
|
||||
});
|
||||
|
||||
@@ -383,7 +383,8 @@ export function tryDrawCustomChar(
|
||||
yOffset: number,
|
||||
scaledCellWidth: number,
|
||||
scaledCellHeight: number,
|
||||
fontSize: number
|
||||
fontSize: number,
|
||||
devicePixelRatio: number
|
||||
): boolean {
|
||||
const blockElementDefinition = blockElementDefinitions[c];
|
||||
if (blockElementDefinition) {
|
||||
@@ -399,13 +400,13 @@ export function tryDrawCustomChar(
|
||||
|
||||
const boxDrawingDefinition = boxDrawingDefinitions[c];
|
||||
if (boxDrawingDefinition) {
|
||||
drawBoxDrawingChar(ctx, boxDrawingDefinition, xOffset, yOffset, scaledCellWidth, scaledCellHeight);
|
||||
drawBoxDrawingChar(ctx, boxDrawingDefinition, xOffset, yOffset, scaledCellWidth, scaledCellHeight, devicePixelRatio);
|
||||
return true;
|
||||
}
|
||||
|
||||
const powerlineDefinition = powerlineDefinitions[c];
|
||||
if (powerlineDefinition) {
|
||||
drawPowerlineChar(ctx, powerlineDefinition, xOffset, yOffset, scaledCellWidth, scaledCellHeight, fontSize);
|
||||
drawPowerlineChar(ctx, powerlineDefinition, xOffset, yOffset, scaledCellWidth, scaledCellHeight, fontSize, devicePixelRatio);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -540,12 +541,13 @@ function drawBoxDrawingChar(
|
||||
xOffset: number,
|
||||
yOffset: number,
|
||||
scaledCellWidth: number,
|
||||
scaledCellHeight: number
|
||||
scaledCellHeight: number,
|
||||
devicePixelRatio: number
|
||||
): void {
|
||||
ctx.strokeStyle = ctx.fillStyle;
|
||||
for (const [fontWeight, instructions] of Object.entries(charDefinition)) {
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = window.devicePixelRatio * Number.parseInt(fontWeight);
|
||||
ctx.lineWidth = devicePixelRatio * Number.parseInt(fontWeight);
|
||||
let actualInstructions: string;
|
||||
if (typeof instructions === 'function') {
|
||||
const xp = .15;
|
||||
@@ -565,7 +567,7 @@ function drawBoxDrawingChar(
|
||||
if (!args[0] || !args[1]) {
|
||||
continue;
|
||||
}
|
||||
f(ctx, translateArgs(args, scaledCellWidth, scaledCellHeight, xOffset, yOffset, true));
|
||||
f(ctx, translateArgs(args, scaledCellWidth, scaledCellHeight, xOffset, yOffset, true, devicePixelRatio));
|
||||
}
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
@@ -579,12 +581,13 @@ function drawPowerlineChar(
|
||||
yOffset: number,
|
||||
scaledCellWidth: number,
|
||||
scaledCellHeight: number,
|
||||
fontSize: number
|
||||
fontSize: number,
|
||||
devicePixelRatio: number
|
||||
): void {
|
||||
ctx.beginPath();
|
||||
// Scale the stroke with DPR and font size
|
||||
const cssLineWidth = fontSize / 12;
|
||||
ctx.lineWidth = window.devicePixelRatio * cssLineWidth;
|
||||
ctx.lineWidth = devicePixelRatio * cssLineWidth;
|
||||
for (const instruction of charDefinition.d.split(' ')) {
|
||||
const type = instruction[0];
|
||||
const f = svgToCanvasInstructionMap[type];
|
||||
@@ -626,7 +629,7 @@ const svgToCanvasInstructionMap: { [index: string]: any } = {
|
||||
'M': (ctx: CanvasRenderingContext2D, args: number[]) => ctx.moveTo(args[0], args[1])
|
||||
};
|
||||
|
||||
function translateArgs(args: string[], cellWidth: number, cellHeight: number, xOffset: number, yOffset: number, doClamp: boolean, leftPadding: number = 0, rightPadding: number = 0): number[] {
|
||||
function translateArgs(args: string[], cellWidth: number, cellHeight: number, xOffset: number, yOffset: number, doClamp: boolean, devicePixelRatio: number, leftPadding: number = 0, rightPadding: number = 0): number[] {
|
||||
const result = args.map(e => parseFloat(e) || parseInt(e));
|
||||
|
||||
if (result.length < 2) {
|
||||
@@ -635,14 +638,14 @@ function translateArgs(args: string[], cellWidth: number, cellHeight: number, xO
|
||||
|
||||
for (let x = 0; x < result.length; x += 2) {
|
||||
// Translate from 0-1 to 0-cellWidth
|
||||
result[x] *= cellWidth - (leftPadding * window.devicePixelRatio) - (rightPadding * window.devicePixelRatio);
|
||||
result[x] *= cellWidth - (leftPadding * devicePixelRatio) - (rightPadding * devicePixelRatio);
|
||||
// Ensure coordinate doesn't escape cell bounds and round to the nearest 0.5 to ensure a crisp
|
||||
// line at 100% devicePixelRatio
|
||||
if (doClamp && result[x] !== 0) {
|
||||
result[x] = clamp(Math.round(result[x] + 0.5) - 0.5, cellWidth, 0);
|
||||
}
|
||||
// Apply the cell's offset (ie. x*cellWidth)
|
||||
result[x] += xOffset + (leftPadding * window.devicePixelRatio);
|
||||
result[x] += xOffset + (leftPadding * devicePixelRatio);
|
||||
}
|
||||
|
||||
for (let y = 1; y < result.length; y += 2) {
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
import { toDisposable } from 'common/Lifecycle';
|
||||
import { IDisposable } from 'common/Types';
|
||||
|
||||
export function observeDevicePixelDimensions(element: HTMLElement, callback: (deviceWidth: number, deviceHeight: number) => void): IDisposable {
|
||||
export function observeDevicePixelDimensions(element: HTMLElement, parentWindow: Window & typeof globalThis, 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) => {
|
||||
let observer: ResizeObserver | undefined = new parentWindow.ResizeObserver((entries) => {
|
||||
const entry = entries.find((entry) => entry.target === element);
|
||||
if (!entry) {
|
||||
return;
|
||||
|
||||
@@ -8,7 +8,7 @@ import { BOLD_CLASS, ITALIC_CLASS, CURSOR_CLASS, CURSOR_STYLE_BLOCK_CLASS, CURSO
|
||||
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/Constants';
|
||||
import { Disposable } from 'common/Lifecycle';
|
||||
import { IColorSet, ILinkifierEvent, ILinkifier2 } from 'browser/Types';
|
||||
import { ICharSizeService } from 'browser/services/Services';
|
||||
import { ICharSizeService, ICoreBrowserService } from 'browser/services/Services';
|
||||
import { IOptionsService, IBufferService, IInstantiationService } from 'common/services/Services';
|
||||
import { EventEmitter, IEvent } from 'common/EventEmitter';
|
||||
import { color } from 'common/Color';
|
||||
@@ -51,7 +51,8 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@ICharSizeService private readonly _charSizeService: ICharSizeService,
|
||||
@IOptionsService private readonly _optionsService: IOptionsService,
|
||||
@IBufferService private readonly _bufferService: IBufferService
|
||||
@IBufferService private readonly _bufferService: IBufferService,
|
||||
@ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService
|
||||
) {
|
||||
super();
|
||||
this._rowContainer = document.createElement('div');
|
||||
@@ -101,16 +102,17 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
}
|
||||
|
||||
private _updateDimensions(): void {
|
||||
this.dimensions.scaledCharWidth = this._charSizeService.width * window.devicePixelRatio;
|
||||
this.dimensions.scaledCharHeight = Math.ceil(this._charSizeService.height * window.devicePixelRatio);
|
||||
const dpr = this._coreBrowserService.dpr;
|
||||
this.dimensions.scaledCharWidth = this._charSizeService.width * dpr;
|
||||
this.dimensions.scaledCharHeight = Math.ceil(this._charSizeService.height * dpr);
|
||||
this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth + Math.round(this._optionsService.rawOptions.letterSpacing);
|
||||
this.dimensions.scaledCellHeight = Math.floor(this.dimensions.scaledCharHeight * this._optionsService.rawOptions.lineHeight);
|
||||
this.dimensions.scaledCharLeft = 0;
|
||||
this.dimensions.scaledCharTop = 0;
|
||||
this.dimensions.scaledCanvasWidth = this.dimensions.scaledCellWidth * this._bufferService.cols;
|
||||
this.dimensions.scaledCanvasHeight = this.dimensions.scaledCellHeight * this._bufferService.rows;
|
||||
this.dimensions.canvasWidth = Math.round(this.dimensions.scaledCanvasWidth / window.devicePixelRatio);
|
||||
this.dimensions.canvasHeight = Math.round(this.dimensions.scaledCanvasHeight / window.devicePixelRatio);
|
||||
this.dimensions.canvasWidth = Math.round(this.dimensions.scaledCanvasWidth / dpr);
|
||||
this.dimensions.canvasHeight = Math.round(this.dimensions.scaledCanvasHeight / dpr);
|
||||
this.dimensions.actualCellWidth = this.dimensions.canvasWidth / this._bufferService.cols;
|
||||
this.dimensions.actualCellHeight = this.dimensions.canvasHeight / this._bufferService.rows;
|
||||
|
||||
|
||||
@@ -9,12 +9,17 @@ export class CoreBrowserService implements ICoreBrowserService {
|
||||
public serviceBrand: undefined;
|
||||
|
||||
constructor(
|
||||
private _textarea: HTMLTextAreaElement
|
||||
private _textarea: HTMLTextAreaElement,
|
||||
public readonly window: Window & typeof globalThis
|
||||
) {
|
||||
}
|
||||
|
||||
public get dpr(): number {
|
||||
return this.window.devicePixelRatio;
|
||||
}
|
||||
|
||||
public get isFocused(): boolean {
|
||||
const docOrShadowRoot = this._textarea.getRootNode ? this._textarea.getRootNode() as Document | ShadowRoot : document;
|
||||
return docOrShadowRoot.activeElement === this._textarea && document.hasFocus();
|
||||
const docOrShadowRoot = this._textarea.getRootNode ? this._textarea.getRootNode() as Document | ShadowRoot : this._textarea.ownerDocument;
|
||||
return docOrShadowRoot.activeElement === this._textarea && this._textarea.ownerDocument.hasFocus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import { ScreenDprMonitor } from 'browser/ScreenDprMonitor';
|
||||
import { addDisposableDomListener } from 'browser/Lifecycle';
|
||||
import { IColorSet, IRenderDebouncerWithCallback } from 'browser/Types';
|
||||
import { IOptionsService, IBufferService, IDecorationService } from 'common/services/Services';
|
||||
import { ICharSizeService, IRenderService } from 'browser/services/Services';
|
||||
import { ICharSizeService, ICoreBrowserService, IRenderService } from 'browser/services/Services';
|
||||
|
||||
interface ISelectionState {
|
||||
start: [number, number] | undefined;
|
||||
@@ -55,16 +55,17 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
@IOptionsService optionsService: IOptionsService,
|
||||
@ICharSizeService private readonly _charSizeService: ICharSizeService,
|
||||
@IDecorationService decorationService: IDecorationService,
|
||||
@IBufferService bufferService: IBufferService
|
||||
@IBufferService bufferService: IBufferService,
|
||||
@ICoreBrowserService coreBrowserService: ICoreBrowserService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.register({ dispose: () => this._renderer.dispose() });
|
||||
|
||||
this._renderDebouncer = new RenderDebouncer((start, end) => this._renderRows(start, end));
|
||||
this._renderDebouncer = new RenderDebouncer(coreBrowserService.window, (start, end) => this._renderRows(start, end));
|
||||
this.register(this._renderDebouncer);
|
||||
|
||||
this._screenDprMonitor = new ScreenDprMonitor();
|
||||
this._screenDprMonitor = new ScreenDprMonitor(coreBrowserService.window);
|
||||
this._screenDprMonitor.setListener(() => this.onDevicePixelRatioChange());
|
||||
this.register(this._screenDprMonitor);
|
||||
|
||||
@@ -84,12 +85,12 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
|
||||
// dprchange should handle this case, we need this as well for browsers that don't support the
|
||||
// matchMedia query.
|
||||
this.register(addDisposableDomListener(window, 'resize', () => this.onDevicePixelRatioChange()));
|
||||
this.register(addDisposableDomListener(coreBrowserService.window, 'resize', () => this.onDevicePixelRatioChange()));
|
||||
|
||||
// Detect whether IntersectionObserver is detected and enable renderer pause
|
||||
// and resume based on terminal visibility if so
|
||||
if ('IntersectionObserver' in window) {
|
||||
const observer = new IntersectionObserver(e => this._onIntersectionChange(e[e.length - 1]), { threshold: 0 });
|
||||
if ('IntersectionObserver' in coreBrowserService.window) {
|
||||
const observer = new coreBrowserService.window.IntersectionObserver(e => this._onIntersectionChange(e[e.length - 1]), { threshold: 0 });
|
||||
observer.observe(screenElement);
|
||||
this.register({ dispose: () => observer.disconnect() });
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { IBufferLine } from 'common/Types';
|
||||
import { MockBufferService, MockOptionsService, MockCoreService } from 'common/TestUtils.test';
|
||||
import { BufferLine } from 'common/buffer/BufferLine';
|
||||
import { IBufferService, IOptionsService } from 'common/services/Services';
|
||||
import { MockMouseService, MockRenderService } from 'browser/TestUtils.test';
|
||||
import { MockCoreBrowserService, MockMouseService, MockRenderService } from 'browser/TestUtils.test';
|
||||
import { CellData } from 'common/buffer/CellData';
|
||||
import { IBuffer } from 'common/buffer/Types';
|
||||
import { IRenderService } from 'browser/services/Services';
|
||||
@@ -21,7 +21,7 @@ class TestSelectionService extends SelectionService {
|
||||
optionsService: IOptionsService,
|
||||
renderService: IRenderService
|
||||
) {
|
||||
super(null!, null!, null!, bufferService, new MockCoreService(), new MockMouseService(), optionsService, renderService);
|
||||
super(null!, null!, null!, bufferService, new MockCoreService(), new MockMouseService(), optionsService, renderService, new MockCoreBrowserService());
|
||||
}
|
||||
|
||||
public get model(): SelectionModel { return this._model; }
|
||||
|
||||
@@ -10,7 +10,7 @@ import * as Browser from 'common/Platform';
|
||||
import { SelectionModel } from 'browser/selection/SelectionModel';
|
||||
import { CellData } from 'common/buffer/CellData';
|
||||
import { EventEmitter, IEvent } from 'common/EventEmitter';
|
||||
import { IMouseService, ISelectionService, IRenderService } from 'browser/services/Services';
|
||||
import { IMouseService, ISelectionService, IRenderService, ICoreBrowserService } from 'browser/services/Services';
|
||||
import { IBufferRange, ILinkifier2 } from 'browser/Types';
|
||||
import { IBufferService, IOptionsService, ICoreService } from 'common/services/Services';
|
||||
import { getCoordsRelativeToElement } from 'browser/input/Mouse';
|
||||
@@ -128,7 +128,8 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
@ICoreService private readonly _coreService: ICoreService,
|
||||
@IMouseService private readonly _mouseService: IMouseService,
|
||||
@IOptionsService private readonly _optionsService: IOptionsService,
|
||||
@IRenderService private readonly _renderService: IRenderService
|
||||
@IRenderService private readonly _renderService: IRenderService,
|
||||
@ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -270,7 +271,7 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
public refresh(isLinuxMouseSelection?: boolean): void {
|
||||
// Queue the refresh for the renderer
|
||||
if (!this._refreshAnimationFrame) {
|
||||
this._refreshAnimationFrame = window.requestAnimationFrame(() => this._refresh());
|
||||
this._refreshAnimationFrame = this._coreBrowserService.window.requestAnimationFrame(() => this._refresh());
|
||||
}
|
||||
|
||||
// If the platform is Linux and the refresh call comes from a mouse event,
|
||||
@@ -406,7 +407,7 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
* @param event The mouse event.
|
||||
*/
|
||||
private _getMouseEventScrollAmount(event: MouseEvent): number {
|
||||
let offset = getCoordsRelativeToElement(window, event, this._screenElement)[1];
|
||||
let offset = getCoordsRelativeToElement(this._coreBrowserService.window, event, this._screenElement)[1];
|
||||
const terminalHeight = this._renderService.dimensions.canvasHeight;
|
||||
if (offset >= 0 && offset <= terminalHeight) {
|
||||
return 0;
|
||||
@@ -491,7 +492,7 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
this._screenElement.ownerDocument.addEventListener('mousemove', this._mouseMoveListener);
|
||||
this._screenElement.ownerDocument.addEventListener('mouseup', this._mouseUpListener);
|
||||
}
|
||||
this._dragScrollIntervalTimer = window.setInterval(() => this._dragScroll(), DRAG_SCROLL_INTERVAL);
|
||||
this._dragScrollIntervalTimer = this._coreBrowserService.window.setInterval(() => this._dragScroll(), DRAG_SCROLL_INTERVAL);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -502,7 +503,7 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
this._screenElement.ownerDocument.removeEventListener('mousemove', this._mouseMoveListener);
|
||||
this._screenElement.ownerDocument.removeEventListener('mouseup', this._mouseUpListener);
|
||||
}
|
||||
clearInterval(this._dragScrollIntervalTimer);
|
||||
this._coreBrowserService.window.clearInterval(this._dragScrollIntervalTimer);
|
||||
this._dragScrollIntervalTimer = undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,16 @@ export interface ICoreBrowserService {
|
||||
serviceBrand: undefined;
|
||||
|
||||
readonly isFocused: boolean;
|
||||
/**
|
||||
* Parent window that the terminal is rendered into. DOM and rendering APIs
|
||||
* (e.g. requestAnimationFrame) should be invoked in the context of this
|
||||
* window.
|
||||
*/
|
||||
readonly window: Window & typeof globalThis;
|
||||
/**
|
||||
* Helper for getting the devicePixelRatio of the parent window.
|
||||
*/
|
||||
readonly dpr: number;
|
||||
}
|
||||
|
||||
export const IMouseService = createDecorator<IMouseService>('MouseService');
|
||||
|
||||
Reference in New Issue
Block a user