Skip to content

Commit

Permalink
Merge pull request #198153 from microsoft/merogge/write-screen-reader
Browse files Browse the repository at this point in the history
fine tune when native text area is updated
  • Loading branch information
meganrogge authored Nov 21, 2023
2 parents 9cc10ae + f068aa4 commit 1ffd522
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 24 deletions.
14 changes: 8 additions & 6 deletions src/vs/editor/browser/controller/textAreaHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { ColorId, ITokenPresentation } from 'vs/editor/common/encodedTokenAttrib
import { Color } from 'vs/base/common/color';
import { IME } from 'vs/base/common/ime';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';

export interface IVisibleRangeProvider {
visibleRangeForPosition(position: Position): HorizontalPosition | null;
Expand Down Expand Up @@ -145,7 +146,8 @@ export class TextAreaHandler extends ViewPart {
context: ViewContext,
viewController: ViewController,
visibleRangeProvider: IVisibleRangeProvider,
@IKeybindingService private readonly _keybindingService: IKeybindingService
@IKeybindingService private readonly _keybindingService: IKeybindingService,
@IInstantiationService private readonly _instantiationService: IInstantiationService
) {
super(context);

Expand Down Expand Up @@ -303,7 +305,7 @@ export class TextAreaHandler extends ViewPart {
};

const textAreaWrapper = this._register(new TextAreaWrapper(this.textArea.domNode));
this._textAreaInput = this._register(new TextAreaInput(textAreaInputHost, textAreaWrapper, platform.OS, {
this._textAreaInput = this._register(this._instantiationService.createInstance(TextAreaInput, textAreaInputHost, textAreaWrapper, platform.OS, {
isAndroid: browser.isAndroid,
isChrome: browser.isChrome,
isFirefox: browser.isFirefox,
Expand Down Expand Up @@ -478,7 +480,7 @@ export class TextAreaHandler extends ViewPart {
}

public writeScreenReaderContent(reason: string): void {
this._textAreaInput.writeScreenReaderContent(reason);
this._textAreaInput.writeNativeTextAreaContent(reason);
}

public override dispose(): void {
Expand Down Expand Up @@ -629,7 +631,7 @@ export class TextAreaHandler extends ViewPart {
}

if (e.hasChanged(EditorOption.accessibilitySupport)) {
this._textAreaInput.writeScreenReaderContent('strategy changed');
this._textAreaInput.writeNativeTextAreaContent('strategy changed');
}

return true;
Expand All @@ -639,7 +641,7 @@ export class TextAreaHandler extends ViewPart {
this._modelSelections = e.modelSelections.slice(0);
// We must update the <textarea> synchronously, otherwise long press IME on macos breaks.
// See https://github.com/microsoft/vscode/issues/165821
this._textAreaInput.writeScreenReaderContent('selection changed');
this._textAreaInput.writeNativeTextAreaContent('selection changed');
return true;
}
public override onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean {
Expand Down Expand Up @@ -726,7 +728,7 @@ export class TextAreaHandler extends ViewPart {
}

public render(ctx: RestrictedRenderingContext): void {
this._textAreaInput.writeScreenReaderContent('render');
this._textAreaInput.writeNativeTextAreaContent('render');
this._render();
}

Expand Down
45 changes: 30 additions & 15 deletions src/vs/editor/browser/controller/textAreaInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ import { inputLatency } from 'vs/base/browser/performance';
import { RunOnceScheduler } from 'vs/base/common/async';
import { Emitter, Event } from 'vs/base/common/event';
import { KeyCode } from 'vs/base/common/keyCodes';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { Disposable, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { Mimes } from 'vs/base/common/mime';
import { OperatingSystem } from 'vs/base/common/platform';
import * as strings from 'vs/base/common/strings';
import { ITextAreaWrapper, ITypeData, TextAreaState, _debugComposition } from 'vs/editor/browser/controller/textAreaState';
import { Position } from 'vs/editor/common/core/position';
import { Selection } from 'vs/editor/common/core/selection';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
import { ILogService } from 'vs/platform/log/common/log';

export namespace TextAreaSyntethicEvents {
export const Tap = '-monaco-textarea-synthetic-tap';
Expand Down Expand Up @@ -193,7 +195,8 @@ export class TextAreaInput extends Disposable {
// ---

private readonly _asyncTriggerCut: RunOnceScheduler;
private readonly _asyncFocusGainWriteScreenReaderContent: RunOnceScheduler;

private _asyncFocusGainWriteScreenReaderContent: MutableDisposable<RunOnceScheduler> = this._register(new MutableDisposable());

private _textAreaState: TextAreaState;

Expand All @@ -210,16 +213,24 @@ export class TextAreaInput extends Disposable {
private readonly _host: ITextAreaInputHost,
private readonly _textArea: ICompleteTextAreaWrapper,
private readonly _OS: OperatingSystem,
private readonly _browser: IBrowser
private readonly _browser: IBrowser,
@IAccessibilityService private readonly _accessibilityService: IAccessibilityService,
@ILogService private readonly _logService: ILogService
) {
super();
this._asyncTriggerCut = this._register(new RunOnceScheduler(() => this._onCut.fire(), 0));
this._asyncFocusGainWriteScreenReaderContent = this._register(new RunOnceScheduler(() => this.writeScreenReaderContent('asyncFocusGain'), 0));

this._textAreaState = TextAreaState.EMPTY;
this._selectionChangeListener = null;
this.writeScreenReaderContent('ctor');

if (this._accessibilityService.isScreenReaderOptimized()) {
this.writeNativeTextAreaContent('ctor');
}
this._register(Event.runAndSubscribe(this._accessibilityService.onDidChangeScreenReaderOptimized, () => {
if (this._accessibilityService.isScreenReaderOptimized() && !this._asyncFocusGainWriteScreenReaderContent.value) {
this._asyncFocusGainWriteScreenReaderContent.value = this._register(new RunOnceScheduler(() => this.writeNativeTextAreaContent('asyncFocusGain'), 0));
} else {
this._asyncFocusGainWriteScreenReaderContent.clear();
}
}));
this._hasFocus = false;
this._currentComposition = null;

Expand Down Expand Up @@ -431,10 +442,13 @@ export class TextAreaInput extends Disposable {

this._setHasFocus(true);

if (this._browser.isSafari && !hadFocus && this._hasFocus) {
if (this._accessibilityService.isScreenReaderOptimized() && this._browser.isSafari && !hadFocus && this._hasFocus) {
// When "tabbing into" the textarea, immediately after dispatching the 'focus' event,
// Safari will always move the selection at offset 0 in the textarea
this._asyncFocusGainWriteScreenReaderContent.schedule();
if (!this._asyncFocusGainWriteScreenReaderContent.value) {
this._asyncFocusGainWriteScreenReaderContent.value = new RunOnceScheduler(() => this.writeNativeTextAreaContent('asyncFocusGain'), 0);
}
this._asyncFocusGainWriteScreenReaderContent.value.schedule();
}
}));
this._register(this._textArea.onBlur(() => {
Expand All @@ -447,7 +461,7 @@ export class TextAreaInput extends Disposable {
this._currentComposition = null;

// Clear the textarea to avoid an unwanted cursor type
this.writeScreenReaderContent('blurWithoutCompositionEnd');
this.writeNativeTextAreaContent('blurWithoutCompositionEnd');

// Fire artificial composition end
this._onCompositionEnd.fire();
Expand All @@ -463,7 +477,7 @@ export class TextAreaInput extends Disposable {
this._currentComposition = null;

// Clear the textarea to avoid an unwanted cursor type
this.writeScreenReaderContent('tapWithoutCompositionEnd');
this.writeNativeTextAreaContent('tapWithoutCompositionEnd');

// Fire artificial composition end
this._onCompositionEnd.fire();
Expand Down Expand Up @@ -602,7 +616,7 @@ export class TextAreaInput extends Disposable {
}

if (this._hasFocus) {
this.writeScreenReaderContent('focusgain');
this.writeNativeTextAreaContent('focusgain');
}

if (this._hasFocus) {
Expand All @@ -621,12 +635,13 @@ export class TextAreaInput extends Disposable {
this._textAreaState = textAreaState;
}

public writeScreenReaderContent(reason: string): void {
if (this._currentComposition) {
public writeNativeTextAreaContent(reason: string): void {
if ((!this._accessibilityService.isScreenReaderOptimized() && reason === 'render') || this._currentComposition) {
// Do not write to the text on render unless a screen reader is being used #192278
// Do not write to the text area when doing composition
return;
}

this._logService.trace(`writeTextAreaState(reason: ${reason})`);
this._setAndWriteTextAreaState(reason, this._host.getScreenReaderContent());
}

Expand Down
6 changes: 4 additions & 2 deletions src/vs/editor/test/browser/controller/imeTester.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import * as dom from 'vs/base/browser/dom';
import * as browser from 'vs/base/browser/browser';
import * as platform from 'vs/base/common/platform';
import { mainWindow } from 'vs/base/browser/window';
import { TestAccessibilityService } from 'vs/platform/accessibility/test/common/testAccessibilityService';
import { NullLogService } from 'vs/platform/log/common/log';

// To run this test, open imeTester.html

Expand Down Expand Up @@ -127,7 +129,7 @@ function doCreateTest(description: string, inputStr: string, expectedStr: string
isFirefox: browser.isFirefox,
isChrome: browser.isChrome,
isSafari: browser.isSafari,
});
}, new TestAccessibilityService(), new NullLogService());

const output = document.createElement('pre');
output.className = 'output';
Expand All @@ -146,7 +148,7 @@ function doCreateTest(description: string, inputStr: string, expectedStr: string
const updatePosition = (off: number, len: number) => {
cursorOffset = off;
cursorLength = len;
handler.writeScreenReaderContent('selection changed');
handler.writeNativeTextAreaContent('selection changed');
handler.focusTextArea();
};

Expand Down
4 changes: 3 additions & 1 deletion src/vs/editor/test/browser/controller/textAreaInput.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { ClipboardDataToCopy, IBrowser, ICompleteTextAreaWrapper, ITextAreaInput
import { TextAreaState } from 'vs/editor/browser/controller/textAreaState';
import { Position } from 'vs/editor/common/core/position';
import { IRecorded, IRecordedEvent, IRecordedTextareaState } from 'vs/editor/test/browser/controller/imeRecordedTypes';
import { TestAccessibilityService } from 'vs/platform/accessibility/test/common/testAccessibilityService';
import { NullLogService } from 'vs/platform/log/common/log';

suite('TextAreaInput', () => {

Expand Down Expand Up @@ -203,7 +205,7 @@ suite('TextAreaInput', () => {

public hasFocus(): boolean { return true; }
});
const input = disposables.add(new TextAreaInput(host, wrapper, recorded.env.OS, recorded.env.browser));
const input = disposables.add(new TextAreaInput(host, wrapper, recorded.env.OS, recorded.env.browser, new TestAccessibilityService(), new NullLogService()));

wrapper._initialize(recorded.initial);
input._initializeFromTest();
Expand Down

0 comments on commit 1ffd522

Please sign in to comment.