Skip to content

Commit

Permalink
Allow custom saturation values for icons
Browse files Browse the repository at this point in the history
  • Loading branch information
cezarsa committed Feb 12, 2019
1 parent f5a2e4c commit aea186a
Show file tree
Hide file tree
Showing 13 changed files with 122 additions and 36 deletions.
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,12 @@ 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
## Custom icon saturation

If colors do not make you happy you can change icons to grayscale:
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.grayscale": true
"material-icon-theme.saturation": 0.5
```

## Commands
Expand All @@ -126,7 +126,9 @@ 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.
- **Toggle Grayscale**: Change icons to saturation to 0 making them look grayscale.

- **Change Saturation**: Change the saturation value of the icons.

## Icon sources

Expand Down
14 changes: 10 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@
{
"command": "material-icon-theme.grayscale",
"title": "%command.grayscale%"
},
{
"command": "material-icon-theme.saturation",
"title": "%command.saturation%"
}
],
"configuration": {
Expand Down Expand Up @@ -162,10 +166,12 @@
"default": false,
"description": "%configuration.hidesExplorerArrows%"
},
"material-icon-theme.grayscale": {
"type": "boolean",
"default": false,
"description": "%configuration.grayscale%"
"material-icon-theme.saturation": {
"type": "number",
"default": 1,
"minimum": 0,
"maximum": 1,
"description": "%configuration.saturation%"
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"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.",
Expand All @@ -27,5 +28,5 @@
"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.grayscale": "Enable grayscale icons."
"configuration.saturation": "Change the saturation of the icons."
}
6 changes: 3 additions & 3 deletions src/commands/grayscale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ const handleQuickPickActions = (value: vscode.QuickPickItem) => {
if (!value || !value.description) return;
switch (value.description) {
case i18n.translate('toggleSwitch.on'): {
helpers.setThemeConfig('grayscale', true, true);
helpers.setThemeConfig('saturation', 0, true);
break;
}
case i18n.translate('toggleSwitch.off'): {
helpers.setThemeConfig('grayscale', false, true);
helpers.setThemeConfig('saturation', 1, true);
break;
}
default:
Expand All @@ -49,5 +49,5 @@ const handleQuickPickActions = (value: vscode.QuickPickItem) => {

/** Is grayscale icons enabled? */
export const checkGrayscaleStatus = (): Promise<boolean> => {
return helpers.getMaterialIconsJSON().then((config) => config.options.grayscale);
return helpers.getMaterialIconsJSON().then((config) => config.options.saturation === 0);
};
9 changes: 8 additions & 1 deletion src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ 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', () => {
Expand Down Expand Up @@ -48,6 +49,11 @@ const grayscaleCommand = vscode.commands.registerCommand('material-icon-theme.gr
toggleGrayscale();
});

// Change the saturation of the icons
const changeSaturationCommand = vscode.commands.registerCommand('material-icon-theme.saturation', () => {
changeSaturation();
});

export const commands = [
activateThemeCommand,
toggleIconPacksCommand,
Expand All @@ -56,5 +62,6 @@ export const commands = [
restoreDefaultConfigCommand,
hidesExplorerArrowsCommand,
changeOpacityCommand,
grayscaleCommand
grayscaleCommand,
changeSaturationCommand
];
2 changes: 1 addition & 1 deletion src/commands/restoreConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +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('saturation', undefined, true);
helpers.setThemeConfig('files.associations', undefined, true);
helpers.setThemeConfig('folders.associations', undefined, true);
helpers.setThemeConfig('languages.associations', undefined, true);
Expand Down
43 changes: 43 additions & 0 deletions src/commands/saturation.ts
Original file line number Diff line number Diff line change
@@ -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<number> => {
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);
}
};
4 changes: 4 additions & 0 deletions src/i18n/lang-en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,9 @@ export const translation: Translation = {
'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!',
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ 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.
* Changes saturation of all icons in the set.
* @param saturation Saturation value.
* @param fileNames Only change the saturation of certain file names.
*/
export const setIconGrayscale = (enable: boolean, fileNames?: string[]) => {
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, '..', '..', '..');
Expand All @@ -28,14 +32,14 @@ export const setIconGrayscale = (enable: boolean, fileNames?: string[]) => {
if (!svgRootElement) return;

let updatedRootElement: string;
if (enable) {
if (saturation < 1) {
updatedRootElement = addFilterAttribute(svgRootElement);
} else {
updatedRootElement = removeFilterAttribute(svgRootElement);
}
let updatedSVG = svg.replace(/<svg[^>]*>/, updatedRootElement);
if (enable) {
updatedSVG = addFilterElement(updatedSVG);
if (saturation < 1) {
updatedSVG = addFilterElement(updatedSVG, saturation);
} else {
updatedSVG = removeFilterElement(updatedSVG);
}
Expand Down Expand Up @@ -71,12 +75,11 @@ const getSVGRootElement = (svg: string) => {
*/
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}"`);
return svgRoot.replace(pattern, ` filter="url(#saturation)"`);
} else {
return svgRoot.replace(/^<svg/, `<svg filter="${filter}"`);
return svgRoot.replace(/^<svg/, `<svg filter="url(#saturation)"`);
}
};

