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 * 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) {
|
||||
|
|
7
src/vs/vscode.proposed.d.ts
vendored
|
@ -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}`;
|
||||
}
|
||||
|
||||
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;
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|