Skip to content

Commit 61755b6

Browse files
aeschlichenjigeng
authored and
chenjigeng
committed
named codicons for views (for microsoft#92791)
1 parent c492e6b commit 61755b6

File tree

24 files changed

+173
-76
lines changed

24 files changed

+173
-76
lines changed

src/vs/platform/theme/common/themeService.ts

+25-2
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,20 @@ import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry';
1111
import { Event, Emitter } from 'vs/base/common/event';
1212
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
1313
import { ColorScheme } from 'vs/platform/theme/common/theme';
14+
import { Codicon } from 'vs/base/common/codicons';
1415

1516
export const IThemeService = createDecorator<IThemeService>('themeService');
1617

1718
export interface ThemeColor {
1819
id: string;
1920
}
2021

22+
export namespace ThemeColor {
23+
export function isThemeColor(obj: any): obj is ThemeColor {
24+
return obj && typeof obj === 'object' && typeof (<ThemeColor>obj).id === 'string';
25+
}
26+
}
27+
2128
export function themeColorFromId(id: ColorIdentifier) {
2229
return { id };
2330
}
@@ -29,8 +36,8 @@ export interface ThemeIcon {
2936
}
3037

3138
export namespace ThemeIcon {
32-
export function isThemeIcon(obj: any): obj is ThemeIcon | { id: string } {
33-
return obj && typeof obj === 'object' && typeof (<ThemeIcon>obj).id === 'string';
39+
export function isThemeIcon(obj: any): obj is ThemeIcon {
40+
return obj && typeof obj === 'object' && typeof (<ThemeIcon>obj).id === 'string' && (typeof (<ThemeIcon>obj).color === 'undefined' || ThemeColor.isThemeColor((<ThemeIcon>obj).color));
3441
}
3542

3643
const _regexFromString = /^\$\(([a-z.]+\/)?([a-z-~]+)\)$/i;
@@ -47,6 +54,15 @@ export namespace ThemeIcon {
4754
return { id: owner + name };
4855
}
4956

57+
export function fromCodicon(codicon: Codicon): ThemeIcon {
58+
return { id: codicon.id };
59+
}
60+
61+
62+
export function isEqual(ti1: ThemeIcon, ti2: ThemeIcon): boolean {
63+
return ti1.id === ti2.id && ti1.color?.id === ti2.color?.id;
64+
}
65+
5066
const _regexAsClassName = /^(codicon\/)?([a-z-]+)(~[a-z]+)?$/i;
5167

5268
export function asClassName(icon: ThemeIcon): string | undefined {
@@ -62,6 +78,13 @@ export namespace ThemeIcon {
6278
}
6379
return className;
6480
}
81+
82+
export function revive(icon: any): ThemeIcon | undefined {
83+
if (ThemeIcon.isThemeIcon(icon)) {
84+
return { id: icon.id, color: icon.color ? { id: icon.color.id } : undefined };
85+
}
86+
return undefined;
87+
}
6588
}
6689

6790
export const FileThemeIcon = { id: 'file' };

src/vs/workbench/api/browser/mainThreadComments.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ import { COMMENTS_VIEW_ID, COMMENTS_VIEW_TITLE } from 'vs/workbench/contrib/comm
2020
import { ViewContainer, IViewContainersRegistry, Extensions as ViewExtensions, ViewContainerLocation, IViewsRegistry, IViewsService, IViewDescriptorService } from 'vs/workbench/common/views';
2121
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
2222
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
23-
import { Codicon } from 'vs/base/common/codicons';
23+
import { Codicon, registerIcon } from 'vs/base/common/codicons';
24+
import { localize } from 'vs/nls';
25+
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
2426

2527

2628
export class MainThreadCommentThread implements modes.CommentThread {
@@ -351,6 +353,9 @@ export class MainThreadCommentController {
351353
}
352354
}
353355

356+
357+
const commentsViewIcon = registerIcon('comments-view-icon', Codicon.commentDiscussion, localize('commentsViewIcon', 'View icon of the comments view.'));
358+
354359
@extHostNamedCustomer(MainContext.MainThreadComments)
355360
export class MainThreadComments extends Disposable implements MainThreadCommentsShape {
356361
private readonly _proxy: ExtHostCommentsShape;
@@ -472,7 +477,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments
472477
ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [COMMENTS_VIEW_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]),
473478
storageId: COMMENTS_VIEW_TITLE,
474479
hideIfEmpty: true,
475-
icon: Codicon.commentDiscussion.classNames,
480+
icon: ThemeIcon.fromCodicon(commentsViewIcon),
476481
order: 10,
477482
}, ViewContainerLocation.Panel);
478483

