diff --git a/src/vs/platform/accessibility/browser/accessibleNotificationService.ts b/src/vs/platform/accessibility/browser/accessibleNotificationService.ts index 28986f44188..6fc2bac7dab 100644 --- a/src/vs/platform/accessibility/browser/accessibleNotificationService.ts +++ b/src/vs/platform/accessibility/browser/accessibleNotificationService.ts @@ -5,26 +5,41 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; -import { IAccessibilityService, IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; +import { AccessibleNotificationEvent, IAccessibilityService, IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export class AccessibleNotificationService extends Disposable implements IAccessibleNotificationService { declare readonly _serviceBrand: undefined; - + private _events: Map = new Map(); constructor( @IAudioCueService private readonly _audioCueService: IAudioCueService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IAccessibilityService private readonly _accessibilityService: IAccessibilityService) { super(); + this._events.set(AccessibleNotificationEvent.Clear, { audioCue: AudioCue.clear, alertMessage: localize('cleared', "Cleared") }); + this._events.set(AccessibleNotificationEvent.Save, { audioCue: AudioCue.save, alertMessage: localize('saved', "Saved") }); } - notifyCleared(): void { - const audioCueValue = this._configurationService.getValue(AudioCue.clear.settingsKey); + notify(event: AccessibleNotificationEvent): void { + const { audioCue, alertMessage } = this._events.get(event)!; + const audioCueValue = this._configurationService.getValue(audioCue.settingsKey); if (audioCueValue === 'on' || audioCueValue === 'auto' && this._accessibilityService.isScreenReaderOptimized()) { - this._audioCueService.playAudioCue(AudioCue.clear); + this._audioCueService.playAudioCue(audioCue); } else { - alert(localize('cleared', "Cleared")); + alert(alertMessage); + } + } + notifySaved(userGesture: boolean): void { + const { audioCue, alertMessage } = this._events.get(AccessibleNotificationEvent.Save)!; + const audioCueSetting = this._configurationService.getValue(audioCue.settingsKey); + if (audioCueSetting === 'off') { + alert(alertMessage); + return; + } else if (audioCueSetting === 'always') { + this._audioCueService.playAudioCue(audioCue); + } else if (audioCueSetting === 'userGesture' && userGesture) { + this._audioCueService.playAudioCue(audioCue); } } } @@ -33,5 +48,6 @@ export class TestAccessibleNotificationService extends Disposable implements IAc declare readonly _serviceBrand: undefined; - notifyCleared(): void { } + notify(event: AccessibleNotificationEvent): void { } + notifySaved(userGesture: boolean): void { } } diff --git a/src/vs/platform/accessibility/common/accessibility.ts b/src/vs/platform/accessibility/common/accessibility.ts index d8b0df4eb0c..78b0ff84ee5 100644 --- a/src/vs/platform/accessibility/common/accessibility.ts +++ b/src/vs/platform/accessibility/common/accessibility.ts @@ -55,6 +55,12 @@ export const IAccessibleNotificationService = createDecorator(ConfigurationExtensions.Configuration).regis ...audioCueFeatureBase, default: 'off' }, + 'audioCues.save': { + 'description': localize('audioCues.save', "Plays a sound when a file is saved."), + 'type': 'string', + 'enum': ['userGesture', 'always', 'off'], + 'default': 'off', + 'enumDescriptions': [ + localize('audioCues.enabled.userGesture', "Plays the audio cue when a user explicitly saves a file."), + localize('audioCues.enabled.always', "Plays the audio cue whenever a file is saved, including auto save."), + localize('audioCues.enabled.off', "Disable audio cue.") + ], + tags: ['accessibility'] + }, }, }); diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts index 21cd019327c..ebd4ed9b647 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts @@ -7,7 +7,7 @@ import { Codicon } from 'vs/base/common/codicons'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { localize } from 'vs/nls'; -import { IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; +import { AccessibleNotificationEvent, IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; import { Action2, IAction2Options, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -118,5 +118,5 @@ export function getClearAction(viewId: string, providerId: string) { } function announceChatCleared(accessor: ServicesAccessor): void { - accessor.get(IAccessibleNotificationService).notifyCleared(); + accessor.get(IAccessibleNotificationService).notify(AccessibleNotificationEvent.Clear); } diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 5db4b41197d..7be4e8eb0bd 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -69,7 +69,7 @@ import { Variable } from 'vs/workbench/contrib/debug/common/debugModel'; import { ReplEvaluationResult, ReplGroup } from 'vs/workbench/contrib/debug/common/replModel'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { registerNavigableContainer } from 'vs/workbench/browser/actions/widgetNavigationCommands'; -import { IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; +import { AccessibleNotificationEvent, IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; const $ = dom.$; @@ -978,7 +978,7 @@ registerAction2(class extends ViewAction { runInView(_accessor: ServicesAccessor, view: Repl): void { const accessibleNotificationService = _accessor.get(IAccessibleNotificationService); view.clearRepl(); - accessibleNotificationService.notifyCleared(); + accessibleNotificationService.notify(AccessibleNotificationEvent.Clear); } }); diff --git a/src/vs/workbench/contrib/output/browser/output.contribution.ts b/src/vs/workbench/contrib/output/browser/output.contribution.ts index c03fe479393..358b67f43e6 100644 --- a/src/vs/workbench/contrib/output/browser/output.contribution.ts +++ b/src/vs/workbench/contrib/output/browser/output.contribution.ts @@ -28,7 +28,7 @@ import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { Disposable, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; -import { IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; +import { AccessibleNotificationEvent, IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; // Register Service registerSingleton(IOutputService, OutputService, InstantiationType.Delayed); @@ -225,7 +225,7 @@ class OutputContribution extends Disposable implements IWorkbenchContribution { const activeChannel = outputService.getActiveChannel(); if (activeChannel) { activeChannel.clear(); - accessibleNotificationService.notifyCleared(); + accessibleNotificationService.notify(AccessibleNotificationEvent.Clear); } } })); diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index 1bb7bf8f194..3b3d418f5d4 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -43,7 +43,7 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService import { debounce } from 'vs/base/common/decorators'; import { MouseWheelClassifier } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { IMouseWheelEvent, StandardWheelEvent } from 'vs/base/browser/mouseEvent'; -import { IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; +import { AccessibleNotificationEvent, IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; const enum RenderConstants { /** @@ -590,7 +590,7 @@ export class XtermTerminal extends Disposable implements IXtermTerminal, IDetach // the prompt being written this._capabilities.get(TerminalCapability.CommandDetection)?.handlePromptStart(); this._capabilities.get(TerminalCapability.CommandDetection)?.handleCommandStart(); - this._accessibleNotificationService.notifyCleared(); + this._accessibleNotificationService.notify(AccessibleNotificationEvent.Clear); } hasSelection(): boolean { diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index dde1c5723e5..3f8e21a8300 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -33,6 +33,7 @@ import { IWorkspaceTrustRequestService, WorkspaceTrustUriResponse } from 'vs/pla import { IHostService } from 'vs/workbench/services/host/browser/host'; import { findGroup } from 'vs/workbench/services/editor/common/editorGroupFinder'; import { ITextEditorService } from 'vs/workbench/services/textfile/common/textEditorService'; +import { IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; export class EditorService extends Disposable implements EditorServiceImpl { @@ -70,7 +71,8 @@ export class EditorService extends Disposable implements EditorServiceImpl { @IEditorResolverService private readonly editorResolverService: IEditorResolverService, @IWorkspaceTrustRequestService private readonly workspaceTrustRequestService: IWorkspaceTrustRequestService, @IHostService private readonly hostService: IHostService, - @ITextEditorService private readonly textEditorService: ITextEditorService + @ITextEditorService private readonly textEditorService: ITextEditorService, + @IAccessibleNotificationService private readonly accessibleNotificationService: IAccessibleNotificationService ) { super(); @@ -972,9 +974,12 @@ export class EditorService extends Disposable implements EditorServiceImpl { } } } - + const success = saveResults.every(result => !!result); + if (success) { + this.accessibleNotificationService.notifySaved(options?.reason === SaveReason.EXPLICIT); + } return { - success: saveResults.every(result => !!result), + success, editors: coalesce(saveResults) }; }