diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 3ca7322116d..e73c915cae0 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -5063,6 +5063,25 @@ declare module 'vscode' { getChildren(element?: T): ProviderResult; } + /** + * 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). @@ -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; diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 7bfaa74be53..8ebda0c64ac 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -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 diff --git a/src/vs/workbench/api/node/extHostTreeViews.ts b/src/vs/workbench/api/node/extHostTreeViews.ts index 6a8c297ddfe..cd4ba1f331a 100644 --- a/src/vs/workbench/api/node/extHostTreeViews.ts +++ b/src/vs/workbench/api/node/extHostTreeViews.ts @@ -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; @@ -315,9 +315,11 @@ class ExtHostTreeView 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']); @@ -325,17 +327,20 @@ class ExtHostTreeView extends Disposable { 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(); } diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index c00cf7b2703..e08a9ecb97e 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -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) { diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index 2ec7b419b42..6a9f4090146 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -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'; @@ -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; @@ -494,7 +505,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; diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index f9269f651ce..e4ab65eb02a 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -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; @@ -212,9 +219,9 @@ export interface ITreeItem { label?: string; - icon?: string; + icon?: string | IThemeIcon; - iconDark?: string; + iconDark?: string | IThemeIcon; resourceUri?: UriComponents;