From f5a2e4cdfb6186799597e9844cc977cb2c09b67d Mon Sep 17 00:00:00 2001 From: Cezar Sa Espinola Date: Thu, 7 Feb 2019 14:23:56 -0200 Subject: [PATCH 1/2] Add config for grayscale icons --- README.md | 10 +++ package.json | 10 +++ package.nls.json | 4 +- src/commands/grayscale.ts | 53 ++++++++++++ src/commands/index.ts | 9 +- src/commands/restoreConfig.ts | 1 + src/i18n/lang-en.ts | 7 +- src/icons/generator/iconGrayscale.ts | 119 +++++++++++++++++++++++++++ src/icons/generator/index.ts | 1 + src/icons/generator/jsonGenerator.ts | 5 +- src/models/i18n/translation.ts | 5 ++ src/models/icons/iconJsonOptions.ts | 1 + 12 files changed, 221 insertions(+), 4 deletions(-) create mode 100644 src/commands/grayscale.ts create mode 100644 src/icons/generator/iconGrayscale.ts diff --git a/README.md b/README.md index ea0263c6e2..de2105caff 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,14 @@ After installation or update you can click on the 'Activate'-button to activate activation +## 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`. @@ -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/) diff --git a/package.json b/package.json index ceccda590e..07255df0d5 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,10 @@ { "command": "material-icon-theme.opacity", "title": "%command.opacity%" + }, + { + "command": "material-icon-theme.grayscale", + "title": "%command.grayscale%" } ], "configuration": { @@ -157,6 +161,11 @@ "type": "boolean", "default": false, "description": "%configuration.hidesExplorerArrows%" + }, + "material-icon-theme.grayscale": { + "type": "boolean", + "default": false, + "description": "%configuration.grayscale%" } } } @@ -195,3 +204,4 @@ "vscode": "^1.1.26" } } + diff --git a/package.nls.json b/package.nls.json index 59f8abd611..25d686cf2f 100644 --- a/package.nls.json +++ b/package.nls.json @@ -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.", @@ -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." } \ No newline at end of file diff --git a/src/commands/grayscale.ts b/src/commands/grayscale.ts new file mode 100644 index 0000000000..82e65198c7 --- /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('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 => { + return helpers.getMaterialIconsJSON().then((config) => config.options.grayscale); +}; diff --git a/src/commands/index.ts b/src/commands/index.ts index aa255012f1..2e56aeaece 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -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', () => { @@ -42,6 +43,11 @@ 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, @@ -49,5 +55,6 @@ export const commands = [ toggleFolderColorCommand, restoreDefaultConfigCommand, hidesExplorerArrowsCommand, - changeOpacityCommand + changeOpacityCommand, + grayscaleCommand ]; diff --git a/src/commands/restoreConfig.ts b/src/commands/restoreConfig.ts index 1f07005700..d18b126f99 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('grayscale', undefined, true); helpers.setThemeConfig('files.associations', undefined, true); helpers.setThemeConfig('folders.associations', undefined, true); helpers.setThemeConfig('languages.associations', undefined, true); diff --git a/src/i18n/lang-en.ts b/src/i18n/lang-en.ts index 000f176f4a..f889829644 100644 --- a/src/i18n/lang-en.ts +++ b/src/i18n/lang-en.ts @@ -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' + } }; diff --git a/src/icons/generator/iconGrayscale.ts b/src/icons/generator/iconGrayscale.ts new file mode 100644 index 0000000000..5da104b00e --- /dev/null +++ b/src/icons/generator/iconGrayscale.ts @@ -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(/]*>/, 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(/]*>/).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(/^ { + 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(/.*<\/svg>/); + if (!pattern.test(svg)) { + const filterElement = ``; + return svg.replace(/<\/svg>/, `${filterElement}`); + } + return svg; +}; + +/** + * Remove filter element from the SVG icon. + * @param svg SVG file as string. + */ +const removeFilterElement = (svg: string) => { + const pattern = new RegExp(/(.*<\/svg>)/); + if (pattern.test(svg)) { + return svg.replace(pattern, `$1`); + } + return svg; +}; diff --git a/src/icons/generator/index.ts b/src/icons/generator/index.ts index 0bda060bbd..e5b19d0cfb 100644 --- a/src/icons/generator/index.ts +++ b/src/icons/generator/index.ts @@ -4,3 +4,4 @@ export * from './languageGenerator'; export * from './constants'; export * from './jsonGenerator'; export * from './iconOpacity'; +export * from './iconGrayscale'; diff --git a/src/icons/generator/jsonGenerator.ts b/src/icons/generator/jsonGenerator.ts index be56f74416..a654077302 100644 --- a/src/icons/generator/jsonGenerator.ts +++ b/src/icons/generator/jsonGenerator.ts @@ -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. @@ -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); diff --git a/src/models/i18n/translation.ts b/src/models/i18n/translation.ts index 79d68d06b3..46ffb01e40 100644 --- a/src/models/i18n/translation.ts +++ b/src/models/i18n/translation.ts @@ -38,4 +38,9 @@ export interface Translation { reload?: string; outdatedVersion?: string; updateVSCode?: string; + grayscale?: { + toggleGrayscale?: string; + enableGrayscale?: string; + disableGrayscale?: string; + }; } diff --git a/src/models/icons/iconJsonOptions.ts b/src/models/icons/iconJsonOptions.ts index 18f645fcf7..4f382f3d25 100644 --- a/src/models/icons/iconJsonOptions.ts +++ b/src/models/icons/iconJsonOptions.ts @@ -2,6 +2,7 @@ export interface IconJsonOptions { activeIconPack?: string; hidesExplorerArrows?: boolean; opacity?: number; + grayscale?: boolean; folders?: { theme?: string; color?: string; From aea186aeebabadce86d3a3c19f38adaa3aff6a2f Mon Sep 17 00:00:00 2001 From: Cezar Sa Espinola Date: Mon, 11 Feb 2019 10:10:11 -0200 Subject: [PATCH 2/2] Allow custom saturation values for icons --- README.md | 10 +++-- package.json | 14 ++++-- package.nls.json | 3 +- src/commands/grayscale.ts | 6 +-- src/commands/index.ts | 9 +++- src/commands/restoreConfig.ts | 2 +- src/commands/saturation.ts | 43 +++++++++++++++++++ src/i18n/lang-en.ts | 4 ++ .../{iconGrayscale.ts => iconSaturation.ts} | 41 ++++++++++++------ src/icons/generator/index.ts | 2 +- src/icons/generator/jsonGenerator.ts | 18 +++++--- src/models/i18n/translation.ts | 4 ++ src/models/icons/iconJsonOptions.ts | 2 +- 13 files changed, 122 insertions(+), 36 deletions(-) create mode 100644 src/commands/saturation.ts rename src/icons/generator/{iconGrayscale.ts => iconSaturation.ts} (67%) diff --git a/README.md b/README.md index de2105caff..3c1d49ec9e 100644 --- a/README.md +++ b/README.md @@ -98,12 +98,12 @@ After installation or update you can click on the 'Activate'-button to activate activation -## 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 @@ -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 diff --git a/package.json b/package.json index 07255df0d5..cb3459b0cf 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,10 @@ { "command": "material-icon-theme.grayscale", "title": "%command.grayscale%" + }, + { + "command": "material-icon-theme.saturation", + "title": "%command.saturation%" } ], "configuration": { @@ -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%" } } } diff --git a/package.nls.json b/package.nls.json index 25d686cf2f..2444fe4c4b 100644 --- a/package.nls.json +++ b/package.nls.json @@ -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.", @@ -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." } \ No newline at end of file diff --git a/src/commands/grayscale.ts b/src/commands/grayscale.ts index 82e65198c7..dfb4a4a3e8 100644 --- a/src/commands/grayscale.ts +++ b/src/commands/grayscale.ts @@ -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: @@ -49,5 +49,5 @@ const handleQuickPickActions = (value: vscode.QuickPickItem) => { /** Is grayscale icons enabled? */ export const checkGrayscaleStatus = (): Promise => { - return helpers.getMaterialIconsJSON().then((config) => config.options.grayscale); + return helpers.getMaterialIconsJSON().then((config) => config.options.saturation === 0); }; diff --git a/src/commands/index.ts b/src/commands/index.ts index 2e56aeaece..77ec0b3386 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -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', () => { @@ -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, @@ -56,5 +62,6 @@ export const commands = [ restoreDefaultConfigCommand, hidesExplorerArrowsCommand, changeOpacityCommand, - grayscaleCommand + grayscaleCommand, + changeSaturationCommand ]; diff --git a/src/commands/restoreConfig.ts b/src/commands/restoreConfig.ts index d18b126f99..4f1652388c 100644 --- a/src/commands/restoreConfig.ts +++ b/src/commands/restoreConfig.ts @@ -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); 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 f889829644..9b72db4848 100644 --- a/src/i18n/lang-en.ts +++ b/src/i18n/lang-en.ts @@ -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!', } }; diff --git a/src/icons/generator/iconGrayscale.ts b/src/icons/generator/iconSaturation.ts similarity index 67% rename from src/icons/generator/iconGrayscale.ts rename to src/icons/generator/iconSaturation.ts index 5da104b00e..33accb6146 100644 --- a/src/icons/generator/iconGrayscale.ts +++ b/src/icons/generator/iconSaturation.ts @@ -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, '..', '..', '..'); @@ -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(/]*>/, updatedRootElement); - if (enable) { - updatedSVG = addFilterElement(updatedSVG); + if (saturation < 1) { + updatedSVG = addFilterElement(updatedSVG, saturation); } else { updatedSVG = removeFilterElement(updatedSVG); } @@ -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(/^ { * Add filter element to the SVG icon. * @param svg SVG file as string. */ -const addFilterElement = (svg: string) => { - const pattern = new RegExp(/.*<\/svg>/); - if (!pattern.test(svg)) { - const filterElement = ``; +const addFilterElement = (svg: string, value: number) => { + const pattern = new RegExp(/(.*<\/svg>)/); + const filterElement = ``; + if (pattern.test(svg)) { + return svg.replace(pattern, `${filterElement}$1`); + } else { return svg.replace(/<\/svg>/, `${filterElement}`); } return svg; @@ -111,9 +116,17 @@ const addFilterElement = (svg: string) => { * @param svg SVG file as string. */ const removeFilterElement = (svg: string) => { - const pattern = new RegExp(/(.*<\/svg>)/); + const pattern = new RegExp(/(.*<\/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; +}; diff --git a/src/icons/generator/index.ts b/src/icons/generator/index.ts index e5b19d0cfb..005e431f84 100644 --- a/src/icons/generator/index.ts +++ b/src/icons/generator/index.ts @@ -4,4 +4,4 @@ export * from './languageGenerator'; export * from './constants'; export * from './jsonGenerator'; export * from './iconOpacity'; -export * from './iconGrayscale'; +export * from './iconSaturation'; diff --git a/src/icons/generator/jsonGenerator.ts b/src/icons/generator/jsonGenerator.ts index a654077302..4805e9ad1f 100644 --- a/src/icons/generator/jsonGenerator.ts +++ b/src/icons/generator/jsonGenerator.ts @@ -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. @@ -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. @@ -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) { @@ -83,6 +88,7 @@ export const getDefaultIconOptions = (): IconJsonOptions => ({ activeIconPack: 'angular', hidesExplorerArrows: false, opacity: 1, + saturation: 1, files: { associations: {} }, languages: { associations: {} }, }); diff --git a/src/models/i18n/translation.ts b/src/models/i18n/translation.ts index 46ffb01e40..827bc32d86 100644 --- a/src/models/i18n/translation.ts +++ b/src/models/i18n/translation.ts @@ -43,4 +43,8 @@ export interface Translation { enableGrayscale?: string; disableGrayscale?: string; }; + saturation?: { + inputPlaceholder?: string; + wrongValue?: string; + }; } diff --git a/src/models/icons/iconJsonOptions.ts b/src/models/icons/iconJsonOptions.ts index 4f382f3d25..dd757bfd43 100644 --- a/src/models/icons/iconJsonOptions.ts +++ b/src/models/icons/iconJsonOptions.ts @@ -2,7 +2,7 @@ export interface IconJsonOptions { activeIconPack?: string; hidesExplorerArrows?: boolean; opacity?: number; - grayscale?: boolean; + saturation?: number; folders?: { theme?: string; color?: string;