提交 9cd74237 编写于 作者: J Joao Moreno

scm viewlet: themable icons

上级 a5812a5e
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#3c8746" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
A
</text>
</svg>
\ No newline at end of file
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#7F4E7E" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
C
</text>
</svg>
\ No newline at end of file
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#692C77" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
C
</text>
</svg>
\ No newline at end of file
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#9E121D" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
D
</text>
</svg>
\ No newline at end of file
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#969696" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
I
</text>
</svg>
\ No newline at end of file
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#1B80B2" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
M
</text>
</svg>
\ No newline at end of file
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#4668C5" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
R
</text>
</svg>
\ No newline at end of file
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#6C6C6C" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
U
</text>
</svg>
\ No newline at end of file
......@@ -9,29 +9,12 @@ import { Uri, Disposable, SCMProvider, SCMResource, SCMResourceDecorations, SCMR
import { Model } from './model';
import * as path from 'path';
enum Theme {
Light,
Dark
}
const iconsRootPath = path.join(path.dirname(__dirname), 'resources', 'icons');
function getIconUri(iconName: string, theme: Theme): Uri {
const themeName = theme === Theme.Light ? 'light' : 'dark';
return Uri.file(path.join(iconsRootPath, themeName, `${iconName}.svg`));
function getIconUri(iconName: string, theme: string): Uri {
return Uri.file(path.join(iconsRootPath, theme, `${iconName}.svg`));
}
const Icons = {
Modified: getIconUri('status-modified', Theme.Light),
Added: getIconUri('status-added', Theme.Light),
Deleted: getIconUri('status-deleted', Theme.Light),
Renamed: getIconUri('status-renamed', Theme.Light),
Copied: getIconUri('status-copied', Theme.Light),
Untracked: getIconUri('status-untracked', Theme.Light),
Ignored: getIconUri('status-ignored', Theme.Light),
Conflict: getIconUri('status-conflict', Theme.Light),
};
enum Status {
INDEX_MODIFIED,
INDEX_ADDED,
......@@ -57,24 +40,47 @@ class Resource implements SCMResource {
get uri(): Uri { return this._uri; }
private get iconPath(): Uri | undefined {
private static Icons = {
light: {
Modified: getIconUri('status-modified', 'light'),
Added: getIconUri('status-added', 'light'),
Deleted: getIconUri('status-deleted', 'light'),
Renamed: getIconUri('status-renamed', 'light'),
Copied: getIconUri('status-copied', 'light'),
Untracked: getIconUri('status-untracked', 'light'),
Ignored: getIconUri('status-ignored', 'light'),
Conflict: getIconUri('status-conflict', 'light'),
},
dark: {
Modified: getIconUri('status-modified', 'dark'),
Added: getIconUri('status-added', 'dark'),
Deleted: getIconUri('status-deleted', 'dark'),
Renamed: getIconUri('status-renamed', 'dark'),
Copied: getIconUri('status-copied', 'dark'),
Untracked: getIconUri('status-untracked', 'dark'),
Ignored: getIconUri('status-ignored', 'dark'),
Conflict: getIconUri('status-conflict', 'dark')
}
};
private getIconPath(theme: string): Uri | undefined {
switch (this.type) {
case Status.INDEX_MODIFIED: return Icons.Modified;
case Status.MODIFIED: return Icons.Modified;
case Status.INDEX_ADDED: return Icons.Added;
case Status.INDEX_DELETED: return Icons.Deleted;
case Status.DELETED: return Icons.Deleted;
case Status.INDEX_RENAMED: return Icons.Renamed;
case Status.INDEX_COPIED: return Icons.Copied;
case Status.UNTRACKED: return Icons.Untracked;
case Status.IGNORED: return Icons.Ignored;
case Status.BOTH_DELETED: return Icons.Conflict;
case Status.ADDED_BY_US: return Icons.Conflict;
case Status.DELETED_BY_THEM: return Icons.Conflict;
case Status.ADDED_BY_THEM: return Icons.Conflict;
case Status.DELETED_BY_US: return Icons.Conflict;
case Status.BOTH_ADDED: return Icons.Conflict;
case Status.BOTH_MODIFIED: return Icons.Conflict;
case Status.INDEX_MODIFIED: return Resource.Icons[theme].Modified;
case Status.MODIFIED: return Resource.Icons[theme].Modified;
case Status.INDEX_ADDED: return Resource.Icons[theme].Added;
case Status.INDEX_DELETED: return Resource.Icons[theme].Deleted;
case Status.DELETED: return Resource.Icons[theme].Deleted;
case Status.INDEX_RENAMED: return Resource.Icons[theme].Renamed;
case Status.INDEX_COPIED: return Resource.Icons[theme].Copied;
case Status.UNTRACKED: return Resource.Icons[theme].Untracked;
case Status.IGNORED: return Resource.Icons[theme].Ignored;
case Status.BOTH_DELETED: return Resource.Icons[theme].Conflict;
case Status.ADDED_BY_US: return Resource.Icons[theme].Conflict;
case Status.DELETED_BY_THEM: return Resource.Icons[theme].Conflict;
case Status.ADDED_BY_THEM: return Resource.Icons[theme].Conflict;
case Status.DELETED_BY_US: return Resource.Icons[theme].Conflict;
case Status.BOTH_ADDED: return Resource.Icons[theme].Conflict;
case Status.BOTH_MODIFIED: return Resource.Icons[theme].Conflict;
default: return void 0;
}
}
......@@ -92,10 +98,10 @@ class Resource implements SCMResource {
}
get decorations(): SCMResourceDecorations {
return {
iconPath: this.iconPath,
strikeThrough: this.strikeThrough
};
const light = { iconPath: this.getIconPath('light') };
const dark = { iconPath: this.getIconPath('dark') };
return { strikeThrough: this.strikeThrough, light, dark };
}
constructor(private _uri: Uri, private type: any) {
......
......@@ -86,9 +86,14 @@ declare module 'vscode' {
getClickCommand?(node: T): string;
}
export interface SCMResourceDecorations {
export interface SCMResourceThemableDecorations {
readonly iconPath?: string | Uri;
}
export interface SCMResourceDecorations extends SCMResourceThemableDecorations {
readonly strikeThrough?: boolean;
readonly light?: SCMResourceThemableDecorations;
readonly dark?: SCMResourceThemableDecorations;
}
export interface SCMResource {
......
......@@ -236,7 +236,11 @@ export interface SCMProviderFeatures {
supportsOriginalResource: boolean;
}
export type SCMRawResource = [string /*uri*/, string /*decoration icon*/, boolean /*strike through*/];
export type SCMRawResource = [
string /*uri*/,
string[] /*icons: light, dark*/,
boolean /*strike through*/
];
export type SCMRawResourceGroup = [string /*id*/, string /*label*/, SCMRawResource[]];
export abstract class MainThreadSCMShape {
......
......@@ -13,6 +13,16 @@ import { Disposable } from 'vs/workbench/api/node/extHostTypes';
import { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceGroup } from './extHost.protocol';
import * as vscode from 'vscode';
function getIconPath(decorations: vscode.SCMResourceThemableDecorations) {
if (!decorations) {
return void 0;
} else if (typeof decorations.iconPath === 'string') {
return URI.file(decorations.iconPath).toString();
} else if (decorations.iconPath) {
return `${decorations.iconPath}`;
}
}
export class ExtHostSCM {
private _proxy: MainThreadSCMShape;
......@@ -49,20 +59,22 @@ export class ExtHostSCM {
const rawResourceGroups = resourceGroups.map(g => {
const rawResources = g.resources.map(r => {
const uri = r.uri.toString();
let strikeThrough = false;
let decorationIcon: string | undefined;
const iconPath = getIconPath(r.decorations);
const lightIconPath = r.decorations && getIconPath(r.decorations.light) || iconPath;
const darkIconPath = r.decorations && getIconPath(r.decorations.dark) || iconPath;
const icons: string[] = [];
if (r.decorations) {
if (typeof r.decorations.iconPath === 'string') {
decorationIcon = URI.file(r.decorations.iconPath).toString();
} else if (r.decorations.iconPath) {
decorationIcon = `${r.decorations.iconPath}`;
}
if (lightIconPath || darkIconPath) {
icons.push(lightIconPath);
}
strikeThrough = !!r.decorations.strikeThrough;
if (darkIconPath !== lightIconPath) {
icons.push(darkIconPath);
}
return [uri, decorationIcon, strikeThrough] as SCMRawResource;
const strikeThrough = r.decorations && !!r.decorations.strikeThrough;
return [uri, icons, strikeThrough] as SCMRawResource;
});
return [g.id, g.label, rawResources] as SCMRawResourceGroup;
});
......
......@@ -87,9 +87,14 @@ class MainThreadSCMProvider implements ISCMProvider {
const [id, label, rawResources] = rawGroup;
const resources = rawResources.map(rawResource => {
const [uri, decorationIcon, strikeThrough] = rawResource;
const [uri, icons, strikeThrough] = rawResource;
const icon = icons[0];
const iconDark = icons[1] || icon;
const decorations = {
icon: decorationIcon && URI.parse(decorationIcon),
icon: icon && URI.parse(icon),
iconDark: iconDark && URI.parse(iconDark),
strikeThrough
};
......
......@@ -35,6 +35,8 @@ import { IAction, IActionItem } from 'vs/base/common/actions';
import { createActionItem } from 'vs/platform/actions/browser/menuItemActionItem';
import { SCMMenus } from './scmMenus';
import { ActionBar, IActionItemProvider } from 'vs/base/browser/ui/actionbar/actionbar';
import { IThemeService } from 'vs/workbench/services/themes/common/themeService';
import { isDarkTheme } from 'vs/platform/theme/common/themes';
interface SearchInputEvent extends Event {
target: HTMLInputElement;
......@@ -95,6 +97,7 @@ class ResourceRenderer implements IRenderer<ISCMResource, ResourceTemplate> {
constructor(
private scmMenus: SCMMenus,
private actionItemProvider: IActionItemProvider,
@IThemeService private themeService: IThemeService,
@IInstantiationService private instantiationService: IInstantiationService
) {
......@@ -117,8 +120,11 @@ class ResourceRenderer implements IRenderer<ISCMResource, ResourceTemplate> {
template.actionBar.push(this.scmMenus.getResourceActions(resource.resourceGroupId));
toggleClass(template.name, 'strike-through', resource.decorations.strikeThrough);
if (resource.decorations.icon) {
template.decorationIcon.style.backgroundImage = `url('${resource.decorations.icon}')`;
const theme = this.themeService.getColorTheme();
const icon = isDarkTheme(theme) ? resource.decorations.iconDark : resource.decorations.icon;
if (icon) {
template.decorationIcon.style.backgroundImage = `url('${icon}')`;
} else {
template.decorationIcon.style.backgroundImage = '';
}
......@@ -160,6 +166,7 @@ export class SCMViewlet extends Viewlet {
@IKeybindingService private keybindingService: IKeybindingService,
@IMessageService private messageService: IMessageService,
@IContextMenuService private contextMenuService: IContextMenuService,
@IThemeService private themeService: IThemeService,
@IMenuService private menuService: IMenuService
) {
super(VIEWLET_ID, telemetryService);
......@@ -223,6 +230,7 @@ export class SCMViewlet extends Viewlet {
this.setActiveProvider(this.scmService.activeProvider);
this.scmService.onDidChangeProvider(this.setActiveProvider, this, this.disposables);
this.themeService.onDidColorThemeChange(this.update, this, this.disposables);
return TPromise.as(null);
}
......
......@@ -18,7 +18,8 @@ export interface IBaselineResourceProvider {
export const ISCMService = createDecorator<ISCMService>('scm');
export interface ISCMResourceDecorations {
icon: URI;
icon?: URI;
iconDark?: URI;
strikeThrough?: boolean;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册