Skip to content

Commit

Permalink
Get tree tooltip when needed
Browse files Browse the repository at this point in the history
Part of #100741
  • Loading branch information
alexr00 committed Jun 23, 2020
1 parent 6493a67 commit 5d60a9c
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 18 deletions.
4 changes: 3 additions & 1 deletion src/vs/vscode.proposed.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1150,6 +1150,8 @@ declare module 'vscode' {

}

export type TreeTooltipProvider = () => Thenable<MarkdownString>;

export class TreeItem2 extends TreeItem {
/**
* Label describing this item. When `falsy`, it is derived from [resourceUri](#TreeItem.resourceUri).
Expand All @@ -1159,7 +1161,7 @@ declare module 'vscode' {
/**
* Content to be shown when you hover over the tree item.
*/
tooltip?: string | MarkdownString | /* for compilation */ any;
tooltip?: string | TreeTooltipProvider | /* for compilation */ any;

/**
* Accessibility information used when screen reader interacts with this tree item.
Expand Down
7 changes: 6 additions & 1 deletion src/vs/workbench/api/browser/mainThreadTreeViews.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import { Disposable } from 'vs/base/common/lifecycle';
import { ExtHostContext, MainThreadTreeViewsShape, ExtHostTreeViewsShape, MainContext, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
import { ITreeViewDataProvider, ITreeItem, IViewsService, ITreeView, IViewsRegistry, ITreeViewDescriptor, IRevealOptions, Extensions } from 'vs/workbench/common/views';
import { ITreeViewDataProvider, ITreeItem, IViewsService, ITreeView, IViewsRegistry, ITreeViewDescriptor, IRevealOptions, Extensions, TREE_TOOLTIP_PROVIDER } from 'vs/workbench/common/views';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { distinct } from 'vs/base/common/arrays';
import { INotificationService } from 'vs/platform/notification/common/notification';
Expand Down Expand Up @@ -219,6 +219,11 @@ class TreeViewDataProvider implements ITreeViewDataProvider {
if (elements) {
for (const element of elements) {
this.itemsMap.set(element.handle, element);
if (element.tooltip === TREE_TOOLTIP_PROVIDER) {
element.tooltip = () => {
return this._proxy.$getTooltip(this.treeViewId, element.handle);
};
}
result.push(element);
}
}
Expand Down
1 change: 1 addition & 0 deletions src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -987,6 +987,7 @@ export interface ExtHostTreeViewsShape {
$setExpanded(treeViewId: string, treeItemHandle: string, expanded: boolean): void;
$setSelection(treeViewId: string, treeItemHandles: string[]): void;
$setVisible(treeViewId: string, visible: boolean): void;
$getTooltip(treeViewId: string, treeItemHandle: string): Promise<IMarkdownString | undefined>;
}

export interface ExtHostWorkspaceShape {
Expand Down
44 changes: 35 additions & 9 deletions src/vs/workbench/api/common/extHostTreeViews.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
import { ExtHostTreeViewsShape, MainThreadTreeViewsShape } from './extHost.protocol';
import { ITreeItem, TreeViewItemHandleArg, ITreeItemLabel, IRevealOptions } from 'vs/workbench/common/views';
import { ITreeItem, TreeViewItemHandleArg, ITreeItemLabel, IRevealOptions, TreeTooltipProvider as TreeTooltipProvider, TREE_TOOLTIP_PROVIDER } from 'vs/workbench/common/views';
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/common/extHostCommands';
import { asPromise } from 'vs/base/common/async';
import { TreeItemCollapsibleState, ThemeIcon } from 'vs/workbench/api/common/extHostTypes';
Expand All @@ -19,8 +19,8 @@ import { equals, coalesce } from 'vs/base/common/arrays';
import { ILogService } from 'vs/platform/log/common/log';
import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { MarkdownString } from 'vs/workbench/api/common/extHostTypeConverters';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { MarkdownString } from 'vs/workbench/api/common/extHostTypeConverters';

type TreeItemHandle = string;

Expand Down Expand Up @@ -143,6 +143,14 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
treeView.setVisible(isVisible);
}

$getTooltip(treeViewId: string, treeItemHandle: string): Promise<IMarkdownString | undefined> {
const treeView = this.treeViews.get(treeViewId);
if (!treeView) {
throw new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId));
}
return treeView.getTooltipMarkdown(treeItemHandle);
}

private createExtHostTreeView<T>(id: string, options: vscode.TreeViewOptions<T>, extension: IExtensionDescription): ExtHostTreeView<T> {
const treeView = new ExtHostTreeView<T>(id, options, this._proxy, this.commands.converter, this.logService, extension);
this.treeViews.set(id, treeView);
Expand All @@ -160,6 +168,7 @@ type TreeData<T> = { message: boolean, element: T | Root | false };

interface TreeNode extends IDisposable {
item: ITreeItem;
tooltipProvider?: TreeTooltipProvider;
parent: TreeNode | Root;
children?: TreeNode[];
}
Expand Down Expand Up @@ -329,6 +338,17 @@ class ExtHostTreeView<T> extends Disposable {
}
}

async getTooltipMarkdown(treeItemHandle: string): Promise<IMarkdownString | undefined> {
const element = this.elements.get(treeItemHandle);
if (element) {
const node = this.nodes.get(element);
if (node && node.tooltipProvider) {
return node.tooltipProvider();
}
}
return undefined;
}

private resolveUnknownParentChain(element: T): Promise<TreeNode[]> {
return this.resolveParent(element)
.then((parent) => {
Expand Down Expand Up @@ -488,28 +508,33 @@ class ExtHostTreeView<T> extends Disposable {
return node;
}

private getTooltip(tooltip?: string | vscode.MarkdownString): string | IMarkdownString | undefined {
if (typeof tooltip === 'string') {
return tooltip;
} else if (tooltip === undefined) {
return undefined;
private getTooltip(tooltip?: string | vscode.TreeTooltipProvider): { tooltip: string | undefined, provider?: TreeTooltipProvider } {
if ((typeof tooltip === 'string') || (tooltip === undefined)) {
return { tooltip };
} else {
checkProposedApiEnabled(this.extension);
return MarkdownString.from(tooltip);
return {
tooltip: TREE_TOOLTIP_PROVIDER,
provider: async () => {
const resolved = await tooltip();
return MarkdownString.from(resolved);
}
};
}
}

private createTreeNode(element: T, extensionTreeItem: vscode.TreeItem2, parent: TreeNode | Root): TreeNode {
const disposable = new DisposableStore();
const handle = this.createHandle(element, extensionTreeItem, parent);
const icon = this.getLightIconPath(extensionTreeItem);
const tooltip = this.getTooltip(extensionTreeItem.tooltip);
const item = {
handle,
parentHandle: parent ? parent.item.handle : undefined,
label: toTreeItemLabel(extensionTreeItem.label, this.extension),
description: extensionTreeItem.description,
resourceUri: extensionTreeItem.resourceUri,
tooltip: this.getTooltip(extensionTreeItem.tooltip),
tooltip: tooltip.tooltip,
command: extensionTreeItem.command ? this.commands.toInternal(extensionTreeItem.command, disposable) : undefined,
contextValue: extensionTreeItem.contextValue,
icon,
Expand All @@ -521,6 +546,7 @@ class ExtHostTreeView<T> extends Disposable {

return {
item,
tooltipProvider: tooltip.provider,
parent,
children: undefined,
dispose(): void { disposable.dispose(); }
Expand Down
5 changes: 4 additions & 1 deletion src/vs/workbench/common/views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,9 @@ export interface ITreeItemLabel {

}

export type TreeTooltipProvider = () => Promise<IMarkdownString | undefined>;
export const TREE_TOOLTIP_PROVIDER = '__TREE_TOOLTIP_PROVIDER__';

export interface ITreeItem {

handle: string;
Expand All @@ -637,7 +640,7 @@ export interface ITreeItem {

resourceUri?: UriComponents;

tooltip?: string | IMarkdownString;
tooltip?: string | TreeTooltipProvider;

contextValue?: string;

Expand Down
12 changes: 6 additions & 6 deletions src/vs/workbench/contrib/views/browser/treeView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
import { IMenuService, MenuId, MenuItemAction, registerAction2, Action2 } from 'vs/platform/actions/common/actions';
import { ContextAwareMenuEntryActionViewItem, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { IContextKeyService, ContextKeyExpr, ContextKeyEqualsExpr, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { ITreeView, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeItemLabel, IViewDescriptorService, ViewContainer, ViewContainerLocation } from 'vs/workbench/common/views';
import { ITreeView, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeItemLabel, IViewDescriptorService, ViewContainer, ViewContainerLocation, TreeTooltipProvider } from 'vs/workbench/common/views';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IProgressService } from 'vs/platform/progress/common/progress';
Expand Down Expand Up @@ -788,11 +788,10 @@ class TreeRenderer extends Disposable implements ITreeRenderer<ITreeItem, FuzzyS
this.setupHovers(node.tooltip, templateData.container, disposableStore);
}

private setupHovers(tooltip: string | IMarkdownString | undefined, htmlElement: HTMLElement, disposableStore: DisposableStore): void {
private setupHovers(tooltip: string | TreeTooltipProvider | undefined, htmlElement: HTMLElement, disposableStore: DisposableStore): void {
if (!tooltip || isString(tooltip)) {
return;
}
const text: IMarkdownString = tooltip;
const hoverService = this.hoverService;
const hoverDelay = this.hoverDelay;
function mouseOver(this: HTMLElement, e: MouseEvent): any {
Expand All @@ -801,9 +800,10 @@ class TreeRenderer extends Disposable implements ITreeRenderer<ITreeItem, FuzzyS
isHovering = false;
}
this.addEventListener(DOM.EventType.MOUSE_LEAVE, mouseLeave, { passive: true });
setTimeout(() => {
if (isHovering) {
hoverService.showHover({ text, target: this });
setTimeout(async () => {
let resolvedTooltip: IMarkdownString | undefined;
if (isHovering && tooltip && !isString(tooltip) && (resolvedTooltip = await tooltip())) {
hoverService.showHover({ text: resolvedTooltip, target: this });
}
this.removeEventListener(DOM.EventType.MOUSE_LEAVE, mouseLeave);
}, hoverDelay);
Expand Down

0 comments on commit 5d60a9c

Please sign in to comment.