mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-12 17:48:19 +01:00
Merge pull request #198153 from microsoft/merogge/write-screen-reader
fine tune when native text area is updated
This commit is contained in:
@@ -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;
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
@@ -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 {
|
||||
@@ -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;
|
||||
@@ -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 {
|
||||
@@ -726,7 +728,7 @@ export class TextAreaHandler extends ViewPart {
|
||||
}
|
||||
|
||||
public render(ctx: RestrictedRenderingContext): void {
|
||||
this._textAreaInput.writeScreenReaderContent('render');
|
||||
this._textAreaInput.writeNativeTextAreaContent('render');
|
||||
this._render();
|
||||
}
|
||||
|
||||
|
||||
@@ -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';
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(() => {
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
@@ -602,7 +616,7 @@ export class TextAreaInput extends Disposable {
|
||||
}
|
||||
|
||||
if (this._hasFocus) {
|
||||
this.writeScreenReaderContent('focusgain');
|
||||
this.writeNativeTextAreaContent('focusgain');
|
||||
}
|
||||
|
||||
if (this._hasFocus) {
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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';
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user