"Fossies" - the Fresh Open Source Software Archive

Member "vscode-1.49.1/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts" (16 Sep 2020, 12899 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!./accessibilityHelp';
    7 import * as dom from 'vs/base/browser/dom';
    8 import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
    9 import { renderFormattedText } from 'vs/base/browser/formattedTextRenderer';
   10 import { alert } from 'vs/base/browser/ui/aria/aria';
   11 import { Widget } from 'vs/base/browser/ui/widget';
   12 import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
   13 import { Disposable } from 'vs/base/common/lifecycle';
   14 import * as platform from 'vs/base/common/platform';
   15 import * as strings from 'vs/base/common/strings';
   16 import { URI } from 'vs/base/common/uri';
   17 import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser';
   18 import { EditorAction, EditorCommand, registerEditorAction, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
   19 import { Selection } from 'vs/editor/common/core/selection';
   20 import { IEditorContribution } from 'vs/editor/common/editorCommon';
   21 import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
   22 import { ToggleTabFocusModeAction } from 'vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode';
   23 import { IStandaloneEditorConstructionOptions } from 'vs/editor/standalone/browser/standaloneCodeEditor';
   24 import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
   25 import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
   26 import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
   27 import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
   28 import { IOpenerService } from 'vs/platform/opener/common/opener';
   29 import { contrastBorder, editorWidgetBackground, widgetShadow, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry';
   30 import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
   31 import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
   32 import { AccessibilityHelpNLS } from 'vs/editor/common/standaloneStrings';
   33 import { EditorOption } from 'vs/editor/common/config/editorOptions';
   34 
   35 const CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE = new RawContextKey<boolean>('accessibilityHelpWidgetVisible', false);
   36 
   37 class AccessibilityHelpController extends Disposable
   38     implements IEditorContribution {
   39     public static readonly ID = 'editor.contrib.accessibilityHelpController';
   40 
   41     public static get(editor: ICodeEditor): AccessibilityHelpController {
   42         return editor.getContribution<AccessibilityHelpController>(
   43             AccessibilityHelpController.ID
   44         );
   45     }
   46 
   47     private readonly _editor: ICodeEditor;
   48     private readonly _widget: AccessibilityHelpWidget;
   49 
   50     constructor(
   51         editor: ICodeEditor,
   52         @IInstantiationService instantiationService: IInstantiationService
   53     ) {
   54         super();
   55 
   56         this._editor = editor;
   57         this._widget = this._register(
   58             instantiationService.createInstance(AccessibilityHelpWidget, this._editor)
   59         );
   60     }
   61 
   62     public show(): void {
   63         this._widget.show();
   64     }
   65 
   66     public hide(): void {
   67         this._widget.hide();
   68     }
   69 }
   70 
   71 
   72 function getSelectionLabel(selections: Selection[] | null, charactersSelected: number): string {
   73     if (!selections || selections.length === 0) {
   74         return AccessibilityHelpNLS.noSelection;
   75     }
   76 
   77     if (selections.length === 1) {
   78         if (charactersSelected) {
   79             return strings.format(AccessibilityHelpNLS.singleSelectionRange, selections[0].positionLineNumber, selections[0].positionColumn, charactersSelected);
   80         }
   81 
   82         return strings.format(AccessibilityHelpNLS.singleSelection, selections[0].positionLineNumber, selections[0].positionColumn);
   83     }
   84 
   85     if (charactersSelected) {
   86         return strings.format(AccessibilityHelpNLS.multiSelectionRange, selections.length, charactersSelected);
   87     }
   88 
   89     if (selections.length > 0) {
   90         return strings.format(AccessibilityHelpNLS.multiSelection, selections.length);
   91     }
   92 
   93     return '';
   94 }
   95 
   96 class AccessibilityHelpWidget extends Widget implements IOverlayWidget {
   97     private static readonly ID = 'editor.contrib.accessibilityHelpWidget';
   98     private static readonly WIDTH = 500;
   99     private static readonly HEIGHT = 300;
  100 
  101     private readonly _editor: ICodeEditor;
  102     private readonly _domNode: FastDomNode<HTMLElement>;
  103     private readonly _contentDomNode: FastDomNode<HTMLElement>;
  104     private _isVisible: boolean;
  105     private readonly _isVisibleKey: IContextKey<boolean>;
  106 
  107     constructor(
  108         editor: ICodeEditor,
  109         @IContextKeyService private readonly _contextKeyService: IContextKeyService,
  110         @IKeybindingService private readonly _keybindingService: IKeybindingService,
  111         @IOpenerService private readonly _openerService: IOpenerService
  112     ) {
  113         super();
  114 
  115         this._editor = editor;
  116         this._isVisibleKey = CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE.bindTo(
  117             this._contextKeyService
  118         );
  119 
  120         this._domNode = createFastDomNode(document.createElement('div'));
  121         this._domNode.setClassName('accessibilityHelpWidget');
  122         this._domNode.setDisplay('none');
  123         this._domNode.setAttribute('role', 'dialog');
  124         this._domNode.setAttribute('aria-hidden', 'true');
  125 
  126         this._contentDomNode = createFastDomNode(document.createElement('div'));
  127         this._contentDomNode.setAttribute('role', 'document');
  128         this._domNode.appendChild(this._contentDomNode);
  129 
  130         this._isVisible = false;
  131 
  132         this._register(this._editor.onDidLayoutChange(() => {
  133             if (this._isVisible) {
  134                 this._layout();
  135             }
  136         }));
  137 
  138         // Intentionally not configurable!
  139         this._register(dom.addStandardDisposableListener(this._contentDomNode.domNode, 'keydown', (e) => {
  140             if (!this._isVisible) {
  141                 return;
  142             }
  143 
  144             if (e.equals(KeyMod.CtrlCmd | KeyCode.KEY_E)) {
  145                 alert(AccessibilityHelpNLS.emergencyConfOn);
  146 
  147                 this._editor.updateOptions({
  148                     accessibilitySupport: 'on'
  149                 });
  150 
  151                 dom.clearNode(this._contentDomNode.domNode);
  152                 this._buildContent();
  153                 this._contentDomNode.domNode.focus();
  154 
  155                 e.preventDefault();
  156                 e.stopPropagation();
  157             }
  158 
  159             if (e.equals(KeyMod.CtrlCmd | KeyCode.KEY_H)) {
  160                 alert(AccessibilityHelpNLS.openingDocs);
  161 
  162                 let url = (<IStandaloneEditorConstructionOptions>this._editor.getRawOptions()).accessibilityHelpUrl;
  163                 if (typeof url === 'undefined') {
  164                     url = 'https://go.microsoft.com/fwlink/?linkid=852450';
  165                 }
  166                 this._openerService.open(URI.parse(url));
  167 
  168                 e.preventDefault();
  169                 e.stopPropagation();
  170             }
  171         }));
  172 
  173         this.onblur(this._contentDomNode.domNode, () => {
  174             this.hide();
  175         });
  176 
  177         this._editor.addOverlayWidget(this);
  178     }
  179 
  180     public dispose(): void {
  181         this._editor.removeOverlayWidget(this);
  182         super.dispose();
  183     }
  184 
  185     public getId(): string {
  186         return AccessibilityHelpWidget.ID;
  187     }
  188 
  189     public getDomNode(): HTMLElement {
  190         return this._domNode.domNode;
  191     }
  192 
  193     public getPosition(): IOverlayWidgetPosition {
  194         return {
  195             preference: null
  196         };
  197     }
  198 
  199     public show(): void {
  200         if (this._isVisible) {
  201             return;
  202         }
  203         this._isVisible = true;
  204         this._isVisibleKey.set(true);
  205         this._layout();
  206         this._domNode.setDisplay('block');
  207         this._domNode.setAttribute('aria-hidden', 'false');
  208         this._contentDomNode.domNode.tabIndex = 0;
  209         this._buildContent();
  210         this._contentDomNode.domNode.focus();
  211     }
  212 
  213     private _descriptionForCommand(commandId: string, msg: string, noKbMsg: string): string {
  214         let kb = this._keybindingService.lookupKeybinding(commandId);
  215         if (kb) {
  216             return strings.format(msg, kb.getAriaLabel());
  217         }
  218         return strings.format(noKbMsg, commandId);
  219     }
  220 
  221     private _buildContent() {
  222         const options = this._editor.getOptions();
  223 
  224         const selections = this._editor.getSelections();
  225         let charactersSelected = 0;
  226 
  227         if (selections) {
  228             const model = this._editor.getModel();
  229             if (model) {
  230                 selections.forEach((selection) => {
  231                     charactersSelected += model.getValueLengthInRange(selection);
  232                 });
  233             }
  234         }
  235 
  236         let text = getSelectionLabel(selections, charactersSelected);
  237 
  238         if (options.get(EditorOption.inDiffEditor)) {
  239             if (options.get(EditorOption.readOnly)) {
  240                 text += AccessibilityHelpNLS.readonlyDiffEditor;
  241             } else {
  242                 text += AccessibilityHelpNLS.editableDiffEditor;
  243             }
  244         } else {
  245             if (options.get(EditorOption.readOnly)) {
  246                 text += AccessibilityHelpNLS.readonlyEditor;
  247             } else {
  248                 text += AccessibilityHelpNLS.editableEditor;
  249             }
  250         }
  251 
  252         const turnOnMessage = (
  253             platform.isMacintosh
  254                 ? AccessibilityHelpNLS.changeConfigToOnMac
  255                 : AccessibilityHelpNLS.changeConfigToOnWinLinux
  256         );
  257         switch (options.get(EditorOption.accessibilitySupport)) {
  258             case AccessibilitySupport.Unknown:
  259                 text += '\n\n - ' + turnOnMessage;
  260                 break;
  261             case AccessibilitySupport.Enabled:
  262                 text += '\n\n - ' + AccessibilityHelpNLS.auto_on;
  263                 break;
  264             case AccessibilitySupport.Disabled:
  265                 text += '\n\n - ' + AccessibilityHelpNLS.auto_off;
  266                 text += ' ' + turnOnMessage;
  267                 break;
  268         }
  269 
  270 
  271         if (options.get(EditorOption.tabFocusMode)) {
  272             text += '\n\n - ' + this._descriptionForCommand(ToggleTabFocusModeAction.ID, AccessibilityHelpNLS.tabFocusModeOnMsg, AccessibilityHelpNLS.tabFocusModeOnMsgNoKb);
  273         } else {
  274             text += '\n\n - ' + this._descriptionForCommand(ToggleTabFocusModeAction.ID, AccessibilityHelpNLS.tabFocusModeOffMsg, AccessibilityHelpNLS.tabFocusModeOffMsgNoKb);
  275         }
  276 
  277         const openDocMessage = (
  278             platform.isMacintosh
  279                 ? AccessibilityHelpNLS.openDocMac
  280                 : AccessibilityHelpNLS.openDocWinLinux
  281         );
  282 
  283         text += '\n\n - ' + openDocMessage;
  284 
  285         text += '\n\n' + AccessibilityHelpNLS.outroMsg;
  286 
  287         this._contentDomNode.domNode.appendChild(renderFormattedText(text));
  288         // Per https://www.w3.org/TR/wai-aria/roles#document, Authors SHOULD provide a title or label for documents
  289         this._contentDomNode.domNode.setAttribute('aria-label', text);
  290     }
  291 
  292     public hide(): void {
  293         if (!this._isVisible) {
  294             return;
  295         }
  296         this._isVisible = false;
  297         this._isVisibleKey.reset();
  298         this._domNode.setDisplay('none');
  299         this._domNode.setAttribute('aria-hidden', 'true');
  300         this._contentDomNode.domNode.tabIndex = -1;
  301         dom.clearNode(this._contentDomNode.domNode);
  302 
  303         this._editor.focus();
  304     }
  305 
  306     private _layout(): void {
  307         let editorLayout = this._editor.getLayoutInfo();
  308 
  309         let w = Math.max(5, Math.min(AccessibilityHelpWidget.WIDTH, editorLayout.width - 40));
  310         let h = Math.max(5, Math.min(AccessibilityHelpWidget.HEIGHT, editorLayout.height - 40));
  311 
  312         this._domNode.setWidth(w);
  313         this._domNode.setHeight(h);
  314 
  315         let top = Math.round((editorLayout.height - h) / 2);
  316         this._domNode.setTop(top);
  317 
  318         let left = Math.round((editorLayout.width - w) / 2);
  319         this._domNode.setLeft(left);
  320     }
  321 }
  322 
  323 class ShowAccessibilityHelpAction extends EditorAction {
  324     constructor() {
  325         super({
  326             id: 'editor.action.showAccessibilityHelp',
  327             label: AccessibilityHelpNLS.showAccessibilityHelpAction,
  328             alias: 'Show Accessibility Help',
  329             precondition: undefined,
  330             kbOpts: {
  331                 kbExpr: EditorContextKeys.focus,
  332                 primary: KeyMod.Alt | KeyCode.F1,
  333                 weight: KeybindingWeight.EditorContrib,
  334                 linux: {
  335                     primary: KeyMod.Alt | KeyMod.Shift | KeyCode.F1,
  336                     secondary: [KeyMod.Alt | KeyCode.F1]
  337                 }
  338             }
  339         });
  340     }
  341 
  342     public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
  343         let controller = AccessibilityHelpController.get(editor);
  344         if (controller) {
  345             controller.show();
  346         }
  347     }
  348 }
  349 
  350 registerEditorContribution(AccessibilityHelpController.ID, AccessibilityHelpController);
  351 registerEditorAction(ShowAccessibilityHelpAction);
  352 
  353 const AccessibilityHelpCommand = EditorCommand.bindToContribution<AccessibilityHelpController>(AccessibilityHelpController.get);
  354 
  355 registerEditorCommand(
  356     new AccessibilityHelpCommand({
  357         id: 'closeAccessibilityHelp',
  358         precondition: CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE,
  359         handler: x => x.hide(),
  360         kbOpts: {
  361             weight: KeybindingWeight.EditorContrib + 100,
  362             kbExpr: EditorContextKeys.focus,
  363             primary: KeyCode.Escape,
  364             secondary: [KeyMod.Shift | KeyCode.Escape]
  365         }
  366     })
  367 );
  368 
  369 registerThemingParticipant((theme, collector) => {
  370     const widgetBackground = theme.getColor(editorWidgetBackground);
  371     if (widgetBackground) {
  372         collector.addRule(`.monaco-editor .accessibilityHelpWidget { background-color: ${widgetBackground}; }`);
  373     }
  374     const widgetForeground = theme.getColor(editorWidgetForeground);
  375     if (widgetForeground) {
  376         collector.addRule(`.monaco-editor .accessibilityHelpWidget { color: ${widgetForeground}; }`);
  377     }
  378 
  379 
  380     const widgetShadowColor = theme.getColor(widgetShadow);
  381     if (widgetShadowColor) {
  382         collector.addRule(`.monaco-editor .accessibilityHelpWidget { box-shadow: 0 2px 8px ${widgetShadowColor}; }`);
  383     }
  384 
  385     const hcBorder = theme.getColor(contrastBorder);
  386     if (hcBorder) {
  387         collector.addRule(`.monaco-editor .accessibilityHelpWidget { border: 2px solid ${hcBorder}; }`);
  388     }
  389 });