"Fossies" - the Fresh Open Source Software Archive

Member "vscode-1.49.1/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts" (16 Sep 2020, 13684 Bytes) of package /linux/misc/vscode-1.49.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) TypeScript source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*---------------------------------------------------------------------------------------------
    2  *  Copyright (c) Microsoft Corporation. All rights reserved.
    3  *  Licensed under the MIT License. See License.txt in the project root for license information.
    4  *--------------------------------------------------------------------------------------------*/
    5 
    6 import 'vs/css!./accessibility';
    7 import * as nls from 'vs/nls';
    8 import * as dom from 'vs/base/browser/dom';
    9 import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
   10 import { renderFormattedText } from 'vs/base/browser/formattedTextRenderer';
   11 import { alert } from 'vs/base/browser/ui/aria/aria';
   12 import { Widget } from 'vs/base/browser/ui/widget';
   13 import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
   14 import { Disposable } from 'vs/base/common/lifecycle';
   15 import * as platform from 'vs/base/common/platform';
   16 import * as strings from 'vs/base/common/strings';
   17 import { URI } from 'vs/base/common/uri';
   18 import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser';
   19 import { EditorAction, EditorCommand, registerEditorAction, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
   20 import { IEditorOptions, EditorOption } from 'vs/editor/common/config/editorOptions';
   21 import { IEditorContribution } from 'vs/editor/common/editorCommon';
   22 import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
   23 import { ToggleTabFocusModeAction } from 'vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode';
   24 import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
   25 import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
   26 import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
   27 import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
   28 import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
   29 import { IOpenerService } from 'vs/platform/opener/common/opener';
   30 import { contrastBorder, editorWidgetBackground, widgetShadow, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry';
   31 import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
   32 import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
   33 
   34 const CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE = new RawContextKey<boolean>('accessibilityHelpWidgetVisible', false);
   35 
   36 class AccessibilityHelpController extends Disposable implements IEditorContribution {
   37 
   38     public static readonly ID = 'editor.contrib.accessibilityHelpController';
   39 
   40     public static get(editor: ICodeEditor): AccessibilityHelpController {
   41         return editor.getContribution<AccessibilityHelpController>(AccessibilityHelpController.ID);
   42     }
   43 
   44     private _editor: ICodeEditor;
   45     private _widget: AccessibilityHelpWidget;
   46 
   47     constructor(
   48         editor: ICodeEditor,
   49         @IInstantiationService instantiationService: IInstantiationService
   50     ) {
   51         super();
   52 
   53         this._editor = editor;
   54         this._widget = this._register(instantiationService.createInstance(AccessibilityHelpWidget, this._editor));
   55     }
   56 
   57     public show(): void {
   58         this._widget.show();
   59     }
   60 
   61     public hide(): void {
   62         this._widget.hide();
   63     }
   64 }
   65 
   66 class AccessibilityHelpWidget extends Widget implements IOverlayWidget {
   67 
   68     private static readonly ID = 'editor.contrib.accessibilityHelpWidget';
   69     private static readonly WIDTH = 500;
   70     private static readonly HEIGHT = 300;
   71 
   72     private _editor: ICodeEditor;
   73     private _domNode: FastDomNode<HTMLElement>;
   74     private _contentDomNode: FastDomNode<HTMLElement>;
   75     private _isVisible: boolean;
   76     private _isVisibleKey: IContextKey<boolean>;
   77 
   78     constructor(
   79         editor: ICodeEditor,
   80         @IContextKeyService private readonly _contextKeyService: IContextKeyService,
   81         @IKeybindingService private readonly _keybindingService: IKeybindingService,
   82         @IConfigurationService private readonly _configurationService: IConfigurationService,
   83         @IOpenerService private readonly _openerService: IOpenerService
   84     ) {
   85         super();
   86 
   87         this._editor = editor;
   88         this._isVisibleKey = CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE.bindTo(this._contextKeyService);
   89 
   90         this._domNode = createFastDomNode(document.createElement('div'));
   91         this._domNode.setClassName('accessibilityHelpWidget');
   92         this._domNode.setWidth(AccessibilityHelpWidget.WIDTH);
   93         this._domNode.setHeight(AccessibilityHelpWidget.HEIGHT);
   94         this._domNode.setDisplay('none');
   95         this._domNode.setAttribute('role', 'dialog');
   96         this._domNode.setAttribute('aria-hidden', 'true');
   97 
   98         this._contentDomNode = createFastDomNode(document.createElement('div'));
   99         this._contentDomNode.setAttribute('role', 'document');
  100         this._domNode.appendChild(this._contentDomNode);
  101 
  102         this._isVisible = false;
  103 
  104         this._register(this._editor.onDidLayoutChange(() => {
  105             if (this._isVisible) {
  106                 this._layout();
  107             }
  108         }));
  109 
  110         // Intentionally not configurable!
  111         this._register(dom.addStandardDisposableListener(this._contentDomNode.domNode, 'keydown', (e) => {
  112             if (!this._isVisible) {
  113                 return;
  114             }
  115 
  116             if (e.equals(KeyMod.CtrlCmd | KeyCode.KEY_E)) {
  117                 alert(nls.localize('emergencyConfOn', "Now changing the setting `editor.accessibilitySupport` to 'on'."));
  118 
  119                 this._configurationService.updateValue('editor.accessibilitySupport', 'on', ConfigurationTarget.USER);
  120 
  121                 e.preventDefault();
  122                 e.stopPropagation();
  123             }
  124 
  125             if (e.equals(KeyMod.CtrlCmd | KeyCode.KEY_H)) {
  126                 alert(nls.localize('openingDocs', "Now opening the VS Code Accessibility documentation page."));
  127 
  128                 this._openerService.open(URI.parse('https://go.microsoft.com/fwlink/?linkid=851010'));
  129 
  130                 e.preventDefault();
  131                 e.stopPropagation();
  132             }
  133         }));
  134 
  135         this.onblur(this._contentDomNode.domNode, () => {
  136             this.hide();
  137         });
  138 
  139         this._editor.addOverlayWidget(this);
  140     }
  141 
  142     public dispose(): void {
  143         this._editor.removeOverlayWidget(this);
  144         super.dispose();
  145     }
  146 
  147     public getId(): string {
  148         return AccessibilityHelpWidget.ID;
  149     }
  150 
  151     public getDomNode(): HTMLElement {
  152         return this._domNode.domNode;
  153     }
  154 
  155     public getPosition(): IOverlayWidgetPosition {
  156         return {
  157             preference: null
  158         };
  159     }
  160 
  161     public show(): void {
  162         if (this._isVisible) {
  163             return;
  164         }
  165         this._isVisible = true;
  166         this._isVisibleKey.set(true);
  167         this._layout();
  168         this._domNode.setDisplay('block');
  169         this._domNode.setAttribute('aria-hidden', 'false');
  170         this._contentDomNode.domNode.tabIndex = 0;
  171         this._buildContent();
  172         this._contentDomNode.domNode.focus();
  173     }
  174 
  175     private _descriptionForCommand(commandId: string, msg: string, noKbMsg: string): string {
  176         let kb = this._keybindingService.lookupKeybinding(commandId);
  177         if (kb) {
  178             return strings.format(msg, kb.getAriaLabel());
  179         }
  180         return strings.format(noKbMsg, commandId);
  181     }
  182 
  183     private _buildContent() {
  184         const options = this._editor.getOptions();
  185         let text = nls.localize('introMsg', "Thank you for trying out VS Code's accessibility options.");
  186 
  187         text += '\n\n' + nls.localize('status', "Status:");
  188 
  189         const configuredValue = this._configurationService.getValue<IEditorOptions>('editor').accessibilitySupport;
  190         const actualValue = options.get(EditorOption.accessibilitySupport);
  191 
  192         const emergencyTurnOnMessage = (
  193             platform.isMacintosh
  194                 ? nls.localize('changeConfigToOnMac', "To configure the editor to be permanently optimized for usage with a Screen Reader press Command+E now.")
  195                 : nls.localize('changeConfigToOnWinLinux', "To configure the editor to be permanently optimized for usage with a Screen Reader press Control+E now.")
  196         );
  197 
  198         switch (configuredValue) {
  199             case 'auto':
  200                 switch (actualValue) {
  201                     case AccessibilitySupport.Unknown:
  202                         // Should never happen in VS Code
  203                         text += '\n\n - ' + nls.localize('auto_unknown', "The editor is configured to use platform APIs to detect when a Screen Reader is attached, but the current runtime does not support this.");
  204                         break;
  205                     case AccessibilitySupport.Enabled:
  206                         text += '\n\n - ' + nls.localize('auto_on', "The editor has automatically detected a Screen Reader is attached.");
  207                         break;
  208                     case AccessibilitySupport.Disabled:
  209                         text += '\n\n - ' + nls.localize('auto_off', "The editor is configured to automatically detect when a Screen Reader is attached, which is not the case at this time.");
  210                         text += ' ' + emergencyTurnOnMessage;
  211                         break;
  212                 }
  213                 break;
  214             case 'on':
  215                 text += '\n\n - ' + nls.localize('configuredOn', "The editor is configured to be permanently optimized for usage with a Screen Reader - you can change this by editing the setting `editor.accessibilitySupport`.");
  216                 break;
  217             case 'off':
  218                 text += '\n\n - ' + nls.localize('configuredOff', "The editor is configured to never be optimized for usage with a Screen Reader.");
  219                 text += ' ' + emergencyTurnOnMessage;
  220                 break;
  221         }
  222 
  223         const NLS_TAB_FOCUS_MODE_ON = nls.localize('tabFocusModeOnMsg', "Pressing Tab in the current editor will move focus to the next focusable element. Toggle this behavior by pressing {0}.");
  224         const NLS_TAB_FOCUS_MODE_ON_NO_KB = nls.localize('tabFocusModeOnMsgNoKb', "Pressing Tab in the current editor will move focus to the next focusable element. The command {0} is currently not triggerable by a keybinding.");
  225         const NLS_TAB_FOCUS_MODE_OFF = nls.localize('tabFocusModeOffMsg', "Pressing Tab in the current editor will insert the tab character. Toggle this behavior by pressing {0}.");
  226         const NLS_TAB_FOCUS_MODE_OFF_NO_KB = nls.localize('tabFocusModeOffMsgNoKb', "Pressing Tab in the current editor will insert the tab character. The command {0} is currently not triggerable by a keybinding.");
  227 
  228         if (options.get(EditorOption.tabFocusMode)) {
  229             text += '\n\n - ' + this._descriptionForCommand(ToggleTabFocusModeAction.ID, NLS_TAB_FOCUS_MODE_ON, NLS_TAB_FOCUS_MODE_ON_NO_KB);
  230         } else {
  231             text += '\n\n - ' + this._descriptionForCommand(ToggleTabFocusModeAction.ID, NLS_TAB_FOCUS_MODE_OFF, NLS_TAB_FOCUS_MODE_OFF_NO_KB);
  232         }
  233 
  234         const openDocMessage = (
  235             platform.isMacintosh
  236                 ? nls.localize('openDocMac', "Press Command+H now to open a browser window with more VS Code information related to Accessibility.")
  237                 : nls.localize('openDocWinLinux', "Press Control+H now to open a browser window with more VS Code information related to Accessibility.")
  238         );
  239 
  240         text += '\n\n' + openDocMessage;
  241 
  242         text += '\n\n' + nls.localize('outroMsg', "You can dismiss this tooltip and return to the editor by pressing Escape or Shift+Escape.");
  243 
  244         this._contentDomNode.domNode.appendChild(renderFormattedText(text));
  245         // Per https://www.w3.org/TR/wai-aria/roles#document, Authors SHOULD provide a title or label for documents
  246         this._contentDomNode.domNode.setAttribute('aria-label', text);
  247     }
  248 
  249     public hide(): void {
  250         if (!this._isVisible) {
  251             return;
  252         }
  253         this._isVisible = false;
  254         this._isVisibleKey.reset();
  255         this._domNode.setDisplay('none');
  256         this._domNode.setAttribute('aria-hidden', 'true');
  257         this._contentDomNode.domNode.tabIndex = -1;
  258         dom.clearNode(this._contentDomNode.domNode);
  259 
  260         this._editor.focus();
  261     }
  262 
  263     private _layout(): void {
  264         let editorLayout = this._editor.getLayoutInfo();
  265 
  266         const width = Math.min(editorLayout.width - 40, AccessibilityHelpWidget.WIDTH);
  267         const height = Math.min(editorLayout.height - 40, AccessibilityHelpWidget.HEIGHT);
  268 
  269         this._domNode.setTop(Math.round((editorLayout.height - height) / 2));
  270         this._domNode.setLeft(Math.round((editorLayout.width - width) / 2));
  271         this._domNode.setWidth(width);
  272         this._domNode.setHeight(height);
  273     }
  274 }
  275 
  276 class ShowAccessibilityHelpAction extends EditorAction {
  277 
  278     constructor() {
  279         super({
  280             id: 'editor.action.showAccessibilityHelp',
  281             label: nls.localize('ShowAccessibilityHelpAction', "Show Accessibility Help"),
  282             alias: 'Show Accessibility Help',
  283             precondition: undefined,
  284             kbOpts: {
  285                 kbExpr: EditorContextKeys.focus,
  286                 primary: KeyMod.Alt | KeyCode.F1,
  287                 weight: KeybindingWeight.EditorContrib,
  288                 linux: {
  289                     primary: KeyMod.Alt | KeyMod.Shift | KeyCode.F1,
  290                     secondary: [KeyMod.Alt | KeyCode.F1]
  291                 }
  292             }
  293         });
  294     }
  295 
  296     public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
  297         let controller = AccessibilityHelpController.get(editor);
  298         if (controller) {
  299             controller.show();
  300         }
  301     }
  302 }
  303 
  304 registerEditorContribution(AccessibilityHelpController.ID, AccessibilityHelpController);
  305 registerEditorAction(ShowAccessibilityHelpAction);
  306 
  307 const AccessibilityHelpCommand = EditorCommand.bindToContribution<AccessibilityHelpController>(AccessibilityHelpController.get);
  308 
  309 registerEditorCommand(new AccessibilityHelpCommand({
  310     id: 'closeAccessibilityHelp',
  311     precondition: CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE,
  312     handler: x => x.hide(),
  313     kbOpts: {
  314         weight: KeybindingWeight.EditorContrib + 100,
  315         kbExpr: EditorContextKeys.focus,
  316         primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape]
  317     }
  318 }));
  319 
  320 registerThemingParticipant((theme, collector) => {
  321     const widgetBackground = theme.getColor(editorWidgetBackground);
  322     if (widgetBackground) {
  323         collector.addRule(`.monaco-editor .accessibilityHelpWidget { background-color: ${widgetBackground}; }`);
  324     }
  325 
  326     const widgetForeground = theme.getColor(editorWidgetForeground);
  327     if (widgetBackground) {
  328         collector.addRule(`.monaco-editor .accessibilityHelpWidget { color: ${widgetForeground}; }`);
  329     }
  330 
  331     const widgetShadowColor = theme.getColor(widgetShadow);
  332     if (widgetShadowColor) {
  333         collector.addRule(`.monaco-editor .accessibilityHelpWidget { box-shadow: 0 2px 8px ${widgetShadowColor}; }`);
  334     }
  335 
  336     const hcBorder = theme.getColor(contrastBorder);
  337     if (hcBorder) {
  338         collector.addRule(`.monaco-editor .accessibilityHelpWidget { border: 2px solid ${hcBorder}; }`);
  339     }
  340 });