Skip to content

Commit

Permalink
named codicons for views (for microsoft#92791)
Browse files Browse the repository at this point in the history
  • Loading branch information
aeschli authored and chenjigeng committed Nov 22, 2020
1 parent c492e6b commit 61755b6
Show file tree
Hide file tree
Showing 24 changed files with 173 additions and 76 deletions.
27 changes: 25 additions & 2 deletions src/vs/platform/theme/common/themeService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,20 @@ import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry';
import { Event, Emitter } from 'vs/base/common/event';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ColorScheme } from 'vs/platform/theme/common/theme';
import { Codicon } from 'vs/base/common/codicons';

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

export interface ThemeColor {
id: string;
}

export namespace ThemeColor {
export function isThemeColor(obj: any): obj is ThemeColor {
return obj && typeof obj === 'object' && typeof (<ThemeColor>obj).id === 'string';
}
}

export function themeColorFromId(id: ColorIdentifier) {
return { id };
}
Expand All @@ -29,8 +36,8 @@ export interface ThemeIcon {
}

export namespace ThemeIcon {
export function isThemeIcon(obj: any): obj is ThemeIcon | { id: string } {
return obj && typeof obj === 'object' && typeof (<ThemeIcon>obj).id === 'string';
export function isThemeIcon(obj: any): obj is ThemeIcon {
return obj && typeof obj === 'object' && typeof (<ThemeIcon>obj).id === 'string' && (typeof (<ThemeIcon>obj).color === 'undefined' || ThemeColor.isThemeColor((<ThemeIcon>obj).color));
}

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

export function fromCodicon(codicon: Codicon): ThemeIcon {
return { id: codicon.id };
}


export function isEqual(ti1: ThemeIcon, ti2: ThemeIcon): boolean {
return ti1.id === ti2.id && ti1.color?.id === ti2.color?.id;
}

const _regexAsClassName = /^(codicon\/)?([a-z-]+)(~[a-z]+)?$/i;

export function asClassName(icon: ThemeIcon): string | undefined {
Expand All @@ -62,6 +78,13 @@ export namespace ThemeIcon {
}
return className;
}

export function revive(icon: any): ThemeIcon | undefined {
if (ThemeIcon.isThemeIcon(icon)) {
return { id: icon.id, color: icon.color ? { id: icon.color.id } : undefined };
}
return undefined;
}
}

export const FileThemeIcon = { id: 'file' };
Expand Down
11 changes: 8 additions & 3 deletions src/vs/workbench/api/browser/mainThreadComments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ import { COMMENTS_VIEW_ID, COMMENTS_VIEW_TITLE } from 'vs/workbench/contrib/comm
import { ViewContainer, IViewContainersRegistry, Extensions as ViewExtensions, ViewContainerLocation, IViewsRegistry, IViewsService, IViewDescriptorService } from 'vs/workbench/common/views';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { Codicon } from 'vs/base/common/codicons';
import { Codicon, registerIcon } from 'vs/base/common/codicons';
import { localize } from 'vs/nls';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';


export class MainThreadCommentThread implements modes.CommentThread {
Expand Down Expand Up @@ -351,6 +353,9 @@ export class MainThreadCommentController {
}
}


const commentsViewIcon = registerIcon('comments-view-icon', Codicon.commentDiscussion, localize('commentsViewIcon', 'View icon of the comments view.'));

@extHostNamedCustomer(MainContext.MainThreadComments)
export class MainThreadComments extends Disposable implements MainThreadCommentsShape {
private readonly _proxy: ExtHostCommentsShape;
Expand Down Expand Up @@ -472,7 +477,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments
ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [COMMENTS_VIEW_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]),
storageId: COMMENTS_VIEW_TITLE,
hideIfEmpty: true,
icon: Codicon.commentDiscussion.classNames,
icon: ThemeIcon.fromCodicon(commentsViewIcon),
order: 10,
}, ViewContainerLocation.Panel);

