Skip to content

Commit

Permalink
Add config for grayscale icons
Browse files Browse the repository at this point in the history
  • Loading branch information
cezarsa committed Feb 7, 2019
1 parent 7db3541 commit f5a2e4c
Show file tree
Hide file tree
Showing 12 changed files with 221 additions and 4 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,14 @@ After installation or update you can click on the 'Activate'-button to activate

<img src="https://raw.githubusercontent.com/PKief/vscode-material-icon-theme/master/images/oneclickactivation.png" alt="activation" width="60%">

## Grayscale icons

If colors do not make you happy you can change icons to grayscale:

```json
"material-icon-theme.grayscale": true
```

## Commands

Press `Ctrl-Shift-P` to open the command palette and type `Material Icons`.
Expand All @@ -118,6 +126,8 @@ 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 grayscale.

## Icon sources

* [Material Design Icons](https://materialdesignicons.com/)
Expand Down
10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@
{
"command": "material-icon-theme.opacity",
"title": "%command.opacity%"
},
{
"command": "material-icon-theme.grayscale",
"title": "%command.grayscale%"
}
],
"configuration": {
Expand Down Expand Up @@ -157,6 +161,11 @@
"type": "boolean",
"default": false,
"description": "%configuration.hidesExplorerArrows%"
},
"material-icon-theme.grayscale": {
"type": "boolean",
"default": false,
"description": "%configuration.grayscale%"
}
}
}
Expand Down Expand Up @@ -195,3 +204,4 @@
"vscode": "^1.1.26"
}
}

4 changes: 3 additions & 1 deletion package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"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",
"configuration.title": "Material Icons",
"configuration.files.associations": "Set custom file icon associations.",
"configuration.folders.associations": "Set custom folder icon associations.",
Expand All @@ -25,5 +26,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.grayscale": "Enable grayscale icons."
}
53 changes: 53 additions & 0 deletions src/commands/grayscale.ts
Original file line number Diff line number Diff line change
@@ -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('grayscale', true, true);
break;
}
case i18n.translate('toggleSwitch.off'): {
helpers.setThemeConfig('grayscale', false, true);
break;
}
default:
break;
}
};

/** Is grayscale icons enabled? */
export const checkGrayscaleStatus = (): Promise<boolean> => {
return helpers.getMaterialIconsJSON().then((config) => config.options.grayscale);
};
9 changes: 8 additions & 1 deletion src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { changeFolderTheme } from './folders';
import { toggleIconPacks } from './iconPacks';
import { changeOpacity } from './opacity';
import { restoreDefaultConfig } from './restoreConfig';
import { toggleGrayscale } from './grayscale';

// Activate theme
const activateThemeCommand = vscode.commands.registerCommand('material-icon-theme.activateIcons', () => {
Expand Down Expand Up @@ -42,12 +43,18 @@ const changeOpacityCommand = vscode.commands.registerCommand('material-icon-them
changeOpacity();
});

// Toggle grayscale icons
const grayscaleCommand = vscode.commands.registerCommand('material-icon-theme.grayscale', () => {
toggleGrayscale();
});

export const commands = [
activateThemeCommand,
toggleIconPacksCommand,
changeFolderThemeCommand,
toggleFolderColorCommand,
restoreDefaultConfigCommand,
hidesExplorerArrowsCommand,
changeOpacityCommand
changeOpacityCommand,
grayscaleCommand
];
1 change: 1 addition & 0 deletions src/commands/restoreConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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('grayscale', undefined, true);
helpers.setThemeConfig('files.associations', undefined, true);
helpers.setThemeConfig('folders.associations', undefined, true);
helpers.setThemeConfig('languages.associations', undefined, true);
Expand Down
7 changes: 6 additions & 1 deletion src/i18n/lang-en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,10 @@ 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'
}
};
119 changes: 119 additions & 0 deletions src/icons/generator/iconGrayscale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import * as fs from 'fs';
import * as path from 'path';

/**
* Changes all icons in the set to grayscale.
* @param fileNames Only change the grayscale of certain file names.
*/
export const setIconGrayscale = (enable: boolean, fileNames?: string[]) => {

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 (enable) {
updatedRootElement = addFilterAttribute(svgRootElement);
} else {
updatedRootElement = removeFilterAttribute(svgRootElement);
}
let updatedSVG = svg.replace(/<svg[^>]*>/, updatedRootElement);
if (enable) {
updatedSVG = addFilterElement(updatedSVG);
} else {
updatedSVG = removeFilterElement(updatedSVG);
}

fs.writeFileSync(svgFilePath, updatedSVG);
resolve();
});
}
catch (e) {
console.log(e);
reject(e);
}
resolve();
});
};

