| Sky Style Language | 
 | ================== | 
 |  | 
 | This is a trimmed down version of the API in (style.md)[style.md] | 
 | that is intended to be a stepping stone to the long-term world where | 
 | there are no hard-coded properties in the engine. | 
 |  | 
 | The Sky style API looks like the following: | 
 |  | 
 | ```dart | 
 |  | 
 |   // all properties can be set as strings: | 
 |   element.style['color'] = 'blue'; | 
 |  | 
 |   // some properties have dedicated APIs | 
 |   // color | 
 |   element.style.color.red += 1; // 0..255 | 
 |   element.style.color.blue += 10; // 0..255 | 
 |   element.style.color.green = 255; // 0..255 | 
 |   element.style.color.alpha = 128; // 0..255 | 
 |   // transform | 
 |   element.style.transform..reset() | 
 |                          ..translate(100, 100) | 
 |                          ..rotate(PI/8) | 
 |                          ..translate(-100, -100); | 
 |   element.style.transform.translate(10, 0); | 
 |   // height, width | 
 |   element.style.height.auto = true; | 
 |   if (element.style.height.auto) | 
 |     element.style.height.pixels = 10; | 
 |   element.style.height.pixels += 1; | 
 |   element.style.height.em = 1; | 
 |  | 
 |   // each property with a dedicated API defines a shorthand setter | 
 |   // style.transform takes a matrix: | 
 |   element.style.transform = new Matrix(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); | 
 |   // style.color takes a 32bit int: | 
 |   element.style.color = 0xFF009900; | 
 |   // style.height and style.width takes pixels or the constant 'auto': | 
 |   element.style.height = auto; | 
 |   element.style.width = 100; | 
 |   // all properties with a dedicated API can also be set to null, inherit, or initial: | 
 |   element.style.transform = null; // unset the property | 
 |   element.style.color = initial; // set it to its initial value | 
 |   element.style.color = inherit; // make it get its parent's value | 
 |  | 
 |   // you can create a blank StyleDeclaration object: | 
 |   var style = new StyleDeclaration(); | 
 |   // you can replace an element's StyleDeclaration object wholesale: | 
 |   element.style = style; | 
 |   // you can clone a StyleDeclaration object: | 
 |   var style2 = new StyleDeclaration.clone(style); | 
 | ``` | 
 |  | 
 | The dart:sky library contains the following to define this API: | 
 |  | 
 | ```dart | 
 | import 'dart:mirrors'; | 
 | import 'dart:math'; | 
 |  | 
 | typedef void StringSetter(Symbol propertySymbol, StyleDeclaration declaration, String value); | 
 | typedef String StringGetter(Symbol propertySymbol, StyleDeclaration declaration); | 
 | typedef Property ObjectConstructor(Symbol propertySymbol, StyleDeclaration declaration); | 
 |  | 
 | class PropertyTable { | 
 |   const PropertyTable({this.symbol, this.inherited, this.stringGetter, this.stringSetter, this.objectConstructor}); | 
 |   final Symbol symbol; | 
 |   final bool inherited; | 
 |   final StringSetter stringSetter; | 
 |   final StringGetter stringGetter; | 
 |   final ObjectConstructor objectConstructor; | 
 | } | 
 |  | 
 | Map<Symbol, PropertyTable> _registeredProperties = new Map<Symbol, PropertyTable>(); | 
 | void registerProperty(PropertyTable data) { | 
 |   assert(data.symbol is Symbol); | 
 |   assert(data.inherited is bool); | 
 |   assert(data.stringSetter is StringSetter); | 
 |   assert(data.stringGetter is StringGetter); | 
 |   assert(data.objectConstructor == null || data.objectConstructor is ObjectConstructor); | 
 |   assert(!_registeredProperties.containsKey(data.symbol)); | 
 |   _registeredProperties[data.symbol] = data; | 
 | } | 
 |  | 
 | @proxy | 
 | class StyleDeclaration { | 
 |   StyleDeclaration() { this._init(); } | 
 |   StyleDeclaration.clone(StyleDeclaration template) { this.init(template); } | 
 |   external void _init([StyleDeclaration template]); // O(1) | 
 |   // This class has C++-backed internal state representing the | 
 |   // properties known to the system. It's assumed that Property | 
 |   // subclasses are also C++-backed and can directly manipulate this | 
 |   // internal state. | 
 |   // If the argument 'template' is provided, then this should be a clone | 
 |   // of the styles of the template StyleDeclaration | 
 |  | 
 |   operator [](String propertyName) { | 
 |     var propertySymbol = new Symbol(propertyName); | 
 |     if (_registeredProperties.containsKey(propertySymbol)) | 
 |       return _registeredProperties[propertySymbol].stringGetter(propertySymbol, this); | 
 |     throw new ArgumentError(propertyName); | 
 |   } | 
 |  | 
 |   operator []=(String propertyName, String newValue) { | 
 |     var propertySymbol = new Symbol(propertyName); | 
 |     if (_registeredProperties.containsKey(propertySymbol)) | 
 |       return _registeredProperties[propertySymbol].stringSetter(propertySymbol, this, newValue); | 
 |     throw new ArgumentError(propertyName); | 
 |   } | 
 |  | 
 |   // some properties expose dedicated APIs so you don't have to use string manipulation | 
 |   MapOfWeakReferences<Symbol, Property> _properties = new MapOfWeakReferences<Symbol, Property>(); | 
 |   noSuchMethod(Invocation invocation) { | 
 |     Symbol propertySymbol; | 
 |     if (invocation.isSetter) { | 
 |       // when it's a setter, the name will be "foo=" rather than "foo" | 
 |       String propertyName = MirrorSystem.getName(invocation.memberName); | 
 |       assert(propertyName[propertyName.length-1] == '='); | 
 |       propertySymbol = new Symbol(propertyName.substring(0, propertyName.length-1)); | 
 |     } else { | 
 |       propertySymbol = invocation.memberName; | 
 |     } | 
 |     Property property; | 
 |     if (!_properties.containsKey(propertySymbol)) { | 
 |       if (_registeredProperties.containsKey(propertySymbol)) { | 
 |         var constructor = _registeredProperties[propertySymbol].objectConstructor; | 
 |         if (constructor == null) | 
 |           return super.noSuchMethod(invocation); | 
 |         property = constructor(propertySymbol, this); | 
 |       } else { | 
 |         return super.noSuchMethod(invocation); | 
 |       } | 
 |     } else { | 
 |       property = _properties[propertySymbol]; | 
 |     } | 
 |     if (invocation.isMethod) { | 
 |       if (property is Function) | 
 |         return Function.apply(property as Function, invocation.positionalArguments, invocation.namedArguments); | 
 |       return super.noSuchMethod(invocation); | 
 |     } | 
 |     if (invocation.isSetter) | 
 |       return Function.apply(property.setter, invocation.positionalArguments, invocation.namedArguments); | 
 |     return property; | 
 |   } | 
 | } | 
 |  | 
 | const initial = const Object(); | 
 | const inherit = const Object(); | 
 |  | 
 | abstract class Property { | 
 |   Property(this.propertySymbol, this.declaration); | 
 |   final StyleDeclaration declaration; | 
 |   final Symbol propertySymbol; | 
 |  | 
 |   bool get inherited => _registeredProperties[propertySymbol].inherited; | 
 |  | 
 |   bool get initial => _isInitial(); | 
 |   void set initial (value) { | 
 |     if (value == true) | 
 |       return _setInitial(); | 
 |     throw new ArgumentError(value); | 
 |   } | 
 |  | 
 |   bool get inherit => _isInherit(); | 
 |   void set inherit (value) { | 
 |     if (value == true) | 
 |       return _setInherit(); | 
 |     throw new ArgumentError(value); | 
 |   } | 
 |  | 
 |   void setter(dynamic newValue) { | 
 |     switch (newValue) { | 
 |       case initial: | 
 |         _setInitial(); | 
 |       case inherit: | 
 |         _setInherit(); | 
 |       case null: | 
 |         _unset(); | 
 |       default: | 
 |         throw new ArgumentError(value); | 
 |     } | 
 |   } | 
 |  | 
 |   external bool _isInitial(); | 
 |   external void _setInitial(); | 
 |   external bool _isInherit(); | 
 |   external void _setInherit(); | 
 |   external void _unset(); | 
 | } | 
 | ``` | 
 |  | 
 | Sky defines the following properties, currently as part of the core, | 
 | but eventually this will be moved to the framework: | 
 |  | 
 | ```dart | 
 | class LengthProperty extends Property { | 
 |   LengthProperty(Symbol propertySymbol, StyleDeclaration declaration) : super(propertySymbol, declaration); | 
 |  | 
 |   double get pixels => _getPixels(); | 
 |   void set pixels (value) => _setPixels(value); | 
 |  | 
 |   double get inches => _getPixels() / 96.0; | 
 |   void set inches (value) => _setPixels(value * 96.0); | 
 |  | 
 |   double get em => _getEm(); | 
 |   void set em (value) => _setEm(value); | 
 |  | 
 |   void setter(dynamic value) { | 
 |     if (value is num) | 
 |       return _setPixels(value.toDouble()); | 
 |     return super.setter(value); | 
 |   } | 
 |  | 
 |   external double _getPixels(); | 
 |   // throws StateError if the value isn't in pixels | 
 |   external void _setPixels(double value); | 
 |  | 
 |   external double _getEm(); | 
 |   // throws StateError if the value isn't in pixels | 
 |   external void _setEm(double value); | 
 | } | 
 |  | 
 | const auto = const Object(); | 
 |  | 
 | class AutoLengthProperty extends LengthProperty { | 
 |   AutoLengthProperty(Symbol propertySymbol, StyleDeclaration declaration) : super(propertySymbol, declaration); | 
 |  | 
 |   bool get auto => _isAuto(); | 
 |   void set auto (value) { | 
 |     if (value == true) | 
 |       _setAuto(); | 
 |     throw new ArgumentError(value); | 
 |   } | 
 |  | 
 |   void setter(dynamic value) { | 
 |     if (value == auto) | 
 |       return _setAuto(); | 
 |     return super.setter(value); | 
 |   } | 
 |  | 
 |   external bool _isAuto(); | 
 |   external void _setAuto(); | 
 | } | 
 |  | 
 | class ColorProperty extends Property { | 
 |   ColorProperty(Symbol propertySymbol, StyleDeclaration declaration) : super(propertySymbol, declaration); | 
 |  | 
 |   int get alpha => _getRGBA() & 0xFF000000 >> 24; | 
 |   void set alpha (int value) => _setRGBA(_getRGBA() & 0x00FFFFFF + value << 24); | 
 |   int get red => _getRGBA() & 0x00FF0000 >> 16; | 
 |   void set red (int value) => _setRGBA(_getRGBA() & 0xFF00FFFF + value << 16); | 
 |   int get green => _getRGBA() & 0x0000FF00 >> 8; | 
 |   void set green (int value) => _setRGBA(_getRGBA() & 0xFFFF00FF + value << 8); | 
 |   int get blue => _getRGBA() & 0x000000FF >> 0; | 
 |   void set blue (int value) => _setRGBA(_getRGBA() & 0xFFFFFF00 + value << 0); | 
 |  | 
 |   int get rgba => _getRGBA(); | 
 |   void set rgba (int value) => _setRGBA(value); | 
 |  | 
 |   void setter(dynamic value) { | 
 |     if (value is int) | 
 |       return _setRGBA(value); | 
 |     return super.setter(value); | 
 |   } | 
 |  | 
 |   external int _getRGBA(); | 
 |   // throws StateError if the value isn't a color | 
 |   external void _setRGBA(int value); | 
 | } | 
 |  | 
 | class Matrix { | 
 |   const Matrix(this.a, this.b, this.c, this.d, this.e, this.f); | 
 |  | 
 |   // +-     -+ | 
 |   // | a c e | | 
 |   // | b d f | | 
 |   // | 0 0 1 | | 
 |   // +-     -+ | 
 |    | 
 |   final double a; | 
 |   final double b; | 
 |   final double c; | 
 |   final double d; | 
 |   final double e; | 
 |   final double f; | 
 | } | 
 |  | 
 | class TransformProperty extends Property { | 
 |   TransformProperty(Symbol propertySymbol, StyleDeclaration declaration) : super(propertySymbol, declaration); | 
 |  | 
 |   void reset() => setTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); | 
 |  | 
 |   void translate(double dx, double dy) => transform(1.0, 0.0, 0.0, 1.0, dx, dy); | 
 |   void scale(double dw, double dh) => transform(dw, 0.0, 0.0, dh, 0.0, 0.0); | 
 |   void rotate(double theta) => transform(cos(theta), -sin(theta), sin(theta), cos(theta), 0.0, 0.0); | 
 |  | 
 |   // there's no "transform" getter since it would always return a new Matrix | 
 |   // such that foo.transform == foo.transform would never be true | 
 |   // and foo.transform = bar; bar == foo.transform would also never be true | 
 |   // which is bad API | 
 |  | 
 |   external Matrix getTransform(); | 
 |   // throws StateError if the value isn't a matrix | 
 |   // returns a new matrix each time | 
 |   external void setTransform(a, b, c, d, e, f); | 
 |   external void transform(a, b, c, d, e, f); | 
 |   // throws StateError if the value isn't a matrix | 
 | } | 
 |  | 
 | external void autoLengthPropertyStringSetter(Symbol propertySymbol, StyleDeclaration declaration, String value); | 
 | external String autoLengthPropertyStringGetter(Symbol propertySymbol, StyleDeclaration declaration); | 
 | external void colorPropertyStringSetter(Symbol propertySymbol, StyleDeclaration declaration, String value); | 
 | external String colorPropertyStringGetter(Symbol propertySymbol, StyleDeclaration declaration); | 
 | external void transformPropertyStringSetter(Symbol propertySymbol, StyleDeclaration declaration, String value); | 
 | external String transformPropertyStringGetter(Symbol propertySymbol, StyleDeclaration declaration); | 
 |  | 
 | void _init() { | 
 |   registerProperty(new PropertyTable(    | 
 |     symbol: #height, | 
 |     inherited: false, | 
 |     stringSetter: autoLengthPropertyStringSetter, | 
 |     stringGetter: autoLengthPropertyStringGetter, | 
 |     objectConstructor: (Symbol propertySymbol, StyleDeclaration declaration) => | 
 |                          new AutoLengthProperty(propertySymbol, declaration))); | 
 |   registerProperty(new PropertyTable(    | 
 |     symbol: #width, | 
 |     inherited: false, | 
 |     stringSetter: autoLengthPropertyStringSetter, | 
 |     stringGetter: autoLengthPropertyStringGetter, | 
 |     objectConstructor: (Symbol propertySymbol, StyleDeclaration declaration) => | 
 |                          new AutoLengthProperty(propertySymbol, declaration))); | 
 |   registerProperty(new PropertyTable(    | 
 |     symbol: #color, | 
 |     inherited: false, | 
 |     stringSetter: colorPropertyStringSetter, | 
 |     stringGetter: colorPropertyStringGetter, | 
 |     objectConstructor: (Symbol propertySymbol, StyleDeclaration declaration) => | 
 |                          new ColorProperty(propertySymbol, declaration))); | 
 |   registerProperty(new PropertyTable(    | 
 |     symbol: #transform, | 
 |     inherited: false, | 
 |     stringSetter: transformPropertyStringSetter, | 
 |     stringGetter: transformPropertyStringGetter, | 
 |     objectConstructor: (Symbol propertySymbol, StyleDeclaration declaration) => | 
 |                          new TransformProperty(propertySymbol, declaration))); | 
 | } | 
 | ``` | 
 |  |