add registerBufferElementProvider (#4402)

This commit is contained in:
Megan Rogge
2023-02-07 09:56:23 -08:00
committed by GitHub
parent 6daadc4c55
commit 72a157df5e
7 changed files with 77 additions and 19 deletions
+5 -1
View File
@@ -151,6 +151,10 @@
color: transparent;
}
.xterm .xterm-accessibility-buffer div {
white-space: pre-wrap;
}
.xterm .xterm-accessibility-buffer {
position: absolute;
left: 0;
@@ -165,7 +169,7 @@
overflow-x: hidden;
}
.xterm .xterm-accessibility-buffer:focus {
.xterm .xterm-accessibility-buffer:focus-within {
opacity: 1;
z-index: 20;
}
+36 -15
View File
@@ -4,20 +4,19 @@
*/
import * as Strings from 'browser/LocalizableStrings';
import { ITerminal, IRenderDebouncer, ReadonlyColorSet } from 'browser/Types';
import { isMac } from 'common/Platform';
import { TimeBasedDebouncer } from 'browser/TimeBasedDebouncer';
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,
@@ -26,7 +25,7 @@ export class AccessibleBuffer extends Disposable {
) {
super();
if (!this._terminal.element) {
throw new Error('Cannot enable accessibility before Terminal.open');
throw new Error('Cannot enable accessibility buffer before Terminal.open');
}
this._accessiblityBuffer = document.createElement('div');
@@ -39,10 +38,15 @@ export class AccessibleBuffer extends Disposable {
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',() => 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)));
@@ -51,19 +55,36 @@ export class AccessibleBuffer extends Disposable {
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;
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);
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 {
+8 -1
View File
@@ -21,7 +21,7 @@
* http://linux.die.net/man/7/urxvt
*/
import { ICompositionHelper, ITerminal, IBrowser, CustomKeyEventHandler, IViewport, ILinkifier2, CharacterJoinerHandler, IBufferRange } from 'browser/Types';
import { ICompositionHelper, ITerminal, IBrowser, CustomKeyEventHandler, IViewport, ILinkifier2, CharacterJoinerHandler, IBufferRange, IBufferElementProvider } from 'browser/Types';
import { IRenderer } from 'browser/renderer/shared/Types';
import { CompositionHelper } from 'browser/input/CompositionHelper';
import { Viewport } from 'browser/Viewport';
@@ -193,6 +193,13 @@ export class Terminal extends CoreTerminal implements ITerminal {
}));
}
public registerBufferElementProvider(bufferProvider: IBufferElementProvider): IDisposable {
if (!this._accessibleBuffer) {
throw new Error ('Cannot register buffer element provider when terminal has not been opened yet');
}
return this._accessibleBuffer.registerBufferElementProvider(bufferProvider);
}
/**
* Handle color event from inputhandler for OSC 4|104 | 10|110 | 11|111 | 12|112.
* An event from OSC 4|104 may contain multiple set or report requests, and multiple
+4 -1
View File
@@ -7,7 +7,7 @@ import { IDisposable, IMarker, ILinkProvider, IDecorationOptions, IDecoration }
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';
import { IColorSet, ITerminal, ILinkifier2, IBrowser, IViewport, ICompositionHelper, CharacterJoinerHandler, IBufferRange, ReadonlyColorSet } from 'browser/Types';
import { IColorSet, ITerminal, ILinkifier2, IBrowser, IViewport, ICompositionHelper, CharacterJoinerHandler, IBufferRange, ReadonlyColorSet, IBufferElementProvider } from 'browser/Types';
import { IBuffer, IBufferSet } from 'common/buffer/Types';
import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener, ICharset, ITerminalOptions, ColorIndex } from 'common/Types';
import { Buffer } from 'common/buffer/Buffer';
@@ -143,6 +143,9 @@ export class MockTerminal implements ITerminal {
public getBufferElements(startLine: number, endLine?: number | undefined): { bufferElements: HTMLElement[], cursorElement?: HTMLElement | undefined } {
throw new Error('Method not implemented.');
}
public registerBufferElementProvider(bufferProvider: IBufferElementProvider): IDisposable {
throw new Error('Method not implemented.');
}
public bracketedPasteMode!: boolean;
public renderer!: IRenderer;
public linkifier2!: ILinkifier2;
+5
View File
@@ -80,6 +80,7 @@ export interface IPublicTerminal extends IDisposable {
refresh(start: number, end: number): void;
clearTextureAtlas(): void;
reset(): void;
registerBufferElementProvider(bufferProvider: IBufferElementProvider): IDisposable;
}
export type CustomKeyEventHandler = (event: KeyboardEvent) => boolean;
@@ -220,3 +221,7 @@ export interface IRenderDebouncer extends IDisposable {
export interface IRenderDebouncerWithCallback extends IRenderDebouncer {
addRefreshCallback(callback: FrameRequestCallback): number;
}
export interface IBufferElementProvider {
provideBufferElements(): DocumentFragment;
}
+4 -1
View File
@@ -3,7 +3,7 @@
* @license MIT
*/
import { Terminal as ITerminalApi, IMarker, IDisposable, ILocalizableStrings, ITerminalAddon, IBufferNamespace as IBufferNamespaceApi, IParser, ILinkProvider, IUnicodeHandling, IModes, IDecorationOptions, IDecoration } from 'xterm';
import { Terminal as ITerminalApi, IMarker, IDisposable, ILocalizableStrings, ITerminalAddon, IBufferNamespace as IBufferNamespaceApi, IParser, ILinkProvider, IUnicodeHandling, IModes, IDecorationOptions, IDecoration, IBufferElementProvider } from 'xterm';
import { IBufferRange, ITerminal } from 'browser/Types';
import { Terminal as TerminalCore } from 'browser/Terminal';
import * as Strings from 'browser/LocalizableStrings';
@@ -236,6 +236,9 @@ export class Terminal implements ITerminalApi {
public loadAddon(addon: ITerminalAddon): void {
return this._addonManager.loadAddon(this, addon);
}
public registerBufferElementProvider(bufferProvider: IBufferElementProvider): IDisposable {
return this._core.registerBufferElementProvider(bufferProvider);
}
public static get strings(): ILocalizableStrings {
return Strings;
}
+15
View File
@@ -1108,6 +1108,14 @@ declare module 'xterm' {
* @param addon The addon to load.
*/
loadAddon(addon: ITerminalAddon): void;
/*
* Registers a buffer element provider which will provide a custom element to
* use in the terminal's accessible buffer. Using this an xterm.js embedder
* has full control over how the buffer is represented in the DOM.
* @param bufferElementProvider The buffer element provider.
*/
registerBufferElementProvider(bufferProvider: IBufferElementProvider): IDisposable;
}
/**
@@ -1360,6 +1368,13 @@ declare module 'xterm' {
getNullCell(): IBufferCell;
}
export interface IBufferElementProvider {
/**
* Provides a document fragment containing the buffer elements.
*/
provideBufferElements(): DocumentFragment;
}
/**
* Represents the terminal's set of buffers.
*/