From 41347912b3cd8ebc31130b7c0ffaa77369345f2a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 19 Dec 2018 12:45:53 +0100 Subject: [PATCH 01/17] labels - introduce ResourceLabels and adopt in tabs --- src/vs/workbench/browser/labels.ts | 293 ++++++++++++++++++ .../browser/parts/editor/tabsTitleControl.ts | 29 +- 2 files changed, 308 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index d5afcc62217..9c760f92849 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -22,6 +22,299 @@ 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 IResourceLabelHandle extends IDisposable { + readonly element: HTMLElement; + + setLabel(label: IResourceLabel, options?: IResourceLabelOptions): void; +} + +export class ResourceLabels extends Disposable { + private _widgets: ResourceLabelWidget[] = []; + private _labels: IResourceLabelHandle[] = []; + + constructor( + @IInstantiationService private instantiationService: IInstantiationService, + @IExtensionService private extensionService: IExtensionService, + @IConfigurationService private configurationService: IConfigurationService, + @IModelService private modelService: IModelService, + @IDecorationsService protected decorationsService: IDecorationsService, + @IThemeService private themeService: IThemeService, + @ILabelService protected labelService: ILabelService + ) { + super(); + + this.registerListeners(); + } + + get labels(): IResourceLabelHandle[] { + return this._labels; + } + + get(index: number): IResourceLabelHandle { + return this._labels[index]; + } + + private registerListeners(): void { + + // notify when extensions are registered with potentially new languages + this._register(this.extensionService.onDidRegisterExtensions(() => this._widgets.forEach(widget => widget.notifyExtensionsRegistered()))); + + // notify when model mode changes + this._register(this.modelService.onModelModeChanged(e => this._widgets.forEach(widget => widget.notifyModelModeChanged(e)))); + + // notify when file decoration changes + this._register(this.decorationsService.onDidChangeDecorations(e => this._widgets.forEach(widget => widget.notifyFileDecorationsChanges(e)))); + + // notify when theme changes + this._register(this.themeService.onThemeChange(() => () => this._widgets.forEach(widget => widget.notifyThemeChange()))); + + // notify when files.associations changes + this._register(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(FILES_ASSOCIATIONS_CONFIG)) { + this._widgets.forEach(widget => widget.notifyFileAssociationsChange()); + } + })); + } + + create(container: HTMLElement, options?: IIconLabelCreationOptions): IResourceLabelHandle { + const widget = this.instantiationService.createInstance(ResourceLabelWidget, container, options); + + // Only expose a handle to the outside + const label: IResourceLabelHandle = { + element: widget.element, + setLabel: (label: IResourceLabel, options?: IResourceLabelOptions) => widget.setLabel(label, options), + 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); + } + + clear(): void { + this._widgets = dispose(this._widgets); + this._labels = []; + } + + dispose(): void { + super.dispose(); + + this.clear(); + } +} + +class ResourceLabelWidget extends IconLabel { + + private _onDidRender = this._register(new Emitter()); + get onDidRender(): Event { return this._onDidRender.event; } + + private label: IResourceLabel; + private options: IResourceLabelOptions; + private computedIconClasses: string[]; + private lastKnownConfiguredLangId: string; + private computedPathLabel: string; + + constructor( + container: HTMLElement, + options: IIconLabelCreationOptions, + @IModeService private modeService: IModeService, + @IModelService private modelService: IModelService, + @IDecorationsService protected decorationsService: IDecorationsService, + @ILabelService protected labelService: ILabelService + ) { + super(container, options); + } + + notifyModelModeChanged(e: { model: ITextModel; oldModeId: string; }): void { + if (!this.label || !this.label.resource) { + return; // only update if label exists + } + + if (!e.model.uri) { + return; // we need the resource to compare + } + + if (e.model.uri.scheme === Schemas.file && e.oldModeId === PLAINTEXT_MODE_ID) { // todo@remote does this apply? + return; // ignore transitions in files from no mode to specific mode because this happens each time a model is created + } + + if (e.model.uri.toString() === this.label.resource.toString()) { + if (this.lastKnownConfiguredLangId !== e.model.getLanguageIdentifier().language) { + this.render(true); // update if the language id of the model has changed from our last known state + } + } + } + + notifyFileDecorationsChanges(e: IResourceDecorationChangeEvent): void { + if (!this.options || !this.label || !this.label.resource) { + return; + } + + if (this.options.fileDecorations && e.affectsResource(this.label.resource)) { + this.render(false); + } + } + + notifyExtensionsRegistered(): void { + this.render(true); + } + + notifyThemeChange(): void { + this.render(false); + } + + notifyFileAssociationsChange(): void { + this.render(true); + } + + setLabel(label: IResourceLabel, options?: IResourceLabelOptions): void { + const hasResourceChanged = this.hasResourceChanged(label, options); + + this.label = label; + this.options = options; + + if (hasResourceChanged) { + this.computedPathLabel = void 0; // reset path label due to resource change + } + + this.render(hasResourceChanged); + } + + private hasResourceChanged(label: IResourceLabel, options: IResourceLabelOptions): boolean { + const newResource = label ? label.resource : void 0; + const oldResource = this.label ? this.label.resource : void 0; + + const newFileKind = options ? options.fileKind : void 0; + const oldFileKind = this.options ? this.options.fileKind : void 0; + + if (newFileKind !== oldFileKind) { + return true; // same resource but different kind (file, folder) + } + + if (newResource && this.computedPathLabel !== this.labelService.getUriLabel(newResource)) { + return true; + } + + if (newResource && oldResource) { + return newResource.toString() !== oldResource.toString(); + } + + if (!newResource && !oldResource) { + return false; + } + + return true; + } + + clear(): void { + this.label = void 0; + this.options = void 0; + this.lastKnownConfiguredLangId = void 0; + this.computedIconClasses = void 0; + this.computedPathLabel = void 0; + + this.setValue(); + } + + private render(clearIconCache: boolean): void { + if (this.label) { + const configuredLangId = getConfiguredLangId(this.modelService, this.label.resource); + if (this.lastKnownConfiguredLangId !== configuredLangId) { + clearIconCache = true; + this.lastKnownConfiguredLangId = configuredLangId; + } + } + + if (clearIconCache) { + this.computedIconClasses = void 0; + } + + if (!this.label) { + return; + } + + const iconLabelOptions: IIconLabelValueOptions = { + title: '', + italic: this.options && this.options.italic, + matches: this.options && this.options.matches, + extraClasses: [] + }; + + const resource = this.label.resource; + const label = this.label.name; + + if (this.options && typeof this.options.title === 'string') { + iconLabelOptions.title = this.options.title; + } else if (resource && resource.scheme !== Schemas.data /* do not accidentally inline Data URIs */) { + if (!this.computedPathLabel) { + this.computedPathLabel = this.labelService.getUriLabel(resource); + } + + iconLabelOptions.title = this.computedPathLabel; + } + + if (this.options && !this.options.hideIcon) { + if (!this.computedIconClasses) { + this.computedIconClasses = getIconClasses(this.modelService, this.modeService, resource, this.options && this.options.fileKind); + } + iconLabelOptions.extraClasses = this.computedIconClasses.slice(0); + } + if (this.options && this.options.extraClasses) { + iconLabelOptions.extraClasses.push(...this.options.extraClasses); + } + + if (this.options && this.options.fileDecorations && resource) { + const deco = this.decorationsService.getDecoration( + resource, + this.options.fileKind !== FileKind.FILE, + this.options.fileDecorations.data + ); + + if (deco) { + if (deco.tooltip) { + iconLabelOptions.title = `${iconLabelOptions.title} • ${deco.tooltip}`; + } + + if (this.options.fileDecorations.colors) { + iconLabelOptions.extraClasses.push(deco.labelClassName); + } + + if (this.options.fileDecorations.badges) { + iconLabelOptions.extraClasses.push(deco.badgeClassName); + } + } + } + + this.setValue(label, this.label.description, iconLabelOptions); + + this._onDidRender.fire(); + } + + dispose(): void { + super.dispose(); + + this.label = void 0; + this.options = void 0; + this.lastKnownConfiguredLangId = void 0; + this.computedIconClasses = void 0; + this.computedPathLabel = void 0; + } +} export interface IResourceLabel { name: string; diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 94e3f71e332..b740e159fce 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, IResourceLabelHandle } 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: IResourceLabelHandle, 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: IResourceLabelHandle, 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: IResourceLabelHandle, 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: IResourceLabelHandle, tabLabel: IEditorInputLabel): void { const name = tabLabel.name; const description = tabLabel.description || ''; const title = tabLabel.title || ''; @@ -861,7 +862,7 @@ export class TabsTitleControl extends TitleControl { tabLabelWidget.setLabel({ 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: IResourceLabelHandle): 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: IResourceLabelHandle): void { // Tab is active if (this.group.isActive(editor)) { From 7a8ad74800a569093596eb8499e0421c2466ffcb Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 19 Dec 2018 12:57:58 +0100 Subject: [PATCH 02/17] labels - adopt SingleResourceLabel for no tabs title --- src/vs/workbench/browser/labels.ts | 35 +++++++++++++++++-- .../parts/editor/noTabsTitleControl.ts | 6 ++-- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 9c760f92849..0f144179c5e 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -29,6 +29,9 @@ export interface IResourceLabelHandle extends IDisposable { readonly element: HTMLElement; setLabel(label: IResourceLabel, options?: IResourceLabelOptions): void; + clear(): void; + + onClick(callback: (event: MouseEvent) => void): IDisposable; } export class ResourceLabels extends Disposable { @@ -40,9 +43,8 @@ export class ResourceLabels extends Disposable { @IExtensionService private extensionService: IExtensionService, @IConfigurationService private configurationService: IConfigurationService, @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(); @@ -86,6 +88,8 @@ export class ResourceLabels extends Disposable { const label: IResourceLabelHandle = { element: widget.element, setLabel: (label: IResourceLabel, options?: IResourceLabelOptions) => widget.setLabel(label, options), + clear: () => widget.clear(), + onClick: (callback: (event: MouseEvent) => void) => widget.onClick(callback), dispose: () => this.disposeWidget(widget) }; @@ -118,6 +122,31 @@ export class ResourceLabels extends Disposable { } } +export class SingleResourceLabel extends ResourceLabels { + + private _label: IResourceLabelHandle; + + 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(): IResourceLabelHandle { + return this._label; + } +} + class ResourceLabelWidget extends IconLabel { private _onDidRender = this._register(new Emitter()); diff --git a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts index 7eddf53b4f0..0894a4f723d 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 { SingleResourceLabel, IResourceLabelHandle } 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: IResourceLabelHandle; private activeLabel: IRenderedEditorLabel = Object.create(null); protected create(parent: HTMLElement): void { @@ -40,7 +40,7 @@ export class NoTabsTitleControl extends TitleControl { this.titleContainer.appendChild(labelContainer); // Editor Label - this.editorLabel = this._register(this.instantiationService.createInstance(ResourceLabel, labelContainer, void 0)); + this.editorLabel = this._register(this.instantiationService.createInstance(SingleResourceLabel, labelContainer, void 0)).element; this._register(this.editorLabel.onClick(e => this.onTitleLabelClick(e))); // Breadcrumbs From ef657c64559d26e00eeb80c3f06abe959e28c1d2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 19 Dec 2018 14:48:37 +0100 Subject: [PATCH 03/17] labels - adopt for custom view --- .../workbench/browser/parts/views/customView.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index df91b8ee956..98bfb54f46d 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -23,7 +23,7 @@ import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/work import { ICommandService } from 'vs/platform/commands/common/commands'; import * as DOM from 'vs/base/browser/dom'; import { IDataSource, ITree, IRenderer, ContextMenuEvent } from 'vs/base/parts/tree/browser/tree'; -import { ResourceLabel } from 'vs/workbench/browser/labels'; +import { ResourceLabels, IResourceLabelHandle } from 'vs/workbench/browser/labels'; import { ActionBar, IActionItemProvider, ActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { URI } from 'vs/base/common/uri'; import { basename } from 'vs/base/common/paths'; @@ -382,13 +382,13 @@ export class CustomTreeView extends Disposable implements ITreeView { private createTree() { const actionItemProvider = (action: IAction) => 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)); + const labels = 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, labels, 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 +593,7 @@ class TreeDataSource implements IDataSource { } interface ITreeExplorerTemplateData { - resourceLabel: ResourceLabel; + resourceLabel: IResourceLabelHandle; icon: HTMLElement; actionBar: ActionBar; aligner: Aligner; @@ -632,8 +632,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 +652,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, { From 2d892bd51b9261b665757c6a9b1dbec0d9df47c2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 19 Dec 2018 14:52:32 +0100 Subject: [PATCH 04/17] labels - adopt for loaded scripts view --- .../parts/debug/browser/loadedScriptsView.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts index 7d9a3c90613..441a1621060 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, IResourceLabel, IResourceLabelOptions, IResourceLabelHandle } 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; @@ -366,6 +367,7 @@ export class LoadedScriptsView extends ViewletPanel { private changeScheduler: RunOnceScheduler; private treeNeedsRefreshOnVisible: boolean; private filter: LoadedScriptsFilter; + private labels: ResourceLabels; constructor( options: IViewletViewOptions, @@ -395,10 +397,10 @@ export class LoadedScriptsView extends ViewletPanel { const root = new RootTreeItem(this.debugService.getModel(), this.environmentService, this.contextService); + this.labels = this.instantiationService.createInstance(ResourceLabels); + this.tree = new WorkbenchAsyncDataTree(this.treeContainer, new LoadedScriptsDelegate(), - [ - this.instantiationService.createInstance(LoadedScriptsRenderer) - ], + [new LoadedScriptsRenderer(this.labels)], new LoadedScriptsDataSource(), { identityProvider: { @@ -518,7 +520,8 @@ export class LoadedScriptsView extends ViewletPanel { */ dispose(): void { - this.tree = undefined; + this.tree = dispose(this.tree); + this.labels = dispose(this.labels); super.dispose(); } } @@ -549,7 +552,7 @@ class LoadedScriptsDataSource implements IAsyncDataSource { @@ -557,7 +560,7 @@ class LoadedScriptsRenderer implements ITreeRenderer Date: Wed, 19 Dec 2018 15:09:01 +0100 Subject: [PATCH 05/17] labels - adopt for markers panel --- src/vs/workbench/browser/labels.ts | 55 ++++++++++++++++++- .../markers/electron-browser/markersPanel.ts | 8 ++- .../electron-browser/markersTreeViewer.ts | 26 ++------- 3 files changed, 64 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 0f144179c5e..335b63cd109 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -28,7 +28,21 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti export interface IResourceLabelHandle extends IDisposable { readonly element: HTMLElement; + /** + * Most basic method to apply a label. + */ setLabel(label: IResourceLabel, options?: IResourceLabelOptions): void; + + /** + * Convinient method to apply a label by passing an editor along. + */ + setEditor(editor: IEditorInput, options?: IResourceLabelOptions): void; + + /** + * Convinient method to render a file label based on a resource. + */ + setFile(resource: uri, options?: IFileLabelOptions): void; + clear(): void; onClick(callback: (event: MouseEvent) => void): IDisposable; @@ -88,6 +102,8 @@ export class ResourceLabels extends Disposable { const label: IResourceLabelHandle = { element: widget.element, setLabel: (label: IResourceLabel, options?: IResourceLabelOptions) => widget.setLabel(label, options), + setEditor: (editor: IEditorInput, options?: IResourceLabelOptions) => widget.setEditor(editor, options), + setFile: (resource: uri, options?: IFileLabelOptions) => widget.setFile(resource, options), clear: () => widget.clear(), onClick: (callback: (event: MouseEvent) => void) => widget.onClick(callback), dispose: () => this.disposeWidget(widget) @@ -163,8 +179,10 @@ class ResourceLabelWidget extends IconLabel { options: IIconLabelCreationOptions, @IModeService private modeService: IModeService, @IModelService private modelService: IModelService, - @IDecorationsService protected decorationsService: IDecorationsService, - @ILabelService protected labelService: ILabelService + @IDecorationsService private decorationsService: IDecorationsService, + @ILabelService private labelService: ILabelService, + @IUntitledEditorService private untitledEditorService: IUntitledEditorService, + @IWorkspaceContextService private contextService: IWorkspaceContextService ) { super(container, options); } @@ -250,6 +268,39 @@ class ResourceLabelWidget extends IconLabel { return true; } + setEditor(editor: IEditorInput, options?: IResourceLabelOptions): void { + this.setLabel({ + 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.setLabel({ resource, name, description }, options); + } + clear(): void { this.label = void 0; this.options = void 0; diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts index 18eb2ab1d2b..2787c86c2f2 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts @@ -33,7 +33,7 @@ import { IExpression, getEmptyExpression } from 'vs/base/common/glob'; import { mixin, deepClone } from 'vs/base/common/objects'; import { IWorkspaceFolder, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { isAbsolute, join } from 'vs/base/common/paths'; -import { FilterData, FileResourceMarkersRenderer, Filter, VirtualDelegate, ResourceMarkersRenderer, MarkerRenderer, RelatedInformationRenderer, TreeElement, MarkersTreeAccessibilityProvider, MarkersViewState } from 'vs/workbench/parts/markers/electron-browser/markersTreeViewer'; +import { FilterData, Filter, VirtualDelegate, ResourceMarkersRenderer, MarkerRenderer, RelatedInformationRenderer, TreeElement, MarkersTreeAccessibilityProvider, MarkersViewState } from 'vs/workbench/parts/markers/electron-browser/markersTreeViewer'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { Separator, ActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; @@ -42,6 +42,7 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { domEvent } from 'vs/base/browser/event'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { ResourceLabels } from 'vs/workbench/browser/labels'; function createModelIterator(model: MarkersModel): Iterator> { const resourcesIt = Iterator.fromArray(model.resourceMarkers); @@ -287,10 +288,11 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { const onDidChangeRenderNodeCount = new Relay>(); + const labels = 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, labels, 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 9dfc41aa0b6..d4f8ea91128 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, IResourceLabelHandle } 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: IResourceLabelHandle; 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,6 +130,7 @@ export class ResourceMarkersRenderer implements ITreeRenderer>, @IInstantiationService protected instantiationService: IInstantiationService, @IThemeService private themeService: IThemeService, @@ -149,7 +145,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,7 +158,7 @@ export class ResourceMarkersRenderer implements ITreeRenderer): void { const templateData = this.renderedNodes.get(node); @@ -205,12 +197,6 @@ export class ResourceMarkersRenderer implements ITreeRenderer { From 5d04ca2ec72c3422d1ed56c6b1957519d6651e7e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 19 Dec 2018 15:13:22 +0100 Subject: [PATCH 06/17] labels - adopt for open editors view --- src/vs/workbench/browser/labels.ts | 43 +++++++------------ .../electron-browser/views/openEditorsView.ts | 15 +++++-- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 335b63cd109..46ad96352c2 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -25,6 +25,22 @@ import { getIconClasses, getConfiguredLangId } from 'vs/editor/common/services/g import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +export interface IResourceLabel { + name: string; + description?: string; + resource?: uri; +} + +export interface IResourceLabelOptions extends IIconLabelValueOptions { + fileKind?: FileKind; + fileDecorations?: { colors: boolean, badges: boolean, data?: IDecorationData }; +} + +export interface IFileLabelOptions extends IResourceLabelOptions { + hideLabel?: boolean; + hidePath?: boolean; +} + export interface IResourceLabelHandle extends IDisposable { readonly element: HTMLElement; @@ -396,17 +412,6 @@ class ResourceLabelWidget extends IconLabel { } } -export interface IResourceLabel { - name: string; - description?: string; - resource?: uri; -} - -export interface IResourceLabelOptions extends IIconLabelValueOptions { - fileKind?: FileKind; - fileDecorations?: { colors: boolean, badges: boolean, data?: IDecorationData }; -} - export class ResourceLabel extends IconLabel { private _onDidRender = this._register(new Emitter()); @@ -620,22 +625,6 @@ export class ResourceLabel extends IconLabel { } } -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( 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 400f005c7d4..50d9591e7eb 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, IResourceLabelHandle } 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 labels: 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.labels) { + this.labels.clear(); + } + this.labels = 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.labels, 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.labels); this.contributedContextMenu = this.menuService.createMenu(MenuId.OpenEditorsContext, this.list.contextKeyService); this.disposables.push(this.contributedContextMenu); @@ -478,7 +484,7 @@ export class OpenEditorsView extends ViewletPanel { interface IOpenEditorTemplateData { container: HTMLElement; - root: EditorLabel; + root: IResourceLabelHandle; actionBar: ActionBar; actionRunner: OpenEditorActionRunner; openEditor: OpenEditor; @@ -619,6 +625,7 @@ class OpenEditorRenderer implements IListRenderer(); constructor( + private labels: ResourceLabels, private getSelectedElements: () => Array, private instantiationService: IInstantiationService, private keybindingService: IKeybindingService, @@ -643,7 +650,7 @@ class OpenEditorRenderer implements IListRenderer Date: Wed, 19 Dec 2018 15:20:07 +0100 Subject: [PATCH 07/17] labels - adopt for search view --- src/vs/workbench/browser/labels.ts | 10 +++++++++- .../parts/search/browser/searchResultsView.ts | 12 +++++++----- src/vs/workbench/parts/search/browser/searchView.ts | 11 +++++++---- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 46ad96352c2..5d8a722a579 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -45,7 +45,14 @@ export interface IResourceLabelHandle extends IDisposable { readonly element: HTMLElement; /** - * Most basic method to apply a label. + * Most generic way to apply a label directly. + */ + setValue(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. */ setLabel(label: IResourceLabel, options?: IResourceLabelOptions): void; @@ -117,6 +124,7 @@ export class ResourceLabels extends Disposable { // Only expose a handle to the outside const label: IResourceLabelHandle = { element: widget.element, + setValue: (label?: string, description?: string, options?: IIconLabelValueOptions) => widget.setValue(label, description, options), setLabel: (label: IResourceLabel, options?: IResourceLabelOptions) => widget.setLabel(label, options), setEditor: (editor: IEditorInput, options?: IResourceLabelOptions) => widget.setEditor(editor, options), setFile: (resource: uri, options?: IFileLabelOptions) => widget.setFile(resource, options), diff --git a/src/vs/workbench/parts/search/browser/searchResultsView.ts b/src/vs/workbench/parts/search/browser/searchResultsView.ts index 56c778b4289..958d2db37a1 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 { IResourceLabelHandle, 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: IResourceLabelHandle; badge: CountBadge; actions: ActionBar; } interface IFileMatchTemplate { el: HTMLElement; - label: FileLabel; + label: IResourceLabelHandle; badge: CountBadge; actions: ActionBar; } @@ -84,6 +84,7 @@ export class FolderMatchRenderer extends Disposable implements ITreeRenderer; + private labels: 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.labels = 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.labels)), + this._register(this.instantiationService.createInstance(FileMatchRenderer, this.viewModel, this, this.labels)), 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 })); From f729ca0e7b11533709075d2b17ed0461fcb4a943 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 19 Dec 2018 15:22:42 +0100 Subject: [PATCH 08/17] labels - adopt for SCM viewlet --- .../parts/scm/electron-browser/scmViewlet.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index 0c9e2165219..65087f5e1c3 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, IResourceLabelHandle } 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 labels: 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.labels = this.instantiationService.createInstance(ResourceLabels); + this.disposables.push(this.labels); + const renderers = [ new ResourceGroupRenderer(actionItemProvider, this.themeService, this.menus), - new ResourceRenderer(actionItemProvider, () => this.getSelectedResources(), this.themeService, this.instantiationService, this.menus) + new ResourceRenderer(this.labels, actionItemProvider, () => this.getSelectedResources(), this.themeService, this.menus) ]; this.list = this.instantiationService.createInstance(WorkbenchList, this.listContainer, delegate, renderers, { From ab168f71b7cda53c2fafd11f5962556bcdc33c63 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 19 Dec 2018 15:29:36 +0100 Subject: [PATCH 09/17] labels - adopt in comments panel --- .../parts/comments/electron-browser/commentsPanel.ts | 10 +++++++--- .../comments/electron-browser/commentsTreeViewer.ts | 10 ++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts b/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts index 5745d58e4f5..b2423f8c6a3 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 labels: 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.labels = 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.labels, 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 => { diff --git a/src/vs/workbench/parts/comments/electron-browser/commentsTreeViewer.ts b/src/vs/workbench/parts/comments/electron-browser/commentsTreeViewer.ts index 8bf50628901..7509fdffaf4 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 { IResourceLabelHandle, 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: IResourceLabelHandle; } 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; } From 5bf377230ac97806f9e8da5ade68c5f0c650d678 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 19 Dec 2018 15:35:03 +0100 Subject: [PATCH 10/17] labels - adopt for explorer --- src/vs/workbench/browser/labels.ts | 2 ++ .../files/electron-browser/views/explorerView.ts | 6 +++++- .../electron-browser/views/explorerViewer.ts | 15 ++++++--------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 5d8a722a579..d37d5ea0ddb 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -43,6 +43,7 @@ export interface IFileLabelOptions extends IResourceLabelOptions { export interface IResourceLabelHandle extends IDisposable { readonly element: HTMLElement; + readonly onDidRender: Event; /** * Most generic way to apply a label directly. @@ -124,6 +125,7 @@ export class ResourceLabels extends Disposable { // Only expose a handle to the outside const label: IResourceLabelHandle = { element: widget.element, + onDidRender: widget.onDidRender, setValue: (label?: string, description?: string, options?: IIconLabelValueOptions) => widget.setValue(label, description, options), setLabel: (label: IResourceLabel, options?: IResourceLabelOptions) => widget.setLabel(label, options), setEditor: (editor: IEditorInput, options?: IResourceLabelOptions) => widget.setEditor(editor, options), diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts index f2af83afd23..889411a3949 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -42,6 +42,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; import { ILabelService } from 'vs/platform/label/common/label'; +import { ResourceLabels } from 'vs/workbench/browser/labels'; export interface IExplorerViewOptions extends IViewletViewOptions { fileViewletState: FileViewletState; @@ -59,6 +60,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView public readonly id: string = ExplorerView.ID; private explorerViewer: WorkbenchTree; + private explorerLabels: ResourceLabels; private filter: FileFilter; private fileViewletState: FileViewletState; @@ -406,7 +408,9 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView private createViewer(container: HTMLElement): WorkbenchTree { const dataSource = this.instantiationService.createInstance(FileDataSource); - const renderer = this.instantiationService.createInstance(FileRenderer, this.fileViewletState); + this.explorerLabels = this.instantiationService.createInstance(ResourceLabels); + this.disposables.push(this.explorerLabels); + const renderer = this.instantiationService.createInstance(FileRenderer, this.fileViewletState, this.explorerLabels); const controller = this.instantiationService.createInstance(FileController); this.disposables.push(controller); const sorter = this.instantiationService.createInstance(FileSorter); diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts index d5b8d08fd31..04c6ada895b 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -17,7 +17,7 @@ import * as comparers from 'vs/base/common/comparers'; import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; import { isMacintosh, isLinux } from 'vs/base/common/platform'; import * as glob from 'vs/base/common/glob'; -import { FileLabel, IFileLabelOptions } from 'vs/workbench/browser/labels'; +import { ResourceLabels, IFileLabelOptions, IResourceLabelHandle } from 'vs/workbench/browser/labels'; import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { IFilesConfiguration, SortOrder } from 'vs/workbench/parts/files/common/files'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; @@ -180,7 +180,7 @@ export class ActionRunner extends BaseActionRunner implements IActionRunner { export interface IFileTemplateData { elementDisposable: IDisposable; - label: FileLabel; + label: IResourceLabelHandle; container: HTMLElement; } @@ -190,20 +190,17 @@ export class FileRenderer implements IRenderer { private static readonly ITEM_HEIGHT = 22; private static readonly FILE_TEMPLATE_ID = 'file'; - private state: FileViewletState; private config: IFilesConfiguration; private configListener: IDisposable; constructor( - state: FileViewletState, + private state: FileViewletState, + private labels: ResourceLabels, @IContextViewService private contextViewService: IContextViewService, - @IInstantiationService private instantiationService: IInstantiationService, @IThemeService private themeService: IThemeService, @IConfigurationService private configurationService: IConfigurationService, @IWorkspaceContextService private contextService: IWorkspaceContextService - ) { - this.state = state; this.config = this.configurationService.getValue(); 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 }; From 5d5e7e715aae1650f81c190b46b9fa1d9953cdcf Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 19 Dec 2018 15:39:30 +0100 Subject: [PATCH 11/17] labels - adopt for breadcrumbs --- .../browser/parts/editor/breadcrumbsControl.ts | 6 +++--- .../browser/parts/editor/breadcrumbsPicker.ts | 14 ++++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index ab096bf28c5..c066ece6ceb 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 { SingleResourceLabel } 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(SingleResourceLabel, container, {}); + label.element.setFile(this.element.uri, { hidePath: true, hideIcon: this.element.kind === FileKind.FOLDER || !this.options.showFileIcons, fileKind: this.element.kind, diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index 88055da1e0e..a69da285dde 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, IResourceLabelHandle } 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: IResourceLabelHandle): 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: IResourceLabelHandle): 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; From c87e3e7929b01c18c577b55c109f55870ae0f02c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 19 Dec 2018 15:47:30 +0100 Subject: [PATCH 12/17] labels - renames --- src/vs/base/browser/ui/iconLabel/iconLabel.ts | 2 +- .../parts/quickopen/browser/quickOpenModel.ts | 2 +- .../contrib/referenceSearch/referencesTree.ts | 2 +- .../editor/contrib/suggest/suggestWidget.ts | 2 +- src/vs/workbench/browser/labels.ts | 305 ++---------------- .../parts/editor/breadcrumbsControl.ts | 8 +- .../browser/parts/editor/breadcrumbsPicker.ts | 6 +- .../parts/editor/noTabsTitleControl.ts | 8 +- .../browser/parts/editor/tabsTitleControl.ts | 16 +- .../parts/quickinput/quickInputList.ts | 2 +- .../browser/parts/views/customView.ts | 8 +- .../electron-browser/commentsTreeViewer.ts | 4 +- .../parts/debug/browser/loadedScriptsView.ts | 8 +- .../electron-browser/views/explorerViewer.ts | 4 +- .../electron-browser/views/openEditorsView.ts | 4 +- .../electron-browser/markersTreeViewer.ts | 6 +- .../parts/scm/electron-browser/scmViewlet.ts | 4 +- .../parts/search/browser/searchResultsView.ts | 8 +- 18 files changed, 73 insertions(+), 326 deletions(-) diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index 752437df1ba..7b0ee1cd2c7 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts @@ -122,7 +122,7 @@ export class IconLabel extends Disposable { ]); } - 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 189f85e4841..132f1207fac 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 e8a43e25ed2..e9282d70092 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 de7330e305e..ae57d64b7db 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 d37d5ea0ddb..1a077032f75 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -25,10 +25,10 @@ import { getIconClasses, getConfiguredLangId } from 'vs/editor/common/services/g 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 { @@ -41,21 +41,21 @@ export interface IFileLabelOptions extends IResourceLabelOptions { hidePath?: boolean; } -export interface IResourceLabelHandle extends IDisposable { +export interface IResourceLabel extends IDisposable { readonly element: HTMLElement; readonly onDidRender: Event; /** * Most generic way to apply a label directly. */ - setValue(label?: string, description?: string, options?: IIconLabelValueOptions): void; + 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. */ - setLabel(label: IResourceLabel, options?: IResourceLabelOptions): void; + setResource(label: IResourceLabelProps, options?: IResourceLabelOptions): void; /** * Convinient method to apply a label by passing an editor along. @@ -74,7 +74,7 @@ export interface IResourceLabelHandle extends IDisposable { export class ResourceLabels extends Disposable { private _widgets: ResourceLabelWidget[] = []; - private _labels: IResourceLabelHandle[] = []; + private _labels: IResourceLabel[] = []; constructor( @IInstantiationService private instantiationService: IInstantiationService, @@ -89,11 +89,11 @@ export class ResourceLabels extends Disposable { this.registerListeners(); } - get labels(): IResourceLabelHandle[] { + get labels(): IResourceLabel[] { return this._labels; } - get(index: number): IResourceLabelHandle { + get(index: number): IResourceLabel { return this._labels[index]; } @@ -119,15 +119,15 @@ export class ResourceLabels extends Disposable { })); } - create(container: HTMLElement, options?: IIconLabelCreationOptions): IResourceLabelHandle { + create(container: HTMLElement, options?: IIconLabelCreationOptions): IResourceLabel { const widget = this.instantiationService.createInstance(ResourceLabelWidget, container, options); // Only expose a handle to the outside - const label: IResourceLabelHandle = { + const label: IResourceLabel = { element: widget.element, onDidRender: widget.onDidRender, - setValue: (label?: string, description?: string, options?: IIconLabelValueOptions) => widget.setValue(label, description, options), - setLabel: (label: IResourceLabel, options?: IResourceLabelOptions) => widget.setLabel(label, options), + 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(), @@ -164,9 +164,13 @@ export class ResourceLabels extends Disposable { } } -export class SingleResourceLabel extends ResourceLabels { +/** + * 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: IResourceLabelHandle; + private _label: IResourceLabel; constructor( container: HTMLElement, @@ -184,7 +188,7 @@ export class SingleResourceLabel extends ResourceLabels { this._label = this._register(this.create(container, options)); } - get element(): IResourceLabelHandle { + get element(): IResourceLabel { return this._label; } } @@ -194,7 +198,7 @@ class ResourceLabelWidget extends IconLabel { private _onDidRender = this._register(new Emitter()); get onDidRender(): Event { return this._onDidRender.event; } - private label: IResourceLabel; + private label: IResourceLabelProps; private options: IResourceLabelOptions; private computedIconClasses: string[]; private lastKnownConfiguredLangId: string; @@ -255,7 +259,7 @@ class ResourceLabelWidget extends IconLabel { this.render(true); } - setLabel(label: IResourceLabel, options?: IResourceLabelOptions): void { + setResource(label: IResourceLabelProps, options?: IResourceLabelOptions): void { const hasResourceChanged = this.hasResourceChanged(label, options); this.label = label; @@ -268,7 +272,7 @@ class ResourceLabelWidget 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; @@ -295,7 +299,7 @@ class ResourceLabelWidget extends IconLabel { } setEditor(editor: IEditorInput, options?: IResourceLabelOptions): void { - this.setLabel({ + this.setResource({ resource: toResource(editor, { supportSideBySide: true }), name: editor.getName(), description: editor.getDescription() @@ -324,7 +328,7 @@ class ResourceLabelWidget extends IconLabel { description = this.labelService.getUriLabel(resources.dirname(resource), { relative: true }); } - this.setLabel({ resource, name, description }, options); + this.setResource({ resource, name, description }, options); } clear(): void { @@ -334,7 +338,7 @@ class ResourceLabelWidget extends IconLabel { this.computedIconClasses = void 0; this.computedPathLabel = void 0; - this.setValue(); + this.setLabel(); } private render(clearIconCache: boolean): void { @@ -406,7 +410,7 @@ class ResourceLabelWidget extends IconLabel { } } - this.setValue(label, this.label.description, iconLabelOptions); + this.setLabel(label, this.label.description, iconLabelOptions); this._onDidRender.fire(); } @@ -421,260 +425,3 @@ class ResourceLabelWidget extends IconLabel { this.computedPathLabel = void 0; } } - -export class ResourceLabel extends IconLabel { - - private _onDidRender = this._register(new Emitter()); - get onDidRender(): Event { return this._onDidRender.event; } - - private label: IResourceLabel; - private options: IResourceLabelOptions; - private computedIconClasses: string[]; - private lastKnownConfiguredLangId: string; - private computedPathLabel: string; - - constructor( - container: HTMLElement, - options: IIconLabelCreationOptions, - @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 - ) { - super(container, options); - - this.registerListeners(); - } - - private registerListeners(): void { - - // update when extensions are registered with potentially new languages - this._register(this.extensionService.onDidRegisterExtensions(() => this.render(true /* clear cache */))); - - // react to model mode changes - this._register(this.modelService.onModelModeChanged(e => this.onModelModeChanged(e))); - - // react to file decoration changes - this._register(this.decorationsService.onDidChangeDecorations(this.onFileDecorationsChanges, this)); - - // react to theme changes - this._register(this.themeService.onThemeChange(() => this.render(false))); - - // react to files.associations changes - this._register(this.configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(FILES_ASSOCIATIONS_CONFIG)) { - this.render(true /* clear cache */); - } - })); - } - - private onModelModeChanged(e: { model: ITextModel; oldModeId: string; }): void { - if (!this.label || !this.label.resource) { - return; // only update if label exists - } - - if (!e.model.uri) { - return; // we need the resource to compare - } - - if (e.model.uri.scheme === Schemas.file && e.oldModeId === PLAINTEXT_MODE_ID) { // todo@remote does this apply? - return; // ignore transitions in files from no mode to specific mode because this happens each time a model is created - } - - if (e.model.uri.toString() === this.label.resource.toString()) { - if (this.lastKnownConfiguredLangId !== e.model.getLanguageIdentifier().language) { - this.render(true); // update if the language id of the model has changed from our last known state - } - } - } - - private onFileDecorationsChanges(e: IResourceDecorationChangeEvent): void { - if (!this.options || !this.label || !this.label.resource) { - return; - } - - if (this.options.fileDecorations && e.affectsResource(this.label.resource)) { - this.render(false); - } - } - - setLabel(label: IResourceLabel, options?: IResourceLabelOptions): void { - const hasResourceChanged = this.hasResourceChanged(label, options); - - this.label = label; - this.options = options; - - if (hasResourceChanged) { - this.computedPathLabel = void 0; // reset path label due to resource change - } - - this.render(hasResourceChanged); - } - - private hasResourceChanged(label: IResourceLabel, options: IResourceLabelOptions): boolean { - const newResource = label ? label.resource : void 0; - const oldResource = this.label ? this.label.resource : void 0; - - const newFileKind = options ? options.fileKind : void 0; - const oldFileKind = this.options ? this.options.fileKind : void 0; - - if (newFileKind !== oldFileKind) { - return true; // same resource but different kind (file, folder) - } - - if (newResource && this.computedPathLabel !== this.labelService.getUriLabel(newResource)) { - return true; - } - - if (newResource && oldResource) { - return newResource.toString() !== oldResource.toString(); - } - - if (!newResource && !oldResource) { - return false; - } - - return true; - } - - clear(): void { - this.label = void 0; - this.options = void 0; - this.lastKnownConfiguredLangId = void 0; - this.computedIconClasses = void 0; - this.computedPathLabel = void 0; - - this.setValue(); - } - - private render(clearIconCache: boolean): void { - if (this.label) { - const configuredLangId = getConfiguredLangId(this.modelService, this.label.resource); - if (this.lastKnownConfiguredLangId !== configuredLangId) { - clearIconCache = true; - this.lastKnownConfiguredLangId = configuredLangId; - } - } - - if (clearIconCache) { - this.computedIconClasses = void 0; - } - - if (!this.label) { - return; - } - - const iconLabelOptions: IIconLabelValueOptions = { - title: '', - italic: this.options && this.options.italic, - matches: this.options && this.options.matches, - extraClasses: [] - }; - - const resource = this.label.resource; - const label = this.label.name; - - if (this.options && typeof this.options.title === 'string') { - iconLabelOptions.title = this.options.title; - } else if (resource && resource.scheme !== Schemas.data /* do not accidentally inline Data URIs */) { - if (!this.computedPathLabel) { - this.computedPathLabel = this.labelService.getUriLabel(resource); - } - - iconLabelOptions.title = this.computedPathLabel; - } - - if (this.options && !this.options.hideIcon) { - if (!this.computedIconClasses) { - this.computedIconClasses = getIconClasses(this.modelService, this.modeService, resource, this.options && this.options.fileKind); - } - iconLabelOptions.extraClasses = this.computedIconClasses.slice(0); - } - if (this.options && this.options.extraClasses) { - iconLabelOptions.extraClasses.push(...this.options.extraClasses); - } - - if (this.options && this.options.fileDecorations && resource) { - const deco = this.decorationsService.getDecoration( - resource, - this.options.fileKind !== FileKind.FILE, - this.options.fileDecorations.data - ); - - if (deco) { - if (deco.tooltip) { - iconLabelOptions.title = `${iconLabelOptions.title} • ${deco.tooltip}`; - } - - if (this.options.fileDecorations.colors) { - iconLabelOptions.extraClasses.push(deco.labelClassName); - } - - if (this.options.fileDecorations.badges) { - iconLabelOptions.extraClasses.push(deco.badgeClassName); - } - } - } - - this.setValue(label, this.label.description, iconLabelOptions); - - this._onDidRender.fire(); - } - - dispose(): void { - super.dispose(); - - this.label = void 0; - this.options = void 0; - this.lastKnownConfiguredLangId = void 0; - this.computedIconClasses = void 0; - this.computedPathLabel = void 0; - } -} - -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 c066ece6ceb..c8d4a5e084c 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 { SingleResourceLabel } 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,7 +78,7 @@ class Item extends BreadcrumbsItem { render(container: HTMLElement): void { if (this.element instanceof FileElement) { // file/folder - let label = this._instantiationService.createInstance(SingleResourceLabel, container, {}); + let label = this._instantiationService.createInstance(ResourceLabel, container, {}); label.element.setFile(this.element.uri, { hidePath: true, hideIcon: this.element.kind === FileKind.FOLDER || !this.options.showFileIcons, @@ -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 a69da285dde..1fc46bb8ed4 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 { ResourceLabels, IResourceLabelHandle } 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'; @@ -345,7 +345,7 @@ export class FileRenderer implements IRenderer { return this._labels.create(container, { supportHighlights: true }); } - renderElement(tree: ITree, element: IFileStat | IWorkspaceFolder, templateId: string, templateData: IResourceLabelHandle): 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: IResourceLabelHandle): void { + disposeTemplate(tree: ITree, templateId: string, templateData: IResourceLabel): void { templateData.dispose(); } } diff --git a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts index 0894a4f723d..ae1dfbdbfa8 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 { SingleResourceLabel, IResourceLabelHandle } 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: IResourceLabelHandle; + private editorLabel: IResourceLabel; private activeLabel: IRenderedEditorLabel = Object.create(null); protected create(parent: HTMLElement): void { @@ -40,7 +40,7 @@ export class NoTabsTitleControl extends TitleControl { this.titleContainer.appendChild(labelContainer); // Editor Label - this.editorLabel = this._register(this.instantiationService.createInstance(SingleResourceLabel, labelContainer, void 0)).element; + this.editorLabel = this._register(this.instantiationService.createInstance(ResourceLabel, labelContainer, void 0)).element; this._register(this.editorLabel.onClick(e => this.onTitleLabelClick(e))); // Breadcrumbs @@ -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 b740e159fce..837303eddca 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 { ResourceLabels, IResourceLabelHandle } 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'; @@ -397,7 +397,7 @@ export class TabsTitleControl extends TitleControl { this.redraw(); } - private withTab(editor: IEditorInput, fn: (tabContainer: HTMLElement, tabLabelWidget: IResourceLabelHandle, 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; @@ -807,7 +807,7 @@ export class TabsTitleControl extends TitleControl { this.layout(this.dimension); } - private forEachTab(fn: (editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: IResourceLabelHandle, 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) { @@ -816,7 +816,7 @@ export class TabsTitleControl extends TitleControl { }); } - private redrawTab(editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: IResourceLabelHandle, tabLabel: IEditorInputLabel): void { + private redrawTab(editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel): void { // Label this.redrawLabel(editor, tabContainer, tabLabelWidget, tabLabel); @@ -849,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: IResourceLabelHandle, 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 || ''; @@ -859,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: IResourceLabelHandle): 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); @@ -870,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: IResourceLabelHandle): 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 05c1ba74d3a..4682fd42f75 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('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/commentsTreeViewer.ts b/src/vs/workbench/parts/comments/electron-browser/commentsTreeViewer.ts index 7509fdffaf4..473341920a2 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentsTreeViewer.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentsTreeViewer.ts @@ -11,7 +11,7 @@ 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 { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IResourceLabelHandle, ResourceLabels } 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 { @@ -55,7 +55,7 @@ export class CommentsDataSource implements IDataSource { } interface IResourceTemplateData { - resourceLabel: IResourceLabelHandle; + resourceLabel: IResourceLabel; } interface ICommentThreadTemplateData { diff --git a/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts index 441a1621060..743369b4882 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 { ResourceLabels, IResourceLabel, IResourceLabelOptions, IResourceLabelHandle } 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'; @@ -552,7 +552,7 @@ class LoadedScriptsDataSource implements IAsyncDataSource { @@ -578,7 +578,7 @@ class LoadedScriptsRenderer implements ITreeRenderer Date: Wed, 19 Dec 2018 15:50:00 +0100 Subject: [PATCH 13/17] labels - :lipstick: --- src/vs/workbench/browser/labels.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 1a077032f75..115e2a84c2c 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -46,7 +46,7 @@ export interface IResourceLabel extends IDisposable { readonly onDidRender: Event; /** - * Most generic way to apply a label directly. + * Most generic way to apply a label with raw information. */ setLabel(label?: string, description?: string, options?: IIconLabelValueOptions): void; @@ -57,16 +57,16 @@ export interface IResourceLabel extends IDisposable { */ setResource(label: IResourceLabelProps, options?: IResourceLabelOptions): void; - /** - * Convinient method to apply a label by passing an editor along. - */ - setEditor(editor: IEditorInput, 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; + clear(): void; onClick(callback: (event: MouseEvent) => void): IDisposable; @@ -89,10 +89,6 @@ export class ResourceLabels extends Disposable { this.registerListeners(); } - get labels(): IResourceLabel[] { - return this._labels; - } - get(index: number): IResourceLabel { return this._labels[index]; } From b07f007596c2ab5a62ba7c307001c6db18df417f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 19 Dec 2018 17:06:58 +0100 Subject: [PATCH 14/17] labels - cleanup click handler --- src/vs/base/browser/ui/iconLabel/iconLabel.ts | 8 +------- src/vs/workbench/browser/labels.ts | 3 --- .../workbench/browser/parts/editor/noTabsTitleControl.ts | 2 +- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index 7b0ee1cd2c7..635e5228bf2 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,12 +116,6 @@ 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)), - ]); - } - setLabel(label?: string, description?: string, options?: IIconLabelValueOptions): void { const classes = ['monaco-icon-label']; if (options) { diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 115e2a84c2c..6bc8cf35fad 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -68,8 +68,6 @@ export interface IResourceLabel extends IDisposable { setEditor(editor: IEditorInput, options?: IResourceLabelOptions): void; clear(): void; - - onClick(callback: (event: MouseEvent) => void): IDisposable; } export class ResourceLabels extends Disposable { @@ -127,7 +125,6 @@ export class ResourceLabels extends Disposable { setEditor: (editor: IEditorInput, options?: IResourceLabelOptions) => widget.setEditor(editor, options), setFile: (resource: uri, options?: IFileLabelOptions) => widget.setFile(resource, options), clear: () => widget.clear(), - onClick: (callback: (event: MouseEvent) => void) => widget.onClick(callback), dispose: () => this.disposeWidget(widget) }; diff --git a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts index ae1dfbdbfa8..0f3d668b463 100644 --- a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts @@ -41,7 +41,7 @@ export class NoTabsTitleControl extends TitleControl { // Editor Label this.editorLabel = this._register(this.instantiationService.createInstance(ResourceLabel, labelContainer, void 0)).element; - this._register(this.editorLabel.onClick(e => this.onTitleLabelClick(e))); + 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 }); From db535e02ea295870566bcd7eaf70a1a93d96c0e5 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 19 Dec 2018 17:10:32 +0100 Subject: [PATCH 15/17] labels - clear() is not needed when the label is set right after --- src/vs/workbench/browser/parts/views/customView.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index 7197283ffaa..b5e17c96d71 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -674,7 +674,6 @@ 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) { From d9bfe033f599f0c77d69e09795b81fb810a3d24f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 19 Dec 2018 18:55:28 +0100 Subject: [PATCH 16/17] labels - visibility --- src/vs/workbench/browser/labels.ts | 44 ++++++++++++++++++- .../browser/parts/views/customView.ts | 13 +++++- .../electron-browser/commentsPanel.ts | 15 +++++-- .../parts/debug/browser/loadedScriptsView.ts | 16 +++++-- .../electron-browser/views/explorerView.ts | 8 ++++ .../electron-browser/views/openEditorsView.ts | 19 +++++--- .../markers/electron-browser/markersPanel.ts | 13 +++++- .../parts/scm/electron-browser/scmViewlet.ts | 20 +++++++-- .../parts/search/browser/searchView.ts | 16 +++++-- 9 files changed, 137 insertions(+), 27 deletions(-) diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 6bc8cf35fad..3742b85b8be 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -67,6 +67,9 @@ export interface IResourceLabel extends IDisposable { */ setEditor(editor: IEditorInput, options?: IResourceLabelOptions): void; + /** + * Resets the label to be empty. + */ clear(): void; } @@ -103,7 +106,7 @@ export class ResourceLabels extends Disposable { this._register(this.decorationsService.onDidChangeDecorations(e => this._widgets.forEach(widget => widget.notifyFileDecorationsChanges(e)))); // notify when theme changes - this._register(this.themeService.onThemeChange(() => () => this._widgets.forEach(widget => widget.notifyThemeChange()))); + this._register(this.themeService.onThemeChange(() => this._widgets.forEach(widget => widget.notifyThemeChange()))); // notify when files.associations changes this._register(this.configurationService.onDidChangeConfiguration(e => { @@ -145,6 +148,14 @@ export class ResourceLabels extends Disposable { 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 = []; @@ -186,6 +197,11 @@ export class ResourceLabel extends ResourceLabels { } } +enum Redraw { + Basic = 1, + Full = 2 +} + class ResourceLabelWidget extends IconLabel { private _onDidRender = this._register(new Emitter()); @@ -197,6 +213,9 @@ class ResourceLabelWidget extends IconLabel { private lastKnownConfiguredLangId: string; private computedPathLabel: string; + private needsRedraw: Redraw; + private isHidden: boolean = false; + constructor( container: HTMLElement, options: IIconLabelCreationOptions, @@ -210,6 +229,17 @@ class ResourceLabelWidget extends IconLabel { 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 @@ -335,6 +365,18 @@ class ResourceLabelWidget extends IconLabel { } 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) { diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index b5e17c96d71..cb4dd721cc4 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -194,6 +194,7 @@ export class CustomTreeView extends Disposable implements ITreeView { private _messageValue: string | IMarkdownString | undefined; private messageElement: HTMLDivElement; private tree: FileIconThemableWorkbenchTree; + private treeLabels: ResourceLabels; private root: ITreeItem; private elementsToRefresh: ITreeItem[] = []; private menus: TitleMenus; @@ -349,6 +350,14 @@ export class CustomTreeView extends Disposable implements ITreeView { } } + if (this.treeLabels) { + if (this.isVisible) { + this.treeLabels.onVisible(); + } else { + this.treeLabels.onHidden(); + } + } + this._onDidChangeVisibility.fire(this.isVisible); } @@ -383,9 +392,9 @@ export class CustomTreeView extends Disposable implements ITreeView { private createTree() { const actionItemProvider = (action: IAction) => action instanceof MenuItemAction ? this.instantiationService.createInstance(ContextAwareMenuItemActionItem, action) : undefined; const menus = this._register(this.instantiationService.createInstance(TreeMenus, this.id)); - const labels = this._register(this.instantiationService.createInstance(ResourceLabels)); + 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, labels, 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._register(this.instantiationService.createInstance(FileIconThemableWorkbenchTree, this.treeContainer, { dataSource, renderer, controller }, {})); this.tree.contextKeyService.createKey(this.id, true); diff --git a/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts b/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts index b2423f8c6a3..35552bea79c 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts @@ -29,7 +29,7 @@ export const COMMENTS_PANEL_ID = 'workbench.panel.comments'; export const COMMENTS_PANEL_TITLE = 'Comments'; export class CommentsPanel extends Panel { - private labels: ResourceLabels; + private treeLabels: ResourceLabels; private tree: WorkbenchTree; private treeContainer: HTMLElement; private messageBoxContainer: HTMLElement; @@ -131,11 +131,11 @@ export class CommentsPanel extends Panel { } private createTree(): void { - this.labels = this._register(this.instantiationService.createInstance(ResourceLabels)); + 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.labels, this.openerService), + renderer: new CommentsModelRenderer(this.treeLabels, this.openerService), accessibilityProvider: new DefaultAccessibilityProvider, controller: new DefaultController(), dnd: new DefaultDragAndDrop(), @@ -176,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; @@ -241,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/debug/browser/loadedScriptsView.ts b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts index 743369b4882..9decf3c88c3 100644 --- a/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts @@ -364,10 +364,10 @@ 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; - private labels: ResourceLabels; constructor( options: IViewletViewOptions, @@ -397,10 +397,11 @@ export class LoadedScriptsView extends ViewletPanel { const root = new RootTreeItem(this.debugService.getModel(), this.environmentService, this.contextService); - this.labels = this.instantiationService.createInstance(ResourceLabels); + this.treeLabels = this.instantiationService.createInstance(ResourceLabels); + this.disposables.push(this.treeLabels); this.tree = new WorkbenchAsyncDataTree(this.treeContainer, new LoadedScriptsDelegate(), - [new LoadedScriptsRenderer(this.labels)], + [new LoadedScriptsRenderer(this.treeLabels)], new LoadedScriptsDataSource(), { identityProvider: { @@ -509,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(); + } + } } /* @@ -521,7 +529,7 @@ export class LoadedScriptsView extends ViewletPanel { dispose(): void { this.tree = dispose(this.tree); - this.labels = dispose(this.labels); + this.treeLabels = dispose(this.treeLabels); super.dispose(); } } diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts index 889411a3949..4a56d682290 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -373,6 +373,14 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView this.openFocusedElement(); }); } + + if (this.explorerLabels) { + if (visible) { + this.explorerLabels.onVisible(); + } else { + this.explorerLabels.onHidden(); + } + } } private openFocusedElement(preserveFocus?: boolean): void { 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 b1b4c6d16dc..dee98bd96ea 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts @@ -51,7 +51,7 @@ export class OpenEditorsView extends ViewletPanel { private listRefreshScheduler: RunOnceScheduler; private structuralRefreshDelay: number; private list: WorkbenchList; - private labels: ResourceLabels; + private listLabels: ResourceLabels; private contributedContextMenu: IMenu; private needsRefresh: boolean; private resourceContext: ResourceContextKey; @@ -214,19 +214,19 @@ export class OpenEditorsView extends ViewletPanel { if (this.list) { this.list.dispose(); } - if (this.labels) { - this.labels.clear(); + if (this.listLabels) { + this.listLabels.clear(); } - this.labels = this.instantiationService.createInstance(ResourceLabels); + this.listLabels = this.instantiationService.createInstance(ResourceLabels); this.list = this.instantiationService.createInstance(WorkbenchList, container, delegate, [ new EditorGroupRenderer(this.keybindingService, this.instantiationService, this.editorGroupService), - new OpenEditorRenderer(this.labels, 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.labels); + this.disposables.push(this.listLabels); this.contributedContextMenu = this.menuService.createMenu(MenuId.OpenEditorsContext, this.list.contextKeyService); this.disposables.push(this.contributedContextMenu); @@ -333,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 { diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts index 2787c86c2f2..bbb5f94b349 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts @@ -67,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[]; @@ -171,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[] { @@ -288,11 +297,11 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { const onDidChangeRenderNodeCount = new Relay>(); - const labels = this._register(this.instantiationService.createInstance(ResourceLabels)); + this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels)); const virtualDelegate = new VirtualDelegate(this.markersViewState); const renderers = [ - this.instantiationService.createInstance(ResourceMarkersRenderer, labels, 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/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index 48e70ce1783..fc4462f4a32 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -728,7 +728,7 @@ export class RepositoryPanel extends ViewletPanel { private inputBox: InputBox; private listContainer: HTMLElement; private list: List; - private labels: ResourceLabels; + private listLabels: ResourceLabels; private menus: SCMMenus; private visibilityDisposables: IDisposable[] = []; protected contextKeyService: IContextKeyService; @@ -870,12 +870,12 @@ export class RepositoryPanel extends ViewletPanel { const actionItemProvider = (action: IAction) => this.getActionItem(action); - this.labels = this.instantiationService.createInstance(ResourceLabels); - this.disposables.push(this.labels); + this.listLabels = this.instantiationService.createInstance(ResourceLabels); + this.disposables.push(this.listLabels); const renderers = [ new ResourceGroupRenderer(actionItemProvider, this.themeService, this.menus), - new ResourceRenderer(this.labels, actionItemProvider, () => this.getSelectedResources(), this.themeService, this.menus) + new ResourceRenderer(this.listLabels, actionItemProvider, () => this.getSelectedResources(), this.themeService, this.menus) ]; this.list = this.instantiationService.createInstance(WorkbenchList, this.listContainer, delegate, renderers, { @@ -911,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/searchView.ts b/src/vs/workbench/parts/search/browser/searchView.ts index dcbfb928381..be55af7fa13 100644 --- a/src/vs/workbench/parts/search/browser/searchView.ts +++ b/src/vs/workbench/parts/search/browser/searchView.ts @@ -137,7 +137,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { private contextMenu: IMenu; private tree: WorkbenchObjectTree; - private labels: ResourceLabels; + private treeLabels: ResourceLabels; private viewletState: object; private globalMemento: object; private messagesElement: HTMLElement; @@ -586,13 +586,13 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { } }; - this.labels = this._register(this.instantiationService.createInstance(ResourceLabels)); + 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.labels)), - this._register(this.instantiationService.createInstance(FileMatchRenderer, this.viewModel, this, this.labels)), + 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)), ], { @@ -769,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 { From 43e502affa148f07eb9848708d7cfcc294a9b0f8 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 19 Dec 2018 19:08:28 +0100 Subject: [PATCH 17/17] labels - :lipstick: --- .../parts/markers/electron-browser/markersTreeViewer.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts index 01390c882ab..0919da43160 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts @@ -130,9 +130,8 @@ export class ResourceMarkersRenderer implements ITreeRenderer>, - @IInstantiationService protected instantiationService: IInstantiationService, @IThemeService private themeService: IThemeService, @ILabelService private labelService: ILabelService ) {