diff --git a/extensions/git/resources/icons/dark/status-added.svg b/extensions/git/resources/icons/dark/status-added.svg
new file mode 100644
index 00000000000..cdc40f45f16
--- /dev/null
+++ b/extensions/git/resources/icons/dark/status-added.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/extensions/git/resources/icons/dark/status-conflict.svg b/extensions/git/resources/icons/dark/status-conflict.svg
new file mode 100644
index 00000000000..53b243c8b9f
--- /dev/null
+++ b/extensions/git/resources/icons/dark/status-conflict.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/extensions/git/resources/icons/dark/status-copied.svg b/extensions/git/resources/icons/dark/status-copied.svg
new file mode 100644
index 00000000000..7bd78c9427e
--- /dev/null
+++ b/extensions/git/resources/icons/dark/status-copied.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/extensions/git/resources/icons/dark/status-deleted.svg b/extensions/git/resources/icons/dark/status-deleted.svg
new file mode 100644
index 00000000000..e7596e2e2ad
--- /dev/null
+++ b/extensions/git/resources/icons/dark/status-deleted.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/extensions/git/resources/icons/dark/status-ignored.svg b/extensions/git/resources/icons/dark/status-ignored.svg
new file mode 100644
index 00000000000..85abc367a29
--- /dev/null
+++ b/extensions/git/resources/icons/dark/status-ignored.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/extensions/git/resources/icons/dark/status-modified.svg b/extensions/git/resources/icons/dark/status-modified.svg
new file mode 100644
index 00000000000..d0de37d3468
--- /dev/null
+++ b/extensions/git/resources/icons/dark/status-modified.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/extensions/git/resources/icons/dark/status-renamed.svg b/extensions/git/resources/icons/dark/status-renamed.svg
new file mode 100644
index 00000000000..878067dd4b1
--- /dev/null
+++ b/extensions/git/resources/icons/dark/status-renamed.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/extensions/git/resources/icons/dark/status-untracked.svg b/extensions/git/resources/icons/dark/status-untracked.svg
new file mode 100644
index 00000000000..c6a48e14f08
--- /dev/null
+++ b/extensions/git/resources/icons/dark/status-untracked.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/extensions/git/src/scmProvider.ts b/extensions/git/src/scmProvider.ts
index 57d74e5d3f3..f397acf5cc9 100644
--- a/extensions/git/src/scmProvider.ts
+++ b/extensions/git/src/scmProvider.ts
@@ -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) {
diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts
index a8f760686f1..4fdb07da97d 100644
--- a/src/vs/vscode.proposed.d.ts
+++ b/src/vs/vscode.proposed.d.ts
@@ -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 {
diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts
index 1f341bd54a6..27b345d4ba0 100644
--- a/src/vs/workbench/api/node/extHost.protocol.ts
+++ b/src/vs/workbench/api/node/extHost.protocol.ts
@@ -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 {
diff --git a/src/vs/workbench/api/node/extHostSCM.ts b/src/vs/workbench/api/node/extHostSCM.ts
index 2ac1569e733..63199d452a3 100644
--- a/src/vs/workbench/api/node/extHostSCM.ts
+++ b/src/vs/workbench/api/node/extHostSCM.ts
@@ -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}`;
- }
-
- strikeThrough = !!r.decorations.strikeThrough;
+ if (lightIconPath || darkIconPath) {
+ icons.push(lightIconPath);
}
- return [uri, decorationIcon, strikeThrough] as SCMRawResource;
+ if (darkIconPath !== lightIconPath) {
+ icons.push(darkIconPath);
+ }
+
+ const strikeThrough = r.decorations && !!r.decorations.strikeThrough;
+
+ return [uri, icons, strikeThrough] as SCMRawResource;
});
return [g.id, g.label, rawResources] as SCMRawResourceGroup;
});
diff --git a/src/vs/workbench/api/node/mainThreadSCM.ts b/src/vs/workbench/api/node/mainThreadSCM.ts
index 90eca27f859..ed4dfee5044 100644
--- a/src/vs/workbench/api/node/mainThreadSCM.ts
+++ b/src/vs/workbench/api/node/mainThreadSCM.ts
@@ -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
};
diff --git a/src/vs/workbench/parts/scm/browser/scmViewlet.ts b/src/vs/workbench/parts/scm/browser/scmViewlet.ts
index d97cfce39da..c477a81b9c4 100644
--- a/src/vs/workbench/parts/scm/browser/scmViewlet.ts
+++ b/src/vs/workbench/parts/scm/browser/scmViewlet.ts
@@ -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 {
constructor(
private scmMenus: SCMMenus,
private actionItemProvider: IActionItemProvider,
+ @IThemeService private themeService: IThemeService,
@IInstantiationService private instantiationService: IInstantiationService
) {
@@ -117,8 +120,11 @@ class ResourceRenderer implements IRenderer {
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);
}
diff --git a/src/vs/workbench/services/scm/common/scm.ts b/src/vs/workbench/services/scm/common/scm.ts
index 68f7810e682..d60e80ae5c5 100644
--- a/src/vs/workbench/services/scm/common/scm.ts
+++ b/src/vs/workbench/services/scm/common/scm.ts
@@ -18,7 +18,8 @@ export interface IBaselineResourceProvider {
export const ISCMService = createDecorator('scm');
export interface ISCMResourceDecorations {
- icon: URI;
+ icon?: URI;
+ iconDark?: URI;
strikeThrough?: boolean;
}