Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for custom icons in various editor inputs #200329

Merged
merged 19 commits into from
Dec 11, 2023
Merged
6 changes: 5 additions & 1 deletion src/vs/editor/common/services/getIconClasses.ts
Original file line number Diff line number Diff line change
@@ -10,10 +10,14 @@ import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry'
import { ILanguageService } from 'vs/editor/common/languages/language';
import { IModelService } from 'vs/editor/common/services/model';
import { FileKind } from 'vs/platform/files/common/files';
import { ThemeIcon } from 'vs/base/common/themables';

const fileIconDirectoryRegex = /(?:\/|^)(?:([^\/]+)\/)?([^\/]+)$/;

export function getIconClasses(modelService: IModelService, languageService: ILanguageService, resource: uri | undefined, fileKind?: FileKind): string[] {
export function getIconClasses(modelService: IModelService, languageService: ILanguageService, resource: uri | undefined, fileKind?: FileKind, icon?: ThemeIcon): string[] {
if (icon) {
return [`codicon-${icon.id}`, 'product-icon'];
}

// we always set these base classes even if we do not have a path
const classes = fileKind === FileKind.ROOT_FOLDER ? ['rootfolder-icon'] : fileKind === FileKind.FOLDER ? ['folder-icon'] : ['file-icon'];
8 changes: 7 additions & 1 deletion src/vs/workbench/browser/labels.ts
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@ import { Disposable, dispose, IDisposable, MutableDisposable } from 'vs/base/com
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { normalizeDriveLetter } from 'vs/base/common/labels';
import { IRange } from 'vs/editor/common/core/range';
import { ThemeIcon } from 'vs/base/common/themables';

export interface IResourceLabelProps {
resource?: URI | { primary?: URI; secondary?: URI };
@@ -59,6 +60,11 @@ export interface IResourceLabelOptions extends IIconLabelValueOptions {
* Will take the provided label as is and e.g. not override it for untitled files.
*/
readonly forceLabel?: boolean;

/**
* Uses the provided icon instead of deriving a resource icon.
*/
readonly icon?: ThemeIcon;
}

export interface IFileLabelOptions extends IResourceLabelOptions {
@@ -573,7 +579,7 @@ class ResourceLabelWidget extends IconLabel {

if (this.options && !this.options.hideIcon) {
if (!this.computedIconClasses) {
this.computedIconClasses = getIconClasses(this.modelService, this.languageService, resource, this.options.fileKind);
this.computedIconClasses = getIconClasses(this.modelService, this.languageService, resource, this.options.fileKind, this.options.icon);
}

iconLabelOptions.extraClasses = this.computedIconClasses.slice(0);
7 changes: 7 additions & 0 deletions src/vs/workbench/browser/media/style.css
Original file line number Diff line number Diff line change
@@ -150,6 +150,13 @@ body.web {
font-size: 16px; /* sets font-size for codicons in workbench (https://github.com/microsoft/vscode/issues/98495) */
}

.monaco-workbench .product-icon[class*='codicon-']::before {
font-family: 'codicon';
width: 16px;
padding-left: 3px; /* width (16px) - font-size (13px) = padding-left (3px) */
padding-right: 3px;
}

.monaco-workbench.modal-dialog-visible .monaco-progress-container.infinite .progress-bit {
display: none; /* stop progress animations when dialogs are visible (https://github.com/microsoft/vscode/issues/138396) */
}
2 changes: 1 addition & 1 deletion src/vs/workbench/browser/parts/editor/editorQuickAccess.ts
Original file line number Diff line number Diff line change
@@ -157,7 +157,7 @@ export abstract class BaseEditorQuickAccessProvider extends PickerQuickAccessPro
return isDirty ? localize('entryAriaLabelDirty', "{0}, unsaved changes", nameAndDescription) : nameAndDescription;
})(),
description,
iconClasses: getIconClasses(this.modelService, this.languageService, resource).concat(editor.getLabelExtraClasses()),
iconClasses: getIconClasses(this.modelService, this.languageService, resource, undefined, editor.getIcon()).concat(editor.getLabelExtraClasses()),
italic: !this.editorGroupService.getGroup(groupId)?.isPinned(editor),
buttons: (() => {
return [
Original file line number Diff line number Diff line change
@@ -1462,7 +1462,9 @@ export class MultiEditorTabsControl extends EditorTabsControl {
fileDecorations: {
colors: fileDecorationColors,
badges: fileDecorationBadges
}
},
icon: editor.getIcon(),
hideIcon: options.showIcons === false,
}
);

Original file line number Diff line number Diff line change
@@ -323,6 +323,8 @@ export class SingleEditorTabsControl extends EditorTabsControl {
colors: Boolean(options.decorations?.colors),
badges: Boolean(options.decorations?.badges)
},
icon: editor.getIcon(),
hideIcon: options.showIcons === false,
}
);

9 changes: 9 additions & 0 deletions src/vs/workbench/common/editor/editorInput.ts
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ import { isEqual } from 'vs/base/common/resources';
import { ConfirmResult } from 'vs/platform/dialogs/common/dialogs';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { IDisposable } from 'vs/base/common/lifecycle';
import { ThemeIcon } from 'vs/base/common/themables';

export interface IEditorCloseHandler {

@@ -163,6 +164,14 @@ export abstract class EditorInput extends AbstractEditorInput {
return this.getTitle(Verbosity.SHORT);
}

/**
* Returns the icon which represents this editor input.
* If undefined, the default icon will be used.
*/
getIcon(): ThemeIcon | undefined {
return undefined;
}

/**
* Returns a descriptor suitable for telemetry events.
*
1 change: 0 additions & 1 deletion src/vs/workbench/contrib/chat/browser/chatEditor.ts
Original file line number Diff line number Diff line change
@@ -3,7 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import 'vs/css!./media/chatEditor';
import * as dom from 'vs/base/browser/dom';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IContextKeyService, IScopedContextKeyService } from 'vs/platform/contextkey/common/contextkey';
6 changes: 4 additions & 2 deletions src/vs/workbench/contrib/chat/browser/chatEditorInput.ts
Original file line number Diff line number Diff line change
@@ -4,9 +4,11 @@
*--------------------------------------------------------------------------------------------*/

import { CancellationToken } from 'vs/base/common/cancellation';
import { Codicon } from 'vs/base/common/codicons';
import { Emitter } from 'vs/base/common/event';
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
import { ThemeIcon } from 'vs/base/common/themables';
import { URI } from 'vs/base/common/uri';
import * as nls from 'vs/nls';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -81,8 +83,8 @@ export class ChatEditorInput extends EditorInput {
return this.model?.title || nls.localize('chatEditorName', "Chat") + (this.inputCount > 0 ? ` ${this.inputCount + 1}` : '');
}

override getLabelExtraClasses(): string[] {
return ['chat-editor-label'];
override getIcon(): ThemeIcon {
return Codicon.commentDiscussion;
}

override async resolve(): Promise<ChatEditorModel | null> {
11 changes: 0 additions & 11 deletions src/vs/workbench/contrib/chat/browser/media/chatEditor.css

This file was deleted.

6 changes: 6 additions & 0 deletions src/vs/workbench/contrib/debug/common/disassemblyViewInput.ts
Original file line number Diff line number Diff line change
@@ -5,6 +5,8 @@

import { EditorInput } from 'vs/workbench/common/editor/editorInput';
import { localize } from 'vs/nls';
import { ThemeIcon } from 'vs/base/common/themables';
import { Codicon } from 'vs/base/common/codicons';

export class DisassemblyViewInput extends EditorInput {

@@ -29,6 +31,10 @@ export class DisassemblyViewInput extends EditorInput {
return localize('disassemblyInputName', "Disassembly");
}

override getIcon(): ThemeIcon {
return Codicon.debug;
}

override matches(other: unknown): boolean {
return other instanceof DisassemblyViewInput;
}
6 changes: 6 additions & 0 deletions src/vs/workbench/contrib/extensions/common/extensionsInput.ts
Original file line number Diff line number Diff line change
@@ -12,6 +12,8 @@ import { ExtensionEditorTab, IExtension } from 'vs/workbench/contrib/extensions/
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { join } from 'vs/base/common/path';
import { IEditorOptions } from 'vs/platform/editor/common/editor';
import { ThemeIcon } from 'vs/base/common/themables';
import { Codicon } from 'vs/base/common/codicons';

export interface IExtensionEditorOptions extends IEditorOptions {
showPreReleaseVersion?: boolean;
@@ -48,6 +50,10 @@ export class ExtensionsInput extends EditorInput {
return localize('extensionsInputName', "Extension: {0}", this._extension.displayName);
}

override getIcon(): ThemeIcon | undefined {
return Codicon.extensions;
}

override matches(other: EditorInput | IUntypedEditorInput): boolean {
if (super.matches(other)) {
return true;
Original file line number Diff line number Diff line change
@@ -7,6 +7,8 @@ import * as nls from 'vs/nls';
import { URI } from 'vs/base/common/uri';
import { EditorInputCapabilities, IUntypedEditorInput } from 'vs/workbench/common/editor';
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
import { ThemeIcon } from 'vs/base/common/themables';
import { Codicon } from 'vs/base/common/codicons';

export class RuntimeExtensionsInput extends EditorInput {

@@ -38,6 +40,10 @@ export class RuntimeExtensionsInput extends EditorInput {
return nls.localize('extensionsInputName', "Running Extensions");
}

override getIcon(): ThemeIcon {
return Codicon.extensions;
}

override matches(other: EditorInput | IUntypedEditorInput): boolean {
if (super.matches(other)) {
return true;
Original file line number Diff line number Diff line change
@@ -650,7 +650,8 @@ class OpenEditorRenderer implements IListRenderer<OpenEditor, IOpenEditorTemplat
italic: openedEditor.isPreview(),
extraClasses: ['open-editor'].concat(openedEditor.editor.getLabelExtraClasses()),
fileDecorations: this.configurationService.getValue<IFilesConfiguration>().explorer.decorations,
title: editor.getTitle(Verbosity.LONG)
title: editor.getTitle(Verbosity.LONG),
icon: editor.getIcon()
});
const editorAction = openedEditor.isSticky() ? this.unpinEditorAction : this.closeEditorAction;
if (!templateData.actionBar.hasAction(editorAction)) {
Original file line number Diff line number Diff line change
@@ -4,10 +4,12 @@
*--------------------------------------------------------------------------------------------*/

import { LazyStatefulPromise, raceTimeout } from 'vs/base/common/async';
import { Codicon } from 'vs/base/common/codicons';
import { onUnexpectedError } from 'vs/base/common/errors';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { IDisposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
import { deepClone } from 'vs/base/common/objects';
import { ThemeIcon } from 'vs/base/common/themables';
import { isDefined, isObject } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import { ConstLazyPromise, IDocumentDiffItem, IMultiDiffEditorModel } from 'vs/editor/browser/widget/multiDiffEditorWidget/model';
@@ -49,6 +51,10 @@ export class MultiDiffEditorInput extends EditorInput implements ILanguageSuppor
return DEFAULT_EDITOR_ASSOCIATION.id;
}

override getIcon(): ThemeIcon {
return Codicon.diffMultiple;
}

constructor(
readonly label: string | undefined,
readonly resources: readonly MultiDiffEditorInputData[],
Original file line number Diff line number Diff line change
@@ -951,13 +951,15 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider<IAnyt
let description: string | undefined = undefined;
let isDirty: boolean | undefined = undefined;
let extraClasses: string[];
let icon: ThemeIcon | undefined = undefined;

if (isEditorInput(resourceOrEditor)) {
resource = EditorResourceAccessor.getOriginalUri(resourceOrEditor);
label = resourceOrEditor.getName();
description = resourceOrEditor.getDescription();
isDirty = resourceOrEditor.isDirty() && !resourceOrEditor.isSaving();
extraClasses = resourceOrEditor.getLabelExtraClasses();
icon = resourceOrEditor.getIcon();
} else {
resource = URI.isUri(resourceOrEditor) ? resourceOrEditor : resourceOrEditor.resource;
label = basenameOrAuthority(resource);
@@ -968,7 +970,7 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider<IAnyt

const labelAndDescription = description ? `${label} ${description}` : label;

const iconClassesValue = new Lazy(() => getIconClasses(this.modelService, this.languageService, resource).concat(extraClasses));
const iconClassesValue = new Lazy(() => getIconClasses(this.modelService, this.languageService, resource, undefined, icon).concat(extraClasses));

const buttonsValue = new Lazy(() => {
const openSideBySideDirection = configuration.openSideBySideDirection;
Original file line number Diff line number Diff line change
@@ -32,6 +32,8 @@ import { bufferToReadable, VSBuffer } from 'vs/base/common/buffer';
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
import { IResourceEditorInput } from 'vs/platform/editor/common/editor';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Codicon } from 'vs/base/common/codicons';
import { ThemeIcon } from 'vs/base/common/themables';

export type SearchConfiguration = {
query: string;
@@ -65,6 +67,10 @@ export class SearchEditorInput extends EditorInput {
return this.typeId;
}

override getIcon(): ThemeIcon {
return Codicon.search;
}

override get capabilities(): EditorInputCapabilities {
let capabilities = EditorInputCapabilities.Singleton;
if (!this.backingUri) {
5 changes: 0 additions & 5 deletions src/vs/workbench/contrib/terminal/browser/media/terminal.css
Original file line number Diff line number Diff line change
@@ -58,11 +58,6 @@
opacity: 0 !important;
}

.monaco-workbench .terminal-tab::before {
font-family: 'codicon' !important;
font-size: 16px !important;
}

.monaco-workbench .terminal-tab:not(.terminal-uri-icon)::before {
background-image: none !important;
}
12 changes: 8 additions & 4 deletions src/vs/workbench/contrib/terminal/browser/terminalEditorInput.ts
Original file line number Diff line number Diff line change
@@ -191,11 +191,18 @@ export class TerminalEditorInput extends EditorInput implements IEditorCloseHand
return this._terminalInstance?.title || this.resource.fragment;
}

override getIcon(): ThemeIcon | undefined {
if (!this._terminalInstance || !ThemeIcon.isThemeIcon(this._terminalInstance.icon)) {
return undefined;
}
return this._terminalInstance.icon;
}

override getLabelExtraClasses(): string[] {
if (!this._terminalInstance) {
return [];
}
const extraClasses: string[] = ['terminal-tab'];
const extraClasses: string[] = ['terminal-tab', 'product-icon'];
const colorClass = getColorClass(this._terminalInstance);
if (colorClass) {
extraClasses.push(colorClass);
@@ -204,9 +211,6 @@ export class TerminalEditorInput extends EditorInput implements IEditorCloseHand
if (uriClasses) {
extraClasses.push(...uriClasses);
}
if (ThemeIcon.isThemeIcon(this._terminalInstance.icon)) {
extraClasses.push(`codicon-${this._terminalInstance.icon.id}`);
}
return extraClasses;
}

Original file line number Diff line number Diff line change
@@ -3,13 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

.monaco-icon-label.file-icon.workspacetrusteditor-name-file-icon.ext-file-icon.tab-label::before {
font-family: 'codicon';
content: '\eb53';
background-image: none;
font-size: 16px;
}

.workspace-trust-editor {
max-width: 1000px;
padding-top: 11px;
Original file line number Diff line number Diff line change
@@ -3,7 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Codicon } from 'vs/base/common/codicons';
import { OS } from 'vs/base/common/platform';
import { ThemeIcon } from 'vs/base/common/themables';
import * as nls from 'vs/nls';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IUntypedEditorInput } from 'vs/workbench/common/editor';
@@ -39,6 +41,10 @@ export class KeybindingsEditorInput extends EditorInput {
return nls.localize('keybindingsInputName', "Keyboard Shortcuts");
}

override getIcon(): ThemeIcon {
return Codicon.keyboard;
}

override async resolve(): Promise<KeybindingsEditorModel> {
return this.keybindingsModel;
}
Original file line number Diff line number Diff line change
@@ -3,7 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Codicon } from 'vs/base/common/codicons';
import { Schemas } from 'vs/base/common/network';
import { ThemeIcon } from 'vs/base/common/themables';
import { URI } from 'vs/base/common/uri';
import * as nls from 'vs/nls';
import { IUntypedEditorInput } from 'vs/workbench/common/editor';
@@ -41,6 +43,10 @@ export class SettingsEditor2Input extends EditorInput {
return nls.localize('settingsEditor2InputName', "Settings");
}

override getIcon(): ThemeIcon {
return Codicon.settings;
}

override async resolve(): Promise<Settings2EditorModel> {
return this._settingsModel;
}
Loading