From 9b1b19a3fcd351674d83a6ec98fd0565512f7e32 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Mon, 26 Jun 2023 14:58:18 -0700 Subject: [PATCH 1/4] notebook accessibility help --- .../notebook/browser/notebook.contribution.ts | 18 ++++++++ .../browser/notebookAccessibilityHelp.ts | 43 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 src/vs/workbench/contrib/notebook/browser/notebookAccessibilityHelp.ts diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index ed15677163a..1766a450421 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -112,6 +112,9 @@ import { NotebookKernelHistoryService } from 'vs/workbench/contrib/notebook/brow import { INotebookLoggingService } from 'vs/workbench/contrib/notebook/common/notebookLoggingService'; import { NotebookLoggingService } from 'vs/workbench/contrib/notebook/browser/services/notebookLoggingServiceImpl'; import product from 'vs/platform/product/common/product'; +import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibilityContribution'; +import { NOTEBOOK_IS_ACTIVE_EDITOR } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; +import { runAccessibilityHelpAction } from 'vs/workbench/contrib/notebook/browser/notebookAccessibilityHelp'; /*--------------------------------------------------------------------------------------------- */ @@ -672,6 +675,20 @@ class NotebookLanguageSelectorScoreRefine { } } +class NotebookAccessibilityHelpContribution extends Disposable { + static ID: 'chatAccessibilityHelpContribution'; + constructor() { + super(); + this._register(AccessibilityHelpAction.addImplementation(105, 'notebook', async accessor => { + const codeEditor = accessor.get(ICodeEditorService).getActiveCodeEditor() || accessor.get(ICodeEditorService).getFocusedCodeEditor(); + if (!codeEditor) { + return; + } + runAccessibilityHelpAction(accessor, codeEditor); + }, NOTEBOOK_IS_ACTIVE_EDITOR)); + } +} + const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchContributionsRegistry.registerWorkbenchContribution(NotebookContribution, LifecyclePhase.Starting); workbenchContributionsRegistry.registerWorkbenchContribution(CellContentProvider, LifecyclePhase.Starting); @@ -680,6 +697,7 @@ workbenchContributionsRegistry.registerWorkbenchContribution(RegisterSchemasCont workbenchContributionsRegistry.registerWorkbenchContribution(NotebookEditorManager, LifecyclePhase.Ready); workbenchContributionsRegistry.registerWorkbenchContribution(NotebookLanguageSelectorScoreRefine, LifecyclePhase.Ready); workbenchContributionsRegistry.registerWorkbenchContribution(SimpleNotebookWorkingCopyEditorHandler, LifecyclePhase.Ready); +workbenchContributionsRegistry.registerWorkbenchContribution(NotebookAccessibilityHelpContribution, LifecyclePhase.Eventually); registerSingleton(INotebookService, NotebookService, InstantiationType.Delayed); registerSingleton(INotebookEditorWorkerService, NotebookEditorWorkerServiceImpl, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookAccessibilityHelp.ts b/src/vs/workbench/contrib/notebook/browser/notebookAccessibilityHelp.ts new file mode 100644 index 00000000000..c359503f7c2 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/notebookAccessibilityHelp.ts @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import { format } from 'vs/base/common/strings'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +import { AccessibleViewType, IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; + +export function getAccessibilityHelpText(accessor: ServicesAccessor): string { + const keybindingService = accessor.get(IKeybindingService); + const content = []; + content.push(localize('notebook.overview', 'The notebook view is a collection of code and markdown cells. Code cells can be executed and will produce output directly below the cell.')); + content.push(descriptionForCommand('notebook.execute', localize('notebook.cell.executeAndFocusContainer', 'The Execute Cell command ({0}) executes the cell that currently has focus.',), localize('notebook.cell.executeAndFocusContainerNoKb', 'The Execute Cell command executes the cell that currently has focus and is currently not triggerable by a keybinding.'), keybindingService)); + + return content.join('\n'); +} + +function descriptionForCommand(commandId: string, msg: string, noKbMsg: string, keybindingService: IKeybindingService): string { + const kb = keybindingService.lookupKeybinding(commandId); + if (kb) { + return format(msg, kb.getAriaLabel()); + } + return format(noKbMsg, commandId); +} + +export async function runAccessibilityHelpAction(accessor: ServicesAccessor, editor: ICodeEditor): Promise { + const accessibleViewService = accessor.get(IAccessibleViewService); + const helpText = getAccessibilityHelpText(accessor); + const provider = accessibleViewService.registerProvider({ + id: 'notebook', + provideContent: () => helpText, + onClose: () => { + editor.focus(); + provider.dispose(); + }, + options: { type: AccessibleViewType.HelpMenu, ariaLabel: 'Notebook accessibility help' } + }); + accessibleViewService.show('notebook'); +} From 9a3ddb3bf7b0c75121f5e3891637f6ec6c6c4340 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Mon, 26 Jun 2023 15:28:15 -0700 Subject: [PATCH 2/4] more content --- .../browser/notebookAccessibilityHelp.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookAccessibilityHelp.ts b/src/vs/workbench/contrib/notebook/browser/notebookAccessibilityHelp.ts index c359503f7c2..d26201c2f21 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookAccessibilityHelp.ts @@ -14,7 +14,22 @@ export function getAccessibilityHelpText(accessor: ServicesAccessor): string { const keybindingService = accessor.get(IKeybindingService); const content = []; content.push(localize('notebook.overview', 'The notebook view is a collection of code and markdown cells. Code cells can be executed and will produce output directly below the cell.')); - content.push(descriptionForCommand('notebook.execute', localize('notebook.cell.executeAndFocusContainer', 'The Execute Cell command ({0}) executes the cell that currently has focus.',), localize('notebook.cell.executeAndFocusContainerNoKb', 'The Execute Cell command executes the cell that currently has focus and is currently not triggerable by a keybinding.'), keybindingService)); + content.push(descriptionForCommand('notebook.cell.edit', + localize('notebook.cell.edit', 'The Edit Cell command ({0}) will focus on the cell input.'), + localize('notebook.cell.editNoKb', 'The Edit Cell command will focus on the cell input and is currently not triggerable by a keybinding.'), keybindingService)); + content.push(descriptionForCommand('notebook.cell.quitEdit', + localize('notebook.cell.quitEdit', 'The Quit Edit command ({0}) will set focus on the cell container.'), // pressing escape twice may be necessary for screen reader users + localize('notebook.cell.quitEditNoKb', 'The Quit Edit command will set focus on the cell container and is currently not triggerable by a keybinding.'), keybindingService)); + content.push(descriptionForCommand('notebook.cell.focusInOutput', + localize('notebook.cell.focusInOutput', 'The Focus Output command ({0}) will set focus in the cell\'s output.'), + localize('notebook.cell.focusInOutputNoKb', 'The Quit Edit command will set focus in the cell\'s output and is currently not triggerable by a keybinding.'), keybindingService)); + content.push(localize('notebook.cellNavigation', 'The up and down arrows will move focus between cells while focused on the outer cell container')); + content.push(descriptionForCommand('notebook.cell.executeAndFocusContainer', + localize('notebook.cell.executeAndFocusContainer', 'The Execute Cell command ({0}) executes the cell that currently has focus.',), + localize('notebook.cell.executeAndFocusContainerNoKb', 'The Execute Cell command executes the cell that currently has focus and is currently not triggerable by a keybinding.'), keybindingService)); + content.push(localize('notebook.cell.insertCodeCellBelowAndFocusContainer', 'The Insert Cell Above/Below commands will create new empty code cells')); + content.push(localize('notebook.changeCellType', 'The Change Cell to Code/Markdown commands are used to switch between cell types.')); + return content.join('\n'); } From 9d8052c47056fc0c558df3986cf2ca20fa779588 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Mon, 26 Jun 2023 15:31:13 -0700 Subject: [PATCH 3/4] remove aria alert covered by help content --- .../notebook/browser/notebookEditorWidget.ts | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 8a9adbe0ad3..ba9403015a5 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -15,7 +15,6 @@ import 'vs/css!./media/notebookCellOutput'; import { PixelRatio } from 'vs/base/browser/browser'; import * as DOM from 'vs/base/browser/dom'; import { IMouseWheelEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import * as aria from 'vs/base/browser/ui/aria/aria'; import { IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; import { DeferredPromise, runWhenIdle, SequencerByKey } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -91,7 +90,6 @@ import { INotebookLoggingService } from 'vs/workbench/contrib/notebook/common/no import { Schemas } from 'vs/base/common/network'; import { DropIntoEditorController } from 'vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorController'; import { CopyPasteController } from 'vs/editor/contrib/dropOrPasteInto/browser/copyPasteController'; -import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityContribution'; const $ = DOM.$; @@ -2239,28 +2237,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD return undefined; } - private _cellFocusAria(cell: ICellViewModel, focusItem: 'editor' | 'container' | 'output') { - const index = this._notebookViewModel?.getCellIndex(cell); - const verboseLabel = this.configurationService.getValue(AccessibilityVerbositySettingId.Notebook); - if (index !== undefined && index >= 0) { - let position = ''; - switch (focusItem) { - case 'editor': - position = `the inner ${cell.cellKind === CellKind.Markup ? 'markdown' : 'code'} editor is focused` + (verboseLabel ? `, press escape to focus the cell container` : ''); - break; - case 'output': - position = `the cell output is focused` + (verboseLabel ? `, press escape to focus the cell container` : ''); - break; - case 'container': - position = `the ${cell.cellKind === CellKind.Markup ? 'markdown preview' : 'cell container'} is focused` + (verboseLabel ? `, press enter to focus the inner ${cell.cellKind === CellKind.Markup ? 'markdown' : 'code'} editor` : ''); - break; - default: - break; - } - aria.alert(`Cell ${this._notebookViewModel?.getCellIndex(cell)}, ${position} `); - } - } - private _toggleNotebookCellSelection(selectedCell: ICellViewModel, selectFromPrevious: boolean): void { const currentSelections = this._list.getSelectedElements(); const isSelected = currentSelections.includes(selectedCell); @@ -2300,7 +2276,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD if (focusItem === 'editor') { this.focusElement(cell); - this._cellFocusAria(cell, focusItem); this._list.focusView(); cell.updateEditState(CellEditState.Editing, 'focusNotebookCell'); @@ -2330,7 +2305,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD } } else if (focusItem === 'output') { this.focusElement(cell); - this._cellFocusAria(cell, focusItem); if (!this.hasEditorFocus()) { this._list.focusView(); @@ -2359,7 +2333,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD cell.focusMode = CellFocusMode.Container; this.focusElement(cell); - this._cellFocusAria(cell, focusItem); if (!options?.skipReveal) { if (typeof options?.focusEditorLine === 'number') { this._cursorNavMode.set(true); From da693d217be06e1bc600900d072d48a09e0ff536 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Mon, 26 Jun 2023 15:36:43 -0700 Subject: [PATCH 4/4] note on escape key --- .../contrib/notebook/browser/notebookAccessibilityHelp.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookAccessibilityHelp.ts b/src/vs/workbench/contrib/notebook/browser/notebookAccessibilityHelp.ts index d26201c2f21..0bd04764dd6 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookAccessibilityHelp.ts @@ -18,7 +18,7 @@ export function getAccessibilityHelpText(accessor: ServicesAccessor): string { localize('notebook.cell.edit', 'The Edit Cell command ({0}) will focus on the cell input.'), localize('notebook.cell.editNoKb', 'The Edit Cell command will focus on the cell input and is currently not triggerable by a keybinding.'), keybindingService)); content.push(descriptionForCommand('notebook.cell.quitEdit', - localize('notebook.cell.quitEdit', 'The Quit Edit command ({0}) will set focus on the cell container.'), // pressing escape twice may be necessary for screen reader users + localize('notebook.cell.quitEdit', 'The Quit Edit command ({0}) will set focus on the cell container. The default (Escape) key may need to be pressed twice first exit the virtual cursor if active.'), localize('notebook.cell.quitEditNoKb', 'The Quit Edit command will set focus on the cell container and is currently not triggerable by a keybinding.'), keybindingService)); content.push(descriptionForCommand('notebook.cell.focusInOutput', localize('notebook.cell.focusInOutput', 'The Focus Output command ({0}) will set focus in the cell\'s output.'),