Skip to content

Commit

Permalink
Adopt custom hover in settings and keybindings editor (#206440)
Browse files Browse the repository at this point in the history
* adopt custom hover in settings and keybindings editor

* fiy smoketests
  • Loading branch information
benibenj authored Feb 28, 2024
1 parent 85a2664 commit cff275a
Show file tree
Hide file tree
Showing 23 changed files with 127 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ export class HighlightedLabel extends Disposable {
} else {
if (!this.customHover && this.title !== '') {
const hoverDelegate = this.options?.hoverDelegate ?? getDefaultHoverDelegate('mouse');
this.customHover = this._store.add(setupCustomHover(hoverDelegate, this.domNode, this.title));
this.customHover = this._register(setupCustomHover(hoverDelegate, this.domNode, this.title));
} else if (this.customHover) {
this.customHover.update(this.title);
}
Expand Down
17 changes: 15 additions & 2 deletions src/vs/base/browser/ui/iconLabel/simpleIconLabel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@
*--------------------------------------------------------------------------------------------*/

import { reset } from 'vs/base/browser/dom';
import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory';
import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget';
import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels';
import { IDisposable } from 'vs/base/common/lifecycle';

export class SimpleIconLabel {
export class SimpleIconLabel implements IDisposable {

private hover?: ICustomHover;

constructor(
private readonly _container: HTMLElement
Expand All @@ -17,6 +22,14 @@ export class SimpleIconLabel {
}

set title(title: string) {
this._container.title = title;
if (!this.hover && title) {
this.hover = setupCustomHover(getDefaultHoverDelegate('mouse'), this._container, title);
} else if (this.hover) {
this.hover.update(title);
}
}

dispose(): void {
this.hover?.dispose();
}
}
17 changes: 11 additions & 6 deletions src/vs/base/browser/ui/keybindingLabel/keybindingLabel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
*--------------------------------------------------------------------------------------------*/

import * as dom from 'vs/base/browser/dom';
import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory';
import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget';
import { UILabelProvider } from 'vs/base/common/keybindingLabels';
import { ResolvedKeybinding, ResolvedChord } from 'vs/base/common/keybindings';
import { Disposable } from 'vs/base/common/lifecycle';
import { equals } from 'vs/base/common/objects';
import { OperatingSystem } from 'vs/base/common/platform';
import 'vs/css!./keybindingLabel';
Expand Down Expand Up @@ -50,18 +53,21 @@ export const unthemedKeybindingLabelOptions: KeybindingLabelOptions = {
keybindingLabelShadow: undefined
};

export class KeybindingLabel {
export class KeybindingLabel extends Disposable {

private domNode: HTMLElement;
private options: KeybindingLabelOptions;

private readonly keyElements = new Set<HTMLSpanElement>();

private hover: ICustomHover;
private keybinding: ResolvedKeybinding | undefined;
private matches: Matches | undefined;
private didEverRender: boolean;

constructor(container: HTMLElement, private os: OperatingSystem, options?: KeybindingLabelOptions) {
super();

this.options = options || Object.create(null);

const labelForeground = this.options.keybindingLabelForeground;
Expand All @@ -71,6 +77,8 @@ export class KeybindingLabel {
this.domNode.style.color = labelForeground;
}

this.hover = this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), this.domNode, ''));

this.didEverRender = false;
container.appendChild(this.domNode);
}
Expand Down Expand Up @@ -102,11 +110,8 @@ export class KeybindingLabel {
this.renderChord(this.domNode, chords[i], this.matches ? this.matches.chordPart : null);
}
const title = (this.options.disableTitle ?? false) ? undefined : this.keybinding.getAriaLabel() || undefined;
if (title !== undefined) {
this.domNode.title = title;
} else {
this.domNode.removeAttribute('title');
}
this.hover.update(title);
this.domNode.setAttribute('aria-label', title || '');
} else if (this.options && this.options.renderUnboundKeybindings) {
this.renderUnbound(this.domNode);
}
Expand Down
1 change: 1 addition & 0 deletions src/vs/base/browser/ui/toggle/toggle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export class ToggleActionViewItem extends BaseActionViewItem {
inputActiveOptionBackground: options.toggleStyles?.inputActiveOptionBackground,
inputActiveOptionBorder: options.toggleStyles?.inputActiveOptionBorder,
inputActiveOptionForeground: options.toggleStyles?.inputActiveOptionForeground,
hoverDelegate: options.hoverDelegate
}));
this._register(this.toggle.onChange(() => this._action.checked = !!this.toggle && this.toggle.checked));
}
Expand Down
1 change: 1 addition & 0 deletions src/vs/editor/contrib/find/browser/findWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1051,6 +1051,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL
icon: findSelectionIcon,
title: NLS_TOGGLE_SELECTION_FIND_TITLE + this._keybindingLabelFor(FIND_IDS.ToggleSearchScopeCommand),
isChecked: false,
hoverDelegate: hoverDelegate,
inputActiveOptionBackground: asCssVariable(inputActiveOptionBackground),
inputActiveOptionBorder: asCssVariable(inputActiveOptionBorder),
inputActiveOptionForeground: asCssVariable(inputActiveOptionForeground),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ class StatusBarViewItem extends MenuEntryActionViewItem {
if (this.label) {
const div = h('div.keybinding').root;

const k = new KeybindingLabel(div, OS, { disableTitle: true, ...unthemedKeybindingLabelOptions });
const k = this._register(new KeybindingLabel(div, OS, { disableTitle: true, ...unthemedKeybindingLabelOptions }));
k.set(kb);
this.label.textContent = this._action.label;
this.label.appendChild(div);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ class StatusBarViewItem extends MenuEntryActionViewItem {
if (this.label) {
const div = h('div.keybinding').root;

const k = new KeybindingLabel(div, OS, { disableTitle: true, ...unthemedKeybindingLabelOptions });
const k = this._register(new KeybindingLabel(div, OS, { disableTitle: true, ...unthemedKeybindingLabelOptions }));
k.set(kb);
this.label.textContent = this._action.label;
this.label.appendChild(div);
Expand Down
2 changes: 1 addition & 1 deletion src/vs/platform/actionWidget/browser/actionList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ class ActionItemRenderer<T> implements IListRenderer<IActionListItem<T>, IAction
}

disposeTemplate(_templateData: IActionMenuTemplateData): void {
// noop
_templateData.keybinding.dispose();
}
}

Expand Down
1 change: 1 addition & 0 deletions src/vs/platform/quickinput/browser/quickInputList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ class ListElementRenderer implements IListRenderer<IListElement, IListElementTem
// Keybinding
const keybindingContainer = dom.append(row1, $('.quick-input-list-entry-keybinding'));
data.keybinding = new KeybindingLabel(keybindingContainer, platform.OS);
data.toDisposeTemplate.push(data.keybinding);

// Detail
const detailContainer = dom.append(row2, $('.quick-input-list-label-meta'));
Expand Down
7 changes: 5 additions & 2 deletions src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export class EditorGroupWatermark extends Disposable {
private readonly transientDisposables = this._register(new DisposableStore());
private enabled: boolean = false;
private workbenchState: WorkbenchState;
private keybindingLabel?: KeybindingLabel;

constructor(
container: HTMLElement,
Expand Down Expand Up @@ -145,8 +146,9 @@ export class EditorGroupWatermark extends Disposable {
const dt = append(dl, $('dt'));
dt.textContent = entry.text;
const dd = append(dl, $('dd'));
const keybinding = new KeybindingLabel(dd, OS, { renderUnboundKeybindings: true, ...defaultKeybindingLabelStyles });
keybinding.set(keys);
this.keybindingLabel?.dispose();
this.keybindingLabel = new KeybindingLabel(dd, OS, { renderUnboundKeybindings: true, ...defaultKeybindingLabelStyles });
this.keybindingLabel.set(keys);
}
};

Expand All @@ -162,5 +164,6 @@ export class EditorGroupWatermark extends Disposable {
override dispose(): void {
super.dispose();
this.clear();
this.keybindingLabel?.dispose();
}
}
2 changes: 1 addition & 1 deletion src/vs/workbench/browser/parts/editor/editorPlaceholder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export abstract class EditorPlaceholder extends EditorPane {

// Icon
const iconContainer = container.appendChild($('.editor-placeholder-icon-container'));
const iconWidget = new SimpleIconLabel(iconContainer);
const iconWidget = disposables.add(new SimpleIconLabel(iconContainer));
iconWidget.text = icon;

// Label
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/browser/parts/statusbar/statusbarItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export class StatusbarEntryItem extends Disposable {
this._register(Gesture.addTarget(this.labelContainer)); // enable touch

// Label (with support for progress)
this.label = new StatusBarCodiconLabel(this.labelContainer);
this.label = this._register(new StatusBarCodiconLabel(this.labelContainer));
this.container.appendChild(this.labelContainer);

// Beak Container
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions, IConfigurationMigrationRegistry } from 'vs/workbench/common/configuration';
import { LOG_MODE_ID, OUTPUT_MODE_ID } from 'vs/workbench/services/output/common/output';
import { SEARCH_RESULT_LANGUAGE_ID } from 'vs/workbench/services/search/common/search';
import { setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget';
import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory';

const $ = dom.$;

Expand Down Expand Up @@ -263,7 +265,7 @@ class EmptyTextEditorHintContentWidget implements IContentWidget {

hintElement.appendChild(before);

const label = new KeybindingLabel(hintElement, OS);
const label = hintHandler.disposables.add(new KeybindingLabel(hintElement, OS));
label.set(keybindingHint);
label.element.style.width = 'min-content';
label.element.style.display = 'inline';
Expand Down Expand Up @@ -382,7 +384,7 @@ class EmptyTextEditorHintContentWidget implements IContentWidget {
anchor.style.cursor = 'pointer';
const id = keybindingsLookup.shift();
const title = id && this.keybindingService.lookupKeybinding(id)?.getLabel();
anchor.title = title ?? '';
hintHandler.disposables.add(setupCustomHover(getDefaultHoverDelegate('mouse'), anchor, title ?? ''));
}

return { hintElement, ariaLabel };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { Disposable, DisposableStore, IDisposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { $, append, clearNode } from 'vs/base/browser/dom';
import { Emitter, Event } from 'vs/base/common/event';
import { ExtensionIdentifier, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
Expand Down Expand Up @@ -447,16 +447,18 @@ class ExtensionFeatureView extends Disposable {

private renderTableData(container: HTMLElement, renderer: IExtensionFeatureTableRenderer): void {
const tableData = this._register(renderer.render(this.manifest));
const tableDisposable = this._register(new MutableDisposable());
if (tableData.onDidChange) {
this._register(tableData.onDidChange(data => {
clearNode(container);
this.renderTable(data, container);
tableDisposable.value = this.renderTable(data, container);
}));
}
this.renderTable(tableData.data, container);
tableDisposable.value = this.renderTable(tableData.data, container);
}

private renderTable(tableData: ITableData, container: HTMLElement): void {
private renderTable(tableData: ITableData, container: HTMLElement): IDisposable {
const disposables = new DisposableStore();
append(container,
$('table', undefined,
$('tr', undefined,
Expand All @@ -478,7 +480,7 @@ class ExtensionFeatureView extends Disposable {
result.push(element);
} else if (item instanceof ResolvedKeybinding) {
const element = $('');
const kbl = new KeybindingLabel(element, OS, defaultKeybindingLabelStyles);
const kbl = disposables.add(new KeybindingLabel(element, OS, defaultKeybindingLabelStyles));
kbl.set(item);
result.push(element);
} else if (item instanceof Color) {
Expand All @@ -490,6 +492,7 @@ class ExtensionFeatureView extends Disposable {
})
);
})));
return disposables;
}

private renderMarkdownData(container: HTMLElement, renderer: IExtensionFeatureMarkdownRenderer): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ class CellStatusBarItem extends Disposable {
this._itemDisposables.clear();

if (!this._currentItem || this._currentItem.text !== item.text) {
new SimpleIconLabel(this.container).text = item.text.replace(/\n/g, ' ');
this._itemDisposables.add(new SimpleIconLabel(this.container)).text = item.text.replace(/\n/g, ' ');
}

const resolveColor = (color: ThemeColor | string) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ export class DefineKeybindingWidget extends Widget {
private _keybindingInputWidget: KeybindingsSearchWidget;
private _outputNode: HTMLElement;
private _showExistingKeybindingsNode: HTMLElement;
private _keybindingDisposables = this._register(new DisposableStore());

private _chords: ResolvedKeybinding[] | null = null;
private _isVisible: boolean = false;
Expand Down Expand Up @@ -238,17 +239,18 @@ export class DefineKeybindingWidget extends Widget {
}

private onKeybinding(keybinding: ResolvedKeybinding[] | null): void {
this._keybindingDisposables.clear();
this._chords = keybinding;
dom.clearNode(this._outputNode);
dom.clearNode(this._showExistingKeybindingsNode);

const firstLabel = new KeybindingLabel(this._outputNode, OS, defaultKeybindingLabelStyles);
const firstLabel = this._keybindingDisposables.add(new KeybindingLabel(this._outputNode, OS, defaultKeybindingLabelStyles));
firstLabel.set(this._chords?.[0] ?? undefined);

if (this._chords) {
for (let i = 1; i < this._chords.length; i++) {
this._outputNode.appendChild(document.createTextNode(nls.localize('defineKeybinding.chordsTo', "chord to")));
const chordLabel = new KeybindingLabel(this._outputNode, OS, defaultKeybindingLabelStyles);
const chordLabel = this._keybindingDisposables.add(new KeybindingLabel(this._outputNode, OS, defaultKeybindingLabelStyles));
chordLabel.set(this._chords[i]);
}
}
Expand Down
Loading

0 comments on commit cff275a

Please sign in to comment.