1490b8695eee2fbc9fe760aa76f9f1b16df995525497911b1715028f5e3472403bc378c4be49805b2634eb13f04ed4debaaca2b46554213848c17d81d6cc5a 14 KB

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