diff --git a/src/vs/workbench/browser/parts/editor/editorLabel.ts b/src/vs/workbench/browser/parts/editor/editorLabel.ts index 57b794e25489c8ea7d28dd9e6a670d57ad086d63..1643bfef1a227f6e212cd3b6051eb203e60c6943 100644 --- a/src/vs/workbench/browser/parts/editor/editorLabel.ts +++ b/src/vs/workbench/browser/parts/editor/editorLabel.ts @@ -71,13 +71,13 @@ export class EditorLabel extends IconLabel { title = resource.fsPath; } - const italic = this.options && this.options.italic; - const extraClasses = getFileIconClasses(resource, path => this.modeService.getModeIdByFilenameOrFirstLine(path)); if (this.options && this.options.extraClasses) { extraClasses.push(...this.options.extraClasses); } + const italic = this.options && this.options.italic; + this.setValue(this.label.name, this.label.description, { title, extraClasses, italic }); } diff --git a/src/vs/workbench/parts/files/browser/files.ts b/src/vs/workbench/parts/files/browser/files.ts index fd8b85e2e49567877fb63da03935797c199add7b..0fab422eb48903d68b67633a047828271cc5fdae 100644 --- a/src/vs/workbench/parts/files/browser/files.ts +++ b/src/vs/workbench/parts/files/browser/files.ts @@ -4,8 +4,16 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; +import uri from 'vs/base/common/uri'; +import paths = require('vs/base/common/paths'); import {EditorDescriptor} from 'vs/workbench/browser/parts/editor/baseEditor'; import {IFileEditorDescriptor} from 'vs/workbench/parts/files/common/files'; +import {getFileIconClasses} from 'vs/base/browser/ui/fileLabel/fileLabel'; +import {IconLabel, IIconLabelOptions} from 'vs/base/browser/ui/iconLabel/iconLabel'; +import {IExtensionService} from 'vs/platform/extensions/common/extensions'; +import {IModeService} from 'vs/editor/common/services/modeService'; +import {getPathLabel} from 'vs/base/common/labels'; +import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace'; /** * A lightweight descriptor of an editor for files. Optionally allows to specify a list of mime types the editor @@ -24,4 +32,75 @@ export class FileEditorDescriptor extends EditorDescriptor implements IFileEdito public getMimeTypes(): string[] { return this.mimetypes; } +} + +export interface IFileIconLabelOptions extends IIconLabelOptions { + hidePath?: boolean; + isFolder?: boolean; +} + +export class FileLabel extends IconLabel { + private file: uri; + private options: IFileIconLabelOptions; + + constructor( + container: HTMLElement, + @IExtensionService private extensionService: IExtensionService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IModeService private modeService: IModeService + ) { + super(container); + + this.extensionService.onReady().then(() => { + this.render(); // there can be additional modes once the extension host is ready so we need to render again + }); + } + + public setFile(resource: uri, options?: IFileIconLabelOptions): void { + this.file = resource; + this.options = options; + + this.render(); + } + + public clear(): void { + this.file = void 0; + this.options = void 0; + + this.setValue(); + } + + private render(): void { + if (!this.file) { + return; + } + + const label = paths.basename(this.file.fsPath); + + let description: string; + if (!this.options || !this.options.hidePath) { + description = getPathLabel(paths.dirname(this.file.fsPath), this.contextService); + } + + let title = ''; + if (this.options && this.options.title) { + title = this.options.title; + } else if (this.file) { + title = this.file.fsPath; + } + + const extraClasses = getFileIconClasses(this.file, path => this.modeService.getModeIdByFilenameOrFirstLine(path), this.options && this.options.isFolder); + if (this.options && this.options.extraClasses) { + extraClasses.push(...this.options.extraClasses); + } + + const italic = this.options && this.options.italic; + + this.setValue(label, description, { title, extraClasses, italic }); + } + + public dispose(): void { + this.file = void 0; + this.options = void 0; + } } \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/explorerviewlet.css b/src/vs/workbench/parts/files/browser/media/explorerviewlet.css index 42d494c2a3c150b597406052a4d307eae94bd142..1ebd28e3ec81e57f76ff58d3e6ca3120d5114d33 100644 --- a/src/vs/workbench/parts/files/browser/media/explorerviewlet.css +++ b/src/vs/workbench/parts/files/browser/media/explorerviewlet.css @@ -20,16 +20,15 @@ flex-wrap: nowrap; } -.explorer-viewlet .explorer-item-label, +.explorer-viewlet .explorer-item > a, .explorer-viewlet .open-editor, .explorer-viewlet .editor-group { text-overflow: ellipsis; overflow: hidden; } -.explorer-viewlet .explorer-item-label, +.explorer-viewlet .explorer-item, .explorer-viewlet .explorer-item .monaco-inputbox { - display: inline-block; /* required for icons support :before rule */ flex: 1; } @@ -37,23 +36,6 @@ display: flex; } -.explorer-viewlet .explorer-folders-view.show-file-icons .folder-icon::before, -.explorer-viewlet .explorer-folders-view.show-file-icons .file-icon::before { - - /* svg icons rendered as background image */ - background-size: 16px; - background-position: left center; - background-repeat: no-repeat; - padding-right: 6px; - width: 16px; - height: 22px; - display: inline-block; - - /* fonts icons */ - -webkit-font-smoothing: antialiased; - vertical-align: top; -} - .explorer-viewlet .explorer-open-editors .monaco-tree .monaco-tree-row > .content > .monaco-action-bar { visibility: hidden; } diff --git a/src/vs/workbench/parts/files/browser/views/explorerViewer.ts b/src/vs/workbench/parts/files/browser/views/explorerViewer.ts index cb86320d7b0a4575afbaf2816337122a6ac820d3..e435d1cb3f3a71f811745a9262047a26e3364a1d 100644 --- a/src/vs/workbench/parts/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/browser/views/explorerViewer.ts @@ -14,7 +14,6 @@ import {MIME_BINARY} from 'vs/base/common/mime'; import async = require('vs/base/common/async'); import paths = require('vs/base/common/paths'); import errors = require('vs/base/common/errors'); -import {getFileIconClasses} from 'vs/base/browser/ui/fileLabel/fileLabel'; import {isString} from 'vs/base/common/types'; import {IAction, ActionRunner as BaseActionRunner, IActionRunner} from 'vs/base/common/actions'; import comparers = require('vs/base/common/comparers'); @@ -22,6 +21,7 @@ import {InputBox} from 'vs/base/browser/ui/inputbox/inputBox'; import {$, Builder} from 'vs/base/browser/builder'; import platform = require('vs/base/common/platform'); import glob = require('vs/base/common/glob'); +import {FileLabel} from 'vs/workbench/parts/files/browser/files'; import {IDisposable} from 'vs/base/common/lifecycle'; import {ContributableActionProvider} from 'vs/workbench/browser/actionBarRegistry'; import {LocalFileChangeEvent, IFilesConfiguration, ITextFileService} from 'vs/workbench/parts/files/common/files'; @@ -33,7 +33,6 @@ import {ClickBehavior, DefaultController} from 'vs/base/parts/tree/browser/treeD import {ActionsRenderer} from 'vs/base/parts/tree/browser/actionsRenderer'; import {FileStat, NewStatPlaceholder} from 'vs/workbench/parts/files/common/explorerViewModel'; import {DragMouseEvent, IMouseEvent} from 'vs/base/browser/mouseEvent'; -import {IExtensionService} from 'vs/platform/extensions/common/extensions'; import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService'; import {IPartService} from 'vs/workbench/services/part/common/partService'; import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace'; @@ -42,7 +41,6 @@ import {IConfigurationService} from 'vs/platform/configuration/common/configurat import {IContextKeyService} from 'vs/platform/contextkey/common/contextkey'; import {IContextViewService, IContextMenuService} from 'vs/platform/contextview/browser/contextView'; import {IEventService} from 'vs/platform/event/common/event'; -import {IModeService} from 'vs/editor/common/services/modeService'; import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation'; import {IMessageService, IConfirmation, Severity} from 'vs/platform/message/common/message'; import {IProgressService} from 'vs/platform/progress/common/progress'; @@ -53,7 +51,6 @@ import {IKeyboardEvent} from 'vs/base/browser/keyboardEvent'; import {IMenuService, IMenu, MenuId} from 'vs/platform/actions/common/actions'; import {fillInActions} from 'vs/platform/actions/browser/menuItemActionItem'; - export class FileDataSource implements IDataSource { private workspace: IWorkspace; @@ -260,19 +257,14 @@ export class ActionRunner extends BaseActionRunner implements IActionRunner { // Explorer Renderer export class FileRenderer extends ActionsRenderer implements IRenderer { - - private static RESOURCE_PATH_KEY = '__resourcePath'; - private state: FileViewletState; - private extensionsReady: boolean; constructor( state: FileViewletState, actionRunner: IActionRunner, private container: HTMLElement, @IContextViewService private contextViewService: IContextViewService, - @IExtensionService private extensionService: IExtensionService, - @IModeService private modeService: IModeService + @IInstantiationService private instantiationService: IInstantiationService ) { super({ actionProvider: state.actionProvider, @@ -280,27 +272,6 @@ export class FileRenderer extends ActionsRenderer implements IRenderer { }); this.state = state; - this.registerListeners(); - } - - private registerListeners(): void { - - // once the extension host is up we need to reapply our CSS classes for - // icons because additional language associations might be present then - this.extensionService.onReady().then(() => { - this.extensionsReady = true; - - const fileItems = this.container.getElementsByClassName('explorer-item file-icon'); - - for (let i = 0; i < fileItems.length; i++) { - const fileItem = $(fileItems.item(i)); - const resourcePath = fileItem.getProperty(FileRenderer.RESOURCE_PATH_KEY); - if (resourcePath) { - fileItem.setClass(['explorer-item', ...getFileIconClasses(resourcePath, path => this.modeService.getModeIdByFilenameOrFirstLine(path))].join(' ')); - fileItem.removeProperty(FileRenderer.RESOURCE_PATH_KEY); - } - } - }); } public getContentHeight(tree: ITree, element: any): number { @@ -310,39 +281,29 @@ export class FileRenderer extends ActionsRenderer implements IRenderer { public renderContents(tree: ITree, stat: FileStat, domElement: HTMLElement, previousCleanupFn: IElementCallback): IElementCallback { const el = $(domElement).clearChildren(); - // Item Container - const item = $('.explorer-item'); - if (stat.isDirectory || (stat instanceof NewStatPlaceholder && stat.isDirectoryPlaceholder())) { - item.addClass(...getFileIconClasses(stat.resource, path => this.modeService.getModeIdByFilenameOrFirstLine(path), true)); - } else { - item.addClass(...getFileIconClasses(stat.resource, path => this.modeService.getModeIdByFilenameOrFirstLine(path))); - - // We need to re-apply the icon CSS classes once the extension host is ready - if (!this.extensionsReady) { - item.setProperty(FileRenderer.RESOURCE_PATH_KEY, stat.resource.fsPath); - } - } - - item.appendTo(el); - - // File/Folder label + // File Rename/Add Input Field const editableData: IEditableData = this.state.getEditableData(stat); - if (!editableData) { - return this.renderFileFolderLabel(item, stat); + if (editableData) { + const item = $('.explorer-item'); + item.appendTo(el); + + return this.renderInputBox(item, tree, stat, editableData); } - // Name Input - return this.renderNameInput(item, tree, stat, editableData); + // Label + return this.renderLabel(el, stat); } - private renderFileFolderLabel(container: Builder, stat: IFileStat): IElementCallback { - const label = $('.explorer-item-label').appendTo(container); - $('a.plain').text(stat.name).title(stat.resource.fsPath).appendTo(label); + private renderLabel(container: Builder, stat: FileStat): IElementCallback { + const label = this.instantiationService.createInstance(FileLabel, container.getHTMLElement()); + + const extraClasses = ['explorer-item']; + label.setFile(stat.resource, { hidePath: true, isFolder: stat.isDirectory, extraClasses }); - return null; + return () => label.dispose(); } - private renderNameInput(container: Builder, tree: ITree, stat: FileStat, editableData: IEditableData): IElementCallback { + private renderInputBox(container: Builder, tree: ITree, stat: FileStat, editableData: IEditableData): IElementCallback { // Input field (when creating a new file or folder or renaming) const inputBox = new InputBox(container.getHTMLElement(), this.contextViewService, {