diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index 752437df1ba59b09bb389a0d3dbb3485e89d58a9..635e5228bf22cdedb2dc9d9c6898f06bdd13bc39 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts @@ -7,7 +7,7 @@ import 'vs/css!./iconlabel'; import * as dom from 'vs/base/browser/dom'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import { IMatch } from 'vs/base/common/filters'; -import { IDisposable, combinedDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; export interface IIconLabelCreationOptions { supportHighlights?: boolean; @@ -116,13 +116,7 @@ export class IconLabel extends Disposable { return this.domNode.element; } - onClick(callback: (event: MouseEvent) => void): IDisposable { - return combinedDisposable([ - dom.addDisposableListener(this.labelDescriptionContainer.element, dom.EventType.CLICK, (e: MouseEvent) => callback(e)), - ]); - } - - setValue(label?: string, description?: string, options?: IIconLabelValueOptions): void { + setLabel(label?: string, description?: string, options?: IIconLabelValueOptions): void { const classes = ['monaco-icon-label']; if (options) { if (options.extraClasses) { diff --git a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts index 189f85e48410ec650ff619f4b67b64e31a14a5eb..132f1207facd5f28c5f0f4977c6a4470a37b2b1f 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts @@ -468,7 +468,7 @@ class Renderer implements IRenderer { options.title = entry.getTooltip(); options.descriptionTitle = entry.getDescriptionTooltip() || entry.getDescription(); // tooltip over description because it could overflow options.descriptionMatches = descriptionHighlights || []; - data.label.setValue(entry.getLabel(), entry.getDescription(), options); + data.label.setLabel(entry.getLabel(), entry.getDescription(), options); // Meta data.detail.set(entry.getDetail(), detailHighlights); diff --git a/src/vs/editor/contrib/referenceSearch/referencesTree.ts b/src/vs/editor/contrib/referenceSearch/referencesTree.ts index e8a43e25ed264ba58918537ad4e2e9c30963ca2c..e9282d70092b88f2d0b032fe4763b53f95d8af30 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesTree.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesTree.ts @@ -118,7 +118,7 @@ class FileReferencesTemplate extends Disposable { set(element: FileReferences) { let parent = dirname(element.uri); - this.file.setValue(getBaseLabel(element.uri), parent ? this._uriLabel.getUriLabel(parent, { relative: true }) : undefined, { title: this._uriLabel.getUriLabel(element.uri) }); + this.file.setLabel(getBaseLabel(element.uri), parent ? this._uriLabel.getUriLabel(parent, { relative: true }) : undefined, { title: this._uriLabel.getUriLabel(element.uri) }); const len = element.children.length; this.badge.setCount(len); if (element.failure) { diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index de7330e305ecca130d3ba7ab66c4ab4b1ecd0606..ae57d64b7db63a39b2d1545b912854f015689e59 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -185,7 +185,7 @@ class Renderer implements IListRenderer ]; } - data.iconLabel.setValue(suggestion.label, undefined, labelOptions); + data.iconLabel.setLabel(suggestion.label, undefined, labelOptions); data.typeLabel.textContent = (suggestion.detail || '').replace(/\n.*$/m, ''); if (canExpandCompletionItem(element)) { diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index d5afcc622175dbba3ffb009b2ea9af84119a9b51..3742b85b8beeca8613ce9d7be7554ba7cb01f054 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -22,11 +22,13 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { Event, Emitter } from 'vs/base/common/event'; import { ILabelService } from 'vs/platform/label/common/label'; import { getIconClasses, getConfiguredLangId } from 'vs/editor/common/services/getIconClasses'; +import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -export interface IResourceLabel { +export interface IResourceLabelProps { + resource?: uri; name: string; description?: string; - resource?: uri; } export interface IResourceLabelOptions extends IIconLabelValueOptions { @@ -34,56 +36,211 @@ export interface IResourceLabelOptions extends IIconLabelValueOptions { fileDecorations?: { colors: boolean, badges: boolean, data?: IDecorationData }; } -export class ResourceLabel extends IconLabel { +export interface IFileLabelOptions extends IResourceLabelOptions { + hideLabel?: boolean; + hidePath?: boolean; +} - private _onDidRender = this._register(new Emitter()); - get onDidRender(): Event { return this._onDidRender.event; } +export interface IResourceLabel extends IDisposable { + readonly element: HTMLElement; + readonly onDidRender: Event; + + /** + * Most generic way to apply a label with raw information. + */ + setLabel(label?: string, description?: string, options?: IIconLabelValueOptions): void; + + /** + * Convinient method to apply a label by passing a resource along. + * + * Note: for file resources consider to use the #setFile() method instead. + */ + setResource(label: IResourceLabelProps, options?: IResourceLabelOptions): void; + + /** + * Convinient method to render a file label based on a resource. + */ + setFile(resource: uri, options?: IFileLabelOptions): void; + + /** + * Convinient method to apply a label by passing an editor along. + */ + setEditor(editor: IEditorInput, options?: IResourceLabelOptions): void; + + /** + * Resets the label to be empty. + */ + clear(): void; +} - private label: IResourceLabel; - private options: IResourceLabelOptions; - private computedIconClasses: string[]; - private lastKnownConfiguredLangId: string; - private computedPathLabel: string; +export class ResourceLabels extends Disposable { + private _widgets: ResourceLabelWidget[] = []; + private _labels: IResourceLabel[] = []; constructor( - container: HTMLElement, - options: IIconLabelCreationOptions, + @IInstantiationService private instantiationService: IInstantiationService, @IExtensionService private extensionService: IExtensionService, @IConfigurationService private configurationService: IConfigurationService, - @IModeService private modeService: IModeService, @IModelService private modelService: IModelService, - @IDecorationsService protected decorationsService: IDecorationsService, - @IThemeService private themeService: IThemeService, - @ILabelService protected labelService: ILabelService + @IDecorationsService private decorationsService: IDecorationsService, + @IThemeService private themeService: IThemeService ) { - super(container, options); + super(); this.registerListeners(); } + get(index: number): IResourceLabel { + return this._labels[index]; + } + private registerListeners(): void { - // update when extensions are registered with potentially new languages - this._register(this.extensionService.onDidRegisterExtensions(() => this.render(true /* clear cache */))); + // notify when extensions are registered with potentially new languages + this._register(this.extensionService.onDidRegisterExtensions(() => this._widgets.forEach(widget => widget.notifyExtensionsRegistered()))); - // react to model mode changes - this._register(this.modelService.onModelModeChanged(e => this.onModelModeChanged(e))); + // notify when model mode changes + this._register(this.modelService.onModelModeChanged(e => this._widgets.forEach(widget => widget.notifyModelModeChanged(e)))); - // react to file decoration changes - this._register(this.decorationsService.onDidChangeDecorations(this.onFileDecorationsChanges, this)); + // notify when file decoration changes + this._register(this.decorationsService.onDidChangeDecorations(e => this._widgets.forEach(widget => widget.notifyFileDecorationsChanges(e)))); - // react to theme changes - this._register(this.themeService.onThemeChange(() => this.render(false))); + // notify when theme changes + this._register(this.themeService.onThemeChange(() => this._widgets.forEach(widget => widget.notifyThemeChange()))); - // react to files.associations changes + // notify when files.associations changes this._register(this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(FILES_ASSOCIATIONS_CONFIG)) { - this.render(true /* clear cache */); + this._widgets.forEach(widget => widget.notifyFileAssociationsChange()); } })); } - private onModelModeChanged(e: { model: ITextModel; oldModeId: string; }): void { + create(container: HTMLElement, options?: IIconLabelCreationOptions): IResourceLabel { + const widget = this.instantiationService.createInstance(ResourceLabelWidget, container, options); + + // Only expose a handle to the outside + const label: IResourceLabel = { + element: widget.element, + onDidRender: widget.onDidRender, + setLabel: (label?: string, description?: string, options?: IIconLabelValueOptions) => widget.setLabel(label, description, options), + setResource: (label: IResourceLabelProps, options?: IResourceLabelOptions) => widget.setResource(label, options), + setEditor: (editor: IEditorInput, options?: IResourceLabelOptions) => widget.setEditor(editor, options), + setFile: (resource: uri, options?: IFileLabelOptions) => widget.setFile(resource, options), + clear: () => widget.clear(), + dispose: () => this.disposeWidget(widget) + }; + + // Store + this._labels.push(label); + this._widgets.push(widget); + + return label; + } + + private disposeWidget(widget: ResourceLabelWidget): void { + const index = this._widgets.indexOf(widget); + if (index > -1) { + this._widgets.splice(index, 1); + this._labels.splice(index, 1); + } + + dispose(widget); + } + + onVisible(): void { + this._widgets.forEach(widget => widget.notifyVisibilityChanged(true)); + } + + onHidden(): void { + this._widgets.forEach(widget => widget.notifyVisibilityChanged(false)); + } + + clear(): void { + this._widgets = dispose(this._widgets); + this._labels = []; + } + + dispose(): void { + super.dispose(); + + this.clear(); + } +} + +/** + * Note: please consider to use ResourceLabels if you are in need + * of more than one label for your widget. + */ +export class ResourceLabel extends ResourceLabels { + + private _label: IResourceLabel; + + constructor( + container: HTMLElement, + options: IIconLabelCreationOptions, + @IInstantiationService instantiationService: IInstantiationService, + @IExtensionService extensionService: IExtensionService, + @IConfigurationService configurationService: IConfigurationService, + @IModelService modelService: IModelService, + @IDecorationsService decorationsService: IDecorationsService, + @IThemeService themeService: IThemeService, + @ILabelService labelService: ILabelService + ) { + super(instantiationService, extensionService, configurationService, modelService, decorationsService, themeService); + + this._label = this._register(this.create(container, options)); + } + + get element(): IResourceLabel { + return this._label; + } +} + +enum Redraw { + Basic = 1, + Full = 2 +} + +class ResourceLabelWidget extends IconLabel { + + private _onDidRender = this._register(new Emitter()); + get onDidRender(): Event { return this._onDidRender.event; } + + private label: IResourceLabelProps; + private options: IResourceLabelOptions; + private computedIconClasses: string[]; + private lastKnownConfiguredLangId: string; + private computedPathLabel: string; + + private needsRedraw: Redraw; + private isHidden: boolean = false; + + constructor( + container: HTMLElement, + options: IIconLabelCreationOptions, + @IModeService private modeService: IModeService, + @IModelService private modelService: IModelService, + @IDecorationsService private decorationsService: IDecorationsService, + @ILabelService private labelService: ILabelService, + @IUntitledEditorService private untitledEditorService: IUntitledEditorService, + @IWorkspaceContextService private contextService: IWorkspaceContextService + ) { + super(container, options); + } + + notifyVisibilityChanged(visible: boolean): void { + if (visible === this.isHidden) { + this.isHidden = !visible; + + if (visible && this.needsRedraw) { + this.render(this.needsRedraw === Redraw.Basic ? false : true); + this.needsRedraw = void 0; + } + } + } + + notifyModelModeChanged(e: { model: ITextModel; oldModeId: string; }): void { if (!this.label || !this.label.resource) { return; // only update if label exists } @@ -103,7 +260,7 @@ export class ResourceLabel extends IconLabel { } } - private onFileDecorationsChanges(e: IResourceDecorationChangeEvent): void { + notifyFileDecorationsChanges(e: IResourceDecorationChangeEvent): void { if (!this.options || !this.label || !this.label.resource) { return; } @@ -113,7 +270,19 @@ export class ResourceLabel extends IconLabel { } } - setLabel(label: IResourceLabel, options?: IResourceLabelOptions): void { + notifyExtensionsRegistered(): void { + this.render(true); + } + + notifyThemeChange(): void { + this.render(false); + } + + notifyFileAssociationsChange(): void { + this.render(true); + } + + setResource(label: IResourceLabelProps, options?: IResourceLabelOptions): void { const hasResourceChanged = this.hasResourceChanged(label, options); this.label = label; @@ -126,7 +295,7 @@ export class ResourceLabel extends IconLabel { this.render(hasResourceChanged); } - private hasResourceChanged(label: IResourceLabel, options: IResourceLabelOptions): boolean { + private hasResourceChanged(label: IResourceLabelProps, options: IResourceLabelOptions): boolean { const newResource = label ? label.resource : void 0; const oldResource = this.label ? this.label.resource : void 0; @@ -152,6 +321,39 @@ export class ResourceLabel extends IconLabel { return true; } + setEditor(editor: IEditorInput, options?: IResourceLabelOptions): void { + this.setResource({ + resource: toResource(editor, { supportSideBySide: true }), + name: editor.getName(), + description: editor.getDescription() + }, options); + } + + setFile(resource: uri, options?: IFileLabelOptions): void { + const hideLabel = options && options.hideLabel; + let name: string; + if (!hideLabel) { + if (options && options.fileKind === FileKind.ROOT_FOLDER) { + const workspaceFolder = this.contextService.getWorkspaceFolder(resource); + if (workspaceFolder) { + name = workspaceFolder.name; + } + } + + if (!name) { + name = resources.basenameOrAuthority(resource); + } + } + + let description: string; + const hidePath = (options && options.hidePath) || (resource.scheme === Schemas.untitled && !this.untitledEditorService.hasAssociatedFilePath(resource)); + if (!hidePath) { + description = this.labelService.getUriLabel(resources.dirname(resource), { relative: true }); + } + + this.setResource({ resource, name, description }, options); + } + clear(): void { this.label = void 0; this.options = void 0; @@ -159,10 +361,22 @@ export class ResourceLabel extends IconLabel { this.computedIconClasses = void 0; this.computedPathLabel = void 0; - this.setValue(); + this.setLabel(); } private render(clearIconCache: boolean): void { + if (this.isHidden) { + if (!this.needsRedraw) { + this.needsRedraw = clearIconCache ? Redraw.Full : Redraw.Basic; + } + + if (this.needsRedraw === Redraw.Basic && clearIconCache) { + this.needsRedraw = Redraw.Full; + } + + return; + } + if (this.label) { const configuredLangId = getConfiguredLangId(this.modelService, this.label.resource); if (this.lastKnownConfiguredLangId !== configuredLangId) { @@ -231,7 +445,7 @@ export class ResourceLabel extends IconLabel { } } - this.setValue(label, this.label.description, iconLabelOptions); + this.setLabel(label, this.label.description, iconLabelOptions); this._onDidRender.fire(); } @@ -246,63 +460,3 @@ export class ResourceLabel extends IconLabel { this.computedPathLabel = void 0; } } - -export class EditorLabel extends ResourceLabel { - - setEditor(editor: IEditorInput, options?: IResourceLabelOptions): void { - this.setLabel({ - resource: toResource(editor, { supportSideBySide: true }), - name: editor.getName(), - description: editor.getDescription() - }, options); - } -} - -export interface IFileLabelOptions extends IResourceLabelOptions { - hideLabel?: boolean; - hidePath?: boolean; -} - -export class FileLabel extends ResourceLabel { - - constructor( - container: HTMLElement, - options: IIconLabelCreationOptions, - @IExtensionService extensionService: IExtensionService, - @IWorkspaceContextService private contextService: IWorkspaceContextService, - @IConfigurationService configurationService: IConfigurationService, - @IModeService modeService: IModeService, - @IModelService modelService: IModelService, - @IDecorationsService decorationsService: IDecorationsService, - @IThemeService themeService: IThemeService, - @IUntitledEditorService private untitledEditorService: IUntitledEditorService, - @ILabelService labelService: ILabelService - ) { - super(container, options, extensionService, configurationService, modeService, modelService, decorationsService, themeService, labelService); - } - - setFile(resource: uri, options?: IFileLabelOptions): void { - const hideLabel = options && options.hideLabel; - let name: string; - if (!hideLabel) { - if (options && options.fileKind === FileKind.ROOT_FOLDER) { - const workspaceFolder = this.contextService.getWorkspaceFolder(resource); - if (workspaceFolder) { - name = workspaceFolder.name; - } - } - - if (!name) { - name = resources.basenameOrAuthority(resource); - } - } - - let description: string; - const hidePath = (options && options.hidePath) || (resource.scheme === Schemas.untitled && !this.untitledEditorService.hasAssociatedFilePath(resource)); - if (!hidePath) { - description = this.labelService.getUriLabel(resources.dirname(resource), { relative: true }); - } - - this.setLabel({ resource, name, description }, options); - } -} diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index ab096bf28c52f04a98e46ed20e52895a7388082b..c8d4a5e084ca5edd55939c66c8580f384f0e174a 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -35,7 +35,7 @@ import { ColorIdentifier, ColorFunction } from 'vs/platform/theme/common/colorRe import { attachBreadcrumbsStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { FileLabel } from 'vs/workbench/browser/labels'; +import { ResourceLabel } from 'vs/workbench/browser/labels'; import { BreadcrumbsConfig, IBreadcrumbsService } from 'vs/workbench/browser/parts/editor/breadcrumbs'; import { BreadcrumbElement, EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel'; import { BreadcrumbsPicker, createBreadcrumbsPicker } from 'vs/workbench/browser/parts/editor/breadcrumbsPicker'; @@ -78,8 +78,8 @@ class Item extends BreadcrumbsItem { render(container: HTMLElement): void { if (this.element instanceof FileElement) { // file/folder - let label = this._instantiationService.createInstance(FileLabel, container, {}); - label.setFile(this.element.uri, { + let label = this._instantiationService.createInstance(ResourceLabel, container, {}); + label.element.setFile(this.element.uri, { hidePath: true, hideIcon: this.element.kind === FileKind.FOLDER || !this.options.showFileIcons, fileKind: this.element.kind, @@ -98,7 +98,7 @@ class Item extends BreadcrumbsItem { } else if (this.element instanceof OutlineGroup) { // provider let label = new IconLabel(container); - label.setValue(this.element.provider.displayName); + label.setLabel(this.element.provider.displayName); this._disposables.push(label); } else if (this.element instanceof OutlineElement) { @@ -111,7 +111,7 @@ class Item extends BreadcrumbsItem { } let label = new IconLabel(container); let title = this.element.symbol.name.replace(/\r|\n|\r\n/g, '\u23CE'); - label.setValue(title); + label.setLabel(title); this._disposables.push(label); } } diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index 88055da1e0e26a1cfdc53241aa87cb3ee923e309..1fc46bb8ed466e6e46667eb8aebd23f2ffcf1ff4 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -25,7 +25,7 @@ import { IConstructorSignature1, IInstantiationService } from 'vs/platform/insta import { HighlightingWorkbenchTree, IHighlighter, IHighlightingTreeConfiguration, IHighlightingTreeOptions } from 'vs/platform/list/browser/listService'; import { breadcrumbsPickerBackground, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { FileLabel } from 'vs/workbench/browser/labels'; +import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs'; import { BreadcrumbElement, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel'; import { IFileIconTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; @@ -329,7 +329,7 @@ export class FileHighlighter implements IHighlighter { export class FileRenderer implements IRenderer { constructor( - @IInstantiationService private readonly _instantiationService: IInstantiationService, + private readonly _labels: ResourceLabels, @IConfigurationService private readonly _configService: IConfigurationService, ) { } @@ -342,10 +342,10 @@ export class FileRenderer implements IRenderer { } renderTemplate(tree: ITree, templateId: string, container: HTMLElement) { - return this._instantiationService.createInstance(FileLabel, container, { supportHighlights: true }); + return this._labels.create(container, { supportHighlights: true }); } - renderElement(tree: ITree, element: IFileStat | IWorkspaceFolder, templateId: string, templateData: FileLabel): void { + renderElement(tree: ITree, element: IFileStat | IWorkspaceFolder, templateId: string, templateData: IResourceLabel): void { let fileDecorations = this._configService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations'); let resource: URI; let fileKind: FileKind; @@ -365,7 +365,7 @@ export class FileRenderer implements IRenderer { }); } - disposeTemplate(tree: ITree, templateId: string, templateData: FileLabel): void { + disposeTemplate(tree: ITree, templateId: string, templateData: IResourceLabel): void { templateData.dispose(); } } @@ -427,7 +427,9 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker { this._disposables.push(filter); config.dataSource = this._instantiationService.createInstance(FileDataSource); - config.renderer = this._instantiationService.createInstance(FileRenderer); + const labels = this._instantiationService.createInstance(ResourceLabels); + this._disposables.push(labels); + config.renderer = this._instantiationService.createInstance(FileRenderer, labels); config.sorter = new FileSorter(); config.highlighter = new FileHighlighter(); config.filter = filter; diff --git a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts index 7eddf53b4f04db00443bb2e98b7bc42cae49dbb7..0f3d668b463a1347050bb39d6c0d5b6dd228fa1e 100644 --- a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/notabstitlecontrol'; import { toResource, Verbosity, IEditorInput } from 'vs/workbench/common/editor'; import { TitleControl, IToolbarActions } from 'vs/workbench/browser/parts/editor/titleControl'; -import { ResourceLabel } from 'vs/workbench/browser/labels'; +import { ResourceLabel, IResourceLabel } from 'vs/workbench/browser/labels'; import { TAB_ACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND } from 'vs/workbench/common/theme'; import { EventType as TouchEventType, GestureEvent, Gesture } from 'vs/base/browser/touch'; import { addDisposableListener, EventType, addClass, EventHelper, removeClass, toggleClass } from 'vs/base/browser/dom'; @@ -22,7 +22,7 @@ interface IRenderedEditorLabel { export class NoTabsTitleControl extends TitleControl { private titleContainer: HTMLElement; - private editorLabel: ResourceLabel; + private editorLabel: IResourceLabel; private activeLabel: IRenderedEditorLabel = Object.create(null); protected create(parent: HTMLElement): void { @@ -40,8 +40,8 @@ export class NoTabsTitleControl extends TitleControl { this.titleContainer.appendChild(labelContainer); // Editor Label - this.editorLabel = this._register(this.instantiationService.createInstance(ResourceLabel, labelContainer, void 0)); - this._register(this.editorLabel.onClick(e => this.onTitleLabelClick(e))); + this.editorLabel = this._register(this.instantiationService.createInstance(ResourceLabel, labelContainer, void 0)).element; + this._register(addDisposableListener(this.editorLabel.element, EventType.CLICK, e => this.onTitleLabelClick(e))); // Breadcrumbs this.createBreadcrumbsControl(labelContainer, { showFileIcons: false, showSymbolIcons: true, showDecorationColors: false, breadcrumbsBackground: () => Color.transparent }); @@ -244,7 +244,7 @@ export class NoTabsTitleControl extends TitleControl { title = ''; // dont repeat what is already shown } - this.editorLabel.setLabel({ name, description, resource }, { title, italic: !isEditorPinned, extraClasses: ['no-tabs', 'title-label'] }); + this.editorLabel.setResource({ name, description, resource }, { title, italic: !isEditorPinned, extraClasses: ['no-tabs', 'title-label'] }); if (isGroupActive) { this.editorLabel.element.style.color = this.getColor(TAB_ACTIVE_FOREGROUND); } else { diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 94e3f71e33291e59d4baad4c236868818998b5df..837303eddca888584af188242be21f153eec1ee5 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -10,7 +10,7 @@ import { toResource, GroupIdentifier, IEditorInput, Verbosity, EditorCommandsCon import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { EventType as TouchEventType, GestureEvent, Gesture } from 'vs/base/browser/touch'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { ResourceLabel } from 'vs/workbench/browser/labels'; +import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -57,7 +57,7 @@ export class TabsTitleControl extends TitleControl { private tabsScrollbar: ScrollableElement; private closeOneEditorAction: CloseOneEditorAction; - private tabLabelWidgets: ResourceLabel[] = []; + private tabResourceLabels: ResourceLabels; private tabLabels: IEditorInputLabel[] = []; private tabDisposeables: IDisposable[] = []; @@ -123,6 +123,9 @@ export class TabsTitleControl extends TitleControl { addClass(breadcrumbsContainer, 'tabs-breadcrumbs'); this.titleContainer.appendChild(breadcrumbsContainer); this.createBreadcrumbsControl(breadcrumbsContainer, { showFileIcons: true, showSymbolIcons: true, showDecorationColors: false, breadcrumbsBackground: breadcrumbsBackground }); + + // Tab Labels + this.tabResourceLabels = this._register(this.instantiationService.createInstance(ResourceLabels)); } private createTabsScrollbar(scrollable: HTMLElement): ScrollableElement { @@ -295,7 +298,6 @@ export class TabsTitleControl extends TitleControl { (this.tabsContainer.lastChild as HTMLElement).remove(); // Remove associated tab label and widget - this.tabLabelWidgets.pop(); this.tabDisposeables.pop().dispose(); } @@ -311,7 +313,7 @@ export class TabsTitleControl extends TitleControl { clearNode(this.tabsContainer); this.tabDisposeables = dispose(this.tabDisposeables); - this.tabLabelWidgets = []; + this.tabResourceLabels.clear(); this.tabLabels = []; this.clearEditorActionsToolbar(); @@ -395,12 +397,12 @@ export class TabsTitleControl extends TitleControl { this.redraw(); } - private withTab(editor: IEditorInput, fn: (tabContainer: HTMLElement, tabLabelWidget: ResourceLabel, tabLabel: IEditorInputLabel) => void): void { + private withTab(editor: IEditorInput, fn: (tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel) => void): void { const editorIndex = this.group.getIndexOfEditor(editor); const tabContainer = this.tabsContainer.children[editorIndex] as HTMLElement; if (tabContainer) { - fn(tabContainer, this.tabLabelWidgets[editorIndex], this.tabLabels[editorIndex]); + fn(tabContainer, this.tabResourceLabels.get(editorIndex), this.tabLabels[editorIndex]); } } @@ -422,8 +424,7 @@ export class TabsTitleControl extends TitleControl { tabContainer.appendChild(tabBorderTopContainer); // Tab Editor Label - const editorLabel = this.instantiationService.createInstance(ResourceLabel, tabContainer, void 0); - this.tabLabelWidgets.push(editorLabel); + const editorLabel = this.tabResourceLabels.create(tabContainer); // Tab Close Button const tabCloseContainer = document.createElement('div'); @@ -806,16 +807,16 @@ export class TabsTitleControl extends TitleControl { this.layout(this.dimension); } - private forEachTab(fn: (editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: ResourceLabel, tabLabel: IEditorInputLabel) => void): void { + private forEachTab(fn: (editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel) => void): void { this.group.editors.forEach((editor, index) => { const tabContainer = this.tabsContainer.children[index] as HTMLElement; if (tabContainer) { - fn(editor, index, tabContainer, this.tabLabelWidgets[index], this.tabLabels[index]); + fn(editor, index, tabContainer, this.tabResourceLabels.get(index), this.tabLabels[index]); } }); } - private redrawTab(editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: ResourceLabel, tabLabel: IEditorInputLabel): void { + private redrawTab(editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel): void { // Label this.redrawLabel(editor, tabContainer, tabLabelWidget, tabLabel); @@ -848,7 +849,7 @@ export class TabsTitleControl extends TitleControl { this.redrawEditorActiveAndDirty(this.accessor.activeGroup === this.group, editor, tabContainer, tabLabelWidget); } - private redrawLabel(editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: ResourceLabel, tabLabel: IEditorInputLabel): void { + private redrawLabel(editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel): void { const name = tabLabel.name; const description = tabLabel.description || ''; const title = tabLabel.title || ''; @@ -858,10 +859,10 @@ export class TabsTitleControl extends TitleControl { tabContainer.title = title; // Label - tabLabelWidget.setLabel({ name, description, resource: toResource(editor, { supportSideBySide: true }) }, { title, extraClasses: ['tab-label'], italic: !this.group.isPinned(editor) }); + tabLabelWidget.setResource({ name, description, resource: toResource(editor, { supportSideBySide: true }) }, { title, extraClasses: ['tab-label'], italic: !this.group.isPinned(editor) }); } - private redrawEditorActiveAndDirty(isGroupActive: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: ResourceLabel): void { + private redrawEditorActiveAndDirty(isGroupActive: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel): void { const isTabActive = this.group.isActive(editor); const hasModifiedBorderTop = this.doRedrawEditorDirty(isGroupActive, isTabActive, editor, tabContainer); @@ -869,7 +870,7 @@ export class TabsTitleControl extends TitleControl { this.doRedrawEditorActive(isGroupActive, !hasModifiedBorderTop, editor, tabContainer, tabLabelWidget); } - private doRedrawEditorActive(isGroupActive: boolean, allowBorderTop: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: ResourceLabel): void { + private doRedrawEditorActive(isGroupActive: boolean, allowBorderTop: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel): void { // Tab is active if (this.group.isActive(editor)) { diff --git a/src/vs/workbench/browser/parts/quickinput/quickInputList.ts b/src/vs/workbench/browser/parts/quickinput/quickInputList.ts index 05c1ba74d3ada5a1dcf9ea0759bf207f19ec3e52..4682fd42f75bb69d3781d8271104e7933f416910 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInputList.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInputList.ts @@ -144,7 +144,7 @@ class ListElementRenderer implements IListRenderer action instanceof MenuItemAction ? this.instantiationService.createInstance(ContextAwareMenuItemActionItem, action) : undefined; - const menus = this.instantiationService.createInstance(TreeMenus, this.id); + const menus = this._register(this.instantiationService.createInstance(TreeMenus, this.id)); + this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels)); const dataSource = this.instantiationService.createInstance(TreeDataSource, this, this.container); - const renderer = this.instantiationService.createInstance(TreeRenderer, this.id, menus, actionItemProvider); + const renderer = this.instantiationService.createInstance(TreeRenderer, this.id, menus, this.treeLabels, actionItemProvider); const controller = this.instantiationService.createInstance(TreeController, this.id, menus); - this.tree = this.instantiationService.createInstance(FileIconThemableWorkbenchTree, this.treeContainer, { dataSource, renderer, controller }, {}); + this.tree = this._register(this.instantiationService.createInstance(FileIconThemableWorkbenchTree, this.treeContainer, { dataSource, renderer, controller }, {})); this.tree.contextKeyService.createKey(this.id, true); - this._register(this.tree); this._register(this.tree.onDidChangeSelection(e => this.onSelection(e))); this._register(this.tree.onDidExpandItem(e => this._onDidExpandItem.fire(e.item.getElement()))); this._register(this.tree.onDidCollapseItem(e => this._onDidCollapseItem.fire(e.item.getElement()))); @@ -593,7 +602,7 @@ class TreeDataSource implements IDataSource { } interface ITreeExplorerTemplateData { - resourceLabel: ResourceLabel; + resourceLabel: IResourceLabel; icon: HTMLElement; actionBar: ActionBar; aligner: Aligner; @@ -632,8 +641,8 @@ class TreeRenderer implements IRenderer { constructor( private treeViewId: string, private menus: TreeMenus, + private labels: ResourceLabels, private actionItemProvider: IActionItemProvider, - @IInstantiationService private instantiationService: IInstantiationService, @IWorkbenchThemeService private themeService: IWorkbenchThemeService, @IConfigurationService private configurationService: IConfigurationService, @ILabelService private labelService: ILabelService @@ -652,7 +661,7 @@ class TreeRenderer implements IRenderer { DOM.addClass(container, 'custom-view-tree-node-item'); const icon = DOM.append(container, DOM.$('.custom-view-tree-node-item-icon')); - const resourceLabel = this.instantiationService.createInstance(ResourceLabel, container, { supportHighlights: true, donotSupportOcticons: true }); + const resourceLabel = this.labels.create(container, { supportHighlights: true, donotSupportOcticons: true }); DOM.addClass(resourceLabel.element, 'custom-view-tree-node-item-resourceLabel'); const actionsContainer = DOM.append(resourceLabel.element, DOM.$('.actions')); const actionBar = new ActionBar(actionsContainer, { @@ -674,14 +683,13 @@ class TreeRenderer implements IRenderer { const title = node.tooltip ? node.tooltip : resource ? void 0 : label; // reset - templateData.resourceLabel.clear(); templateData.actionBar.clear(); if (resource || node.themeIcon) { const fileDecorations = this.configurationService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations'); - templateData.resourceLabel.setLabel({ name: label, description, resource: resource ? resource : URI.parse('missing:_icon_resource') }, { fileKind: this.getFileKind(node), title, hideIcon: !!iconUrl, fileDecorations, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches }); + templateData.resourceLabel.setResource({ name: label, description, resource: resource ? resource : URI.parse('missing:_icon_resource') }, { fileKind: this.getFileKind(node), title, hideIcon: !!iconUrl, fileDecorations, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches }); } else { - templateData.resourceLabel.setLabel({ name: label, description }, { title, hideIcon: true, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches }); + templateData.resourceLabel.setResource({ name: label, description }, { title, hideIcon: true, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches }); } templateData.icon.style.backgroundImage = iconUrl ? `url('${iconUrl.toString(true)}')` : ''; diff --git a/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts b/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts index 5745d58e4f577fef4a2f975b1cb6a6752bcbb6b7..35552bea79c00b8faeddd01e9720ffa9d21b82ac 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts @@ -23,11 +23,13 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { textLinkForeground, textLinkActiveForeground, focusBorder } from 'vs/platform/theme/common/colorRegistry'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ResourceLabels } from 'vs/workbench/browser/labels'; export const COMMENTS_PANEL_ID = 'workbench.panel.comments'; export const COMMENTS_PANEL_TITLE = 'Comments'; export class CommentsPanel extends Panel { + private treeLabels: ResourceLabels; private tree: WorkbenchTree; private treeContainer: HTMLElement; private messageBoxContainer: HTMLElement; @@ -129,9 +131,11 @@ export class CommentsPanel extends Panel { } private createTree(): void { - this.tree = this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, { + this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels)); + + this.tree = this._register(this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, { dataSource: new CommentsDataSource(), - renderer: new CommentsModelRenderer(this.instantiationService, this.openerService), + renderer: new CommentsModelRenderer(this.treeLabels, this.openerService), accessibilityProvider: new DefaultAccessibilityProvider, controller: new DefaultController(), dnd: new DefaultDragAndDrop(), @@ -139,7 +143,7 @@ export class CommentsPanel extends Panel { }, { twistiePixels: 20, ariaLabel: COMMENTS_PANEL_TITLE - }); + })); const commentsNavigator = this._register(new TreeResourceNavigator(this.tree, { openOnFocus: true })); this._register(Event.debounce(commentsNavigator.openResource, (last, event) => event, 100, true)(options => { @@ -172,7 +176,6 @@ export class CommentsPanel extends Panel { return true; } - const threadToReveal = element instanceof ResourceWithCommentThreads ? element.commentThreads[0].threadId : element.threadId; const commentToReveal = element instanceof ResourceWithCommentThreads ? element.commentThreads[0].comment : element.comment; @@ -237,6 +240,14 @@ export class CommentsPanel extends Panel { this.refresh(); } } + + if (this.treeLabels) { + if (visible) { + this.treeLabels.onVisible(); + } else { + this.treeLabels.onHidden(); + } + } } private refresh(): void { diff --git a/src/vs/workbench/parts/comments/electron-browser/commentsTreeViewer.ts b/src/vs/workbench/parts/comments/electron-browser/commentsTreeViewer.ts index 8bf5062890138940eeea75107fea3902397c962e..473341920a2e39bdc701670a4087926c60ed6412 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentsTreeViewer.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentsTreeViewer.ts @@ -10,9 +10,8 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IDataSource, IFilter, IRenderer as ITreeRenderer, ITree } from 'vs/base/parts/tree/browser/tree'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { FileLabel } from 'vs/workbench/browser/labels'; +import { IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels'; import { CommentNode, CommentsModel, ResourceWithCommentThreads } from 'vs/workbench/parts/comments/common/commentModel'; export class CommentsDataSource implements IDataSource { @@ -56,7 +55,7 @@ export class CommentsDataSource implements IDataSource { } interface IResourceTemplateData { - resourceLabel: FileLabel; + resourceLabel: IResourceLabel; } interface ICommentThreadTemplateData { @@ -70,9 +69,8 @@ export class CommentsModelRenderer implements ITreeRenderer { private static RESOURCE_ID = 'resource-with-comments'; private static COMMENT_ID = 'comment-node'; - constructor( - @IInstantiationService private instantiationService: IInstantiationService, + private labels: ResourceLabels, @IOpenerService private openerService: IOpenerService ) { } @@ -124,7 +122,7 @@ export class CommentsModelRenderer implements ITreeRenderer { private renderResourceTemplate(container: HTMLElement): IResourceTemplateData { const data = Object.create(null); const labelContainer = dom.append(container, dom.$('.resource-container')); - data.resourceLabel = this.instantiationService.createInstance(FileLabel, labelContainer, {}); + data.resourceLabel = this.labels.create(labelContainer); return data; } diff --git a/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts index 7d9a3c90613132a25d8b829cac37ce98ac9c4ec2..9decf3c88c3c46cec931581a8f5d5457237f1e0b 100644 --- a/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts @@ -23,7 +23,7 @@ import { isWindows } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { ltrim } from 'vs/base/common/strings'; import { RunOnceScheduler } from 'vs/base/common/async'; -import { ResourceLabel, IResourceLabel, IResourceLabelOptions } from 'vs/workbench/browser/labels'; +import { ResourceLabels, IResourceLabelProps, IResourceLabelOptions, IResourceLabel } from 'vs/workbench/browser/labels'; import { FileKind } from 'vs/platform/files/common/files'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; @@ -32,6 +32,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { WorkbenchAsyncDataTree, IListService, TreeResourceNavigator2 } from 'vs/platform/list/browser/listService'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { DebugContentProvider } from 'vs/workbench/parts/debug/browser/debugContentProvider'; +import { dispose } from 'vs/base/common/lifecycle'; const SMART = true; @@ -363,6 +364,7 @@ export class LoadedScriptsView extends ViewletPanel { private treeContainer: HTMLElement; private loadedScriptsItemType: IContextKey; private tree: WorkbenchAsyncDataTree; + private treeLabels: ResourceLabels; private changeScheduler: RunOnceScheduler; private treeNeedsRefreshOnVisible: boolean; private filter: LoadedScriptsFilter; @@ -395,10 +397,11 @@ export class LoadedScriptsView extends ViewletPanel { const root = new RootTreeItem(this.debugService.getModel(), this.environmentService, this.contextService); + this.treeLabels = this.instantiationService.createInstance(ResourceLabels); + this.disposables.push(this.treeLabels); + this.tree = new WorkbenchAsyncDataTree(this.treeContainer, new LoadedScriptsDelegate(), - [ - this.instantiationService.createInstance(LoadedScriptsRenderer) - ], + [new LoadedScriptsRenderer(this.treeLabels)], new LoadedScriptsDataSource(), { identityProvider: { @@ -507,6 +510,13 @@ export class LoadedScriptsView extends ViewletPanel { if (visible && this.treeNeedsRefreshOnVisible) { this.changeScheduler.schedule(); } + if (this.treeLabels) { + if (visible) { + this.treeLabels.onVisible(); + } else { + this.treeLabels.onHidden(); + } + } } /* @@ -518,7 +528,8 @@ export class LoadedScriptsView extends ViewletPanel { */ dispose(): void { - this.tree = undefined; + this.tree = dispose(this.tree); + this.treeLabels = dispose(this.treeLabels); super.dispose(); } } @@ -549,7 +560,7 @@ class LoadedScriptsDataSource implements IAsyncDataSource { @@ -557,7 +568,7 @@ class LoadedScriptsRenderer implements ITreeRenderer(); this.configListener = this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('explorer')) { @@ -231,7 +228,7 @@ export class FileRenderer implements IRenderer { public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): IFileTemplateData { const elementDisposable = Disposable.None; - const label = this.instantiationService.createInstance(FileLabel, container, void 0); + const label = this.labels.create(container); return { elementDisposable, label, container }; } @@ -268,7 +265,7 @@ export class FileRenderer implements IRenderer { private renderInputBox(container: HTMLElement, tree: ITree, stat: ExplorerItem, editableData: IEditableData): void { // Use a file label only for the icon next to the input box - const label = this.instantiationService.createInstance(FileLabel, container, void 0); + const label = this.labels.create(container); const extraClasses = ['explorer-item', 'explorer-item-edited']; const fileKind = stat.isRoot ? FileKind.ROOT_FOLDER : (stat.isDirectory || (stat instanceof NewStatPlaceholder && stat.isDirectoryPlaceholder())) ? FileKind.FOLDER : FileKind.FILE; const labelOptions: IFileLabelOptions = { hidePath: true, hideLabel: true, fileKind, extraClasses }; diff --git a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts index 400f005c7d4dcd678edc6e45c5930f5f3444e585..dee98bd96eabba929840a61857e9c963fb64b56d 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts @@ -26,7 +26,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { badgeBackground, badgeForeground, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { WorkbenchList } from 'vs/platform/list/browser/listService'; import { IListVirtualDelegate, IListRenderer, IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; -import { EditorLabel } from 'vs/workbench/browser/labels'; +import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; @@ -51,6 +51,7 @@ export class OpenEditorsView extends ViewletPanel { private listRefreshScheduler: RunOnceScheduler; private structuralRefreshDelay: number; private list: WorkbenchList; + private listLabels: ResourceLabels; private contributedContextMenu: IMenu; private needsRefresh: boolean; private resourceContext: ResourceContextKey; @@ -213,14 +214,19 @@ export class OpenEditorsView extends ViewletPanel { if (this.list) { this.list.dispose(); } + if (this.listLabels) { + this.listLabels.clear(); + } + this.listLabels = this.instantiationService.createInstance(ResourceLabels); this.list = this.instantiationService.createInstance(WorkbenchList, container, delegate, [ new EditorGroupRenderer(this.keybindingService, this.instantiationService, this.editorGroupService), - new OpenEditorRenderer(getSelectedElements, this.instantiationService, this.keybindingService, this.configurationService, this.editorGroupService) + new OpenEditorRenderer(this.listLabels, getSelectedElements, this.instantiationService, this.keybindingService, this.configurationService, this.editorGroupService) ], { identityProvider: { getId: (element: OpenEditor | IEditorGroup) => element instanceof OpenEditor ? element.getId() : element.id.toString() }, selectOnMouseDown: false /* disabled to better support DND */ }) as WorkbenchList; this.disposables.push(this.list); + this.disposables.push(this.listLabels); this.contributedContextMenu = this.menuService.createMenu(MenuId.OpenEditorsContext, this.list.contextKeyService); this.disposables.push(this.contributedContextMenu); @@ -327,6 +333,13 @@ export class OpenEditorsView extends ViewletPanel { dom.hide(this.list.getHTMLElement()); // make sure the list goes out of the tabindex world by hiding it } } + if (this.listLabels) { + if (isVisible) { + this.listLabels.onVisible(); + } else { + this.listLabels.onHidden(); + } + } } private get showGroups(): boolean { @@ -478,7 +491,7 @@ export class OpenEditorsView extends ViewletPanel { interface IOpenEditorTemplateData { container: HTMLElement; - root: EditorLabel; + root: IResourceLabel; actionBar: ActionBar; actionRunner: OpenEditorActionRunner; openEditor: OpenEditor; @@ -619,6 +632,7 @@ class OpenEditorRenderer implements IListRenderer(); constructor( + private labels: ResourceLabels, private getSelectedElements: () => Array, private instantiationService: IInstantiationService, private keybindingService: IKeybindingService, @@ -643,7 +657,7 @@ class OpenEditorRenderer implements IListRenderer> { const resourcesIt = Iterator.fromArray(model.resourceMarkers); @@ -66,6 +67,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { private currentActiveResource: URI | null = null; private tree: WorkbenchObjectTree; + private treeLabels: ResourceLabels; private rangeHighlightDecorations: RangeHighlightDecorations; private actions: IAction[]; @@ -170,6 +172,14 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { } else { this.rangeHighlightDecorations.removeHighlightRange(); } + + if (this.treeLabels) { + if (visible) { + this.treeLabels.onVisible(); + } else { + this.treeLabels.onHidden(); + } + } } public getActions(): IAction[] { @@ -287,10 +297,11 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { const onDidChangeRenderNodeCount = new Relay>(); + this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels)); + const virtualDelegate = new VirtualDelegate(this.markersViewState); const renderers = [ - this.instantiationService.createInstance(FileResourceMarkersRenderer, onDidChangeRenderNodeCount.event), - this.instantiationService.createInstance(ResourceMarkersRenderer, onDidChangeRenderNodeCount.event), + this.instantiationService.createInstance(ResourceMarkersRenderer, this.treeLabels, onDidChangeRenderNodeCount.event), this.instantiationService.createInstance(MarkerRenderer, this.markersViewState, a => this.getActionItem(a)), this.instantiationService.createInstance(RelatedInformationRenderer) ]; diff --git a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts index 9dfc41aa0b655944a38ea5bd6e4a2a95fde60cfa..0919da43160c7de1e39de0a32f7c552fda5e7510 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts @@ -7,7 +7,7 @@ import * as dom from 'vs/base/browser/dom'; import * as network from 'vs/base/common/network'; import * as paths from 'vs/base/common/paths'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; -import { FileLabel, ResourceLabel } from 'vs/workbench/browser/labels'; +import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import { IMarker, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { ResourceMarkers, Marker, RelatedInformation } from 'vs/workbench/parts/markers/electron-browser/markersModel'; @@ -34,7 +34,7 @@ import { localize } from 'vs/nls'; export type TreeElement = ResourceMarkers | Marker | RelatedInformation; interface IResourceMarkersTemplateData { - resourceLabel: ResourceLabel; + resourceLabel: IResourceLabel; count: CountBadge; styler: IDisposable; } @@ -69,7 +69,6 @@ export class MarkersTreeAccessibilityProvider implements IAccessibilityProvider< } const enum TemplateId { - FileResourceMarkers = 'frm', ResourceMarkers = 'rm', Marker = 'm', RelatedInformation = 'ri' @@ -90,11 +89,7 @@ export class VirtualDelegate implements IListVirtualDelegate { getTemplateId(element: TreeElement): string { if (element instanceof ResourceMarkers) { - if ((element).resource.scheme === network.Schemas.file || (element).resource.scheme === network.Schemas.untitled) { - return TemplateId.FileResourceMarkers; - } else { - return TemplateId.ResourceMarkers; - } + return TemplateId.ResourceMarkers; } else if (element instanceof Marker) { return TemplateId.Marker; } else { @@ -135,8 +130,8 @@ export class ResourceMarkersRenderer implements ITreeRenderer>, - @IInstantiationService protected instantiationService: IInstantiationService, @IThemeService private themeService: IThemeService, @ILabelService private labelService: ILabelService ) { @@ -149,7 +144,7 @@ export class ResourceMarkersRenderer implements ITreeRendererObject.create(null); const resourceLabelContainer = dom.append(container, dom.$('.resource-label-container')); - data.resourceLabel = this.createResourceLabel(resourceLabelContainer); + data.resourceLabel = this.labels.create(resourceLabelContainer, { supportHighlights: true }); const badgeWrapper = dom.append(container, dom.$('.count-badge-wrapper')); data.count = new CountBadge(badgeWrapper); @@ -162,10 +157,10 @@ export class ResourceMarkersRenderer implements ITreeRenderer): void { const templateData = this.renderedNodes.get(node); @@ -205,12 +196,6 @@ export class ResourceMarkersRenderer implements ITreeRenderer { diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index 0c9e2165219e6cbb1f55bd9d57e95c9229122914..fc4462f4a32650a2f98c9767e5b4608d6ab11b60 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -15,7 +15,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { List } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IListRenderer, IListContextMenuEvent, IListEvent, IKeyboardNavigationLabelProvider, IIdentityProvider } from 'vs/base/browser/ui/list/list'; import { VIEWLET_ID, VIEW_CONTAINER } from 'vs/workbench/parts/scm/common/scm'; -import { FileLabel } from 'vs/workbench/browser/labels'; +import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; import { ISCMService, ISCMRepository, ISCMResourceGroup, ISCMResource, InputValidationType } from 'vs/workbench/services/scm/common/scm'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -444,7 +444,7 @@ class ResourceGroupRenderer implements IListRenderer get templateId(): string { return ResourceRenderer.TEMPLATE_ID; } constructor( + private labels: ResourceLabels, private actionItemProvider: IActionItemProvider, private getSelectedResources: () => ISCMResource[], private themeService: IThemeService, - private instantiationService: IInstantiationService, private menus: SCMMenus ) { } renderTemplate(container: HTMLElement): ResourceTemplate { const element = append(container, $('.resource')); const name = append(element, $('.name')); - const fileLabel = this.instantiationService.createInstance(FileLabel, name, void 0); + const fileLabel = this.labels.create(name); const actionsContainer = append(fileLabel.element, $('.actions')); const actionBar = new ActionBar(actionsContainer, { actionItemProvider: this.actionItemProvider, @@ -728,6 +728,7 @@ export class RepositoryPanel extends ViewletPanel { private inputBox: InputBox; private listContainer: HTMLElement; private list: List; + private listLabels: ResourceLabels; private menus: SCMMenus; private visibilityDisposables: IDisposable[] = []; protected contextKeyService: IContextKeyService; @@ -869,9 +870,12 @@ export class RepositoryPanel extends ViewletPanel { const actionItemProvider = (action: IAction) => this.getActionItem(action); + this.listLabels = this.instantiationService.createInstance(ResourceLabels); + this.disposables.push(this.listLabels); + const renderers = [ new ResourceGroupRenderer(actionItemProvider, this.themeService, this.menus), - new ResourceRenderer(actionItemProvider, () => this.getSelectedResources(), this.themeService, this.instantiationService, this.menus) + new ResourceRenderer(this.listLabels, actionItemProvider, () => this.getSelectedResources(), this.themeService, this.menus) ]; this.list = this.instantiationService.createInstance(WorkbenchList, this.listContainer, delegate, renderers, { @@ -907,6 +911,18 @@ export class RepositoryPanel extends ViewletPanel { this.inputBox.setEnabled(this.isVisible() && this.isExpanded()); } + setVisible(visible: boolean): void { + super.setVisible(visible); + + if (this.listLabels) { + if (visible) { + this.listLabels.onVisible(); + } else { + this.listLabels.onHidden(); + } + } + } + setExpanded(expanded: boolean): void { super.setExpanded(expanded); this.inputBox.setEnabled(this.isVisible() && this.isExpanded()); diff --git a/src/vs/workbench/parts/search/browser/searchResultsView.ts b/src/vs/workbench/parts/search/browser/searchResultsView.ts index 56c778b42890f482271c169ca46b69b44639faab..87f7ee2457c46ae75f13d6365a4e2d5f695a95ce 100644 --- a/src/vs/workbench/parts/search/browser/searchResultsView.ts +++ b/src/vs/workbench/parts/search/browser/searchResultsView.ts @@ -23,7 +23,7 @@ import { ISearchConfigurationProperties } from 'vs/platform/search/common/search import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { FileLabel } from 'vs/workbench/browser/labels'; +import { IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels'; import { RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction } from 'vs/workbench/parts/search/browser/searchActions'; import { SearchView } from 'vs/workbench/parts/search/browser/searchView'; import { FileMatch, FolderMatch, Match, RenderableMatch, searchMatchComparer, SearchModel } from 'vs/workbench/parts/search/common/searchModel'; @@ -35,14 +35,14 @@ export class SearchSorter implements ISorter { } interface IFolderMatchTemplate { - label: FileLabel; + label: IResourceLabel; badge: CountBadge; actions: ActionBar; } interface IFileMatchTemplate { el: HTMLElement; - label: FileLabel; + label: IResourceLabel; badge: CountBadge; actions: ActionBar; } @@ -84,6 +84,7 @@ export class FolderMatchRenderer extends Disposable implements ITreeRenderer; + private treeLabels: ResourceLabels; private viewletState: object; private globalMemento: object; private messagesElement: HTMLElement; @@ -584,18 +586,19 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { } }; - this.tree = >this.instantiationService.createInstance(WorkbenchObjectTree, + this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels)); + this.tree = this._register(>this.instantiationService.createInstance(WorkbenchObjectTree, this.resultsElement, delegate, [ - this._register(this.instantiationService.createInstance(FolderMatchRenderer, this.viewModel, this)), - this._register(this.instantiationService.createInstance(FileMatchRenderer, this.viewModel, this)), + this._register(this.instantiationService.createInstance(FolderMatchRenderer, this.viewModel, this, this.treeLabels)), + this._register(this.instantiationService.createInstance(FileMatchRenderer, this.viewModel, this, this.treeLabels)), this._register(this.instantiationService.createInstance(MatchRenderer, this.viewModel, this)), ], { identityProvider, accessibilityProvider: this.instantiationService.createInstance(SearchAccessibilityProvider, this.viewModel) - }); + })); this._register(this.tree.onContextMenu(e => this.onContextMenu(e))); const resourceNavigator = this._register(new TreeResourceNavigator2(this.tree, { openOnFocus: true })); @@ -766,6 +769,14 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this.onFocus(focus, true); } } + + if (this.treeLabels) { + if (visible) { + this.treeLabels.onVisible(); + } else { + this.treeLabels.onHidden(); + } + } } public moveFocusToResults(): void {