mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 21:09:43 +00:00
pull accessibility mode out of editor status (#168103)
This commit is contained in:
parent
8893a14941
commit
aa6168def9
100
src/vs/workbench/browser/parts/editor/accessibilityStatus.ts
Normal file
100
src/vs/workbench/browser/parts/editor/accessibilityStatus.ts
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { INotificationHandle, INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar';
|
||||
import { themeColorFromId } from 'vs/platform/theme/common/themeService';
|
||||
import { STATUS_BAR_PROMINENT_ITEM_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_FOREGROUND } from 'vs/workbench/common/theme';
|
||||
|
||||
export class AccessibilityStatus extends Disposable implements IWorkbenchContribution {
|
||||
private screenReaderNotification: INotificationHandle | null = null;
|
||||
private promptedScreenReader: boolean = false;
|
||||
private readonly screenReaderModeElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IAccessibilityService private readonly _accessibilityService: IAccessibilityService,
|
||||
@IStatusbarService private readonly statusbarService: IStatusbarService
|
||||
) {
|
||||
super();
|
||||
|
||||
this._register(this._accessibilityService.onDidChangeScreenReaderOptimized(() => this.onScreenReaderModeChange()));
|
||||
this._register(configurationService.onDidChangeConfiguration(c => {
|
||||
if (c.affectsConfiguration('editor.accessibilitySupport')) {
|
||||
this.onScreenReaderModeChange();
|
||||
}
|
||||
}));
|
||||
CommandsRegistry.registerCommand({ id: 'showEditorScreenReaderNotification', handler: () => this.showScreenReaderNotification() });
|
||||
}
|
||||
|
||||
private showScreenReaderNotification(): void {
|
||||
if (!this.screenReaderNotification) {
|
||||
this.screenReaderNotification = this.notificationService.prompt(
|
||||
Severity.Info,
|
||||
localize('screenReaderDetectedExplanation.question', "Are you using a screen reader to operate VS Code?"),
|
||||
[{
|
||||
label: localize('screenReaderDetectedExplanation.answerYes', "Yes"),
|
||||
run: () => {
|
||||
this.configurationService.updateValue('editor.accessibilitySupport', 'on', ConfigurationTarget.USER);
|
||||
}
|
||||
}, {
|
||||
label: localize('screenReaderDetectedExplanation.answerNo', "No"),
|
||||
run: () => {
|
||||
this.configurationService.updateValue('editor.accessibilitySupport', 'off', ConfigurationTarget.USER);
|
||||
}
|
||||
}],
|
||||
{ sticky: true }
|
||||
);
|
||||
|
||||
Event.once(this.screenReaderNotification.onDidClose)(() => this.screenReaderNotification = null);
|
||||
}
|
||||
}
|
||||
private updateScreenReaderModeElement(visible: boolean): void {
|
||||
if (visible) {
|
||||
if (!this.screenReaderModeElement.value) {
|
||||
const text = localize('screenReaderDetected', "Screen Reader Optimized");
|
||||
this.screenReaderModeElement.value = this.statusbarService.addEntry({
|
||||
name: localize('status.editor.screenReaderMode', "Screen Reader Mode"),
|
||||
text,
|
||||
ariaLabel: text,
|
||||
command: 'showEditorScreenReaderNotification',
|
||||
backgroundColor: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_BACKGROUND),
|
||||
color: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_FOREGROUND)
|
||||
}, 'status.editor.screenReaderMode', StatusbarAlignment.RIGHT, 100.6);
|
||||
}
|
||||
} else {
|
||||
this.screenReaderModeElement.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private onScreenReaderModeChange(): void {
|
||||
|
||||
// We only support text based editors
|
||||
const screenReaderDetected = this._accessibilityService.isScreenReaderOptimized();
|
||||
if (screenReaderDetected) {
|
||||
const screenReaderConfiguration = this.configurationService.getValue('editor.accessibilitySupport');
|
||||
if (screenReaderConfiguration === 'auto') {
|
||||
if (!this.promptedScreenReader) {
|
||||
this.promptedScreenReader = true;
|
||||
setTimeout(() => this.showScreenReaderNotification(), 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.screenReaderNotification) {
|
||||
this.screenReaderNotification.close();
|
||||
}
|
||||
this.updateScreenReaderModeElement(this._accessibilityService.isScreenReaderOptimized());
|
||||
}
|
||||
}
|
|
@ -67,6 +67,7 @@ import { Codicon } from 'vs/base/common/codicons';
|
|||
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
|
||||
import { UntitledTextEditorInputSerializer, UntitledTextEditorWorkingCopyEditorHandler } from 'vs/workbench/services/untitled/common/untitledTextEditorHandler';
|
||||
import { DynamicEditorConfigurations } from 'vs/workbench/browser/parts/editor/editorConfiguration';
|
||||
import { AccessibilityStatus } from 'vs/workbench/browser/parts/editor/accessibilityStatus';
|
||||
|
||||
//#region Editor Registrations
|
||||
|
||||
|
@ -125,6 +126,7 @@ Registry.as<IEditorFactoryRegistry>(EditorExtensions.EditorFactory).registerEdit
|
|||
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(EditorAutoSave, LifecyclePhase.Ready);
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(EditorStatus, LifecyclePhase.Ready);
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(AccessibilityStatus, LifecyclePhase.Ready);
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(UntitledTextEditorWorkingCopyEditorHandler, LifecyclePhase.Ready);
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DynamicEditorConfigurations, LifecyclePhase.Ready);
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/c
|
|||
import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { EncodingMode, IEncodingSupport, ILanguageSupport, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { SUPPORTED_ENCODINGS } from 'vs/workbench/services/textfile/common/encoding';
|
||||
import { ConfigurationChangedEvent, IEditorOptions, EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration';
|
||||
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { deepClone } from 'vs/base/common/objects';
|
||||
|
@ -43,9 +43,7 @@ import { IPreferencesService } from 'vs/workbench/services/preferences/common/pr
|
|||
import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { getIconClassesForLanguageId } from 'vs/editor/common/services/getIconClasses';
|
||||
import { Promises, timeout } from 'vs/base/common/async';
|
||||
import { INotificationHandle, INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment, IStatusbarEntry } from 'vs/workbench/services/statusbar/browser/statusbar';
|
||||
import { IMarker, IMarkerService, MarkerSeverity, IMarkerData } from 'vs/platform/markers/common/markers';
|
||||
|
@ -148,7 +146,6 @@ class StateChange {
|
|||
EOL: boolean = false;
|
||||
tabFocusMode: boolean = false;
|
||||
columnSelectionMode: boolean = false;
|
||||
screenReaderMode: boolean = false;
|
||||
metadata: boolean = false;
|
||||
|
||||
combine(other: StateChange) {
|
||||
|
@ -160,7 +157,6 @@ class StateChange {
|
|||
this.EOL = this.EOL || other.EOL;
|
||||
this.tabFocusMode = this.tabFocusMode || other.tabFocusMode;
|
||||
this.columnSelectionMode = this.columnSelectionMode || other.columnSelectionMode;
|
||||
this.screenReaderMode = this.screenReaderMode || other.screenReaderMode;
|
||||
this.metadata = this.metadata || other.metadata;
|
||||
}
|
||||
|
||||
|
@ -173,7 +169,6 @@ class StateChange {
|
|||
|| this.EOL
|
||||
|| this.tabFocusMode
|
||||
|| this.columnSelectionMode
|
||||
|| this.screenReaderMode
|
||||
|| this.metadata;
|
||||
}
|
||||
}
|
||||
|
@ -186,7 +181,6 @@ type StateDelta = (
|
|||
| { type: 'indentation'; indentation: string | undefined }
|
||||
| { type: 'tabFocusMode'; tabFocusMode: boolean }
|
||||
| { type: 'columnSelectionMode'; columnSelectionMode: boolean }
|
||||
| { type: 'screenReaderMode'; screenReaderMode: boolean }
|
||||
| { type: 'metadata'; metadata: string | undefined }
|
||||
);
|
||||
|
||||
|
@ -213,9 +207,6 @@ class State {
|
|||
private _columnSelectionMode: boolean | undefined;
|
||||
get columnSelectionMode(): boolean | undefined { return this._columnSelectionMode; }
|
||||
|
||||
private _screenReaderMode: boolean | undefined;
|
||||
get screenReaderMode(): boolean | undefined { return this._screenReaderMode; }
|
||||
|
||||
private _metadata: string | undefined;
|
||||
get metadata(): string | undefined { return this._metadata; }
|
||||
|
||||
|
@ -272,13 +263,6 @@ class State {
|
|||
}
|
||||
break;
|
||||
|
||||
case 'screenReaderMode':
|
||||
if (this._screenReaderMode !== update.screenReaderMode) {
|
||||
this._screenReaderMode = update.screenReaderMode;
|
||||
change.screenReaderMode = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'metadata':
|
||||
if (this._metadata !== update.metadata) {
|
||||
this._metadata = update.metadata;
|
||||
|
@ -302,7 +286,6 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
|||
|
||||
private readonly tabFocusModeElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
|
||||
private readonly columnSelectionModeElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
|
||||
private readonly screenRedearModeElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
|
||||
private readonly indentationElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
|
||||
private readonly selectionElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
|
||||
private readonly encodingElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
|
||||
|
@ -315,17 +298,13 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
|||
private readonly activeEditorListeners = this._register(new DisposableStore());
|
||||
private readonly delayedRender = this._register(new MutableDisposable());
|
||||
private toRender: StateChange | null = null;
|
||||
private screenReaderNotification: INotificationHandle | null = null;
|
||||
private promptedScreenReader: boolean = false;
|
||||
|
||||
|
||||
constructor(
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IQuickInputService private readonly quickInputService: IQuickInputService,
|
||||
@ILanguageService private readonly languageService: ILanguageService,
|
||||
@ITextFileService private readonly textFileService: ITextFileService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IAccessibilityService private readonly accessibilityService: IAccessibilityService,
|
||||
@IStatusbarService private readonly statusbarService: IStatusbarService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService
|
||||
) {
|
||||
|
@ -336,10 +315,6 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
|||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(this.accessibilityService.onDidChangeScreenReaderOptimized(() => {
|
||||
const screenReaderMode = this.accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled;
|
||||
this.updateState({ type: 'screenReaderMode', screenReaderMode });
|
||||
}));
|
||||
this._register(this.editorService.onDidActiveEditorChange(() => this.updateStatusBar()));
|
||||
this._register(this.textFileService.untitled.onDidChangeEncoding(model => this.onResourceEncodingChange(model.resource)));
|
||||
this._register(this.textFileService.files.onDidChangeEncoding(model => this.onResourceEncodingChange((model.resource))));
|
||||
|
@ -347,32 +322,9 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
|||
}
|
||||
|
||||
private registerCommands(): void {
|
||||
CommandsRegistry.registerCommand({ id: 'showEditorScreenReaderNotification', handler: () => this.showScreenReaderNotification() });
|
||||
CommandsRegistry.registerCommand({ id: 'changeEditorIndentation', handler: () => this.showIndentationPicker() });
|
||||
}
|
||||
|
||||
private showScreenReaderNotification(): void {
|
||||
if (!this.screenReaderNotification) {
|
||||
this.screenReaderNotification = this.notificationService.prompt(
|
||||
Severity.Info,
|
||||
localize('screenReaderDetectedExplanation.question', "Are you using a screen reader to operate VS Code?"),
|
||||
[{
|
||||
label: localize('screenReaderDetectedExplanation.answerYes', "Yes"),
|
||||
run: () => {
|
||||
this.configurationService.updateValue('editor.accessibilitySupport', 'on', ConfigurationTarget.USER);
|
||||
}
|
||||
}, {
|
||||
label: localize('screenReaderDetectedExplanation.answerNo', "No"),
|
||||
run: () => {
|
||||
this.configurationService.updateValue('editor.accessibilitySupport', 'off', ConfigurationTarget.USER);
|
||||
}
|
||||
}],
|
||||
{ sticky: true }
|
||||
);
|
||||
|
||||
Event.once(this.screenReaderNotification.onDidClose)(() => this.screenReaderNotification = null);
|
||||
}
|
||||
}
|
||||
|
||||
private async showIndentationPicker(): Promise<unknown> {
|
||||
const activeTextEditorControl = getCodeEditor(this.editorService.activeTextEditorControl);
|
||||
|
@ -449,24 +401,6 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
|||
}
|
||||
}
|
||||
|
||||
private updateScreenReaderModeElement(visible: boolean): void {
|
||||
if (visible) {
|
||||
if (!this.screenRedearModeElement.value) {
|
||||
const text = localize('screenReaderDetected', "Screen Reader Optimized");
|
||||
this.screenRedearModeElement.value = this.statusbarService.addEntry({
|
||||
name: localize('status.editor.screenReaderMode', "Screen Reader Mode"),
|
||||
text,
|
||||
ariaLabel: text,
|
||||
command: 'showEditorScreenReaderNotification',
|
||||
backgroundColor: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_BACKGROUND),
|
||||
color: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_FOREGROUND)
|
||||
}, 'status.editor.screenReaderMode', StatusbarAlignment.RIGHT, 100.6);
|
||||
}
|
||||
} else {
|
||||
this.screenRedearModeElement.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private updateSelectionElement(text: string | undefined): void {
|
||||
if (!text) {
|
||||
this.selectionElement.clear();
|
||||
|
@ -602,7 +536,6 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
|||
private doRenderNow(changed: StateChange): void {
|
||||
this.updateTabFocusModeElement(!!this.state.tabFocusMode);
|
||||
this.updateColumnSelectionModeElement(!!this.state.columnSelectionMode);
|
||||
this.updateScreenReaderModeElement(!!this.state.screenReaderMode);
|
||||
this.updateIndentationElement(this.state.indentation);
|
||||
this.updateSelectionElement(this.state.selectionStatus);
|
||||
this.updateEncodingElement(this.state.encoding);
|
||||
|
@ -642,7 +575,6 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
|||
|
||||
// Update all states
|
||||
this.onColumnSelectionModeChange(activeCodeEditor);
|
||||
this.onScreenReaderModeChange(activeCodeEditor);
|
||||
this.onSelectionChange(activeCodeEditor);
|
||||
this.onLanguageChange(activeCodeEditor, activeInput);
|
||||
this.onEOLChange(activeCodeEditor);
|
||||
|
@ -672,9 +604,6 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
|||
if (event.hasChanged(EditorOption.columnSelection)) {
|
||||
this.onColumnSelectionModeChange(activeCodeEditor);
|
||||
}
|
||||
if (event.hasChanged(EditorOption.accessibilitySupport)) {
|
||||
this.onScreenReaderModeChange(activeCodeEditor);
|
||||
}
|
||||
}));
|
||||
|
||||
// Hook Listener for Selection changes
|
||||
|
@ -796,31 +725,6 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
|||
this.updateState(info);
|
||||
}
|
||||
|
||||
private onScreenReaderModeChange(editorWidget: ICodeEditor | undefined): void {
|
||||
let screenReaderMode = false;
|
||||
|
||||
// We only support text based editors
|
||||
if (editorWidget) {
|
||||
const screenReaderDetected = this.accessibilityService.isScreenReaderOptimized();
|
||||
if (screenReaderDetected) {
|
||||
const screenReaderConfiguration = this.configurationService.getValue<IEditorOptions>('editor')?.accessibilitySupport;
|
||||
if (screenReaderConfiguration === 'auto') {
|
||||
if (!this.promptedScreenReader) {
|
||||
this.promptedScreenReader = true;
|
||||
setTimeout(() => this.showScreenReaderNotification(), 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
screenReaderMode = (editorWidget.getOption(EditorOption.accessibilitySupport) === AccessibilitySupport.Enabled);
|
||||
}
|
||||
|
||||
if (screenReaderMode === false && this.screenReaderNotification) {
|
||||
this.screenReaderNotification.close();
|
||||
}
|
||||
|
||||
this.updateState({ type: 'screenReaderMode', screenReaderMode: screenReaderMode });
|
||||
}
|
||||
|
||||
private onSelectionChange(editorWidget: ICodeEditor | undefined): void {
|
||||
const info: IEditorSelectionStatus = Object.create(null);
|
||||
|
||||
|
|
|
@ -1849,7 +1849,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
|||
|
||||
updateAccessibilitySupport(): void {
|
||||
const isEnabled = this._accessibilityService.isScreenReaderOptimized();
|
||||
if (isEnabled) {
|
||||
if (isEnabled && this.xterm?.raw) {
|
||||
this._navigationModeAddon = new NavigationModeAddon(this._terminalA11yTreeFocusContextKey, this._navigationModeActiveContextKey);
|
||||
this.xterm!.raw.loadAddon(this._navigationModeAddon);
|
||||
} else {
|
||||
|
|
Loading…
Reference in a new issue