Listen to window change and pass through to ScreenDprMonitor

This commit is contained in:
Daniel Imms
2023-10-17 08:39:48 -07:00
parent c80453f602
commit 45605db3c5
8 changed files with 64 additions and 18 deletions
+3 -1
View File
@@ -11,6 +11,7 @@ import { ScreenDprMonitor } from 'browser/ScreenDprMonitor';
import { ICoreBrowserService, IRenderService } from 'browser/services/Services';
import { addDisposableDomListener } from 'browser/Lifecycle';
import { IBuffer } from 'common/buffer/Types';
import { IInstantiationService } from 'common/services/Services';
const MAX_ROWS_TO_READ = 20;
@@ -49,6 +50,7 @@ export class AccessibilityManager extends Disposable {
constructor(
private readonly _terminal: ITerminal,
@IInstantiationService instantiationService: IInstantiationService,
@ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService,
@IRenderService private readonly _renderService: IRenderService
) {
@@ -95,7 +97,7 @@ export class AccessibilityManager extends Disposable {
this.register(this._terminal.onBlur(() => this._clearLiveRegion()));
this.register(this._renderService.onDimensionsChange(() => this._refreshRowsDimensions()));
this._screenDprMonitor = new ScreenDprMonitor(window);
this._screenDprMonitor = this.register(instantiationService.createInstance(ScreenDprMonitor));
this.register(this._screenDprMonitor);
this._screenDprMonitor.setListener(() => this._refreshRowsDimensions());
// This shouldn't be needed on modern browsers but is present in case the
+11 -1
View File
@@ -3,6 +3,7 @@
* @license MIT
*/
import { ICoreBrowserService } from 'browser/services/Services';
import { Disposable, toDisposable } from 'common/Lifecycle';
export type ScreenDprListener = (newDevicePixelRatio?: number, oldDevicePixelRatio?: number) => void;
@@ -22,9 +23,18 @@ export class ScreenDprMonitor extends Disposable {
private _outerListener: ((this: MediaQueryList, ev: MediaQueryListEvent) => any) | undefined;
private _listener: ScreenDprListener | undefined;
private _resolutionMediaMatchList: MediaQueryList | undefined;
private _parentWindow: Window;
constructor(private _parentWindow: Window) {
constructor(@ICoreBrowserService coreBrowserService: ICoreBrowserService) {
super();
this._parentWindow = coreBrowserService.window;
this.register(coreBrowserService.onWindowChange(w => {
this._parentWindow = w;
if (this._listener && this._parentWindow.devicePixelRatio !== this._currentDevicePixelRatio) {
this._listener(this._parentWindow.devicePixelRatio, this._currentDevicePixelRatio);
}
this._updateDpr();
}));
this._currentDevicePixelRatio = this._parentWindow.devicePixelRatio;
this.register(toDisposable(() => {
this.clearListener();
+16 -5
View File
@@ -58,9 +58,6 @@ import { IDecoration, IDecorationOptions, IDisposable, ILinkProvider, IMarker }
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,16 @@ 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;
}
@@ -444,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, parent.ownerDocument.defaultView ?? window, this._document ?? window.document);
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)));
+1
View File
@@ -350,6 +350,7 @@ export class MockCompositionHelper implements ICompositionHelper {
}
export class MockCoreBrowserService implements ICoreBrowserService {
public onWindowChange = new EventEmitter<Window & typeof globalThis, void>().event;
public serviceBrand: undefined;
public isFocused: boolean = true;
public get window(): Window & typeof globalThis {
@@ -112,6 +112,7 @@ export class OverviewRulerRenderer extends Disposable {
// overview ruler width changed
this.register(this._optionsService.onSpecificOptionChange('overviewRulerWidth', () => this._queueRefresh(true)));
// device pixel ratio changed
// TODO: Observe DPR instead
this.register(addDisposableDomListener(this._coreBrowserService.window, 'resize', () => this._queueRefresh(true)));
// set the canvas dimensions
this._queueRefresh(true);
+21 -2
View File
@@ -3,23 +3,42 @@
* @license MIT
*/
import { Disposable } from 'common/Lifecycle';
import { ICoreBrowserService } from './Services';
import { EventEmitter } from 'common/EventEmitter';
export class CoreBrowserService implements ICoreBrowserService {
export class CoreBrowserService extends Disposable implements ICoreBrowserService {
public serviceBrand: undefined;
private _isFocused = false;
private _cachedIsFocused: boolean | undefined = undefined;
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,
// TODO: Add getter and setter and event
private _window: Window & typeof globalThis,
public readonly mainDocument: Document
) {
super();
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;
}
+3 -3
View File
@@ -12,7 +12,7 @@ import { ICharSizeService, ICoreBrowserService, IRenderService, IThemeService }
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;
@@ -59,6 +59,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 +67,8 @@ 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 = this.register(instantiationService.createInstance(ScreenDprMonitor));
this._screenDprMonitor.setListener(() => this.handleDevicePixelRatioChange());
this.register(this._screenDprMonitor);
this.register(bufferService.onResize(() => this._fullRefresh()));
this.register(bufferService.buffers.onBufferActivate(() => this._renderer.value?.clear()));
+8 -6
View File
@@ -28,15 +28,17 @@ export interface ICoreBrowserService {
serviceBrand: undefined;
readonly isFocused: boolean;
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 if working with multiple windows. This
* is set by the documentOverride setting.
* 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;
/**