Skip to content

Commit

Permalink
Merge pull request #189681 from microsoft/merogge/fix-hover-hint
Browse files Browse the repository at this point in the history
add accessible view hint to hover aria labels instead of using `status`, provide way to disable the hint
  • Loading branch information
meganrogge authored Aug 4, 2023
2 parents 90fb257 + 659356f commit 1606401
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 42 deletions.
5 changes: 5 additions & 0 deletions src/vs/base/browser/ui/hover/hoverWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableEle
import { KeyCode } from 'vs/base/common/keyCodes';
import { Disposable } from 'vs/base/common/lifecycle';
import 'vs/css!./hover';
import { localize } from 'vs/nls';

const $ = dom.$;

Expand Down Expand Up @@ -94,3 +95,7 @@ export class HoverAction extends Disposable {
}
}
}

export function getHoverAriaLabel(shouldHaveHint?: boolean, keybinding?: string | null): string | undefined {
return shouldHaveHint ? localize('acessibleViewHint', "Inspect this in the accessible view with {0}.", keybinding) : localize('acessibleViewHintNoKbOpen', "Inspect this in the accessible view via the command Open Accessible View which is currently not triggerable via keybinding.");
}
12 changes: 10 additions & 2 deletions src/vs/editor/contrib/hover/browser/contentHover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import * as dom from 'vs/base/browser/dom';
import { HoverAction, HoverWidget } from 'vs/base/browser/ui/hover/hoverWidget';
import { HoverAction, HoverWidget, getHoverAriaLabel } from 'vs/base/browser/ui/hover/hoverWidget';
import { coalesce } from 'vs/base/common/arrays';
import { CancellationToken } from 'vs/base/common/cancellation';
import { KeyCode } from 'vs/base/common/keyCodes';
Expand All @@ -24,6 +24,9 @@ import { AsyncIterableObject } from 'vs/base/common/async';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ResizableContentWidget } from 'vs/editor/contrib/hover/browser/resizableContentWidget';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';

const $ = dom.$;

