diff --git a/README.md b/README.md
index 51e10f19c7..c614def1f9 100644
--- a/README.md
+++ b/README.md
@@ -99,6 +99,14 @@ After installation or update you can click on the 'Activate'-button to activate
+## Custom icon saturation
+
+If colors do not make you happy you can change icons to have less saturation making them look grayish or completely grayscale by setting saturation to 0:
+
+```json
+"material-icon-theme.saturation": 0.5
+```
+
## Commands
Press `Ctrl-Shift-P` to open the command palette and type `Material Icons`.
@@ -119,6 +127,10 @@ Press `Ctrl-Shift-P` to open the command palette and type `Material Icons`.
- **Restore Default Configuration**: Reset the default configurations of the icon theme.
+- **Toggle Grayscale**: Change icons to saturation to 0 making them look grayscale.
+
+- **Change Saturation**: Change the saturation value of the icons.
+
## Icon sources
* [Material Design Icons](https://materialdesignicons.com/)
diff --git a/package.json b/package.json
index 91dfb645e3..648237325d 100644
--- a/package.json
+++ b/package.json
@@ -70,6 +70,14 @@
{
"command": "material-icon-theme.opacity",
"title": "%command.opacity%"
+ },
+ {
+ "command": "material-icon-theme.grayscale",
+ "title": "%command.grayscale%"
+ },
+ {
+ "command": "material-icon-theme.saturation",
+ "title": "%command.saturation%"
}
],
"configuration": {
@@ -157,6 +165,13 @@
"type": "boolean",
"default": false,
"description": "%configuration.hidesExplorerArrows%"
+ },
+ "material-icon-theme.saturation": {
+ "type": "number",
+ "default": 1,
+ "minimum": 0,
+ "maximum": 1,
+ "description": "%configuration.saturation%"
}
}
}
@@ -195,3 +210,4 @@
"vscode": "^1.1.29"
}
}
+
diff --git a/package.nls.json b/package.nls.json
index 59f8abd611..2444fe4c4b 100644
--- a/package.nls.json
+++ b/package.nls.json
@@ -6,6 +6,8 @@
"command.restoreDefaultConfig": "Material Icons: Restore Default Configuration",
"command.hidesExplorerArrows": "Material Icons: Hide Folder Arrows",
"command.opacity": "Material Icons: Change Opacity",
+ "command.grayscale": "Material Icons: Toggle Grayscale",
+ "command.saturation": "Material Icons: Change Saturation",
"configuration.title": "Material Icons",
"configuration.files.associations": "Set custom file icon associations.",
"configuration.folders.associations": "Set custom folder icon associations.",
@@ -25,5 +27,6 @@
"configuration.folders.theme.none": "No folder icons.",
"configuration.folders.color": "Change the color of the folder icons.",
"configuration.hidesExplorerArrows": "Hide explorer arrows before folder.",
- "configuration.opacity": "Change the opacity of the icons."
+ "configuration.opacity": "Change the opacity of the icons.",
+ "configuration.saturation": "Change the saturation of the icons."
}
\ No newline at end of file
diff --git a/src/commands/grayscale.ts b/src/commands/grayscale.ts
new file mode 100644
index 0000000000..dfb4a4a3e8
--- /dev/null
+++ b/src/commands/grayscale.ts
@@ -0,0 +1,53 @@
+import * as vscode from 'vscode';
+import * as helpers from './../helpers';
+import * as i18n from './../i18n';
+
+/** Command to toggle grayscale. */
+export const toggleGrayscale = () => {
+ return checkGrayscaleStatus()
+ .then(showQuickPickItems)
+ .then(handleQuickPickActions)
+ .catch(err => console.log(err));
+};
+
+/** Show QuickPick items to select preferred configuration for grayscale icons. */
+const showQuickPickItems = (status: boolean) => {
+ const on: vscode.QuickPickItem = {
+ description: i18n.translate('toggleSwitch.on'),
+ detail: i18n.translate(`grayscale.enableGrayscale`),
+ label: status ? '\u2714' : '\u25FB'
+ };
+ const off: vscode.QuickPickItem = {
+ description: i18n.translate('toggleSwitch.off'),
+ detail: i18n.translate(`grayscale.disableGrayscale`),
+ label: !status ? '\u2714' : '\u25FB'
+ };
+ return vscode.window.showQuickPick(
+ [on, off], {
+ placeHolder: i18n.translate('grayscale.toggleGrayscale'),
+ ignoreFocusOut: false,
+ matchOnDescription: true
+ });
+};
+
+/** Handle the actions from the QuickPick. */
+const handleQuickPickActions = (value: vscode.QuickPickItem) => {
+ if (!value || !value.description) return;
+ switch (value.description) {
+ case i18n.translate('toggleSwitch.on'): {
+ helpers.setThemeConfig('saturation', 0, true);
+ break;
+ }
+ case i18n.translate('toggleSwitch.off'): {
+ helpers.setThemeConfig('saturation', 1, true);
+ break;
+ }
+ default:
+ break;
+ }
+};
+
+/** Is grayscale icons enabled? */
+export const checkGrayscaleStatus = (): Promise => {
+ return helpers.getMaterialIconsJSON().then((config) => config.options.saturation === 0);
+};
diff --git a/src/commands/index.ts b/src/commands/index.ts
index aa255012f1..77ec0b3386 100644
--- a/src/commands/index.ts
+++ b/src/commands/index.ts
@@ -6,6 +6,8 @@ import { changeFolderTheme } from './folders';
import { toggleIconPacks } from './iconPacks';
import { changeOpacity } from './opacity';
import { restoreDefaultConfig } from './restoreConfig';
+import { toggleGrayscale } from './grayscale';
+import { changeSaturation } from './saturation';
// Activate theme
const activateThemeCommand = vscode.commands.registerCommand('material-icon-theme.activateIcons', () => {
@@ -42,6 +44,16 @@ const changeOpacityCommand = vscode.commands.registerCommand('material-icon-them
changeOpacity();
});
+// Toggle grayscale icons
+const grayscaleCommand = vscode.commands.registerCommand('material-icon-theme.grayscale', () => {
+ toggleGrayscale();
+});
+
+// Change the saturation of the icons
+const changeSaturationCommand = vscode.commands.registerCommand('material-icon-theme.saturation', () => {
+ changeSaturation();
+});
+
export const commands = [
activateThemeCommand,
toggleIconPacksCommand,
@@ -49,5 +61,7 @@ export const commands = [
toggleFolderColorCommand,
restoreDefaultConfigCommand,
hidesExplorerArrowsCommand,
- changeOpacityCommand
+ changeOpacityCommand,
+ grayscaleCommand,
+ changeSaturationCommand
];
diff --git a/src/commands/restoreConfig.ts b/src/commands/restoreConfig.ts
index 1f07005700..4f1652388c 100644
--- a/src/commands/restoreConfig.ts
+++ b/src/commands/restoreConfig.ts
@@ -7,6 +7,7 @@ export const restoreDefaultConfig = () => {
helpers.setThemeConfig('folders.color', undefined, true);
helpers.setThemeConfig('hidesExplorerArrows', undefined, true);
helpers.setThemeConfig('opacity', undefined, true);
+ helpers.setThemeConfig('saturation', undefined, true);
helpers.setThemeConfig('files.associations', undefined, true);
helpers.setThemeConfig('folders.associations', undefined, true);
helpers.setThemeConfig('languages.associations', undefined, true);
diff --git a/src/commands/saturation.ts b/src/commands/saturation.ts
new file mode 100644
index 0000000000..19016e1dd5
--- /dev/null
+++ b/src/commands/saturation.ts
@@ -0,0 +1,43 @@
+import * as vscode from 'vscode';
+import { getDefaultIconOptions, validateSaturationValue } from '../icons';
+import * as helpers from './../helpers';
+import * as i18n from './../i18n';
+
+/** Command to toggle the folder icons. */
+export const changeSaturation = () => {
+ return getCurrentSaturationValue()
+ .then(showInput)
+ .catch(err => console.log(err));
+};
+
+/** Show input to enter the saturation value. */
+const showInput = (saturation: number) => {
+ vscode.window.showInputBox({
+ placeHolder: i18n.translate('saturation.inputPlaceholder'),
+ ignoreFocusOut: true,
+ value: String(saturation),
+ validateInput: validateSaturationInput
+ }).then(value => setSaturationConfig(+value));
+};
+
+/** Validate the saturation value which was inserted by the user. */
+const validateSaturationInput = (saturationInput: string) => {
+ if (!validateSaturationValue(+saturationInput)) {
+ return i18n.translate('saturation.wrongValue');
+ }
+ return undefined;
+};
+
+/** Get the current value of the saturation of the icons. */
+export const getCurrentSaturationValue = (): Promise => {
+ const defaultOptions = getDefaultIconOptions();
+ return helpers.getMaterialIconsJSON().then((config) =>
+ config.options.saturation === undefined ?
+ defaultOptions.saturation : config.options.saturation);
+};
+
+const setSaturationConfig = (saturation: number) => {
+ if (saturation !== undefined) {
+ helpers.setThemeConfig('saturation', saturation, true);
+ }
+};
diff --git a/src/i18n/lang-en.ts b/src/i18n/lang-en.ts
index 000f176f4a..9b72db4848 100644
--- a/src/i18n/lang-en.ts
+++ b/src/i18n/lang-en.ts
@@ -39,5 +39,14 @@ export const translation: Translation = {
'confirmReload': 'You have to restart VS Code to activate the changes to the icons.',
'reload': 'Restart',
'outdatedVersion': 'You have to update VS Code to use this command.',
- 'updateVSCode': 'Update VS Code'
+ 'updateVSCode': 'Update VS Code',
+ 'grayscale': {
+ 'toggleGrayscale': 'Toggle grayscale icons',
+ 'enableGrayscale': 'Enable grayscale icons',
+ 'disableGrayscale': 'Disable grayscale icons'
+ },
+ 'saturation': {
+ 'inputPlaceholder': 'Saturation value (between 0 and 1)',
+ 'wrongValue': 'The value must be between 0 and 1!',
+ }
};
diff --git a/src/icons/generator/iconSaturation.ts b/src/icons/generator/iconSaturation.ts
new file mode 100644
index 0000000000..33accb6146
--- /dev/null
+++ b/src/icons/generator/iconSaturation.ts
@@ -0,0 +1,132 @@
+import * as fs from 'fs';
+import * as path from 'path';
+
+/**
+ * Changes saturation of all icons in the set.
+ * @param saturation Saturation value.
+ * @param fileNames Only change the saturation of certain file names.
+ */
+export const setIconSaturation = (saturation: number, fileNames?: string[]) => {
+ if (!validateSaturationValue(saturation)) {
+ return console.error('Invalid saturation value! Saturation must be a decimal number between 0 and 1!');
+ }
+
+ return new Promise((resolve, reject) => {
+ let iconsPath = path.join(__dirname, '..', '..', '..');
+ const parentFolder = iconsPath.split(path.sep).pop();
+ if (parentFolder === 'out') {
+ iconsPath = path.join(iconsPath, '..');
+ }
+ iconsPath = path.join(iconsPath, 'icons');
+
+ // read all icon files from the icons folder
+ try {
+ (fileNames || fs.readdirSync(iconsPath)).forEach(iconFileName => {
+ const svgFilePath = path.join(iconsPath, iconFileName);
+
+ // Read SVG file
+ const svg = fs.readFileSync(svgFilePath, 'utf-8');
+
+ // Get the root element of the SVG file
+ const svgRootElement = getSVGRootElement(svg);
+ if (!svgRootElement) return;
+
+ let updatedRootElement: string;
+ if (saturation < 1) {
+ updatedRootElement = addFilterAttribute(svgRootElement);
+ } else {
+ updatedRootElement = removeFilterAttribute(svgRootElement);
+ }
+ let updatedSVG = svg.replace(/