scm viewlet: themable icons
6
extensions/git/resources/icons/dark/status-added.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<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, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||||
|
A
|
||||||
|
</text>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 431 B |
6
extensions/git/resources/icons/dark/status-conflict.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<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, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||||
|
C
|
||||||
|
</text>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 431 B |
6
extensions/git/resources/icons/dark/status-copied.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<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, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||||
|
C
|
||||||
|
</text>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 431 B |
6
extensions/git/resources/icons/dark/status-deleted.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<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, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||||
|
D
|
||||||
|
</text>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 431 B |
6
extensions/git/resources/icons/dark/status-ignored.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<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, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||||
|
I
|
||||||
|
</text>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 431 B |
6
extensions/git/resources/icons/dark/status-modified.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<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, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||||
|
M
|
||||||
|
</text>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 431 B |
6
extensions/git/resources/icons/dark/status-renamed.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<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, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||||
|
R
|
||||||
|
</text>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 431 B |
6
extensions/git/resources/icons/dark/status-untracked.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<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, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||||
|
U
|
||||||
|
</text>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 431 B |
|
@ -9,29 +9,12 @@ import { Uri, Disposable, SCMProvider, SCMResource, SCMResourceDecorations, SCMR
|
||||||
import { Model } from './model';
|
import { Model } from './model';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
enum Theme {
|
|
||||||
Light,
|
|
||||||
Dark
|
|
||||||
}
|
|
||||||
|
|
||||||
const iconsRootPath = path.join(path.dirname(__dirname), 'resources', 'icons');
|
const iconsRootPath = path.join(path.dirname(__dirname), 'resources', 'icons');
|
||||||
|
|
||||||
function getIconUri(iconName: string, theme: Theme): Uri {
|
function getIconUri(iconName: string, theme: string): Uri {
|
||||||
const themeName = theme === Theme.Light ? 'light' : 'dark';
|
return Uri.file(path.join(iconsRootPath, theme, `${iconName}.svg`));
|
||||||
return Uri.file(path.join(iconsRootPath, themeName, `${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 {
|
enum Status {
|
||||||
INDEX_MODIFIED,
|
INDEX_MODIFIED,
|
||||||
INDEX_ADDED,
|
INDEX_ADDED,
|
||||||
|
@ -57,24 +40,47 @@ class Resource implements SCMResource {
|
||||||
|
|
||||||
get uri(): Uri { return this._uri; }
|
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) {
|
switch (this.type) {
|
||||||
case Status.INDEX_MODIFIED: return Icons.Modified;
|
case Status.INDEX_MODIFIED: return Resource.Icons[theme].Modified;
|
||||||
case Status.MODIFIED: return Icons.Modified;
|
case Status.MODIFIED: return Resource.Icons[theme].Modified;
|
||||||
case Status.INDEX_ADDED: return Icons.Added;
|
case Status.INDEX_ADDED: return Resource.Icons[theme].Added;
|
||||||
case Status.INDEX_DELETED: return Icons.Deleted;
|
case Status.INDEX_DELETED: return Resource.Icons[theme].Deleted;
|
||||||
case Status.DELETED: return Icons.Deleted;
|
case Status.DELETED: return Resource.Icons[theme].Deleted;
|
||||||
case Status.INDEX_RENAMED: return Icons.Renamed;
|
case Status.INDEX_RENAMED: return Resource.Icons[theme].Renamed;
|
||||||
case Status.INDEX_COPIED: return Icons.Copied;
|
case Status.INDEX_COPIED: return Resource.Icons[theme].Copied;
|
||||||
case Status.UNTRACKED: return Icons.Untracked;
|
case Status.UNTRACKED: return Resource.Icons[theme].Untracked;
|
||||||
case Status.IGNORED: return Icons.Ignored;
|
case Status.IGNORED: return Resource.Icons[theme].Ignored;
|
||||||
case Status.BOTH_DELETED: return Icons.Conflict;
|
case Status.BOTH_DELETED: return Resource.Icons[theme].Conflict;
|
||||||
case Status.ADDED_BY_US: return Icons.Conflict;
|
case Status.ADDED_BY_US: return Resource.Icons[theme].Conflict;
|
||||||
case Status.DELETED_BY_THEM: return Icons.Conflict;
|
case Status.DELETED_BY_THEM: return Resource.Icons[theme].Conflict;
|
||||||
case Status.ADDED_BY_THEM: return Icons.Conflict;
|
case Status.ADDED_BY_THEM: return Resource.Icons[theme].Conflict;
|
||||||
case Status.DELETED_BY_US: return Icons.Conflict;
|
case Status.DELETED_BY_US: return Resource.Icons[theme].Conflict;
|
||||||
case Status.BOTH_ADDED: return Icons.Conflict;
|
case Status.BOTH_ADDED: return Resource.Icons[theme].Conflict;
|
||||||
case Status.BOTH_MODIFIED: return Icons.Conflict;
|
case Status.BOTH_MODIFIED: return Resource.Icons[theme].Conflict;
|
||||||
default: return void 0;
|
default: return void 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,10 +98,10 @@ class Resource implements SCMResource {
|
||||||
}
|
}
|
||||||
|
|
||||||
get decorations(): SCMResourceDecorations {
|
get decorations(): SCMResourceDecorations {
|
||||||
return {
|
const light = { iconPath: this.getIconPath('light') };
|
||||||
iconPath: this.iconPath,
|
const dark = { iconPath: this.getIconPath('dark') };
|
||||||
strikeThrough: this.strikeThrough
|
|
||||||
};
|
return { strikeThrough: this.strikeThrough, light, dark };
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(private _uri: Uri, private type: any) {
|
constructor(private _uri: Uri, private type: any) {
|
||||||
|
|
7
src/vs/vscode.proposed.d.ts
vendored
|
@ -86,9 +86,14 @@ declare module 'vscode' {
|
||||||
getClickCommand?(node: T): string;
|
getClickCommand?(node: T): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SCMResourceDecorations {
|
export interface SCMResourceThemableDecorations {
|
||||||
readonly iconPath?: string | Uri;
|
readonly iconPath?: string | Uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SCMResourceDecorations extends SCMResourceThemableDecorations {
|
||||||
readonly strikeThrough?: boolean;
|
readonly strikeThrough?: boolean;
|
||||||
|
readonly light?: SCMResourceThemableDecorations;
|
||||||
|
readonly dark?: SCMResourceThemableDecorations;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SCMResource {
|
export interface SCMResource {
|
||||||
|
|
|
@ -236,7 +236,11 @@ export interface SCMProviderFeatures {
|
||||||
supportsOriginalResource: boolean;
|
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 type SCMRawResourceGroup = [string /*id*/, string /*label*/, SCMRawResource[]];
|
||||||
|
|
||||||
export abstract class MainThreadSCMShape {
|
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 { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceGroup } from './extHost.protocol';
|
||||||
import * as vscode from 'vscode';
|
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 {
|
export class ExtHostSCM {
|
||||||
|
|
||||||
private _proxy: MainThreadSCMShape;
|
private _proxy: MainThreadSCMShape;
|
||||||
|
@ -49,20 +59,22 @@ export class ExtHostSCM {
|
||||||
const rawResourceGroups = resourceGroups.map(g => {
|
const rawResourceGroups = resourceGroups.map(g => {
|
||||||
const rawResources = g.resources.map(r => {
|
const rawResources = g.resources.map(r => {
|
||||||
const uri = r.uri.toString();
|
const uri = r.uri.toString();
|
||||||
let strikeThrough = false;
|
const iconPath = getIconPath(r.decorations);
|
||||||
let decorationIcon: string | undefined;
|
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 (lightIconPath || darkIconPath) {
|
||||||
if (typeof r.decorations.iconPath === 'string') {
|
icons.push(lightIconPath);
|
||||||
decorationIcon = URI.file(r.decorations.iconPath).toString();
|
|
||||||
} else if (r.decorations.iconPath) {
|
|
||||||
decorationIcon = `${r.decorations.iconPath}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
strikeThrough = !!r.decorations.strikeThrough;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
return [g.id, g.label, rawResources] as SCMRawResourceGroup;
|
||||||
});
|
});
|
||||||
|
|
|
@ -87,9 +87,14 @@ class MainThreadSCMProvider implements ISCMProvider {
|
||||||
const [id, label, rawResources] = rawGroup;
|
const [id, label, rawResources] = rawGroup;
|
||||||
|
|
||||||
const resources = rawResources.map(rawResource => {
|
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 = {
|
const decorations = {
|
||||||
icon: decorationIcon && URI.parse(decorationIcon),
|
icon: icon && URI.parse(icon),
|
||||||
|
iconDark: iconDark && URI.parse(iconDark),
|
||||||
strikeThrough
|
strikeThrough
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,8 @@ import { IAction, IActionItem } from 'vs/base/common/actions';
|
||||||
import { createActionItem } from 'vs/platform/actions/browser/menuItemActionItem';
|
import { createActionItem } from 'vs/platform/actions/browser/menuItemActionItem';
|
||||||
import { SCMMenus } from './scmMenus';
|
import { SCMMenus } from './scmMenus';
|
||||||
import { ActionBar, IActionItemProvider } from 'vs/base/browser/ui/actionbar/actionbar';
|
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 {
|
interface SearchInputEvent extends Event {
|
||||||
target: HTMLInputElement;
|
target: HTMLInputElement;
|
||||||
|
@ -95,6 +97,7 @@ class ResourceRenderer implements IRenderer<ISCMResource, ResourceTemplate> {
|
||||||
constructor(
|
constructor(
|
||||||
private scmMenus: SCMMenus,
|
private scmMenus: SCMMenus,
|
||||||
private actionItemProvider: IActionItemProvider,
|
private actionItemProvider: IActionItemProvider,
|
||||||
|
@IThemeService private themeService: IThemeService,
|
||||||
@IInstantiationService private instantiationService: IInstantiationService
|
@IInstantiationService private instantiationService: IInstantiationService
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
@ -117,8 +120,11 @@ class ResourceRenderer implements IRenderer<ISCMResource, ResourceTemplate> {
|
||||||
template.actionBar.push(this.scmMenus.getResourceActions(resource.resourceGroupId));
|
template.actionBar.push(this.scmMenus.getResourceActions(resource.resourceGroupId));
|
||||||
toggleClass(template.name, 'strike-through', resource.decorations.strikeThrough);
|
toggleClass(template.name, 'strike-through', resource.decorations.strikeThrough);
|
||||||
|
|
||||||
if (resource.decorations.icon) {
|
const theme = this.themeService.getColorTheme();
|
||||||
template.decorationIcon.style.backgroundImage = `url('${resource.decorations.icon}')`;
|
const icon = isDarkTheme(theme) ? resource.decorations.iconDark : resource.decorations.icon;
|
||||||
|
|
||||||
|
if (icon) {
|
||||||
|
template.decorationIcon.style.backgroundImage = `url('${icon}')`;
|
||||||
} else {
|
} else {
|
||||||
template.decorationIcon.style.backgroundImage = '';
|
template.decorationIcon.style.backgroundImage = '';
|
||||||
}
|
}
|
||||||
|
@ -160,6 +166,7 @@ export class SCMViewlet extends Viewlet {
|
||||||
@IKeybindingService private keybindingService: IKeybindingService,
|
@IKeybindingService private keybindingService: IKeybindingService,
|
||||||
@IMessageService private messageService: IMessageService,
|
@IMessageService private messageService: IMessageService,
|
||||||
@IContextMenuService private contextMenuService: IContextMenuService,
|
@IContextMenuService private contextMenuService: IContextMenuService,
|
||||||
|
@IThemeService private themeService: IThemeService,
|
||||||
@IMenuService private menuService: IMenuService
|
@IMenuService private menuService: IMenuService
|
||||||
) {
|
) {
|
||||||
super(VIEWLET_ID, telemetryService);
|
super(VIEWLET_ID, telemetryService);
|
||||||
|
@ -223,6 +230,7 @@ export class SCMViewlet extends Viewlet {
|
||||||
|
|
||||||
this.setActiveProvider(this.scmService.activeProvider);
|
this.setActiveProvider(this.scmService.activeProvider);
|
||||||
this.scmService.onDidChangeProvider(this.setActiveProvider, this, this.disposables);
|
this.scmService.onDidChangeProvider(this.setActiveProvider, this, this.disposables);
|
||||||
|
this.themeService.onDidColorThemeChange(this.update, this, this.disposables);
|
||||||
|
|
||||||
return TPromise.as(null);
|
return TPromise.as(null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,8 @@ export interface IBaselineResourceProvider {
|
||||||
export const ISCMService = createDecorator<ISCMService>('scm');
|
export const ISCMService = createDecorator<ISCMService>('scm');
|
||||||
|
|
||||||
export interface ISCMResourceDecorations {
|
export interface ISCMResourceDecorations {
|
||||||
icon: URI;
|
icon?: URI;
|
||||||
|
iconDark?: URI;
|
||||||
strikeThrough?: boolean;
|
strikeThrough?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|