export class ContentHoverController extends Disposable {
Expand Down Expand Up @@ -497,7 +500,10 @@ export class ContentHoverWidget extends ResizableContentWidget {
constructor(
editor: ICodeEditor,
minimumSize: dom.Dimension,
@IContextKeyService contextKeyService: IContextKeyService
@IContextKeyService contextKeyService: IContextKeyService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
@IAccessibilityService private readonly _accessibilityService: IAccessibilityService,
@IKeybindingService private readonly _keybindingService: IKeybindingService
) {
super(editor, minimumSize);
this._hoverVisibleKey = EditorContextKeys.hoverVisible.bindTo(contextKeyService);
Expand All @@ -522,6 +528,8 @@ export class ContentHoverWidget extends ResizableContentWidget {
this._setHoverData(undefined);
this._layout();
this._editor.addContentWidget(this);

this._hover.containerDomNode.ariaLabel = getHoverAriaLabel(this._configurationService.getValue('accessibility.verbosity.hover') === true && this._accessibilityService.isScreenReaderOptimized(), this._keybindingService.lookupKeybinding('editor.action.accessibleView')?.getAriaLabel()) ?? '';
}

public override dispose(): void {
Expand Down
12 changes: 1 addition & 11 deletions src/vs/editor/contrib/hover/browser/hover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { GotoDefinitionAtPositionEditorContribution } from 'vs/editor/contrib/go
import { HoverStartMode, HoverStartSource } from 'vs/editor/contrib/hover/browser/hoverOperation';
import { ContentHoverWidget, ContentHoverController } from 'vs/editor/contrib/hover/browser/contentHover';
import { MarginHoverWidget } from 'vs/editor/contrib/hover/browser/marginHover';
import { AccessibilitySupport, IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IOpenerService } from 'vs/platform/opener/common/opener';
Expand All @@ -31,8 +31,6 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ResultKind } from 'vs/platform/keybinding/common/keybindingResolver';
import * as nls from 'vs/nls';
import 'vs/css!./hover';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { status } from 'vs/base/browser/ui/aria/aria';

// sticky hover widget which doesn't disappear on focus out and such
const _sticky = false
Expand Down Expand Up @@ -363,9 +361,6 @@ class ShowOrFocusHoverAction extends EditorAction {
}

public run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void {
const configurationService = accessor.get(IConfigurationService);
const accessibilityService = accessor.get(IAccessibilityService);
const keybindingService = accessor.get(IKeybindingService);
if (!editor.hasModel()) {
return;
}
Expand All @@ -382,11 +377,6 @@ class ShowOrFocusHoverAction extends EditorAction {
} else {
controller.showContentHover(range, HoverStartMode.Immediate, HoverStartSource.Keyboard, focus);
}
if (configurationService.getValue('accessibility.verbosity.hover') && accessibilityService.isScreenReaderOptimized()) {
const keybinding = keybindingService.lookupKeybinding('editor.action.accessibleView')?.getAriaLabel();
const hint = keybinding ? nls.localize('chatAccessibleViewHint', "Inspect this in the accessible view with {0}", keybinding) : nls.localize('chatAccessibleViewHintNoKb', "Inspect this in the accessible view via the command Open Accessible View which is currently not triggerable via keybinding");
status(hint);
}
}
}

Expand Down
115 changes: 89 additions & 26 deletions src/vs/workbench/contrib/accessibility/browser/accessibleView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { AccessibilityHelpNLS } from 'vs/editor/common/standaloneStrings';
import { localize } from 'vs/nls';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ContextKeyExpr, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextViewDelegate, IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IOpenerService } from 'vs/platform/opener/common/opener';
Expand All @@ -33,6 +33,30 @@ import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { IPickerQuickAccessItem } from 'vs/platform/quickinput/browser/pickerQuickAccess';
import { marked } from 'vs/base/common/marked/marked';


class AccessibleViewDisableHintAction extends Action2 {
constructor() {
super({
id: 'editor.action.accessibleViewDisableHint',
keybinding: {
when: ContextKeyExpr.or(accessibleViewIsShown, accessibilityHelpIsShown),
primary: KeyMod.Alt | KeyCode.F6,
weight: KeybindingWeight.WorkbenchContrib
},
menu: [{
id: MenuId.CommandPalette,
group: '',
order: 1
}],
title: localize('editor.action.accessibleViewDisableHint', "Disable Accessible View Hint")
});
}
run(accessor: ServicesAccessor): void {
accessor.get(IAccessibleViewService).disableHint();
}
}
registerAction2(AccessibleViewDisableHintAction);

const enum DIMENSIONS {
MAX_WIDTH = 600
}
Expand All @@ -59,6 +83,7 @@ export interface IAccessibleViewService {
next(): void;
previous(): void;
goToSymbol(): void;
disableHint(): void;
/**
* If the setting is enabled, provides the open accessible view hint as a localized string.
* @param verbositySettingKey The setting key for the verbosity of the feature
Expand Down Expand Up @@ -129,7 +154,7 @@ class AccessibleView extends Disposable {
}
}));
this._register(this._configurationService.onDidChangeConfiguration(e => {
if (this._currentProvider && this._accessiblityHelpIsShown.get() && e.affectsConfiguration(`accessibility.verbosity.${this._currentProvider.verbositySettingKey}`)) {
if (this._currentProvider && this._accessiblityHelpIsShown.get() && e.affectsConfiguration(this._currentProvider.verbositySettingKey)) {
this.show(this._currentProvider);
}
}));
Expand All @@ -142,6 +167,11 @@ class AccessibleView extends Disposable {
if (!provider) {
return;
}
if (provider.options.type === AccessibleViewType.Help) {
this._accessiblityHelpIsShown.set(true);
} else {
this._accessibleViewIsShown.set(true);
}
const delegate: IContextViewDelegate = {
getAnchor: () => { return { x: (window.innerWidth / 2) - ((Math.min(this._layoutService.dimension.width * 0.62 /* golden cut */, DIMENSIONS.MAX_WIDTH)) / 2), y: this._layoutService.offset.quickPickTop }; },
render: (container) => {
Expand All @@ -158,11 +188,6 @@ class AccessibleView extends Disposable {
}
};
this._contextViewService.showContextView(delegate);
if (provider.options.type === AccessibleViewType.Help) {
this._accessiblityHelpIsShown.set(true);
} else {
this._accessibleViewIsShown.set(true);
}
if (symbol && this._currentProvider) {
this.showSymbol(this._currentProvider, symbol);
}
Expand Down Expand Up @@ -228,12 +253,29 @@ class AccessibleView extends Disposable {
}
}

disableHint(): void {
if (!this._currentProvider) {
return;
}
this._configurationService.updateValue(this._currentProvider?.verbositySettingKey, false);
alert(localize('disableAccessibilityHelp', '{0} accessibility verbosity is now disabled', this._currentProvider.verbositySettingKey));
}

