Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add registerBufferElementProvider #4402

Merged
merged 8 commits into from
Feb 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion css/xterm.css
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@
color: transparent;
}

.xterm .xterm-accessibility-buffer div {
white-space: pre-wrap;
}

.xterm .xterm-accessibility-buffer {
position: absolute;
left: 0;
Expand All @@ -165,7 +169,7 @@
overflow-x: hidden;
}

.xterm .xterm-accessibility-buffer:focus {
.xterm .xterm-accessibility-buffer:focus-within {
opacity: 1;
z-index: 20;
}
Expand Down
49 changes: 35 additions & 14 deletions src/browser/AccessibleBuffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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');
Expand All @@ -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)));
Expand All @@ -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.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);
}
this._accessiblityBuffer.replaceChildren(...bufferElements);
this._accessiblityBuffer.scrollTop = this._accessiblityBuffer.scrollHeight;
}

private _handleColorChange(colorSet: ReadonlyColorSet): void {
Expand Down
9 changes: 8 additions & 1 deletion src/browser/Terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion src/browser/TestUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions src/browser/Types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -220,3 +221,7 @@ export interface IRenderDebouncer extends IDisposable {
export interface IRenderDebouncerWithCallback extends IRenderDebouncer {
addRefreshCallback(callback: FrameRequestCallback): number;
}

export interface IBufferElementProvider {
provideBufferElements(): DocumentFragment;
}
5 changes: 4 additions & 1 deletion src/browser/public/Terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
}
Expand Down
15 changes: 15 additions & 0 deletions typings/xterm.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -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.
*/
Expand Down