| OLD | NEW |
| (Empty) |
| 1 Sky Style Language | |
| 2 ================== | |
| 3 | |
| 4 This is a trimmed down version of the API in (style.md)[style.md] | |
| 5 that is intended to be a stepping stone to the long-term world where | |
| 6 there are no hard-coded properties in the engine. | |
| 7 | |
| 8 The Sky style API looks like the following: | |
| 9 | |
| 10 ```dart | |
| 11 | |
| 12 // all properties can be set as strings: | |
| 13 element.style['color'] = 'blue'; | |
| 14 | |
| 15 // some properties have dedicated APIs | |
| 16 // color | |
| 17 element.style.color.red += 1; // 0..255 | |
| 18 element.style.color.blue += 10; // 0..255 | |
| 19 element.style.color.green = 255; // 0..255 | |
| 20 element.style.color.alpha = 128; // 0..255 | |
| 21 // transform | |
| 22 element.style.transform..reset() | |
| 23 ..translate(100, 100) | |
| 24 ..rotate(PI/8) | |
| 25 ..translate(-100, -100); | |
| 26 element.style.transform.translate(10, 0); | |
| 27 // height, width | |
| 28 element.style.height.auto = true; | |
| 29 if (element.style.height.auto) | |
| 30 element.style.height.pixels = 10; | |
| 31 element.style.height.pixels += 1; | |
| 32 element.style.height.em = 1; | |
| 33 | |
| 34 // each property with a dedicated API defines a shorthand setter | |
| 35 // style.transform takes a matrix: | |
| 36 element.style.transform = new Matrix(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); | |
| 37 // style.color takes a 32bit int: | |
| 38 element.style.color = 0xFF009900; | |
| 39 // style.height and style.width takes pixels or the constant 'auto': | |
| 40 element.style.height = auto; | |
| 41 element.style.width = 100; | |
| 42 // all properties with a dedicated API can also be set to null, inherit, or in
itial: | |
| 43 element.style.transform = null; // unset the property | |
| 44 element.style.color = initial; // set it to its initial value | |
| 45 element.style.color = inherit; // make it get its parent's value | |
| 46 | |
| 47 // you can create a blank StyleDeclaration object: | |
| 48 var style = new StyleDeclaration(); | |
| 49 // you can replace an element's StyleDeclaration object wholesale: | |
| 50 element.style = style; | |
| 51 // you can clone a StyleDeclaration object: | |
| 52 var style2 = new StyleDeclaration.clone(style); | |
| 53 ``` | |
| 54 | |
| 55 The dart:sky library contains the following to define this API: | |
| 56 | |
| 57 ```dart | |
| 58 import 'dart:mirrors'; | |
| 59 import 'dart:math'; | |
| 60 | |
| 61 typedef void StringSetter(Symbol propertySymbol, StyleDeclaration declaration, S
tring value); | |
| 62 typedef String StringGetter(Symbol propertySymbol, StyleDeclaration declaration)
; | |
| 63 typedef Property ObjectConstructor(Symbol propertySymbol, StyleDeclaration decla
ration); | |
| 64 | |
| 65 class PropertyTable { | |
| 66 const PropertyTable({this.symbol, this.inherited, this.stringGetter, this.stri
ngSetter, this.objectConstructor}); | |
| 67 final Symbol symbol; | |
| 68 final bool inherited; | |
| 69 final StringSetter stringSetter; | |
| 70 final StringGetter stringGetter; | |
| 71 final ObjectConstructor objectConstructor; | |
| 72 } | |
| 73 | |
| 74 Map<Symbol, PropertyTable> _registeredProperties = new Map<Symbol, PropertyTable
>(); | |
| 75 void registerProperty(PropertyTable data) { | |
| 76 assert(data.symbol is Symbol); | |
| 77 assert(data.inherited is bool); | |
| 78 assert(data.stringSetter is StringSetter); | |
| 79 assert(data.stringGetter is StringGetter); | |
| 80 assert(data.objectConstructor == null || data.objectConstructor is ObjectConst
ructor); | |
| 81 assert(!_registeredProperties.containsKey(data.symbol)); | |
| 82 _registeredProperties[data.symbol] = data; | |
| 83 } | |
| 84 | |
| 85 @proxy | |
| 86 class StyleDeclaration { | |
| 87 StyleDeclaration() { this._init(); } | |
| 88 StyleDeclaration.clone(StyleDeclaration template) { this.init(template); } | |
| 89 external void _init([StyleDeclaration template]); // O(1) | |
| 90 // This class has C++-backed internal state representing the | |
| 91 // properties known to the system. It's assumed that Property | |
| 92 // subclasses are also C++-backed and can directly manipulate this | |
| 93 // internal state. | |
| 94 // If the argument 'template' is provided, then this should be a clone | |
| 95 // of the styles of the template StyleDeclaration | |
| 96 | |
| 97 operator [](String propertyName) { | |
| 98 var propertySymbol = new Symbol(propertyName); | |
| 99 if (_registeredProperties.containsKey(propertySymbol)) | |
| 100 return _registeredProperties[propertySymbol].stringGetter(propertySymbol,
this); | |
| 101 throw new ArgumentError(propertyName); | |
| 102 } | |
| 103 | |
| 104 operator []=(String propertyName, String newValue) { | |
| 105 var propertySymbol = new Symbol(propertyName); | |
| 106 if (_registeredProperties.containsKey(propertySymbol)) | |
| 107 return _registeredProperties[propertySymbol].stringSetter(propertySymbol,
this, newValue); | |
| 108 throw new ArgumentError(propertyName); | |
| 109 } | |
| 110 | |
| 111 // some properties expose dedicated APIs so you don't have to use string manip
ulation | |
| 112 MapOfWeakReferences<Symbol, Property> _properties = new MapOfWeakReferences<Sy
mbol, Property>(); | |
| 113 noSuchMethod(Invocation invocation) { | |
| 114 Symbol propertySymbol; | |
| 115 if (invocation.isSetter) { | |
| 116 // when it's a setter, the name will be "foo=" rather than "foo" | |
| 117 String propertyName = MirrorSystem.getName(invocation.memberName); | |
| 118 assert(propertyName[propertyName.length-1] == '='); | |
| 119 propertySymbol = new Symbol(propertyName.substring(0, propertyName.length-
1)); | |
| 120 } else { | |
| 121 propertySymbol = invocation.memberName; | |
| 122 } | |
| 123 Property property; | |
| 124 if (!_properties.containsKey(propertySymbol)) { | |
| 125 if (_registeredProperties.containsKey(propertySymbol)) { | |
| 126 var constructor = _registeredProperties[propertySymbol].objectConstructo
r; | |
| 127 if (constructor == null) | |
| 128 return super.noSuchMethod(invocation); | |
| 129 property = constructor(propertySymbol, this); | |
| 130 } else { | |
| 131 return super.noSuchMethod(invocation); | |
| 132 } | |
| 133 } else { | |
| 134 property = _properties[propertySymbol]; | |
| 135 } | |
| 136 if (invocation.isMethod) { | |
| 137 if (property is Function) | |
| 138 return Function.apply(property as Function, invocation.positionalArgumen
ts, invocation.namedArguments); | |
| 139 return super.noSuchMethod(invocation); | |
| 140 } | |
| 141 if (invocation.isSetter) | |
| 142 return Function.apply(property.setter, invocation.positionalArguments, inv
ocation.namedArguments); | |
| 143 return property; | |
| 144 } | |
| 145 } | |
| 146 | |
| 147 const initial = const Object(); | |
| 148 const inherit = const Object(); | |
| 149 | |
| 150 abstract class Property { | |
| 151 Property(this.propertySymbol, this.declaration); | |
| 152 final StyleDeclaration declaration; | |
| 153 final Symbol propertySymbol; | |
| 154 | |
| 155 bool get inherited => _registeredProperties[propertySymbol].inherited; | |
| 156 | |
| 157 bool get initial => _isInitial(); | |
| 158 void set initial (value) { | |
| 159 if (value == true) | |
| 160 return _setInitial(); | |
| 161 throw new ArgumentError(value); | |
| 162 } | |
| 163 | |
| 164 bool get inherit => _isInherit(); | |
| 165 void set inherit (value) { | |
| 166 if (value == true) | |
| 167 return _setInherit(); | |
| 168 throw new ArgumentError(value); | |
| 169 } | |
| 170 | |
| 171 void setter(dynamic newValue) { | |
| 172 switch (newValue) { | |
| 173 case initial: | |
| 174 _setInitial(); | |
| 175 case inherit: | |
| 176 _setInherit(); | |
| 177 case null: | |
| 178 _unset(); | |
| 179 default: | |
| 180 throw new ArgumentError(value); | |
| 181 } | |
| 182 } | |
| 183 | |
| 184 external bool _isInitial(); | |
| 185 external void _setInitial(); | |
| 186 external bool _isInherit(); | |
| 187 external void _setInherit(); | |
| 188 external void _unset(); | |
| 189 } | |
| 190 ``` | |
| 191 | |
| 192 Sky defines the following properties, currently as part of the core, | |
| 193 but eventually this will be moved to the framework: | |
| 194 | |
| 195 ```dart | |
| 196 class LengthProperty extends Property { | |
| 197 LengthProperty(Symbol propertySymbol, StyleDeclaration declaration) : super(pr
opertySymbol, declaration); | |
| 198 | |
| 199 double get pixels => _getPixels(); | |
| 200 void set pixels (value) => _setPixels(value); | |
| 201 | |
| 202 double get inches => _getPixels() / 96.0; | |
| 203 void set inches (value) => _setPixels(value * 96.0); | |
| 204 | |
| 205 double get em => _getEm(); | |
| 206 void set em (value) => _setEm(value); | |
| 207 | |
| 208 void setter(dynamic value) { | |
| 209 if (value is num) | |
| 210 return _setPixels(value.toDouble()); | |
| 211 return super.setter(value); | |
| 212 } | |
| 213 | |
| 214 external double _getPixels(); | |
| 215 // throws StateError if the value isn't in pixels | |
| 216 external void _setPixels(double value); | |
| 217 | |
| 218 external double _getEm(); | |
| 219 // throws StateError if the value isn't in pixels | |
| 220 external void _setEm(double value); | |
| 221 } | |
| 222 | |
| 223 const auto = const Object(); | |
| 224 | |
| 225 class AutoLengthProperty extends LengthProperty { | |
| 226 AutoLengthProperty(Symbol propertySymbol, StyleDeclaration declaration) : supe
r(propertySymbol, declaration); | |
| 227 | |
| 228 bool get auto => _isAuto(); | |
| 229 void set auto (value) { | |
| 230 if (value == true) | |
| 231 _setAuto(); | |
| 232 throw new ArgumentError(value); | |
| 233 } | |
| 234 | |
| 235 void setter(dynamic value) { | |
| 236 if (value == auto) | |
| 237 return _setAuto(); | |
| 238 return super.setter(value); | |
| 239 } | |
| 240 | |
| 241 external bool _isAuto(); | |
| 242 external void _setAuto(); | |
| 243 } | |
| 244 | |
| 245 class ColorProperty extends Property { | |
| 246 ColorProperty(Symbol propertySymbol, StyleDeclaration declaration) : super(pro
pertySymbol, declaration); | |
| 247 | |
| 248 int get alpha => _getRGBA() & 0xFF000000 >> 24; | |
| 249 void set alpha (int value) => _setRGBA(_getRGBA() & 0x00FFFFFF + value << 24); | |
| 250 int get red => _getRGBA() & 0x00FF0000 >> 16; | |
| 251 void set red (int value) => _setRGBA(_getRGBA() & 0xFF00FFFF + value << 16); | |
| 252 int get green => _getRGBA() & 0x0000FF00 >> 8; | |
| 253 void set green (int value) => _setRGBA(_getRGBA() & 0xFFFF00FF + value << 8); | |
| 254 int get blue => _getRGBA() & 0x000000FF >> 0; | |
| 255 void set blue (int value) => _setRGBA(_getRGBA() & 0xFFFFFF00 + value << 0); | |
| 256 | |
| 257 int get rgba => _getRGBA(); | |
| 258 void set rgba (int value) => _setRGBA(value); | |
| 259 | |
| 260 void setter(dynamic value) { | |
| 261 if (value is int) | |
| 262 return _setRGBA(value); | |
| 263 return super.setter(value); | |
| 264 } | |
| 265 | |
| 266 external int _getRGBA(); | |
| 267 // throws StateError if the value isn't a color | |
| 268 external void _setRGBA(int value); | |
| 269 } | |
| 270 | |
| 271 class Matrix { | |
| 272 const Matrix(this.a, this.b, this.c, this.d, this.e, this.f); | |
| 273 | |
| 274 // +- -+ | |
| 275 // | a c e | | |
| 276 // | b d f | | |
| 277 // | 0 0 1 | | |
| 278 // +- -+ | |
| 279 | |
| 280 final double a; | |
| 281 final double b; | |
| 282 final double c; | |
| 283 final double d; | |
| 284 final double e; | |
| 285 final double f; | |
| 286 } | |
| 287 | |
| 288 class TransformProperty extends Property { | |
| 289 TransformProperty(Symbol propertySymbol, StyleDeclaration declaration) : super
(propertySymbol, declaration); | |
| 290 | |
| 291 void reset() => setTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); | |
| 292 | |
| 293 void translate(double dx, double dy) => transform(1.0, 0.0, 0.0, 1.0, dx, dy); | |
| 294 void scale(double dw, double dh) => transform(dw, 0.0, 0.0, dh, 0.0, 0.0); | |
| 295 void rotate(double theta) => transform(cos(theta), -sin(theta), sin(theta), co
s(theta), 0.0, 0.0); | |
| 296 | |
| 297 // there's no "transform" getter since it would always return a new Matrix | |
| 298 // such that foo.transform == foo.transform would never be true | |
| 299 // and foo.transform = bar; bar == foo.transform would also never be true | |
| 300 // which is bad API | |
| 301 | |
| 302 external Matrix getTransform(); | |
| 303 // throws StateError if the value isn't a matrix | |
| 304 // returns a new matrix each time | |
| 305 external void setTransform(a, b, c, d, e, f); | |
| 306 external void transform(a, b, c, d, e, f); | |
| 307 // throws StateError if the value isn't a matrix | |
| 308 } | |
| 309 | |
| 310 external void autoLengthPropertyStringSetter(Symbol propertySymbol, StyleDeclara
tion declaration, String value); | |
| 311 external String autoLengthPropertyStringGetter(Symbol propertySymbol, StyleDecla
ration declaration); | |
| 312 external void colorPropertyStringSetter(Symbol propertySymbol, StyleDeclaration
declaration, String value); | |
| 313 external String colorPropertyStringGetter(Symbol propertySymbol, StyleDeclaratio
n declaration); | |
| 314 external void transformPropertyStringSetter(Symbol propertySymbol, StyleDeclarat
ion declaration, String value); | |
| 315 external String transformPropertyStringGetter(Symbol propertySymbol, StyleDeclar
ation declaration); | |
| 316 | |
| 317 void _init() { | |
| 318 registerProperty(new PropertyTable( | |
| 319 symbol: #height, | |
| 320 inherited: false, | |
| 321 stringSetter: autoLengthPropertyStringSetter, | |
| 322 stringGetter: autoLengthPropertyStringGetter, | |
| 323 objectConstructor: (Symbol propertySymbol, StyleDeclaration declaration) => | |
| 324 new AutoLengthProperty(propertySymbol, declaration))); | |
| 325 registerProperty(new PropertyTable( | |
| 326 symbol: #width, | |
| 327 inherited: false, | |
| 328 stringSetter: autoLengthPropertyStringSetter, | |
| 329 stringGetter: autoLengthPropertyStringGetter, | |
| 330 objectConstructor: (Symbol propertySymbol, StyleDeclaration declaration) => | |
| 331 new AutoLengthProperty(propertySymbol, declaration))); | |
| 332 registerProperty(new PropertyTable( | |
| 333 symbol: #color, | |
| 334 inherited: false, | |
| 335 stringSetter: colorPropertyStringSetter, | |
| 336 stringGetter: colorPropertyStringGetter, | |
| 337 objectConstructor: (Symbol propertySymbol, StyleDeclaration declaration) => | |
| 338 new ColorProperty(propertySymbol, declaration))); | |
| 339 registerProperty(new PropertyTable( | |
| 340 symbol: #transform, | |
| 341 inherited: false, | |
| 342 stringSetter: transformPropertyStringSetter, | |
| 343 stringGetter: transformPropertyStringGetter, | |
| 344 objectConstructor: (Symbol propertySymbol, StyleDeclaration declaration) => | |
| 345 new TransformProperty(propertySymbol, declaration))); | |
| 346 } | |
| 347 ``` | |
| 348 | |
| OLD | NEW |