@@ -482,7 +487,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments
482487
canToggleVisibility: false,
483488
ctorDescriptor: new SyncDescriptor(CommentsPanel),
484489
canMoveView: true,
485-
containerIcon: Codicon.commentDiscussion.classNames,
490+
containerIcon: ThemeIcon.fromCodicon(commentsViewIcon),
486491
focusCommand: {
487492
id: 'workbench.action.focusCommentsPanel'
488493
}

src/vs/workbench/api/browser/viewsExtensionPoint.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { forEach } from 'vs/base/common/collections';
88
import { IJSONSchema } from 'vs/base/common/jsonSchema';
99
import * as resources from 'vs/base/common/resources';
1010
import { ExtensionMessageCollector, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
11-
import { ViewContainer, IViewsRegistry, ITreeViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, TEST_VIEW_CONTAINER_ID, IViewDescriptor, ViewContainerLocation } from 'vs/workbench/common/views';
11+
import { ViewContainer, IViewsRegistry, ITreeViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, TEST_VIEW_CONTAINER_ID, IViewDescriptor, ViewContainerLocation, testViewIcon } from 'vs/workbench/common/views';
1212
import { CustomTreeView, TreeViewPane } from 'vs/workbench/browser/parts/views/treeView';
1313
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
1414
import { coalesce, } from 'vs/base/common/arrays';
@@ -30,7 +30,6 @@ import { IWorkbenchActionRegistry, Extensions as ActionExtensions, CATEGORIES }
3030
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
3131
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
3232
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
33-
import { Codicon } from 'vs/base/common/codicons';
3433
import { WebviewViewPane } from 'vs/workbench/contrib/webviewView/browser/webviewViewPane';
3534
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
3635

@@ -321,7 +320,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
321320

322321
private registerTestViewContainer(): void {
323322
const title = localize('test', "Test");
324-
const icon = Codicon.beaker.classNames;
323+
const icon = ThemeIcon.fromCodicon(testViewIcon);
325324

326325
this.registerCustomViewContainer(TEST_VIEW_CONTAINER_ID, title, icon, TEST_VIEW_CONTAINER_ORDER, undefined, ViewContainerLocation.Sidebar);
327326
}
@@ -357,8 +356,8 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
357356
private registerCustomViewContainers(containers: IUserFriendlyViewsContainerDescriptor[], extension: IExtensionDescription, order: number, existingViewContainers: ViewContainer[], location: ViewContainerLocation): number {
358357
containers.forEach(descriptor => {
359358
const themeIcon = ThemeIcon.fromString(descriptor.icon);
360-
const className = themeIcon ? ThemeIcon.asClassName(themeIcon) : undefined;
361-
const icon = className || resources.joinPath(extension.extensionLocation, descriptor.icon);
359+
360+
const icon = themeIcon || resources.joinPath(extension.extensionLocation, descriptor.icon);
362361
const id = `workbench.view.extension.${descriptor.id}`;
363362
const viewContainer = this.registerCustomViewContainer(id, descriptor.title, icon, order++, extension.identifier, location);
364363

@@ -378,7 +377,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
378377
return order;
379378
}
380379

381-
private registerCustomViewContainer(id: string, title: string, icon: URI | string, order: number, extensionId: ExtensionIdentifier | undefined, location: ViewContainerLocation): ViewContainer {
380+
private registerCustomViewContainer(id: string, title: string, icon: URI | ThemeIcon, order: number, extensionId: ExtensionIdentifier | undefined, location: ViewContainerLocation): ViewContainer {
382381
let viewContainer = this.viewContainersRegistry.get(id);
383382

384383
if (!viewContainer) {

src/vs/workbench/browser/parts/activitybar/activitybarPart.ts

+9-9
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/bro
1414
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
1515
import { IDisposable, toDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
1616
import { ToggleActivityBarVisibilityAction, ToggleMenuBarAction, ToggleSidebarPositionAction } from 'vs/workbench/browser/actions/layoutActions';
17-
import { IThemeService, IColorTheme } from 'vs/platform/theme/common/themeService';
17+
import { IThemeService, IColorTheme, ThemeIcon } from 'vs/platform/theme/common/themeService';
1818
import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER, ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_ACTIVE_BORDER, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_INACTIVE_FOREGROUND, ACTIVITY_BAR_ACTIVE_BACKGROUND, ACTIVITY_BAR_DRAG_AND_DROP_BORDER } from 'vs/workbench/common/theme';
1919
import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
2020
import { CompositeBar, ICompositeBarItem, CompositeDragAndDrop } from 'vs/workbench/browser/parts/compositeBar';
@@ -25,7 +25,7 @@ import { URI, UriComponents } from 'vs/base/common/uri';
2525
import { ToggleCompositePinnedAction, ICompositeBarColors, ActivityAction, ICompositeActivity } from 'vs/workbench/browser/parts/compositeBarActions';
2626
import { IViewDescriptorService, ViewContainer, TEST_VIEW_CONTAINER_ID, IViewContainerModel, ViewContainerLocation, IViewsService } from 'vs/workbench/common/views';
2727
import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
28-
import { isUndefinedOrNull, assertIsDefined, isString } from 'vs/base/common/types';
28+
import { isUndefinedOrNull, assertIsDefined } from 'vs/base/common/types';
2929
import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService';
3030
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
3131
import { Schemas } from 'vs/base/common/network';
@@ -47,7 +47,7 @@ interface IPlaceholderViewContainer {
4747
id: string;
4848
name?: string;
4949
iconUrl?: UriComponents;
50-
iconCSS?: string;
50+
themeIcon?: ThemeIcon;
5151
views?: { when?: string }[];
5252
}
5353

@@ -61,7 +61,7 @@ interface IPinnedViewContainer {
6161
interface ICachedViewContainer {
6262
id: string;
6363
name?: string;
64-
icon?: URI | string;
64+
icon?: URI | ThemeIcon;
6565
pinned: boolean;
6666
order?: number;
6767
visible: boolean;
@@ -717,7 +717,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
717717
return ActivitybarPart.toActivity(id, name, icon, focusCommand?.id || id);
718718
}
719719

720-
private static toActivity(id: string, name: string, icon: URI | string | undefined, keybindingId: string | undefined): IActivity {
720+
private static toActivity(id: string, name: string, icon: URI | ThemeIcon | undefined, keybindingId: string | undefined): IActivity {
721721
let cssClass: string | undefined = undefined;
722722
let iconUrl: URI | undefined = undefined;
723723
if (URI.isUri(icon)) {
@@ -730,8 +730,8 @@ export class ActivitybarPart extends Part implements IActivityBarService {
730730
-webkit-mask: ${asCSSUrl(icon)} no-repeat 50% 50%;
731731
-webkit-mask-size: 24px;
732732
`);
733-
} else if (isString(icon)) {
734-
cssClass = icon;
733+
} else if (ThemeIcon.isThemeIcon(icon)) {
734+
cssClass = ThemeIcon.asClassName(icon);
735735
}
736736
return { id, name, cssClass, iconUrl, keybindingId };
737737
}
@@ -911,7 +911,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
911911
const cachedViewContainer = this._cachedViewContainers.filter(cached => cached.id === placeholderViewContainer.id)[0];
912912
if (cachedViewContainer) {
913913
cachedViewContainer.name = placeholderViewContainer.name;
914-
cachedViewContainer.icon = placeholderViewContainer.iconCSS ? placeholderViewContainer.iconCSS :
914+
cachedViewContainer.icon = placeholderViewContainer.themeIcon ? ThemeIcon.revive(placeholderViewContainer.themeIcon) :
915915
placeholderViewContainer.iconUrl ? URI.revive(placeholderViewContainer.iconUrl) : undefined;
916916
cachedViewContainer.views = placeholderViewContainer.views;
917917
}
@@ -930,7 +930,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
930930
this.setPlaceholderViewContainers(cachedViewContainers.map(({ id, icon, name, views }) => (<IPlaceholderViewContainer>{
931931
id,
932932
iconUrl: URI.isUri(icon) ? icon : undefined,
933-
iconCSS: isString(icon) ? icon : undefined,
933+
themeIcon: ThemeIcon.isThemeIcon(icon) ? icon : undefined,
934934
name,
935935
views
936936
})));

src/vs/workbench/browser/parts/views/viewPaneContainer.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
1818
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
1919
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
2020
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
21-
import { IThemeService, Themable } from 'vs/platform/theme/common/themeService';
21+
import { IThemeService, Themable, ThemeIcon } from 'vs/platform/theme/common/themeService';
2222
import { PaneView, IPaneViewOptions, IPaneOptions, Pane, IPaneStyles } from 'vs/base/browser/ui/splitview/paneview';
2323
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
2424
import { IWorkbenchLayoutService, Position } from 'vs/workbench/services/layout/browser/layoutService';
2525
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
26-
import { Extensions as ViewContainerExtensions, IView, FocusedViewContext, IViewDescriptor, ViewContainer, IViewDescriptorService, ViewContainerLocation, IViewPaneContainer, IViewsRegistry, IViewContentDescriptor, IAddedViewDescriptorRef, IViewDescriptorRef, IViewContainerModel } from 'vs/workbench/common/views';
26+
import { Extensions as ViewContainerExtensions, IView, FocusedViewContext, IViewDescriptor, ViewContainer, IViewDescriptorService, ViewContainerLocation, IViewPaneContainer, IViewsRegistry, IViewContentDescriptor, IAddedViewDescriptorRef, IViewDescriptorRef, IViewContainerModel, defaultViewIcon } from 'vs/workbench/common/views';
2727
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
2828
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
2929
import { assertIsDefined, isString } from 'vs/base/common/types';
@@ -338,8 +338,8 @@ export abstract class ViewPane extends Pane implements IView {
338338
}
339339
}
340340

341-
private getIcon(): string | URI {
342-
return this.viewDescriptorService.getViewDescriptorById(this.id)?.containerIcon || 'codicon-window';
341+
private getIcon(): ThemeIcon | URI {
342+
return this.viewDescriptorService.getViewDescriptorById(this.id)?.containerIcon || ThemeIcon.fromCodicon(defaultViewIcon);
343343
}
344344

345345
protected renderHeaderTitle(container: HTMLElement, title: string): void {

src/vs/workbench/common/views.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,12 @@ import { IPaneComposite } from 'vs/workbench/common/panecomposite';
2525
import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility';
2626
import { IMarkdownString } from 'vs/base/common/htmlContent';
2727
import { mixin } from 'vs/base/common/objects';
28+
import { Codicon, registerIcon } from 'vs/base/common/codicons';
2829

2930
export const TEST_VIEW_CONTAINER_ID = 'workbench.view.extension.test';
31+
export const testViewIcon = registerIcon('test-view-icon', Codicon.beaker, localize('testViewIcon', 'View icon of the test view.'));
32+
33+
export const defaultViewIcon = registerIcon('default-view-icon', Codicon.window, localize('defaultViewIcon', 'Default view icon.'));
3034

3135
export namespace Extensions {
3236
export const ViewContainersRegistry = 'workbench.registry.view.containers';
@@ -48,7 +52,7 @@ export interface IViewContainerDescriptor {
4852

4953
readonly storageId?: string;
5054

51-
readonly icon?: string | URI;
55+
readonly icon?: ThemeIcon | URI;
5256

5357
readonly alwaysUseContainerInfo?: boolean;
5458

@@ -216,7 +220,7 @@ export interface IViewDescriptor {
216220

217221
readonly canMoveView?: boolean;
218222

219-
readonly containerIcon?: string | URI;
223+
readonly containerIcon?: ThemeIcon | URI;
220224

221225
readonly containerTitle?: string;
222226

@@ -252,7 +256,7 @@ export interface IAddedViewDescriptorState {
252256
export interface IViewContainerModel {
253257

254258
readonly title: string;
255-
readonly icon: string | URI | undefined;
259+
readonly icon: ThemeIcon | URI | undefined;
256260
readonly onDidChangeContainerInfo: Event<{ title?: boolean, icon?: boolean }>;
257261

258262
readonly allViewDescriptors: ReadonlyArray<IViewDescriptor>;

src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ import type { ServicesAccessor } from 'vs/platform/instantiation/common/instanti
2727
import { CancellationTokenSource } from 'vs/base/common/cancellation';
2828
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
2929
import Severity from 'vs/base/common/severity';
30-
import { Codicon } from 'vs/base/common/codicons';
30+
import { Codicon, registerIcon } from 'vs/base/common/codicons';
31+
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
3132

3233
async function getBulkEditPane(viewsService: IViewsService): Promise<BulkEditPane | undefined> {
3334
const view = await viewsService.openView(BulkEditPane.ID, true);
@@ -341,6 +342,8 @@ Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).regi
341342
BulkEditPreviewContribution, LifecyclePhase.Ready
342343
);
343344

345+
const refactorPreviewViewIcon = registerIcon('refactor-preview-view-icon', Codicon.lightbulb, localize('refactorPreviewViewIcon', 'View icon of the refactor preview view.'));
346+
344347
const container = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({
345348
id: BulkEditPane.ID,
346349
name: localize('panel', "Refactor Preview"),
@@ -349,7 +352,7 @@ const container = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.V
349352
ViewPaneContainer,
350353
[BulkEditPane.ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]
351354
),
352-
icon: Codicon.lightbulb.classNames,
355+
icon: ThemeIcon.fromCodicon(refactorPreviewViewIcon),
353356
storageId: BulkEditPane.ID
354357
}, ViewContainerLocation.Panel);
355358

@@ -358,5 +361,5 @@ Registry.as<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry).registerViews
358361
name: localize('panel', "Refactor Preview"),
359362
when: BulkEditPreviewContribution.ctxEnabled,
360363
ctorDescriptor: new SyncDescriptor(BulkEditPane),
361-
containerIcon: Codicon.lightbulb.classNames,
364+
containerIcon: ThemeIcon.fromCodicon(refactorPreviewViewIcon),
362365
}], container);

0 commit comments

Comments
 (0)