Expand All @@ -482,7 +487,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments
canToggleVisibility: false,
ctorDescriptor: new SyncDescriptor(CommentsPanel),
canMoveView: true,
containerIcon: Codicon.commentDiscussion.classNames,
containerIcon: ThemeIcon.fromCodicon(commentsViewIcon),
focusCommand: {
id: 'workbench.action.focusCommentsPanel'
}
Expand Down
11 changes: 5 additions & 6 deletions src/vs/workbench/api/browser/viewsExtensionPoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { forEach } from 'vs/base/common/collections';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import * as resources from 'vs/base/common/resources';
import { ExtensionMessageCollector, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { ViewContainer, IViewsRegistry, ITreeViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, TEST_VIEW_CONTAINER_ID, IViewDescriptor, ViewContainerLocation } from 'vs/workbench/common/views';
import { ViewContainer, IViewsRegistry, ITreeViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, TEST_VIEW_CONTAINER_ID, IViewDescriptor, ViewContainerLocation, testViewIcon } from 'vs/workbench/common/views';
import { CustomTreeView, TreeViewPane } from 'vs/workbench/browser/parts/views/treeView';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { coalesce, } from 'vs/base/common/arrays';
Expand All @@ -30,7 +30,6 @@ import { IWorkbenchActionRegistry, Extensions as ActionExtensions, CATEGORIES }
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { Codicon } from 'vs/base/common/codicons';
import { WebviewViewPane } from 'vs/workbench/contrib/webviewView/browser/webviewViewPane';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';

Expand Down Expand Up @@ -321,7 +320,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {

private registerTestViewContainer(): void {
const title = localize('test', "Test");
const icon = Codicon.beaker.classNames;
const icon = ThemeIcon.fromCodicon(testViewIcon);

this.registerCustomViewContainer(TEST_VIEW_CONTAINER_ID, title, icon, TEST_VIEW_CONTAINER_ORDER, undefined, ViewContainerLocation.Sidebar);
}
Expand Down Expand Up @@ -357,8 +356,8 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
private registerCustomViewContainers(containers: IUserFriendlyViewsContainerDescriptor[], extension: IExtensionDescription, order: number, existingViewContainers: ViewContainer[], location: ViewContainerLocation): number {
containers.forEach(descriptor => {
const themeIcon = ThemeIcon.fromString(descriptor.icon);
const className = themeIcon ? ThemeIcon.asClassName(themeIcon) : undefined;
const icon = className || resources.joinPath(extension.extensionLocation, descriptor.icon);

const icon = themeIcon || resources.joinPath(extension.extensionLocation, descriptor.icon);
const id = `workbench.view.extension.${descriptor.id}`;
const viewContainer = this.registerCustomViewContainer(id, descriptor.title, icon, order++, extension.identifier, location);

Expand All @@ -378,7 +377,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
return order;
}

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

if (!viewContainer) {
Expand Down
18 changes: 9 additions & 9 deletions src/vs/workbench/browser/parts/activitybar/activitybarPart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/bro
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IDisposable, toDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
import { ToggleActivityBarVisibilityAction, ToggleMenuBarAction, ToggleSidebarPositionAction } from 'vs/workbench/browser/actions/layoutActions';
import { IThemeService, IColorTheme } from 'vs/platform/theme/common/themeService';
import { IThemeService, IColorTheme, ThemeIcon } from 'vs/platform/theme/common/themeService';
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';
import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { CompositeBar, ICompositeBarItem, CompositeDragAndDrop } from 'vs/workbench/browser/parts/compositeBar';
Expand All @@ -25,7 +25,7 @@ import { URI, UriComponents } from 'vs/base/common/uri';
import { ToggleCompositePinnedAction, ICompositeBarColors, ActivityAction, ICompositeActivity } from 'vs/workbench/browser/parts/compositeBarActions';
import { IViewDescriptorService, ViewContainer, TEST_VIEW_CONTAINER_ID, IViewContainerModel, ViewContainerLocation, IViewsService } from 'vs/workbench/common/views';
import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { isUndefinedOrNull, assertIsDefined, isString } from 'vs/base/common/types';
import { isUndefinedOrNull, assertIsDefined } from 'vs/base/common/types';
import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { Schemas } from 'vs/base/common/network';
Expand All @@ -47,7 +47,7 @@ interface IPlaceholderViewContainer {
id: string;
name?: string;
iconUrl?: UriComponents;
iconCSS?: string;
themeIcon?: ThemeIcon;
views?: { when?: string }[];
}

Expand All @@ -61,7 +61,7 @@ interface IPinnedViewContainer {
interface ICachedViewContainer {
id: string;
name?: string;
icon?: URI | string;
icon?: URI | ThemeIcon;
pinned: boolean;
order?: number;
visible: boolean;
Expand Down Expand Up @@ -717,7 +717,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
return ActivitybarPart.toActivity(id, name, icon, focusCommand?.id || id);
}

private static toActivity(id: string, name: string, icon: URI | string | undefined, keybindingId: string | undefined): IActivity {
private static toActivity(id: string, name: string, icon: URI | ThemeIcon | undefined, keybindingId: string | undefined): IActivity {
let cssClass: string | undefined = undefined;
let iconUrl: URI | undefined = undefined;
if (URI.isUri(icon)) {
Expand All @@ -730,8 +730,8 @@ export class ActivitybarPart extends Part implements IActivityBarService {
-webkit-mask: ${asCSSUrl(icon)} no-repeat 50% 50%;
-webkit-mask-size: 24px;
`);
} else if (isString(icon)) {
cssClass = icon;
} else if (ThemeIcon.isThemeIcon(icon)) {
cssClass = ThemeIcon.asClassName(icon);
}
return { id, name, cssClass, iconUrl, keybindingId };
}
Expand Down Expand Up @@ -911,7 +911,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
const cachedViewContainer = this._cachedViewContainers.filter(cached => cached.id === placeholderViewContainer.id)[0];
if (cachedViewContainer) {
cachedViewContainer.name = placeholderViewContainer.name;
cachedViewContainer.icon = placeholderViewContainer.iconCSS ? placeholderViewContainer.iconCSS :
cachedViewContainer.icon = placeholderViewContainer.themeIcon ? ThemeIcon.revive(placeholderViewContainer.themeIcon) :
placeholderViewContainer.iconUrl ? URI.revive(placeholderViewContainer.iconUrl) : undefined;
cachedViewContainer.views = placeholderViewContainer.views;
}
Expand All @@ -930,7 +930,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
this.setPlaceholderViewContainers(cachedViewContainers.map(({ id, icon, name, views }) => (<IPlaceholderViewContainer>{
id,
iconUrl: URI.isUri(icon) ? icon : undefined,
iconCSS: isString(icon) ? icon : undefined,
themeIcon: ThemeIcon.isThemeIcon(icon) ? icon : undefined,
name,
views
})));
Expand Down
8 changes: 4 additions & 4 deletions src/vs/workbench/browser/parts/views/viewPaneContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService, Themable } from 'vs/platform/theme/common/themeService';
import { IThemeService, Themable, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { PaneView, IPaneViewOptions, IPaneOptions, Pane, IPaneStyles } from 'vs/base/browser/ui/splitview/paneview';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkbenchLayoutService, Position } from 'vs/workbench/services/layout/browser/layoutService';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { Extensions as ViewContainerExtensions, IView, FocusedViewContext, IViewDescriptor, ViewContainer, IViewDescriptorService, ViewContainerLocation, IViewPaneContainer, IViewsRegistry, IViewContentDescriptor, IAddedViewDescriptorRef, IViewDescriptorRef, IViewContainerModel } from 'vs/workbench/common/views';
import { Extensions as ViewContainerExtensions, IView, FocusedViewContext, IViewDescriptor, ViewContainer, IViewDescriptorService, ViewContainerLocation, IViewPaneContainer, IViewsRegistry, IViewContentDescriptor, IAddedViewDescriptorRef, IViewDescriptorRef, IViewContainerModel, defaultViewIcon } from 'vs/workbench/common/views';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { assertIsDefined, isString } from 'vs/base/common/types';
Expand Down Expand Up @@ -338,8 +338,8 @@ export abstract class ViewPane extends Pane implements IView {
}
}

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

protected renderHeaderTitle(container: HTMLElement, title: string): void {
Expand Down
10 changes: 7 additions & 3 deletions src/vs/workbench/common/views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,12 @@ import { IPaneComposite } from 'vs/workbench/common/panecomposite';
import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { mixin } from 'vs/base/common/objects';
import { Codicon, registerIcon } from 'vs/base/common/codicons';

export const TEST_VIEW_CONTAINER_ID = 'workbench.view.extension.test';
export const testViewIcon = registerIcon('test-view-icon', Codicon.beaker, localize('testViewIcon', 'View icon of the test view.'));

export const defaultViewIcon = registerIcon('default-view-icon', Codicon.window, localize('defaultViewIcon', 'Default view icon.'));

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

readonly storageId?: string;

readonly icon?: string | URI;
readonly icon?: ThemeIcon | URI;

readonly alwaysUseContainerInfo?: boolean;

Expand Down Expand Up @@ -216,7 +220,7 @@ export interface IViewDescriptor {

readonly canMoveView?: boolean;

readonly containerIcon?: string | URI;
readonly containerIcon?: ThemeIcon | URI;

readonly containerTitle?: string;

Expand Down Expand Up @@ -252,7 +256,7 @@ export interface IAddedViewDescriptorState {
export interface IViewContainerModel {

readonly title: string;
readonly icon: string | URI | undefined;
readonly icon: ThemeIcon | URI | undefined;
readonly onDidChangeContainerInfo: Event<{ title?: boolean, icon?: boolean }>;

readonly allViewDescriptors: ReadonlyArray<IViewDescriptor>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ import type { ServicesAccessor } from 'vs/platform/instantiation/common/instanti
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import Severity from 'vs/base/common/severity';
import { Codicon } from 'vs/base/common/codicons';
import { Codicon, registerIcon } from 'vs/base/common/codicons';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';

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

const refactorPreviewViewIcon = registerIcon('refactor-preview-view-icon', Codicon.lightbulb, localize('refactorPreviewViewIcon', 'View icon of the refactor preview view.'));

const container = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({
id: BulkEditPane.ID,
name: localize('panel', "Refactor Preview"),
Expand All @@ -349,7 +352,7 @@ const container = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.V
ViewPaneContainer,
[BulkEditPane.ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]
),
icon: Codicon.lightbulb.classNames,
icon: ThemeIcon.fromCodicon(refactorPreviewViewIcon),
storageId: BulkEditPane.ID
}, ViewContainerLocation.Panel);

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

0 comments on commit 61755b6

Please sign in to comment.