diff --git a/src/vs/base/browser/ui/list/listPaging.ts b/src/vs/base/browser/ui/list/listPaging.ts index d980e915cb53920d7e4f72c39f1c26de2b0990d9..d0e5097f9e6da7c2e3f702b8d0ee6a89e878259f 100644 --- a/src/vs/base/browser/ui/list/listPaging.ts +++ b/src/vs/base/browser/ui/list/listPaging.ts @@ -78,6 +78,10 @@ class PagedAccessibilityProvider implements IListAccessibilityProvider ) { } + getWidgetAriaLabel(): string { + return this.accessibilityProvider.getWidgetAriaLabel(); + } + getAriaLabel(index: number): string | null { const model = this.modelProvider(); diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 0e7bac0e657f61ec8a87772673277c0035d0c899..25e62b4421b567e97fc16ef85257692199d2fde6 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -688,6 +688,8 @@ export interface IStyleController { export interface IListAccessibilityProvider extends IListViewAccessibilityProvider { getAriaLabel(element: T): string | null; + getWidgetAriaLabel(): string; + getWidgetRole?(): string; getAriaLevel?(element: T): number | undefined; onDidChangeActiveDescendant?: Event; getActiveDescendantId?(element: T): string | undefined; @@ -820,8 +822,6 @@ export interface IListOptions { readonly automaticKeyboardNavigation?: boolean; readonly keyboardNavigationLabelProvider?: IKeyboardNavigationLabelProvider; readonly keyboardNavigationDelegate?: IKeyboardNavigationDelegate; - readonly ariaRole?: string; - readonly ariaLabel?: string; readonly keyboardSupport?: boolean; readonly multipleSelectionSupport?: boolean; readonly multipleSelectionController?: IMultipleSelectionController; @@ -1106,6 +1106,7 @@ export class List implements ISpliceable, IDisposable { private styleController: IStyleController; private typeLabelController?: TypeLabelController; private accessibilityProvider?: IListAccessibilityProvider; + private _ariaLabel: string = ''; protected readonly disposables = new DisposableStore(); @@ -1184,7 +1185,8 @@ export class List implements ISpliceable, IDisposable { renderers: IListRenderer[], private _options: IListOptions = DefaultOptions ) { - this.selection = new SelectionTrait(this._options.ariaRole !== 'listbox'); + const role = this._options.accessibilityProvider && this._options.accessibilityProvider.getWidgetRole ? this._options.accessibilityProvider?.getWidgetRole() : 'list'; + this.selection = new SelectionTrait(role !== 'listbox'); this.focus = new Trait('focused'); mixin(_options, defaultStyles, false); @@ -1209,7 +1211,7 @@ export class List implements ISpliceable, IDisposable { }; this.view = new ListView(container, virtualDelegate, renderers, viewOptions); - this.view.domNode.setAttribute('role', _options.ariaRole ?? 'list'); + this.view.domNode.setAttribute('role', role); if (_options.styleController) { this.styleController = _options.styleController(this.view.domId); @@ -1250,8 +1252,8 @@ export class List implements ISpliceable, IDisposable { this.onDidChangeFocus(this._onFocusChange, this, this.disposables); this.onDidChangeSelection(this._onSelectionChange, this, this.disposables); - if (_options.ariaLabel) { - this.view.domNode.setAttribute('aria-label', localize('aria list', "{0}. Use the navigation keys to navigate.", _options.ariaLabel)); + if (this.accessibilityProvider) { + this.ariaLabel = this.accessibilityProvider.getWidgetAriaLabel(); } if (_options.multipleSelectionSupport) { this.view.domNode.setAttribute('aria-multiselectable', 'true'); @@ -1354,6 +1356,15 @@ export class List implements ISpliceable, IDisposable { return this.view.lastVisibleIndex; } + get ariaLabel(): string { + return this._ariaLabel; + } + + set ariaLabel(value: string) { + this._ariaLabel = value; + this.view.domNode.setAttribute('aria-label', localize('aria list', "{0}. Use the navigation keys to navigate.", value)); + } + domFocus(): void { this.view.domNode.focus(); } diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index 19233efb75cfa7c331d32fce3e36d497adcbab62..4924646f11c822ce80a807eac7cd42b634aa1ac9 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -20,6 +20,7 @@ import { ISelectBoxDelegate, ISelectOptionItem, ISelectBoxOptions, ISelectBoxSty import { isMacintosh } from 'vs/base/common/platform'; import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; import { IContentActionHandler } from 'vs/base/browser/formattedTextRenderer'; +import { localize } from 'vs/nls'; const $ = dom.$; @@ -720,12 +721,20 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi this.listRenderer = new SelectListRenderer(); this.selectList = new List('SelectBoxCustom', this.selectDropDownListContainer, this, [this.listRenderer], { - ariaLabel: this.selectBoxOptions.ariaLabel, useShadows: false, verticalScrollMode: ScrollbarVisibility.Visible, keyboardSupport: false, - mouseSupport: false + mouseSupport: false, + accessibilityProvider: { + getAriaLabel: (element) => element.text, + getWidgetAriaLabel: () => localize('selectBox', "Select Box"), + getRole: () => 'option', + getWidgetRole: () => 'listbox' + } }); + if (this.selectBoxOptions.ariaLabel) { + this.selectList.ariaLabel = this.selectBoxOptions.ariaLabel; + } // SetUp list keyboard controller - control navigation, disabled items, focus const onSelectDropDownKeyDown = Event.chain(domEvent(this.selectDropDownListContainer, 'keydown')) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index d15118e774ab4eeba653e31a18d0c269c45c4e7e..e5553f65390b91fb46d21a87243f16ea15f80bd9 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -189,6 +189,10 @@ function asListOptions(modelProvider: () => ITreeModel options.accessibilityProvider!.getWidgetRole!() : () => 'tree', getAriaLevel(node) { return node.depth; }, @@ -202,8 +206,7 @@ function asListOptions(modelProvider: () => ITreeModel implements IDisposable return node.element; } + get ariaLabel(): string { + return this.view.ariaLabel; + } + + set ariaLabel(value: string) { + this.view.ariaLabel = value; + } + domFocus(): void { this.view.domFocus(); } diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index d921bb20459c508b42d1529c76e6413687ed81fe..b18c82167b0e195d2d2f88b80614fd7fb2625b3a 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -242,6 +242,10 @@ function asObjectTreeOptions(options?: IAsyncDataTreeOpt getAriaLabel(e) { return options.accessibilityProvider!.getAriaLabel(e.element as T); }, + getWidgetAriaLabel() { + return options.accessibilityProvider!.getWidgetAriaLabel(); + }, + getWidgetRole: options.accessibilityProvider!.getWidgetRole ? () => options.accessibilityProvider!.getWidgetRole!() : () => 'tree', getAriaLevel: options.accessibilityProvider!.getAriaLevel && (node => { return options.accessibilityProvider!.getAriaLevel!(node.element as T); }), @@ -266,7 +270,6 @@ function asObjectTreeOptions(options?: IAsyncDataTreeOpt e => (options.expandOnlyOnTwistieClick as ((e: T) => boolean))(e.element as T) ) ), - ariaRole: 'tree', additionalScrollHeight: options.additionalScrollHeight }; } @@ -442,6 +445,14 @@ export class AsyncDataTree implements IDisposable return this.tree.lastVisibleElement!.element as T; } + get ariaLabel(): string { + return this.tree.ariaLabel; + } + + set ariaLabel(value: string) { + this.tree.ariaLabel = value; + } + domFocus(): void { this.tree.domFocus(); } diff --git a/src/vs/base/parts/quickinput/browser/quickInputList.ts b/src/vs/base/parts/quickinput/browser/quickInputList.ts index 4b4cf3ca416f05e4ad7ee6b381502f83ec846a4c..1d37629e94a62c2ea50593dc2444a0abfab69b07 100644 --- a/src/vs/base/parts/quickinput/browser/quickInputList.ts +++ b/src/vs/base/parts/quickinput/browser/quickInputList.ts @@ -27,6 +27,7 @@ import { withNullAsUndefined } from 'vs/base/common/types'; import { IQuickInputOptions } from 'vs/base/parts/quickinput/browser/quickInput'; import { IListOptions, List, IListStyles, IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel'; +import { localize } from 'vs/nls'; const $ = dom.$; @@ -286,8 +287,7 @@ export class QuickInputList { setRowLineHeight: false, multipleSelectionSupport: false, horizontalScrolling: false, - accessibilityProvider, - ariaRole: 'listbox' + accessibilityProvider } as IListOptions); this.list.getHTMLElement().id = id; this.disposables.push(this.list); @@ -714,10 +714,18 @@ function compareEntries(elementA: ListElement, elementB: ListElement, lookFor: s class QuickInputAccessibilityProvider implements IListAccessibilityProvider { + getWidgetAriaLabel(): string { + return localize('quickInput', "Quick Input"); + } + getAriaLabel(element: ListElement): string | null { return element.saneAriaLabel; } + getWidgetRole() { + return 'listbox'; + } + getRole() { return 'option'; } diff --git a/src/vs/editor/contrib/documentSymbols/outlineTree.ts b/src/vs/editor/contrib/documentSymbols/outlineTree.ts index 880cc5beccb7054f1e48f0a3c005de06b2911a17..dc76b01e12279e94e1e704235ae57e2cb1d17a68 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineTree.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineTree.ts @@ -42,6 +42,12 @@ export class OutlineNavigationLabelProvider implements IKeyboardNavigationLabelP export class OutlineAccessibilityProvider implements IListAccessibilityProvider { + constructor(private readonly ariaLabel: string) { } + + getWidgetAriaLabel(): string { + return this.ariaLabel; + } + getAriaLabel(element: OutlineItem): string | null { if (element instanceof OutlineGroup) { return element.label; diff --git a/src/vs/editor/contrib/gotoSymbol/peek/referencesTree.ts b/src/vs/editor/contrib/gotoSymbol/peek/referencesTree.ts index 187b717b46e8939c0d51d68ae7711fe0a6088ea4..359b383774362a571d8b4245bf093b8488bc0a3f 100644 --- a/src/vs/editor/contrib/gotoSymbol/peek/referencesTree.ts +++ b/src/vs/editor/contrib/gotoSymbol/peek/referencesTree.ts @@ -215,6 +215,10 @@ export class OneReferenceRenderer implements ITreeRenderer { + getWidgetAriaLabel(): string { + return localize('treeAriaLabel', "References"); + } + getAriaLabel(element: FileReferences | OneReference): string | null { return element.ariaMessage; } diff --git a/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts b/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts index abffc47cde72263ce0fb8e398e5e96016c3b7eed..15cbac19aa408c31b75bb89cb3be6bc129cb5016 100644 --- a/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts +++ b/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts @@ -311,7 +311,6 @@ export class ReferenceWidget extends peekView.PeekViewWidget { // tree this._treeContainer = dom.append(containerElement, dom.$('div.ref-tree.inline')); const treeOptions: IWorkbenchAsyncDataTreeOptions = { - ariaLabel: nls.localize('treeAriaLabel', "References"), keyboardSupport: this._defaultTreeKeyboardSupport, accessibilityProvider: new AccessibilityProvider(), keyboardNavigationLabelProvider: this._instantiationService.createInstance(StringRepresentationProvider), diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 2c0e2d5a3f8141e10caea5594858635901bfcc80..fa6e6352dc51b0bf0267f67d2ae2efd693b76b90 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -607,7 +607,6 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate false }, mouseSupport: false, - ariaRole: 'listbox', accessibilityProvider: { getRole: () => 'option', getAriaLabel: (item: CompletionItem) => { @@ -623,7 +622,9 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate nls.localize('suggest', "Suggest"), + getWidgetRole: () => 'listbox' } }); diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index 67a3f71f61556841522d729f03c7c9cb69abca26..7fd860d282883a1b4af951dee4cd5e4437f170ee 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -31,6 +31,7 @@ import { IIdentityProvider, IListVirtualDelegate, IKeyboardNavigationLabelProvid import { IFileIconTheme, IThemeService } from 'vs/platform/theme/common/themeService'; import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { IModeService } from 'vs/editor/common/services/modeService'; +import { localize } from 'vs/nls'; export function createBreadcrumbsPicker(instantiationService: IInstantiationService, parent: HTMLElement, element: BreadcrumbElement): BreadcrumbsPicker { return element instanceof FileElement @@ -276,6 +277,10 @@ class FileNavigationLabelProvider implements IKeyboardNavigationLabelProvider { + getWidgetAriaLabel(): string { + return localize('breadcrumbs', "Breadcrumbs"); + } + getAriaLabel(element: IWorkspaceFolder | IFileStat): string | null { return element.name; } @@ -475,7 +480,7 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { sorter: this._outlineComparator, identityProvider: new OutlineIdentityProvider(), keyboardNavigationLabelProvider: new OutlineNavigationLabelProvider(), - accessibilityProvider: new OutlineAccessibilityProvider(), + accessibilityProvider: new OutlineAccessibilityProvider(localize('breadcrumbs', "Breadcrumbs")), filter: this._instantiationService.createInstance(OutlineFilter, 'breadcrumbs') } ); diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts index 8d245dce1cf6a2f473aeaa89853d6e1519226069..d1dec4f90fd50c1b475b5af9ac6b3b7f5900e14e 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts @@ -157,10 +157,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente notificationsToolBar.push(hideAllAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(hideAllAction) }); // Notifications List - this.notificationsList = this.instantiationService.createInstance(NotificationsList, this.notificationsCenterContainer, { - ariaLabel: localize('notificationsList', "Notifications List") - }); - + this.notificationsList = this.instantiationService.createInstance(NotificationsList, this.notificationsCenterContainer, {}); this.container.appendChild(this.notificationsCenterContainer); } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsList.ts b/src/vs/workbench/browser/parts/notifications/notificationsList.ts index 3644a656a218599b80725452ce87b3dbde885274..126f1ea74c597e746103d7e2258fef4bd9dc1916 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsList.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsList.ts @@ -97,6 +97,9 @@ export class NotificationsList extends Themable { } return localize('notificationWithSourceAriaLabel', "{0}, source: {1}, notification", element.message.raw, element.source); + }, + getWidgetAriaLabel(): string { + return localize('notificationsList', "Notifications List"); } } } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts index 3fd8e6d8e113b1601944a7d0af7a54eb487202f1..b2ab6c301ed4689ae9bcf4d12912816ead67c66a 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts @@ -17,7 +17,6 @@ import { widgetShadow } from 'vs/platform/theme/common/colorRegistry'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { NotificationsToastsVisibleContext, INotificationsToastController } from 'vs/workbench/browser/parts/notifications/notificationsCommands'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { localize } from 'vs/nls'; import { Severity, NotificationsFilter } from 'vs/platform/notification/common/notification'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; @@ -171,7 +170,6 @@ export class NotificationsToasts extends Themable implements INotificationsToast // Create toast with item and show const notificationList = this.instantiationService.createInstance(NotificationsList, notificationToast, { ariaRole: 'dialog', // https://github.com/microsoft/vscode/issues/82728 - ariaLabel: localize('notificationsToast', "Notification Toast"), verticalScrollMode: ScrollbarVisibility.Hidden }); itemDisposables.add(notificationList); diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 6d5dfd28ef952b21941a9e6d9b2793944473d4df..f640264a4be36db13d79028cb441b6347f8a1bdf 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -418,6 +418,7 @@ export class TreeView extends Disposable implements ITreeView { const dataSource = this.instantiationService.createInstance(TreeDataSource, this, (task: Promise) => this.progressService.withProgress({ location: this.id }, () => task)); const aligner = new Aligner(this.themeService); const renderer = this.instantiationService.createInstance(TreeRenderer, this.id, treeMenus, this.treeLabels, actionViewItemProvider, aligner); + const widgetAriaLabel = this._title; this.tree = this._register(this.instantiationService.createInstance(Tree, this.id, this.treeContainer, new TreeViewDelegate(), [renderer], dataSource, { @@ -425,9 +426,11 @@ export class TreeView extends Disposable implements ITreeView { accessibilityProvider: { getAriaLabel(element: ITreeItem): string { return element.tooltip ? element.tooltip : element.label ? element.label.label : ''; + }, + getWidgetAriaLabel(): string { + return widgetAriaLabel; } }, - ariaLabel: this._title, keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: (item: ITreeItem) => { return item.label ? item.label.label : (item.resourceUri ? basename(URI.revive(item.resourceUri)) : undefined); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts index 6ce8e7a1264c58a1d5ef5a1e546b969f5d19a295..a899a846a6ad73602f4506bb05152df59f23780b 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -276,6 +276,10 @@ export class BulkEditAccessibilityProvider implements IListAccessibilityProvider constructor(@ILabelService private readonly _labelService: ILabelService) { } + getWidgetAriaLabel(): string { + return localize('bulkEdit', "Bulk Edit"); + } + getRole(_element: BulkEditElement): string { return 'checkbox'; } diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts index 7f9a739501d59af1233e464e68509aa2474ab62a..54780c42bd70f4b0b45317f1c92759fd887db451 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts @@ -201,7 +201,6 @@ export class CallHierarchyTreePeekWidget extends peekView.PeekViewWidget { sorter: new callHTree.Sorter(), accessibilityProvider: new callHTree.AccessibilityProvider(() => this._direction), identityProvider: new callHTree.IdentityProvider(() => this._direction), - ariaLabel: localize('tree.aria', "Call Hierarchy"), expandOnlyOnTwistieClick: true, overrideStyles: { listBackground: peekView.peekViewResultsBackground diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts index 445d67916f02acff3f7f20a301038c12287968da..47ee16fd343ceea1ec5c41e35ceeee9497a66227 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts @@ -147,6 +147,10 @@ export class AccessibilityProvider implements IListAccessibilityProvider { public getDirection: () => CallHierarchyDirection ) { } + getWidgetAriaLabel(): string { + return localize('tree.aria', "Call Hierarchy"); + } + getAriaLabel(element: Call): string | null { if (this.getDirection() === CallHierarchyDirection.CallsFrom) { return localize('from', "calls from {0}", element.item.name); diff --git a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts index 6f53ec80720ee1270a5db6605313dcb7e59bf197..6c2745d2ec846e4f314cccb2db99be23166e542f 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts @@ -201,9 +201,11 @@ export class CommentsList extends WorkbenchAsyncDataTree { ); } return ''; + }, + getWidgetAriaLabel(): string { + return COMMENTS_VIEW_TITLE; } }, - ariaLabel: COMMENTS_VIEW_TITLE, keyboardSupport: true, identityProvider: { getId: (element: any) => { diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index 9aa16147769532356d9b8e474bd4939a0bf4a452..e80412868259d39b42fb925ed743f9bc3bc56855 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -623,6 +623,10 @@ class BreakpointsAccessibilityProvider implements IListAccessibilityProvider { if (typeof element === 'string') { @@ -813,6 +812,11 @@ class CallStackDataSource implements IAsyncDataSource { + + getWidgetAriaLabel(): string { + return nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'callStackAriaLabel' }, "Debug Call Stack"); + } + getAriaLabel(element: CallStackItem): string { if (element instanceof Thread) { return nls.localize('threadAriaLabel', "Thread {0}, callstack, debug", (element).name); diff --git a/src/vs/workbench/contrib/debug/browser/debugHover.ts b/src/vs/workbench/contrib/debug/browser/debugHover.ts index ff8715dd1707e2de5e14f92b2a503f2aa05a4d45..83f8681a99673cffe0422169ae036eee762f175f 100644 --- a/src/vs/workbench/contrib/debug/browser/debugHover.ts +++ b/src/vs/workbench/contrib/debug/browser/debugHover.ts @@ -105,7 +105,6 @@ export class DebugHoverWidget implements IContentWidget { this.tree = >this.instantiationService.createInstance(WorkbenchAsyncDataTree, 'DebugHover', this.treeContainer, new DebugHoverDelegate(), [this.instantiationService.createInstance(VariablesRenderer)], dataSource, { - ariaLabel: nls.localize('treeAriaLabel', "Debug Hover"), accessibilityProvider: new DebugHoverAccessibilityProvider(), mouseSupport: false, horizontalScrolling: true, @@ -336,6 +335,11 @@ export class DebugHoverWidget implements IContentWidget { } class DebugHoverAccessibilityProvider implements IListAccessibilityProvider { + + getWidgetAriaLabel(): string { + return nls.localize('treeAriaLabel', "Debug Hover"); + } + getAriaLabel(element: IExpression): string { return nls.localize('variableAriaLabel', "{0} value {1}, variables, debug", element.name, element.value); } diff --git a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts index 85f2a8058beffe3babab414f52972ddd045d5ce6..3a160385f6ddff23efa295559cf7ff409d3d8c97 100644 --- a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts @@ -473,7 +473,6 @@ export class LoadedScriptsView extends ViewPane { }, filter: this.filter, accessibilityProvider: new LoadedSciptsAccessibilityProvider(), - ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'loadedScriptsAriaLabel' }, "Debug Loaded Scripts"), overrideStyles: { listBackground: this.getBackgroundColor() } @@ -711,6 +710,10 @@ class LoadedScriptsRenderer implements ICompressibleTreeRenderer { + getWidgetAriaLabel(): string { + return nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'loadedScriptsAriaLabel' }, "Debug Loaded Scripts"); + } + getAriaLabel(element: LoadedScriptsItem): string { if (element instanceof RootFolderTreeItem) { diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index bec9f44fdccf0c54e33d1eb5561530d5bfc9f4b9..16bdbd92f9cbb1f42904bc6e243c73091ebc7ae0 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -531,7 +531,6 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { // https://github.com/microsoft/TypeScript/issues/32526 new ReplDataSource() as IAsyncDataSource, { - ariaLabel: localize('replAriaLabel', "Read Eval Print Loop Panel"), accessibilityProvider: new ReplAccessibilityProvider(), identityProvider: { getId: (element: IReplElement) => element.getId() }, mouseSupport: false, diff --git a/src/vs/workbench/contrib/debug/browser/replViewer.ts b/src/vs/workbench/contrib/debug/browser/replViewer.ts index cb267ed7adcbbabf8fc29f7538c2389e6057f44e..b64c555128a074e17628422e8d12c96f59677e13 100644 --- a/src/vs/workbench/contrib/debug/browser/replViewer.ts +++ b/src/vs/workbench/contrib/debug/browser/replViewer.ts @@ -367,6 +367,11 @@ export class ReplDataSource implements IAsyncDataSource { + + getWidgetAriaLabel(): string { + return localize('debugConsole', "Debug Console"); + } + getAriaLabel(element: IReplElement): string { if (element instanceof Variable) { return localize('replVariableAriaLabel', "Variable {0} has value {1}, read eval print loop, debug", element.name, element.value); diff --git a/src/vs/workbench/contrib/debug/browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts index 348f247a2518acf1b092275513995f2dc9bfdff9..887f75027c83070fec72116d6b2f1155b9b3779e 100644 --- a/src/vs/workbench/contrib/debug/browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -102,7 +102,6 @@ export class VariablesView extends ViewPane { this.tree = >this.instantiationService.createInstance(WorkbenchAsyncDataTree, 'VariablesView', treeContainer, new VariablesDelegate(), [this.instantiationService.createInstance(VariablesRenderer), new ScopesRenderer(), new ScopeErrorRenderer()], new VariablesDataSource(), { - ariaLabel: nls.localize('variablesAriaTreeLabel', "Debug Variables"), accessibilityProvider: new VariablesAccessibilityProvider(), identityProvider: { getId: (element: IExpression | IScope) => element.getId() }, keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: (e: IExpression | IScope) => e }, @@ -350,6 +349,11 @@ export class VariablesRenderer extends AbstractExpressionsRenderer { } class VariablesAccessibilityProvider implements IListAccessibilityProvider { + + getWidgetAriaLabel(): string { + return nls.localize('variablesAriaTreeLabel', "Debug Variables"); + } + getAriaLabel(element: IExpression | IScope): string | null { if (element instanceof Scope) { return nls.localize('variableScopeAriaLabel', "Scope {0}, variables, debug", element.name); diff --git a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts index 67d3d00787721ebcffc21ad5b2e3df145a0c3700..0f72fea35bf4222a1e6c4cfb3968a6b3dd1f0b4b 100644 --- a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts +++ b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts @@ -76,7 +76,6 @@ export class WatchExpressionsView extends ViewPane { const expressionsRenderer = this.instantiationService.createInstance(WatchExpressionsRenderer); this.tree = >this.instantiationService.createInstance(WorkbenchAsyncDataTree, 'WatchExpressions', treeContainer, new WatchExpressionsDelegate(), [expressionsRenderer, this.instantiationService.createInstance(VariablesRenderer)], new WatchExpressionsDataSource(), { - ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'watchAriaTreeLabel' }, "Debug Watch Expressions"), accessibilityProvider: new WatchExpressionsAccessibilityProvider(), identityProvider: { getId: (element: IExpression) => element.getId() }, keyboardNavigationLabelProvider: { @@ -94,7 +93,6 @@ export class WatchExpressionsView extends ViewPane { listBackground: this.getBackgroundColor() } }); - this.tree.setInput(this.debugService); CONTEXT_WATCH_EXPRESSIONS_FOCUSED.bindTo(this.tree.contextKeyService); @@ -298,6 +296,11 @@ export class WatchExpressionsRenderer extends AbstractExpressionsRenderer { } class WatchExpressionsAccessibilityProvider implements IListAccessibilityProvider { + + getWidgetAriaLabel(): string { + return nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'watchAriaTreeLabel' }, "Debug Watch Expressions"); + } + getAriaLabel(element: IExpression): string { if (element instanceof Expression) { return nls.localize('watchExpressionAriaLabel', "{0} value {1}, watch, debug", (element).name, (element).value); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts index ab5dfc23c866a1435ec825d9900c8d12f91cbec5..89ac951350f99d4ccaac4fb29b4d8944cb052d67 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts @@ -272,6 +272,9 @@ export class ExtensionsTree extends WorkbenchAsyncDataTree>{ getAriaLabel(extensionData: IExtensionData): string { return localize('extension-arialabel', "{0}. Press enter for extension details.", extensionData.extension.displayName); + }, + getWidgetAriaLabel(): string { + return localize('extensions', "Extensions"); } } }, diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 4a74c699878fd853b4bf3cea2e30d9b85fc4ac18..0cb220e341c1ec862c4726a3579913f810bd5306 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -137,13 +137,15 @@ export class ExtensionsListView extends ViewPane { const extensionsViewState = new ExtensionsViewState(); const renderer = this.instantiationService.createInstance(Renderer, extensionsViewState); this.list = this.instantiationService.createInstance>(WorkbenchPagedList, 'Extensions', extensionsList, delegate, [renderer], { - ariaLabel: localize('extensions', "Extensions"), multipleSelectionSupport: false, setRowLineHeight: false, horizontalScrolling: false, accessibilityProvider: >{ getAriaLabel(extension: IExtension | null): string { return extension ? localize('extension-arialabel', "{0}. Press enter for extension details.", extension.displayName) : ''; + }, + getWidgetAriaLabel(): string { + return localize('extensions', "Extensions"); } }, overrideStyles: { diff --git a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts index 8d005a5b4f4652266614d761d3c12979569511b4..cc851c4bc4d89620828a73bd4be6026288b24de2 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -698,6 +698,10 @@ export class SaveExtensionHostProfileAction extends Action { } class RuntimeExtensionsEditorAccessibilityProvider implements IListAccessibilityProvider { + getWidgetAriaLabel(): string { + return nls.localize('runtimeExtensions', "Runtime Extensions"); + } + getAriaLabel(element: IRuntimeExtension): string | null { return element.description.name; } diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index 12dc9ee5031632ea85678fe5d6886c010c702e6a..367d06468d48263715162b85865eb4688f3e448a 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -374,7 +374,6 @@ export class ExplorerView extends ViewPane { this.instantiationService.createInstance(ExplorerDataSource), { compressionEnabled: isCompressionEnabled(), accessibilityProvider: this.renderer, - ariaLabel: nls.localize('treeAriaLabel', "Files Explorer"), identityProvider: { getId: (stat: ExplorerItem) => { if (stat instanceof NewExplorerItem) { diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 32f36adae904683e4f881c9a2f5b00b841e899c5..79836599b9e613f0383704e7d7706bd3714a00f2 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -254,6 +254,10 @@ export class FilesRenderer implements ICompressibleTreeRenderer { + + getWidgetAriaLabel(): string { + return nls.localize('openEditors', "Open Editors"); + } + getAriaLabel(element: OpenEditor | IEditorGroup): string | null { if (element instanceof OpenEditor) { return `${element.editor.getName()} ${element.editor.getDescription()}`; diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index 1ca8c91921be9c2f0ad7b223ecc413716fc37786..ac8ceb7f6bde0b1a076cb72c624f19aa7419f9e4 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -75,6 +75,10 @@ export class MarkersTreeAccessibilityProvider implements IListAccessibilityProvi constructor(@ILabelService private readonly labelService: ILabelService) { } + getWidgetAriaLabel(): string { + return localize('problemsView', "Problems View"); + } + public getAriaLabel(element: TreeElement): string | null { if (element instanceof ResourceMarkers) { const path = this.labelService.getUriLabel(element.resource, { relative: true }) || element.resource.fsPath; diff --git a/src/vs/workbench/contrib/markers/browser/markersView.ts b/src/vs/workbench/contrib/markers/browser/markersView.ts index 6a7245691504fd647c53423e325cb4a591761d6c..71cdf815988ed60416161a9ba1f02b89166302d7 100644 --- a/src/vs/workbench/contrib/markers/browser/markersView.ts +++ b/src/vs/workbench/contrib/markers/browser/markersView.ts @@ -600,9 +600,9 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { } else { this.messageBoxContainer.style.display = 'none'; if (filtered === total) { - this.ariaLabelElement.setAttribute('aria-label', localize('No problems filtered', "Showing {0} problems", total)); + this.setAriaLabel(localize('No problems filtered', "Showing {0} problems", total)); } else { - this.ariaLabelElement.setAttribute('aria-label', localize('problems filtered', "Showing {0} of {1} problems", filtered, total)); + this.setAriaLabel(localize('problems filtered', "Showing {0} of {1} problems", filtered, total)); } this.messageBoxContainer.removeAttribute('tabIndex'); } @@ -631,19 +631,26 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { e.stopPropagation(); } }); - this.ariaLabelElement!.setAttribute('aria-label', Messages.MARKERS_PANEL_NO_PROBLEMS_FILTERS); + this.setAriaLabel(Messages.MARKERS_PANEL_NO_PROBLEMS_FILTERS); } private renderNoProblemsMessageForActiveFile(container: HTMLElement) { const span = dom.append(container, dom.$('span')); span.textContent = Messages.MARKERS_PANEL_NO_PROBLEMS_ACTIVE_FILE_BUILT; - this.ariaLabelElement!.setAttribute('aria-label', Messages.MARKERS_PANEL_NO_PROBLEMS_ACTIVE_FILE_BUILT); + this.setAriaLabel(Messages.MARKERS_PANEL_NO_PROBLEMS_ACTIVE_FILE_BUILT); } private renderNoProblemsMessage(container: HTMLElement) { const span = dom.append(container, dom.$('span')); span.textContent = Messages.MARKERS_PANEL_NO_PROBLEMS_BUILT; - this.ariaLabelElement!.setAttribute('aria-label', Messages.MARKERS_PANEL_NO_PROBLEMS_BUILT); + this.setAriaLabel(Messages.MARKERS_PANEL_NO_PROBLEMS_BUILT); + } + + private setAriaLabel(label: string): void { + if (this.tree) { + this.tree.ariaLabel = label; + } + this.ariaLabelElement!.setAttribute('aria-label', label); } private clearFilters(): void { diff --git a/src/vs/workbench/contrib/outline/browser/outlinePane.ts b/src/vs/workbench/contrib/outline/browser/outlinePane.ts index b0bc1c67624df65bc437e63ebb84e877c0207b3e..fa38d36671ea3a32d5ab4486c787f80417c7881b 100644 --- a/src/vs/workbench/contrib/outline/browser/outlinePane.ts +++ b/src/vs/workbench/contrib/outline/browser/outlinePane.ts @@ -340,7 +340,7 @@ export class OutlinePane extends ViewPane { filter: this._treeFilter, identityProvider: new OutlineIdentityProvider(), keyboardNavigationLabelProvider: new OutlineNavigationLabelProvider(), - accessibilityProvider: new OutlineAccessibilityProvider(), + accessibilityProvider: new OutlineAccessibilityProvider(localize('outline', "Outline")), hideTwistiesOfChildlessElements: true, overrideStyles: { listBackground: this.getBackgroundColor() diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts index a5bb6604061c6f51d7d7ad76aea2771b31e6ee8b..fbafa5570c3abfa901d6e816ba3dee9ca6f99403 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts @@ -456,7 +456,6 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditorP this.keybindingsListContainer = DOM.append(parent, $('.keybindings-list-container')); this.keybindingsList = this._register(this.instantiationService.createInstance(WorkbenchList, 'KeybindingsEditor', this.keybindingsListContainer, new Delegate(), [new KeybindingItemRenderer(this, this.instantiationService)], { identityProvider: { getId: (e: IListEntry) => e.id }, - ariaLabel: localize('keybindingsLabel', "Keybindings"), setRowLineHeight: false, horizontalScrolling: false, accessibilityProvider: new AccessibilityProvider(), @@ -464,6 +463,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditorP listBackground: editorBackground } })) as WorkbenchList; + this._register(this.keybindingsList.onContextMenu(e => this.onContextMenu(e))); this._register(this.keybindingsList.onDidChangeFocus(e => this.onFocusChange(e))); this._register(this.keybindingsList.onDidFocus(() => { @@ -1106,6 +1106,10 @@ class WhenColumn extends Column { class AccessibilityProvider implements IListAccessibilityProvider { + getWidgetAriaLabel(): string { + return localize('keybindingsLabel', "Keybindings"); + } + getAriaLabel(keybindingItemEntry: IKeybindingItemEntry): string { let ariaLabel = localize('commandAriaLabel', "Command is {0}.", keybindingItemEntry.keybindingItem.commandLabel ? keybindingItemEntry.keybindingItem.commandLabel : keybindingItemEntry.keybindingItem.command); ariaLabel += keybindingItemEntry.keybindingItem.keybinding ? localize('keybindingAriaLabel', "Keybinding is {0}.", keybindingItemEntry.keybindingItem.keybinding.getAriaLabel()) : localize('noKeybinding', "No Keybinding assigned."); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 41f3c36978d7ae00f81606e092bd32d33833a4fd..bbe27d81be1935721af4ff9354115135b1cfa3f6 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -1558,13 +1558,23 @@ export class SettingsTree extends ObjectTree { renderers, { supportDynamicHeights: true, - ariaRole: 'form', - ariaLabel: localize('treeAriaLabel', "Settings"), identityProvider: { getId(e) { return e.id; } }, + accessibilityProvider: { + getWidgetRole() { + return 'form'; + }, + getAriaLabel() { + // TODO@roblourens https://github.com/microsoft/vscode/issues/95862 + return ''; + }, + getWidgetAriaLabel() { + return localize('settings', "Settings"); + } + }, styleController: id => new DefaultStyleController(DOM.createStyleSheet(container), id), filter: instantiationService.createInstance(SettingsTreeFilter, viewState) }); diff --git a/src/vs/workbench/contrib/preferences/browser/tocTree.ts b/src/vs/workbench/contrib/preferences/browser/tocTree.ts index cbe46dff65ac3fde06fa6ffd0c7ba8bc4cfce4cb..70189937d1caca18ef4daab8d132607e806ebff3 100644 --- a/src/vs/workbench/contrib/preferences/browser/tocTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/tocTree.ts @@ -156,6 +156,10 @@ export function createTOCIterator(model: TOCTreeModel | SettingsTreeGroupElement } class SettingsAccessibilityProvider implements IListAccessibilityProvider { + getWidgetAriaLabel(): string { + return localize('settingsTOC', "Settings Table of Contents"); + } + getAriaLabel(element: SettingsTreeElement): string { if (!element) { return ''; diff --git a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts index 9ef3c16651e46efe8fc0bcc8fae625ae8a83004f..e90af1a664ba1864338755d09f43c27bd55e78e7 100644 --- a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts +++ b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts @@ -395,6 +395,10 @@ export class SCMAccessibilityProvider implements IListAccessibilityProvider