Expand All @@ -97,10 +100,12 @@ const removeFilterAttribute = (svgRoot: string) => {
* 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>`;
const addFilterElement = (svg: string, value: number) => {
const pattern = new RegExp(/<filter id="saturation".+<\/filter>(.*<\/svg>)/);
const filterElement = `<filter id="saturation"><feColorMatrix type="saturate" values="${value}"/></filter>`;
if (pattern.test(svg)) {
return svg.replace(pattern, `${filterElement}$1`);
} else {
return svg.replace(/<\/svg>/, `${filterElement}</svg>`);
}
return svg;
Expand All @@ -111,9 +116,17 @@ const addFilterElement = (svg: string) => {
* @param svg SVG file as string.
*/
const removeFilterElement = (svg: string) => {
const pattern = new RegExp(/<filter id="grayscale".+<\/filter>(.*<\/svg>)/);
const pattern = new RegExp(/<filter id="saturation".+<\/filter>(.*<\/svg>)/);
if (pattern.test(svg)) {
return svg.replace(pattern, `$1`);
}
return svg;
};

/**
* Validate the saturation value.
* @param saturation Saturation value
*/
export const validateSaturationValue = (saturation: number) => {
return saturation !== null && saturation <= 1 && saturation >= 0;
};
2 changes: 1 addition & 1 deletion src/icons/generator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ export * from './languageGenerator';
export * from './constants';
export * from './jsonGenerator';
export * from './iconOpacity';
export * from './iconGrayscale';
export * from './iconSaturation';
18 changes: 12 additions & 6 deletions 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, setIconGrayscale, validateHEXColorCode, validateOpacityValue } from './index';
import { generateFolderIcons, getFileIconDefinitions, getFolderIconDefinitions, getLanguageIconDefinitions, setIconOpacity, setIconSaturation, validateHEXColorCode, validateOpacityValue, validateSaturationValue } from './index';

/**
* Generate the complete icon configuration object that can be written as JSON file.
Expand All @@ -32,9 +32,14 @@ export const createIconFile = async (updatedConfigs?: IconJsonOptions, updatedJS
const iconJSONPath = path.join(__dirname, '../../../', 'src', iconJsonName);
const json = generateIconConfigurationObject(options);

// make sure that the opacity value must be entered correctly to trigger a reload.
if (updatedConfigs && updatedConfigs.opacity !== undefined && !validateOpacityValue(updatedConfigs.opacity)) {
return Promise.reject('Material Icons: Invalid opacity value!');
// make sure that the opacity and saturation values must be entered correctly to trigger a reload.
if (updatedConfigs) {
if (updatedConfigs.opacity !== undefined && !validateOpacityValue(updatedConfigs.opacity)) {
return Promise.reject('Material Icons: Invalid opacity value!');
}
if (updatedConfigs.saturation !== undefined && !validateSaturationValue(updatedConfigs.saturation)) {
return Promise.reject('Material Icons: Invalid saturation value!');
}
}

// make sure that the value for the folder color is entered correctly to trigger a reload.
Expand All @@ -61,8 +66,8 @@ 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);
if (!updatedConfigs || updatedConfigs.saturation !== undefined) {
await setIconSaturation(options.saturation);
}
});
} catch (error) {
Expand All @@ -83,6 +88,7 @@ export const getDefaultIconOptions = (): IconJsonOptions => ({
activeIconPack: 'angular',
hidesExplorerArrows: false,
opacity: 1,
saturation: 1,
files: { associations: {} },
languages: { associations: {} },
});
4 changes: 4 additions & 0 deletions src/models/i18n/translation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,8 @@ export interface Translation {
enableGrayscale?: string;
disableGrayscale?: string;
};
saturation?: {
inputPlaceholder?: string;
wrongValue?: string;
};
}
2 changes: 1 addition & 1 deletion src/models/icons/iconJsonOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export interface IconJsonOptions {
activeIconPack?: string;
hidesExplorerArrows?: boolean;
opacity?: number;
grayscale?: boolean;
saturation?: number;
folders?: {
theme?: string;
color?: string;
Expand Down

0 comments on commit aea186a

Please sign in to comment.