Merge remote-tracking branch 'upstream/master' into pr/tisilent/4703
This commit is contained in:
@@ -5,13 +5,11 @@
|
||||
|
||||
import * as Strings from 'browser/LocalizableStrings';
|
||||
import { ITerminal, IRenderDebouncer } from 'browser/Types';
|
||||
import { isMac } from 'common/Platform';
|
||||
import { TimeBasedDebouncer } from 'browser/TimeBasedDebouncer';
|
||||
import { Disposable, toDisposable } from 'common/Lifecycle';
|
||||
import { ScreenDprMonitor } from 'browser/ScreenDprMonitor';
|
||||
import { IRenderService } from 'browser/services/Services';
|
||||
import { addDisposableDomListener } from 'browser/Lifecycle';
|
||||
import { ICoreBrowserService, IRenderService } from 'browser/services/Services';
|
||||
import { IBuffer } from 'common/buffer/Types';
|
||||
import { IInstantiationService } from 'common/services/Services';
|
||||
|
||||
const MAX_ROWS_TO_READ = 20;
|
||||
|
||||
@@ -30,8 +28,6 @@ export class AccessibilityManager extends Disposable {
|
||||
private _liveRegionLineCount: number = 0;
|
||||
private _liveRegionDebouncer: IRenderDebouncer;
|
||||
|
||||
private _screenDprMonitor: ScreenDprMonitor;
|
||||
|
||||
private _topBoundaryFocusListener: (e: FocusEvent) => void;
|
||||
private _bottomBoundaryFocusListener: (e: FocusEvent) => void;
|
||||
|
||||
@@ -50,13 +46,15 @@ export class AccessibilityManager extends Disposable {
|
||||
|
||||
constructor(
|
||||
private readonly _terminal: ITerminal,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService,
|
||||
@IRenderService private readonly _renderService: IRenderService
|
||||
) {
|
||||
super();
|
||||
this._accessibilityContainer = document.createElement('div');
|
||||
this._accessibilityContainer = this._coreBrowserService.mainDocument.createElement('div');
|
||||
this._accessibilityContainer.classList.add('xterm-accessibility');
|
||||
|
||||
this._rowContainer = document.createElement('div');
|
||||
this._rowContainer = this._coreBrowserService.mainDocument.createElement('div');
|
||||
this._rowContainer.setAttribute('role', 'list');
|
||||
this._rowContainer.classList.add('xterm-accessibility-tree');
|
||||
this._rowElements = [];
|
||||
@@ -73,7 +71,7 @@ export class AccessibilityManager extends Disposable {
|
||||
this._refreshRowsDimensions();
|
||||
this._accessibilityContainer.appendChild(this._rowContainer);
|
||||
|
||||
this._liveRegion = document.createElement('div');
|
||||
this._liveRegion = this._coreBrowserService.mainDocument.createElement('div');
|
||||
this._liveRegion.classList.add('live-region');
|
||||
this._liveRegion.setAttribute('aria-live', 'assertive');
|
||||
this._accessibilityContainer.appendChild(this._liveRegion);
|
||||
@@ -94,13 +92,7 @@ export class AccessibilityManager extends Disposable {
|
||||
this.register(this._terminal.onKey(e => this._handleKey(e.key)));
|
||||
this.register(this._terminal.onBlur(() => this._clearLiveRegion()));
|
||||
this.register(this._renderService.onDimensionsChange(() => this._refreshRowsDimensions()));
|
||||
|
||||
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
|
||||
// media query that drives the ScreenDprMonitor isn't supported
|
||||
this.register(addDisposableDomListener(window, 'resize', () => this._refreshRowsDimensions()));
|
||||
this.register(this._coreBrowserService.onDprChange(() => this._refreshRowsDimensions()));
|
||||
|
||||
this._refreshRows();
|
||||
this.register(toDisposable(() => {
|
||||
@@ -133,26 +125,12 @@ export class AccessibilityManager extends Disposable {
|
||||
this._liveRegion.textContent += Strings.tooMuchOutput;
|
||||
}
|
||||
}
|
||||
|
||||
// Only detach/attach on mac as otherwise messages can go unaccounced
|
||||
if (isMac) {
|
||||
if (this._liveRegion.textContent && this._liveRegion.textContent.length > 0 && !this._liveRegion.parentNode) {
|
||||
setTimeout(() => {
|
||||
this._accessibilityContainer.appendChild(this._liveRegion);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _clearLiveRegion(): void {
|
||||
this._liveRegion.textContent = '';
|
||||
this._liveRegionLineCount = 0;
|
||||
|
||||
// Only detach/attach on mac as otherwise messages can go unaccounced
|
||||
if (isMac) {
|
||||
this._liveRegion.remove();
|
||||
}
|
||||
}
|
||||
|
||||
private _handleKey(keyChar: string): void {
|
||||
@@ -276,7 +254,7 @@ export class AccessibilityManager extends Disposable {
|
||||
}
|
||||
|
||||
private _createAccessibilityTreeNode(): HTMLElement {
|
||||
const element = document.createElement('div');
|
||||
const element = this._coreBrowserService.mainDocument.createElement('div');
|
||||
element.setAttribute('role', 'listitem');
|
||||
element.tabIndex = -1;
|
||||
this._refreshRowDimensions(element);
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { Disposable, toDisposable } from 'common/Lifecycle';
|
||||
|
||||
export type ScreenDprListener = (newDevicePixelRatio?: number, oldDevicePixelRatio?: number) => void;
|
||||
|
||||
/**
|
||||
* The screen device pixel ratio monitor allows listening for when the
|
||||
* window.devicePixelRatio value changes. This is done not with polling but with
|
||||
* the use of window.matchMedia to watch media queries. When the event fires,
|
||||
* the listener will be reattached using a different media query to ensure that
|
||||
* any further changes will register.
|
||||
*
|
||||
* The listener should fire on both window zoom changes and switching to a
|
||||
* monitor with a different DPI.
|
||||
*/
|
||||
export class ScreenDprMonitor extends Disposable {
|
||||
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;
|
||||
this.register(toDisposable(() => {
|
||||
this.clearListener();
|
||||
}));
|
||||
}
|
||||
|
||||
public setListener(listener: ScreenDprListener): void {
|
||||
if (this._listener) {
|
||||
this.clearListener();
|
||||
}
|
||||
this._listener = listener;
|
||||
this._outerListener = () => {
|
||||
if (!this._listener) {
|
||||
return;
|
||||
}
|
||||
this._listener(this._parentWindow.devicePixelRatio, this._currentDevicePixelRatio);
|
||||
this._updateDpr();
|
||||
};
|
||||
this._updateDpr();
|
||||
}
|
||||
|
||||
private _updateDpr(): void {
|
||||
if (!this._outerListener) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear listeners for old DPR
|
||||
this._resolutionMediaMatchList?.removeListener(this._outerListener);
|
||||
|
||||
// Add listeners for new DPR
|
||||
this._currentDevicePixelRatio = this._parentWindow.devicePixelRatio;
|
||||
this._resolutionMediaMatchList = this._parentWindow.matchMedia(`screen and (resolution: ${this._parentWindow.devicePixelRatio}dppx)`);
|
||||
this._resolutionMediaMatchList.addListener(this._outerListener);
|
||||
}
|
||||
|
||||
public clearListener(): void {
|
||||
if (!this._resolutionMediaMatchList || !this._listener || !this._outerListener) {
|
||||
return;
|
||||
}
|
||||
this._resolutionMediaMatchList.removeListener(this._outerListener);
|
||||
this._resolutionMediaMatchList = undefined;
|
||||
this._listener = undefined;
|
||||
this._outerListener = undefined;
|
||||
}
|
||||
}
|
||||
+28
-14
@@ -54,13 +54,10 @@ import { evaluateKeyboardEvent } from 'common/input/Keyboard';
|
||||
import { toRgbString } from 'common/input/XParseColor';
|
||||
import { DecorationService } from 'common/services/DecorationService';
|
||||
import { IDecorationService } from 'common/services/Services';
|
||||
import { IDecoration, IDecorationOptions, IDisposable, ILinkProvider, IMarker } from 'xterm';
|
||||
import { IDecoration, IDecorationOptions, IDisposable, ILinkProvider, IMarker } from '@xterm/xterm';
|
||||
import { WindowsOptionsReportType } from '../common/InputHandler';
|
||||
import { AccessibilityManager } from './AccessibilityManager';
|
||||
|
||||
// Let it work inside Node.js for automated testing purposes.
|
||||
const document: Document = (typeof window !== 'undefined') ? window.document : null as any;
|
||||
|
||||
export class Terminal extends CoreTerminal implements ITerminal {
|
||||
public textarea: HTMLTextAreaElement | undefined;
|
||||
public element: HTMLElement | undefined;
|
||||
@@ -397,7 +394,19 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
this._logService.debug('Terminal.open was called on an element that was not attached to the DOM');
|
||||
}
|
||||
|
||||
this._document = parent.ownerDocument!;
|
||||
// If the terminal is already opened
|
||||
if (this.element?.ownerDocument.defaultView && this._coreBrowserService) {
|
||||
// Adjust the window if needed
|
||||
if (this.element.ownerDocument.defaultView !== this._coreBrowserService.window) {
|
||||
this._coreBrowserService.window = this.element.ownerDocument.defaultView;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this._document = parent.ownerDocument;
|
||||
if (this.options.documentOverride && this.options.documentOverride instanceof Document) {
|
||||
this._document = this.optionsService.rawOptions.documentOverride as Document;
|
||||
}
|
||||
|
||||
// Create main element container
|
||||
this.element = this._document.createElement('div');
|
||||
@@ -408,25 +417,25 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
|
||||
// Performance: Use a document fragment to build the terminal
|
||||
// viewport and helper elements detached from the DOM
|
||||
const fragment = document.createDocumentFragment();
|
||||
this._viewportElement = document.createElement('div');
|
||||
const fragment = this._document.createDocumentFragment();
|
||||
this._viewportElement = this._document.createElement('div');
|
||||
this._viewportElement.classList.add('xterm-viewport');
|
||||
fragment.appendChild(this._viewportElement);
|
||||
|
||||
this._viewportScrollArea = document.createElement('div');
|
||||
this._viewportScrollArea = this._document.createElement('div');
|
||||
this._viewportScrollArea.classList.add('xterm-scroll-area');
|
||||
this._viewportElement.appendChild(this._viewportScrollArea);
|
||||
|
||||
this.screenElement = document.createElement('div');
|
||||
this.screenElement = this._document.createElement('div');
|
||||
this.screenElement.classList.add('xterm-screen');
|
||||
// Create the container that will hold helpers like the textarea for
|
||||
// capturing DOM Events. Then produce the helpers.
|
||||
this._helperContainer = document.createElement('div');
|
||||
this._helperContainer = this._document.createElement('div');
|
||||
this._helperContainer.classList.add('xterm-helpers');
|
||||
this.screenElement.appendChild(this._helperContainer);
|
||||
fragment.appendChild(this.screenElement);
|
||||
|
||||
this.textarea = document.createElement('textarea');
|
||||
this.textarea = this._document.createElement('textarea');
|
||||
this.textarea.classList.add('xterm-helper-textarea');
|
||||
this.textarea.setAttribute('aria-label', Strings.promptLabel);
|
||||
if (!Browser.isChromeOS) {
|
||||
@@ -441,7 +450,12 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
|
||||
// Register the core browser service before the generic textarea handlers are registered so it
|
||||
// handles them first. Otherwise the renderers may use the wrong focus state.
|
||||
this._coreBrowserService = this._instantiationService.createInstance(CoreBrowserService, this.textarea, this._document.defaultView ?? window);
|
||||
this._coreBrowserService = this.register(this._instantiationService.createInstance(CoreBrowserService,
|
||||
this.textarea,
|
||||
parent.ownerDocument.defaultView ?? window,
|
||||
// Force unsafe null in node.js environment for tests
|
||||
this._document ?? (typeof window !== 'undefined') ? window.document : null as any
|
||||
));
|
||||
this._instantiationService.setService(ICoreBrowserService, this._coreBrowserService);
|
||||
|
||||
this.register(addDisposableDomListener(this.textarea, 'focus', (ev: KeyboardEvent) => this._handleTextAreaFocus(ev)));
|
||||
@@ -463,7 +477,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
this.register(this._renderService.onRenderedViewportChange(e => this._onRender.fire(e)));
|
||||
this.onResize(e => this._renderService!.resize(e.cols, e.rows));
|
||||
|
||||
this._compositionView = document.createElement('div');
|
||||
this._compositionView = this._document.createElement('div');
|
||||
this._compositionView.classList.add('composition-view');
|
||||
this._compositionHelper = this._instantiationService.createInstance(CompositionHelper, this.textarea, this._compositionView);
|
||||
this._helperContainer.appendChild(this._compositionView);
|
||||
@@ -561,7 +575,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
}
|
||||
|
||||
private _createRenderer(): IRenderer {
|
||||
return this._instantiationService.createInstance(DomRenderer, this.element!, this.screenElement!, this._viewportElement!, this.linkifier2);
|
||||
return this._instantiationService.createInstance(DomRenderer, this, this._document!, this.element!, this.screenElement!, this._viewportElement!, this._helperContainer!, this.linkifier2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,7 @@ import * as os from 'os';
|
||||
import * as fs from 'fs';
|
||||
import * as pty from 'node-pty';
|
||||
import { Terminal } from 'browser/Terminal';
|
||||
import { IDisposable } from 'xterm';
|
||||
import { IDisposable } from '@xterm/xterm';
|
||||
|
||||
// all test files expect terminal in 80x25
|
||||
const COLS = 80;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { IDisposable, IMarker, ILinkProvider, IDecorationOptions, IDecoration } from 'xterm';
|
||||
import { IDisposable, IMarker, ILinkProvider, IDecorationOptions, IDecoration } from '@xterm/xterm';
|
||||
import { IEvent, EventEmitter } from 'common/EventEmitter';
|
||||
import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IMouseService, IRenderService, ISelectionService, IThemeService } from 'browser/services/Services';
|
||||
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types';
|
||||
@@ -350,11 +350,16 @@ export class MockCompositionHelper implements ICompositionHelper {
|
||||
}
|
||||
|
||||
export class MockCoreBrowserService implements ICoreBrowserService {
|
||||
public onDprChange = new EventEmitter<number>().event;
|
||||
public onWindowChange = new EventEmitter<Window & typeof globalThis, void>().event;
|
||||
public serviceBrand: undefined;
|
||||
public isFocused: boolean = true;
|
||||
public get window(): Window & typeof globalThis {
|
||||
throw Error('Window object not available in tests');
|
||||
}
|
||||
public get mainDocument(): Document {
|
||||
throw Error('Document object not available in tests');
|
||||
}
|
||||
public dpr: number = 1;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -6,7 +6,7 @@
|
||||
import { IEvent } from 'common/EventEmitter';
|
||||
import { CharData, IColor, ICoreTerminal, ITerminalOptions } from 'common/Types';
|
||||
import { IBuffer } from 'common/buffer/Types';
|
||||
import { IDisposable, Terminal as ITerminalApi } from 'xterm';
|
||||
import { IDisposable, Terminal as ITerminalApi } from '@xterm/xterm';
|
||||
import { IMouseService, IRenderService } from './services/Services';
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { addDisposableDomListener } from 'browser/Lifecycle';
|
||||
import { IRenderService } from 'browser/services/Services';
|
||||
import { ICoreBrowserService, IRenderService } from 'browser/services/Services';
|
||||
import { Disposable, toDisposable } from 'common/Lifecycle';
|
||||
import { IBufferService, IDecorationService, IInternalDecoration } from 'common/services/Services';
|
||||
|
||||
@@ -19,6 +18,7 @@ export class BufferDecorationRenderer extends Disposable {
|
||||
constructor(
|
||||
private readonly _screenElement: HTMLElement,
|
||||
@IBufferService private readonly _bufferService: IBufferService,
|
||||
@ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService,
|
||||
@IDecorationService private readonly _decorationService: IDecorationService,
|
||||
@IRenderService private readonly _renderService: IRenderService
|
||||
) {
|
||||
@@ -33,7 +33,7 @@ export class BufferDecorationRenderer extends Disposable {
|
||||
this._dimensionsChanged = true;
|
||||
this._queueRefresh();
|
||||
}));
|
||||
this.register(addDisposableDomListener(window, 'resize', () => this._queueRefresh()));
|
||||
this.register(this._coreBrowserService.onDprChange(() => this._queueRefresh()));
|
||||
this.register(this._bufferService.buffers.onBufferActivate(() => {
|
||||
this._altBufferIsActive = this._bufferService.buffer === this._bufferService.buffers.alt;
|
||||
}));
|
||||
@@ -70,7 +70,7 @@ export class BufferDecorationRenderer extends Disposable {
|
||||
}
|
||||
|
||||
private _createElement(decoration: IInternalDecoration): HTMLElement {
|
||||
const element = document.createElement('div');
|
||||
const element = this._coreBrowserService.mainDocument.createElement('div');
|
||||
element.classList.add('xterm-decoration');
|
||||
element.classList.toggle('xterm-decoration-top-layer', decoration?.options?.layer === 'top');
|
||||
element.style.width = `${Math.round((decoration.options.width || 1) * this._renderService.dimensions.css.cell.width)}px`;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ColorZoneStore, IColorZone, IColorZoneStore } from 'browser/decorations/ColorZoneStore';
|
||||
import { addDisposableDomListener } from 'browser/Lifecycle';
|
||||
import { ICoreBrowserService, IRenderService } from 'browser/services/Services';
|
||||
import { Disposable, toDisposable } from 'common/Lifecycle';
|
||||
import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services';
|
||||
@@ -52,10 +51,10 @@ export class OverviewRulerRenderer extends Disposable {
|
||||
@IDecorationService private readonly _decorationService: IDecorationService,
|
||||
@IRenderService private readonly _renderService: IRenderService,
|
||||
@IOptionsService private readonly _optionsService: IOptionsService,
|
||||
@ICoreBrowserService private readonly _coreBrowseService: ICoreBrowserService
|
||||
@ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService
|
||||
) {
|
||||
super();
|
||||
this._canvas = document.createElement('canvas');
|
||||
this._canvas = this._coreBrowserService.mainDocument.createElement('canvas');
|
||||
this._canvas.classList.add('xterm-decoration-overview-ruler');
|
||||
this._refreshCanvasDimensions();
|
||||
this._viewportElement.parentElement?.insertBefore(this._canvas, this._viewportElement);
|
||||
@@ -112,7 +111,7 @@ export class OverviewRulerRenderer extends Disposable {
|
||||
// overview ruler width changed
|
||||
this.register(this._optionsService.onSpecificOptionChange('overviewRulerWidth', () => this._queueRefresh(true)));
|
||||
// device pixel ratio changed
|
||||
this.register(addDisposableDomListener(this._coreBrowseService.window, 'resize', () => this._queueRefresh(true)));
|
||||
this.register(this._coreBrowserService.onDprChange(() => this._queueRefresh(true)));
|
||||
// set the canvas dimensions
|
||||
this._queueRefresh(true);
|
||||
}
|
||||
@@ -135,11 +134,11 @@ export class OverviewRulerRenderer extends Disposable {
|
||||
}
|
||||
|
||||
private _refreshDrawHeightConstants(): void {
|
||||
drawHeight.full = Math.round(2 * this._coreBrowseService.dpr);
|
||||
drawHeight.full = Math.round(2 * this._coreBrowserService.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) * this._coreBrowseService.dpr);
|
||||
const nonFullHeight = Math.round(Math.max(Math.min(pixelsPerLine, 12), 6) * this._coreBrowserService.dpr);
|
||||
drawHeight.left = nonFullHeight;
|
||||
drawHeight.center = nonFullHeight;
|
||||
drawHeight.right = nonFullHeight;
|
||||
@@ -157,9 +156,9 @@ export class OverviewRulerRenderer extends Disposable {
|
||||
|
||||
private _refreshCanvasDimensions(): void {
|
||||
this._canvas.style.width = `${this._width}px`;
|
||||
this._canvas.width = Math.round(this._width * this._coreBrowseService.dpr);
|
||||
this._canvas.width = Math.round(this._width * this._coreBrowserService.dpr);
|
||||
this._canvas.style.height = `${this._screenElement.clientHeight}px`;
|
||||
this._canvas.height = Math.round(this._screenElement.clientHeight * this._coreBrowseService.dpr);
|
||||
this._canvas.height = Math.round(this._screenElement.clientHeight * this._coreBrowserService.dpr);
|
||||
this._refreshDrawConstants();
|
||||
this._refreshColorZonePadding();
|
||||
}
|
||||
@@ -211,7 +210,7 @@ export class OverviewRulerRenderer extends Disposable {
|
||||
if (this._animationFrame !== undefined) {
|
||||
return;
|
||||
}
|
||||
this._animationFrame = this._coreBrowseService.window.requestAnimationFrame(() => {
|
||||
this._animationFrame = this._coreBrowserService.window.requestAnimationFrame(() => {
|
||||
this._refreshDecorations();
|
||||
this._animationFrame = undefined;
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ import { AddonManager } from 'common/public/AddonManager';
|
||||
import { BufferNamespaceApi } from 'common/public/BufferNamespaceApi';
|
||||
import { ParserApi } from 'common/public/ParserApi';
|
||||
import { UnicodeApi } from 'common/public/UnicodeApi';
|
||||
import { IBufferNamespace as IBufferNamespaceApi, IDecoration, IDecorationOptions, IDisposable, ILinkProvider, ILocalizableStrings, IMarker, IModes, IParser, ITerminalAddon, Terminal as ITerminalApi, ITerminalInitOnlyOptions, IUnicodeHandling } from 'xterm';
|
||||
import { IBufferNamespace as IBufferNamespaceApi, IDecoration, IDecorationOptions, IDisposable, ILinkProvider, ILocalizableStrings, IMarker, IModes, IParser, ITerminalAddon, Terminal as ITerminalApi, ITerminalInitOnlyOptions, IUnicodeHandling } from '@xterm/xterm';
|
||||
|
||||
/**
|
||||
* The set of options that only have an effect when set in the Terminal constructor.
|
||||
|
||||
@@ -7,9 +7,10 @@ import { DomRendererRowFactory, RowCss } from 'browser/renderer/dom/DomRendererR
|
||||
import { WidthCache } from 'browser/renderer/dom/WidthCache';
|
||||
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/shared/Constants';
|
||||
import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils';
|
||||
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types';
|
||||
import { createSelectionRenderModel } from 'browser/renderer/shared/SelectionRenderModel';
|
||||
import { IRenderDimensions, IRenderer, IRequestRedrawEvent, ISelectionRenderModel } from 'browser/renderer/shared/Types';
|
||||
import { ICharSizeService, ICoreBrowserService, IThemeService } from 'browser/services/Services';
|
||||
import { ILinkifier2, ILinkifierEvent, ReadonlyColorSet } from 'browser/Types';
|
||||
import { ILinkifier2, ILinkifierEvent, ITerminal, ReadonlyColorSet } from 'browser/Types';
|
||||
import { color } from 'common/Color';
|
||||
import { EventEmitter } from 'common/EventEmitter';
|
||||
import { Disposable, toDisposable } from 'common/Lifecycle';
|
||||
@@ -25,7 +26,6 @@ const SELECTION_CLASS = 'xterm-selection';
|
||||
|
||||
let nextTerminalId = 1;
|
||||
|
||||
|
||||
/**
|
||||
* A fallback renderer for when canvas is slow. This is not meant to be
|
||||
* particularly fast or feature complete, more just stable and usable for when
|
||||
@@ -41,15 +41,19 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
private _rowElements: HTMLElement[] = [];
|
||||
private _selectionContainer: HTMLElement;
|
||||
private _widthCache: WidthCache;
|
||||
private _selectionRenderModel: ISelectionRenderModel = createSelectionRenderModel();
|
||||
|
||||
public dimensions: IRenderDimensions;
|
||||
|
||||
public readonly onRequestRedraw = this.register(new EventEmitter<IRequestRedrawEvent>()).event;
|
||||
|
||||
constructor(
|
||||
private readonly _terminal: ITerminal,
|
||||
private readonly _document: Document,
|
||||
private readonly _element: HTMLElement,
|
||||
private readonly _screenElement: HTMLElement,
|
||||
private readonly _viewportElement: HTMLElement,
|
||||
private readonly _helperContainer: HTMLElement,
|
||||
private readonly _linkifier2: ILinkifier2,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@ICharSizeService private readonly _charSizeService: ICharSizeService,
|
||||
@@ -59,12 +63,12 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
@IThemeService private readonly _themeService: IThemeService
|
||||
) {
|
||||
super();
|
||||
this._rowContainer = document.createElement('div');
|
||||
this._rowContainer = this._document.createElement('div');
|
||||
this._rowContainer.classList.add(ROW_CONTAINER_CLASS);
|
||||
this._rowContainer.style.lineHeight = 'normal';
|
||||
this._rowContainer.setAttribute('aria-hidden', 'true');
|
||||
this._refreshRowElements(this._bufferService.cols, this._bufferService.rows);
|
||||
this._selectionContainer = document.createElement('div');
|
||||
this._selectionContainer = this._document.createElement('div');
|
||||
this._selectionContainer.classList.add(SELECTION_CLASS);
|
||||
this._selectionContainer.setAttribute('aria-hidden', 'true');
|
||||
|
||||
@@ -96,7 +100,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
this._dimensionsStyleElement.remove();
|
||||
}));
|
||||
|
||||
this._widthCache = new WidthCache(document);
|
||||
this._widthCache = new WidthCache(this._document, this._helperContainer);
|
||||
this._widthCache.setFont(
|
||||
this._optionsService.rawOptions.fontFamily,
|
||||
this._optionsService.rawOptions.fontSize,
|
||||
@@ -130,7 +134,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
}
|
||||
|
||||
if (!this._dimensionsStyleElement) {
|
||||
this._dimensionsStyleElement = document.createElement('style');
|
||||
this._dimensionsStyleElement = this._document.createElement('style');
|
||||
this._screenElement.appendChild(this._dimensionsStyleElement);
|
||||
}
|
||||
|
||||
@@ -150,7 +154,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
|
||||
private _injectCss(colors: ReadonlyColorSet): void {
|
||||
if (!this._themeStyleElement) {
|
||||
this._themeStyleElement = document.createElement('style');
|
||||
this._themeStyleElement = this._document.createElement('style');
|
||||
this._screenElement.appendChild(this._themeStyleElement);
|
||||
}
|
||||
|
||||
@@ -276,7 +280,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
private _refreshRowElements(cols: number, rows: number): void {
|
||||
// Add missing elements
|
||||
for (let i = this._rowElements.length; i <= rows; i++) {
|
||||
const row = document.createElement('div');
|
||||
const row = this._document.createElement('div');
|
||||
this._rowContainer.appendChild(row);
|
||||
this._rowElements.push(row);
|
||||
}
|
||||
@@ -289,6 +293,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
public handleResize(cols: number, rows: number): void {
|
||||
this._refreshRowElements(cols, rows);
|
||||
this._updateDimensions();
|
||||
this.handleSelectionChanged(this._selectionRenderModel.selectionStart, this._selectionRenderModel.selectionEnd, this._selectionRenderModel.columnSelectMode);
|
||||
}
|
||||
|
||||
public handleCharSizeChanged(): void {
|
||||
@@ -318,11 +323,13 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
return;
|
||||
}
|
||||
|
||||
this._selectionRenderModel.update(this._terminal, start, end, columnSelectMode);
|
||||
|
||||
// Translate from buffer position to viewport position
|
||||
const viewportStartRow = start[1] - this._bufferService.buffer.ydisp;
|
||||
const viewportEndRow = end[1] - this._bufferService.buffer.ydisp;
|
||||
const viewportCappedStartRow = Math.max(viewportStartRow, 0);
|
||||
const viewportCappedEndRow = Math.min(viewportEndRow, this._bufferService.rows - 1);
|
||||
const viewportStartRow = this._selectionRenderModel.viewportStartRow;
|
||||
const viewportEndRow = this._selectionRenderModel.viewportEndRow;
|
||||
const viewportCappedStartRow = this._selectionRenderModel.viewportCappedStartRow;
|
||||
const viewportCappedEndRow = this._selectionRenderModel.viewportCappedEndRow;
|
||||
|
||||
// No need to draw the selection
|
||||
if (viewportCappedStartRow >= this._bufferService.rows || viewportCappedEndRow < 0) {
|
||||
@@ -330,7 +337,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
}
|
||||
|
||||
// Create the selections
|
||||
const documentFragment = document.createDocumentFragment();
|
||||
const documentFragment = this._document.createDocumentFragment();
|
||||
|
||||
if (columnSelectMode) {
|
||||
const isXFlipped = start[0] > end[0];
|
||||
@@ -362,11 +369,17 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
* @param colEnd The end columns.
|
||||
*/
|
||||
private _createSelectionElement(row: number, colStart: number, colEnd: number, rowCount: number = 1): HTMLElement {
|
||||
const element = document.createElement('div');
|
||||
const element = this._document.createElement('div');
|
||||
const left = colStart * this.dimensions.css.cell.width;
|
||||
let width = this.dimensions.css.cell.width * (colEnd - colStart);
|
||||
if (left + width > this.dimensions.css.canvas.width) {
|
||||
width = this.dimensions.css.canvas.width - left;
|
||||
}
|
||||
|
||||
element.style.height = `${rowCount * this.dimensions.css.cell.height}px`;
|
||||
element.style.top = `${row * this.dimensions.css.cell.height}px`;
|
||||
element.style.left = `${colStart * this.dimensions.css.cell.width}px`;
|
||||
element.style.width = `${this.dimensions.css.cell.width * (colEnd - colStart)}px`;
|
||||
element.style.left = `${left}px`;
|
||||
element.style.width = `${width}px`;
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ import { MockCoreService, MockDecorationService, MockOptionsService } from 'comm
|
||||
import { MockCharacterJoinerService, MockCoreBrowserService, MockThemeService } from 'browser/TestUtils.test';
|
||||
import { TestWidthCache } from 'browser/renderer/dom/WidthCache.test';
|
||||
|
||||
|
||||
const EMPTY_WIDTH = new TestWidthCache(new jsdom.JSDOM('').window.document);
|
||||
const dom = new jsdom.JSDOM('');
|
||||
const EMPTY_WIDTH = new TestWidthCache(dom.window.document, dom.window.document.createElement('div'));
|
||||
|
||||
|
||||
describe('DomRendererRowFactory', () => {
|
||||
@@ -96,6 +96,24 @@ describe('DomRendererRowFactory', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should not display cursor for before initializing', () => {
|
||||
const coreService = new MockCoreService();
|
||||
coreService.isCursorInitialized = false;
|
||||
const rowFactory = new DomRendererRowFactory(
|
||||
dom.window.document,
|
||||
new MockCharacterJoinerService(),
|
||||
new MockOptionsService(),
|
||||
new MockCoreBrowserService(),
|
||||
coreService,
|
||||
new MockDecorationService(),
|
||||
new MockThemeService()
|
||||
);
|
||||
const spans = rowFactory.createRow(lineData, 0, true, 'block', undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
`<span> </span>`
|
||||
);
|
||||
});
|
||||
|
||||
describe('attributes', () => {
|
||||
it('should add class for bold', () => {
|
||||
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
|
||||
@@ -218,7 +218,7 @@ export class DomRendererRowFactory {
|
||||
}
|
||||
}
|
||||
|
||||
if (!this._coreService.isCursorHidden && isCursorCell) {
|
||||
if (!this._coreService.isCursorHidden && isCursorCell && this._coreService.isCursorInitialized) {
|
||||
classes.push(RowCss.CURSOR_CLASS);
|
||||
if (this._coreBrowserService.isFocused) {
|
||||
if (cursorBlink) {
|
||||
|
||||
@@ -36,7 +36,8 @@ function castf32(v: number): number {
|
||||
describe('WidthCache', () => {
|
||||
let wc: TestWidthCache;
|
||||
beforeEach(() => {
|
||||
wc = new TestWidthCache(new jsdom.JSDOM('').window.document);
|
||||
const dom = new jsdom.JSDOM('');
|
||||
wc = new TestWidthCache(dom.window.document, dom.window.document.createElement('div'));
|
||||
wc.setFont('monospace', 15, 'normal', 'bold');
|
||||
});
|
||||
describe('cache invalidation', () => {
|
||||
|
||||
@@ -45,25 +45,28 @@ export class WidthCache implements IDisposable {
|
||||
private _container: HTMLDivElement;
|
||||
private _measureElements: HTMLSpanElement[] = [];
|
||||
|
||||
constructor(_document: Document) {
|
||||
constructor(_document: Document, _helperContainer: HTMLElement) {
|
||||
this._container = _document.createElement('div');
|
||||
this._container.style.position = 'absolute';
|
||||
this._container.style.top = '-50000px';
|
||||
this._container.style.width = '50000px';
|
||||
this._container.classList.add('xterm-width-cache-measure-container');
|
||||
this._container.setAttribute('aria-hidden', 'true');
|
||||
// SP should stack in spans
|
||||
this._container.style.whiteSpace = 'pre';
|
||||
// avoid undercuts in non-monospace fonts from kerning
|
||||
this._container.style.fontKerning = 'none';
|
||||
|
||||
const regular = _document.createElement('span');
|
||||
regular.classList.add('xterm-char-measure-element');
|
||||
|
||||
const bold = _document.createElement('span');
|
||||
bold.classList.add('xterm-char-measure-element');
|
||||
bold.style.fontWeight = 'bold';
|
||||
|
||||
const italic = _document.createElement('span');
|
||||
italic.classList.add('xterm-char-measure-element');
|
||||
italic.style.fontStyle = 'italic';
|
||||
|
||||
const boldItalic = _document.createElement('span');
|
||||
boldItalic.classList.add('xterm-char-measure-element');
|
||||
boldItalic.style.fontWeight = 'bold';
|
||||
boldItalic.style.fontStyle = 'italic';
|
||||
|
||||
@@ -74,7 +77,7 @@ export class WidthCache implements IDisposable {
|
||||
this._container.appendChild(italic);
|
||||
this._container.appendChild(boldItalic);
|
||||
|
||||
_document.body.appendChild(this._container);
|
||||
_helperContainer.appendChild(this._container);
|
||||
|
||||
this.clear();
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ReadonlyColorSet } from 'browser/Types';
|
||||
import { Attributes, BgFlags, ExtFlags, FgFlags, NULL_CELL_CODE, UnderlineStyle } from 'common/buffer/Constants';
|
||||
import { IDecorationService, IOptionsService } from 'common/services/Services';
|
||||
import { ICellData } from 'common/Types';
|
||||
import { Terminal } from 'xterm';
|
||||
import { Terminal } from '@xterm/xterm';
|
||||
|
||||
// Work variables to avoid garbage collection
|
||||
let $fg = 0;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { TextureAtlas } from 'browser/renderer/shared/TextureAtlas';
|
||||
import { ITerminalOptions, Terminal } from 'xterm';
|
||||
import { ITerminalOptions, Terminal } from '@xterm/xterm';
|
||||
import { ITerminal, ReadonlyColorSet } from 'browser/Types';
|
||||
import { ICharAtlasConfig, ITextureAtlas } from 'browser/renderer/shared/Types';
|
||||
import { generateConfig, configEquals } from 'browser/renderer/shared/CharAtlasUtils';
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { ICharAtlasConfig } from './Types';
|
||||
import { Attributes } from 'common/buffer/Constants';
|
||||
import { ITerminalOptions } from 'xterm';
|
||||
import { ITerminalOptions } from '@xterm/xterm';
|
||||
import { IColorSet, ReadonlyColorSet } from 'browser/Types';
|
||||
import { NULL_COLOR } from 'common/Color';
|
||||
|
||||
|
||||
@@ -474,7 +474,7 @@ function drawPatternChar(
|
||||
if (!pattern) {
|
||||
const width = charDefinition[0].length;
|
||||
const height = charDefinition.length;
|
||||
const tmpCanvas = document.createElement('canvas');
|
||||
const tmpCanvas = ctx.canvas.ownerDocument.createElement('canvas');
|
||||
tmpCanvas.width = width;
|
||||
tmpCanvas.height = height;
|
||||
const tmpCtx = throwIfFalsy(tmpCanvas.getContext('2d'));
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { ITerminal } from 'browser/Types';
|
||||
import { ISelectionRenderModel } from 'browser/renderer/shared/Types';
|
||||
import { Terminal } from 'xterm';
|
||||
import { Terminal } from '@xterm/xterm';
|
||||
|
||||
class SelectionRenderModel implements ISelectionRenderModel {
|
||||
public hasSelection!: boolean;
|
||||
@@ -35,7 +36,7 @@ class SelectionRenderModel implements ISelectionRenderModel {
|
||||
this.selectionEnd = undefined;
|
||||
}
|
||||
|
||||
public update(terminal: Terminal, start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean = false): void {
|
||||
public update(terminal: ITerminal, start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean = false): void {
|
||||
this.selectionStart = start;
|
||||
this.selectionEnd = end;
|
||||
// Selection does not exist
|
||||
@@ -45,8 +46,9 @@ class SelectionRenderModel implements ISelectionRenderModel {
|
||||
}
|
||||
|
||||
// Translate from buffer position to viewport position
|
||||
const viewportStartRow = start[1] - terminal.buffer.active.viewportY;
|
||||
const viewportEndRow = end[1] - terminal.buffer.active.viewportY;
|
||||
const viewportY = terminal.buffers.active.ydisp;
|
||||
const viewportStartRow = start[1] - viewportY;
|
||||
const viewportEndRow = end[1] - viewportY;
|
||||
const viewportCappedStartRow = Math.max(viewportStartRow, 0);
|
||||
const viewportCappedEndRow = Math.min(viewportEndRow, terminal.rows - 1);
|
||||
|
||||
|
||||
+3
-3
@@ -3,8 +3,8 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { FontWeight, Terminal } from 'xterm';
|
||||
import { IColorSet } from 'browser/Types';
|
||||
import { FontWeight, Terminal } from '@xterm/xterm';
|
||||
import { IColorSet, ITerminal } from 'browser/Types';
|
||||
import { IDisposable } from 'common/Types';
|
||||
import { IEvent } from 'common/EventEmitter';
|
||||
|
||||
@@ -168,6 +168,6 @@ export interface ISelectionRenderModel {
|
||||
readonly selectionStart: [number, number] | undefined;
|
||||
readonly selectionEnd: [number, number] | undefined;
|
||||
clear(): void;
|
||||
update(terminal: Terminal, start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode?: boolean): void;
|
||||
update(terminal: ITerminal, start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode?: boolean): void;
|
||||
isCellSelected(terminal: Terminal, x: number, y: number): boolean;
|
||||
}
|
||||
|
||||
@@ -3,22 +3,49 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle';
|
||||
import { ICoreBrowserService } from './Services';
|
||||
import { EventEmitter, forwardEvent } from 'common/EventEmitter';
|
||||
import { addDisposableDomListener } from 'browser/Lifecycle';
|
||||
|
||||
export class CoreBrowserService implements ICoreBrowserService {
|
||||
export class CoreBrowserService extends Disposable implements ICoreBrowserService {
|
||||
public serviceBrand: undefined;
|
||||
|
||||
private _isFocused = false;
|
||||
private _cachedIsFocused: boolean | undefined = undefined;
|
||||
private _screenDprMonitor = new ScreenDprMonitor(this._window);
|
||||
|
||||
private readonly _onDprChange = this.register(new EventEmitter<number>());
|
||||
public readonly onDprChange = this._onDprChange.event;
|
||||
private readonly _onWindowChange = this.register(new EventEmitter<Window & typeof globalThis>());
|
||||
public readonly onWindowChange = this._onWindowChange.event;
|
||||
|
||||
constructor(
|
||||
private _textarea: HTMLTextAreaElement,
|
||||
public readonly window: Window & typeof globalThis
|
||||
private _window: Window & typeof globalThis,
|
||||
public readonly mainDocument: Document
|
||||
) {
|
||||
super();
|
||||
|
||||
// Monitor device pixel ratio
|
||||
this.register(this.onWindowChange(w => this._screenDprMonitor.setWindow(w)));
|
||||
this.register(forwardEvent(this._screenDprMonitor.onDprChange, this._onDprChange));
|
||||
|
||||
this._textarea.addEventListener('focus', () => this._isFocused = true);
|
||||
this._textarea.addEventListener('blur', () => this._isFocused = false);
|
||||
}
|
||||
|
||||
public get window(): Window & typeof globalThis {
|
||||
return this._window;
|
||||
}
|
||||
|
||||
public set window(value: Window & typeof globalThis) {
|
||||
if (this._window !== value) {
|
||||
this._window = value;
|
||||
this._onWindowChange.fire(this._window);
|
||||
}
|
||||
}
|
||||
|
||||
public get dpr(): number {
|
||||
return this.window.devicePixelRatio;
|
||||
}
|
||||
@@ -31,3 +58,80 @@ export class CoreBrowserService implements ICoreBrowserService {
|
||||
return this._cachedIsFocused;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The screen device pixel ratio monitor allows listening for when the
|
||||
* window.devicePixelRatio value changes. This is done not with polling but with
|
||||
* the use of window.matchMedia to watch media queries. When the event fires,
|
||||
* the listener will be reattached using a different media query to ensure that
|
||||
* any further changes will register.
|
||||
*
|
||||
* The listener should fire on both window zoom changes and switching to a
|
||||
* monitor with a different DPI.
|
||||
*/
|
||||
class ScreenDprMonitor extends Disposable {
|
||||
private _currentDevicePixelRatio: number;
|
||||
private _outerListener: ((this: MediaQueryList, ev: MediaQueryListEvent) => any) | undefined;
|
||||
private _resolutionMediaMatchList: MediaQueryList | undefined;
|
||||
private _windowResizeListener = this.register(new MutableDisposable());
|
||||
|
||||
private readonly _onDprChange = this.register(new EventEmitter<number>());
|
||||
public readonly onDprChange = this._onDprChange.event;
|
||||
|
||||
constructor(private _parentWindow: Window) {
|
||||
super();
|
||||
|
||||
// Initialize listener and dpr value
|
||||
this._outerListener = () => this._setDprAndFireIfDiffers();
|
||||
this._currentDevicePixelRatio = this._parentWindow.devicePixelRatio;
|
||||
this._updateDpr();
|
||||
|
||||
// Monitor active window resize
|
||||
this._setWindowResizeListener();
|
||||
|
||||
// Setup additional disposables
|
||||
this.register(toDisposable(() => this.clearListener()));
|
||||
}
|
||||
|
||||
|
||||
public setWindow(parentWindow: Window): void {
|
||||
this._parentWindow = parentWindow;
|
||||
this._setWindowResizeListener();
|
||||
this._setDprAndFireIfDiffers();
|
||||
}
|
||||
|
||||
private _setWindowResizeListener(): void {
|
||||
this._windowResizeListener.value = addDisposableDomListener(this._parentWindow, 'resize', () => this._setDprAndFireIfDiffers());
|
||||
}
|
||||
|
||||
private _setDprAndFireIfDiffers(): void {
|
||||
if (this._parentWindow.devicePixelRatio !== this._currentDevicePixelRatio) {
|
||||
this._onDprChange.fire(this._parentWindow.devicePixelRatio);
|
||||
}
|
||||
this._updateDpr();
|
||||
}
|
||||
|
||||
private _updateDpr(): void {
|
||||
if (!this._outerListener) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear listeners for old DPR
|
||||
this._resolutionMediaMatchList?.removeListener(this._outerListener);
|
||||
|
||||
// Add listeners for new DPR
|
||||
this._currentDevicePixelRatio = this._parentWindow.devicePixelRatio;
|
||||
this._resolutionMediaMatchList = this._parentWindow.matchMedia(`screen and (resolution: ${this._parentWindow.devicePixelRatio}dppx)`);
|
||||
this._resolutionMediaMatchList.addListener(this._outerListener);
|
||||
}
|
||||
|
||||
public clearListener(): void {
|
||||
if (!this._resolutionMediaMatchList || !this._outerListener) {
|
||||
return;
|
||||
}
|
||||
this._resolutionMediaMatchList.removeListener(this._outerListener);
|
||||
this._resolutionMediaMatchList = undefined;
|
||||
this._outerListener = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,14 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { addDisposableDomListener } from 'browser/Lifecycle';
|
||||
import { RenderDebouncer } from 'browser/RenderDebouncer';
|
||||
import { ScreenDprMonitor } from 'browser/ScreenDprMonitor';
|
||||
import { IRenderDebouncerWithCallback } from 'browser/Types';
|
||||
import { IRenderDimensions, IRenderer } from 'browser/renderer/shared/Types';
|
||||
import { ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services';
|
||||
import { EventEmitter } from 'common/EventEmitter';
|
||||
import { Disposable, MutableDisposable } from 'common/Lifecycle';
|
||||
import { DebouncedIdleTask } from 'common/TaskQueue';
|
||||
import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services';
|
||||
import { IBufferService, IDecorationService, IInstantiationService, IOptionsService } from 'common/services/Services';
|
||||
|
||||
interface ISelectionState {
|
||||
start: [number, number] | undefined;
|
||||
@@ -25,7 +23,6 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
|
||||
private _renderer: MutableDisposable<IRenderer> = this.register(new MutableDisposable());
|
||||
private _renderDebouncer: IRenderDebouncerWithCallback;
|
||||
private _screenDprMonitor: ScreenDprMonitor;
|
||||
private _pausedResizeTask = new DebouncedIdleTask();
|
||||
|
||||
private _isPaused: boolean = false;
|
||||
@@ -59,6 +56,7 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
@IDecorationService decorationService: IDecorationService,
|
||||
@IBufferService bufferService: IBufferService,
|
||||
@ICoreBrowserService coreBrowserService: ICoreBrowserService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService
|
||||
) {
|
||||
super();
|
||||
@@ -66,9 +64,7 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
this._renderDebouncer = new RenderDebouncer(coreBrowserService.window, (start, end) => this._renderRows(start, end));
|
||||
this.register(this._renderDebouncer);
|
||||
|
||||
this._screenDprMonitor = new ScreenDprMonitor(coreBrowserService.window);
|
||||
this._screenDprMonitor.setListener(() => this.handleDevicePixelRatioChange());
|
||||
this.register(this._screenDprMonitor);
|
||||
this.register(coreBrowserService.onDprChange(() => this.handleDevicePixelRatioChange()));
|
||||
|
||||
this.register(bufferService.onResize(() => this._fullRefresh()));
|
||||
this.register(bufferService.buffers.onBufferActivate(() => this._renderer.value?.clear()));
|
||||
@@ -104,10 +100,6 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
'cursorStyle'
|
||||
], () => this.refreshRows(bufferService.buffer.y, bufferService.buffer.y, true)));
|
||||
|
||||
// dprchange should handle this case, we need this as well for browsers that don't support the
|
||||
// matchMedia query.
|
||||
this.register(addDisposableDomListener(coreBrowserService.window, 'resize', () => this.handleDevicePixelRatioChange()));
|
||||
|
||||
this.register(themeService.onChangeColors(() => this._fullRefresh()));
|
||||
|
||||
// Detect whether IntersectionObserver is detected and enable renderer pause
|
||||
|
||||
@@ -644,7 +644,9 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
if (this._model.selectionEnd[1] < buffer.lines.length) {
|
||||
const line = buffer.lines.get(this._model.selectionEnd[1]);
|
||||
if (line && line.hasWidth(this._model.selectionEnd[0]) === 0) {
|
||||
this._model.selectionEnd[0]++;
|
||||
if (this._model.selectionEnd[0] < this._bufferService.cols) {
|
||||
this._model.selectionEnd[0]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,12 +28,21 @@ export interface ICoreBrowserService {
|
||||
serviceBrand: undefined;
|
||||
|
||||
readonly isFocused: boolean;
|
||||
|
||||
readonly onDprChange: IEvent<number>;
|
||||
readonly onWindowChange: IEvent<Window & typeof globalThis>;
|
||||
|
||||
/**
|
||||
* Parent window that the terminal is rendered into. DOM and rendering APIs
|
||||
* (e.g. requestAnimationFrame) should be invoked in the context of this
|
||||
* window.
|
||||
* Gets or sets the parent window that the terminal is rendered into. DOM and rendering APIs (e.g.
|
||||
* requestAnimationFrame) should be invoked in the context of this window. This should be set when
|
||||
* the window hosting the xterm.js instance changes.
|
||||
*/
|
||||
readonly window: Window & typeof globalThis;
|
||||
window: Window & typeof globalThis;
|
||||
/**
|
||||
* The document of the primary window to be used to create elements when working with multiple
|
||||
* windows. This is defined by the documentOverride setting.
|
||||
*/
|
||||
readonly mainDocument: Document;
|
||||
/**
|
||||
* Helper for getting the devicePixelRatio of the parent window.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user