Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow extensions to specify custom tree view resoure type #43261

Merged
merged 4 commits into from
Feb 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions src/vs/vscode.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5063,6 +5063,25 @@ declare module 'vscode' {
getChildren(element?: T): ProviderResult<T[]>;
}

/**
* A category in a File Icon Theme, either [file](#ThemeIcon.file) or [folder](#ThemeIcon.folder)
*/
export class ThemeIcon {
/**
* Use the File Icon Theme for files
*/
static readonly File: ThemeIcon;

/**
* Use the File Icon Theme for files
*/
static readonly Folder: ThemeIcon;

readonly id: string;

private constructor(id: string);
}

export class TreeItem {
/**
* A human-readable string describing this item. When `falsy`, it is derived from [resourceUri](#TreeItem.resourceUri).
Expand All @@ -5077,15 +5096,17 @@ declare module 'vscode' {
id?: string;

/**
* The icon path for the tree item. When `falsy`, it is derived from [resourceUri](#TreeItem.resourceUri).
* The icon path for the tree item.
* When `falsy` it is derived from [resourceUri](#TreeItem.resourceUri) and the current theme (As a file if the node isn't collapsible or for folders otherwise)
* When a [ThemeIcon](#ThemeIcon) is specified it is derived from [resourceUri](#TreeItem.resourceUri) and the current theme for the specified category.
*/
iconPath?: string | Uri | { light: string | Uri; dark: string | Uri };
iconPath?: string | Uri | { light: string | Uri | ThemeIcon; dark: string | Uri | ThemeIcon } | ThemeIcon;

/**
* The [uri](#Uri) of the resource representing this item.
*
* Will be used to derive the [label](#TreeItem.label), when it is not provided.
* Will be used to derive the icon from current file icon theme, when [iconPath](#TreeItem.iconPath) is not provided.
* Will be used to derive the icon from current icon theme, when [iconPath](#TreeItem.iconPath) is not provided or is a [ThemeIcon](#ThemeIcon).
*/
resourceUri?: Uri;

Expand Down
1 change: 1 addition & 0 deletions src/vs/workbench/api/node/extHost.api.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,7 @@ export function createApiFactory(
WorkspaceEdit: extHostTypes.WorkspaceEdit,
ProgressLocation: extHostTypes.ProgressLocation,
TreeItemCollapsibleState: extHostTypes.TreeItemCollapsibleState,
ThemeIcon: extHostTypes.ThemeIcon,
TreeItem: extHostTypes.TreeItem,
ThemeColor: extHostTypes.ThemeColor,
// functions
Expand Down
17 changes: 11 additions & 6 deletions src/vs/workbench/api/node/extHostTreeViews.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import { debounceEvent } from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base';
import { Disposable } from 'vs/base/common/lifecycle';
import { ExtHostTreeViewsShape, MainThreadTreeViewsShape } from './extHost.protocol';
import { ITreeItem, TreeViewItemHandleArg } from 'vs/workbench/common/views';
import { ITreeItem, TreeViewItemHandleArg, IThemeIcon } from 'vs/workbench/common/views';
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands';
import { asWinJsPromise } from 'vs/base/common/async';
import { TreeItemCollapsibleState } from 'vs/workbench/api/node/extHostTypes';
import { TreeItemCollapsibleState, ThemeIcon } from 'vs/workbench/api/node/extHostTypes';
import { isUndefinedOrNull } from 'vs/base/common/types';

type TreeItemHandle = string;
Expand Down Expand Up @@ -315,27 +315,32 @@ class ExtHostTreeView<T> extends Disposable {
throw new Error('This should not be reached');
}

private getLightIconPath(extensionTreeItem: vscode.TreeItem): string {
private getLightIconPath(extensionTreeItem: vscode.TreeItem): string | IThemeIcon {
if (extensionTreeItem.iconPath) {
if (typeof extensionTreeItem.iconPath === 'string' || extensionTreeItem.iconPath instanceof URI) {
if (typeof extensionTreeItem.iconPath === 'string'
|| extensionTreeItem.iconPath instanceof URI
|| extensionTreeItem.iconPath instanceof ThemeIcon) {
return this.getIconPath(extensionTreeItem.iconPath);
}
return this.getIconPath(extensionTreeItem.iconPath['light']);
}
return void 0;
}

private getDarkIconPath(extensionTreeItem: vscode.TreeItem): string {
private getDarkIconPath(extensionTreeItem: vscode.TreeItem): string | IThemeIcon {
if (extensionTreeItem.iconPath && extensionTreeItem.iconPath['dark']) {
return this.getIconPath(extensionTreeItem.iconPath['dark']);
}
return void 0;
}

private getIconPath(iconPath: string | URI): string {
private getIconPath(iconPath: string | URI | ThemeIcon): string | IThemeIcon {
if (iconPath instanceof URI) {
return iconPath.toString();
}
if (iconPath instanceof ThemeIcon) {
return { id: iconPath.id };
}
return URI.file(iconPath).toString();
}

Expand Down
12 changes: 12 additions & 0 deletions src/vs/workbench/api/node/extHostTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1544,6 +1544,18 @@ export enum TreeItemCollapsibleState {
Expanded = 2
}

export class ThemeIcon {
static readonly File = new ThemeIcon('file');

static readonly Folder = new ThemeIcon('folder');

readonly id: string;

private constructor(id: string) {
this.id = id;
}
}

export class ThemeColor {
id: string;
constructor(id: string) {
Expand Down
19 changes: 15 additions & 4 deletions src/vs/workbench/browser/parts/views/customView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import * as DOM from 'vs/base/browser/dom';
import { $ } from 'vs/base/browser/builder';
import { LIGHT } from 'vs/platform/theme/common/themeService';
import { ITree, IDataSource, IRenderer, ContextMenuEvent } from 'vs/base/parts/tree/browser/tree';
import { TreeItemCollapsibleState, ITreeItem, ITreeViewer, ICustomViewsService, ITreeViewDataProvider, ViewsRegistry, IViewDescriptor, TreeViewItemHandleArg, ICustomViewDescriptor, IViewsViewlet } from 'vs/workbench/common/views';
import { TreeItemCollapsibleState, ITreeItem, ITreeViewer, ICustomViewsService, ITreeViewDataProvider, ViewsRegistry, IViewDescriptor, TreeViewItemHandleArg, ICustomViewDescriptor, IViewsViewlet, FileThemeIconId, FolderThemeIconId } from 'vs/workbench/common/views';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IProgressService2, ProgressLocation } from 'vs/platform/progress/common/progress';
Expand Down Expand Up @@ -447,8 +447,19 @@ class TreeRenderer implements IRenderer {
DOM.removeClass(templateData.label, 'custom-view-tree-node-item-label');
DOM.removeClass(templateData.resourceLabel.element, 'custom-view-tree-node-item-resourceLabel');

if (resource && !icon) {
templateData.resourceLabel.setLabel({ name: label, resource }, { fileKind: node.collapsibleState === TreeItemCollapsibleState.Collapsed || node.collapsibleState === TreeItemCollapsibleState.Expanded ? FileKind.FOLDER : FileKind.FILE, title: node.tooltip });
if (resource && (typeof icon !== 'string')) {
let fileKind = node.collapsibleState === TreeItemCollapsibleState.Collapsed || node.collapsibleState === TreeItemCollapsibleState.Expanded ? FileKind.FOLDER : FileKind.FILE;
if (icon && icon.id) {
switch (icon.id) {
case FileThemeIconId:
fileKind = FileKind.FILE;
break;
case FolderThemeIconId:
fileKind = FileKind.FOLDER;
break;
}
}
templateData.resourceLabel.setLabel({ name: label, resource }, { fileKind, title: node.tooltip });
DOM.addClass(templateData.resourceLabel.element, 'custom-view-tree-node-item-resourceLabel');
} else {
templateData.label.textContent = label;
Expand Down Expand Up @@ -493,7 +504,7 @@ class TreeItemIcon extends Disposable {
const fileIconTheme = this.themeService.getFileIconTheme();
const contributedIcon = this.themeService.getTheme().type === LIGHT ? this._treeItem.icon : this._treeItem.iconDark;

const hasContributedIcon = !!contributedIcon;
const hasContributedIcon = typeof contributedIcon === 'string';
const hasChildren = this._treeItem.collapsibleState !== TreeItemCollapsibleState.None;
const hasResource = !!this._treeItem.resourceUri;
const isFolder = hasResource && hasChildren;
Expand Down
11 changes: 9 additions & 2 deletions src/vs/workbench/common/views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,13 @@ export enum TreeItemCollapsibleState {
Expanded = 2
}

export const FileThemeIconId = 'file';
export const FolderThemeIconId = 'folder';

export interface IThemeIcon {
readonly id: string;
}

export interface ITreeItem {

handle: string;
Expand All @@ -212,9 +219,9 @@ export interface ITreeItem {

label?: string;

icon?: string;
icon?: string | IThemeIcon;

iconDark?: string;
iconDark?: string | IThemeIcon;

resourceUri?: UriComponents;

Expand Down