未验证 提交 51582b9d 编写于 作者: B Benjamin Pasero 提交者: GitHub

Merge pull request #65408 from Microsoft/ben/labels

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