mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-18 22:29:56 +01:00
Fixes #32166: Clarify what happens when VS Code detects a Screen Reader
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
import 'vs/css!./media/editorstatus';
|
||||
import nls = require('vs/nls');
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { $, append, runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
|
||||
import { $, append, runAtThisOrScheduleAtNextAnimationFrame, addDisposableListener } from 'vs/base/browser/dom';
|
||||
import strings = require('vs/base/common/strings');
|
||||
import paths = require('vs/base/common/paths');
|
||||
import types = require('vs/base/common/types');
|
||||
@@ -17,6 +17,7 @@ import errors = require('vs/base/common/errors');
|
||||
import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { language, LANGUAGE_DEFAULT, AccessibilitySupport } from 'vs/base/common/platform';
|
||||
import * as browser from 'vs/base/browser/browser';
|
||||
import { IMode } from 'vs/editor/common/modes';
|
||||
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
|
||||
import { IFileEditorInput, EncodingMode, IEncodingSupport, toResource, SideBySideEditorInput } from 'vs/workbench/common/editor';
|
||||
@@ -45,8 +46,12 @@ import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { getCodeEditor as getEditorWidget, getCodeOrDiffEditor } from 'vs/editor/common/services/codeEditorService';
|
||||
import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
|
||||
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
|
||||
import { IConfigurationChangedEvent, IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { attachStylerCallback } from 'vs/platform/theme/common/styler';
|
||||
import { widgetShadow, editorWidgetBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
|
||||
// TODO@Sandeep layer breaker
|
||||
// tslint:disable-next-line:import-patterns
|
||||
@@ -269,7 +274,8 @@ export class EditorStatus implements IStatusbarItem {
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IUntitledEditorService private untitledEditorService: IUntitledEditorService,
|
||||
@IModeService private modeService: IModeService,
|
||||
@ITextFileService private textFileService: ITextFileService
|
||||
@ITextFileService private textFileService: ITextFileService,
|
||||
@IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService,
|
||||
) {
|
||||
this.toDispose = [];
|
||||
this.activeEditorListeners = [];
|
||||
@@ -288,6 +294,7 @@ export class EditorStatus implements IStatusbarItem {
|
||||
this.screenRedearModeElement = append(this.element, $('a.editor-status-screenreadermode.status-bar-info'));
|
||||
this.screenRedearModeElement.textContent = nlsScreenReaderDetected;
|
||||
this.screenRedearModeElement.title = nlsScreenReaderDetectedTitle;
|
||||
this.screenRedearModeElement.onclick = () => this.onScreenReaderModeClick();
|
||||
hide(this.screenRedearModeElement);
|
||||
|
||||
this.selectionElement = append(this.element, $('a.editor-status-selection'));
|
||||
@@ -469,6 +476,10 @@ export class EditorStatus implements IStatusbarItem {
|
||||
action.dispose();
|
||||
}
|
||||
|
||||
private onScreenReaderModeClick(): void {
|
||||
this.instantiationService.createInstance(ScreenReaderDetectedExplanation, this.screenRedearModeElement);
|
||||
}
|
||||
|
||||
private onSelectionClick(): void {
|
||||
this.quickOpenService.show(':'); // "Go to line"
|
||||
}
|
||||
@@ -607,11 +618,26 @@ export class EditorStatus implements IStatusbarItem {
|
||||
this.updateState(update);
|
||||
}
|
||||
|
||||
private _promptedScreenReader: boolean = false;
|
||||
|
||||
private onScreenReaderModeChange(editorWidget: ICommonCodeEditor): void {
|
||||
let screenReaderMode = false;
|
||||
|
||||
// We only support text based editors
|
||||
if (editorWidget) {
|
||||
const screenReaderDetected = (browser.getAccessibilitySupport() === AccessibilitySupport.Enabled);
|
||||
if (screenReaderDetected) {
|
||||
const screenReaderConfiguration = this.configurationService.getConfiguration<IEditorOptions>('editor').accessibilitySupport;
|
||||
if (screenReaderConfiguration === 'auto') {
|
||||
// show explanation
|
||||
if (!this._promptedScreenReader) {
|
||||
this._promptedScreenReader = true;
|
||||
setTimeout(() => {
|
||||
this.onScreenReaderModeClick();
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
screenReaderMode = (editorWidget.getConfiguration().accessibilitySupport === AccessibilitySupport.Enabled);
|
||||
}
|
||||
@@ -1184,3 +1210,103 @@ export class ChangeEncodingAction extends Action {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ScreenReaderDetectedExplanation {
|
||||
|
||||
private toDispose: IDisposable[];
|
||||
|
||||
constructor(
|
||||
anchorElement: HTMLElement,
|
||||
@IThemeService private readonly themeService: IThemeService,
|
||||
@IContextViewService private readonly contextViewService: IContextViewService,
|
||||
@IConfigurationEditingService private readonly configurationEditingService: IConfigurationEditingService,
|
||||
) {
|
||||
this.toDispose = [];
|
||||
|
||||
this.contextViewService.showContextView({
|
||||
getAnchor: () => anchorElement,
|
||||
|
||||
render: (container) => {
|
||||
return this.renderContents(container);
|
||||
},
|
||||
|
||||
onDOMEvent: (e, activeElement) => {
|
||||
},
|
||||
|
||||
onHide: () => {
|
||||
this.dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
}
|
||||
|
||||
protected renderContents(container: HTMLElement): IDisposable {
|
||||
const domNode = $('div.screen-reader-detected-explanation', {
|
||||
'aria-hidden': 'true'
|
||||
});
|
||||
|
||||
const title = $('h2.title', {}, nls.localize('screenReaderDetectedExplanation.title', "Screen Reader Detected"));
|
||||
domNode.appendChild(title);
|
||||
|
||||
const closeBtn = $('div.cancel');
|
||||
this.toDispose.push(addDisposableListener(closeBtn, 'click', () => {
|
||||
this.contextViewService.hideContextView();
|
||||
}));
|
||||
domNode.appendChild(closeBtn);
|
||||
|
||||
const question = $('p.question', {}, nls.localize('screenReaderDetectedExplanation.question', "Are you using a screen reader to operate VS Code?"));
|
||||
domNode.appendChild(question);
|
||||
|
||||
const yesBtn = $('div.button', {}, nls.localize('screenReaderDetectedExplanation.answerYes', "Yes"));
|
||||
this.toDispose.push(addDisposableListener(yesBtn, 'click', () => {
|
||||
this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, {
|
||||
key: 'editor.accessibilitySupport',
|
||||
value: 'on'
|
||||
});
|
||||
this.contextViewService.hideContextView();
|
||||
}));
|
||||
domNode.appendChild(yesBtn);
|
||||
|
||||
const noBtn = $('div.button', {}, nls.localize('screenReaderDetectedExplanation.answerNo', "No"));
|
||||
this.toDispose.push(addDisposableListener(noBtn, 'click', () => {
|
||||
this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, {
|
||||
key: 'editor.accessibilitySupport',
|
||||
value: 'off'
|
||||
});
|
||||
this.contextViewService.hideContextView();
|
||||
}));
|
||||
domNode.appendChild(noBtn);
|
||||
|
||||
const clear = $('div');
|
||||
clear.style.clear = 'both';
|
||||
domNode.appendChild(clear);
|
||||
|
||||
const br = $('br');
|
||||
domNode.appendChild(br);
|
||||
|
||||
const hr = $('hr');
|
||||
domNode.appendChild(hr);
|
||||
|
||||
const explanation1 = $('p.body1', {}, nls.localize('screenReaderDetectedExplanation.body1', "VS Code is now optimized for usage with a screen reader."));
|
||||
domNode.appendChild(explanation1);
|
||||
|
||||
const explanation2 = $('p.body2', {}, nls.localize('screenReaderDetectedExplanation.body2', "Additionally, due to limitations of WAI-ARIA, we have disabled certain editor features which cannot be currently expressed to screen readers: e.g. word wrapping, folding, auto closing brackets, etc."));
|
||||
domNode.appendChild(explanation2);
|
||||
|
||||
container.appendChild(domNode);
|
||||
|
||||
this.toDispose.push(attachStylerCallback(this.themeService, { widgetShadow, editorWidgetBackground }, colors => {
|
||||
domNode.style.backgroundColor = colors.editorWidgetBackground;
|
||||
if (colors.widgetShadow) {
|
||||
domNode.style.boxShadow = `0 2px 8px ${colors.widgetShadow}`;
|
||||
}
|
||||
}));
|
||||
|
||||
return {
|
||||
dispose: () => { this.dispose(); }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="3 3 16 16" enable-background="new 3 3 16 16"><polygon fill="#C5C5C5" points="12.597,11.042 15.4,13.845 13.844,15.4 11.042,12.598 8.239,15.4 6.683,13.845 9.485,11.042 6.683,8.239 8.238,6.683 11.042,9.486 13.845,6.683 15.4,8.239"/></svg>
|
||||
|
After Width: | Height: | Size: 307 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="3 3 16 16" enable-background="new 3 3 16 16"><polygon fill="#424242" points="12.597,11.042 15.4,13.845 13.844,15.4 11.042,12.598 8.239,15.4 6.683,13.845 9.485,11.042 6.683,8.239 8.238,6.683 11.042,9.486 13.845,6.683 15.4,8.239"/></svg>
|
||||
|
After Width: | Height: | Size: 307 B |
@@ -18,7 +18,78 @@
|
||||
padding: 0 5px 0 5px;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-statusbar-item > .editor-status-metadata,
|
||||
.monaco-workbench > .part.statusbar > .statusbar-item > .editor-statusbar-item > a.editor-status-screenreadermode {
|
||||
.monaco-workbench .editor-statusbar-item > .editor-status-metadata {
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
|
||||
.monaco-shell .screen-reader-detected-explanation {
|
||||
width: 420px;
|
||||
top: 30px;
|
||||
right: 6px;
|
||||
padding: 1em;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.monaco-shell .screen-reader-detected-explanation .cancel {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
margin: .5em 0 0;
|
||||
padding: .5em;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.monaco-shell .screen-reader-detected-explanation h2 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: 400;
|
||||
font-size: 1.8em;
|
||||
}
|
||||
|
||||
.monaco-shell .screen-reader-detected-explanation p {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.monaco-shell .screen-reader-detected-explanation p.question {
|
||||
font-size: 1.4em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.monaco-shell .screen-reader-detected-explanation .button {
|
||||
color: white;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
background-color: #007ACC;
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
border: 4px solid #007ACC;
|
||||
border-radius: 4px;
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.monaco-shell.vs .screen-reader-detected-explanation .cancel {
|
||||
background: url('close-big.svg') center center no-repeat;
|
||||
}
|
||||
.monaco-shell.vs .screen-reader-detected-explanation .cancel:hover {
|
||||
background-color: #eaeaea;
|
||||
}
|
||||
|
||||
.monaco-shell.vs-dark .screen-reader-detected-explanation .cancel,
|
||||
.monaco-shell.hc-black .screen-reader-detected-explanation .cancel {
|
||||
background: url('close-big-dark.svg') center center no-repeat;
|
||||
}
|
||||
.monaco-shell.vs-dark .screen-reader-detected-explanation .cancel:hover {
|
||||
background-color: rgba(30,30,30,0.8);
|
||||
}
|
||||
|
||||
.monaco-shell.hc-black .screen-reader-detected-explanation .cancel {
|
||||
opacity: 0.6;
|
||||
}
|
||||
.monaco-shell.hc-black .screen-reader-detected-explanation .cancel:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user