Merge pull request #4930 from atom/ks-add-colors-to-config-schema

Add color support to config schema
This commit is contained in:
Kevin Sawicki
2015-01-12 17:54:14 -08:00
5 changed files with 198 additions and 5 deletions

View File

@@ -24,7 +24,7 @@
"grunt-peg": "~1.1.0",
"grunt-shell": "~0.3.1",
"harmony-collections": "~0.3.8",
"legal-eagle": "~0.6.0",
"legal-eagle": "~0.8.0",
"minidump": "~0.8",
"npm": "~1.4.5",
"rcedit": "~0.3.0",

View File

@@ -25,6 +25,7 @@
"clear-cut": "0.4.0",
"coffee-script": "1.8.0",
"coffeestack": "0.8.0",
"color": "^0.7.3",
"delegato": "^1",
"emissary": "^1.3.1",
"event-kit": "^1.0.1",

View File

@@ -1060,6 +1060,9 @@ describe "Config", ->
type: 'integer'
default: 12
expect(atom.config.getSchema('foo.baz')).toBeUndefined()
expect(atom.config.getSchema('foo.bar.anInt.baz')).toBeUndefined()
it "respects the schema for scoped settings", ->
schema =
type: 'string'
@@ -1287,6 +1290,79 @@ describe "Config", ->
atom.config.set 'foo.bar', ['2', '3', '4']
expect(atom.config.get('foo.bar')).toEqual [2, 3, 4]
describe 'when the value has a "color" type', ->
beforeEach ->
schema =
type: 'color'
default: 'white'
atom.config.setSchema('foo.bar.aColor', schema)
it 'returns a Color object', ->
color = atom.config.get('foo.bar.aColor')
expect(color.toHexString()).toBe '#ffffff'
expect(color.toRGBAString()).toBe 'rgba(255, 255, 255, 1)'
color.red = 0
color.green = 0
color.blue = 0
color.alpha = 0
atom.config.set('foo.bar.aColor', color)
color = atom.config.get('foo.bar.aColor')
expect(color.toHexString()).toBe '#000000'
expect(color.toRGBAString()).toBe 'rgba(0, 0, 0, 0)'
color.red = 300
color.green = -200
color.blue = -1
color.alpha = 'not see through'
atom.config.set('foo.bar.aColor', color)
color = atom.config.get('foo.bar.aColor')
expect(color.toHexString()).toBe '#ff0000'
expect(color.toRGBAString()).toBe 'rgba(255, 0, 0, 1)'
it 'coerces various types to a color object', ->
atom.config.set('foo.bar.aColor', 'red')
expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 0, blue: 0, alpha: 1}
atom.config.set('foo.bar.aColor', '#020')
expect(atom.config.get('foo.bar.aColor')).toEqual {red: 0, green: 34, blue: 0, alpha: 1}
atom.config.set('foo.bar.aColor', '#abcdef')
expect(atom.config.get('foo.bar.aColor')).toEqual {red: 171, green: 205, blue: 239, alpha: 1}
atom.config.set('foo.bar.aColor', 'rgb(1,2,3)')
expect(atom.config.get('foo.bar.aColor')).toEqual {red: 1, green: 2, blue: 3, alpha: 1}
atom.config.set('foo.bar.aColor', 'rgba(4,5,6,.7)')
expect(atom.config.get('foo.bar.aColor')).toEqual {red: 4, green: 5, blue: 6, alpha: .7}
atom.config.set('foo.bar.aColor', 'hsl(120,100%,50%)')
expect(atom.config.get('foo.bar.aColor')).toEqual {red: 0, green: 255, blue: 0, alpha: 1}
atom.config.set('foo.bar.aColor', 'hsla(120,100%,50%,0.3)')
expect(atom.config.get('foo.bar.aColor')).toEqual {red: 0, green: 255, blue: 0, alpha: .3}
atom.config.set('foo.bar.aColor', {red: 100, green: 255, blue: 2, alpha: .5})
expect(atom.config.get('foo.bar.aColor')).toEqual {red: 100, green: 255, blue: 2, alpha: .5}
atom.config.set('foo.bar.aColor', {red: 255})
expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 0, blue: 0, alpha: 1}
atom.config.set('foo.bar.aColor', {red: 1000})
expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 0, blue: 0, alpha: 1}
atom.config.set('foo.bar.aColor', {red: 'dark'})
expect(atom.config.get('foo.bar.aColor')).toEqual {red: 0, green: 0, blue: 0, alpha: 1}
it 'reverts back to the default value when undefined is passed to set', ->
atom.config.set('foo.bar.aColor', undefined)
expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 255, blue: 255, alpha: 1}
it 'will not set non-colors', ->
atom.config.set('foo.bar.aColor', null)
expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 255, blue: 255, alpha: 1}
atom.config.set('foo.bar.aColor', 'nope')
expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 255, blue: 255, alpha: 1}
atom.config.set('foo.bar.aColor', 30)
expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 255, blue: 255, alpha: 1}
atom.config.set('foo.bar.aColor', false)
expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 255, blue: 255, alpha: 1}
describe 'when the `enum` key is used', ->
beforeEach ->
schema =