/**
* Get the SVG root element.
* @param svg SVG file as string.
*/
const getSVGRootElement = (svg: string) => {
const result = new RegExp(/<svg[^>]*>/).exec(svg);
if (result.length > 0) {
return result[0];
} else {
return undefined;
}
};

/**
* Add an filter attribute to the SVG icon.
* @param svgRoot Root element of the SVG icon.
*/
const addFilterAttribute = (svgRoot: string) => {
const pattern = new RegExp(/\sfilter="[^"]+?"/);
const filter = 'url(#grayscale)';
// if the filter attribute already exists
if (pattern.test(svgRoot)) {
return svgRoot.replace(pattern, ` filter="${filter}"`);
} else {
return svgRoot.replace(/^<svg/, `<svg filter="${filter}"`);
}
};

/**
* Remove the filter attribute of the SVG icon.
* @param svgRoot Root element of the SVG icon.
*/
const removeFilterAttribute = (svgRoot: string) => {
const pattern = new RegExp(/\sfilter="[^"]+?"/);
// check if the filter attribute exists
if (pattern.test(svgRoot)) {
return svgRoot.replace(pattern, '');
}
return svgRoot;
};

/**
* Add filter element to the SVG icon.
* @param svg SVG file as string.
*/
const addFilterElement = (svg: string) => {
const pattern = new RegExp(/<filter id="grayscale".+<\/filter>.*<\/svg>/);
if (!pattern.test(svg)) {
const filterElement = `<filter id="grayscale"><feColorMatrix type="saturate" values="0"/></filter>`;
return svg.replace(/<\/svg>/, `${filterElement}</svg>`);
}
return svg;
};

/**
* Remove filter element from the SVG icon.
* @param svg SVG file as string.
*/
const removeFilterElement = (svg: string) => {
const pattern = new RegExp(/<filter id="grayscale".+<\/filter>(.*<\/svg>)/);
if (pattern.test(svg)) {
return svg.replace(pattern, `$1`);
}
return svg;
};
1 change: 1 addition & 0 deletions src/icons/generator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './languageGenerator';
export * from './constants';
export * from './jsonGenerator';
export * from './iconOpacity';
export * from './iconGrayscale';
5 changes: 4 additions & 1 deletion src/icons/generator/jsonGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { fileIcons } from '../fileIcons';
import { folderIcons } from '../folderIcons';
import { languageIcons } from '../languageIcons';
import { iconJsonName } from './constants';
import { generateFolderIcons, getFileIconDefinitions, getFolderIconDefinitions, getLanguageIconDefinitions, setIconOpacity, validateHEXColorCode, validateOpacityValue } from './index';
import { generateFolderIcons, getFileIconDefinitions, getFolderIconDefinitions, getLanguageIconDefinitions, setIconOpacity, setIconGrayscale, validateHEXColorCode, validateOpacityValue } from './index';

/**
* Generate the complete icon configuration object that can be written as JSON file.
Expand Down Expand Up @@ -61,6 +61,9 @@ export const createIconFile = async (updatedConfigs?: IconJsonOptions, updatedJS
if (!updatedConfigs || updatedConfigs.opacity !== undefined) {
await setIconOpacity(options.opacity);
}
if (!updatedConfigs || updatedConfigs.grayscale !== undefined) {
await setIconGrayscale(options.grayscale);
}
});
} catch (error) {
throw Error(error);
Expand Down
5 changes: 5 additions & 0 deletions src/models/i18n/translation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,9 @@ export interface Translation {
reload?: string;
outdatedVersion?: string;
updateVSCode?: string;
grayscale?: {
toggleGrayscale?: string;
enableGrayscale?: string;
disableGrayscale?: string;
};
}
1 change: 1 addition & 0 deletions src/models/icons/iconJsonOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export interface IconJsonOptions {
activeIconPack?: string;
hidesExplorerArrows?: boolean;
opacity?: number;
grayscale?: boolean;
folders?: {
theme?: string;
color?: string;
Expand Down

0 comments on commit f5a2e4c

Please sign in to comment.