Skip to content

Commit a3cfa39

Browse files
authored
Merge pull request microsoft#187879 from microsoft/merogge/notification
add notification `AccessibleView`, `next/previous` functions
2 parents 683fc37 + 6d6bd0a commit a3cfa39

File tree

6 files changed

+186
-28
lines changed

6 files changed

+186
-28
lines changed

src/vs/workbench/browser/parts/notifications/notificationsCommands.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,23 +61,23 @@ export interface INotificationsToastController {
6161
hide(): void;
6262
}
6363

64-
export function registerNotificationCommands(center: INotificationsCenterController, toasts: INotificationsToastController, model: NotificationsModel): void {
64+
export function getNotificationFromContext(listService: IListService, context?: unknown): INotificationViewItem | undefined {
65+
if (isNotificationViewItem(context)) {
66+
return context;
67+
}
6568

66-
function getNotificationFromContext(listService: IListService, context?: unknown): INotificationViewItem | undefined {
67-
if (isNotificationViewItem(context)) {
68-
return context;
69+
const list = listService.lastFocusedList;
70+
if (list instanceof WorkbenchList) {
71+
const focusedElement = list.getFocusedElements()[0];
72+
if (isNotificationViewItem(focusedElement)) {
73+
return focusedElement;
6974
}
75+
}
7076

71-
const list = listService.lastFocusedList;
72-
if (list instanceof WorkbenchList) {
73-
const focusedElement = list.getFocusedElements()[0];
74-
if (isNotificationViewItem(focusedElement)) {
75-
return focusedElement;
76-
}
77-
}
77+
return undefined;
78+
}
7879

79-
return undefined;
80-
}
80+
export function registerNotificationCommands(center: INotificationsCenterController, toasts: INotificationsToastController, model: NotificationsModel): void {
8181

8282
// Show Notifications Cneter
8383
CommandsRegistry.registerCommand(SHOW_NOTIFICATIONS_CENTER, () => {

src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ import { ToggleTabFocusModeAction } from 'vs/editor/contrib/toggleTabFocusMode/b
1212
import { localize } from 'vs/nls';
1313
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
1414
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
15-
import { AccessibilityHelpAction, AccessibleViewAction, registerAccessibilityConfiguration } from 'vs/workbench/contrib/accessibility/browser/accessibilityContribution';
16-
import { AccessibleViewService, AccessibleViewType, IAccessibleContentProvider, IAccessibleViewOptions, IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView';
15+
import { AccessibilityHelpAction, AccessibleViewAction, AccessibleViewNextAction, AccessibleViewPreviousAction, registerAccessibilityConfiguration } from 'vs/workbench/contrib/accessibility/browser/accessibilityContribution';
1716
import * as strings from 'vs/base/common/strings';
1817
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
1918
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
@@ -24,6 +23,10 @@ import { NEW_UNTITLED_FILE_COMMAND_ID } from 'vs/workbench/contrib/files/browser
2423
import { ModesHoverController } from 'vs/editor/contrib/hover/browser/hover';
2524
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
2625
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
26+
import { getNotificationFromContext } from 'vs/workbench/browser/parts/notifications/notificationsCommands';
27+
import { IListService, WorkbenchList } from 'vs/platform/list/browser/listService';
28+
import { NotificationFocusedContext } from 'vs/workbench/common/contextkeys';
29+
import { IAccessibleViewService, AccessibleViewService, IAccessibleContentProvider, IAccessibleViewOptions, AccessibleViewType, accessibleViewIsShown } from 'vs/workbench/contrib/accessibility/browser/accessibleView';
2730

2831
registerAccessibilityConfiguration();
2932
registerSingleton(IAccessibleViewService, AccessibleViewService, InstantiationType.Delayed);
@@ -145,3 +148,96 @@ class HoverAccessibleViewContribution extends Disposable {
145148
const workbenchContributionsRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
146149
workbenchContributionsRegistry.registerWorkbenchContribution(HoverAccessibleViewContribution, LifecyclePhase.Eventually);
147150

151+
152+
class NotificationAccessibleViewContribution extends Disposable {
153+
static ID: 'notificationAccessibleViewContribution';
154+
constructor() {
155+
super();
156+
this._register(AccessibleViewAction.addImplementation(90, 'notifications', accessor => {
157+
const accessibleViewService = accessor.get(IAccessibleViewService);
158+
const listService = accessor.get(IListService);
159+
const commandService = accessor.get(ICommandService);
160+
161+
function renderAccessibleView(): boolean {
162+
const notification = getNotificationFromContext(listService);
163+
if (!notification) {
164+
return false;
165+
}
166+
commandService.executeCommand('notifications.showList');
167+
let notificationIndex: number | undefined;
168+
const list = listService.lastFocusedList;
169+
if (list instanceof WorkbenchList) {
170+
notificationIndex = list.indexOf(notification);
171+
}
172+
if (notificationIndex === undefined) {
173+
return false;
174+
}
175+
function focusList(): void {
176+
commandService.executeCommand('notifications.showList');
177+
if (list && notificationIndex !== undefined) {
178+
list.domFocus();
179+
try {
180+
list.setFocus([notificationIndex]);
181+
} catch { }
182+
}
183+
}
184+
const message = notification.message.original.toString();
185+
if (!message) {
186+
return false;
187+
}
188+
accessibleViewService.show({
189+
provideContent: () => {
190+
return localize('notification.accessibleView', '{0} Source: {1}', message, notification.source);
191+
},
192+
onClose(): void {
193+
focusList();
194+
},
195+
next(): void {
196+
if (!list) {
197+
return;
198+
}
199+
focusList();
200+
list.focusNext();
201+
renderAccessibleView();
202+
},
203+
previous(): void {
204+
if (!list) {
205+
return;
206+
}
207+
focusList();
208+
list.focusPrevious();
209+
renderAccessibleView();
210+
},
211+
verbositySettingKey: 'notifications',
212+
options: {
213+
ariaLabel: localize('notification', "Notification Accessible View"),
214+
type: AccessibleViewType.View
215+
}
216+
});
217+
return true;
218+
}
219+
return renderAccessibleView();
220+
}, NotificationFocusedContext));
221+
}
222+
}
223+
224+
workbenchContributionsRegistry.registerWorkbenchContribution(NotificationAccessibleViewContribution, LifecyclePhase.Eventually);
225+
226+
class AccessibleViewNavigatorContribution extends Disposable {
227+
static ID: 'AccessibleViewNavigatorContribution';
228+
constructor() {
229+
super();
230+
this._register(AccessibleViewNextAction.addImplementation(95, 'next', accessor => {
231+
const accessibleViewService = accessor.get(IAccessibleViewService);
232+
accessibleViewService.next();
233+
return true;
234+
}, accessibleViewIsShown));
235+
this._register(AccessibleViewPreviousAction.addImplementation(95, 'previous', accessor => {
236+
const accessibleViewService = accessor.get(IAccessibleViewService);
237+
accessibleViewService.previous();
238+
return true;
239+
}, accessibleViewIsShown));
240+
}
241+
}
242+
243+
workbenchContributionsRegistry.registerWorkbenchContribution(AccessibleViewNavigatorContribution, LifecyclePhase.Eventually);

src/vs/workbench/contrib/accessibility/browser/accessibilityContribution.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,34 @@ export const AccessibleViewAction = registerCommand(new MultiCommand({
106106
order: 1
107107
}],
108108
}));
109+
110+
111+
export const AccessibleViewNextAction = registerCommand(new MultiCommand({
112+
id: 'editor.action.accessibleViewNext',
113+
precondition: undefined,
114+
kbOpts: {
115+
primary: KeyMod.Alt | KeyCode.BracketRight,
116+
weight: KeybindingWeight.WorkbenchContrib
117+
},
118+
menuOpts: [{
119+
menuId: MenuId.CommandPalette,
120+
group: '',
121+
title: localize('editor.action.accessibleViewNext', "Next Accessible View"),
122+
order: 1
123+
}],
124+
}));
125+
126+
export const AccessibleViewPreviousAction = registerCommand(new MultiCommand({
127+
id: 'editor.action.accessibleViewPrevious',
128+
precondition: undefined,
129+
kbOpts: {
130+
primary: KeyMod.Alt | KeyCode.BracketLeft,
131+
weight: KeybindingWeight.WorkbenchContrib
132+
},
133+
menuOpts: [{
134+
menuId: MenuId.CommandPalette,
135+
group: '',
136+
title: localize('editor.action.accessibleViewPrevious', "Previous Accessible View"),
137+
order: 1
138+
}],
139+
}));

src/vs/workbench/contrib/accessibility/browser/accessibleView.ts

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/cont
2222
import { IContextViewDelegate, IContextViewService } from 'vs/platform/contextview/browser/contextView';
2323
import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation';
2424
import { IOpenerService } from 'vs/platform/opener/common/opener';
25-
import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard';
26-
import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions';
2725
import { alert } from 'vs/base/browser/ui/aria/aria';
26+
import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions';
27+
import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard';
2828

2929
const enum DEFAULT {
3030
WIDTH = 800,
@@ -36,6 +36,8 @@ export interface IAccessibleContentProvider {
3636
provideContent(): string;
3737
onClose(): void;
3838
onKeyDown?(e: IKeyboardEvent): void;
39+
previous?(): void;
40+
next?(): void;
3941
options: IAccessibleViewOptions;
4042
}
4143

@@ -44,6 +46,8 @@ export const IAccessibleViewService = createDecorator<IAccessibleViewService>('a
4446
export interface IAccessibleViewService {
4547
readonly _serviceBrand: undefined;
4648
show(provider: IAccessibleContentProvider): void;
49+
next(): void;
50+
previous(): void;
4751
}
4852

4953
export const enum AccessibleViewType {
@@ -59,9 +63,11 @@ export interface IAccessibleViewOptions {
5963
}
6064

6165
export const accessibilityHelpIsShown = new RawContextKey<boolean>('accessibilityHelpIsShown', false, true);
66+
export const accessibleViewIsShown = new RawContextKey<boolean>('accessibleViewIsShown', false, true);
6267
class AccessibleView extends Disposable {
6368
private _editorWidget: CodeEditorWidget;
6469
private _accessiblityHelpIsShown: IContextKey<boolean>;
70+
private _accessibleViewIsShown: IContextKey<boolean>;
6571
get editorWidget() { return this._editorWidget; }
6672
private _editorContainer: HTMLElement;
6773
private _currentProvider: IAccessibleContentProvider | undefined;
@@ -77,6 +83,7 @@ class AccessibleView extends Disposable {
7783
) {
7884
super();
7985
this._accessiblityHelpIsShown = accessibilityHelpIsShown.bindTo(this._contextKeyService);
86+
this._accessibleViewIsShown = accessibleViewIsShown.bindTo(this._contextKeyService);
8087
this._editorContainer = document.createElement('div');
8188
this._editorContainer.classList.add('accessible-view');
8289
const codeEditorWidgetOptions: ICodeEditorWidgetOptions = {
@@ -119,14 +126,33 @@ class AccessibleView extends Disposable {
119126
onHide: () => {
120127
if (provider.options.type === AccessibleViewType.HelpMenu) {
121128
this._accessiblityHelpIsShown.reset();
129+
} else {
130+
this._accessibleViewIsShown.reset();
122131
}
123132
this._currentProvider = undefined;
124133
}
125134
};
126135
this._contextViewService.showContextView(delegate);
127136
if (provider.options.type === AccessibleViewType.HelpMenu) {
128137
this._accessiblityHelpIsShown.set(true);
138+
} else {
139+
this._accessibleViewIsShown.set(true);
140+
}
141+
this._currentProvider = provider;
142+
}
143+
144+
previous(): void {
145+
if (!this._currentProvider) {
146+
return;
147+
}
148+
this._currentProvider.previous?.();
149+
}
150+
151+
next(): void {
152+
if (!this._currentProvider) {
153+
return;
129154
}
155+
this._currentProvider.next?.();
130156
}
131157

132158
private _render(provider: IAccessibleContentProvider, container: HTMLElement): IDisposable {
@@ -172,19 +198,20 @@ class AccessibleView extends Disposable {
172198
});
173199
const disposableStore = new DisposableStore();
174200
disposableStore.add(this._editorWidget.onKeyUp((e) => {
175-
if (e.keyCode === KeyCode.Escape) {
176-
this._contextViewService.hideContextView();
177-
// Delay to allow the context view to hide #186514
178-
setTimeout(() => provider.onClose(), 100);
179-
} else if (e.keyCode === KeyCode.KeyD && this._configurationService.getValue(settingKey)) {
201+
if (e.keyCode === KeyCode.KeyD && this._configurationService.getValue(settingKey)) {
180202
alert(localize('disableAccessibilityHelp', '{0} accessibility verbosity is now disabled', provider.verbositySettingKey));
181203
this._configurationService.updateValue(settingKey, false);
182204
}
183-
e.stopPropagation();
184205
provider.onKeyDown?.(e);
206+
// e.stopPropagation();
185207
}));
186208
disposableStore.add(this._editorWidget.onKeyDown((e) => {
187-
if (e.keyCode === KeyCode.KeyH && provider.options.readMoreUrl) {
209+
if (e.keyCode === KeyCode.Escape) {
210+
e.stopPropagation();
211+
this._contextViewService.hideContextView();
212+
// Delay to allow the context view to hide #186514
213+
setTimeout(() => provider.onClose(), 100);
214+
} else if (e.keyCode === KeyCode.KeyH && provider.options.readMoreUrl) {
188215
const url: string = provider.options.readMoreUrl!;
189216
alert(AccessibilityHelpNLS.openingDocs);
190217
this._openerService.open(URI.parse(url));
@@ -226,4 +253,10 @@ export class AccessibleViewService extends Disposable implements IAccessibleView
226253
}
227254
this._accessibleView.show(provider);
228255
}
256+
next(): void {
257+
this._accessibleView?.next();
258+
}
259+
previous(): void {
260+
this._accessibleView?.previous();
261+
}
229262
}

src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ import { withNullAsUndefined } from 'vs/base/common/types';
1010
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
1111
import { ServicesAccessor } from 'vs/editor/browser/editorExtensions';
1212
import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat';
13-
import { AccessibleViewType, IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView';
1413
import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController';
15-
16-
14+
import { AccessibleViewType, IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView';
1715

1816
export function getAccessibilityHelpText(accessor: ServicesAccessor, type: 'panelChat' | 'inlineChat'): string {
1917
const keybindingService = accessor.get(IKeybindingService);

src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
2020
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
2121
import { FloatingClickWidget } from 'vs/workbench/browser/codeeditor';
2222
import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibilityContribution';
23-
import { AccessibleViewType, IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView';
2423
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
24+
import { AccessibleViewType, IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView';
2525

2626
const enum WidgetState {
2727
Hidden,

0 commit comments

Comments
 (0)