87
src/color.coffee Normal file
View File

@@ -0,0 +1,87 @@
_ = require 'underscore-plus'
ParsedColor = require 'color'
# Essential: A simple color class returned from {Config::get} when the value
# at the key path is of type 'color'.
module.exports =
class Color
# Essential: Parse a {String} or {Object} into a {Color}.
#
# * `value` - A {String} such as `'white'`, `#ff00ff`, or
# `'rgba(255, 15, 60, .75)'` or an {Object} with `red`, `green`,
# `blue`, and `alpha` properties.
#
# Returns a {Color} or `null` if it cannot be parsed.
@parse: (value) ->
return null if _.isArray(value) or _.isFunction(value)
return null unless _.isObject(value) or _.isString(value)
try
parsedColor = new ParsedColor(value)
catch error
return null
new Color(parsedColor.red(), parsedColor.green(), parsedColor.blue(), parsedColor.alpha())
constructor: (red, green, blue, alpha) ->
Object.defineProperties this,
red:
set: (newRed) -> red = parseColor(newRed)
get: -> red
enumerable: true
configurable: false
green:
set: (newGreen) -> green = parseColor(newGreen)
get: -> green
enumerable: true
configurable: false
blue:
set: (newBlue) -> blue = parseColor(newBlue)
get: -> blue
enumerable: true
configurable: false
alpha:
set: (newAlpha) -> alpha = parseAlpha(newAlpha)
get: -> alpha
enumerable: true
configurable: false
@red = red
@green = green
@blue = blue
@alpha = alpha
# Esssential: Returns a {String} in the form `'#abcdef'`.
toHexString: ->
"##{numberToHexString(@red)}#{numberToHexString(@green)}#{numberToHexString(@blue)}"
# Esssential: Returns a {String} in the form `'rgba(25, 50, 75, .9)'`.
toRGBAString: ->
"rgba(#{@red}, #{@green}, #{@blue}, #{@alpha})"
isEqual: (color) ->
return true if this is color
color = Color.parse(color) unless color instanceof Color
return false unless color?
color.red is @red and color.blue is @blue and color.green is @green and color.alpha is @alpha
clone: -> new Color(@red, @green, @blue, @alpha)
parseColor = (color) ->
color = parseInt(color)
color = 0 if isNaN(color)
color = Math.max(color, 0)
color = Math.min(color, 255)
color
parseAlpha = (alpha) ->
alpha = parseFloat(alpha)
alpha = 1 if isNaN(alpha)
alpha = Math.max(alpha, 0)
alpha = Math.min(alpha, 1)
alpha
numberToHexString = (number) ->
hex = number.toString(16)
hex = "0#{hex}" if number < 10
hex

View File

@@ -8,6 +8,7 @@ async = require 'async'
pathWatcher = require 'pathwatcher'
Grim = require 'grim'
Color = require './color'
ScopedPropertyStore = require 'scoped-property-store'
ScopeDescriptor = require './scope-descriptor'
@@ -216,6 +217,21 @@ ScopeDescriptor = require './scope-descriptor'
# maximum: 11.5
# ```
#
# #### color
#
# Values will be coerced into a {Color} with `red`, `green`, `blue`, and `alpha`
# properties that all have numeric values. `red`, `green`, `blue` will be in
# the range 0 to 255 and `value` will be in the range 0 to 1. Values can be any
# valid CSS color format such as `#abc`, `#abcdef`, `white`,
# `rgb(50, 100, 150)`, and `rgba(25, 75, 125, .75)`.
#
# ```coffee
# config:
# someSetting:
# type: 'color'
# default: 'white'
# ```
#
# ### Other Supported Keys
#
# #### enum
@@ -687,7 +703,7 @@ class Config
schema = @schema
for key in keys
break unless schema?
schema = schema.properties[key]
schema = schema.properties?[key]
schema
# Deprecated: Returns a new {Object} containing all of the global settings and
@@ -872,10 +888,16 @@ class Config
defaultValue = _.valueForKeyPath(@defaultSettings, keyPath)
if value?
value = _.deepClone(value)
_.defaults(value, defaultValue) if isPlainObject(value) and isPlainObject(defaultValue)
if value instanceof Color
value = value.clone()
else
value = _.deepClone(value)
_.defaults(value, defaultValue) if isPlainObject(value) and isPlainObject(defaultValue)
else
value = _.deepClone(defaultValue)
if defaultValue instanceof Color
value = defaultValue.clone()
else
value = _.deepClone(defaultValue)
value
@@ -1103,6 +1125,13 @@ Config.addSchemaEnforcers
else
value
'color':
coerce: (keyPath, value, schema) ->
color = Color.parse(value)
unless color?
throw new Error("Validation failed at #{keyPath}, #{JSON.stringify(value)} cannot be coerced into a color")
color
'*':
coerceMinimumAndMaximum: (keyPath, value, schema) ->
return value unless typeof value is 'number'