diff --git a/src/vs/base/common/color.ts b/src/vs/base/common/color.ts new file mode 100644 index 0000000000000..a532c64cc86b3 --- /dev/null +++ b/src/vs/base/common/color.ts @@ -0,0 +1,245 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as Object from 'vs/base/common/objects'; + +export interface RGBA { r: number; g: number; b: number; a: number; } +export interface HSLA { h: number; s: number; l: number; a: number; } + +/** + * Converts an Hex color value to RGB. + * returns r, g, and b are contained in the set [0, 255] + */ +function hex2rgba(hex: string): RGBA { + function parseHex(str: string) { + return parseInt('0x' + str); + } + if (hex.charAt(0) === '#' && hex.length >= 7) { + let r = parseHex(hex.substr(1, 2)); + let g = parseHex(hex.substr(3, 2)); + let b = parseHex(hex.substr(5, 2)); + let a = hex.length === 9 ? parseHex(hex.substr(7, 2)) / 0xff : 1; + return { r, g, b, a }; + } + return { r: 255, g: 0, b: 0, a: 1 }; +} + +/** + * Converts an RGB color value to HSL. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes r, g, and b are contained in the set [0, 255] and + * returns h in the set [0, 360], s, and l in the set [0, 1]. + */ +function rgba2hsla(rgba: RGBA): HSLA { + let r = rgba.r / 255; + let g = rgba.g / 255; + let b = rgba.b / 255; + let a = rgba.a === void 0 ? rgba.a : 1; + + let max = Math.max(r, g, b), min = Math.min(r, g, b); + let h = 0, s = 0, l = Math.round(((min + max) / 2) * 1000) / 1000, chroma = max - min; + + if (chroma > 0) { + s = Math.min(Math.round((l <= 0.5 ? chroma / (2 * l) : chroma / (2 - (2 * l))) * 1000) / 1000, 1); + switch (max) { + case r: h = (g - b) / chroma + (g < b ? 6 : 0); break; + case g: h = (b - r) / chroma + 2; break; + case b: h = (r - g) / chroma + 4; break; + } + h *= 60; + h = Math.round(h); + } + return { h, s, l, a }; +} + +/** + * Converts an HSL color value to RGB. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes h in the set [0, 360] s, and l are contained in the set [0, 1] and + * returns r, g, and b in the set [0, 255]. + */ +function hsla2rgba(hsla: HSLA): RGBA { + let h = hsla.h / 360; + let s = Math.min(hsla.s, 1); + let l = Math.min(hsla.l, 1); + let a = hsla.a === void 0 ? hsla.a : 1; + let r, g, b; + + if (s === 0) { + r = g = b = l; // achromatic + } else { + let hue2rgb = function hue2rgb(p, q, t) { + if (t < 0) { + t += 1; + } + if (t > 1) { + t -= 1; + } + if (t < 1 / 6) { + return p + (q - p) * 6 * t; + } + if (t < 1 / 2) { + return q; + } + if (t < 2 / 3) { + return p + (q - p) * (2 / 3 - t) * 6; + } + return p; + }; + + let q = l < 0.5 ? l * (1 + s) : l + s - l * s; + let p = 2 * l - q; + r = hue2rgb(p, q, h + 1 / 3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1 / 3); + } + + return { r: Math.round(r * 255), g: Math.round(g * 255), b: Math.round(b * 255), a }; +} + +export class Color { + + private rgba: RGBA; + private hsla: HSLA; + private str: string; + + constructor(arg: string | RGBA) { + this.rgba = typeof arg === 'string' ? hex2rgba(arg) : arg; + this.str = null; + } + + /** + * http://www.w3.org/TR/WCAG20/#relativeluminancedef + * Returns the number in the set [0, 1]. O => Darkest Black. 1 => Lightest white. + */ + public getLuminosity(): number { + let luminosityFor = function (color): number { + let c = color / 255; + return (c <= 0.03928) ? c / 12.92 : Math.pow(((c + 0.055) / 1.055), 2.4); + }; + let R = luminosityFor(this.rgba.r); + let G = luminosityFor(this.rgba.g); + let B = luminosityFor(this.rgba.b); + let luminosity = 0.2126 * R + 0.7152 * G + 0.0722 * B; + return Math.round(luminosity * 10000) / 10000; + } + + /** + * http://www.w3.org/TR/WCAG20/#contrast-ratiodef + * Returns the contrast ration number in the set [1, 21]. + */ + public getContrast(another: Color): number { + let lum1 = this.getLuminosity(); + let lum2 = another.getLuminosity(); + return lum1 > lum2 ? (lum1 + 0.05) / (lum2 + 0.05) : (lum2 + 0.05) / (lum1 + 0.05); + } + + /** + * http://24ways.org/2010/calculating-color-contrast + * Return 'true' if darker color otherwise 'false' + */ + public isDarker(): boolean { + var yiq = (this.rgba.r * 299 + this.rgba.g * 587 + this.rgba.b * 114) / 1000; + return yiq < 128; + } + + /** + * http://24ways.org/2010/calculating-color-contrast + * Return 'true' if lighter color otherwise 'false' + */ + public isLighter(): boolean { + var yiq = (this.rgba.r * 299 + this.rgba.g * 587 + this.rgba.b * 114) / 1000; + return yiq >= 128; + } + + public isLighterThan(another: Color): boolean { + let lum1 = this.getLuminosity(); + let lum2 = another.getLuminosity(); + return lum1 > lum2; + } + + public isDarkerThan(another: Color): boolean { + let lum1 = this.getLuminosity(); + let lum2 = another.getLuminosity(); + return lum1 < lum2; + } + + public lighten(factor: number): Color { + let hsl = this.toHSLA(); + hsl.l += hsl.l * factor; + return new Color(hsla2rgba(hsl)); + } + + public darken(factor: number): Color { + let hsl = this.toHSLA(); + hsl.l -= hsl.l * factor; + return new Color(hsla2rgba(hsl)); + } + + public transparent(factor: number): Color { + let p = this.rgba; + return new Color({ r: p.r, g: p.g, b: p.b, a: p.a * factor }); + } + + public opposite(): Color { + return new Color({ + r: 255 - this.rgba.r, + g: 255 - this.rgba.g, + b: 255 - this.rgba.b, + a: this.rgba.a + }); + } + + public toString(): string { + if (!this.str) { + let p = this.rgba; + this.str = `rgba(${p.r}, ${p.g}, ${p.b}, ${+p.a.toFixed(2)})`; + } + return this.str; + } + + public toHSLA(): HSLA { + if (!this.hsla) { + this.hsla = rgba2hsla(this.rgba); + } + return Object.clone(this.hsla); + } + + public toRGBA(): RGBA { + return Object.clone(this.rgba); + } + + public static fromRGBA(rgba: RGBA): Color { + return new Color(rgba); + } + + public static fromHex(hex: string): Color { + return new Color(hex); + } + + public static fromHSLA(hsla: HSLA): Color { + return new Color(hsla2rgba(hsla)); + } + + public static getLighterColor(of: Color, relative: Color, factor?: number): Color { + if (of.isLighterThan(relative)) { + return of; + } + factor = factor ? factor : 0.5; + let lum1 = of.getLuminosity(), lum2 = relative.getLuminosity(); + factor = factor * (lum2 - lum1) / lum2; + return of.lighten(factor); + } + + public static getDarkerColor(of: Color, relative: Color, factor?: number): Color { + if (of.isDarkerThan(relative)) { + return of; + } + factor = factor ? factor : 0.5; + let lum1 = of.getLuminosity(), lum2 = relative.getLuminosity(); + factor = factor * (lum1 - lum2) / lum1; + return of.darken(factor); + } +} \ No newline at end of file diff --git a/src/vs/base/test/common/color.test.ts b/src/vs/base/test/common/color.test.ts new file mode 100644 index 0000000000000..67d2469f11ec3 --- /dev/null +++ b/src/vs/base/test/common/color.test.ts @@ -0,0 +1,168 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import {Color} from 'vs/base/common/color'; +import * as assert from 'assert'; + +suite('Color', () => { + + test('rgba2hsla', function () { + assert.deepEqual({ h: 0, s: 0, l: 0, a: 1 }, Color.fromRGBA({ r: 0, g: 0, b: 0, a: 1 }).toHSLA()); + assert.deepEqual({ h: 0, s: 0, l: 1, a: 1 }, Color.fromRGBA({ r: 255, g: 255, b: 255, a: 1 }).toHSLA()); + + assert.deepEqual({ h: 0, s: 1, l: 0.5, a: 1 }, Color.fromRGBA({ r: 255, g: 0, b: 0, a: 1 }).toHSLA()); + assert.deepEqual({ h: 120, s: 1, l: 0.5, a: 1 }, Color.fromRGBA({ r: 0, g: 255, b: 0, a: 1 }).toHSLA()); + assert.deepEqual({ h: 240, s: 1, l: 0.5, a: 1 }, Color.fromRGBA({ r: 0, g: 0, b: 255, a: 1 }).toHSLA()); + + assert.deepEqual({ h: 60, s: 1, l: 0.5, a: 1 }, Color.fromRGBA({ r: 255, g: 255, b: 0, a: 1 }).toHSLA()); + assert.deepEqual({ h: 180, s: 1, l: 0.5, a: 1 }, Color.fromRGBA({ r: 0, g: 255, b: 255, a: 1 }).toHSLA()); + assert.deepEqual({ h: 300, s: 1, l: 0.5, a: 1 }, Color.fromRGBA({ r: 255, g: 0, b: 255, a: 1 }).toHSLA()); + + assert.deepEqual({ h: 0, s: 0, l: 0.753, a: 1 }, Color.fromRGBA({ r: 192, g: 192, b: 192, a: 1 }).toHSLA()); + + assert.deepEqual({ h: 0, s: 0, l: 0.502, a: 1 }, Color.fromRGBA({ r: 128, g: 128, b: 128, a: 1 }).toHSLA()); + assert.deepEqual({ h: 0, s: 1, l: 0.251, a: 1 }, Color.fromRGBA({ r: 128, g: 0, b: 0, a: 1 }).toHSLA()); + assert.deepEqual({ h: 60, s: 1, l: 0.251, a: 1 }, Color.fromRGBA({ r: 128, g: 128, b: 0, a: 1 }).toHSLA()); + assert.deepEqual({ h: 120, s: 1, l: 0.251, a: 1 }, Color.fromRGBA({ r: 0, g: 128, b: 0, a: 1 }).toHSLA()); + assert.deepEqual({ h: 300, s: 1, l: 0.251, a: 1 }, Color.fromRGBA({ r: 128, g: 0, b: 128, a: 1 }).toHSLA()); + assert.deepEqual({ h: 180, s: 1, l: 0.251, a: 1 }, Color.fromRGBA({ r: 0, g: 128, b: 128, a: 1 }).toHSLA()); + assert.deepEqual({ h: 240, s: 1, l: 0.251, a: 1 }, Color.fromRGBA({ r: 0, g: 0, b: 128, a: 1 }).toHSLA()); + }); + + test('hsla2rgba', function () { + assert.deepEqual({ r: 0, g: 0, b: 0, a: 1 }, Color.fromHSLA({ h: 0, s: 0, l: 0, a: 1 }).toRGBA()); + assert.deepEqual({ r: 255, g: 255, b: 255, a: 1 }, Color.fromHSLA({ h: 0, s: 0, l: 1, a: 1 }).toRGBA()); + + assert.deepEqual({ r: 255, g: 0, b: 0, a: 1 }, Color.fromHSLA({ h: 0, s: 1, l: 0.5, a: 1 }).toRGBA()); + assert.deepEqual({ r: 0, g: 255, b: 0, a: 1 }, Color.fromHSLA({ h: 120, s: 1, l: 0.5, a: 1 }).toRGBA()); + assert.deepEqual({ r: 0, g: 0, b: 255, a: 1 }, Color.fromHSLA({ h: 240, s: 1, l: 0.5, a: 1 }).toRGBA()); + + assert.deepEqual({ r: 255, g: 255, b: 0, a: 1 }, Color.fromHSLA({ h: 60, s: 1, l: 0.5, a: 1 }).toRGBA()); + assert.deepEqual({ r: 0, g: 255, b: 255, a: 1 }, Color.fromHSLA({ h: 180, s: 1, l: 0.5, a: 1 }).toRGBA()); + assert.deepEqual({ r: 255, g: 0, b: 255, a: 1 }, Color.fromHSLA({ h: 300, s: 1, l: 0.5, a: 1 }).toRGBA()); + + assert.deepEqual({ r: 192, g: 192, b: 192, a: 1 }, Color.fromHSLA({ h: 0, s: 0, l: 0.753, a: 1 }).toRGBA()); + + assert.deepEqual({ r: 128, g: 128, b: 128, a: 1 }, Color.fromHSLA({ h: 0, s: 0, l: 0.502, a: 1 }).toRGBA()); + assert.deepEqual({ r: 128, g: 0, b: 0, a: 1 }, Color.fromHSLA({ h: 0, s: 1, l: 0.251, a: 1 }).toRGBA()); + assert.deepEqual({ r: 128, g: 128, b: 0, a: 1 }, Color.fromHSLA({ h: 60, s: 1, l: 0.251, a: 1 }).toRGBA()); + assert.deepEqual({ r: 0, g: 128, b: 0, a: 1 }, Color.fromHSLA({ h: 120, s: 1, l: 0.251, a: 1 }).toRGBA()); + assert.deepEqual({ r: 128, g: 0, b: 128, a: 1 }, Color.fromHSLA({ h: 300, s: 1, l: 0.251, a: 1 }).toRGBA()); + assert.deepEqual({ r: 0, g: 128, b: 128, a: 1 }, Color.fromHSLA({ h: 180, s: 1, l: 0.251, a: 1 }).toRGBA()); + assert.deepEqual({ r: 0, g: 0, b: 128, a: 1 }, Color.fromHSLA({ h: 240, s: 1, l: 0.251, a: 1 }).toRGBA()); + }); + + test('hex2rgba', function () { + assert.deepEqual({ r: 0, g: 0, b: 0, a: 1 }, Color.fromHex('#000000').toRGBA()); + assert.deepEqual({ r: 255, g: 255, b: 255, a: 1 }, Color.fromHex('#FFFFFF').toRGBA()); + + assert.deepEqual({ r: 255, g: 0, b: 0, a: 1 }, Color.fromHex('#FF0000').toRGBA()); + assert.deepEqual({ r: 0, g: 255, b: 0, a: 1 }, Color.fromHex('#00FF00').toRGBA()); + assert.deepEqual({ r: 0, g: 0, b: 255, a: 1 }, Color.fromHex('#0000FF').toRGBA()); + + assert.deepEqual({ r: 255, g: 255, b: 0, a: 1 }, Color.fromHex('#FFFF00').toRGBA()); + assert.deepEqual({ r: 0, g: 255, b: 255, a: 1 }, Color.fromHex('#00FFFF').toRGBA()); + assert.deepEqual({ r: 255, g: 0, b: 255, a: 1 }, Color.fromHex('#FF00FF').toRGBA()); + + assert.deepEqual({ r: 192, g: 192, b: 192, a: 1 }, Color.fromHex('#C0C0C0').toRGBA()); + + assert.deepEqual({ r: 128, g: 128, b: 128, a: 1 }, Color.fromHex('#808080').toRGBA()); + assert.deepEqual({ r: 128, g: 0, b: 0, a: 1 }, Color.fromHex('#800000').toRGBA()); + assert.deepEqual({ r: 128, g: 128, b: 0, a: 1 }, Color.fromHex('#808000').toRGBA()); + assert.deepEqual({ r: 0, g: 128, b: 0, a: 1 }, Color.fromHex('#008000').toRGBA()); + assert.deepEqual({ r: 128, g: 0, b: 128, a: 1 }, Color.fromHex('#800080').toRGBA()); + assert.deepEqual({ r: 0, g: 128, b: 128, a: 1 }, Color.fromHex('#008080').toRGBA()); + assert.deepEqual({ r: 0, g: 0, b: 128, a: 1 }, Color.fromHex('#000080').toRGBA()); + }); + + test('isLighterColor', function () { + let color1 = Color.fromHSLA({ h: 60, s: 1, l: 0.5, a: 1 }), color2 = Color.fromHSLA({ h: 0, s: 0, l: 0.753, a: 1 }); + + assert.ok(color1.isLighterThan(color2)); + + // Abyss theme + assert.ok(Color.fromHex('#770811').isLighterThan(Color.fromHex('#000c18'))); + }); + + test('getLighterColor', function () { + let color1 = Color.fromHSLA({ h: 60, s: 1, l: 0.5, a: 1 }), color2 = Color.fromHSLA({ h: 0, s: 0, l: 0.753, a: 1 }); + + assert.deepEqual(color1.toHSLA(), Color.getLighterColor(color1, color2).toHSLA()); + assert.deepEqual({ h: 0, s: 0, l: 0.914, a: 1 }, Color.getLighterColor(color2, color1).toHSLA()); + assert.deepEqual({ h: 0, s: 0, l: 0.851, a: 1 }, Color.getLighterColor(color2, color1, 0.3).toHSLA()); + assert.deepEqual({ h: 0, s: 0, l: 0.98, a: 1 }, Color.getLighterColor(color2, color1, 0.7).toHSLA()); + assert.deepEqual({ h: 0, s: 0, l: 1, a: 1 }, Color.getLighterColor(color2, color1, 1).toHSLA()); + + }); + + test('isDarkerColor', function () { + let color1 = Color.fromHSLA({ h: 60, s: 1, l: 0.5, a: 1 }), color2 = Color.fromHSLA({ h: 0, s: 0, l: 0.753, a: 1 }); + + assert.ok(color2.isDarkerThan(color1)); + + }); + + test('getDarkerColor', function () { + let color1 = Color.fromHSLA({ h: 60, s: 1, l: 0.5, a: 1 }), color2 = Color.fromHSLA({ h: 0, s: 0, l: 0.753, a: 1 }); + + assert.deepEqual(color2.toHSLA(), Color.getDarkerColor(color2, color1).toHSLA()); + assert.deepEqual({ h: 60, s: 1, l: 0.392, a: 1 }, Color.getDarkerColor(color1, color2).toHSLA()); + assert.deepEqual({ h: 60, s: 1, l: 0.435, a: 1 }, Color.getDarkerColor(color1, color2, 0.3).toHSLA()); + assert.deepEqual({ h: 60, s: 1, l: 0.349, a: 1 }, Color.getDarkerColor(color1, color2, 0.7).toHSLA()); + assert.deepEqual({ h: 60, s: 1, l: 0.284, a: 1 }, Color.getDarkerColor(color1, color2, 1).toHSLA()); + + // Abyss theme + assert.deepEqual({ h: 355, s: 0.874, l: 0.157, a: 1 }, Color.getDarkerColor(Color.fromHex('#770811'), Color.fromHex('#000c18'), 0.4).toHSLA()); + }); + + test('luminosity', function () { + assert.deepEqual(0, Color.fromRGBA({ r: 0, g: 0, b: 0, a: 1 }).getLuminosity()); + assert.deepEqual(1, Color.fromRGBA({ r: 255, g: 255, b: 255, a: 1 }).getLuminosity()); + + assert.deepEqual(0.2126, Color.fromRGBA({ r: 255, g: 0, b: 0, a: 1 }).getLuminosity()); + assert.deepEqual(0.7152, Color.fromRGBA({ r: 0, g: 255, b: 0, a: 1 }).getLuminosity()); + assert.deepEqual(0.0722, Color.fromRGBA({ r: 0, g: 0, b: 255, a: 1 }).getLuminosity()); + + assert.deepEqual(0.9278, Color.fromRGBA({ r: 255, g: 255, b: 0, a: 1 }).getLuminosity()); + assert.deepEqual(0.7874, Color.fromRGBA({ r: 0, g: 255, b: 255, a: 1 }).getLuminosity()); + assert.deepEqual(0.2848, Color.fromRGBA({ r: 255, g: 0, b: 255, a: 1 }).getLuminosity()); + + assert.deepEqual(0.5271, Color.fromRGBA({ r: 192, g: 192, b: 192, a: 1 }).getLuminosity()); + + assert.deepEqual(0.2159, Color.fromRGBA({ r: 128, g: 128, b: 128, a: 1 }).getLuminosity()); + assert.deepEqual(0.0459, Color.fromRGBA({ r: 128, g: 0, b: 0, a: 1 }).getLuminosity()); + assert.deepEqual(0.2003, Color.fromRGBA({ r: 128, g: 128, b: 0, a: 1 }).getLuminosity()); + assert.deepEqual(0.1544, Color.fromRGBA({ r: 0, g: 128, b: 0, a: 1 }).getLuminosity()); + assert.deepEqual(0.0615, Color.fromRGBA({ r: 128, g: 0, b: 128, a: 1 }).getLuminosity()); + assert.deepEqual(0.17, Color.fromRGBA({ r: 0, g: 128, b: 128, a: 1 }).getLuminosity()); + assert.deepEqual(0.0156, Color.fromRGBA({ r: 0, g: 0, b: 128, a: 1 }).getLuminosity()); + }); + + test('contrast', function () { + assert.deepEqual(0, Color.fromRGBA({ r: 0, g: 0, b: 0, a: 1 }).getLuminosity()); + assert.deepEqual(1, Color.fromRGBA({ r: 255, g: 255, b: 255, a: 1 }).getLuminosity()); + + assert.deepEqual(0.2126, Color.fromRGBA({ r: 255, g: 0, b: 0, a: 1 }).getLuminosity()); + assert.deepEqual(0.7152, Color.fromRGBA({ r: 0, g: 255, b: 0, a: 1 }).getLuminosity()); + assert.deepEqual(0.0722, Color.fromRGBA({ r: 0, g: 0, b: 255, a: 1 }).getLuminosity()); + + assert.deepEqual(0.9278, Color.fromRGBA({ r: 255, g: 255, b: 0, a: 1 }).getLuminosity()); + assert.deepEqual(0.7874, Color.fromRGBA({ r: 0, g: 255, b: 255, a: 1 }).getLuminosity()); + assert.deepEqual(0.2848, Color.fromRGBA({ r: 255, g: 0, b: 255, a: 1 }).getLuminosity()); + + assert.deepEqual(0.5271, Color.fromRGBA({ r: 192, g: 192, b: 192, a: 1 }).getLuminosity()); + + assert.deepEqual(0.2159, Color.fromRGBA({ r: 128, g: 128, b: 128, a: 1 }).getLuminosity()); + assert.deepEqual(0.0459, Color.fromRGBA({ r: 128, g: 0, b: 0, a: 1 }).getLuminosity()); + assert.deepEqual(0.2003, Color.fromRGBA({ r: 128, g: 128, b: 0, a: 1 }).getLuminosity()); + assert.deepEqual(0.1544, Color.fromRGBA({ r: 0, g: 128, b: 0, a: 1 }).getLuminosity()); + assert.deepEqual(0.0615, Color.fromRGBA({ r: 128, g: 0, b: 128, a: 1 }).getLuminosity()); + assert.deepEqual(0.17, Color.fromRGBA({ r: 0, g: 128, b: 128, a: 1 }).getLuminosity()); + assert.deepEqual(0.0156, Color.fromRGBA({ r: 0, g: 0, b: 128, a: 1 }).getLuminosity()); + }); + +}); diff --git a/src/vs/workbench/services/themes/common/color.ts b/src/vs/workbench/services/themes/common/color.ts deleted file mode 100644 index ea5328b19237e..0000000000000 --- a/src/vs/workbench/services/themes/common/color.ts +++ /dev/null @@ -1,58 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export interface RGBA { r: number; g: number; b: number; a: number; } - -export class Color { - - private parsed: RGBA; - private str: string; - - constructor(arg: string | RGBA) { - if (typeof arg === 'string') { - this.parsed = Color.parse(arg); - } else { - this.parsed = arg; - } - this.str = null; - } - - private static parse(color: string): RGBA { - function parseHex(str: string) { - return parseInt('0x' + str); - } - - if (color.charAt(0) === '#' && color.length >= 7) { - let r = parseHex(color.substr(1, 2)); - let g = parseHex(color.substr(3, 2)); - let b = parseHex(color.substr(5, 2)); - let a = color.length === 9 ? parseHex(color.substr(7, 2)) / 0xff : 1; - return { r, g, b, a }; - } - return { r: 255, g: 0, b: 0, a: 1 }; - } - - public toString(): string { - if (!this.str) { - let p = this.parsed; - this.str = `rgba(${p.r}, ${p.g}, ${p.b}, ${+p.a.toFixed(2)})`; - } - return this.str; - } - - public transparent(factor: number): Color { - let p = this.parsed; - return new Color({ r: p.r, g: p.g, b: p.b, a: p.a * factor }); - } - - public opposite(): Color { - return new Color({ - r: 255 - this.parsed.r, - g: 255 - this.parsed.g, - b: 255 - this.parsed.b, - a : this.parsed.a - }); - } -} \ No newline at end of file diff --git a/src/vs/workbench/services/themes/electron-browser/editorStyles.ts b/src/vs/workbench/services/themes/electron-browser/editorStyles.ts index d4e7dc1432ab9..28b2ac9e90f78 100644 --- a/src/vs/workbench/services/themes/electron-browser/editorStyles.ts +++ b/src/vs/workbench/services/themes/electron-browser/editorStyles.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import {IThemeDocument, IThemeSetting, IThemeSettingStyle} from 'vs/workbench/services/themes/common/themeService'; -import {Color} from 'vs/workbench/services/themes/common/color'; +import {Color} from 'vs/base/common/color'; import {getBaseThemeId, getSyntaxThemeId, isLightTheme, isDarkTheme} from 'vs/platform/theme/common/themes'; export class TokenStylesContribution { @@ -189,9 +189,24 @@ class EditorSelectionStyleRules extends EditorStyleRule { this.addBackgroundColorRule(editorStyles, '.focused .selected-text', selection, cssRules); this.addBackgroundColorRule(editorStyles, '.selected-text', selection.transparent(0.5), cssRules); } - this.addBackgroundColorRule(editorStyles, '.selectionHighlight', editorStyles.getEditorStyleSettings().selectionHighlight, cssRules); + + this.addBackgroundColorRule(editorStyles, '.focused .selectionHighlight', this.getSelectionHighlightColor(editorStyles), cssRules); return cssRules; } + + private getSelectionHighlightColor(editorStyles: EditorStyles) { + if (editorStyles.getEditorStyleSettings().selectionHighlight) { + return new Color(editorStyles.getEditorStyleSettings().selectionHighlight); + } + + if (editorStyles.getEditorStyleSettings().selection && editorStyles.getEditorStyleSettings().background) { + let selection = new Color(editorStyles.getEditorStyleSettings().selection); + let background = new Color(editorStyles.getEditorStyleSettings().background); + return deriveLessProminentColor(selection, background); + } + + return null; + } } class EditorWordHighlightStyleRules extends EditorStyleRule { @@ -265,4 +280,15 @@ class EditorIndentGuidesStyleRules extends EditorStyleRule { } return null; } +} + +function deriveLessProminentColor(from: Color, backgroundColor: Color): Color { + let contrast = from.getContrast(backgroundColor); + if (contrast < 1.7 || contrast > 4.5) { + return null; + } + if (from.isDarkerThan(backgroundColor)) { + return Color.getLighterColor(from, backgroundColor, 0.4); + } + return Color.getDarkerColor(from, backgroundColor, 0.4); } \ No newline at end of file