private _render(provider: IAccessibleContentProvider, container: HTMLElement): IDisposable {
this._currentProvider = provider;
const settingKey = `accessibility.verbosity.${provider.verbositySettingKey}`;
const value = this._configurationService.getValue(settingKey);
const value = this._configurationService.getValue(provider.verbositySettingKey);
const readMoreLink = provider.options.readMoreUrl ? localize("openDoc", "\nPress H now to open a browser window with more information related to accessibility.\n") : '';
const disableHelpHint = provider.options.type === AccessibleViewType.Help && !!value ? localize('disable-help-hint', '\nTo disable the `accessibility.verbosity` hint for this feature, press D now.\n') : '\n';
const disableHintKb = this._keybindingService.lookupKeybinding('editor.action.accessibleViewDisableHint')?.getAriaLabel();
let disableHelpHint;
if (provider.options.type === AccessibleViewType.Help && !!value) {
if (disableHintKb) {
disableHelpHint = localize('disable-help-hint', '\nTo disable the `accessibility.verbosity` hint for this feature, press {0} now.\n', disableHintKb);
} else {
disableHelpHint = localize('disable-help-hint-no-b', '\nTo disable the `accessibility.verbosity` hint for this feature, assign a keybinding to the Disable Accessible View Hint command, which is currently not triggerable via keybinding.\n');
}
} else {
disableHelpHint = '\n';
}
const accessibilitySupport = this._accessibilityService.isScreenReaderOptimized();
let message = '';
if (provider.options.type === AccessibleViewType.Help) {
Expand Down Expand Up @@ -264,15 +306,24 @@ class AccessibleView extends Disposable {
}
model.setLanguage(provider.options.language ?? 'markdown');
container.appendChild(this._editorContainer);
this._editorWidget.updateOptions({ ariaLabel: provider.next && provider.previous ? localize('accessibleViewAriaLabelWithNav', "{0} {1}", provider.options.ariaLabel, this._getNavigationAriaHint(provider.verbositySettingKey)) : localize('accessibleViewAriaLabel', "{0}", provider.options.ariaLabel) });
let ariaLabel = undefined;
const label = provider.options.ariaLabel;
const navigationHint = this._getNavigationAriaHint(provider.verbositySettingKey);
const disableHint = this._getDisableVerbosityHint(provider.verbositySettingKey);
if (label && navigationHint && disableHint) {
ariaLabel = localize('ariaLabelAll', '{0}, {1}, {2}', label, navigationHint, disableHint);
} else if (label && navigationHint) {
ariaLabel = localize('ariaLabelLabelNavigation', '{0}, {1}', label, navigationHint);
} else if (label && disableHint) {
ariaLabel = localize('ariaLabelLabelDisable', '{0}, {1}', label, disableHint);
} else if (navigationHint && disableHint) {
ariaLabel = localize('ariaLabelNavigationDisable', '{0}, {1}', navigationHint, disableHint);
}
this._editorWidget.updateOptions({ ariaLabel });
this._editorWidget.focus();
});
const disposableStore = new DisposableStore();
disposableStore.add(this._editorWidget.onKeyUp((e) => {
if (e.keyCode === KeyCode.KeyD && this._configurationService.getValue(settingKey)) {
alert(localize('disableAccessibilityHelp', '{0} accessibility verbosity is now disabled', provider.verbositySettingKey));
this._configurationService.updateValue(settingKey, false);
}
provider.onKeyDown?.(e);
}));
disposableStore.add(this._editorWidget.onKeyDown((e) => {
Expand Down Expand Up @@ -313,13 +364,20 @@ class AccessibleView extends Disposable {

private _getNavigationAriaHint(verbositySettingKey: AccessibilityVerbositySettingId): string {
let hint = '';
const nextKeybinding = this._keybindingService.lookupKeybinding(AccessibleViewNextAction.id)?.getAriaLabel();
const previousKeybinding = this._keybindingService.lookupKeybinding(AccessibleViewPreviousAction.id)?.getAriaLabel();
const nextKeybinding = this._keybindingService.lookupKeybinding('editor.action.accessibleViewNext')?.getAriaLabel();
const previousKeybinding = this._keybindingService.lookupKeybinding('editor.action.accessibleViewPrevious')?.getAriaLabel();
if (this._configurationService.getValue(verbositySettingKey)) {
hint = (nextKeybinding && previousKeybinding) ? localize('chatAccessibleViewNextPreviousHint', "Show the next {0} or previous {1} item in the accessible view", nextKeybinding, previousKeybinding) : localize('chatAccessibleViewNextPreviousHintNoKb', "Show the next or previous item in the accessible view by configuring keybindings for Show Next / Previous in Accessible View");
hint = (nextKeybinding && previousKeybinding) ? localize('accessibleViewNextPreviousHint', "Show the next ({0}) or previous ({1}) item in the accessible view", nextKeybinding, previousKeybinding) : localize('chatAccessibleViewNextPreviousHintNoKb', "Show the next or previous item in the accessible view by configuring keybindings for Show Next / Previous in Accessible View");
}
return hint;
}
private _getDisableVerbosityHint(verbositySettingKey: AccessibilityVerbositySettingId): string {
if (!this._configurationService.getValue(verbositySettingKey)) {
return '';
}
const disableKeybinding = this._keybindingService.lookupKeybinding('editor.action.accessibleViewDisableHint', this._contextKeyService)?.getAriaLabel();
return !!disableKeybinding ? localize('acessibleViewDisableHint', "Disable the hint to open the accessible view by pressing ({0}).", disableKeybinding) : localize('accessibleViewDisableHintNoKb', 'Add a keybinding for the command Disable Accessible View Hint to disable this hint."');
}
}

export class AccessibleViewService extends Disposable implements IAccessibleViewService {
Expand Down Expand Up @@ -355,13 +413,20 @@ export class AccessibleViewService extends Disposable implements IAccessibleView
return null;
}
const keybinding = this._keybindingService.lookupKeybinding(AccessibleViewAction.id)?.getAriaLabel();
return keybinding ? localize('chatAccessibleViewHint', "Inspect this in the accessible view with {0}", keybinding) : localize('chatAccessibleViewHintNoKb', "Inspect this in the accessible view via the command Open Accessible View which is currently not triggerable via keybinding");
let hint = null;
if (keybinding) {
hint = localize('acessibleViewHint', "Inspect this in the accessible view with {0}", keybinding);
} else {
hint = localize('acessibleViewHintNoKbEither', "Inspect this in the accessible view via the command Open Accessible View which is currently not triggerable via keybinding.");
}
return hint;
}
disableHint(): void {
this._accessibleView?.disableHint();
}
}


class AccessibleViewNextAction extends Action2 {
static id: 'editor.action.accessibleViewNext';
constructor() {
super({
id: 'editor.action.accessibleViewNext',
Expand All @@ -378,15 +443,14 @@ class AccessibleViewNextAction extends Action2 {
title: localize('editor.action.accessibleViewNext', "Show Next in Accessible View")
});
}
run(accessor: ServicesAccessor, ...args: unknown[]): void {
run(accessor: ServicesAccessor): void {
accessor.get(IAccessibleViewService).next();
}
}
registerAction2(AccessibleViewNextAction);


class AccessibleViewGoToSymbolAction extends Action2 {
static id: 'editor.action.accessibleViewGoToSymbol';
constructor() {
super({
id: 'editor.action.accessibleViewGoToSymbol',
Expand All @@ -403,14 +467,13 @@ class AccessibleViewGoToSymbolAction extends Action2 {
title: localize('editor.action.accessibleViewGoToSymbol', "Go To Symbol in Accessible View")
});
}
run(accessor: ServicesAccessor, ...args: unknown[]): void {
run(accessor: ServicesAccessor): void {
accessor.get(IAccessibleViewService).goToSymbol();
}
}
registerAction2(AccessibleViewGoToSymbolAction);

class AccessibleViewPreviousAction extends Action2 {
static id: 'editor.action.accessibleViewPrevious';
constructor() {
super({
id: 'editor.action.accessibleViewPrevious',
Expand All @@ -427,7 +490,7 @@ class AccessibleViewPreviousAction extends Action2 {
title: localize('editor.action.accessibleViewPrevious', "Show Previous in Accessible View")
});
}
run(accessor: ServicesAccessor, ...args: unknown[]): void {
run(accessor: ServicesAccessor): void {
accessor.get(IAccessibleViewService).previous();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export async function runAccessibilityHelpAction(accessor: ServicesAccessor, edi
inputEditor.getSupportedActions();
const helpText = getAccessibilityHelpText(accessor, type);
accessibleViewService.show({
verbositySettingKey: type as AccessibilityVerbositySettingId,
verbositySettingKey: type === 'panelChat' ? AccessibilityVerbositySettingId.Chat : AccessibilityVerbositySettingId.InlineChat,
provideContent: () => helpText,
onClose: () => {
if (type === 'panelChat' && cachedPosition) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/termin
import type { Terminal } from 'xterm';

export const enum ClassName {
AccessibleBuffer = 'terminal-accessibility-help',
Active = 'active',
EditorTextArea = 'textarea'
}
Expand Down
Loading

0 comments on commit 1606401

Please sign in to comment.