102 lines
4.3 KiB
TypeScript
102 lines
4.3 KiB
TypeScript
/**
|
|
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
|
|
* @license MIT
|
|
*/
|
|
|
|
import * as Strings from 'browser/LocalizableStrings';
|
|
import { IBufferElementProvider, ITerminal, ReadonlyColorSet } from 'browser/Types';
|
|
import { addDisposableDomListener } from 'browser/Lifecycle';
|
|
import { Disposable, toDisposable } from 'common/Lifecycle';
|
|
import { IRenderService, IThemeService } from 'browser/services/Services';
|
|
import { IOptionsService } from 'common/services/Services';
|
|
import { ITerminalOptions } from 'xterm';
|
|
import { IDisposable } from 'common/Types';
|
|
|
|
export class AccessibleBuffer extends Disposable {
|
|
private _accessiblityBuffer: HTMLElement;
|
|
private _isAccessibilityBufferActive: boolean = false;
|
|
public get isAccessibilityBufferActive(): boolean { return this._isAccessibilityBufferActive; }
|
|
private _provider: IBufferElementProvider | undefined;
|
|
constructor(
|
|
private readonly _terminal: ITerminal,
|
|
@IOptionsService optionsService: IOptionsService,
|
|
@IRenderService private readonly _renderService: IRenderService,
|
|
@IThemeService themeService: IThemeService
|
|
) {
|
|
super();
|
|
if (!this._terminal.element) {
|
|
throw new Error('Cannot enable accessibility buffer before Terminal.open');
|
|
}
|
|
|
|
this._accessiblityBuffer = document.createElement('div');
|
|
this._accessiblityBuffer.setAttribute('role', 'document');
|
|
this._accessiblityBuffer.ariaRoleDescription = Strings.accessibilityBuffer;
|
|
this._accessiblityBuffer.tabIndex = 0;
|
|
this._accessiblityBuffer.classList.add('xterm-accessibility-buffer');
|
|
this._terminal.element.insertAdjacentElement('afterbegin', this._accessiblityBuffer);
|
|
|
|
this.register(addDisposableDomListener(this._accessiblityBuffer, 'keydown', (ev: KeyboardEvent) => {
|
|
if (ev.key === 'Tab') {
|
|
this._isAccessibilityBufferActive = false;
|
|
}
|
|
}
|
|
));
|
|
this.register(addDisposableDomListener(this._accessiblityBuffer, 'focus', () => this._refreshAccessibilityBuffer()));
|
|
this.register(addDisposableDomListener(this._accessiblityBuffer, 'focusout', (e) => {
|
|
if (!this._accessiblityBuffer.contains(e.element)) {
|
|
this._isAccessibilityBufferActive = false;
|
|
}
|
|
}));
|
|
|
|
this._handleColorChange(themeService.colors);
|
|
this.register(themeService.onChangeColors(e => this._handleColorChange(e)));
|
|
this._handleFontOptionChange(optionsService.options);
|
|
this.register(optionsService.onMultipleOptionChange(['fontSize', 'fontFamily', 'letterSpacing', 'lineHeight'], () => this._handleFontOptionChange(optionsService.options)));
|
|
this.register(toDisposable(() => this._accessiblityBuffer.remove()));
|
|
}
|
|
|
|
public registerBufferElementProvider(bufferProvider: IBufferElementProvider): IDisposable {
|
|
if (this._provider) {
|
|
throw new Error('Buffer element provider already registered');
|
|
}
|
|
this._provider = bufferProvider;
|
|
return {
|
|
dispose: () => {
|
|
this._provider = undefined;
|
|
}
|
|
};
|
|
}
|
|
|
|
private _refreshAccessibilityBuffer(): void {
|
|
if (!this._terminal.viewport) {
|
|
return;
|
|
}
|
|
this._isAccessibilityBufferActive = true;
|
|
this._accessiblityBuffer.scrollTop = this._accessiblityBuffer.scrollHeight;
|
|
const bufferElements = this._provider?.provideBufferElements();
|
|
if (!bufferElements) {
|
|
const { bufferElements } = this._terminal.viewport.getBufferElements(0);
|
|
for (const element of bufferElements) {
|
|
if (element.textContent) {
|
|
element.textContent = element.textContent.replace(new RegExp(' ', 'g'), '\xA0');
|
|
}
|
|
}
|
|
this._accessiblityBuffer.replaceChildren(...bufferElements);
|
|
} else {
|
|
this._accessiblityBuffer.replaceChildren(bufferElements);
|
|
}
|
|
}
|
|
|
|
private _handleColorChange(colorSet: ReadonlyColorSet): void {
|
|
this._accessiblityBuffer.style.backgroundColor = colorSet.background.css;
|
|
this._accessiblityBuffer.style.color = colorSet.foreground.css;
|
|
}
|
|
|
|
private _handleFontOptionChange(options: Required<ITerminalOptions>): void {
|
|
this._accessiblityBuffer.style.fontFamily = options.fontFamily;
|
|
this._accessiblityBuffer.style.fontSize = `${options.fontSize}px`;
|
|
this._accessiblityBuffer.style.lineHeight = `${options.lineHeight * (this._renderService.dimensions.css.cell.height)}px`;
|
|
this._accessiblityBuffer.style.letterSpacing = `${options.letterSpacing}px`;
|
|
}
|
|
}
|